Technical discussion part 3: property caching madness in JIT

Dynamic languages like JavaScript have a lot of interesting fetures: we can create or destroy new classes during runtime or assign anything to any variable regardless of its type. These features makes them popular and since computers are getting faster, more and more tasks are performed by these languages. The fact that a language is dynamic does not necessarily mean that programs written in it have to be slow. Perhaps they will never be as fast as a compiled language, but there are some nice optimization algorithms for them. Those algorithms are different form static compiler optimizations. One such feature is property and call target caching. Let's see how these cachings are implemented in WebKit.

Property caching is based on the observation that the type of a value at a given code location is the same most of the time even for dynamic languages. The following example shows this behaviour in practice:

var array = [ "x", "y", "z" ]
var object = { separator : ","  }
var s = "a"
for (i = 0; < 10, ++i) {
  s += array[i % 3] + object.separator

As you can see, the types do not change in this small example, the variables retain their type even after an assignment ( s+= ...) operator. Resolving an identifier using the current scope chain or using a member of an object is a very slow operation. How can we make it faster? Let's cache the type and the result of the last resolve operation. Next time, when this particular location is reached again, we only have to compare the type of the variable to the cached type. If they are the same, we can use the cached value. This is true for function calls as well. We can cache the target of calls, and use this cached value for fast calls (that means we can skip several checks, like "Has the JIT code been generated already?" or "Does the arity of the function match the number of passed arguments?")

In the following, I will focus on the JIT level. The JIT compiler provides some utility functions for the high-level resolve operations. When there is an opportunity to cache a value, those functions are called. The cache itself operates in two different ways. One way is to patch the code itself, and store these values directly into the code. Since those values are pointers or 32 bit integers, we put these constants into the constant pool on ARM and do not directly patch the payload field of the instructions like it is done on x86. Sometimes even the instructions must be changed. The JSObject in JavaScriptCore can store its members in an inline buffer, provided that the number of these members is at most four (this approach saves some memory):

class JSObject {
  union {
    PropertyStorage* propertyStorage;
    JSValue inlineStorage[4];

By default, a "ldr reg, [JSObjectPtrReg, GET_FIELD_FFSET(JSObject, propertyStorage)]" instruction is generated into the jit code to access the cached property storage. This ldr instruction may be patched to an "add reg, JSObjectPtrReg, GET_FIELD_FFSET(JSObject, propertyStorage)" if the cached JSObject does not allocate a propertyStorage object. On x86, this is a "mov reg, [address]" to "lea reg, [address]" transformation.

The second way is creating small stub functions, which compare the input type to their cached value. These stub functions are connected by unconditional branches. The following figure is intended to show the general concept so some technical details are omitted:

[get_by_id fast case entry]
[check type] --- fail --> [stub entry]
[get cached value]        [check type] --- fail --> [stub entry]
      <----- return ----  [get cached value]        [check type] --- fail --> [slow case entry]
      <----------------- return ------------------  [get cached value]        [call resolve]
      <------------------------------- return ------------------------------  [return]
[get_by_id done]

There is a limit for such functions, which is 4 for a particular operation, to avoid long chains. Probably it is not worth to cache more than 4 values, it is just a waste of memory and speed.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • No HTML tags allowed
  • Lines and paragraphs break automatically.

More information about formatting options

This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Fill in the blank