-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Add D8 JavaScript assembly output #264
Comments
Take a look at this and tell me it is not hilarious: https://youtu.be/B9igDWV5ZUg?t=930 |
I'm aware that d8 can generate assembly dumps, but usually only in the context of executing the code (at least in my understanding/experience). So far I don't execute any of the user's code (though that's on the agenda at some point); this might open the door to such. |
Yep you could potentially launch a Node.js instance and let it run for a maximum of say 1 second. You can get the assembly printed to stdout by running |
Also mentioned recently in a twitter conversation - https://twitter.com/scanlime/status/962741616690606080 ... a great idea if we can pull it off. Executing user code is coming Real Soon Now™ for compiled languages...I guess PGO should be considered too. |
I got an email "any chance you can add the D8 compiler debugging tool for the V8 engine the list of supported compilers?" -- I think the time is coming. My objections to JITted code are evaporating :) |
I am 90% sure we can do it with out the need to run the code.
I did a small experiment a while ago. It is possible to get v8 to apply
optimistic optimisations on first compile.
…On Thu, 18 Oct 2018 at 08:07 Matt Godbolt ***@***.***> wrote:
I got an email "any chance you can add the D8 compiler debugging tool for
the V8 engine the list of supported compilers?" -- I think the time is
coming. My objections to JITted code are evaporating :)
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#264 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AASId7bW4zR5itRnC2dNGOl2XvHv5myWks5ul5wSgaJpZM4L7NQY>
.
|
That would be like a debug build or even worse though. JS provide almost no information to the compiler so it cannot make any good decisions without running the code at least once (I don't know exactly how many times it needs but probably a few). That, or it will generate way overoptimistic code that will be completely invalid. |
Take the snippet, wrap it in a for loop behind rhe scenes and run it until V8 emits optkmized code or until 1 second elapsed whichever comes first. |
It seems the flag that allowed the optimising compiler kick in based on the
type information ignition gathered on its first pass is now dead anyway :(
It wouldn't be worse than a debug build, it would be highly optimistic(e.g.
missing any deopts) but still the code would be coming from turbofan not
just the lazy version.
…On Thu, 18 Oct 2018 at 09:01 Alex Hultman ***@***.***> wrote:
Take the snippet, wrap it in a for loop behind rhe scenes and run it until
V8 emits optkmized code or until 1 second elapsed whichever comes first.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#264 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AASId3n0fGt0qnCxEBz_s_7oggdPBaPuks5ul6jWgaJpZM4L7NQY>
.
|
Problem is without any deoptimizations you basically get the JS variant of xor eax, eax That is, it's completely invalid |
We now support executing the user's code, so maybe this can be fiddled someday. |
V8 developer here. We would be thrilled to see d8 added to the Compiler Explorer; however for JavaScript the difficulty is that what the optimizing compiler does depends heavily on the type feedback that the function has collected while running in non-optimized mode. To illustrate: consider a very simple If you call it a few times with small integers as arguments, e.g.
If you call it with two floating-point (or very large) numbers, e.g.
If you call it with a combination, e.g. If you call it with two strings, e.g.
If you call it with random objects, e.g.
Lastly, if you force optimization of that function without any type feedback, you get:
Because the compiler wouldn't know which of the variants above it should pick. Also, a bunch of this (frankly, shockingly large amount of) code is due to the fact that the function is so short; the same addition somewhere in the middle of a larger function (where the inputs are known, as opposed to having to type-check function parameters first) would result in a much shorter sequence. Long story short, I think you'd need to provide a way for users to specify the arguments that the function should get called with. On another note: things are very different for WebAssembly! Wasm contains enough information in the module that it can be optimized without any type feedback. You'd probably want to specify the Wasm code in the "WAT" text format, which V8 doesn't understand directly, so you'd have to convert that to a binary module first (e.g. using wabt's wat2wasm). The binary module could then be compiled by d8 (via a tiny JS wrapper), and the disassembly printed to stdout. Feel free to ping me if you want to discuss this more (and/or need help getting it to work). |
Thanks @jakobkummerow for the detailed reply! |
This is true for all scripting languages. V8 is never really compiling anything, it's more of a glorified interpreter. You never get a true integer add or floating add because you always need bailout checks. |
I wonder if Godbolt could run the whole input JS file, but display assembly output for functions defined in the global scope. // Defined in the global scope, so Godbolt shows the assembly.
function getXProperty(obj) {
return obj.x;
}
(() => {
// Not defined in the global scope, so no assembly output.
function callMonomorphic() {
for (let i = 0; i < 1000; ++i) {
getXProperty({x: 1});
getXProperty({x: 2});
}
}
function callPolymorphic() {
for (let i = 0; i < 1000; ++i) {
getXProperty({x: 1});
getXProperty({x: 2, y: 2});
}
}
// Uncomment one of these lines to see optimized assembly output for getXProperty.
// callMonomorphic()
// callPolymorphic()
})(); |
Another alternative is displaying assembly output for functions that are exported. Then one could inspect the assembly output for functions that capture variables from a bunch of different stack frames: let asm;
function a() {
const someVarA = 1;
function b() {
const someVarB = 2;
function c() {
const someVarC = { x: 3 };
function d() {
const someVarD = { y: 4 };
let asm = () => {
return someVarA + someVarB + someVarC.x + someVarD.y
}
}
d();
}
c();
}
b();
}
a();
for (let i = 0; i < 1000; ++i) {
asm(); // optimize
}
export { asm, /* a, b, c, d */}; |
Not sure we need a different issue for this ? but probably only show v8 bytecode ? |
I would like to see V8 bytecode for JS if possible. If someone can point me in the right direction I could try and contribute. |
I have this working in a dev setup. Ill make a PR: Note that in order to get the assembly we have to force v8 to optimize the code. That only happens if the function runs many times. Notice I'm running the loop so many times. Although the bytecode will get printed regardless of whether the code is executed. I do not know if I can force v8 to optimize the code even if it has not run off the top of my head. It doesn't seem to have any command line options for doing that. But then this means someone can trigger on a program that requires a LOT of execution which can kill your server. On the other hand, if I modify v8 a little bit to force it to optimize the functions even when the code has not been executed I will have to fork v8. In either case, I can get it to print the bytecode. How does one go about adding an executable to the server? I have v8 compiled locally but I don't think its available as an executable for download somewhere. I could give you the executable I created to put it on s3 or something. |
@jakobkummerow would it not be useful the way I have done it above? I just call the function several times and I'm assuming when I want to check what kind of code will get generated for a specific kind of argument Ill just add a snippet at the bottom to call it with those kinds of arguments. Would that be enough to hint v8 ? And thinking just pie in the sky here, would be possible to maybe make a build of d8 where we can make it output all the possible kinds of codes it can generate? I don't know if the number of different kinds of optimizations v8 can do to a function is very large. If that is the case, then maybe I can ask it to somehow tell me the things it will check to do the optimization and I could show those things on the UI in the code editor in some way. Thus enabling me to interact with some kind of inputs that let me interact with optimizations that are done. For example, for the add function I could provide something like a dropdown of the possible kinds of types the arguments can be, and as someone fiddles with those the output on the right could change. I am completely out of my depth here though and could be saying something that is completely beyond my ability to do. For wasm though Im going to start working on adding a little thing somewhere where you can write both C and javascript and it will show the wasm on the right. Ill try to update my PR with that in a couple weeks. |
I have this working now as well (although this is not pushed to a PR yet). You can make a tree view and add a javascript file, a c file and a javascript compiler. You can use a special string in javascript where the server will compile the c file and insert the bytes of the wasm file generated. I think there are probably better ways to do this, like perhaps I could enable CMake in the treeview for javascript where I could provide some way to have more control over how the C source is built and linked. I could also maybe figure out how to not have to instantiate the module using a string, but just open that file and load it in a C++ program (by embedding v8 in it) and then running it.... Basically I could do a bunch of stuff here, but it does seem to work. |
@tylerhou In d8 its possible to print the bytecode for a specific function using the flag By default it is set to |
@jaredwy I see its possible to make v8 print out the machine code generated without having to run the functions by passing |
Cool stuff. You asked whether this is "useful", that's not really for me to decide. Personally, I don't think it's useful to show bytecode or unoptimized code. My reasoning is: when I use the Compiler Explorer (for C++), then almost always the question I'm trying to answer is "how do I need to write my code so that compilers generate the best possible optimized machine code for it?". There is, by definition, never a reason to make decisions about the JavaScript you write with bytecode generation in mind: (1) because by looking at bytecode you can't really tell how efficient it is anyway, because each handler could be doing an arbitrary amount of work, and (2) because bytecode can be as inefficient as it wants, as any hot functions will get tiered up anyway. What the "Sparkplug" baseline compiler does is basically just glue together the interpreter's bytecode handlers; it is hence uninteresting for the same reasons as bytecode IMHO. So I think that What I'm concerned about is the
I'm not a Compiler Explorer developer myself, so my concerns aren't necessarily blocking; just something to consider when finalizing the design here. As you can see, it's not that hard to come up with something that approximately works; it's quite a bit harder to make it as well-designed as the rest of the Compiler Explorer. I don't have a particular alternative suggestion. What we tend to do for our own tests is something like:
and then run
I suppose we probably wouldn't mind adding a To end with something helpful, I have an idea how to get |
@jakobkummerow thankyou for the reply. I will read the reply again carefully before I go further. However, for the security concerns, compiler explorer supports executing user code now. And one of the things I saw in the code was I think nodejs does not allow the native syntax way you have shown to force an optimization above. Correct me if I am wrong. I could come up with a UX design that will allow users to easily use that without having access to the native syntax themselves (like you said in the second point). I think I could just build some customizations over the editor for javascript files which will allow them to mark a function for optimization and then provide some kind of loop to call it a few times. Like you said, its important that we are able to get the optimized code as the output, so just using it through nodejs may not be a good solution. Nodejs may also open CE up way more than if I just use d8, since nodejs will accept so many functions that can start modifying the server itself. Let me think about what you said above some more and I will get back with a more thought out response. Meanwhile, I am also not a compiler explorer developer. So if anyone from the team has insights about this I am looking forward to reading about it. |
@jakobkummerow is turbolizer a supported tool from v8? I do not understand everything it is doing, but as far as I understand its just parsing the output of and it made more sense to me (I am only starting to learn assembly), than just looking at the ouput of If nothing, I could definitely look at the source of this tool to figure out how to parse the assembly. I see that compiler explorer parses the assembly output of other compilers to make it more usable. And I think maybe at some point I could take help from this source code to understand how to parse things to make sense of them. I definitely could not understand how the javascript was mapping to the assembly before I saw this. It also seems to show which bytecodes map to which assembly instructions. Although I can't really say that for sure because I don't see what I expect (Im thinking the B1, B2) etc are the bytecodes: |
Define "supported". It's a tool made by V8 and for V8. We'll keep maintaining it as long as we find it useful for debugging V8. I'm not sure adding it to Compiler Explorer is particularly useful; the reason is that its primary purpose is to debug very specific versions of V8 (often even in-progress patches that aren't working quite right yet), so a hosted copy that runs it against a fixed and typically somewhat outdated version of V8 wouldn't serve a need I have. (Of course, there could be others who would find such an integration useful or at least interesting for curiosity purposes.)
Like all off-by-default flags, it is not officially supported. In practice, it probably won't break and its behavior probably won't typically change much, but nobody is giving any guarantees about that: it's always possible that it will change without notice, and possibly in an incompatible way. This implies that Turbolizer and V8 generally need to be in sync, and that's why https://v8.github.io/tools/ allows you to select the version of the tool that you want to use. Especially as work on the "Turboshaft" project intensifies, I wouldn't be surprised if that included changes to
No, they're the basic blocks in the IR.
I agree that there's much potential for better output than just a raw dump of |
@jakobkummerow thanks again for replying. I am using I also checked how compiler explorer integrates python. And actually each distribution of python is built from source using a docker container. So, I surmised that I would actually have to build nodejs if I was going to use it. Which didn't make sense to me as it will take even more work than building v8 from one of the /branch-heads/ branches. So currently I have the pull request open for having a build of v8 made as specified in the v8 docs using a docker container. My first PR right now is attempting to get a versioned d8 build into the server as well as allow anyone to run d8 with all the d8 flags available in |
For anyone who might be wondering why the v8 (trunk) build is not generating any assembly code output, it seems there has been a change between v8 11.3 and trunk which is doing this. Now you have to run the function a few times before it will generate some assembly output. Here is how: https://godbolt.org/z/8M7fh9qd3 |
or use |
@jakobkummerow thankyou. For anyone who lands on this thread, the flag mentioned can be added through the UI like this: You can see that the loop of calls that is present in the previous link is not required with this flag. I am not sure how I can put all this information into one place for everyone who is landing at the ce page though. @jakobkummerow if I add that flag into the default configuration then it cannot be removed by a user. But this way it can be added by anyone who wants to add it. |
Maybe this issue can be closed now? |
hell no your solution doesn't work and is a hack at best, would like a less adventurous alternative first |
Can you tell me what is not working so I can try to fix it? |
Hi,
It would be hilarious if we could have an option to use V8's D8 disassembler to show some assembly generation of JavaScript code.
The text was updated successfully, but these errors were encountered: