As I said before, the runtime profiler has nothing to do with ICs, neither
does optimized code. (Also, there isn't "the IC", there are many ICs,
e.g. function
f(x) { x.a.b.c = 1; } has two LoadICs and one StoreIC.)

The "full compiler" generates unoptimized code for each function the first
time it is called. This code is full of uninitialized ICs. When the
function's unoptimized code is then executed, the ICs will miss and get
patched ("transition") to states corresponding to the occurring types. If
necessary they'll miss repeatedly. After a while they've seen all the types
and will stop missing. That's how the IC system works.

One of the basic ideas of the optimizing compiler is to look at the state
of the ICs in unoptimized code for the given function (specifically, which
types they've seen) and use that information to generate optimized code *for
these types*. If all goes well, optimized code doesn't use ICs at all,
because it chooses fixed assumptions over flexibility (which is one reason
why it can be faster than non-optimized code -- at the risk of having to
deoptimize when an assumption turns out to be wrong).

For load and store ICs, "megamorphic" state has replaced "generic" state,
here's an example:

function load(x) {
  return x.foo;
}
               // The LoadIC in load() transitions to:
load({});      // Premonomorphic
load({});      // Monomorphic
load({b: 1});  // Polymorphic (2)
load({c: 1});  // Polymorphic (3)
load({d: 1});  // Polymorphic (4)
load({e: 1});  // Megamorphic

--trace-ic output:
[LoadIC in ~load+52 at scratch.js:2 (0->.) #foo]
[LoadIC in ~load+52 at scratch.js:2 (.->1) #foo]
[LoadIC in ~load+52 at scratch.js:2 (1->P) #foo]
[LoadIC in ~load+52 at scratch.js:2 (P->P) #foo]
[LoadIC in ~load+52 at scratch.js:2 (P->P) #foo]
[LoadIC in ~load+52 at scratch.js:2 (P->N) #foo]

On Tue, Sep 22, 2015 at 10:17 AM, Ignacio Queralt <[email protected]>
wrote:

> Thank you for your answer!
>
> The runtime profiler is the one that decides if a function is hot, right?
> I wanted to have a general understanding of how the IC works. How the
> profiler runs, detects hot functions, or small functions, to optimize, and
> tries to optimize them.
> Basically, the Full compiler compiles the full code (except functions, for
> them the compiler generates stubs), after that, the runtime profiler starts
> running, and choosing which functions are hot, based on how many times are
> the functions placed in the stack, or if the function are small, or if
> there's enough type information about the function. And for this functions,
> there are attempts to optimize them with Crankshaft, and save the optimized
> code in IC stubs. If the assumptions about the type expected are right,
> then there's some time gaining due to the optimized code (cached in the IC
> stub), but if there the assumptions are wrong (i.e. there's a new type
> other than the expected) then deoptimization happens, the optimized code is
> thrown away, and the function is re-compiled with the Full compiler.
> Would that be an accurate description of how the IC works?
>
> I've read online in a Google V8 presentation that there are the following
> states regarding IC:
> Uninitialized -> Pre-monomorphic -> Monomorphic -> Polymorphic/Megamorphic
> -> Generic
> How should I build an example, or which flag should I use to see the
> transitions between these states?
>
> Thank you for your help! I'm new with the v8, and the replies are really
> helpful.
>
>
>
> On Monday, September 21, 2015 at 5:43:07 PM UTC+2, Jakob Kummerow wrote:
>>
>> On Mon, Sep 21, 2015 at 4:53 PM, Ignacio Queralt <[email protected]>
>> wrote:
>>
>>> [snip]
>>>
>>> My questions would be:
>>> 1) Where are the transitions between Uninitialized, and Monomorphic of
>>> the newadd function, for instance? I read that there are some Uninitialized
>>> -> Smi, but I can't see the relation in the output to my function newadd().
>>>
>> We don't use the term "monomorphic" for binary ops, only for loads and
>> stores, so the BinaryOpIC transitions you're seeing in "newadd" are exactly
>> what's expected to happen.
>>
>>> 2) How does the IC technique works here? I've seen in the code, in
>>> runtime-profiler.cc that here's where the OptimizeNow() function is called,
>>> but I don't understand how many times or why is it called. Does anyone here
>>> knows how it works?
>>>
>> The runtime profiler has nothing to do with ICs. (Well, almost nothing,
>> it uses some hints from the IC system to make its decisions, but is not
>> required for the IC system to work.) An IC misses when it sees a new type,
>> then the code in src/ic/ic.cc patches it to fit the new types.
>>
>>> 3) What does LoadIC, StoreIC, CompareIC and BinaryOpcIC mean?
>>>
>> A LoadIC handles loads, e.g. "foo.bar". A StoreIC handles stores, e.g.
>> "foo.bar = 1". A Compare IC handles comparisons, e.g. "foo == bar".
>> Finally, a BinaryOpIC handles binary ops (hard to guess, eh?), e.g. "foo +
>> bar".
>>
>> --
> --
> v8-users mailing list
> [email protected]
> http://groups.google.com/group/v8-users
> ---
> You received this message because you are subscribed to the Google Groups
> "v8-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
-- 
v8-users mailing list
[email protected]
http://groups.google.com/group/v8-users
--- 
You received this message because you are subscribed to the Google Groups 
"v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to