[heap] Fix saving the callee-saved registers on stack

This CL reinstates the trampoline for pushing the values of
callee-saved registers on the stack, which is used for stack scanning.
It reintroduces the set of architecture-specific functions
PushAllRegistersAndIterateStack, removed in crrev.com/c/3989143.
The reason for this change is that the simpler architecture-specific
functions SaveCalleeSavedRegisters failed to correctly save the
values of the registers, in the presence of C++ compiler optimizations.

It also removes the stack context, introduced in crrev.com/c/4017512,
and uses again the trampoline for iterating through the stack.

Bug: v8:13257
Change-Id: I9e656a9b3ba6616168602300f2180b4f340593f3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4171639
Commit-Queue: Nikolaos Papaspyrou <nikolaos@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85394}
This commit is contained in:
Nikolaos Papaspyrou 2023-01-18 21:11:59 +01:00 committed by V8 LUCI CQ
parent 562b65a86e
commit 472429e623
47 changed files with 921 additions and 927 deletions

View File

@ -3201,16 +3201,16 @@ filegroup(
# Note these cannot be v8_target_is_* selects because these contain
# inline assembly that runs inside the executable. Since these are
# linked directly into mksnapshot, they must use the actual target cpu.
"@v8//bazel/config:is_inline_asm_ia32": ["src/heap/base/asm/ia32/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_x64": ["src/heap/base/asm/x64/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_arm": ["src/heap/base/asm/arm/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_arm64": ["src/heap/base/asm/arm64/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_s390x": ["src/heap/base/asm/s390/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_riscv64": ["src/heap/base/asm/riscv64/save_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_ppc64le": ["src/heap/base/asm/ppc/save_registers_asm.cc"],
"@v8//bazel/config:is_msvc_asm_ia32": ["src/heap/base/asm/ia32/save_registers_masm.asm"],
"@v8//bazel/config:is_msvc_asm_x64": ["src/heap/base/asm/x64/save_registers_masm.asm"],
"@v8//bazel/config:is_msvc_asm_arm64": ["src/heap/base/asm/arm64/save_registers_masm.S"],
"@v8//bazel/config:is_inline_asm_ia32": ["src/heap/base/asm/ia32/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_x64": ["src/heap/base/asm/x64/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_arm": ["src/heap/base/asm/arm/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_arm64": ["src/heap/base/asm/arm64/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_s390x": ["src/heap/base/asm/s390/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_riscv64": ["src/heap/base/asm/riscv64/push_registers_asm.cc"],
"@v8//bazel/config:is_inline_asm_ppc64le": ["src/heap/base/asm/ppc/push_registers_asm.cc"],
"@v8//bazel/config:is_msvc_asm_ia32": ["src/heap/base/asm/ia32/push_registers_masm.asm"],
"@v8//bazel/config:is_msvc_asm_x64": ["src/heap/base/asm/x64/push_registers_masm.asm"],
"@v8//bazel/config:is_msvc_asm_arm64": ["src/heap/base/asm/arm64/push_registers_masm.S"],
}),
)

View File

@ -5989,31 +5989,31 @@ v8_source_set("v8_heap_base") {
if (is_clang || !is_win) {
if (current_cpu == "x64") {
sources += [ "src/heap/base/asm/x64/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/x64/push_registers_asm.cc" ]
} else if (current_cpu == "x86") {
sources += [ "src/heap/base/asm/ia32/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/ia32/push_registers_asm.cc" ]
} else if (current_cpu == "arm") {
sources += [ "src/heap/base/asm/arm/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/arm/push_registers_asm.cc" ]
} else if (current_cpu == "arm64") {
sources += [ "src/heap/base/asm/arm64/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/arm64/push_registers_asm.cc" ]
} else if (current_cpu == "ppc64") {
sources += [ "src/heap/base/asm/ppc/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/ppc/push_registers_asm.cc" ]
} else if (current_cpu == "s390x") {
sources += [ "src/heap/base/asm/s390/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/s390/push_registers_asm.cc" ]
} else if (current_cpu == "mips64el") {
sources += [ "src/heap/base/asm/mips64/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/mips64/push_registers_asm.cc" ]
} else if (current_cpu == "loong64") {
sources += [ "src/heap/base/asm/loong64/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/loong64/push_registers_asm.cc" ]
} else if (current_cpu == "riscv64" || current_cpu == "riscv32") {
sources += [ "src/heap/base/asm/riscv/save_registers_asm.cc" ]
sources += [ "src/heap/base/asm/riscv/push_registers_asm.cc" ]
}
} else if (is_win) {
if (current_cpu == "x64") {
sources += [ "src/heap/base/asm/x64/save_registers_masm.asm" ]
sources += [ "src/heap/base/asm/x64/push_registers_masm.asm" ]
} else if (current_cpu == "x86") {
sources += [ "src/heap/base/asm/ia32/save_registers_masm.asm" ]
sources += [ "src/heap/base/asm/ia32/push_registers_masm.asm" ]
} else if (current_cpu == "arm64") {
sources += [ "src/heap/base/asm/arm64/save_registers_masm.S" ]
sources += [ "src/heap/base/asm/arm64/push_registers_masm.S" ]
}
}

View File

@ -3075,8 +3075,7 @@ void Isolate::RecordStackSwitchForScanning() {
.get()
.get();
current = WasmContinuationObject::cast(current).parent();
thread_local_top()->stack_.SetStackStart(
reinterpret_cast<void*>(stack->base()));
heap()->SetStackStart(reinterpret_cast<void*>(stack->base()));
// We don't need to add all inactive stacks. Only the ones in the active chain
// may contain cpp heap pointers.
while (!current.IsUndefined()) {
@ -3372,9 +3371,12 @@ void Isolate::Delete(Isolate* isolate) {
Isolate* saved_isolate = isolate->TryGetCurrent();
SetIsolateThreadLocals(isolate, nullptr);
isolate->set_thread_id(ThreadId::Current());
isolate->thread_local_top()->stack_ =
saved_isolate ? std::move(saved_isolate->thread_local_top()->stack_)
: ::heap::base::Stack(base::Stack::GetStackStart());
if (saved_isolate) {
isolate->thread_local_top()->stack_ =
std::move(saved_isolate->thread_local_top()->stack_);
} else {
isolate->heap()->SetStackStart(base::Stack::GetStackStart());
}
bool owns_shared_isolate = isolate->owns_shared_isolate_;
Isolate* maybe_shared_isolate = isolate->shared_isolate_;

View File

@ -44,10 +44,13 @@ void ThreadLocalTop::Initialize(Isolate* isolate) {
Clear();
isolate_ = isolate;
thread_id_ = ThreadId::Current();
stack_.SetStackStart(base::Stack::GetStackStart());
#if V8_ENABLE_WEBASSEMBLY
stack_.SetStackStart(base::Stack::GetStackStart(),
v8_flags.experimental_wasm_stack_switching);
thread_in_wasm_flag_address_ = reinterpret_cast<Address>(
trap_handler::GetThreadInWasmThreadLocalAddress());
#else
stack_.SetStackStart(base::Stack::GetStackStart(), false);
#endif // V8_ENABLE_WEBASSEMBLY
#ifdef USE_SIMULATOR
simulator_ = Simulator::current(isolate);

View File

@ -0,0 +1,39 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// We maintain 8-byte alignment at calls by pushing an additional
// non-callee-saved register (r3).
//
// Calling convention source:
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A32)
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
asm(".globl PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers and save return address.
// Only {r4-r11} are callee-saved registers. Push r3 in addition to align
// the stack back to 8 bytes.
" push {r3-r11, lr} \n"
// Pass 1st parameter (r0) unchanged (Stack*).
// Pass 2nd parameter (r1) unchanged (StackVisitor*).
// Save 3rd parameter (r2; IterateStackCallback).
" mov r3, r2 \n"
// Pass 3rd parameter as sp (stack pointer).
" mov r2, sp \n"
// Call the callback.
" blx r3 \n"
// Discard all the registers.
" add sp, sp, #36 \n"
// Pop lr into pc which returns and switches mode if needed.
" pop {pc} \n");

View File

@ -1,36 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
//
// We maintain 8-byte alignment at calls by pushing an additional
// non-callee-saved register (r3).
//
// Calling convention source:
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A32)
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
// 8 32-bit registers = 8 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 8,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 4, "Mismatch in word size");
asm(".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// r0: [ intptr_t* buffer ]
// Save the callee-saved registers: {r4-r11}.
" stm r0, {r4-r11} \n"
// Return.
" bx lr \n");

View File

@ -0,0 +1,62 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// We maintain 16-byte alignment.
//
// Calling convention source:
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
asm(
#if defined(__APPLE__)
".globl _PushAllRegistersAndIterateStack \n"
".private_extern _PushAllRegistersAndIterateStack \n"
".p2align 2 \n"
"_PushAllRegistersAndIterateStack: \n"
#else // !defined(__APPLE__)
".globl PushAllRegistersAndIterateStack \n"
#if !defined(_WIN64)
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
#endif // !defined(_WIN64)
".p2align 2 \n"
"PushAllRegistersAndIterateStack: \n"
#endif // !defined(__APPLE__)
// x19-x29 are callee-saved.
" stp x19, x20, [sp, #-16]! \n"
" stp x21, x22, [sp, #-16]! \n"
" stp x23, x24, [sp, #-16]! \n"
" stp x25, x26, [sp, #-16]! \n"
" stp x27, x28, [sp, #-16]! \n"
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
// Sign return address.
" paciasp \n"
#endif
" stp fp, lr, [sp, #-16]! \n"
// Maintain frame pointer.
" mov fp, sp \n"
// Pass 1st parameter (x0) unchanged (Stack*).
// Pass 2nd parameter (x1) unchanged (StackVisitor*).
// Save 3rd parameter (x2; IterateStackCallback)
" mov x7, x2 \n"
// Pass 3rd parameter as sp (stack pointer).
" mov x2, sp \n"
" blr x7 \n"
// Load return address and frame pointer.
" ldp fp, lr, [sp], #16 \n"
#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
// Authenticate return address.
" autiasp \n"
#endif
// Drop all callee-saved registers.
" add sp, sp, #80 \n"
" ret \n");

View File

@ -0,0 +1,32 @@
; Copyright 2020 the V8 project authors. All rights reserved.
; Use of this source code is governed by a BSD-style license that can be
; found in the LICENSE file.
; This file is exactly the same as push_registers_asm.cc, just formatted for
; the Microsoft Arm Assembler.
AREA |.text|, CODE, ALIGN=4, READONLY
EXPORT PushAllRegistersAndIterateStack
PushAllRegistersAndIterateStack
; x19-x29 are callee-saved
STP x19, x20, [sp, #-16]!
STP x21, x22, [sp, #-16]!
STP x23, x24, [sp, #-16]!
STP x25, x26, [sp, #-16]!
STP x27, x28, [sp, #-16]!
STP fp, lr, [sp, #-16]!
; Maintain frame pointer
MOV fp, sp
; Pass 1st parameter (x0) unchanged (Stack*).
; Pass 2nd parameter (x1) unchanged (StackVisitor*).
; Save 3rd parameter (x2; IterateStackCallback)
MOV x7, x2
; Pass 3rd parameter as sp (stack pointer)
MOV x2, sp
BLR x7
; Load return address
LDR lr, [sp, #8]
; Restore frame pointer and pop all callee-saved registers.
LDR fp, [sp], #96
RET
END

View File

@ -1,50 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
//
// We maintain 16-byte alignment.
//
// Calling convention source:
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
// 11 64-bit registers = 11 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 11,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(
#if defined(__APPLE__)
".globl _SaveCalleeSavedRegisters \n"
".private_extern _SaveCalleeSavedRegisters \n"
".p2align 2 \n"
"_SaveCalleeSavedRegisters: \n"
#else // !defined(__APPLE__)
".globl SaveCalleeSavedRegisters \n"
#if !defined(_WIN64)
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
#endif // !defined(_WIN64)
".p2align 2 \n"
"SaveCalleeSavedRegisters: \n"
#endif // !defined(__APPLE__)
// $x0: [ intptr_t* buffer ]
// Save the callee-saved registers: x19-x29.
" stp x19, x20, [x0], #16 \n"
" stp x21, x22, [x0], #16 \n"
" stp x23, x24, [x0], #16 \n"
" stp x25, x26, [x0], #16 \n"
" stp x27, x28, [x0], #16 \n"
" str x29, [x0] \n"
// Return.
" ret \n");

View File

@ -1,24 +0,0 @@
; Copyright 2020 the V8 project authors. All rights reserved.
; Use of this source code is governed by a BSD-style license that can be
; found in the LICENSE file.
; This file is exactly the same as save_registers_asm.cc, just formatted for
; the Microsoft Arm Assembler.
; Save all callee-saved registers in the specified buffer.
; extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
AREA |.text|, CODE, ALIGN=4, READONLY
EXPORT SaveCalleeSavedRegisters
SaveCalleeSavedRegisters
; x0: [ intptr_t* buffer ]
; x19-x29 are callee-saved
STP x19, x20, [x0], #16
STP x21, x22, [x0], #16
STP x23, x24, [x0], #16
STP x25, x26, [x0], #16
STP x27, x28, [x0], #16
STR x29, [x0]
; Return.
RET
END

View File

@ -0,0 +1,53 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// We maintain 16-byte alignment at calls. There is an 4-byte return address
// on the stack and we push 28 bytes which maintains 16-byte stack alignment
// at the call.
//
// The following assumes cdecl calling convention.
// Source: https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
asm(
#ifdef _WIN32
".globl _PushAllRegistersAndIterateStack \n"
"_PushAllRegistersAndIterateStack: \n"
#else // !_WIN32
".globl PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
#endif // !_WIN32
// [ IterateStackCallback ]
// [ StackVisitor* ]
// [ Stack* ]
// [ ret ]
// ebp is callee-saved. Maintain proper frame pointer for debugging.
" push %ebp \n"
" movl %esp, %ebp \n"
" push %ebx \n"
" push %esi \n"
" push %edi \n"
// Save 3rd parameter (IterateStackCallback).
" movl 28(%esp), %ecx \n"
// Pass 3rd parameter as esp (stack pointer).
" push %esp \n"
// Pass 2nd parameter (StackVisitor*).
" push 28(%esp) \n"
// Pass 1st parameter (Stack*).
" push 28(%esp) \n"
" call *%ecx \n"
// Pop the callee-saved registers.
" addl $24, %esp \n"
// Restore rbp as it was used as frame pointer.
" pop %ebp \n"
" ret \n");

View File

@ -0,0 +1,48 @@
;; Copyright 2020 the V8 project authors. All rights reserved.
;; Use of this source code is governed by a BSD-style license that can be
;; found in the LICENSE file.
;; MASM syntax
;; https://docs.microsoft.com/en-us/cpp/assembler/masm/microsoft-macro-assembler-reference?view=vs-2019
.model flat, C
public PushAllRegistersAndIterateStack
.code
PushAllRegistersAndIterateStack:
;; Push all callee-saved registers to get them on the stack for conservative
;; stack scanning.
;;
;; We maintain 16-byte alignment at calls. There is an 8-byte return address
;; on the stack and we push 72 bytes which maintains 16-byte stack alignment
;; at the call.
;;
;; The following assumes cdecl calling convention.
;; Source: https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019
;;
;; [ IterateStackCallback ]
;; [ StackVisitor* ]
;; [ Stack* ]
;; [ ret ]
push ebp
mov ebp, esp
push ebx
push esi
push edi
;; Save 3rd parameter (IterateStackCallback).
mov ecx, [ esp + 28 ]
;; Pass 3rd parameter as esp (stack pointer).
push esp
;; Pass 2nd parameter (StackVisitor*).
push [ esp + 28 ]
;; Pass 1st parameter (Stack*).
push [ esp + 28 ]
call ecx
;; Pop the callee-saved registers.
add esp, 24
;; Restore rbp as it was used as frame pointer.
pop ebp
ret
end

View File

@ -1,48 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
//
// The following assumes cdecl calling convention.
// Source: https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
// 3 32-bit registers = 3 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 3,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 4, "Mismatch in word size");
asm(
#ifdef _WIN32
".globl _SaveCalleeSavedRegisters \n"
"_SaveCalleeSavedRegisters: \n"
#else // !_WIN32
".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
#endif // !_WIN32
// 8: [ intptr_t* buffer ]
// 4: [ ret ]
// 0: [ saved %ebp ]
// %ebp is callee-saved. Maintain proper frame pointer for debugging.
" push %ebp \n"
" movl %esp, %ebp \n"
// Load the buffer's address in %ecx.
" movl 8(%ebp), %ecx \n"
// Save the callee-saved registers.
" movl %ebx, 0(%ecx) \n"
" movl %esi, 4(%ecx) \n"
" movl %edi, 8(%ecx) \n"
// Restore %ebp as it was used as frame pointer and return.
" pop %ebp \n"
" ret \n");

View File

@ -1,36 +0,0 @@
;; Copyright 2020 the V8 project authors. All rights reserved.
;; Use of this source code is governed by a BSD-style license that can be
;; found in the LICENSE file.
;; MASM syntax
;; https://docs.microsoft.com/en-us/cpp/assembler/masm/microsoft-macro-assembler-reference?view=vs-2019
.model flat, C
public SaveCalleeSavedRegisters
.code
;; Save all callee-saved registers in the specified buffer.
;; extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
;;
;; The following assumes cdecl calling convention.
;; Source: https://docs.microsoft.com/en-us/cpp/cpp/cdecl?view=vs-2019
SaveCalleeSavedRegisters:
;; 8: [ intptr_t* buffer ]
;; 4: [ ret ]
;; 0: [ saved %ebp ]
;; %ebp is callee-saved. Maintain proper frame pointer for debugging.
push ebp
mov ebp, esp
;; Load the buffer's address in %ecx.
mov ecx, [ebp + 8]
;; Save the callee-saved registers.
mov [ecx], ebx
mov [ecx + 4], esi
mov [ecx + 8], edi
;; Restore %ebp as it was used as frame pointer and return.
pop ebp
ret
end

View File

@ -0,0 +1,48 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
asm(".text \n"
".global PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers and save return address.
" addi.d $sp, $sp, -96 \n"
" st.d $ra, $sp, 88 \n"
" st.d $s8, $sp, 80 \n"
" st.d $sp, $sp, 72 \n"
" st.d $fp, $sp, 64 \n"
" st.d $s7, $sp, 56 \n"
" st.d $s6, $sp, 48 \n"
" st.d $s5, $sp, 40 \n"
" st.d $s4, $sp, 32 \n"
" st.d $s3, $sp, 24 \n"
" st.d $s2, $sp, 16 \n"
" st.d $s1, $sp, 8 \n"
" st.d $s0, $sp, 0 \n"
// Maintain frame pointer.
" addi.d $s8, $sp, 0 \n"
// Pass 1st parameter (a0) unchanged (Stack*).
// Pass 2nd parameter (a1) unchanged (StackVisitor*).
// Save 3rd parameter (a2; IterateStackCallback).
" addi.d $a3, $a2, 0 \n"
// Call the callback.
// Pass 3rd parameter as sp (stack pointer).
" addi.d $a2, $sp, 0 \n"
" jirl $ra, $a3, 0 \n"
// Load return address.
" ld.d $ra, $sp, 88 \n"
// Restore frame pointer.
" ld.d $s8, $sp, 80 \n"
// Discard all callee-saved registers.
" addi.d $sp, $sp, 96 \n"
" jirl $zero, $ra, 0 \n");

View File

@ -1,40 +0,0 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// 11 64-bit registers = 11 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 11,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(".text \n"
".global SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// $a0: [ intptr_t* buffer ]
// Save the callee-saved registers.
" st.d $s8, $a0, 0 \n"
" st.d $sp, $a0, 8 \n"
" st.d $fp, $a0, 16 \n"
" st.d $s7, $a0, 24 \n"
" st.d $s6, $a0, 32 \n"
" st.d $s5, $a0, 40 \n"
" st.d $s4, $a0, 48 \n"
" st.d $s3, $a0, 56 \n"
" st.d $s2, $a0, 64 \n"
" st.d $s1, $a0, 72 \n"
" st.d $s0, $a0, 80 \n"
// Return.
" jirl $zero, $ra, 0 \n");

View File

@ -0,0 +1,49 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
asm(".text \n"
".set noreorder \n"
".global PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers and save return address.
" daddiu $sp, $sp, -96 \n"
" sd $ra, 88($sp) \n"
" sd $s8, 80($sp) \n"
" sd $sp, 72($sp) \n"
" sd $gp, 64($sp) \n"
" sd $s7, 56($sp) \n"
" sd $s6, 48($sp) \n"
" sd $s5, 40($sp) \n"
" sd $s4, 32($sp) \n"
" sd $s3, 24($sp) \n"
" sd $s2, 16($sp) \n"
" sd $s1, 8($sp) \n"
" sd $s0, 0($sp) \n"
// Maintain frame pointer.
" move $s8, $sp \n"
// Pass 1st parameter (a0) unchanged (Stack*).
// Pass 2nd parameter (a1) unchanged (StackVisitor*).
// Save 3rd parameter (a2; IterateStackCallback).
" move $a3, $a2 \n"
// Call the callback.
" jalr $a3 \n"
// Delay slot: Pass 3rd parameter as sp (stack pointer).
" move $a2, $sp \n"
// Load return address.
" ld $ra, 88($sp) \n"
// Restore frame pointer.
" ld $s8, 80($sp) \n"
" jr $ra \n"
// Delay slot: Discard all callee-saved registers.
" daddiu $sp, $sp, 96 \n");

View File

@ -1,41 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// 9 64-bit registers = 9 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 9,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(".text \n"
".set noreorder \n"
".global SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// $a0: [ intptr_t* buffer ]
// Save the callee-saved registers.
" sd $gp, 64($a0) \n"
" sd $s7, 56($a0) \n"
" sd $s6, 48($a0) \n"
" sd $s5, 40($a0) \n"
" sd $s4, 32($a0) \n"
" sd $s3, 24($a0) \n"
" sd $s2, 16($a0) \n"
" sd $s1, 8($a0) \n"
// ... one more in the delay slot!
// Return.
" jr $ra \n"
// Delay slot:
" sd $s0, 0($a0) \n");

View File

@ -0,0 +1,97 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// PPC ABI source:
// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
// AIX Runtime process stack:
// https://www.ibm.com/support/knowledgecenter/ssw_aix_71/assembler/idalangref_runtime_process.html
asm(
#if defined(_AIX)
".csect .text[PR] \n"
".align 2 \n"
".globl .PushAllRegistersAndIterateStack, hidden \n"
".PushAllRegistersAndIterateStack: \n"
#else
".text \n"
".align 2 \n"
".globl PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
#endif
// Push all callee-saved registers.
// lr, TOC pointer, r16 to r31. 160 bytes.
// The parameter save area shall be allocated by the caller. 112 bytes.
// At anytime, SP (r1) needs to be multiple of 16 (i.e. 16-aligned).
" mflr 0 \n"
" std 0, 16(1) \n"
#if defined(_AIX)
" std 2, 40(1) \n"
#else
" std 2, 24(1) \n"
#endif
" stdu 1, -256(1) \n"
" std 14, 112(1) \n"
" std 15, 120(1) \n"
" std 16, 128(1) \n"
" std 17, 136(1) \n"
" std 18, 144(1) \n"
" std 19, 152(1) \n"
" std 20, 160(1) \n"
" std 21, 168(1) \n"
" std 22, 176(1) \n"
" std 23, 184(1) \n"
" std 24, 192(1) \n"
" std 25, 200(1) \n"
" std 26, 208(1) \n"
" std 27, 216(1) \n"
" std 28, 224(1) \n"
" std 29, 232(1) \n"
" std 30, 240(1) \n"
" std 31, 248(1) \n"
// Pass 1st parameter (r3) unchanged (Stack*).
// Pass 2nd parameter (r4) unchanged (StackVisitor*).
// Save 3rd parameter (r5; IterateStackCallback).
" mr 6, 5 \n"
#if defined(_AIX)
// Set up TOC for callee.
" ld 2,8(5) \n"
// AIX uses function descriptors, which means that
// pointers to functions do not point to code, but
// instead point to metadata about them, hence
// need to deterrence.
" ld 6,0(6) \n"
#endif
// Pass 3rd parameter as sp (stack pointer).
" mr 5, 1 \n"
#if !defined(_AIX)
// Set up r12 to be equal to the callee address (in order for TOC
// relocation). Only needed on LE Linux.
" mr 12, 6 \n"
#endif
// Call the callback.
" mtctr 6 \n"
" bctrl \n"
// Discard all the registers.
" addi 1, 1, 256 \n"
// Restore lr.
" ld 0, 16(1) \n"
" mtlr 0 \n"
#if defined(_AIX)
// Restore TOC pointer.
" ld 2, 40(1) \n"
#else
" ld 2, 24(1) \n"
#endif
" blr \n");

View File

@ -1,113 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
//
// PPC ABI source:
// http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html
// AIX Runtime process stack:
// https://www.ibm.com/support/knowledgecenter/ssw_aix_71/assembler/idalangref_runtime_process.html
#ifdef __PPC64__
// 20 64-bit registers = 20 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 20,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(
#if defined(_AIX)
".csect .text[PR] \n"
".align 2 \n"
".globl .SaveCalleeSavedRegisters, hidden \n"
".SaveCalleeSavedRegisters: \n"
#else
".text \n"
".align 2 \n"
".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
#endif
// r3: [ intptr_t* buffer ]
// Save the callee-saved registers: lr, TOC pointer (r2), r14-r31.
" mflr 0 \n"
" std 0, 8(3) \n"
" std 2, 16(3) \n"
" std 14, 24(3) \n"
" std 15, 32(3) \n"
" std 16, 40(3) \n"
" std 17, 48(3) \n"
" std 18, 56(3) \n"
" std 19, 64(3) \n"
" std 20, 72(3) \n"
" std 21, 80(3) \n"
" std 22, 88(3) \n"
" std 23, 96(3) \n"
" std 24, 104(3) \n"
" std 25, 112(3) \n"
" std 26, 120(3) \n"
" std 27, 128(3) \n"
" std 28, 136(3) \n"
" std 29, 144(3) \n"
" std 30, 152(3) \n"
" std 31, 160(3) \n"
// Return.
" blr \n");
#else // !__PPC64__
// 20 32-bit registers = 20 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 20,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 4, "Mismatch in word size");
asm(
#if defined(_AIX)
".globl .SaveCalleeSavedRegisters, hidden \n"
".csect .text[PR] \n"
".SaveCalleeSavedRegisters: \n"
#else
".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
#endif
// r3: [ intptr_t* buffer ]
// Save the callee-saved registers: lr, TOC pointer (r2), r14-r31.
" mflr 0 \n"
" st 0, 4(3) \n"
" st 2, 8(3) \n"
" st 14, 12(3) \n"
" st 15, 16(3) \n"
" st 16, 20(3) \n"
" st 17, 24(3) \n"
" st 18, 28(3) \n"
" st 19, 32(3) \n"
" st 20, 36(3) \n"
" st 21, 40(3) \n"
" st 22, 44(3) \n"
" st 23, 48(3) \n"
" st 24, 52(3) \n"
" st 25, 56(3) \n"
" st 26, 60(3) \n"
" st 27, 64(3) \n"
" st 28, 68(3) \n"
" st 29, 72(3) \n"
" st 30, 76(3) \n"
" st 31, 80(3) \n"
// Return.
" blr \n");
#endif // __PPC64__

View File

@ -0,0 +1,93 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// See asm/x64/push_registers_asm.cc for why the function is not generated
// using clang.
//
// Calling convention source:
// https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf Table 18.2
#ifdef V8_TARGET_ARCH_RISCV64
asm(".global PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers and save return address.
" addi sp, sp, -112 \n"
// Save return address.
" sd ra, 104(sp) \n"
// sp is callee-saved.
" sd sp, 96(sp) \n"
// s0-s11 are callee-saved.
" sd s11, 88(sp) \n"
" sd s10, 80(sp) \n"
" sd s9, 72(sp) \n"
" sd s8, 64(sp) \n"
" sd s7, 56(sp) \n"
" sd s6, 48(sp) \n"
" sd s5, 40(sp) \n"
" sd s4, 32(sp) \n"
" sd s3, 24(sp) \n"
" sd s2, 16(sp) \n"
" sd s1, 8(sp) \n"
" sd s0, 0(sp) \n"
// Maintain frame pointer(fp is s0).
" mv s0, sp \n"
// Pass 1st parameter (a0) unchanged (Stack*).
// Pass 2nd parameter (a1) unchanged (StackVisitor*).
// Save 3rd parameter (a2; IterateStackCallback) to a3.
" mv a3, a2 \n"
// Pass 3rd parameter as sp (stack pointer).
" mv a2, sp \n"
// Call the callback.
" jalr a3 \n"
// Load return address.
" ld ra, 104(sp) \n"
// Restore frame pointer.
" ld s0, 0(sp) \n"
" addi sp, sp, 112 \n"
" jr ra \n");
#elif V8_TARGET_ARCH_RISCV32
asm(".global PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers and save return address.
" addi sp, sp, -56 \n"
// Save return address.
" sw ra, 52(sp) \n"
// sp is callee-saved.
" sw sp, 48(sp) \n"
// s0-s11 are callee-saved.
" sw s11, 44(sp) \n"
" sw s10, 40(sp) \n"
" sw s9, 36(sp) \n"
" sw s8, 32(sp) \n"
" sw s7, 28(sp) \n"
" sw s6, 24(sp) \n"
" sw s5, 20(sp) \n"
" sw s4, 16(sp) \n"
" sw s3, 12(sp) \n"
" sw s2, 8(sp) \n"
" sw s1, 4(sp) \n"
" sw s0, 0(sp) \n"
// Maintain frame pointer(fp is s0).
" mv s0, sp \n"
// Pass 1st parameter (a0) unchanged (Stack*).
// Pass 2nd parameter (a1) unchanged (StackVisitor*).
// Save 3rd parameter (a2; IterateStackCallback) to a3.
" mv a3, a2 \n"
// Pass 3rd parameter as sp (stack pointer).
" mv a2, sp \n"
// Call the callback.
" jalr a3 \n"
// Load return address.
" lw ra, 52(sp) \n"
// Restore frame pointer.
" lw s0, 0(sp) \n"
" addi sp, sp, 56 \n"
" jr ra \n");
#endif

View File

@ -1,68 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Calling convention source:
// https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf Table 18.2
#if V8_HOST_ARCH_RISCV64
// 12 64-bit registers = 12 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 12,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(".global SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// a0: [ intptr_t* buffer ]
// Save the callee-saved registers: s0-s11.
" sd s11, 88(a0) \n"
" sd s10, 80(a0) \n"
" sd s9, 72(a0) \n"
" sd s8, 64(a0) \n"
" sd s7, 56(a0) \n"
" sd s6, 48(a0) \n"
" sd s5, 40(a0) \n"
" sd s4, 32(a0) \n"
" sd s3, 24(a0) \n"
" sd s2, 16(a0) \n"
" sd s1, 8(a0) \n"
" sd s0, 0(a0) \n"
// Return.
" jr ra \n");
#elif V8_HOST_ARCH_RISCV32
// 12 32-bit registers = 12 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 12,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 4, "Mismatch in word size");
asm(".global SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// a0: [ intptr_t* buffer ]
// Save the callee-saved registers: s0-s11.
" sw s11, 44(a0) \n"
" sw s10, 40(a0) \n"
" sw s9, 36(a0) \n"
" sw s8, 32(a0) \n"
" sw s7, 28(a0) \n"
" sw s6, 24(a0) \n"
" sw s5, 20(a0) \n"
" sw s4, 16(a0) \n"
" sw s3, 12(a0) \n"
" sw s2, 8(a0) \n"
" sw s1, 4(a0) \n"
" sw s0, 0(a0) \n"
// Return.
" jr ra \n");
#endif

View File

@ -0,0 +1,37 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
// See asm/x64/push_registers_clang.cc for why the function is not generated
// using clang.
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// S390 ABI source:
// http://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html
asm(".text \n"
".align 8 \n"
".globl PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// Push all callee-saved registers.
// r6-r13, r14 and sp(r15)
" stmg %r6, %sp, 48(%sp) \n"
// Allocate frame.
" lay %sp, -160(%sp) \n"
// Pass 1st parameter (r2) unchanged (Stack*).
// Pass 2nd parameter (r3) unchanged (StackVisitor*).
// Save 3rd parameter (r4; IterateStackCallback).
" lgr %r5, %r4 \n"
// Pass sp as 3rd parameter. 160+48 to point
// to callee saved region stored above.
" lay %r4, 208(%sp) \n"
// Call the callback.
" basr %r14, %r5 \n"
" lmg %r14,%sp, 272(%sp) \n"
" br %r14 \n");

View File

@ -1,34 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
// See asm/x64/save_registers_asm.cc for why the function is not generated
// using clang.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// S390 ABI source:
// http://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html
// 10 64-bit registers = 10 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 10,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(".text \n"
".align 8 \n"
".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// r2: [ intptr_t* buffer ]
// Save the callee-saved registers: r6-r13, r14 and sp(r15).
" stmg %r6, %sp, 0(%r2) \n"
// Return.
" br %r14 \n");

View File

@ -0,0 +1,106 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Push all callee-saved registers to get them on the stack for conservative
// stack scanning.
//
// We cannot rely on clang generating the function and right symbol mangling
// as `__attribute__((naked))` does not prevent clang from generating TSAN
// function entry stubs (`__tsan_func_entry`). Even with
// `__attribute__((no_sanitize_thread)` annotation clang generates the entry
// stub.
// See https://bugs.llvm.org/show_bug.cgi?id=45400.
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// _WIN64 Defined as 1 when the compilation target is 64-bit ARM or x64.
// Otherwise, undefined.
#ifdef _WIN64
// We maintain 16-byte alignment at calls. There is an 8-byte return address
// on the stack and we push 232 bytes which maintains 16-byte stack alignment
// at the call.
// Source: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
asm(".globl PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
// rbp is callee-saved. Maintain proper frame pointer for debugging.
" push %rbp \n"
" mov %rsp, %rbp \n"
// Dummy for alignment.
" push $0xCDCDCD \n"
" push %rsi \n"
" push %rdi \n"
" push %rbx \n"
" push %r12 \n"
" push %r13 \n"
" push %r14 \n"
" push %r15 \n"
" sub $160, %rsp \n"
// Use aligned instrs as we are certain that the stack is properly aligned.
" movdqa %xmm6, 144(%rsp) \n"
" movdqa %xmm7, 128(%rsp) \n"
" movdqa %xmm8, 112(%rsp) \n"
" movdqa %xmm9, 96(%rsp) \n"
" movdqa %xmm10, 80(%rsp) \n"
" movdqa %xmm11, 64(%rsp) \n"
" movdqa %xmm12, 48(%rsp) \n"
" movdqa %xmm13, 32(%rsp) \n"
" movdqa %xmm14, 16(%rsp) \n"
" movdqa %xmm15, (%rsp) \n"
// Pass 1st parameter (rcx) unchanged (Stack*).
// Pass 2nd parameter (rdx) unchanged (StackVisitor*).
// Save 3rd parameter (r8; IterateStackCallback)
" mov %r8, %r9 \n"
// Pass 3rd parameter as rsp (stack pointer).
" mov %rsp, %r8 \n"
// Call the callback.
" call *%r9 \n"
// Pop the callee-saved registers.
" add $224, %rsp \n"
// Restore rbp as it was used as frame pointer.
" pop %rbp \n"
" ret \n");
#else // !_WIN64
// We maintain 16-byte alignment at calls. There is an 8-byte return address
// on the stack and we push 56 bytes which maintains 16-byte stack alignment
// at the call.
// Source: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
asm(
#ifdef __APPLE__
".globl _PushAllRegistersAndIterateStack \n"
".private_extern _PushAllRegistersAndIterateStack \n"
"_PushAllRegistersAndIterateStack: \n"
#else // !__APPLE__
".globl PushAllRegistersAndIterateStack \n"
".type PushAllRegistersAndIterateStack, %function \n"
".hidden PushAllRegistersAndIterateStack \n"
"PushAllRegistersAndIterateStack: \n"
#endif // !__APPLE__
// rbp is callee-saved. Maintain proper frame pointer for debugging.
" push %rbp \n"
" mov %rsp, %rbp \n"
// Dummy for alignment.
" push $0xCDCDCD \n"
" push %rbx \n"
" push %r12 \n"
" push %r13 \n"
" push %r14 \n"
" push %r15 \n"
// Pass 1st parameter (rdi) unchanged (Stack*).
// Pass 2nd parameter (rsi) unchanged (StackVisitor*).
// Save 3rd parameter (rdx; IterateStackCallback)
" mov %rdx, %r8 \n"
// Pass 3rd parameter as rsp (stack pointer).
" mov %rsp, %rdx \n"
// Call the callback.
" call *%r8 \n"
// Pop the callee-saved registers.
" add $48, %rsp \n"
// Restore rbp as it was used as frame pointer.
" pop %rbp \n"
" ret \n");
#endif // !_WIN64

View File

@ -0,0 +1,57 @@
;; Copyright 2020 the V8 project authors. All rights reserved.
;; Use of this source code is governed by a BSD-style license that can be
;; found in the LICENSE file.
;; MASM syntax
;; https://docs.microsoft.com/en-us/cpp/assembler/masm/microsoft-macro-assembler-reference?view=vs-2019
public PushAllRegistersAndIterateStack
.code
PushAllRegistersAndIterateStack:
;; Push all callee-saved registers to get them on the stack for conservative
;; stack scanning.
;;
;; We maintain 16-byte alignment at calls. There is an 8-byte return address
;; on the stack and we push 232 bytes which maintains 16-byte stack
;; alignment at the call.
;; Source: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
;;
;; rbp is callee-saved. Maintain proper frame pointer for debugging.
push rbp
mov rbp, rsp
push 0CDCDCDh ;; Dummy for alignment.
push rsi
push rdi
push rbx
push r12
push r13
push r14
push r15
sub rsp, 160
;; Use aligned instrs as we are certain that the stack is properly aligned.
movdqa xmmword ptr [rsp + 144], xmm6
movdqa xmmword ptr [rsp + 128], xmm7
movdqa xmmword ptr [rsp + 112], xmm8
movdqa xmmword ptr [rsp + 96], xmm9
movdqa xmmword ptr [rsp + 80], xmm10
movdqa xmmword ptr [rsp + 64], xmm11
movdqa xmmword ptr [rsp + 48], xmm12
movdqa xmmword ptr [rsp + 32], xmm13
movdqa xmmword ptr [rsp + 16], xmm14
movdqa xmmword ptr [rsp], xmm15
;; Pass 1st parameter (rcx) unchanged (Stack*).
;; Pass 2nd parameter (rdx) unchanged (StackVisitor*).
;; Save 3rd parameter (r8; IterateStackCallback)
mov r9, r8
;; Pass 3rd parameter as rsp (stack pointer).
mov r8, rsp
;; Call the callback.
call r9
;; Pop the callee-saved registers.
add rsp, 224
;; Restore rbp as it was used as frame pointer.
pop rbp
ret
end

View File

@ -1,94 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <src/heap/base/stack.h>
// Save all callee-saved registers in the specified buffer.
// extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
//
// We cannot rely on clang generating the function and right symbol mangling
// as `__attribute__((naked))` does not prevent clang from generating TSAN
// function entry stubs (`__tsan_func_entry`). Even with
// `__attribute__((no_sanitize_thread)` annotation clang generates the entry
// stub.
// See https://bugs.llvm.org/show_bug.cgi?id=45400.
//
// Do not depend on V8_TARGET_OS_* defines as some embedders may override the
// GN toolchain (e.g. ChromeOS) and not provide them.
// _WIN64 Defined as 1 when the compilation target is 64-bit ARM or x64.
// Otherwise, undefined.
#ifdef _WIN64
// Source: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
// 7 64-bit registers + 1 for alignment purposes = 8 * 1 = 8 intprt_t
// 10 128-bit registers = 10 * 2 = 20 intptr_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 28,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(".globl SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
// %rcx: [ intptr_t* buffer ]
// %rbp is callee-saved. Maintain proper frame pointer for debugging.
" push %rbp \n"
" mov %rsp, %rbp \n"
// Save the callee-saved registers.
" mov %rsi, 0(%rcx) \n"
" mov %rdi, 8(%rcx) \n"
" mov %rbx, 16(%rcx) \n"
" mov %r12, 24(%rcx) \n"
" mov %r13, 32(%rcx) \n"
" mov %r14, 40(%rcx) \n"
" mov %r15, 48(%rcx) \n"
// Skip one slot to achieve proper alignment and use aligned instructions,
// as we are sure that the buffer is properly aligned.
" movdqa %xmm6, 64(%rcx) \n"
" movdqa %xmm7, 80(%rcx) \n"
" movdqa %xmm8, 96(%rcx) \n"
" movdqa %xmm9, 112(%rcx) \n"
" movdqa %xmm10, 128(%rcx) \n"
" movdqa %xmm11, 144(%rcx) \n"
" movdqa %xmm12, 160(%rcx) \n"
" movdqa %xmm13, 176(%rcx) \n"
" movdqa %xmm14, 192(%rcx) \n"
" movdqa %xmm15, 208(%rcx) \n"
// Return.
" pop %rbp \n"
" ret \n");
#else // !_WIN64
// Source: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
// 5 64-bit registers = 5 intprt_t
static_assert(heap::base::Stack::NumberOfCalleeSavedRegisters() == 5,
"Mismatch in the number of callee-saved registers");
static_assert(sizeof(intptr_t) == 8, "Mismatch in word size");
asm(
#ifdef __APPLE__
".globl _SaveCalleeSavedRegisters \n"
".private_extern _SaveCalleeSavedRegisters \n"
"_SaveCalleeSavedRegisters: \n"
#else // !__APPLE__
".globl SaveCalleeSavedRegisters \n"
".type SaveCalleeSavedRegisters, %function \n"
".hidden SaveCalleeSavedRegisters \n"
"SaveCalleeSavedRegisters: \n"
#endif // !__APPLE__
// %rdi: [ intptr_t* buffer ]
// %rbp is callee-saved. Maintain proper frame pointer for debugging.
" push %rbp \n"
" mov %rsp, %rbp \n"
// Save the callee-saved registers.
" mov %rbx, 0(%rdi) \n"
" mov %r12, 8(%rdi) \n"
" mov %r13, 16(%rdi) \n"
" mov %r14, 24(%rdi) \n"
" mov %r15, 32(%rdi) \n"
// Restore %rbp as it was used as frame pointer and return.
" pop %rbp \n"
" ret \n");
#endif // !_WIN64

View File

@ -1,43 +0,0 @@
;; Copyright 2020 the V8 project authors. All rights reserved.
;; Use of this source code is governed by a BSD-style license that can be
;; found in the LICENSE file.
;; MASM syntax
;; https://docs.microsoft.com/en-us/cpp/assembler/masm/microsoft-macro-assembler-reference?view=vs-2019
public SaveCalleeSavedRegisters
.code
;; Save all callee-saved registers in the specified buffer.
;; extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
SaveCalleeSavedRegisters:
;; %rcx: [ intptr_t* buffer ]
;; %rbp is callee-saved. Maintain proper frame pointer for debugging.
push rbp
mov rbp, rsp
;; Save the callee-saved registers.
mov qword ptr [rcx], rsi
mov qword ptr [rcx + 8], rdi
mov qword ptr [rcx + 16], rbx
mov qword ptr [rcx + 24], r12
mov qword ptr [rcx + 32], r13
mov qword ptr [rcx + 40], r14
mov qword ptr [rcx + 48], r15
;; Skip one slot to achieve proper alignment and use aligned instructions,
;; as we are sure that the buffer is properly aligned.
movdqa xmmword ptr [rcx + 64], xmm6
movdqa xmmword ptr [rcx + 80], xmm7
movdqa xmmword ptr [rcx + 96], xmm8
movdqa xmmword ptr [rcx + 112], xmm9
movdqa xmmword ptr [rcx + 128], xmm10
movdqa xmmword ptr [rcx + 144], xmm11
movdqa xmmword ptr [rcx + 160], xmm12
movdqa xmmword ptr [rcx + 176], xmm13
movdqa xmmword ptr [rcx + 192], xmm14
movdqa xmmword ptr [rcx + 208], xmm15
;; Restore %rbp as it was used as frame pointer and return.
pop rbp
ret
end

View File

@ -6,18 +6,21 @@
#include <limits>
#include "src/base/platform/platform.h"
#include "src/base/sanitizer/asan.h"
#include "src/base/sanitizer/msan.h"
#include "src/base/sanitizer/tsan.h"
namespace heap::base {
Stack::Stack(const void* stack_start) : stack_start_(stack_start) {}
void Stack::SetStackStart(const void* stack_start) {
DCHECK(!context_);
stack_start_ = stack_start;
}
// Function with architecture-specific implementation:
// Pushes all callee-saved registers to the stack and invokes the callback,
// passing the supplied pointers (stack and argument) and the intended stack
// marker.
using IterateStackCallback = void (*)(const Stack*, StackVisitor*, const void*);
extern "C" void PushAllRegistersAndIterateStack(const Stack* stack,
StackVisitor* visitor,
IterateStackCallback callback);
bool Stack::IsOnStack(const void* slot) const {
DCHECK_NOT_NULL(stack_start_);
@ -141,97 +144,62 @@ void IteratePointersInStack(StackVisitor* visitor, const void* top,
} // namespace
void Stack::IteratePointers(StackVisitor* visitor) const {
DCHECK_NOT_NULL(stack_start_);
DCHECK(context_);
DCHECK_NOT_NULL(context_->stack_marker);
// static
void Stack::IteratePointersImpl(const Stack* stack, StackVisitor* visitor,
const void* stack_end) {
#ifdef V8_USE_ADDRESS_SANITIZER
const void* asan_fake_stack = __asan_get_current_fake_stack();
#else
const void* asan_fake_stack = nullptr;
#endif // V8_USE_ADDRESS_SANITIZER
// Iterate through the registers.
for (intptr_t value : context_->registers) {
const void* address = reinterpret_cast<const void*>(value);
MSAN_MEMORY_IS_INITIALIZED(&address, sizeof(address));
if (address == nullptr) continue;
visitor->VisitPointer(address);
IterateAsanFakeFrameIfNecessary(visitor, asan_fake_stack, stack_start_,
context_->stack_marker, address);
}
// Iterate through the stack.
// All supported platforms should have their stack aligned to at least
// sizeof(void*).
constexpr size_t kMinStackAlignment = sizeof(void*);
CHECK_EQ(0u, reinterpret_cast<uintptr_t>(context_->stack_marker) &
(kMinStackAlignment - 1));
IteratePointersInStack(
visitor, reinterpret_cast<const void* const*>(context_->stack_marker),
stack_start_, asan_fake_stack);
CHECK_EQ(0u,
reinterpret_cast<uintptr_t>(stack_end) & (kMinStackAlignment - 1));
IteratePointersInStack(visitor,
reinterpret_cast<const void* const*>(stack_end),
stack->stack_start_, asan_fake_stack);
for (const auto& stack : inactive_stacks_) {
IteratePointersInStack(visitor, stack.top, stack.start, asan_fake_stack);
for (const auto& segment : stack->inactive_stacks_) {
IteratePointersInStack(visitor, segment.top, segment.start,
asan_fake_stack);
}
IterateUnsafeStackIfNecessary(visitor);
}
namespace {
// Function with architecture-specific implementation:
// Saves all callee-saved registers in the specified buffer.
extern "C" void SaveCalleeSavedRegisters(intptr_t* buffer);
void Stack::IteratePointers(StackVisitor* visitor) const {
// TODO(v8:13493): Remove the implication as soon as IsOnCurrentStack is
// compatible with stack switching.
DCHECK_IMPLIES(!wasm_stack_switching_, IsOnCurrentStack(stack_start_));
PushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl);
// No need to deal with callee-saved registers as they will be kept alive by
// the regular conservative stack iteration.
// TODO(chromium:1056170): Add support for SIMD and/or filtering.
IterateUnsafeStackIfNecessary(visitor);
}
void Stack::IteratePointersUnsafe(StackVisitor* visitor,
const void* stack_end) const {
DCHECK_NOT_NULL(stack_start_);
DCHECK_NOT_NULL(stack_end);
DCHECK_GE(stack_start_, stack_end);
IteratePointersImpl(this, visitor, stack_end);
}
#ifdef DEBUG
bool IsOnCurrentStack(const void* ptr) {
// static
bool Stack::IsOnCurrentStack(const void* ptr) {
DCHECK_NOT_NULL(ptr);
const void* current_stack_start = v8::base::Stack::GetStackStart();
const void* current_stack_top = v8::base::Stack::GetCurrentStackPosition();
return ptr <= current_stack_start && ptr >= current_stack_top;
}
#endif // DEBUG
} // namespace
void Stack::SaveContext(bool check_invariant) {
// TODO(v8:13493): Remove the method's parameter and the implication as soon
// as IsOnCurrentStack is compatible with stack switching.
DCHECK_IMPLIES(check_invariant, IsOnCurrentStack(stack_start_));
// Contexts can be nested but the marker and the registers are only saved on
// the first invocation.
if (context_) {
++context_->nesting_counter;
return;
}
// Allocate the context and set the marker.
const void* stack_top = v8::base::Stack::GetCurrentStackPosition();
DCHECK_NOT_NULL(stack_top);
context_ = std::make_unique<Context>(stack_top);
// TODO(v8:13493): Remove the implication as soon as IsValidMarker is
// compatible with stack switching.
DCHECK_IMPLIES(check_invariant, stack_top <= stack_start_);
context_->stack_marker = stack_top;
// Save the registers.
SaveCalleeSavedRegisters(context_->registers.data());
}
void Stack::ClearContext(bool check_invariant) {
// TODO(v8:13493): Remove the method's parameter and the implication as soon
// as IsOnCurrentStack is compatible with stack switching.
DCHECK_IMPLIES(check_invariant, IsOnCurrentStack(stack_start_));
DCHECK(context_);
// Skip clearing the context if that was a nested invocation.
if (context_->nesting_counter > 0) {
--context_->nesting_counter;
return;
}
context_.reset();
}
void Stack::AddStackSegment(const void* start, const void* top) {
DCHECK_LE(top, start);
inactive_stacks_.push_back({start, top});

View File

@ -5,10 +5,9 @@
#ifndef V8_HEAP_BASE_STACK_H_
#define V8_HEAP_BASE_STACK_H_
#include <memory>
#include <vector>
#include "src/base/macros.h"
#include "src/base/platform/platform.h"
namespace heap::base {
@ -30,96 +29,54 @@ class StackVisitor {
// of relevant GC stack regions where interesting pointers can be found.
class V8_EXPORT_PRIVATE Stack final {
public:
// The size of the buffer for storing the callee-saved registers is going to
// be equal to kNumberOfCalleeSavedRegisters * sizeof(intptr_t).
// This is architecture-specific.
static constexpr int NumberOfCalleeSavedRegisters() {
return Context::kNumberOfCalleeSavedRegisters;
}
explicit Stack(const void* stack_start = nullptr);
explicit Stack(const void* stack_start = nullptr,
bool wasm_stack_switching = false)
: stack_start_(stack_start),
wasm_stack_switching_(wasm_stack_switching) {}
// Sets the start of the stack.
void SetStackStart(const void* stack_start);
void SetStackStart(const void* stack_start, bool wasm_stack_switching) {
stack_start_ = stack_start;
wasm_stack_switching_ = wasm_stack_switching;
}
// Returns true if |slot| is part of the stack and false otherwise.
bool IsOnStack(const void* slot) const;
// Word-aligned iteration of the stack and the saved registers.
// Slot values are passed on to `visitor`.
// Word-aligned iteration of the stack. Callee-saved registers are pushed to
// the stack before iterating pointers. Slot values are passed on to
// `visitor`.
void IteratePointers(StackVisitor* visitor) const;
// Saves and clears the stack context, i.e., it sets the stack marker and
// saves the registers.
// TODO(v8:13493): The parameter is for suppressing the invariant check in
// the case of WASM stack switching. It will be removed as soon as context
// saving becomes compatible with stack switching.
void SaveContext(bool check_invariant = true);
void ClearContext(bool check_invariant = true);
// Word-aligned iteration of the stack, starting at `stack_end`. Slot values
// are passed on to `visitor`. This is intended to be used with verifiers that
// only visit a subset of the stack of IteratePointers().
//
// **Ignores:**
// - Callee-saved registers.
// - SafeStack.
void IteratePointersUnsafe(StackVisitor* visitor,
const void* stack_end) const;
void AddStackSegment(const void* start, const void* top);
void ClearStackSegments();
private:
struct Context {
// The following constant is architecture-specific.
#if V8_HOST_ARCH_IA32
// Must be consistent with heap/base/asm/ia32/.
static constexpr int kNumberOfCalleeSavedRegisters = 3;
#elif V8_HOST_ARCH_X64
#ifdef _WIN64
// Must be consistent with heap/base/asm/x64/.
static constexpr int kNumberOfCalleeSavedRegisters = 28;
#else // !_WIN64
// Must be consistent with heap/base/asm/x64/.
static constexpr int kNumberOfCalleeSavedRegisters = 5;
#endif // !_WIN64
#elif V8_HOST_ARCH_ARM64
// Must be consistent with heap/base/asm/arm64/.
static constexpr int kNumberOfCalleeSavedRegisters = 11;
#elif V8_HOST_ARCH_ARM
// Must be consistent with heap/base/asm/arm/.
static constexpr int kNumberOfCalleeSavedRegisters = 8;
#elif V8_HOST_ARCH_PPC64
// Must be consistent with heap/base/asm/ppc/.
static constexpr int kNumberOfCalleeSavedRegisters = 20;
#elif V8_HOST_ARCH_PPC
// Must be consistent with heap/base/asm/ppc/.
static constexpr int kNumberOfCalleeSavedRegisters = 20;
#elif V8_HOST_ARCH_MIPS64
// Must be consistent with heap/base/asm/mips64el/.
static constexpr int kNumberOfCalleeSavedRegisters = 9;
#elif V8_HOST_ARCH_LOONG64
// Must be consistent with heap/base/asm/loong64/.
static constexpr int kNumberOfCalleeSavedRegisters = 11;
#elif V8_HOST_ARCH_S390
// Must be consistent with heap/base/asm/s390/.
static constexpr int kNumberOfCalleeSavedRegisters = 10;
#elif V8_HOST_ARCH_RISCV32
// Must be consistent with heap/base/asm/riscv/.
static constexpr int kNumberOfCalleeSavedRegisters = 12;
#elif V8_HOST_ARCH_RISCV64
// Must be consistent with heap/base/asm/riscv/.
static constexpr int kNumberOfCalleeSavedRegisters = 12;
#else
#error Unknown architecture.
#ifdef DEBUG
static bool IsOnCurrentStack(const void* ptr);
#endif
explicit Context(const void* marker) : stack_marker(marker) {}
int nesting_counter = 0;
const void* stack_marker;
// We always double-align this buffer, to support for longer registers,
// e.g., 128-bit registers in WIN64.
alignas(2 * sizeof(intptr_t))
std::array<intptr_t, kNumberOfCalleeSavedRegisters> registers;
};
static void IteratePointersImpl(const Stack* stack, StackVisitor* visitor,
const void* stack_end);
const void* stack_start_;
std::unique_ptr<Context> context_;
// Stack segments that may also contain pointers and should be
// scanned.
// TODO(v8:13493): This is for suppressing the check that we are in the
// correct stack, in the case of WASM stack switching. It will be removed as
// soon as context saving becomes compatible with stack switching.
bool wasm_stack_switching_;
// Stack segments that may also contain pointers and should be scanned.
struct StackSegments {
const void* start;
const void* top;

View File

@ -16,6 +16,7 @@
#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/optional.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/execution/isolate-inl.h"
#include "src/flags/flags.h"
@ -839,7 +840,7 @@ void CppHeap::TraceEpilogue() {
const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers();
#if CPPGC_VERIFY_HEAP
UnifiedHeapMarkingVerifier verifier(*this, *collection_type_);
verifier.Run(stack_state_of_prev_gc(),
verifier.Run(stack_state_of_prev_gc(), stack_end_of_current_gc(),
stats_collector()->marked_bytes_on_current_cycle() +
bytes_allocated_in_prefinalizers);
#endif // CPPGC_VERIFY_HEAP
@ -942,7 +943,7 @@ void CppHeap::CollectGarbageForTesting(CollectionType collection_type,
// Finish sweeping in case it is still running.
sweeper().FinishIfRunning();
SaveStackContextScope stack_context_scope(stack());
SetStackEndOfCurrentGC(v8::base::Stack::GetCurrentStackPosition());
if (isolate_) {
reinterpret_cast<v8::Isolate*>(isolate_)

View File

@ -183,6 +183,13 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
stack_state_of_prev_gc_ = stack_state;
}
const void* stack_end_of_current_gc() const {
return stack_end_of_current_gc_;
}
void SetStackEndOfCurrentGC(const void* stack_end) {
stack_end_of_current_gc_ = stack_end;
}
void SetInAtomicPauseForTesting(bool value) { in_atomic_pause_ = value; }
virtual void StartIncrementalGarbageCollectionForTesting() = 0;
@ -288,6 +295,10 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
EmbedderStackState::kNoHeapPointers;
std::unique_ptr<EmbedderStackState> override_stack_state_;
// Marker that signals end of the interesting stack region in which on-heap
// pointers can be found.
const void* stack_end_of_current_gc_ = nullptr;
bool in_atomic_pause_ = false;
int creation_thread_id_ = v8::base::OS::GetCurrentThreadId();

View File

@ -166,10 +166,9 @@ void Heap::FinalizeGarbageCollection(StackState stack_state) {
DCHECK(!in_no_gc_scope());
CHECK(!in_disallow_gc_scope());
config_.stack_state = stack_state;
SetStackEndOfCurrentGC(v8::base::Stack::GetCurrentStackPosition());
in_atomic_pause_ = true;
stack()->SaveContext();
#if defined(CPPGC_YOUNG_GENERATION)
// Check if the young generation was enabled. We must enable young generation
// before calling the custom weak callbacks to make sure that the callbacks
@ -188,7 +187,7 @@ void Heap::FinalizeGarbageCollection(StackState stack_state) {
const size_t bytes_allocated_in_prefinalizers = ExecutePreFinalizers();
#if CPPGC_VERIFY_HEAP
MarkingVerifier verifier(*this, config_.collection_type);
verifier.Run(config_.stack_state,
verifier.Run(config_.stack_state, stack_end_of_current_gc(),
stats_collector()->marked_bytes_on_current_cycle() +
bytes_allocated_in_prefinalizers);
#endif // CPPGC_VERIFY_HEAP
@ -197,8 +196,6 @@ void Heap::FinalizeGarbageCollection(StackState stack_state) {
#endif
USE(bytes_allocated_in_prefinalizers);
stack()->ClearContext();
#if defined(CPPGC_YOUNG_GENERATION)
ResetRememberedSet();
#endif // defined(CPPGC_YOUNG_GENERATION)

View File

@ -45,7 +45,8 @@ MarkingVerifierBase::MarkingVerifierBase(
collection_type_(collection_type) {}
void MarkingVerifierBase::Run(
StackState stack_state, v8::base::Optional<size_t> expected_marked_bytes) {
StackState stack_state, const void* stack_end,
v8::base::Optional<size_t> expected_marked_bytes) {
Traverse(heap_.raw_heap());
// Avoid verifying the stack when running with TSAN as the TSAN runtime changes
// stack contents when e.g. working with locks. Specifically, the marker uses
@ -62,7 +63,7 @@ void MarkingVerifierBase::Run(
#if !defined(THREAD_SANITIZER) && !defined(CPPGC_POINTER_COMPRESSION)
if (stack_state == StackState::kMayContainHeapPointers) {
in_construction_objects_ = &in_construction_objects_stack_;
heap_.stack()->IteratePointers(this);
heap_.stack()->IteratePointersUnsafe(this, stack_end);
// The objects found through the unsafe iteration are only a subset of the
// regular iteration as they miss objects held alive only from callee-saved
// registers that are never pushed on the stack and SafeStack.

View File

@ -41,7 +41,7 @@ class V8_EXPORT_PRIVATE MarkingVerifierBase
MarkingVerifierBase(const MarkingVerifierBase&) = delete;
MarkingVerifierBase& operator=(const MarkingVerifierBase&) = delete;
void Run(StackState, v8::base::Optional<size_t>);
void Run(StackState, const void*, v8::base::Optional<size_t>);
protected:
MarkingVerifierBase(HeapBase&, CollectionType, VerificationState&,

View File

@ -1685,7 +1685,22 @@ bool Heap::CollectGarbage(AllocationSpace space,
DevToolsTraceEventScope devtools_trace_event_scope(
this, IsYoungGenerationCollector(collector) ? "MinorGC" : "MajorGC",
GarbageCollectionReasonToString(gc_reason));
SaveStackContextScope stack_context_scope(&stack());
if (cpp_heap()) {
if (collector == GarbageCollector::MARK_COMPACTOR ||
(collector == GarbageCollector::MINOR_MARK_COMPACTOR &&
CppHeap::From(cpp_heap())->generational_gc_supported())) {
// CppHeap needs a stack marker at the top of all entry points to allow
// deterministic passes over the stack. E.g., a verifier that should
// only find a subset of references of the marker.
//
// TODO(chromium:1056170): Consider adding a component that keeps track
// of relevant GC stack regions where interesting pointers can be found.
static_cast<v8::internal::CppHeap*>(cpp_heap())
->SetStackEndOfCurrentGC(
v8::base::Stack::GetCurrentStackPosition());
}
}
GarbageCollectionPrologue(gc_reason, gc_callback_flags);
{
@ -2396,8 +2411,6 @@ void Heap::PerformSharedGarbageCollection(Isolate* initiator,
DCHECK(incremental_marking_->IsStopped());
DCHECK_NOT_NULL(isolate()->global_safepoint());
SaveStackContextScope stack_context_scope(&stack());
isolate()->global_safepoint()->IterateClientIsolates([](Isolate* client) {
client->heap()->FreeSharedLinearAllocationAreas();
@ -5809,7 +5822,12 @@ const cppgc::EmbedderStackState* Heap::overriden_stack_state() const {
}
void Heap::SetStackStart(void* stack_start) {
stack().SetStackStart(stack_start);
#if V8_ENABLE_WEBASSEMBLY
stack().SetStackStart(stack_start,
v8_flags.experimental_wasm_stack_switching);
#else
stack().SetStackStart(stack_start, false);
#endif // V8_ENABLE_WEBASSEMBLY
}
::heap::base::Stack& Heap::stack() {
@ -6391,8 +6409,7 @@ HeapObjectIterator::HeapObjectIterator(
filtering_(filtering),
filter_(nullptr),
space_iterator_(nullptr),
object_iterator_(nullptr),
stack_context_scope_(&heap->stack()) {
object_iterator_(nullptr) {
heap_->MakeHeapIterable();
// Start the iteration.
space_iterator_ = new SpaceIterator(heap_);
@ -7371,28 +7388,5 @@ CppClassNamesAsHeapObjectNameScope::CppClassNamesAsHeapObjectNameScope(
CppClassNamesAsHeapObjectNameScope::~CppClassNamesAsHeapObjectNameScope() =
default;
SaveStackContextScope::SaveStackContextScope(::heap::base::Stack* stack)
: stack_(stack) {
#if V8_ENABLE_WEBASSEMBLY
// TODO(v8:13493): Do not check the stack context invariant if WASM stack
// switching is enabled. This will be removed as soon as context saving
// becomes compatible with stack switching.
stack_->SaveContext(!v8_flags.experimental_wasm_stack_switching);
#else
stack_->SaveContext();
#endif // V8_ENABLE_WEBASSEMBLY
}
SaveStackContextScope::~SaveStackContextScope() {
#if V8_ENABLE_WEBASSEMBLY
// TODO(v8:13493): Do not check the stack context invariant if WASM stack
// switching is enabled. This will be removed as soon as context saving
// becomes compatible with stack switching.
stack_->ClearContext(!v8_flags.experimental_wasm_stack_switching);
#else
stack_->ClearContext();
#endif // V8_ENABLE_WEBASSEMBLY
}
} // namespace internal
} // namespace v8

View File

@ -2633,17 +2633,6 @@ class V8_NODISCARD IgnoreLocalGCRequests {
Heap* heap_;
};
// TODO(v8:13493): This class will move to src/heap/base/stack.h once its
// implementation no longer needs access to V8 flags.
class V8_EXPORT_PRIVATE V8_NODISCARD SaveStackContextScope {
public:
explicit SaveStackContextScope(::heap::base::Stack* stack);
~SaveStackContextScope();
protected:
::heap::base::Stack* stack_;
};
// Space iterator for iterating over all the paged spaces of the heap: Map
// space, old space and code space. Returns each space in turn, and null when it
// is done.
@ -2695,7 +2684,6 @@ class V8_EXPORT_PRIVATE HeapObjectIterator {
SpaceIterator* space_iterator_;
// Object iterator for the space currently being iterated.
std::unique_ptr<ObjectIterator> object_iterator_;
SaveStackContextScope stack_context_scope_;
DISALLOW_GARBAGE_COLLECTION(no_heap_allocation_)
};

View File

@ -2055,16 +2055,14 @@ bool V8HeapExplorer::IterateAndExtractReferences(
// its custom name to a generic builtin.
RootsReferencesExtractor extractor(this);
ReadOnlyRoots(heap_).Iterate(&extractor);
{
SaveStackContextScope scope(&heap_->stack());
heap_->IterateRoots(&extractor, base::EnumSet<SkipRoot>{SkipRoot::kWeak});
// TODO(v8:11800): The heap snapshot generator incorrectly considers the
// weak string tables as strong retainers. Move IterateWeakRoots after
// SetVisitingWeakRoots.
heap_->IterateWeakRoots(&extractor, {});
extractor.SetVisitingWeakRoots();
heap_->IterateWeakGlobalHandles(&extractor);
}
heap_->IterateRoots(&extractor, base::EnumSet<SkipRoot>{SkipRoot::kWeak});
// TODO(v8:11800): The heap snapshot generator incorrectly considers the weak
// string tables as strong retainers. Move IterateWeakRoots after
// SetVisitingWeakRoots.
heap_->IterateWeakRoots(&extractor, {});
extractor.SetVisitingWeakRoots();
heap_->IterateWeakGlobalHandles(&extractor);
bool interrupted = false;
CombinedHeapObjectIterator iterator(heap_,

View File

@ -75,6 +75,8 @@ TEST(ExternalString_ExternalBackingStoreSizeDecreases) {
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
i::DisableConservativeStackScanningScopeForTesting no_stack_scanning(heap);
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
@ -104,6 +106,8 @@ TEST(ExternalString_ExternalBackingStoreSizeIncreasesMarkCompact) {
heap::AbandonCurrentlyFreeMemory(heap->old_space());
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
i::DisableConservativeStackScanningScopeForTesting no_stack_scanning(heap);
const size_t backing_store_before =
heap->old_space()->ExternalBackingStoreBytes(type);
@ -139,6 +143,8 @@ TEST(ExternalString_ExternalBackingStoreSizeIncreasesAfterExternalization) {
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
size_t old_backing_store_before = 0, new_backing_store_before = 0;
i::DisableConservativeStackScanningScopeForTesting no_stack_scanning(heap);
{
v8::HandleScope handle_scope(isolate);
@ -166,9 +172,9 @@ TEST(ExternalString_ExternalBackingStoreSizeIncreasesAfterExternalization) {
}
heap::GcAndSweep(heap, OLD_SPACE);
CHECK_EQ(0, heap->old_space()->ExternalBackingStoreBytes(type) -
old_backing_store_before);
const size_t backing_store_after =
heap->old_space()->ExternalBackingStoreBytes(type);
CHECK_EQ(0, backing_store_after - old_backing_store_before);
}
TEST(ExternalString_PromotedThinString) {

View File

@ -6873,27 +6873,33 @@ UNINITIALIZED_TEST(RestoreHeapLimit) {
reinterpret_cast<Isolate*>(v8::Isolate::New(create_params));
Heap* heap = isolate->heap();
Factory* factory = isolate->factory();
OutOfMemoryState state;
state.heap = heap;
state.oom_triggered = false;
heap->AddNearHeapLimitCallback(NearHeapLimitCallback, &state);
heap->AutomaticallyRestoreInitialHeapLimit(0.5);
const int kFixedArrayLength = 1000000;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
DisableConservativeStackScanningScopeForTesting no_stack_scanning(heap);
OutOfMemoryState state;
state.heap = heap;
state.oom_triggered = false;
heap->AddNearHeapLimitCallback(NearHeapLimitCallback, &state);
heap->AutomaticallyRestoreInitialHeapLimit(0.5);
const int kFixedArrayLength = 1000000;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
}
}
}
heap->MemoryPressureNotification(MemoryPressureLevel::kCritical, true);
state.oom_triggered = false;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
heap->MemoryPressureNotification(MemoryPressureLevel::kCritical, true);
state.oom_triggered = false;
{
HandleScope handle_scope(isolate);
while (!state.oom_triggered) {
factory->NewFixedArray(kFixedArrayLength);
}
}
CHECK_EQ(state.current_heap_limit, state.initial_heap_limit);
}
CHECK_EQ(state.current_heap_limit, state.initial_heap_limit);
reinterpret_cast<v8::Isolate*>(isolate)->Dispose();
}

View File

@ -29110,6 +29110,9 @@ TEST(TriggerMainThreadMetricsEvent) {
using v8::Local;
using v8::MaybeLocal;
i::DisableConservativeStackScanningScopeForTesting no_stack_scanning(
CcTest::heap());
// Set up isolate and context.
v8::Isolate* iso = CcTest::isolate();
i::Isolate* i_iso = reinterpret_cast<i::Isolate*>(iso);

View File

@ -86,8 +86,7 @@ TEST_F(ConservativeStackVisitorTest, DirectBasePointer) {
volatile Address ptr = recorder->base_address();
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(kNullAddress, ptr);
@ -108,8 +107,7 @@ TEST_F(ConservativeStackVisitorTest, TaggedBasePointer) {
volatile Address ptr = recorder->tagged_address();
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(kNullAddress, ptr);
@ -130,8 +128,7 @@ TEST_F(ConservativeStackVisitorTest, InnerPointer) {
volatile Address ptr = recorder->inner_address();
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(kNullAddress, ptr);
@ -154,8 +151,7 @@ TEST_F(ConservativeStackVisitorTest, HalfWord1) {
volatile uint32_t ptr[] = {recorder->compr_address(), 0};
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(static_cast<uint32_t>(0), ptr[0]);
@ -176,8 +172,7 @@ TEST_F(ConservativeStackVisitorTest, HalfWord2) {
volatile uint32_t ptr[] = {0, recorder->compr_address()};
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(static_cast<uint32_t>(0), ptr[1]);
@ -198,8 +193,7 @@ TEST_F(ConservativeStackVisitorTest, InnerHalfWord1) {
volatile uint32_t ptr[] = {recorder->compr_inner(), 0};
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(static_cast<uint32_t>(0), ptr[0]);
@ -220,8 +214,7 @@ TEST_F(ConservativeStackVisitorTest, InnerHalfWord2) {
volatile uint32_t ptr[] = {0, recorder->compr_inner()};
ConservativeStackVisitor stack_visitor(isolate(), recorder.get());
SaveStackContextScope stack_context_scope(&heap()->stack());
isolate()->heap()->stack().IteratePointers(&stack_visitor);
heap()->stack().IteratePointers(&stack_visitor);
// Make sure to keep the pointer alive.
EXPECT_NE(static_cast<uint32_t>(0), ptr[1]);

View File

@ -29,9 +29,7 @@ class MarkerTest : public testing::TestWithHeap {
const MarkingConfig config = {CollectionType::kMajor, stack_state};
auto* heap = Heap::From(GetHeap());
InitializeMarker(*heap, GetPlatformHandle().get(), config);
heap->stack()->SaveContext();
marker_->FinishMarking(stack_state);
heap->stack()->ClearContext();
// Pretend do finish sweeping as StatsCollector verifies that Notify*
// methods are called in the right order.
heap->stats_collector()->NotifySweepingCompleted(
@ -252,9 +250,7 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedEmptyStack) {
marker->Visitor().Trace(member);
});
EXPECT_FALSE(HeapObjectHeader::FromObject(object).IsMarked());
Heap::From(GetHeap())->stack()->SaveContext();
marker()->FinishMarking(StackState::kMayContainHeapPointers);
Heap::From(GetHeap())->stack()->ClearContext();
EXPECT_TRUE(HeapObjectHeader::FromObject(object).IsMarked());
}
@ -263,14 +259,11 @@ TEST_F(MarkerTest, InConstructionObjectIsEventuallyMarkedNonEmptyStack) {
StackState::kMayContainHeapPointers};
InitializeMarker(*Heap::From(GetHeap()), GetPlatformHandle().get(), config);
MakeGarbageCollected<GCedWithCallback>(
GetAllocationHandle(), [stack = Heap::From(GetHeap())->stack(),
marker = marker()](GCedWithCallback* obj) {
GetAllocationHandle(), [marker = marker()](GCedWithCallback* obj) {
Member<GCedWithCallback> member(obj);
marker->Visitor().Trace(member);
EXPECT_FALSE(HeapObjectHeader::FromObject(obj).IsMarked());
stack->SaveContext();
marker->FinishMarking(StackState::kMayContainHeapPointers);
stack->ClearContext();
EXPECT_TRUE(HeapObjectHeader::FromObject(obj).IsMarked());
});
}
@ -327,9 +320,7 @@ TEST_F(MarkerTest,
RegisterInConstructionObject(GetAllocationHandle(), marker()->Visitor(),
storage);
EXPECT_FALSE(HeapObjectHeader::FromObject(storage.object()).IsMarked());
Heap::From(GetHeap())->stack()->SaveContext();
marker()->FinishMarking(StackState::kMayContainHeapPointers);
Heap::From(GetHeap())->stack()->ClearContext();
EXPECT_TRUE(HeapObjectHeader::FromObject(storage.object()).IsMarked());
}
@ -409,9 +400,7 @@ class IncrementalMarkingTest : public testing::TestWithHeap {
}
void FinishMarking() {
Heap::From(GetHeap())->stack()->SaveContext();
GetMarkerRef()->FinishMarking(StackState::kMayContainHeapPointers);
Heap::From(GetHeap())->stack()->ClearContext();
// Pretend do finish sweeping as StatsCollector verifies that Notify*
// methods are called in the right order.
GetMarkerRef().reset();

View File

@ -23,10 +23,9 @@ class MarkingVerifierTest : public testing::TestWithHeap {
V8_NOINLINE void VerifyMarking(HeapBase& heap, StackState stack_state,
size_t expected_marked_bytes) {
Heap::From(GetHeap())->object_allocator().ResetLinearAllocationBuffers();
Heap::From(GetHeap())->stack()->SaveContext();
MarkingVerifier verifier(heap, CollectionType::kMajor);
verifier.Run(stack_state, expected_marked_bytes);
Heap::From(GetHeap())->stack()->ClearContext();
verifier.Run(stack_state, v8::base::Stack::GetCurrentStackPosition(),
expected_marked_bytes);
}
};

View File

@ -83,9 +83,7 @@ TEST_F(GCStackTest, IteratePointersFindsOnStackValue) {
{
int* volatile tmp = scanner->needle();
USE(tmp);
GetStack()->SaveContext();
GetStack()->IteratePointers(scanner.get());
GetStack()->ClearContext();
EXPECT_TRUE(scanner->found());
}
}
@ -100,9 +98,7 @@ TEST_F(GCStackTest, IteratePointersFindsOnStackValuePotentiallyUnaligned) {
USE(a);
int* volatile tmp = scanner->needle();
USE(tmp);
GetStack()->SaveContext();
GetStack()->IteratePointers(scanner.get());
GetStack()->ClearContext();
EXPECT_TRUE(scanner->found());
}
}
@ -147,9 +143,7 @@ V8_NOINLINE void* RecursivelyPassOnParameterImpl(void* p1, void* p2, void* p3,
nullptr, nullptr, nullptr, p7, stack,
visitor);
} else if (p8) {
stack->SaveContext();
stack->IteratePointers(visitor);
stack->ClearContext();
return p8;
}
return nullptr;
@ -160,9 +154,7 @@ V8_NOINLINE void* RecursivelyPassOnParameter(size_t num, void* parameter,
StackVisitor* visitor) {
switch (num) {
case 0:
stack->SaveContext();
stack->IteratePointers(visitor);
stack->ClearContext();
return parameter;
case 1:
return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
@ -298,9 +290,7 @@ extern "C" V8_NOINLINE
#endif // defined(__clang__)
void
IteratePointersNoMangling(Stack* stack, StackVisitor* visitor) {
stack->SaveContext();
stack->IteratePointers(visitor);
stack->ClearContext();
}
} // namespace
@ -478,9 +468,7 @@ class CheckStackAlignmentVisitor final : public StackVisitor {
TEST_F(GCStackTest, StackAlignment) {
auto checker = std::make_unique<CheckStackAlignmentVisitor>();
GetStack()->SaveContext();
GetStack()->IteratePointers(checker.get());
GetStack()->ClearContext();
}
#endif // V8_OS_LINUX && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64)

View File

@ -29,9 +29,7 @@ class WeakContainerTest : public testing::TestWithHeap {
}
void FinishMarking(StackState stack_state) {
Heap::From(GetHeap())->stack()->SaveContext();
GetMarkerRef()->FinishMarking(stack_state);
Heap::From(GetHeap())->stack()->ClearContext();
marked_bytes_ =
Heap::From(GetHeap())->AsBase().stats_collector()->marked_bytes();
GetMarkerRef().reset();

View File

@ -26,9 +26,7 @@ class V8_NODISCARD IncrementalMarkingScope {
explicit IncrementalMarkingScope(MarkerBase* marker) : marker_(marker) {}
~IncrementalMarkingScope() V8_NOEXCEPT {
marker_->heap().stack()->SaveContext();
marker_->FinishMarking(kIncrementalConfig.stack_state);
marker_->heap().stack()->ClearContext();
}
static constexpr MarkingConfig kIncrementalConfig{