Description
It would be good if using __emit
as a function parameter maybe bypassed some checks, for example:
Func(const arr[])
{
printf("%s", arr);
}
main()
{
new string[] = "hello";
new a = ref(string);
Func(__emit(load.s.pri a));
}
That currently gives a type mismatch error, but maybe it shouldn't? If you're using __emit
you're clearly not worrying about the normal language limitations, and so you should be able to pass addresses around quite easily.
I was trying to think about how this would look, and how it would work most efficiently. As I understand __emit
, it basically uses pri
as the return value and therefore operates on that as if it were a normal function return. Thus using it as a function parameter there is equivalent to load.s.pri a, push.pri
. That then begs the question - why not just use push.s a
? There's no way to here, because of how __emit
works, but maybe we as the assembly writers know there is a better way.
So I came up with an idea. I don't know if it will work or is compatible with existing code, but have some separation between __emit()
and __emit{}
in semantics. ()
will mirror normal expression syntax - so the code above remains exactly as it is and gives an error. {}
, which mirrors existing statement syntax is a little more clever. It DOESN'T treat pri
as a return value, so the following becomes invalid:
new var = __emit { lctrl 5 };
(this might be a breaking change, but I don't think it will affect many people yet, as this feature isn't even properly documented outside several PRs). Instead, it asserts that we know what we are doing and we are taking responsibility for ensuring that the state is correct after we are done. So, as a function parameter we can do:
Func(__emit { push.s a });
And this will disable all standard code and checks for that parameter - no push
is generated for us, no type checks, nothing. the compiler still knows there should be a parameter there, for the purposes of pushing the parameter byte count, but does nothing else. I'm not quite sure how to marry the semantics of "this is fine as a parameter, but not as an allocation" - it seems like they should always be the same thing, but there is still a way to do the allocation using ()
instead, or you could just use a {}
block after the new
and use stor.s var
if you really want to use {}
.
I thought about a few ways to somewhat keep existing sane return syntax, which will be wanted a lot of the time, but also allow for more efficient and controlled pushes. This was the best I could come up with, but there may be better ways. I've finally gotten around to using __emit
in YSI, hence the increase in issues/suggestions related to it.