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

Add D8 JavaScript assembly output #264

Open
ghost opened this issue Feb 8, 2017 · 35 comments
Open

Add D8 JavaScript assembly output #264

ghost opened this issue Feb 8, 2017 · 35 comments
Labels
new-language probably-wont-happen PRs are welcomed, but we do not currently plan on implementing this question request Request for something

Comments

@ghost
Copy link

ghost commented Feb 8, 2017

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.

@ghost
Copy link
Author

ghost commented Feb 8, 2017

Take a look at this and tell me it is not hilarious: https://youtu.be/B9igDWV5ZUg?t=930

@mattgodbolt
Copy link
Member

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.

@ghost
Copy link
Author

ghost commented Feb 9, 2017

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 node --print-code file.js. It prints a lot of shit but you can filter out stuff. This is probably the simplest way if you don't have time to compile V8 (it takes ages and it filled my HDD up to the brim).

@mattgodbolt
Copy link
Member

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.

@mattgodbolt
Copy link
Member

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 :)

@jaredwy
Copy link
Contributor

jaredwy commented Oct 17, 2018 via email

@ghost
Copy link
Author

ghost commented Oct 17, 2018

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.

@ghost
Copy link
Author

ghost commented Oct 17, 2018

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.

@jaredwy
Copy link
Contributor

jaredwy commented Oct 17, 2018 via email

@ghost
Copy link
Author

ghost commented Oct 17, 2018

Problem is without any deoptimizations you basically get the JS variant of

xor eax, eax
ret

That is, it's completely invalid

@mattgodbolt
Copy link
Member

We now support executing the user's code, so maybe this can be fiddled someday.

@jakobkummerow
Copy link

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 function f(x, y) { return x + y; }. What does that compile to?

If you call it a few times with small integers as arguments, e.g. f(42, 12345), and then force optimization, you get (aside from function entry/exit boilerplate and handling slow cases):

0x2b7200084b51    31  488b4d18       REX.W movq rcx,[rbp+0x18]
0x2b7200084b55    35  f6c101         testb rcx,0x1
0x2b7200084b58    38  0f85dd000000   jnz 0x2b7200084c3b  <+0x11b>
0x2b7200084b5e    3e  488b7d10       REX.W movq rdi,[rbp+0x10]
0x2b7200084b62    42  40f6c701       testb rdi,0x1
0x2b7200084b66    46  0f85db000000   jnz 0x2b7200084c47  <+0x127>
0x2b7200084b6c    4c  4c8bc7         REX.W movq r8,rdi
0x2b7200084b6f    4f  41d1f8         sarl r8, 1
0x2b7200084b72    52  4c8bc9         REX.W movq r9,rcx
0x2b7200084b75    55  41d1f9         sarl r9, 1

0x2b7200084b78    58  4503c1         addl r8,r9

0x2b7200084b7b    5b  0f80d2000000   jo 0x2b7200084c53  <+0x133>
0x2b7200084b81    61  498bc8         REX.W movq rcx,r8
0x2b7200084b84    64  4103c8         addl rcx,r8
0x2b7200084b87    67  0f803d000000   jo 0x2b7200084bca  <+0xaa>
0x2b7200084b8d    6d  488bc1         REX.W movq rax,rcx
0x2b7200084b90    70  488be5         REX.W movq rsp,rbp
0x2b7200084b93    73  5d             pop rbp
0x2b7200084b94    74  c21800         ret 0x18

If you call it with two floating-point (or very large) numbers, e.g. f(2.5, 1e20), you get:

0x2b7200085011    31  488b4d18       REX.W movq rcx,[rbp+0x18]
0x2b7200085015    35  f6c101         testb rcx,0x1
0x2b7200085018    38  0f8428000000   jz 0x2b7200085046  <+0x66>
0x2b720008501e    3e  8b79ff         movl rdi,[rcx-0x1]
0x2b7200085021    41  4903fd         REX.W addq rdi,r13
0x2b7200085024    44  4139bd40010000 cmpl [r13+0x140] (root (heap_number_map)),rdi
0x2b720008502b    4b  0f840b000000   jz 0x2b720008503c  <+0x5c>
0x2b7200085031    51  66837f0743     cmpw [rdi+0x7],0x43
0x2b7200085036    56  0f854a010000   jnz 0x2b7200085186  <+0x1a6>
0x2b720008503c    5c  c5fb104103     vmovsd xmm0,[rcx+0x3]
0x2b7200085041    61  e909000000     jmp 0x2b720008504f  <+0x6f>
0x2b7200085046    66  488bf9         REX.W movq rdi,rcx
0x2b7200085049    69  d1ff           sarl rdi, 1
0x2b720008504b    6b  c5832ac7       vcvtlsi2sd xmm0,xmm15,rdi
0x2b720008504f    6f  488b7d10       REX.W movq rdi,[rbp+0x10]
0x2b7200085053    73  40f6c701       testb rdi,0x1
0x2b7200085057    77  0f842a000000   jz 0x2b7200085087  <+0xa7>
0x2b720008505d    7d  448b47ff       movl r8,[rdi-0x1]
0x2b7200085061    81  4d03c5         REX.W addq r8,r13
0x2b7200085064    84  45398540010000 cmpl [r13+0x140] (root (heap_number_map)),r8
0x2b720008506b    8b  0f840c000000   jz 0x2b720008507d  <+0x9d>
0x2b7200085071    91  664183780743   cmpw [r8+0x7],0x43
0x2b7200085077    97  0f8515010000   jnz 0x2b7200085192  <+0x1b2>
0x2b720008507d    9d  c5fb104f03     vmovsd xmm1,[rdi+0x3]
0x2b7200085082    a2  e906000000     jmp 0x2b720008508d  <+0xad>
0x2b7200085087    a7  d1ff           sarl rdi, 1
0x2b7200085089    a9  c5832acf       vcvtlsi2sd xmm1,xmm15,rdi

0x2b720008508d    ad  c5fb58c1       vaddsd xmm0,xmm0,xmm1

0x2b7200085091    b1  c5fb2cc8       vcvttsd2si rcx,xmm0
0x2b7200085095    b5  c5832ac9       vcvtlsi2sd xmm1,xmm15,rcx
0x2b7200085099    b9  c5f92ec8       vucomisd xmm1,xmm0
0x2b720008509d    bd  0f8a24000000   jpe 0x2b72000850c7  <+0xe7>
0x2b72000850a3    c3  0f851e000000   jnz 0x2b72000850c7  <+0xe7>
0x2b72000850a9    c9  83f900         cmpl rcx,0x0
0x2b72000850ac    cc  0f848e000000   jz 0x2b7200085140  <+0x160>
0x2b72000850b2    d2  488bf9         REX.W movq rdi,rcx
0x2b72000850b5    d5  03f9           addl rdi,rcx
0x2b72000850b7    d7  0f800a000000   jo 0x2b72000850c7  <+0xe7>
0x2b72000850bd    dd  488bc7         REX.W movq rax,rdi
0x2b72000850c0    e0  488be5         REX.W movq rsp,rbp
0x2b72000850c3    e3  5d             pop rbp
0x2b72000850c4    e4  c21800         ret 0x18
0x2b72000850c7    e7  48b90862d501be550000 REX.W movq rcx,0x55be01d56208
0x2b72000850d1    f1  488b39         REX.W movq rdi,[rcx]
0x2b72000850d4    f4  4c8d470c       REX.W leaq r8,[rdi+0xc]
0x2b72000850d8    f8  49b91062d501be550000 REX.W movq r9,0x55be01d56210
0x2b72000850e2   102  4d3901         REX.W cmpq [r9],r8
0x2b72000850e5   105  0f866e000000   jna 0x2b7200085159  <+0x179>
0x2b72000850eb   10b  4c8d470c       REX.W leaq r8,[rdi+0xc]
0x2b72000850ef   10f  4c8901         REX.W movq [rcx],r8
0x2b72000850f2   112  488d4f01       REX.W leaq rcx,[rdi+0x1]
0x2b72000850f6   116  498bbd40010000 REX.W movq rdi,[r13+0x140] (root (heap_number_map))
0x2b72000850fd   11d  8979ff         movl [rcx-0x1],rdi
0x2b7200085100   120  c5fb114103     vmovsd [rcx+0x3],xmm0
0x2b7200085105   125  488bc1         REX.W movq rax,rcx
0x2b7200085108   128  ebb6           jmp 0x2b72000850c0  <+0xe0>

If you call it with a combination, e.g. f(2, 3.5), you'll get something in between; I'm skipping that here for brevity.

If you call it with two strings, e.g. f("hello", "world"), you'll get:

0x2b7200085f11    31  488b4518       REX.W movq rax,[rbp+0x18]
0x2b7200085f15    35  a801           test al,0x1
0x2b7200085f17    37  0f8490000000   jz 0x2b7200085fad  <+0xcd>
0x2b7200085f1d    3d  8b50ff         movl rdx,[rax-0x1]
0x2b7200085f20    40  4903d5         REX.W addq rdx,r13
0x2b7200085f23    43  66837a0740     cmpw [rdx+0x7],0x40
0x2b7200085f28    48  0f838b000000   jnc 0x2b7200085fb9  <+0xd9>
0x2b7200085f2e    4e  488b5d10       REX.W movq rbx,[rbp+0x10]
0x2b7200085f32    52  f6c301         testb rbx,0x1
0x2b7200085f35    55  0f848a000000   jz 0x2b7200085fc5  <+0xe5>
0x2b7200085f3b    5b  8b53ff         movl rdx,[rbx-0x1]
0x2b7200085f3e    5e  4903d5         REX.W addq rdx,r13
0x2b7200085f41    61  66837a0740     cmpw [rdx+0x7],0x40
0x2b7200085f46    66  0f8385000000   jnc 0x2b7200085fd1  <+0xf1>
0x2b7200085f4c    6c  8b5307         movl rdx,[rbx+0x7]
0x2b7200085f4f    6f  8b4807         movl rcx,[rax+0x7]
0x2b7200085f52    72  03d1           addl rdx,rcx
0x2b7200085f54    74  81fae9ffff1f   cmpl rdx,0x1fffffe9
0x2b7200085f5a    7a  0f837d000000   jnc 0x2b7200085fdd  <+0xfd>
0x2b7200085f60    80  33f6           xorl rsi,rsi

0x2b7200085f62    82  49ba2091feffbd550000 REX.W movq r10,0x55bdfffe9120  (StringAdd_CheckNone)
0x2b7200085f6c    8c  41ffd2         call r10

0x2b7200085f6f    8f  488be5         REX.W movq rsp,rbp
0x2b7200085f72    92  5d             pop rbp
0x2b7200085f73    93  c21800         ret 0x18

If you call it with random objects, e.g. f(true, {valueOf: () => 42}), you'll get this, which looks simpler, but that's because it delegates all the work to some builtin:

0x2b7200086311    31  33ff           xorl rdi,rdi
0x2b7200086313    33  48bbd12d2108722b0000 REX.W movq rbx,0x2b7208212dd1    ;; object: 0x2b7208212dd1 <FeedbackVector[1]>
0x2b720008631d    3d  488b5518       REX.W movq rdx,[rbp+0x18]
0x2b7200086321    41  488b4510       REX.W movq rax,[rbp+0x10]
0x2b7200086325    45  48bec52f2008722b0000 REX.W movq rsi,0x2b7208202fc5    ;; object: 0x2b7208202fc5 <NativeContext[240]>

0x2b720008632f    4f  49ba0031fcffbd550000 REX.W movq r10,0x55bdfffc3100  (Add_WithFeedback)
0x2b7200086339    59  41ffd2         call r10

0x2b720008633c    5c  488be5         REX.W movq rsp,rbp
0x2b720008633f    5f  5d             pop rbp
0x2b7200086340    60  c21800         ret 0x18

Lastly, if you force optimization of that function without any type feedback, you get:

0x335000086651    31  49c7c500000000 REX.W movq r13,0x0
0x335000086658    38  e8e3d90b00     call 0x335000144040     ;; deopt-soft deoptimization bailout

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).

@mattgodbolt
Copy link
Member

Thanks @jakobkummerow for the detailed reply!

@ghost
Copy link

ghost commented Jul 16, 2020

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.

@tylerhou
Copy link

tylerhou commented Jan 1, 2021

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()
})();

@tylerhou
Copy link

tylerhou commented Jan 1, 2021

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 */};

@partouf partouf added the probably-wont-happen PRs are welcomed, but we do not currently plan on implementing this label Mar 29, 2021
@gengjiawen
Copy link

Not sure we need a different issue for this ? but probably only show v8 bytecode ?

@STRd6
Copy link

STRd6 commented Sep 9, 2022

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.

@gautam1168
Copy link
Contributor

gautam1168 commented Feb 11, 2023

I have this working in a dev setup. Ill make a PR:

image

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.

@gautam1168
Copy link
Contributor

@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.

@gautam1168
Copy link
Contributor

In the above PR you already have the ability to see the output of wasm if you are willing to take the bytes of a .wasm file and put them inside javascript like this:
image

@gautam1168
Copy link
Contributor

gautam1168 commented Feb 12, 2023

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.

image

@gautam1168
Copy link
Contributor

@tylerhou In d8 its possible to print the bytecode for a specific function using the flag --print-bytecode-filter

By default it is set to --print-bytecode-filter="*"

@gautam1168
Copy link
Contributor

@jaredwy I see its possible to make v8 print out the machine code generated without having to run the functions by passing --always-sparkplug. But that is the baseline compiler. If you want the output of turbofan you will need to push the engine to optimize it. I have tried other flags that fiddle with how early d8 will generate the optimized code, but its not really working for a very simple add function I am using.

@jakobkummerow
Copy link

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 --print-opt-code is really what you want.

What I'm concerned about is the for(...; i < 1e6; ...) method to trigger optimization. It does address the issue of providing type feedback. However:

  • It's quite inefficient, as it actually runs the loop a million times. For non-trivial functions, this will be waaaay more than what's necessary, and could get pretty slow. I don't know how scarce godbolt.org's execution resources are.
  • It's difficult/tedious for users to estimate reasonable iteration counts, because both the number of calls it takes to trigger optimization, and the time it takes for the concurrent optimized compilation task to complete, depend on the function in question. People might resort to always using a super high iteration count, which exacerbates the wastefulness problem.
  • It's quite unobvious, at least compared to the Compiler Explorer's behavior for AOT-compiled languages, where you only need to provide function definitions and never have to call them.
  • Due to OSR, it causes optimization not just of the function that gets called, but also of the top-level code that calls it, causing extra noise in the output.

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:

function foo() { /* interesting stuff goes here */ }

%PrepareFunctionForOptimization(foo);  // Force eager type feedback collection.
for (let i = 0; i < 3; i++) foo();  // Very few iterations are enough.
%OptimizeFunctionOnNextCall(foo);  // Mark for optimization.
foo();

and then run d8 with --allow-natives-syntax to enable the %...() calls, but I suspect that that approach is unsuitable here:

  • The features that --allow-natives-syntax enables generally aren't vetted for security. I don't know whether malicious actors trying to compromise the Compiler Explorer server are a concern. Considering that (AFAIK) it can execute C/C++ binaries these days, maybe that's a solved problem.
  • If you wanted to automatically run a snippet like that behind the scenes (so that users only need to type up the function foo() {...} definition), you'd have to address the issue of that function's name. You could either hard-code a name that users must use, or try to somehow detect all functions that are defined in the top level.

I suppose we probably wouldn't mind adding a --godbolt-mode flag to the d8 shell if that would help, but that alone doesn't solve the problems here: we'd still need a well-thought-through design for how to select functions and their arguments.
As a point of comparison: common JS microbenchmarking sites are struggling with somewhat similar issues. One approach they've come up with is to separate preparational code, and the definition of various tests, into separate UI elements. Doing something similar here (maybe one text box to specify function foo() { ... }, and one to specify how to call it, e.g. foo(1, 2)) would be a fairly significant deviation from the Compiler Explorer's existing design though.


To end with something helpful, I have an idea how to get d8 onto the server: don't ☺, and just use node instead. Seeing that the Compiler Explorer is already a Node application, you can use the same Node binary (running in a fresh process) to execute JavaScript.

@gautam1168
Copy link
Contributor

gautam1168 commented Feb 13, 2023

@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 nsjail for executing inside a sandbox. I do not know if that will be enough to guard against execution of c executables as you said, but I think there would probably be a way to accommodate execution of javascript in a safe way.

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.

@gautam1168
Copy link
Contributor

gautam1168 commented Feb 18, 2023

@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 --trace-turbo. I tried it in a hosted version:
image

and it made more sense to me (I am only starting to learn assembly), than just looking at the ouput of --print-opt-code. I am not sure if this can be integrated with compiler explorer or if something like this can be had in the UI of compiler explorer. But, it does look useful to me. I guess the --trace-turbo is definitely official. I'm just wondering if compiler explorer could eventually show this directly. I do not know if that sits well with the direction of compiler explorer. I'm just asking, is this useful?

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:
image

@jakobkummerow
Copy link

is turbolizer a supported tool from v8?

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.)

I guess the --trace-turbo is definitely official

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 --trace-turbo's output; in fact I think it already has.

--print-opt-code is also off-by-default and as such not officially supported. But it's been around for 10+ years, and I see no reason why it wouldn't continue to be available. That's why I suggested using that flag.

I'm thinking the B1, B2 etc are the bytecodes

No, they're the basic blocks in the IR.

compiler explorer parses the assembly output of other compilers to make it more usable.

I agree that there's much potential for better output than just a raw dump of --print-opt-code. Which is related to the overall point I've been trying to make on this issue: how to integrate JavaScript into the Compiler Explorer IMHO is more of a "what behavior/UX do we want?" question than one of technical feasibility.

@gautam1168
Copy link
Contributor

@jakobkummerow thanks again for replying. I am using --print-opt-code in the pull request I have open right now. As per your recommendation I also considered using nodejs instead of a v8 build in the server. However, compiler explorer runs on an old version of nodejs so I cannot directly use that nodejs.

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 d8 --help which includes --allow-natives-syntax. I use the --print-opt-code flag by default. I will ask more about how to improve the output by parsing it once I succeed in getting the first PR merged. Thankyou for helping me so far :)

@gautam1168
Copy link
Contributor

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

@jakobkummerow
Copy link

Now you have to run the function a few times

or use --no-lazy-feedback-allocation as I have suggested on the PR.

@gautam1168
Copy link
Contributor

gautam1168 commented Apr 7, 2023

@jakobkummerow thankyou. For anyone who lands on this thread, the flag mentioned can be added through the UI like this:
https://godbolt.org/z/hT4czxsnY

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.

@gautam1168
Copy link
Contributor

Maybe this issue can be closed now?

@surajsharma
Copy link

surajsharma commented Apr 8, 2023

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: godbolt.org/z/8M7fh9qd3

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

@gautam1168
Copy link
Contributor

Can you tell me what is not working so I can try to fix it?

@jeremy-rifkin jeremy-rifkin added the request Request for something label Jun 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new-language probably-wont-happen PRs are welcomed, but we do not currently plan on implementing this question request Request for something
Projects
None yet
Development

No branches or pull requests

10 participants