Let’s Understand Chrome V8 — Chapter 7: Stack Frame

Written by huidou | Published 2022/09/01
Tech Story Tags: javascript | chrome-v8 | google-chrome | javascript-development | security | programming | google | technology

TLDRIn this paper, we’ll talk about the stack frame of two common function calls, “the arguments are less than the declared parameters” and “the arguments are more than the declared parameters”. Explain why these two function calls(arguments mismatch) do not cause a stack overflow, but can still run and output results correctly.via the TL;DR App

Welcome to other chapters of Let’s Understand Chrome V8

In this paper, we’ll talk about the stack frame of two common function calls, “the arguments are less than the declared parameters” and “the arguments are more than the declared parameters”. Explain why these two function calls(arguments mismatch) do not cause a stack overflow, but can still run and output results correctly.

1. Introduction

The stack frame is used to store arguments, the return of callee, local variables, and registers. The steps of creating a stack frame is below:

  • Callee’s parameters. Push the stack if there are params.

  • Push the return of the callee.

  • Step into callee, Push ESP.

  • Make EBP = ESP.

  • Reserve stack space for local variables if there are.

  • Push the register to be protected onto the stack.

Here’s an example:

Figure 1 shows the call stack when add42 is executed.

When the add42 executes, Ignition generates it’s bytecode, installs the stack, enters the add42’s stack space, and finally returns to the caller.

Figure 2 is the stack frame of add42, which is created by InterpreterPushArgsThenCall that there are more details. Let’s look at the following two cases:

  • call add42 without argument — add42()
  • call add32 with three arguments — add42(91,1,2)

In sloppy mode, both two calls execute normally and the add(91,1,2) can return the expected result — 133. How does the stack frame work?

2. Register, Bytecode

Before diving into the stack frame, let’s take a look at the registers and bytecodes, which helps to understand the stack better. The call of add42(91) is below.

V8 uses non-negative hexadecimal integers to encode registers, fa is registers-r1, f9 is r2…

In the last line — 5f fa f9 02 CallNoFeedback r1, r2-r3, fa is r1, f9 is r2, 02 is length. The combination of f9 and 02 means: a register list of length 2 starting with r2.

The add42’s bytecde is below:

In 1st line of code, the encoding of the parameter a0 is 02, which is the offset of the parameter in the stack frame, shown in Figure 2. The complement of fa is 6, and FP -6 is exactly register r1; FP +2 is exactly argument 91.

By moving the FP pointer, you can quickly access parameters and registers, which simplifies the design of bytecodes.

Register addressing formula: r[i]=FP-2-kFixedFrameHeaderSize-i. Arguments addressing formula: a[i]=FP+2+parameter_count-i-1.

3. Adaptor Frame

Although the adaptor frame has been abandoned, learning it still helps to learn stack frame better.

add42()add42(91,1,2,)

We know that add42() returns Nan, add42(91,1,2) returns 133. Figure 3 shows the adaptor frame.

The adaptor frame has two important members:

  • Number of arguments, the number records the number of arguments, it comes from JSFunction. For add42(x), it is 1;
  • Arguments slot, the slot is used to store the arguments, the number of slots is (1).

For add42(), one argument is missing, so fill slot with Undefined, see left side in Figure 3.

For add42(91,1,2), although there are three arguments, Number of arguments is 1, so fill the slot with the first argument — 91.

No matter how many arguments are passed in, the add42 always can get the correct arguments, so it executes correctly and returns the expected value.

The adaptor frame uses Number of parameters and slot to normalize arguments mismatch so that a function can be called correctly.

Why is the adapter framework scrapped? Of course, for improving performance!When a function is called, two stack frames are created. One is the adapter framework, and the other is the called framework, which is too time-consuming.

4. Stack Frame

The new stack frame removes adaptor frame, but still uses number of arguments, as shown in Figure 4.

The intention of the new stack frame is to meet the four requirements below:

(1) Get parameters and registers using FP pointer and offset.

(2) Normalize arguments mismatch, add42() and add42(91,1,2) can be called correctly.

(3) The stack can be rolled back correctly when callee returns.

(4) Abandon adaptor frame, reduce the number of stack builds.

Let’s explain how to meet these four requirements.

For (1): The push order and encoding are not changed, so it is met.

For (2): Number of arguments is here. When the length of args >= Number, only the args[0:Number-1] are retained. When it < Number, use Undefined as the missing parameter.

For (3): From figure 4, we met this requirement.

For (4): The adaptor frame has already gone.

The code to build stack frame is below.

The above code shows the process of building stack frame, it also includes some architecture-related preparations, see builtins-x64.cc for details.

The above code is the entry to execute callee, the Add42’s bytecodes will be executed.

Okay, that wraps it up for this share. I’ll see you guys next time, take care!

Please reach out to me if you have any issues.

WeChat: qq9123013 Email: v8blink@outlook.com

Also published here.


Written by huidou | a big fan of chrome V8
Published by HackerNoon on 2022/09/01