Skip to content

Instantly share code, notes, and snippets.

@hi-ogawa
Last active June 19, 2024 06:48
Show Gist options
  • Save hi-ogawa/637e1d95da20a522b7bae4c4401090db to your computer and use it in GitHub Desktop.
Save hi-ogawa/637e1d95da20a522b7bae4c4401090db to your computer and use it in GitHub Desktop.
Reading V8

reading v8

todo / summary

  • editor setup
    • compilation database
    • vscode debugger
      • debug cctest
      • debug mjsunit
  • testing
  • build system
    • torque
    • builtins snapshot
  • JS feature
  • implementation
    • bytecode interpreter (ignition)
    • tier up from bytecode
    • OSR (on stack replacement)
    • compiler (turbofan)
    • deoptimization
    • CodeStubAssembler
      • debugging generated code?
      • x64 backend
    • torque
    • four tiers (Sparkplug, Maglev)
  • gc
  • debugger
  • wasm
  • embedding
    • chromium/blink integration
    • nodejs
    • deno

references

hacking

# clone
fetch v8
cd v8

# pull changes
git pull
gclient sync

# gn gen + ninja
./tools/dev/gm.py x64.debug

# (for torque-language-server)
./tools/dev/gm.py x64.release
ninja -C out/x64.release torque-language-server

# generate `out/x64.debug/compile_commands.json` for vscode
ninja -C out/x64.debug -t compdb cxx cc > out/x64.debug/compile_commands.json

# build and run hello-world.cc
ninja -C out/x64.debug v8_hello_world
./out/x64.debug/v8_hello_world
# Hello, World!
# 3 + 4 = 7

# testing
ninja -C out/x64.debug cctest unittests # cf. `BUILD_TARGETS_TEST` in gm.py
tools/run-tests.py --outdir=out/x64.debug -p verbose mjsunit/interrupt-budget-override
tools/run-tests.py --outdir=out/x64.debug -p verbose 'cctest/test-elements-kind/*'

# run unittest directly
./out/x64.debug/unittests --gtest_filter=InterpreterTest.InterpreterGenerators

# run cctest directly (is it disappearing? https://bugs.chromium.org/p/v8/issues/detail?id=12781)
out/x64.debug/cctest --list
out/x64.debug/cctest test-elements-kind/JSObjectAddingProperties

# run mjsunit directly
out/x64.debug/d8 --test test/mjsunit/mjsunit.js test/mjsunit/interrupt-budget-override.js --turbofan --interrupt-budget=100 --interrupt-budget-for-feedback-allocation=10 --allow-natives-syntax
  • .vscode/c_cpp_properties.json
{
  "version": 4,
  "configurations": [
    {
      "name": "Linux",
      "compilerPath": "${workspaceFolder}/third_party/llvm-build/Release+Asserts/bin/clang++",
      "compileCommands": "${workspaceFolder}/out/x64.debug/compile_commands.json"
    }
  ]
}
  • .vscode/launch.json
    • tweak args of cctest
    • it seems there are some problems in recent change in gdbinit, so rollback a few commits: (it seems reverted)
      • git co b04d9eea02a97f745db23e218a59aa04b5028e58 -- tools/gdbinit
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "unittest",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/out/x64.debug/unittests",
      // "args": ["--gtest_filter=InterpreterTest.InterpreterGenerators"],
      "args": ["--gtest_filter=BytecodeGeneratorTest.AsyncModules"],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        { "text": "-enable-pretty-printing" },
        { "text": "-gdb-set disassembly-flavor intel" },
        {
          "text": "-interpreter-exec console \"source -v ${workspaceFolder}/tools/gdbinit\""
        }
      ]
    },
    {
      "name": "mjsunit",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/out/x64.debug/d8",
      "args": [
        "--test",
        "test/mjsunit/mjsunit.js",
        "test/mjsunit/interrupt-budget-override.js",
        "--turbofan",
        "--interrupt-budget=100",
        "--interrupt-budget-for-feedback-allocation=10",
        "--allow-natives-syntax"
      ],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        { "text": "-enable-pretty-printing" },
        { "text": "-gdb-set disassembly-flavor intel" },
        {
          "text": "-interpreter-exec console \"source -v ${workspaceFolder}/tools/gdbinit\""
        }
      ]
    },
    {
      "name": "d8",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/out/x64.debug/d8",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${fileDirname}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        { "text": "-enable-pretty-printing" },
        { "text": "-gdb-set disassembly-flavor intel" },
        {
          "text": "-interpreter-exec console \"source -v ${workspaceFolder}/tools/gdbinit\""
        }
      ]
    },
    {
      "name": "hello-world.cc",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/out/x64.debug/v8_hello_world",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${fileDirname}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "setupCommands": [
        { "text": "-enable-pretty-printing" },
        { "text": "-gdb-set disassembly-flavor intel" },
        {
          "text": "-interpreter-exec console \"source -v ${workspaceFolder}/tools/gdbinit\""
        }
      ]
    }
  ]
}
{
  // git remote add origin-github git@github.com:v8/v8.git
  "openInGitHub.remote.name": "origin-github",
  "openInGitHub.useLocalLine": true
}

macros

  • constant list
    • BYTECODE_LIST e.g. LdaNamedProperty (interpreter/bytecodes.h)
    • FOR_EACH_INTRINSIC e.g. LoadIC_Miss (runtime/runtime.h)
    • BUILTIN_LIST (builtins/builtins-definitions.h)
      • BUILTIN_LIST_BYTECODE_HANDLERS (generate-bytecodes-builtins-list.cc)
    • AST_NODE_LIST e.g. VariableDeclaration (ast/ast.h)
    • OPEN_HANDLE_LIST
    • ROOT_LIST
    • ALL_OP_LIST (src/compiler/opcodes.h TurboFan IR)
  • others
    • DEF_GETTER

basic data structure

  • objects/*.h
  • objects/*.tq (some fields are defined/generated by torque)
Smi < Object < TaggedImpl

HeapObject < Object < TaggedImpl
  Map

JSReceiver < HeapObject
  PropertyArray
  NameDictionary

JSObject < JSReceiver
  elements

Map < HeapObject
  NativeContext < Context
  DescriptorArray instance_descriptors
    descriptors[] DescriptorEntry
      (fixed length on instantiation?)
      (not all values are compatible to save in the same way? (cf. Representation, FieldType))
  prototype
  constructor_or_back_pointer
  raw_transitions

Isolate
  Heap
  IsolateData
    ThreadLocalTop
      Context (TODO: how is this relate to `HandleScopeImplementer.entered_contexts_`?)

ignition

  • bytecodes.h

  • bytecode-generator.cc

  • interpreter-generator.cc

  • test-bytecode-generator.cc bytecode-generator-unittest.cc in https://chromium-review.googlesource.com/c/v8/v8/+/3609752

    • PropertyLoads, PropertyStores, FunctionLiterals
  • test-interpreter.cc

    • InterpreterLoadNamedProperty, InterpreterSetNamedProperty
  • virtual registers (cf. bytecode-register.cc)

  • TODO

    • map construction and modification
      • CreateObjectLiteral
      • SetNamedProperty
    • smi arithmetic (cf. out/x64.debug/cctest test-bytecode-generator/BasicLoops)
    • prototype chain
    • IC
      • property access (LoadIC and StoreIC)
      • call
    • tiering up to sparkplug/turbofan
    • accessing outer-scope variables ("the hole"?)

bytecode generator test entrypoint

  • e.g. out/x64.debug/cctest test-bytecode-generator/PropertyLoads
TEST(PropertyLoads) => BuildActual => BytecodeExpectationsPrinter.PrintExpectation =>
  CompileScript => v8::Script::Compile => v8::ScriptCompiler::Compile =>
    CompileUnboundInternal => i::Compiler::GetSharedFunctionInfoForScript => GetSharedFunctionInfoForScriptImpl =>
      CompileScriptOnMainThread => CompileToplevel =>
        (Script -> FunctionLiteral) parsing::ParseProgram => Parser::ParseProgram =>
          DoParseProgram =>
            ParseStatementList (as body of FunctionLiteral) => (usual recursive descent)
            AstNodeFactory::NewScriptOrEvalFunctionLiteral
          MaybeProcessSourceRanges
        (FunctionLiteral -> SharedFunctionInfo) CreateTopLevelSharedFunctionInfo => NewSharedFunctionInfoForLiteral =>
          NewSharedFunctionInfo
          SharedFunctionInfo::InitFromFunctionLiteral
          SharedFunctionInfo::SetScript
        IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs =>
          ExecuteSingleUnoptimizedCompilationJob =>
            UnoptimizedCompilationJob::ExecuteJob, ExecuteJobImpl =>
              MaybePrintAst (via FLAG_print_ast)
              BytecodeGenerator::GenerateBytecode => ...
                InitializeAstVisitor
                GenerateBytecodeBody =>
                  VisitStatements (as AstVisitor) => ...
        FinalizeUnoptimizedScriptCompilation =>
          UnoptimizedCompilationJob::FinalizeJob, FinalizeJobImpl, DoFinalizeJobImpl =>
            BytecodeGenerator::FinalizeBytecode => BytecodeArrayBuilder::ToBytecodeArray
            (dump BytecodeArray::Disassemble if FLAG_print_bytecode)
          InstallUnoptimizedCode => SharedFunctionInfo::set_bytecode_array
    (SharedFunctionInfo => JSFunction) BindToCurrentContext =>
      i::Factory::JSFunctionBuilder::Build =>
        PrepareMap (based on SharedFunctionInfo::function_map_index)
        PrepareFeedbackCell
        SharedFunctionInfo::GetCode (e.g. Builtin::kInterpreterEntryTrampoline if function_data is BytecodeArray)
        BuildRaw =>
          Factory::New(Map, ...) =>
            Heap::AllocateRawWith
            HeapObject::set_map_after_allocation
          JSReceiver::initialize_properties
          JSObject::initialize_elements
          JSFunction::set_code
          JSFunction::set_prototype_or_initial_map
        JSFunction::EnsureFeedbackVector =>
          JSFunction::InitializeFeedbackCell
  Run(v8::Script) => Script::Run =>
    i::Execution::CallScript(Isolate, JSFunction, JSGlobalProxy, ...) =>
      InvokeParams::SetUpForCall
      Invoke =>
        JSEntry (e.g. BUILTIN_CODE(JSEntry) for non-microtask and non-constructor)
        CodeDataContainer::code_entry_point => ReadExternalPointerField ...
        GeneratedCode::FromAddress
        GeneratedCode::Call (calling function pointer)
  GetBytecodeArrayForGlobal (grab BytecodeArray e.g. via `context->Global()->Get` and `SharedFunctionInfo::GetBytecodeArray`)

#
# data structure
#

ParseInfo
  FunctionLiteral < Expression

BytecodeGenerator < AstVisitor
  BytecodeArrayBuilder
  FeedbackSlotCache
  UnoptimizedCompilationInfo
    FeedbackVectorSpec
    FunctionLiteral

JSFunction < JSFunctionOrBoundFunction < JSObject
  prototype_or_initial_map
  FeedbackCell (e.g. FeedbackVector)
  CodeT
  Context
  SharedFunctionInfo
    function_literal_id
    function_data (e.g. BytecodeArray (for ignition), CodeT (for sparkplug))
    feedback_metadata
    function_map_index (TODO: where does it come from?)

NativeContext < Context
  JSGlobalProxy

closure construction and invocation

  • out/x64.debug/cctest test-bytecode-generator/FunctionLiterals
#
# closure construction
#

BytecodeGenerator::VisitFunctionLiteral =>
  CreateClosureFlags::Encode
  GetCachedCreateClosureSlot
  BytecodeArrayBuilder::CreateClosure


IGNITION_HANDLER(CreateClosure, ...) =>
  CallBuiltin(Builtin::kFastNewClosure, ...)


TF_BUILTIN(FastNewClosure) =>
  ...
  StoreObjectFieldNoWriteBarrier(..., JSFunction::kCodeOffset, BUILTIN_CODE(CompileLazy))


TF_BUILTIN(CompileLazy) =>
  LazyBuiltinsAssembler::CompileLazy =>
    GenerateTailCallToReturnedCode(Runtime::kCompileLazy)


RUNTIME_FUNCTION(Runtime_CompileLazy) =>
  Compiler::Compile (looks similar to `CompileToplevel` above) =>
    IterativelyExecuteAndFinalizeUnoptimizedCompilationJobs
    FinalizeUnoptimizedCompilation
    ...


#
# closure invocation
#

BytecodeGenerator::VisitCall =>
  GetCallType (e.g. GLOBAL_CALL, NAMED_PROPERTY_CALL, super, optional, private, etc...)
  BytecodeArrayBuilder::StoreAccumulatorInRegister
  VisitArguments => VisitAndPushIntoRegisterList
  BytecodeArrayBuilder::SetExpressionPosition
  FeedbackVectorSpec::AddCallICSlot
  BytecodeArrayBuilder::CallUndefinedReceiver (if no receiver)


IGNITION_HANDLER(CallUndefinedReceiver0)
  JSCallN(0, ConvertReceiverMode::kNullOrUndefined) =>
    LoadFeedbackVector
    CollectCallFeedback (TODO: ic-callbacle.tq)
    CallJSAndDispatch =>
      CodeFactory::InterpreterPushArgsThenCall (e.g. Builtin::kInterpreterPushUndefinedAndArgsThenCall)
      TailCallStubThenBytecodeDispatch (magical linkage matching?)

reading property

#
# bytecode generation
#

BytecodeGenerator::VisitProperty =>
  VisitForRegisterValue (for Property::obj)
  VisitPropertyLoad =>
    Property::GetAssignType (e.g. check Expression::IsPropertyName to choose NAMED_PROPERTY or KEYED_PROPERTY)
    BuildLoadNamedProperty =>
      GetCachedLoadICSlot =>
        FeedbackVectorSpec::AddLoadICSlot => AddSlot(FeedbackSlotKind::kLoadProperty)
        FeedbackSlotCache::Put
      BytecodeArrayBuilder::LoadNamedProperty => OutputGetNamedProperty

#
# execution
#

IGNITION_HANDLER(GetNamedProperty, ...)  # changed from `LdaNamedProperty` (https://chromium-review.googlesource.com/c/v8/v8/+/3481475)
  CodeStubAssembler::LoadFeedbackVector (load JSFunction::feedback_cell if exists)
  AccessorAssembler::LoadIC_BytecodeHandler =>
    LoadReceiverMap(receiver_and_lookup_start_object)
    (fast path for common case)
      TryMonomorphicCase =>
        IsWeakReferenceTo (check if current receiver's map is same as the one in feedback vector cf. `IC::SetCache` below)
      (if monomorphic)
        HandleLoadICHandlerCase =>
          (if smi_handler (cf. `LoadIC::ComputeHandler` below) (TODO: what's the other case `proto_handler` ?))
            decode LoadHandler::Kind from smi_handler
            HandleLoadICSmiHandlerLoadNamedCase =>
              HandleLoadField (CSA equivalent of JSObject::FastPropertyAt?)
      (if polymorphic)
        HandlePolymorphicCase => ??
    (stub_call)
      Builtin::kLoadIC_Noninlined => ??
    (no_feedback)
      Builtin::kLoadIC_NoFeedback => ??
    (miss)
      Runtime::kLoadIC_Miss


RUNTIME_FUNCTION(Runtime_LoadIC_Miss) =>
  LoadIC::UpdateState(lookup_start_object, name) =>
    IC::update_lookup_start_object_map
  LoadIC::Load =>
    JSObject::MakePrototypesFast (what?)
    IC::update_lookup_start_object_map (again)
    LookupIterator => Start => ??
    LookupForRead
    (if property is found)
      UpdateCaches(LookupIterator) =>
        ComputeHandler =>
          (if LookupIterator::DATA)
            (if JSReceiver::HasFastProperties (i.e. not Map::is_dictionary_map))
              LookupIterator::GetFieldIndex => FieldIndex::ForDescriptor(Map, ...)
              LoadHandler::LoadField (found smi_handler)
        IC::SetCache =>
          (if UNINITIALIZED)
            UpdateMonomorphicIC => ConfigureVectorState => FeedbackNexus::ConfigureMonomorphic
              SetFeedback (write the entry as (address of receiver map, handler object))
      Object::GetProperty(LookupIterator, ...) =>
        (if LookupIterator::DATA) LookupIterator::GetDataValue =>
          FetchValue => JSObject::FastPropertyAt => RawFastPropertyAt (handle property-array or in-object)
    (otherwise)
      ReferenceError


#
# data structure
#

Property < Expression
  obj Expression
  key Expression

IC
  Map lookup_start_object_map_
  InlineCacheState (e.g. UNINITIALIZED, MONOMORPHIC, POLYMORPHIC, ..)
  FeedbackNexus
    FeedbackVector
    FeedbackSlot

LoadIC < IC

LookupIterator
  Object receiver_, lookup_start_object_
  Name name_ (property name)
  JSReceiver holder_ (usually same as lookup_start_object_)
  State (e.g. DATA, JSPROXY, ..)
  PropertyDetails (e.g. PropertyLocation)
  Map transition_ (TODO: when is this computed?)

writing property

#
# bytecode generation
#

BytecodeGenerator::VisitAssignment =>
  PrepareAssignmentLhs (evaluting LHS before RHS) => VisitForRegisterValue
  VisitForAccumulatorValue
  BuildAssignment =>
    (if x.y = z aka NAMED_PROPERTY)
      BuildSetNamedProperty =>
        GetCachedStoreICSlot
        BytecodeArrayBuilder::SetNamedProperty

#
# execution
#

IGNITION_HANDLER(SetNamedProperty, ...)
  InterpreterSetNamedPropertyAssembler::SetNamedProperty =>
    Builtin::kStoreIC (TODO: where does this defined?)


Builtins::Generate_StoreIC => AccessorAssembler::GenerateStoreIC =>
  AccessorAssembler::StoreIC =>
    LoadReceiverMap
    TryMonomorphicCase
    (if monomorphic)
      HandleStoreICHandlerCase =>
        (if handler is smi e.g. StoreHandler::StoreField)
          probably something like `JSObject::WriteToField` written in CSA
        (if handler is "store transition" (cf. StoreHandler::StoreTransition below))
          HandleStoreICTransitionMapHandlerCase
    ..
    (miss)
      Runtime::kStoreIC_Miss


RUNTIME_FUNCTION(Runtime_StoreIC_Miss)
  StoreIC::UpdateState
  StoreIC::Store =>
    LookupIterator => LookupIterator::Start =>
      LookupInHolder(Map, JSReceiver) => LookupInRegularHolder =>
        (if not is_dictionary_map i.e. "fast property")
          DescriptorArray::SearchWithCache
      (if not found)
        NextInternal (loop `LookupInHolder` for prototype chain (cf. Map::prototype))
    UpdateCaches =>
      LookupForWrite =>
        (if not found)
          LookupIterator::PrepareTransitionToDataProperty =>
            update state from NOT_FOUND to TRANSITION
            (if is_dictionary_map)
              is it possible to always resuse old dictionary map?
            (otherwise (i.e. "fast property"))
              Map::TransitionToDataProperty (construct map based on old map and new property name) =>
                TransitionsAccessor::SearchTransition => ??
                (if found)
                  return UpdateDescriptorForValue (mostly reuse but modify based on FieldType)
                (if not Map::TooManyFastProperties (cf. kMaxNumberOfDescriptors))
                  Map::CopyWithField(..., INSERT_TRANSITION) =>
                    Map::CopyAddDescriptor =>
                      DescriptorArray::Append
                      CopyReplaceDescriptors =>
                        CopyDropDescriptors
                        ConnectTransition (setup child's "backpointer" and parent's "raw transitions")
                (otherwise)
                  Map::Normalize (promote to "dictionary map")
      ComputeHandler =>
        (if LookupIterator::TRANSITION)
          StoreHandler::StoreTransition =>
            (if dictionary_map) ?
            (otherwise) transition_map itself is handler?
        (if LookupIterator::DATA)
          e.g. StoreHandler::StoreField if PropertyLocation::kField
      SetCache
    Object::SetProperty(LookupIterator, ...) =>
      (if LookupIterator::IsFound)
        SetPropertyInternal =>
          (if LookupIterator::DATA)
            SetDataProperty =>
              LookupIterator::WriteDataValue =>
                (if JSReceiver::HasFastProperties)
                  (if PropertyLocation::kField (other case `kDescriptor` is for constant saved in "map"?))
                    JSObject::WriteToField => FastPropertyAtPut
      (otherwise)
        AddDataProperty(LookupIterator, ...) =>
          TransitionAndWriteDataProperty =>
            LookupIterator::ApplyTransitionToDataProperty =>
              JSObject::MigrateToMap =>
                e.g. MigrateFastToFast (if simple, HeapObject::set_map, otherwise shuffling with actual storage)
              update LookupIterator state `PropertyDetails`, `State` (e.g. TRANSITION to DATA for "simple transition")
            LookupIterator::WriteDataValue


#
# data structure
#

Assignment < Expression < AstNode
  target_
  value_
  bit_field_ (assignment operator is kept in AstNode::bit_field_)

object literal construction

#
# parsing
#

ParserBase<Parser>::ParseObjectLiteral =>
  while not "}"
    ParseObjectPropertyDefinition => ...
    # check if e.g. `is_computed_name`, `isBoilerplateProperty`, etc...
  AstNodeFactory::NewObjectLiteral
  Parser::InitializeObjectLiteral

#
# bytecode generation
#

BytecodeGenerator::VisitObjectLiteral =>
  ObjectLiteralBoilerplateBuilder::InitDepthAndFlags (loop properties and detect if the object is "simple" value etc...)
  (if ObjectLiteralBoilerplateBuilder::IsEmptyObjectLiteral)
    BytecodeArrayBuilder::CreateEmptyObjectLiteral
  (otherwise)
    push ObjectLiteralBoilerplateBuilder to object_literals_
    BuildCreateObjectLiteral =>
      FeedbackVectorSpec::AddLiteralSlot
      BytecodeArrayBuilder::CreateObjectLiteral(<ObjectBoilerplateDescription in constant pool entry>, <feedback vector index>, <flags>)
    # loop for "computed value" (i.e. not `IsCompileTimeValue`)
      handle DefineNamedOwnProperty or DefineKeyedOwnProperty bytecode
    # loop for "computed property name"
      handle via `DefineKeyedOwnPropertyInLiteral` bytecode or runtime

BytecodeGenerator::FinalizeBytecode =>
  BytecodeGenerator::AllocateDeferredConstants =>
    # loop ObjectLiteralBoilerplateBuilder
      ObjectLiteralBoilerplateBuilder::GetOrBuildBoilerplateDescription => BuildBoilerplateDescription =>
        NewObjectBoilerplateDescription
        # loop properties
          ObjectBoilerplateDescription::set_key_value

#
# execution
#

IGNITION_HANDLER(CreateObjectLiteral, ...) =>
  (fast path with feedback)
    ConstructorBuiltinsAssembler::CreateShallowObjectLiteral (shallow copy implemented in CSA)
  (otherwise)
    CallRuntime(Runtime::kCreateObjectLiteral, ...)


RUNTIME_FUNCTION(Runtime_CreateObjectLiteral) => CreateLiteral<ObjectLiteralHelper> =>
  (if feedback slot already has `AllocationSite`)
    use `JSObject` from `AllocationSite::boilerplate`
  (otherwise)
    ObjectLiteralHelper::Create => CreateObjectLiteral =>
      (just constructing JSObject from ObjectBoilerplateDescription with usual API)
  DeepCopy(boilerplate, ...) => ...


IGNITION_HANDLER(DefineNamedOwnProperty, ...) => SetNamedProperty with Builtin::kDefineNamedOwnIC


#
# data structure
#

ObjectLiteral < AggregateLiteral < MaterializedLiteral < Expression
  Variable home_object_ (TODO: `super` for object literal?)
  ZoneList<ObjectLiteralProperty*>
    Kind (e.g. CONSTANT, COMPUTED, SPREAD, etc..)
  ObjectLiteralBoilerplateBuilder
    ObjectBoilerplateDescription
    ZoneList<Property*>

BytecodeGenerator
  ObjectLiteralBoilerplateBuilder object_literals_ (pre-built in constant pool?)

AllocationSite < Struct < HeapObject
  boilerplate JSObject

new operator

#
# bytecode generation
#

BytecodeGenerator::VisitCallNew =>
  # for simple arguments without spreads
  BytecodeArrayBuilder::LoadAccumulatorWithRegister(constructor)
    (set constructor itself as "new target" (relevant only when `CallSuper`?))
  BytecodeArrayBuilder::Construct

#
# execution
#

IGNITION_HANDLER(Construct, InterpreterAssembler) =>
  InterpreterAssembler::Construct =>
    CollectConstructFeedback (optimization for array construction based on feedback?)
    # for general case (InterpreterPushArgsMode::kOther)
    CodeFactory::InterpreterPushArgsThenConstruct =>
      Builtin::kInterpreterPushArgsThenConstruct
    CallStub

# asm builtin (e.g. builtins-x64.cc)
Builtins::Generate_InterpreterPushArgsThenConstructImpl =>
  (if InterpreterPushArgsMode::kOther)
    Jump(Construct) =>
      # check [[Construct]] internal method via `IsConstructorBit` on `Map`
      # dispatch based on "instance type" (how come this end up in `r8`?)
      Jump(ConstructFunction) =>
        # special path for builtin
        Jump(JSBuiltinsConstructStub) => InvokeFunction => ...
        Jump(JSConstructStubGeneric) => ??

#
# data structure
#

Map
  is_constructor # indicating [[Construct]] internal method

smi arithmetic, heap number, ic

  • out/x64.debug/cctest test-bytecode-generator/BasicLoops
  • out/x64.debug/cctest test-interpreter/InterpreterBinaryOpsSmi
  • out/x64.debug/cctest test-interpreter/InterpreterBinaryOpsHeapNumber
  • V8_31BIT_SMIS_ON_64BIT_ARCH
#
# execution
#
IGNITION_HANDLER(Add, ...) =>
  InterpreterBinaryOpAssembler::BinaryOpSmiWithFeedback =>
    BinaryOpAssembler::Generate_AddWithFeedback =>
      (if both lhs and rhs are Smi)
        CodeStubAssembler::TrySmiAdd =>
          # converting v8 smi to actual machin integers
          BitcastTaggedToWordForTagAndSmiBits
          TruncateIntPtrToInt32
          Int32AddWithOverflow => AddNode(machine()->Int32AddWithOverflow(), ...) (what?)
          # converting back to smi
          ChangeInt32ToIntPtr
          BitcastWordToTaggedSigned
        (if overflow)
          SmiToFloat64 and go "do_fadd"
      (do_fadd)
        Float64Add
        AllocateHeapNumberWithValue

tiering up from bytecode

  • execution/tiering-manager.cc
    • budget system
      • JSFunction::SetInterruptBudget, FeedbackCell::SetInitialInterruptBudget
      • v8 options e.g. --interrupt-budget
#
# "--always-sparkplug" flag
#

CompileTopLevel (see above "bytecode generator test entrypoint") =>
  parsing::ParseProgram => ...
  ...
  FinalizeUnoptimizedScriptCompilation
  (if FLAG_always_sparkplug)
    CompileAllWithBaseline =>
      CanCompileWithBaseline (check e.g. debugger mode enabled not supported by baseline)
      Compiler::CompileSharedWithBaseline =>
        GenerateBaselineCode (SharedFunctionInfo -> Code) => ??
        SharedFunctionInfo::set_baseline_code

#
# setting TieringState
#

(ignition)
IGNITION_HANDLER(Return, ...) => InterpreterAssembler::UpdateInterruptBudgetOnReturn =>
  InterpreterAssembler::UpdateInterruptBudget =>
    (if "budget" reaches zero (keep subtracting executed bytecode length from initial budget))
      call Runtime::kBytecodeBudgetInterrupt

IGNITION_HANDLER(Jump, ...) => InterpreterAssembler::Jump =>
  InterpreterAssembler::UpdateInterruptBudget => ...

(sparkplug)
BaselineAssembler::EmitReturn (e.g. baseline-assembler-x64-inl.h) =>
  call Runtime::kBytecodeBudgetInterrupt
  ...

RUNTIME_FUNCTION(Runtime_BytecodeBudgetInterrupt) => TieringManager::OnInterruptTick =>
  (if FLAG_baseline_batch_compilation (default true))
    BaselineBatchCompiler::EnqueueFunction => ...
  (otherwise)
    Compiler::CompileBaseline =>
      CompileSharedWithBaseline => ...
      JSFunction::set_code
  FeedbackVector::SaturatingIncrementProfilerTicks
  JSFunction::GetActiveTier (e.g. CodeKind::INTERPRETED_FUNCTION, BASELINE, MAGLEV, TURBOFAN)
  MaybeOptimizeFrame =>
    ShouldOptimize =>
      (if INTERPRETED_FUNCTION or BASELINE)
        OptimizationDecision::Maglev
      (decide based on "FeedbackVector::profiler_ticks" and bytecode length)
        OptimizationDecision::TurbofanHotAndStable or TurbofanSmallFunction
    Optimize => JSFunction::MarkForOptimization =>
      TieringStateFor(CodeKind, ConcurrencyMode) (return e.g. TieringState::kRequestTurbofan_Synchronous)

(is this only for testing/debugging?)
RUNTIME_FUNCTION(Runtime_OptimizeFunctionOnNextCall) =>
  OptimizeFunctionOnNextCall => JSFunction::MarkForOptimization => ...


#
# tiering up via `CompileLazy`
#

TF_BUILTIN(CompileLazy) (see above "closure construction") =>
  LazyBuiltinsAssembler::CompileLazy =>
    MaybeTailCallOptimizedCodeSlot =>
      # extracting TieringState from FeedbackVector
      (if TieringState::kRequestTurbofan_Synchronous)
        tailcall to Runtime::kCompileTurbofan_Synchronous
      ...
    (tail call to shared function code if any (e.g. baseline or bytecode trampoline))
    (otherwise go compiling to bytecode)
      GenerateTailCallToReturnedCode(Runtime::kCompileLazy)

#
# tiering up via interpreter trampoline builtin (architecture dependent e.g. builtins-x64.cc)
#

Builtins::Generate_InterpreterEntryTrampoline =>
  MaybeOptimizeCodeOrTailCallOptimizedCodeSlot =>
    MaybeOptimizeCode =>
      # similar to `CompileLazy`
      # check tiering_state in feedback verctor and tail call to e.g. Runtime::kCompileTurbofan_Synchronous

#
# optimize (maglev/turbofan)
#

RUNTIME_FUNCTION(Runtime_CompileTurbofan_Synchronous) =>
  CompileOptimized(... JSFunction, CodeKind, ...) => Compiler::CompileOptimized => GetOrCompileOptimized =>
    (if CodeKind::TURBOFAN)
      CompileTurbofan => ... (see below "turbofan")
    (if CodeKind::MAGLEV)
      CompileMaglev => ??

#
# data structure
#

Isolate
  TieringManager

JSFunction
  feedback_cell_ (as FeedbackVector)
    TieringState
    profiler_ticks

FeedbackCell
  interrupt_budget

turbofan

# e.g. ./out/x64.debug/cctest test-api/PromiseRejectCallbackConstructError
CompileTurbofan =>
  compiler::Pipeline::NewCompilationJob (instantiate PipelineCompilationJob)
  CompileTurbofan_NotConcurrent =>
    PrepareJobWithHandleScope => OptimizedCompilationJob::PrepareJob
    OptimizedCompilationJob::ExecuteJob => PipelineCompilationJob::ExecuteJobImpl =>
      PipelineImpl::CreateGraph =>
        GraphBuilderPhase::Run => .. => BytecodeGraphBuilder::CreateGraph =>
          VisitBytecodes => ...
        InliningPhase::Run => ??
      PipelineImpl::OptimizeGraph => ...
      PipelineImpl::AssembleCode =>
        PipelineData::InitializeCodeGenerator => new CodeGenerator
        AssembleCodePhase::Run => CodeGenerator::AssembleCode => ??
    OptimizedCompilationJob::FinalizeJob => PipelineCompilationJob::FinalizeJobImpl =>
      PipelineImpl::FinalizeCode => ??

#
# data structure
#

PipelineCompilationJob < TurbofanCompilationJob < OptimizedCompilationJob < CompilationJob
  PipelineImpl
  PipelineData
    Graph
    Typer
  OptimizedCompilationInfo
    BytecodeArray
    SharedFunctionInfo
    JSFunction
    Code
    osr_offset_

BytecodeGraphBuilder
  FeedbackVectorRef (from JSFunction?)
  BytecodeArrayRef

CodeGenerator
  TurboAssembler

overview

  • bytecode -> graph -> optimize -> schedule -> assemble
#
# bytecode -> graph
#

BytecodeGraphBuilder::VisitAdd =>
  CreateFeedbackSource
  JSOperatorBuilder::Add
  BuildBinaryOp =>
    PrepareEagerCheckpoint => ??
    TryBuildSimplifiedBinaryOp => ??


#
# optimize
#

PipelineImpl::OptimizeGraph =>

  TyperPhase::Run => Typer::Run =>
    GraphReducer::AddReducer(Typer::Visitor)
    GraphReducer::ReduceNode(root) => .. => Reduce => Typer::Visitor::Reduce =>
      TypeNode => UpdateType => NodeProperties::SetType
    GraphReducer::ReduceGraph => ReduceNode(end)

  TypedLoweringPhase::Run =>
    AddReducer (e.g. JSTypedLowering, TypedOptimization, etc...)

  ComputeScheduledGraph::Run => Scheduler::ComputeSchedule => ??
  SelectInstructions => ??

#
# example reductions
#

TypeNode =>
  # e.g. if IrOpcode::kJsAdd
  TypeJsAdd (generated via `JS_SIMPLE_BINOP_LIST` macro) => TypeBinaryOp => JSAddTyper =>
    # if lhs or rhs is string, then Type::String
    # otherwise
    BinaryNumberOpTyper(.., NumberAdd) =>
      # if both lhs and rhs are Number
        NumberAdd => OperationTyper.NumberAdd (OperationTyper is for "simplified" ops) =>
          NaN, -0 (minus zero) check, etc...
          integer range type analysis


JSTypedLowering::Reduce =>
  # if IrOpcode::kJSAdd
  JSTypedLowering::ReduceJSAdd =>
    # if BothInputsAre(Type::Number())
      SimplifiedOperatorBuilder::NumberAdd
    JSBinopReduction::GetBinaryOperationHint => GetFeedbackForBinaryOperation =>
      JSHeapBroker::GetFeedbackForBinaryOperation
    ??

#
# data structure
#

Graph

Node
  Operator
  Type
  Mark
  Node::Inputs (actual memory layout is optimized e.g. via inline/outline)

Effect < NodeWrapper

Control < NodeWrapper

Edge

GraphReducer

Reduction

Typer

Typer::Visitor < Reducer

FeedbackSource

JSTypedLowering
  JSHeapBroker

JSGraph

Schedule

cctest

#
# FunctionTester (./out/x64.debug/cctest test-run-jsops/BinopAdd)
#

TEST(BinopAdd) =>
  FuntionTester =>
    NewFunction => CompileRun => v8_compile => v8::Script::Compile (see above)
    Compile => Optimize =>
      Pipeline::GenerateCodeForTesting => ??
      JSFunction::set_code
  FuntionTester::CheckCall => ??

#
# data structure
#

FunctionTester
  JSFunction

promise, microtask

#
# Promise (promise.tq, js-promise.tq)
#

# new Promise(executor)
PromiseConstructor (builtins/promise-constructor.tq) =>
  NewJSPromise (builtins/promise-misc.tq) =>
    InnerNewJSPromise
    PromiseInit (set PromiseState::kPending, etc...)
  CreatePromiseResolvingFunctions (builtins/promise-abstract-operations.tq) =>
    PromiseCapabilityDefaultResolveSharedFunConstant (TODO: does torque generate it for javascript builtin PromiseCapabilityDefaultResolve?)
    PromiseCapabilityDefaultRejectSharedFunConstant
  Call(... executor ...) => ...

# resolve (https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise-resolve-functions)
PromiseCapabilityDefaultResolve (promise-abstract-operations.tq) =>
  # for normal case
  ResolvePromise (promise-resolve.tq) =>
    # if not "thenable"
      FulfillPromise =>
        set PromiseState::kFulfilled
        TriggerPromiseReactions with JSPromise::reactions_or_result (aka [[PromiseFulfillReactions]]) =>
          MorphAndEnqueuePromiseReaction =>
            EnqueueMicrotask(... PromiseFulfillReactionJobTask) => ...
    # otherwise
      NewPromiseResolveThenableJobTask(... thenAction) => ...
      EnqueueMicrotask => ...

# reject
PromiseCapabilityDefaultReject => ...

# promise.then https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise.prototype.then
PromisePrototypeThen =>
  NewJSPromise (aka "resultPromise" as a return value of "then")
  PerformPromiseThenImpl =>
    # if PromiseState::kPending
      NewPromiseReaction and set it to JSPromise::reactions_or_result
    # if PromiseState::kFulfilled
      EnqueueMicrotask onFullfilled
    # if PromiseState::kRejected
      EnqueueMicrotask onRejected

# src/builtins/builtins-microtask-queue-gen.cc
TF_BUILTIN(EnqueueMicrotask, ..)  (CSA version of MicrotaskQueue::EnqueueMicrotask)


#
# Running Microtask (three patterns to invoke microtasks? cf. `MicrotasksPolicy`)
#

(see e.g. d8 below)
Shell::CompleteMessageLoop => ProcessMessages =>
  v8::platform::PumpMessageLoop
  MicrotasksScope::PerformCheckpoint => MicrotaskQueue::PerformCheckpoint => PerformCheckpointInternal =>
    RunMicrotasks => Execution::TryRunMicrotasks =>
      InvokeParams::SetUpForRunMicrotasks (set Execution::Target::kRunMicrotasks)
      InvokeWithTryCatch
        Invoke (cf. above where we saw Execution::Target::kCallable) =>
          JSEntry => builtin JSRunMicrotasksEntry
          ...

Builtins::Generate_JSRunMicrotasksEntry
Builtins::Generate_RunMicrotasksTrampoline =>
  BUILTIN_CODE(..., RunMicrotasks)

TF_BUILTIN(RunMicrotasks, ...) =>
  MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask =>
    # only 5 kinds of Microtask
    # (CallbackTask, CallableTask, PromiseFulfillReactionJobTask, PromiseRejectReactionJobTask, PromiseResolveThenableJobTask)
    # e.g. if PromiseFulfillReactionJobTask
      Builtin::kPromiseFulfillReactionJob (promise-reaction-job.tq) =>
        PromiseReactionJob(.. kPromiseReactionFulfill) =>
          result = Call(... handler) (call "then handler" as in `nextPromise = somePromise.then(handler)`)
          FuflfillPromiseReactionJob (with `nextPromise` and `result`) => ResolvePromise

#
# data structure
#

internal::MicrotaskQueue < v8::MicrotaskQueue

JSPromise
  reactions_or_result
  JSPromiseFlags
    PromiseState
    has_handler

PromiseReaction
  reject_handler
  fulfill_handler
  promise_or_capability (e.g. "resultPromise" of `then`)

PromiseFulfillReactionJobTask < PromiseReactionJobTask < Microtask

MicrotaskQueue
  MicrotasksPolicy (kExplicit, kScoped, kAuto)

generator, async/await (aka. resumable function)

  • docs
  • examples
    • test-bytecode-generator.cc
      • Generators, AsyncGenerators, AsyncModules
    • runtime-generator.cc
    • builtins-async-function-gen.cc
    • node -e 'async function g() { console.log(2); }; async function f(x) { console.log(1); await g(); console.log(4); }; f().then(() => console.log(5)); console.log(3)'
    • ./out/x64.debug/d8 --print-bytecode --print-bytecode-filter=f2 -e 'async function f1(x) { return 2 * x; } async function f2() { const x = await f1(1); return x + x; }; f2()'
  • generator
    • construction
      • Generator and GeneratorFunction (cf. Genesis::CreateIteratorMaps)
    • execution (next iterator protocol)
  • async/await
    • prototype (AsyncFunction, async_function_object_map, etc... initialized in Genesis::InitializeIteratorFunctions)
    • instantiate AsyncFunction object
    • awaited value resolve/reject
    • return result
  • yield* (see comments in BytecodeGenerator::VisitYieldStar)
#
# generator/async ast rewrite
#

ParseAndRewriteGeneratorFunctionBody =>
  # this corresponds initial suspend/resume pair right after `CreateJSGeneratorObject`
  # which will return `JSGeneratorObject` itself on generator function call
  BuildInitialYield => AstNodeFactory::NewYield
  ParseStatementList (parse later so that `yield` appears first)

ParseAsyncFunctionBody =>
  ParseStatementList (parse first and wrapped in a block for rewrite)
  RewriteAsyncFunctionBody (see comments for desugaring pseudo code) =>
    # inject `return undefined` so that async function can resolve without it
    NewSyntheticAsyncReturnStatement (append ReturnStatement::kSyntheticAsyncReturn)
    BuildRejectPromiseOnException =>
      NewHiddenCatchScope
      NewCallRuntime(.. InlineAsyncFunctionReject ..)

#
# BytecodeGenerator
#

BytecodeGenerator::GenerateBytecode =>
  # if resumable function
    BytecodeGenerator::BuildGeneratorPrologue =>
      BytecodeArrayBuilder::AllocateJumpTable
      BytecodeArrayBuilder::SwitchOnGeneratorState
  GenerateBytecodeBody =>
    BuildGeneratorObjectVariableInitialization =>
      # TODO: is inline verson only for tagging to be used by turbofan in `JSIntrinsicLowering::Reduce`?
      CallRuntime with kInlineCreateJSGeneratorObject or kInlineAsyncFunctionEnter

BytecodeGenerator::VisitYield =>
  CallRuntime with kInlineCreateIterResultObject or kInlineAsyncGeneratorYield
  BuildSuspendPoint =>
    # increment `suspend_count_` and use it as `suspend_id`
    BytecodeArrayBuilder::SuspendGenerator (with suspend id)
    BytecodeArrayBuilder::Bind(BytecodeJumpTable, suspend id)
    BytecodeArrayBuilder::ResumeGenerator
  AllocateJumpTable (for switching by ResumeMode throw/return/next)

# note that `async` function executes until first `await` without suspending
BytecodeGenerator::VisitAwait =>
  VisitForAccumulatorValue # evaluate `await`-ed expression
  BytecodeGenerator::BuildAwait =>
    CallRuntime(kInlineAsyncFunctionAwaitUncaught, ...)
    # TODO: do we have `JSAsyncFunctionObject::promise` in the accumulator at this point?
    BuildSuspendPoint => ...
    CallRuntime(Runtime::kInlineGeneratorGetResumeMode, ...)

BytecodeGenerator::VisitReturnStatement =>
  ControlScope::AsyncReturnAccumulator => PerformCommand(CMD_ASYNC_RETURN ..) =>
    ControlScopeForTopLevel::Execute => BytecodeGenerator::BuildAsyncReturn =>
      CallRuntime(Runtime::kInlineAsyncFunctionResolve ..)
      BuildReturn

#
# generator execution
#

IGNITION_HANDLER(SwitchOnGeneratorState, InterpreterAssembler) =>
  jump to JSGeneratorObject::continuation

RUNTIME_FUNCTION(Runtime_CreateJSGeneratorObject) =>
  NewJSGeneratorObject =>
    JSFunction::EnsureHasInitialMap =>
      # InstanceType is either JS_OBJECT_TYPE, JS_GENERATOR_OBJECT_TYPE, or JS_ASYNC_GENERATOR_OBJECT_TYPE
      NewMap
      Factory::NewFunctionPrototype =>
        # instantiate prototype
        NewJSObjectFromMap with either
          NativeContext::async_generator_object_prototype_map
          NativeContext::generator_object_prototype_map (initialized during Genesis::CreateIteratorMaps)
          NativeContext::object_function's initial_map
      JSFunction::SetInitialMap
    NewJSObjectFromMap
  set properties e.g. ResumeMode::kNext, kGeneratorExecuting

IGNITION_HANDLER(SuspendGenerator, InterpreterAssembler) =>
  ExportParametersAndRegisterFile
  store `suspend_id` in `JSGeneratorObject::continuation`
  Return(GetAccumulator())  # e.g. accumulator will be `yield` result if `CreateIterResultObject` opcode is right before `SuspendGenerator`

IGNITION_HANDLER(ResumeGenerator, InterpreterAssembler) =>
  ImportRegisterFile

# yield
RUNTIME_FUNCTION(Runtime_CreateIterResultObject) => NewJSIteratorResult

# next
TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) =>
  GeneratorBuiltinsAssembler::GeneratorPrototypeNext =>
    # assert receiver is JS_GENERATOR_OBJECT_TYPE
    InnerResume =>
      set JSGeneratorObject::resume_mode (here JSGeneratorObject::kNext)
      CodeFactory::ResumeGenerator => Builtin::kResumeGeneratorTrampoline => ...
      CodeStubArguments::PopAndReturn

(e.g. builtins-x64.cc)
Builtins::Generate_ResumeGeneratorTrampoline => ...

#
# async/await
#

# initial call of async function
TF_BUILTIN(AsyncFunctionEnter, AsyncFunctionBuiltinsAssembler) =>
  NewJSPromise (aka "implicit promise" which becomes a return value of async function call)
  # instantiate JSAsyncFunctionObject with async_function_object_map from native context
  # set JSAsyncFunctionObject::promise
  Return(async_function_object)


# await
TF_BUILTIN(AsyncFunctionAwaitUncaught ..) => AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait =>
  AsyncFunctionAwaitResolveSharedFunConstant (corresponds to Builtin::kAsyncFunctionAwaitResolveClosure (setup in `Heap::CreateInitialObjects`))
  Await =>
    # if awaited value is not promise (this is a non-optimized path as explained in https://v8.dev/blog/fast-async)
      NewJSPromise to wrap non-promise value (what does "parent" do? only for debugging purpose? i.e. non-spec purpose?)
      Builtin::kResolvePromise (wrapping promise will resolve non-promise value awaited)
    # initialize dedicated `await_context` and store `JSAsyncFunctionObject` in `EXTENSION_INDEX`
    Builtin::kPerformPromiseThen (similar to <awaited-promise>.then(onResolve, onReject)) # see `PromisePrototypeThen` above
  Return(outer_promise) # aka. JSAsyncFunctionObject::promise which will be put on the accumulator and then returned to the caller by `SuspendGenerator`


# resolve callback of awaited value
TF_BUILTIN(AsyncFunctionAwaitResolveClosure ..) =>
  AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure(... JSGeneratorObject::kNext) =>
    # retrieve JSAsyncFunctionObject from context
    # save JSGeneratorObject::resume_mode (here JSGeneratorObject::kNext)
    CodeFactory::ResumeGenerator (call `Builtin::kResumeGeneratorTrampoline` similar to `Generator.prototype.next`)


# reject callback of awaited value
TF_BUILTIN(AsyncFunctionAwaitRejectClosure ..) =>
  AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitResumeClosure(... JSGeneratorObject::kThrow) => ...


# return async function by finally resolving `JSAsyncFunctionObject::promise`
TF_BUILTIN(AsyncFunctionResolve, AsyncFunctionBuiltinsAssembler) =>
  Builtin::kResolvePromise (resolve JSAsyncFunctionObject::promise with "value" (TODO: where does this come from?))
  Return(promise)


# return error as rejection (via parser rewrite)
AsyncFunctionReject => ??


#
# data structure
#

BytecodeGenerator
  incoming_new_target_or_generator_
  generator_jump_table_
  suspend_count_

FunctionLiteral
  FunctionKind (e.g. kAsyncArrowFunction)

JSGeneratorObject < JSObject
  JSFunction
  Context
  resume_mode: Smi
  continuation: Smi (used as jump destination when resuming generator (cf. `SwitchOnGeneratorState`))
  parameters_and_registers

JSAsyncFunctionObject < JSGeneratorObject
  JSPromise

JSAsyncGeneratorObject < JSGeneratorObject

d8

v8::Shell::Main =>
  v8::V8::Initialize => ??
  RunMain =>
    CreateEvaluationContext =>
      CreateGlobalTemplate (setup print, load, etc...)
      Context::New => NewContext (context_snapshot_index = 0) => CreateEnvironment =>
        InvokeBootstrapper::Invoke => Bootstrapper::CreateEnvironment =>
          Genesis::Genesis =>
            (if Isolate::initialized_from_snapshot)
              Snapshot::NewContextFromSnapshot => ??
            (if snapshot)
              CreateNewGlobals => ??
              HookUpGlobalObject => Context::extension as global object
            (otherwise)
              InitializeGlobal =>
                # define JS runtime e.g. Object, Promise, etc...
                # cf. https://tc39.es/ecma262/multipage/global-object.html#sec-value-properties-of-the-global-object
                (Promise)
                  InstallFunction(..., "Promise", ..., Builtin::kPromiseConstructor) => ??
                  InstallWithIntrinsicDefaultProto(..., Context::PROMISE_FUNCTION_INDEX)
            NativeContext::set_microtask_queue
    # if source is given on CLI e.g. "-e" option
      SourceGroup::Execute => Shell::ExecuteString =>
        CompileString => ...
        Script::Run => ...
      CompleteMessageLoop => ??
  RunShell => ??

SourceGroup::Execute => ??

#
# data structure
#

JSGlobalObject < JSSpecialObject < JSCustomElementsObject < JSObject

cctest.cc

  • e.g. via out/x64.debug/cctest test-elements-kind/JSObjectAddingProperties
main =>
  cppgc::InitializeProcess
  V8::Initialize
  CcTest::Run =>
    Isolate::New
    Isolate::Enter
    internal::test_elements_kind::TestJSObjectAddingProperties =>
      CcTest::InitializeVM =>
        Context::New => NewContext => CreateEnvironment
        Context::Enter =>
          Utils::OpenHandle
          HandleScopeImplementer::EnterContext
          i::Isolate::set_context
      ...
      Factory::NewFunctionForTesting =>
        JSFunctionBuilder::Build =>
          BuildRaw =>
            Factory::New(Map, AllocationType) : HeapObject =>
              Heap::AllocateRawWith
              HeapObject::set_map_after_allocation
          Compiler::PostInstantiation
      ...
      Factory::NewJSObject => ???
      ...
    Isolate::Exit
  V8::Dispose

hello-world.cc

main =>
  platform::NewDefaultPlatform => DefaultPlatform::DefaultPlatform =>
    EnsureBackgroundTaskRunnerInitialized => DefaultWorkerThreadsTaskRunner => WorkerThread
  V8::Initialize ... internal::V8::Initialize =>
    various FLAG checks (NOTE: global options set via e.g. V8::SetFlagsFromCommandLine)
    base::OS::Initialize
    IsolateAllocator::InitializeOncePerProcess => VirtualMemoryCage::InitReservation => ???
    Isolate::InitializeOncePerProcess
    ElementsAccessor, Bootstrapper, WasmEngine, etc... with `InitializeOncePerProcess`
  Isolate::new => Isolate::Initialize => ???
  Isolate::Scope
  Context::New
  Context::Scope
  Script::Compile
  Script::Run

torque

nodejs

# we can use local ninja (not from depot tools)
./configure --ninja -C
ninja -C out/Debug
  • .vscode/c_cpp_properties.json
{
  "version": 4,
  "configurations": [
    {
      "name": "Linux",
      "compileCommands": "${workspaceFolder}/out/Debug/compile_commands.json"
    }
  ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment