[turboassembler] Introduce hard-abort mode

For checks and assertions (mostly for debug code, like stack alignment
or zero extension), we had two modes: Emit a call to the {Abort}
runtime function (the default), and emit a debug break (used for
testing, enabled via --trap-on-abort).
In wasm, where we cannot just call a runtime function because code must
be isolate independent, we always used the trap-on-abort behaviour.
This causes problems for our fuzzers, which do not catch SIGTRAP, and
hence do not detect debug code failures.

This CL introduces a third mode ("hard abort"), which calls a C
function via {ExternalReference}. The C function still outputs the
abort reason, but does not print the stack trace. It then aborts via
"OS::Abort", just like the runtime function.
This will allow fuzzers to detect the crash and even find a nice error
message.

Even though this looks like a lot of code churn, it is actually not.
Most added lines are new tests, and other changes are minimal.

R=mstarzinger@chromium.org

Bug: chromium:863799
Change-Id: I77c58ff72db552d49014614436259ccfb49ba87b
Reviewed-on: https://chromium-review.googlesource.com/1142163
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54592}
This commit is contained in:
Clemens Hammacher 2018-07-20 16:00:30 +02:00 committed by Commit Bot
parent bced36d203
commit a462a7854a
56 changed files with 616 additions and 137 deletions

View File

@ -612,7 +612,7 @@ struct VmovIndex {
constexpr VmovIndex VmovIndexLo = { 0 };
constexpr VmovIndex VmovIndexHi = { 1 };
class Assembler : public AssemblerBase {
class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
public:
// Create an assembler. Instructions and relocation information are emitted
// into a buffer, with the instructions starting from the beginning and the

View File

@ -1881,6 +1881,16 @@ void TurboAssembler::Abort(AbortReason reason) {
return;
}
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
Move32BitImmediate(r0, Operand(static_cast<int>(reason)));
PrepareCallCFunction(0, 0, r1);
Move(r1, ExternalReference::abort_with_reason());
Call(r1);
return;
}
Move(r1, Smi::FromInt(static_cast<int>(reason)));
// Disable stub call restrictions to always allow calls to abort.
@ -2249,13 +2259,14 @@ int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments,
}
void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
int num_double_arguments) {
int num_double_arguments,
Register scratch) {
int frame_alignment = ActivationFrameAlignment();
int stack_passed_arguments = CalculateStackPassedWords(
num_reg_arguments, num_double_arguments);
if (frame_alignment > kPointerSize) {
UseScratchRegisterScope temps(this);
Register scratch = temps.Acquire();
if (!scratch.is_valid()) scratch = temps.Acquire();
// Make stack end at alignment and make room for num_arguments - 4 words
// and the original value of sp.
mov(scratch, sp);
@ -2263,7 +2274,7 @@ void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
and_(sp, sp, Operand(-frame_alignment));
str(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
} else {
} else if (stack_passed_arguments > 0) {
sub(sp, sp, Operand(stack_passed_arguments * kPointerSize));
}
}

View File

@ -90,7 +90,7 @@ enum TargetAddressStorageMode {
NEVER_INLINE_TARGET_ADDRESS
};
class TurboAssembler : public TurboAssemblerBase {
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
public:
TurboAssembler(Isolate* isolate, const AssemblerOptions& options,
void* buffer, int buffer_size,
@ -259,8 +259,8 @@ class TurboAssembler : public TurboAssemblerBase {
// C++ code.
// Needs a scratch register to do some arithmetic. This register will be
// trashed.
void PrepareCallCFunction(int num_reg_arguments,
int num_double_registers = 0);
void PrepareCallCFunction(int num_reg_arguments, int num_double_registers = 0,
Register scratch = no_reg);
// Removes current frame and its arguments from the stack preserving
// the arguments and a return address pushed to the stack for the next call.

View File

@ -882,7 +882,7 @@ class ConstPool {
// -----------------------------------------------------------------------------
// Assembler.
class Assembler : public AssemblerBase {
class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
public:
// Create an assembler. Instructions and relocation information are emitted
// into a buffer, with the instructions starting from the beginning and the

View File

@ -249,7 +249,7 @@ void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm);
HardAbortScope hard_aborts(masm);
// Save all kCallerSaved registers (including lr), since this can be called
// from anywhere.

View File

@ -1429,7 +1429,7 @@ void MacroAssembler::PopCalleeSavedRegisters() {
void TurboAssembler::AssertSpAligned() {
if (emit_debug_code()) {
TrapOnAbortScope trap_on_abort_scope(this); // Avoid calls to Abort.
HardAbortScope hard_abort(this); // Avoid calls to Abort.
// Arm64 requires the stack pointer to be 16-byte aligned prior to address
// calculation.
UseScratchRegisterScope scope(this);
@ -2998,9 +2998,16 @@ void TurboAssembler::Abort(AbortReason reason) {
RegList old_tmp_list = TmpList()->list();
TmpList()->Combine(MacroAssembler::DefaultTmpList());
if (use_real_aborts()) {
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
Mov(w0, static_cast<int>(reason));
Call(ExternalReference::abort_with_reason());
return;
}
// Avoid infinite recursion; Push contains some assertions that use Abort.
NoUseRealAbortsScope no_real_aborts(this);
HardAbortScope hard_aborts(this);
Move(x1, Smi::FromInt(static_cast<int>(reason)));
@ -3012,25 +3019,6 @@ void TurboAssembler::Abort(AbortReason reason) {
} else {
Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
}
} else {
// Load the string to pass to Printf.
Label msg_address;
Adr(x0, &msg_address);
// Call Printf directly to report the error.
CallPrintf();
// We need a way to stop execution on both the simulator and real hardware,
// and Unreachable() is the best option.
Unreachable();
// Emit the message string directly in the instruction stream.
{
BlockPoolsScope scope(this);
Bind(&msg_address);
EmitStringData(GetAbortReason(reason));
}
}
TmpList()->set_list(old_tmp_list);
}

View File

@ -177,7 +177,7 @@ enum PreShiftImmMode {
kAnyShift // Allow any pre-shift.
};
class TurboAssembler : public TurboAssemblerBase {
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
public:
TurboAssembler(Isolate* isolate, const AssemblerOptions& options,
void* buffer, int buffer_size,
@ -185,27 +185,6 @@ class TurboAssembler : public TurboAssemblerBase {
: TurboAssemblerBase(isolate, options, buffer, buffer_size,
create_code_object) {}
// The Abort method should call a V8 runtime function, but the CallRuntime
// mechanism depends on CEntry. If use_real_aborts is false, Abort will
// use a simpler abort mechanism that doesn't depend on CEntry.
//
// The purpose of this is to allow Aborts to be compiled whilst CEntry is
// being generated.
bool use_real_aborts() const { return use_real_aborts_; }
class NoUseRealAbortsScope {
public:
explicit NoUseRealAbortsScope(TurboAssembler* tasm)
: saved_(tasm->use_real_aborts_), tasm_(tasm) {
tasm_->use_real_aborts_ = false;
}
~NoUseRealAbortsScope() { tasm_->use_real_aborts_ = saved_; }
private:
bool saved_;
TurboAssembler* tasm_;
};
#if DEBUG
void set_allow_macro_instructions(bool value) {
allow_macro_instructions_ = value;
@ -1267,8 +1246,6 @@ class TurboAssembler : public TurboAssemblerBase {
CPURegList tmp_list_ = DefaultTmpList();
CPURegList fptmp_list_ = DefaultFPTmpList();
bool use_real_aborts_ = true;
// Helps resolve branching to labels potentially out of range.
// If the label is not bound, it registers the information necessary to later
// be able to emit a veneer for this branch if necessary.

View File

@ -167,7 +167,7 @@ struct V8_EXPORT_PRIVATE AssemblerOptions {
Isolate* isolate, bool explicitly_support_serialization = false);
};
class AssemblerBase : public Malloced {
class V8_EXPORT_PRIVATE AssemblerBase : public Malloced {
public:
AssemblerBase(const AssemblerOptions& options, void* buffer, int buffer_size);
virtual ~AssemblerBase();

View File

@ -26,6 +26,11 @@ const char* GetAbortReason(AbortReason reason) {
return error_messages_[static_cast<int>(reason)];
}
bool IsValidAbortReason(int reason_id) {
return reason_id >= static_cast<int>(AbortReason::kNoReason) &&
reason_id < static_cast<int>(AbortReason::kLastErrorMessage);
}
#undef ERROR_MESSAGES_TEXTS
} // namespace internal
} // namespace v8

View File

@ -125,6 +125,7 @@ enum class AbortReason {
const char* GetBailoutReason(BailoutReason reason);
const char* GetAbortReason(AbortReason reason);
bool IsValidAbortReason(int reason_id);
} // namespace internal
} // namespace v8

View File

@ -2218,7 +2218,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(r4, r4);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameAndConstantPoolScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be
@ -2415,7 +2415,7 @@ void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size,
void Builtins::Generate_DoubleToI(MacroAssembler* masm) {
Label negate, done;
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
UseScratchRegisterScope temps(masm);
Register result_reg = r7;
Register double_low = GetRegisterThatIsNotOneOf(result_reg);

View File

@ -2676,7 +2676,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
__ sxtw(x8, w8);
__ SmiTag(x8, x8);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be
@ -2718,7 +2718,7 @@ void Builtins::Generate_CEntry(MacroAssembler* masm, int result_size,
// fall-back Abort mechanism.
//
// Note that this stub must be generated before any use of Abort.
MacroAssembler::NoUseRealAbortsScope no_use_real_aborts(masm);
HardAbortScope hard_aborts(masm);
ASM_LOCATION("CEntry::Generate entry");
ProfileEntryHookStub::MaybeCallEntryHook(masm);
@ -2938,7 +2938,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) {
DCHECK(result.Is64Bits());
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
UseScratchRegisterScope temps(masm);
Register scratch1 = temps.AcquireX();
Register scratch2 = temps.AcquireX();

View File

@ -2384,7 +2384,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(edi);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be

View File

@ -2272,7 +2272,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(t0);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be

View File

@ -2289,7 +2289,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call
__ SmiTag(t0);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be

View File

@ -2300,7 +2300,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(r15, r15);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameAndConstantPoolScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be
@ -2531,7 +2531,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) {
Label out_of_range, only_low, negate, done, fastpath_done;
Register result_reg = r3;
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
// Immediate values for this stub fit in instructions, so it's safe to use ip.
Register scratch = GetRegisterThatIsNotOneOf(result_reg);

View File

@ -2305,7 +2305,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(r7, r7);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameAndConstantPoolScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be
@ -2525,7 +2525,7 @@ void Builtins::Generate_DoubleToI(MacroAssembler* masm) {
Label out_of_range, only_low, negate, done, fastpath_done;
Register result_reg = r2;
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
// Immediate values for this stub fit in instructions, so it's safe to use ip.
Register scratch = GetRegisterThatIsNotOneOf(result_reg);

View File

@ -2339,7 +2339,7 @@ void Builtins::Generate_WasmCompileLazy(MacroAssembler* masm) {
// Convert to Smi for the runtime call.
__ SmiTag(r11, r11);
{
TrapOnAbortScope trap_on_abort_scope(masm); // Avoid calls to Abort.
HardAbortScope hard_abort(masm); // Avoid calls to Abort.
FrameScope scope(masm, StackFrame::WASM_COMPILE_LAZY);
// Save all parameter registers (see wasm-linkage.cc). They might be

View File

@ -90,7 +90,7 @@ CodeGenerator::CodeGenerator(
if (code_kind == Code::WASM_FUNCTION ||
code_kind == Code::WASM_TO_JS_FUNCTION ||
code_kind == Code::WASM_INTERPRETER_ENTRY) {
tasm_.set_trap_on_abort(true);
tasm_.set_abort_hard(true);
}
tasm_.set_builtin_index(builtin_index);
}

View File

@ -459,6 +459,10 @@ ExternalReference ExternalReference::address_of_pending_message_obj(
return ExternalReference(isolate->pending_message_obj_address());
}
ExternalReference ExternalReference::abort_with_reason() {
return ExternalReference(Redirect(FUNCTION_ADDR(i::abort_with_reason)));
}
ExternalReference ExternalReference::address_of_min_int() {
return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant));
}
@ -951,5 +955,16 @@ std::ostream& operator<<(std::ostream& os, ExternalReference reference) {
return os;
}
void abort_with_reason(int reason) {
if (IsValidAbortReason(reason)) {
const char* message = GetAbortReason(static_cast<AbortReason>(reason));
base::OS::PrintError("abort: %s\n", message);
} else {
base::OS::PrintError("abort: <unknown reason: %d>\n", reason);
}
base::OS::Abort();
UNREACHABLE();
}
} // namespace internal
} // namespace v8

View File

@ -71,6 +71,7 @@ class StatsCounter;
EXTERNAL_REFERENCE_LIST_NON_INTERPRETED_REGEXP(V)
#define EXTERNAL_REFERENCE_LIST(V) \
V(abort_with_reason, "abort_with_reason") \
V(address_of_double_abs_constant, "double_absolute_constant") \
V(address_of_double_neg_constant, "double_negate_constant") \
V(address_of_float_abs_constant, "float_absolute_constant") \
@ -301,6 +302,8 @@ size_t hash_value(ExternalReference);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&, ExternalReference);
void abort_with_reason(int reason);
} // namespace internal
} // namespace v8

View File

@ -321,7 +321,7 @@ enum ScaleFactor {
times_twice_pointer_size = times_8
};
class Operand {
class V8_EXPORT_PRIVATE Operand {
public:
// reg
V8_INLINE explicit Operand(Register reg) { set_modrm(3, reg); }
@ -472,8 +472,7 @@ class Displacement BASE_EMBEDDED {
void init(Label* L, Type type);
};
class Assembler : public AssemblerBase {
class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
private:
// We check before assembling an instruction that there is sufficient
// space to write an instruction and its relocation information.

View File

@ -1591,6 +1591,15 @@ void TurboAssembler::Abort(AbortReason reason) {
return;
}
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
PrepareCallCFunction(1, eax);
mov(Operand(esp, 0), Immediate(static_cast<int>(reason)));
CallCFunction(ExternalReference::abort_with_reason(), 1);
return;
}
Move(edx, Smi::FromInt(static_cast<int>(reason)));
// Disable stub call restrictions to always allow calls to abort.

View File

@ -54,7 +54,7 @@ bool AreAliased(Register reg1, Register reg2, Register reg3 = no_reg,
Register reg8 = no_reg);
#endif
class TurboAssembler : public TurboAssemblerBase {
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
public:
TurboAssembler(Isolate* isolate, const AssemblerOptions& options,
void* buffer, int buffer_size,

View File

@ -4695,6 +4695,15 @@ void TurboAssembler::Abort(AbortReason reason) {
return;
}
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
PrepareCallCFunction(0, a0);
li(a0, Operand(static_cast<int>(reason)));
CallCFunction(ExternalReference::abort_with_reason(), 1);
return;
}
Move(a0, Smi::FromInt(static_cast<int>(reason)));
// Disable stub call restrictions to always allow calls to abort.

View File

@ -5024,6 +5024,15 @@ void TurboAssembler::Abort(AbortReason reason) {
return;
}
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
PrepareCallCFunction(0, a0);
li(a0, Operand(static_cast<int>(reason)));
CallCFunction(ExternalReference::abort_with_reason(), 1);
return;
}
Move(a0, Smi::FromInt(static_cast<int>(reason)));
// Disable stub call restrictions to always allow calls to abort.

View File

@ -1757,7 +1757,9 @@ void TurboAssembler::Abort(AbortReason reason) {
#endif
// Avoid emitting call to builtin if requested.
if (trap_on_abort()) {
if (trap_on_abort() || should_abort_hard()) {
// TODO(ppc): Call {ExternalReference::abort_with_reason} if
// {should_abort_hard} is set.
stop(msg);
return;
}

View File

@ -1688,7 +1688,9 @@ void TurboAssembler::Abort(AbortReason reason) {
#endif
// Avoid emitting call to builtin if requested.
if (trap_on_abort()) {
if (trap_on_abort() || should_abort_hard()) {
// TODO(s390): Call {ExternalReference::abort_with_reason} if
// {should_abort_hard} is set.
stop(msg);
return;
}

View File

@ -13,7 +13,7 @@ namespace internal {
// Common base class for platform-specific TurboAssemblers containing
// platform-independent bits.
class TurboAssemblerBase : public Assembler {
class V8_EXPORT_PRIVATE TurboAssemblerBase : public Assembler {
public:
Isolate* isolate() const { return isolate_; }
@ -26,7 +26,9 @@ class TurboAssemblerBase : public Assembler {
void set_root_array_available(bool v) { root_array_available_ = v; }
bool trap_on_abort() const { return trap_on_abort_; }
void set_trap_on_abort(bool v) { trap_on_abort_ = v; }
bool should_abort_hard() const { return hard_abort_; }
void set_abort_hard(bool v) { hard_abort_ = v; }
void set_builtin_index(int i) { maybe_builtin_index_ = i; }
@ -78,6 +80,9 @@ class TurboAssemblerBase : public Assembler {
// Immediately trap instead of calling {Abort} when debug code fails.
bool trap_on_abort_ = FLAG_trap_on_abort;
// Emit a C call to abort instead of a runtime call.
bool hard_abort_ = false;
// May be set while generating builtins.
int maybe_builtin_index_ = Builtins::kNoBuiltinId;
@ -89,13 +94,13 @@ class TurboAssemblerBase : public Assembler {
// Avoids emitting calls to the {Builtins::kAbort} builtin when emitting debug
// code during the lifetime of this scope object. For disabling debug code
// entirely use the {DontEmitDebugCodeScope} instead.
class TrapOnAbortScope BASE_EMBEDDED {
class HardAbortScope BASE_EMBEDDED {
public:
explicit TrapOnAbortScope(TurboAssemblerBase* assembler)
: assembler_(assembler), old_value_(assembler->trap_on_abort()) {
assembler_->set_trap_on_abort(true);
explicit HardAbortScope(TurboAssemblerBase* assembler)
: assembler_(assembler), old_value_(assembler->should_abort_hard()) {
assembler_->set_abort_hard(true);
}
~TrapOnAbortScope() { assembler_->set_trap_on_abort(old_value_); }
~HardAbortScope() { assembler_->set_abort_hard(old_value_); }
private:
TurboAssemblerBase* assembler_;

View File

@ -349,7 +349,7 @@ constexpr AssemblerOptions DefaultLiftoffOptions() {
LiftoffAssembler::LiftoffAssembler()
: TurboAssembler(nullptr, DefaultLiftoffOptions(), nullptr, 0,
CodeObjectRequired::kNo) {
set_trap_on_abort(true); // Avoid calls to Abort.
set_abort_hard(true); // Avoid calls to Abort.
}
LiftoffAssembler::~LiftoffAssembler() {

View File

@ -1380,14 +1380,8 @@ void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
}
DCHECK_LE(arg_bytes, stack_bytes);
// Pass a pointer to the buffer with the arguments to the C function.
// On win64, the first argument is in {rcx}, otherwise it is {rdi}.
#ifdef _WIN64
constexpr Register kFirstArgReg = rcx;
#else
constexpr Register kFirstArgReg = rdi;
#endif
movp(kFirstArgReg, rsp);
// Pass a pointer to the buffer with the arguments to the C function.
movp(arg_reg_1, rsp);
constexpr int kNumCCallArgs = 1;

View File

@ -422,7 +422,7 @@ static_assert(sizeof(Operand) <= 2 * kPointerSize,
V(shr, 0x5) \
V(sar, 0x7)
class Assembler : public AssemblerBase {
class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
private:
// We check before assembling an instruction that there is sufficient
// space to write an instruction and its relocation information.

View File

@ -427,6 +427,16 @@ void TurboAssembler::Abort(AbortReason reason) {
return;
}
if (should_abort_hard()) {
// We don't care if we constructed a frame. Just pretend we did.
FrameScope assume_frame(this, StackFrame::NONE);
movl(arg_reg_1, Immediate(static_cast<int>(reason)));
PrepareCallCFunction(1);
LoadAddress(rax, ExternalReference::abort_with_reason());
call(rax);
return;
}
Move(rdx, Smi::FromInt(static_cast<int>(reason)));
if (!has_frame()) {

View File

@ -125,7 +125,7 @@ class StackArgumentsAccessor BASE_EMBEDDED {
DISALLOW_IMPLICIT_CONSTRUCTORS(StackArgumentsAccessor);
};
class TurboAssembler : public TurboAssemblerBase {
class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase {
public:
TurboAssembler(Isolate* isolate, const AssemblerOptions& options,
void* buffer, int buffer_size,

View File

@ -50,9 +50,10 @@ v8_source_set("cctest_sources") {
"$target_gen_dir/resources.cc",
### gcmole(all) ###
"../../test/common/wasm/flag-utils.h",
"../../test/common/wasm/test-signatures.h",
"../../test/common/wasm/wasm-macro-gen.h",
"../common/assembler-tester.h",
"../common/wasm/flag-utils.h",
"../common/wasm/test-signatures.h",
"../common/wasm/wasm-macro-gen.h",
"cctest.cc",
"cctest.h",
"compiler/c-signature.h",

View File

@ -548,32 +548,6 @@ static inline void CheckDoubleEquals(double expected, double actual) {
CHECK_GE(expected, actual - kEpsilon);
}
static inline uint8_t* AllocateAssemblerBuffer(
size_t* allocated,
size_t requested = v8::internal::AssemblerBase::kMinimalBufferSize) {
size_t page_size = v8::internal::AllocatePageSize();
size_t alloc_size = RoundUp(requested, page_size);
void* result = v8::internal::AllocatePages(
nullptr, alloc_size, page_size, v8::PageAllocator::kReadWriteExecute);
CHECK(result);
*allocated = alloc_size;
return static_cast<uint8_t*>(result);
}
static inline void MakeAssemblerBufferExecutable(uint8_t* buffer,
size_t allocated) {
bool result = v8::internal::SetPermissions(buffer, allocated,
v8::PageAllocator::kReadExecute);
CHECK(result);
}
static inline void MakeAssemblerBufferWritable(uint8_t* buffer,
size_t allocated) {
bool result = v8::internal::SetPermissions(buffer, allocated,
v8::PageAllocator::kReadWrite);
CHECK(result);
}
static v8::debug::DebugDelegate dummy_delegate;
static inline void EnableDebugger(v8::Isolate* isolate) {

View File

@ -45,6 +45,7 @@
#include "src/macro-assembler.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-utils-arm64.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -39,6 +39,7 @@
#include "src/ostreams.h"
#include "src/simulator.h"
#include "test/cctest/cctest.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -37,6 +37,7 @@
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -37,6 +37,7 @@
#include "src/simulator.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -38,6 +38,7 @@
#include "src/objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -39,6 +39,7 @@
#include "src/simulator.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -39,6 +39,7 @@
#include "src/simulator.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -37,6 +37,7 @@
#include "src/register-configuration.h"
#include "test/cctest/cctest.h"
#include "test/cctest/test-code-stubs.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -11,6 +11,7 @@
#include "src/simulator.h"
#include "src/snapshot/macros.h"
#include "src/snapshot/snapshot.h"
#include "test/common/assembler-tester.h"
// To generate the binary files for the test function, enable this section and
// run GenerateTestFunctionData once on each arch.

View File

@ -33,6 +33,7 @@
#include "src/simulator.h"
#include "src/v8.h"
#include "test/cctest/cctest.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -35,6 +35,7 @@
#include "src/objects-inl.h"
#include "src/simulator.h"
#include "test/cctest/cctest.h"
#include "test/common/assembler-tester.h"
namespace v8 {
namespace internal {

View File

@ -0,0 +1,42 @@
// Copyright 2018 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.
#ifndef V8_TEST_COMMON_ASSEMBLER_TESTER_H_
#define V8_TEST_COMMON_ASSEMBLER_TESTER_H_
#include "src/assembler.h"
namespace v8 {
namespace internal {
static inline uint8_t* AllocateAssemblerBuffer(
size_t* allocated,
size_t requested = v8::internal::AssemblerBase::kMinimalBufferSize) {
size_t page_size = v8::internal::AllocatePageSize();
size_t alloc_size = RoundUp(requested, page_size);
void* result = v8::internal::AllocatePages(
nullptr, alloc_size, page_size, v8::PageAllocator::kReadWriteExecute);
CHECK(result);
*allocated = alloc_size;
return static_cast<uint8_t*>(result);
}
static inline void MakeAssemblerBufferExecutable(uint8_t* buffer,
size_t allocated) {
bool result = v8::internal::SetPermissions(buffer, allocated,
v8::PageAllocator::kReadExecute);
CHECK(result);
}
static inline void MakeAssemblerBufferWritable(uint8_t* buffer,
size_t allocated) {
bool result = v8::internal::SetPermissions(buffer, allocated,
v8::PageAllocator::kReadWrite);
CHECK(result);
}
} // namespace internal
} // namespace v8
#endif // V8_TEST_COMMON_ASSEMBLER_TESTER_H_

View File

@ -41,6 +41,7 @@ v8_source_set("unittests_sources") {
testonly = true
sources = [
"../../test/common/assembler-tester.h",
"../../test/common/wasm/wasm-macro-gen.h",
"../../testing/gmock-support.h",
"../../testing/gtest-support.h",
@ -222,17 +223,35 @@ v8_source_set("unittests_sources") {
}
if (v8_current_cpu == "arm") {
sources += [ "compiler/arm/instruction-selector-arm-unittest.cc" ]
sources += [
"assembler/turbo-assembler-arm-unittest.cc",
"compiler/arm/instruction-selector-arm-unittest.cc",
]
} else if (v8_current_cpu == "arm64") {
sources += [ "compiler/arm64/instruction-selector-arm64-unittest.cc" ]
sources += [
"assembler/turbo-assembler-arm64-unittest.cc",
"compiler/arm64/instruction-selector-arm64-unittest.cc",
]
} else if (v8_current_cpu == "x86") {
sources += [ "compiler/ia32/instruction-selector-ia32-unittest.cc" ]
sources += [
"assembler/turbo-assembler-ia32-unittest.cc",
"compiler/ia32/instruction-selector-ia32-unittest.cc",
]
} else if (v8_current_cpu == "mips" || v8_current_cpu == "mipsel") {
sources += [ "compiler/mips/instruction-selector-mips-unittest.cc" ]
sources += [
"assembler/turbo-assembler-mips-unittest.cc",
"compiler/mips/instruction-selector-mips-unittest.cc",
]
} else if (v8_current_cpu == "mips64" || v8_current_cpu == "mips64el") {
sources += [ "compiler/mips64/instruction-selector-mips64-unittest.cc" ]
sources += [
"assembler/turbo-assembler-mips64-unittest.cc",
"compiler/mips64/instruction-selector-mips64-unittest.cc",
]
} else if (v8_current_cpu == "x64") {
sources += [ "compiler/x64/instruction-selector-x64-unittest.cc" ]
sources += [
"assembler/turbo-assembler-x64-unittest.cc",
"compiler/x64/instruction-selector-x64-unittest.cc",
]
} else if (v8_current_cpu == "ppc" || v8_current_cpu == "ppc64") {
sources += [ "compiler/ppc/instruction-selector-ppc-unittest.cc" ]
} else if (v8_current_cpu == "s390" || v8_current_cpu == "s390x") {

View File

@ -0,0 +1,68 @@
// Copyright 2018 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/arm/assembler-arm-inl.h"
#include "src/macro-assembler.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST_F(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
__ Move32BitImmediate(r1, Operand(17));
__ cmp(r0, r1); // 1st parameter is in {r0}.
__ Check(Condition::ne, AbortReason::kNoReason);
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,68 @@
// Copyright 2018 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/arm64/macro-assembler-arm64-inl.h"
#include "src/macro-assembler.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST_F(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
__ Mov(w1, Immediate(17));
__ Cmp(w0, w1); // 1st parameter is in {w0}.
__ Check(Condition::ne, AbortReason::kNoReason);
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,62 @@
// Copyright 2018 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/macro-assembler.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
TEST(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
auto f = GeneratedCode<void>::FromBuffer(nullptr, buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
__ mov(eax, 17);
__ cmp(eax, Operand(esp, 4)); // compare with 1st parameter.
__ Check(Condition::not_equal, AbortReason::kNoReason);
__ ret(0);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
auto f = GeneratedCode<void, int>::FromBuffer(nullptr, buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,66 @@
// Copyright 2018 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/macro-assembler.h"
#include "src/mips/assembler-mips-inl.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST_F(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter (in {a0}) is 17.
__ Check(Condition::ne, AbortReason::kNoReason, a0, Operand(17));
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,66 @@
// Copyright 2018 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/macro-assembler.h"
#include "src/mips64/assembler-mips64-inl.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
class TurboAssemblerTest : public TestWithIsolate {};
TEST_F(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void>::FromBuffer(isolate(), buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST_F(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter (in {a0}) is 17.
__ Check(Condition::ne, AbortReason::kNoReason, a0, Operand(17));
__ Ret();
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
// We need an isolate here to execute in the simulator.
auto f = GeneratedCode<void, int>::FromBuffer(isolate(), buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,62 @@
// Copyright 2018 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/macro-assembler.h"
#include "src/simulator.h"
#include "test/common/assembler-tester.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
#define __ tasm.
// Test the x64 assembler by compiling some simple functions into
// a buffer and executing them. These tests do not initialize the
// V8 library, create a context, or use any V8 objects.
TEST(TurboAssemblerTest, TestHardAbort) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
__ Abort(AbortReason::kNoReason);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
auto f = GeneratedCode<void>::FromBuffer(nullptr, buffer);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(); }, "abort: no reason");
}
TEST(TurboAssemblerTest, TestCheck) {
size_t allocated;
byte* buffer = AllocateAssemblerBuffer(&allocated);
TurboAssembler tasm(nullptr, AssemblerOptions{}, buffer,
static_cast<int>(allocated), CodeObjectRequired::kNo);
__ set_abort_hard(true);
// Fail if the first parameter is 17.
__ movl(rax, Immediate(17));
__ cmpl(rax, arg_reg_1);
__ Check(Condition::not_equal, AbortReason::kNoReason);
__ ret(0);
CodeDesc desc;
tasm.GetCode(nullptr, &desc);
MakeAssemblerBufferExecutable(buffer, allocated);
auto f = GeneratedCode<void, int>::FromBuffer(nullptr, buffer);
f.Call(0);
f.Call(18);
ASSERT_DEATH_IF_SUPPORTED({ f.Call(17); }, "abort: no reason");
}
#undef __
} // namespace internal
} // namespace v8

View File

@ -20,6 +20,7 @@ group("v8_run_gcmole") {
"../../include/",
"../../src/",
"../../test/cctest/",
"../../test/common/",
"../../testing/gtest/include/gtest/gtest_prod.h",
"../../third_party/googletest/src/googletest/include/gtest/gtest_prod.h",
"../../third_party/icu/source/",