Commit 6cd28b52 authored by Emanuel Ziegler's avatar Emanuel Ziegler Committed by Commit Bot

[wasm] Save FP & PC when calling C functions

Added implementations for ia32, arm, arm64.

mips/mips64 will be committed in separate CL once the build is green
again in order not to stall this CL with the supported architectures.

Drive-by: Fixed issues with kScratchRegister being overwritten in case of RegExp
compilation by using alternative temp register for x64.

Drive-by: Added missing NoRootArrayScope to ia32, arm and arm64 RegExp
macro assemblers.

R=clemensb@chromium.org
R=petermarshall@chromium.org
R=jgruber@chromium.org

Bug: chromium:1045860
Change-Id: I716d852b9bf780ae7b8d61376c6505dd3af96a50
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2071866
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66482}
parent 74895882
......@@ -2366,15 +2366,25 @@ void TurboAssembler::CallCFunctionHelper(Register function,
// Save the frame pointer and PC so that the stack layout remains iterable,
// even without an ExitFrame which normally exists between JS and C frames.
if (isolate() != nullptr) {
Register scratch = r4;
Push(scratch);
Register addr_scratch = r4;
// See x64 code for reasoning about how to address the isolate data fields.
if (root_array_available()) {
str(pc,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()));
str(fp,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
} else {
DCHECK_NOT_NULL(isolate());
Push(addr_scratch);
Move(addr_scratch,
ExternalReference::fast_c_call_caller_pc_address(isolate()));
str(pc, MemOperand(addr_scratch));
Move(addr_scratch,
ExternalReference::fast_c_call_caller_fp_address(isolate()));
str(fp, MemOperand(addr_scratch));
Move(scratch, ExternalReference::fast_c_call_caller_pc_address(isolate()));
str(pc, MemOperand(scratch));
Move(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate()));
str(fp, MemOperand(scratch));
Pop(scratch);
Pop(addr_scratch);
}
// Just call directly. The function called cannot cause a GC, or
......@@ -2382,19 +2392,25 @@ void TurboAssembler::CallCFunctionHelper(Register function,
// stays correct.
Call(function);
if (isolate() != nullptr) {
// We don't unset the PC; the FP is the source of truth.
Register scratch1 = r4;
Register scratch2 = r5;
Push(scratch1);
Push(scratch2);
Move(scratch1, ExternalReference::fast_c_call_caller_fp_address(isolate()));
mov(scratch2, Operand::Zero());
str(scratch2, MemOperand(scratch1));
Pop(scratch2);
Pop(scratch1);
// We don't unset the PC; the FP is the source of truth.
Register zero_scratch = r5;
Push(zero_scratch);
mov(zero_scratch, Operand::Zero());
if (root_array_available()) {
str(zero_scratch,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
} else {
DCHECK_NOT_NULL(isolate());
Push(addr_scratch);
Move(addr_scratch,
ExternalReference::fast_c_call_caller_fp_address(isolate()));
str(zero_scratch, MemOperand(addr_scratch));
Pop(addr_scratch);
}
Pop(zero_scratch);
int stack_passed_arguments =
CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
if (ActivationFrameAlignment() > kPointerSize) {
......
......@@ -1633,34 +1633,47 @@ void TurboAssembler::CallCFunction(Register function, int num_of_reg_args,
// Save the frame pointer and PC so that the stack layout remains iterable,
// even without an ExitFrame which normally exists between JS and C frames.
if (isolate() != nullptr) {
Register scratch1 = x4;
Register scratch2 = x5;
Push(scratch1, scratch2);
Label get_pc;
Bind(&get_pc);
Adr(scratch2, &get_pc);
Mov(scratch1, ExternalReference::fast_c_call_caller_pc_address(isolate()));
Str(scratch2, MemOperand(scratch1));
Mov(scratch1, ExternalReference::fast_c_call_caller_fp_address(isolate()));
Str(fp, MemOperand(scratch1));
Pop(scratch2, scratch1);
Register pc_scratch = x4;
Register addr_scratch = x5;
Push(pc_scratch, addr_scratch);
Label get_pc;
Bind(&get_pc);
Adr(pc_scratch, &get_pc);
// See x64 code for reasoning about how to address the isolate data fields.
if (root_array_available()) {
Str(pc_scratch,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()));
Str(fp,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
} else {
DCHECK_NOT_NULL(isolate());
Mov(addr_scratch,
ExternalReference::fast_c_call_caller_pc_address(isolate()));
Str(pc_scratch, MemOperand(addr_scratch));
Mov(addr_scratch,
ExternalReference::fast_c_call_caller_fp_address(isolate()));
Str(fp, MemOperand(addr_scratch));
}
Pop(addr_scratch, pc_scratch);
// Call directly. The function called cannot cause a GC, or allow preemption,
// so the return address in the link register stays correct.
Call(function);
if (isolate() != nullptr) {
// We don't unset the PC; the FP is the source of truth.
Register scratch = x4;
Push(scratch, xzr);
Mov(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate()));
Str(xzr, MemOperand(scratch));
Pop(xzr, scratch);
// We don't unset the PC; the FP is the source of truth.
if (root_array_available()) {
Str(xzr,
MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
} else {
DCHECK_NOT_NULL(isolate());
Push(addr_scratch, xzr);
Mov(addr_scratch,
ExternalReference::fast_c_call_caller_fp_address(isolate()));
Str(xzr, MemOperand(addr_scratch));
Pop(xzr, addr_scratch);
}
if (num_of_reg_args > kRegisterPassedArguments) {
......
......@@ -1764,32 +1764,38 @@ void TurboAssembler::CallCFunction(Register function, int num_arguments) {
// Save the frame pointer and PC so that the stack layout remains iterable,
// even without an ExitFrame which normally exists between JS and C frames.
if (isolate() != nullptr) {
// Find two caller-saved scratch registers.
Register scratch1 = eax;
Register scratch2 = ecx;
if (function == eax) scratch1 = edx;
if (function == ecx) scratch2 = edx;
PushPC();
pop(scratch1);
mov(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_pc_address(isolate()),
scratch2),
scratch1);
mov(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate()),
scratch2),
ebp);
}
// Find two caller-saved scratch registers.
Register pc_scratch = eax;
Register scratch = ecx;
if (function == eax) pc_scratch = edx;
if (function == ecx) scratch = edx;
PushPC();
pop(pc_scratch);
// See x64 code for reasoning about how to address the isolate data fields.
DCHECK_IMPLIES(!root_array_available(), isolate() != nullptr);
mov(root_array_available()
? Operand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset())
: ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_pc_address(isolate()),
scratch),
pc_scratch);
mov(root_array_available()
? Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset())
: ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate()),
scratch),
ebp);
call(function);
if (isolate() != nullptr) {
// We don't unset the PC; the FP is the source of truth.
mov(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate()), edx),
Immediate(0));
}
// We don't unset the PC; the FP is the source of truth.
mov(root_array_available()
? Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset())
: ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate()),
scratch),
Immediate(0));
if (base::OS::ActivationFrameAlignment() != 0) {
mov(esp, Operand(esp, num_arguments * kSystemPointerSize));
......
......@@ -2651,35 +2651,60 @@ void TurboAssembler::CallCFunction(Register function, int num_arguments) {
// Save the frame pointer and PC so that the stack layout remains iterable,
// even without an ExitFrame which normally exists between JS and C frames.
if (isolate() != nullptr || root_array_available_) {
Label get_pc;
DCHECK(!AreAliased(kScratchRegister, function));
leaq(kScratchRegister, Operand(&get_pc, 0));
bind(&get_pc);
movq(isolate() != nullptr
? ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_pc_address(isolate()))
: Operand(kRootRegister,
IsolateData::fast_c_call_caller_pc_offset()),
Label get_pc;
DCHECK(!AreAliased(kScratchRegister, function));
leaq(kScratchRegister, Operand(&get_pc, 0));
bind(&get_pc);
// Addressing the following external references is tricky because we need
// this to work in three situations:
// 1. In wasm compilation, the isolate is nullptr and thus no
// ExternalReference can be created, but we can construct the address
// directly using the root register and a static offset.
// 2. In normal JIT (and builtin) compilation, the external reference is
// usually addressed through the root register, so we can use the direct
// offset directly in most cases.
// 3. In regexp compilation, the external reference is embedded into the reloc
// info.
// The solution here is to use root register offsets wherever possible in
// which case we can construct it directly. When falling back to external
// references we need to ensure that the scratch register does not get
// accidentally overwritten. If we run into more such cases in the future, we
// should implement a more general solution.
if (root_array_available()) {
movq(Operand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()),
kScratchRegister);
movq(isolate() != nullptr
? ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate()))
: Operand(kRootRegister,
IsolateData::fast_c_call_caller_fp_offset()),
movq(Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()),
rbp);
} else {
DCHECK_NOT_NULL(isolate());
// Use alternative scratch register in order not to overwrite
// kScratchRegister.
Register scratch = r12;
pushq(scratch);
movq(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_pc_address(isolate()),
scratch),
kScratchRegister);
movq(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate())),
rbp);
popq(scratch);
}
call(function);
// We don't unset the PC; the FP is the source of truth.
if (isolate() != nullptr) {
if (root_array_available()) {
movq(Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()),
Immediate(0));
} else {
DCHECK_NOT_NULL(isolate());
movq(ExternalReferenceAsOperand(
ExternalReference::fast_c_call_caller_fp_address(isolate())),
Immediate(0));
} else if (root_array_available_) {
movq(Operand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()),
Immediate(0));
}
DCHECK_NE(base::OS::ActivationFrameAlignment(), 0);
......
......@@ -102,6 +102,7 @@ RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(Isolate* isolate, Zone* zone,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
......
......@@ -189,6 +189,7 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (Latin1 or UC16).
Mode mode_;
......
......@@ -112,6 +112,7 @@ RegExpMacroAssemblerARM64::RegExpMacroAssemblerARM64(Isolate* isolate,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
......
......@@ -264,6 +264,7 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerARM64
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (LATIN1 or UC16).
Mode mode_;
......
......@@ -90,6 +90,7 @@ RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(Isolate* isolate, Zone* zone,
: NativeRegExpMacroAssembler(isolate, zone),
masm_(new MacroAssembler(isolate, CodeObjectRequired::kYes,
NewAssemblerBuffer(kRegExpCodeSize))),
no_root_array_scope_(masm_),
mode_(mode),
num_registers_(registers_to_save),
num_saved_registers_(registers_to_save),
......
......@@ -178,6 +178,7 @@ class V8_EXPORT_PRIVATE RegExpMacroAssemblerIA32
Isolate* isolate() const { return masm_->isolate(); }
MacroAssembler* masm_;
NoRootArrayScope no_root_array_scope_;
// Which mode to generate code for (LATIN1 or UC16).
Mode mode_;
......
......@@ -32,14 +32,15 @@ class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer->start());
......@@ -49,8 +50,9 @@ TEST_F(TurboAssemblerTest, TestHardAbort) {
TEST_F(TurboAssemblerTest, TestCheck) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
......@@ -60,7 +62,7 @@ TEST_F(TurboAssemblerTest, TestCheck) {
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer->start());
......
......@@ -32,14 +32,15 @@ class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer->start());
......@@ -49,8 +50,9 @@ TEST_F(TurboAssemblerTest, TestHardAbort) {
TEST_F(TurboAssemblerTest, TestCheck) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
......@@ -60,7 +62,7 @@ TEST_F(TurboAssemblerTest, TestCheck) {
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer->start());
......
......@@ -5,6 +5,7 @@
#include "src/codegen/macro-assembler.h"
#include "src/execution/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
......@@ -16,26 +17,30 @@ namespace internal {
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
TEST(TurboAssemblerTest, TestHardAbort) {
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
auto f = GeneratedCode<void>::FromBuffer(nullptr, buffer->start());
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer->start());
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST(TurboAssemblerTest, TestCheck) {
TEST_F(TurboAssemblerTest, TestCheck) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
......@@ -45,9 +50,9 @@ TEST(TurboAssemblerTest, TestCheck) {
__ ret(0);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
auto f = GeneratedCode<void, int>::FromBuffer(nullptr, buffer->start());
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer->start());
f.Call(0);
f.Call(18);
......
......@@ -5,6 +5,7 @@
#include "src/codegen/macro-assembler.h"
#include "src/execution/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
......@@ -16,26 +17,30 @@ namespace internal {
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
TEST(TurboAssemblerTest, TestHardAbort) {
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
auto f = GeneratedCode<void>::FromBuffer(nullptr, buffer->start());
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer->start());
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST(TurboAssemblerTest, TestCheck) {
TEST_F(TurboAssemblerTest, TestCheck) {
auto buffer = AllocateAssemblerBuffer();
TurboAssembler tasm(nullptr, AssemblerOptions{}, CodeObjectRequired::kNo,
TurboAssembler tasm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
__ set_root_array_available(false);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
......@@ -45,9 +50,9 @@ TEST(TurboAssemblerTest, TestCheck) {
__ ret(0);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
tasm.GetCode(isolate(), &desc);
buffer->MakeExecutable();
auto f = GeneratedCode<void, int>::FromBuffer(nullptr, buffer->start());
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer->start());
f.Call(0);
f.Call(18);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment