omarandlorraine / strop

Stochastically generates machine code
MIT License
97 stars 1 forks source link

Support for "pseudo-code" output #26

Closed LoganDark closed 2 years ago

LoganDark commented 2 years ago

Right now strop outputs assembly/machine code - this is OK if you're going to run it directly on some architecture, but what if you want to put the idea into code to compile it for any architecture? Every architecture has a version of some primitive operations, like bit shifting and masking, moving bytes around and so on, and of course variables/registers. For example, fast-inverse-square-root was implemented in C, not x86 raw assembly.

I think a "holy grail" of strop will be when it can come up with a fast-inverse-square-root algorithm - or even a version that's even faster - as pseudo-code, such that it's a relatively straightforward port to C or Rust. Right now, strop is compiling to one architecture, and then you have to reverse-engineer it.

To achieve the desired effects, the "pseudo-code" would have to be interpreted by strop "as if" it were compiled for some abstract machine, for the purposes of achieving the desired behavior from the pseudo-subroutine. If this is possible, strop wouldn't be useful just for generating subroutines, but for generating new algorithms that can be implemented in any programming language.

For example, I want an sRGB color blending function that doesn't use any floating-point math. It can be implemented by a couple lookup tables, but then you spend kilobytes of space (or 512 bytes if you want to completely defeat the gamma curve) for them.

Strop could generate ridiculous stuff that happens to do exactly what I want for the entire input space (all 56 bits of it), but that's no use if I can't decode the assembly (this is not a trivial algorithm).

So pseudo-code would further open up the door to use strop as a real tool for finding real performant solutions to problems that currently require verbose/expensive implementations in order to be correct. My example is approximately what strop is eventually intended to solve, right? :)

P.S. this is my first issue here, how'd I do?

omarandlorraine commented 2 years ago

Right now strop outputs assembly/machine code - this is OK if you're going to run it directly on some architecture,

This is the intended use-case. Most compilers have asm directives you can use to embed what strop comes up with. Of course it's not cross platform, but as the programmer you probably know what the target is (or what the targets plural are), so then you can generate the assembly for what you need.

but what if you want to put the idea into code to compile it for any architecture?

LLVM has a C backend (or least, it has had. I don't know if it's still there), which can be used for compiling a C program, and then outputting optimized C. Maybe a C backend could be useful for strop as well. Of course, you don't get access to the carry flag and things like that, but.

For example, fast-inverse-square-root was implemented in C, not x86 raw assembly.

It was, but not in a platform-agnostic way. It assumes IEEE-754 floats and maybe other platform specific assumptions. It has undefined behavior all over. I imagine that same routine would not do the same thing on the Commodore 64 or whatever. For these reasons, it could be argued that it would have been better to do it in assembly. That would also cut out needing to go through memory for that line which casts the float to int and back again.

So pseudo-code would further open up the door to use strop as a real tool for finding real performant solutions to problems that currently require verbose/expensive implementations in order to be correct.

Do you have a particular one in mind? Maybe the intermediate representation from a compiler, like GIMPLE or something? Or would C actually do the trick?

My example is approximately what strop is eventually intended to solve, right? :)

Exactly so.

P.S. this is my first issue here, how'd I do?

This is the kind of thing I want to hear about, so it's good 🙂

LoganDark commented 2 years ago

This is the intended use-case. Most compilers have asm directives you can use to embed what strop comes up with. Of course it's not cross platform, but as the programmer you probably know what the target is (or what the targets plural are), so then you can generate the assembly for what you need.

I'm aware that it's intended and healthy to use machine code when you know the architecture. But in reusable library crates, I'd prefer to let rustc create the machine code for me. :)

It was, but not in a platform-agnostic way. It assumes IEEE-754 floats and maybe other platform specific assumptions.

Of course, knowing what assumptions to make is a part of using strop effectively. If there were to be a "pseudo-code" backend, or even C as you mention, it would have to be configurable to what operations you have available and the semantics of your target architecture...

Do you have a particular one in mind?

Yes, sRGB color blending. I can currently do it with two (or three, or four, but two is fastest) lookup tables and a bit of integer math. However, the lookup tables are large (RGB16 to sRGB8 is 64KiB), so I wonder if strop can come up with something to replace the lookup table. In order to do that I need to be able to put it into Rust - no asm.

(Edit: I actually realize you may have meant "a particular pseudo-code language" in which case I don't really have any idea. C is difficult to read, so I was envisioning something with a bit less sigils. Think "Wikipedia algorithm pseudo-code")

I don't have the profiling tools or the domain knowledge to confirm this, but I suspect using pure integer math would allow the function to execute entirely from the instruction cache without having to touch memory. I can't say if that would help performance but I'd like to try it at least.

omarandlorraine commented 2 years ago

I've been thinking about this lately, and wanted to ask, have you considered another superoptimizer? Maybe souper or denali?

This one has a focus on targeting architectures not supported by LLVM-based toolchains like rustc (which is of couse not set in stone). Just in case you have luck with a more mature project. I would be interested to see what you come up with!

LoganDark commented 2 years ago

I've been thinking about this lately, and wanted to ask, have you considered another superoptimizer? Maybe souper or denali?

No. I mean I guess I have considered "something not written in Rust", but the problem is that they tend to be huge C/C++ projects with finicky Makefiles and tons of dependencies and I hate it.

I guess if there was something out there that was as easy to try as strop I could check it out, but the search engine is soo far awaayy... xD

omarandlorraine commented 2 years ago

I'm going to close this as not planned, but if you come up with something suitable, please go ahead and open a pull request.

LoganDark commented 2 years ago

I guess strop is only going to be useful for assembly programming then. :p

omarandlorraine commented 2 years ago

I guess strop is only going to be useful for assembly programming then. :p

I think it's more important to get the codebase readable and navigable. Maybe then we can plan a way ahead with this. What do you think?

omarandlorraine commented 1 year ago

Do you think an LLVM IR backend will do the trick for you?

LoganDark commented 1 year ago

Do you think an LLVM IR backend will do the trick for you?

I'm not sure. I don't think that's a great target. It's also difficult to translate back into source code.