Commit 0d8ec36b authored by Pierre Langlois's avatar Pierre Langlois Committed by Commit Bot

[ic] Do not decode instructions to detect deoptimized code.

This fixes a crash when using --trace-ic on Arm64 debug. For a given return
address, the assembler's `target_address_from_return_address()` method will
displace it to give you the call-site address. However, this is fragile because
it needs to decode the instruction stream to distinguish between different call
sequences. So it triggered an assertion on Arm64 because we now use BL for
builtin to buitin calls.

We only use this when tracing IC states to detect if the caller is a deoptimized
function. But to do this it doesn't matter if the address we have is the return
or the call-site address. So we can just remove the need for the fragile
Assembler method.

As a drive-by, also remove `return_address_from_call_start()` which was doing
the opposite and was unused.

Change-Id: I5988d17eadd1652ed85d662e62bc4c579665dd31
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1594566
Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61337}
parent b0400674
......@@ -215,69 +215,6 @@ void Assembler::emit(Instr x) {
pc_ += kInstrSize;
}
Address Assembler::target_address_from_return_address(Address pc) {
// Returns the address of the call target from the return address that will
// be returned to after a call.
// Call sequence on V7 or later is:
// movw ip, #... @ call address low 16
// movt ip, #... @ call address high 16
// blx ip
// @ return address
// For V6 when the constant pool is unavailable, it is:
// mov ip, #... @ call address low 8
// orr ip, ip, #... @ call address 2nd 8
// orr ip, ip, #... @ call address 3rd 8
// orr ip, ip, #... @ call address high 8
// blx ip
// @ return address
// In cases that need frequent patching, the address is in the
// constant pool. It could be a small constant pool load:
// ldr ip, [pc, #...] @ call address
// blx ip
// @ return address
Address candidate = pc - 2 * kInstrSize;
Instr candidate_instr(Memory<int32_t>(candidate));
if (IsLdrPcImmediateOffset(candidate_instr)) {
return candidate;
} else {
if (CpuFeatures::IsSupported(ARMv7)) {
candidate -= 1 * kInstrSize;
DCHECK(IsMovW(Memory<int32_t>(candidate)) &&
IsMovT(Memory<int32_t>(candidate + kInstrSize)));
} else {
candidate -= 3 * kInstrSize;
DCHECK(IsMovImmed(Memory<int32_t>(candidate)) &&
IsOrrImmed(Memory<int32_t>(candidate + kInstrSize)) &&
IsOrrImmed(Memory<int32_t>(candidate + 2 * kInstrSize)) &&
IsOrrImmed(Memory<int32_t>(candidate + 3 * kInstrSize)));
}
return candidate;
}
}
Address Assembler::return_address_from_call_start(Address pc) {
if (IsLdrPcImmediateOffset(Memory<int32_t>(pc))) {
// Load from constant pool, small section.
return pc + kInstrSize * 2;
} else {
if (CpuFeatures::IsSupported(ARMv7)) {
DCHECK(IsMovW(Memory<int32_t>(pc)));
DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
// A movw / movt load immediate.
return pc + kInstrSize * 3;
} else {
DCHECK(IsMovImmed(Memory<int32_t>(pc)));
DCHECK(IsOrrImmed(Memory<int32_t>(pc + kInstrSize)));
DCHECK(IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)));
DCHECK(IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
// A mov / orr load immediate.
return pc + kInstrSize * 5;
}
}
}
void Assembler::deserialization_set_special_target_at(
Address constant_pool_entry, Code code, Address target) {
DCHECK(!Builtins::IsIsolateIndependentBuiltin(code));
......
......@@ -358,14 +358,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
V8_INLINE static Address target_address_from_return_address(Address pc);
// Given the address of the beginning of a call, return the address
// in the instruction stream that the call will return from.
V8_INLINE static Address return_address_from_call_start(Address pc);
// This sets the branch destination (which is in the constant pool on ARM).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
......
......@@ -570,19 +570,6 @@ Address Assembler::runtime_entry_at(Address pc) {
}
}
Address Assembler::target_address_from_return_address(Address pc) {
// Returns the address of the call target from the return address that will
// be returned to after a call.
// Call sequence on ARM64 is:
// ldr ip0, #... @ load from literal pool
// blr ip0
Address candidate = pc - 2 * kInstrSize;
Instruction* instr = reinterpret_cast<Instruction*>(candidate);
USE(instr);
DCHECK(instr->IsLdrLiteralX());
return candidate;
}
int Assembler::deserialization_special_target_size(Address location) {
Instruction* instr = reinterpret_cast<Instruction*>(location);
if (instr->IsBranchAndLink() || instr->IsUnconditionalBranch()) {
......
......@@ -348,10 +348,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// before it is moved into the code space.
inline Address runtime_entry_at(Address pc);
// Return the code target address at a call site from the return address of
// that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
// This sets the branch destination. 'location' here can be either the pc of
// an immediate branch or the address of an entry in the constant pool.
// This is for calls and branches within generated code.
......
......@@ -254,10 +254,6 @@ void Assembler::set_target_address_at(Address pc, Address constant_pool,
}
}
Address Assembler::target_address_from_return_address(Address pc) {
return pc - kCallTargetAddressOffset;
}
void Assembler::deserialization_set_special_target_at(
Address instruction_payload, Code code, Address target) {
set_target_address_at(instruction_payload,
......
......@@ -402,10 +402,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
// This sets the branch destination (which is in the instruction on x86).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
......@@ -422,10 +418,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
static constexpr int kSpecialTargetSize = kSystemPointerSize;
// Distance between the address of the code target in the call instruction
// and the return address
static constexpr int kCallTargetAddressOffset = kSystemPointerSize;
// One byte opcode for test al, 0xXX.
static constexpr byte kTestAlByte = 0xA8;
// One byte opcode for nop.
......
......@@ -16,13 +16,6 @@
namespace v8 {
namespace internal {
Address IC::address() const {
// Get the address of the call.
return Assembler::target_address_from_return_address(pc());
}
Address IC::constant_pool() const {
if (FLAG_enable_embedded_constant_pool) {
return raw_constant_pool();
......@@ -57,14 +50,9 @@ bool IC::IsHandler(MaybeObject object) {
(heap_object->IsDataHandler() || heap_object->IsCode()));
}
bool IC::AddressIsDeoptimizedCode() const {
return AddressIsDeoptimizedCode(isolate(), address());
}
// static
bool IC::AddressIsDeoptimizedCode(Isolate* isolate, Address address) {
bool IC::HostIsDeoptimizedCode() const {
Code host =
isolate->inner_pointer_to_code_cache()->GetCacheEntry(address)->code;
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(pc())->code;
return (host->kind() == Code::OPTIMIZED_FUNCTION &&
host->marked_for_deoptimization());
}
......
......@@ -90,7 +90,7 @@ const char* GetModifier(KeyedAccessStoreMode mode) {
void IC::TraceIC(const char* type, Handle<Object> name) {
if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled())) return;
if (AddressIsDeoptimizedCode()) return;
if (HostIsDeoptimizedCode()) return;
State new_state =
(state() == NO_FEEDBACK) ? NO_FEEDBACK : nexus()->ic_state();
TraceIC(type, name, state(), new_state);
......
......@@ -38,7 +38,6 @@ class IC {
virtual ~IC() = default;
State state() const { return state_; }
inline Address address() const;
// Compute the current IC state based on the target stub, receiver and name.
void UpdateState(Handle<Object> receiver, Handle<Object> name);
......@@ -80,9 +79,7 @@ class IC {
// Get the caller function object.
JSFunction GetHostFunction() const;
inline bool AddressIsDeoptimizedCode() const;
inline static bool AddressIsDeoptimizedCode(Isolate* isolate,
Address address);
inline bool HostIsDeoptimizedCode() const;
bool is_vector_set() { return vector_set_; }
inline bool vector_needs_update();
......
......@@ -117,10 +117,6 @@ int RelocInfo::target_address_size() {
return Assembler::kSpecialTargetSize;
}
Address Assembler::target_address_from_return_address(Address pc) {
return pc - kCallTargetAddressOffset;
}
void Assembler::deserialization_set_special_target_at(
Address instruction_payload, Code code, Address target) {
set_target_address_at(instruction_payload,
......
......@@ -262,10 +262,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, uint32_t target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
// This sets the branch destination (which gets loaded at the call address).
// This is for calls and branches within generated code. The serializer
// has already deserialized the lui/ori instructions etc.
......@@ -318,14 +314,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// pair.
static constexpr int kInstructionsFor32BitConstant = 2;
// Distance between the instruction referring to the address of the call
// target and the return address.
#ifdef _MIPS_ARCH_MIPS32R6
static constexpr int kCallTargetAddressOffset = 2 * kInstrSize;
#else
static constexpr int kCallTargetAddressOffset = 4 * kInstrSize;
#endif
// Max offset for instructions with 16-bit offset field
static constexpr int kMaxBranchOffset = (1 << (18 - 1)) - 1;
......
......@@ -107,10 +107,6 @@ int RelocInfo::target_address_size() {
return Assembler::kSpecialTargetSize;
}
Address Assembler::target_address_from_return_address(Address pc) {
return pc - kCallTargetAddressOffset;
}
void Assembler::deserialization_set_special_target_at(
Address instruction_payload, Code code, Address target) {
set_target_address_at(instruction_payload,
......
......@@ -261,10 +261,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, uint64_t target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
static void JumpLabelToJumpRegister(Address pc);
// This sets the branch destination (which gets loaded at the call address).
......@@ -311,14 +307,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
static constexpr int kInstructionsFor32BitConstant = 2;
static constexpr int kInstructionsFor64BitConstant = 4;
// Distance between the instruction referring to the address of the call
// target and the return address.
#ifdef _MIPS_ARCH_MIPS64R6
static constexpr int kCallTargetAddressOffset = 5 * kInstrSize;
#else
static constexpr int kCallTargetAddressOffset = 6 * kInstrSize;
#endif
// Difference between address of current opcode and value read from pc
// register.
static constexpr int kPcLoadDelta = 4;
......
......@@ -127,38 +127,6 @@ Address RelocInfo::constant_pool_entry_address() {
int RelocInfo::target_address_size() { return Assembler::kSpecialTargetSize; }
Address Assembler::target_address_from_return_address(Address pc) {
// Returns the address of the call target from the return address that will
// be returned to after a call.
// Call sequence is :
// mov ip, @ call address
// mtlr ip
// blrl
// @ return address
int len;
ConstantPoolEntry::Access access;
if (FLAG_enable_embedded_constant_pool &&
IsConstantPoolLoadEnd(pc - 3 * kInstrSize, &access)) {
len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1;
} else {
len = kMovInstructionsNoConstantPool;
}
return pc - (len + 2) * kInstrSize;
}
Address Assembler::return_address_from_call_start(Address pc) {
int len;
ConstantPoolEntry::Access access;
if (FLAG_enable_embedded_constant_pool &&
IsConstantPoolLoadStart(pc, &access)) {
len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1;
} else {
len = kMovInstructionsNoConstantPool;
}
return pc + (len + 2) * kInstrSize;
}
HeapObject RelocInfo::target_object() {
DCHECK(IsCodeTarget(rmode_) || rmode_ == FULL_EMBEDDED_OBJECT);
return HeapObject::cast(
......
......@@ -266,14 +266,6 @@ class Assembler : public AssemblerBase {
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
// Given the address of the beginning of a call, return the address
// in the instruction stream that the call will return to.
V8_INLINE static Address return_address_from_call_start(Address pc);
// This sets the branch destination.
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
......@@ -314,17 +306,6 @@ class Assembler : public AssemblerBase {
? kMovInstructionsConstantPool
: kMovInstructionsNoConstantPool;
// Distance between the instruction referring to the address of the call
// target and the return address.
// Call sequence is a FIXED_SEQUENCE:
// mov r8, @ call address
// mtlr r8
// blrl
// @ return address
static constexpr int kCallTargetAddressOffset =
(kMovInstructions + 2) * kInstrSize;
static inline int encode_crbit(const CRegister& cr, enum CRBit crbit) {
return ((cr.code() * CRWIDTH) + crbit);
}
......
......@@ -118,20 +118,6 @@ Address RelocInfo::constant_pool_entry_address() {
int RelocInfo::target_address_size() { return Assembler::kSpecialTargetSize; }
Address Assembler::target_address_from_return_address(Address pc) {
// Returns the address of the call target from the return address that will
// be returned to after a call.
// Sequence is:
// BRASL r14, RI
return pc - kCallTargetAddressOffset;
}
Address Assembler::return_address_from_call_start(Address pc) {
// Sequence is:
// BRASL r14, RI
return pc + kCallTargetAddressOffset;
}
Handle<Object> Assembler::code_target_object_handle_at(Address pc) {
SixByteInstr instr =
Instruction::InstructionBits(reinterpret_cast<const byte*>(pc));
......
......@@ -285,14 +285,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
inline static Address target_address_from_return_address(Address pc);
// Given the address of the beginning of a call, return the address
// in the instruction stream that the call will return to.
V8_INLINE static Address return_address_from_call_start(Address pc);
inline Handle<Object> code_target_object_handle_at(Address pc);
// This sets the branch destination.
// This is for calls and branches within generated code.
......@@ -323,14 +315,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
static constexpr int kBytesForPtrConstant = 6; // IILF
#endif
// Distance between the instruction referring to the address of the call
// target and the return address.
// Offset between call target address and return address
// for BRASL calls
// Patch will be appiled to other FIXED_SEQUENCE call
static constexpr int kCallTargetAddressOffset = 6;
// ---------------------------------------------------------------------------
// Code generation
......
......@@ -245,11 +245,6 @@ void Assembler::deserialization_set_target_internal_reference_at(
WriteUnalignedValue(pc, target);
}
Address Assembler::target_address_from_return_address(Address pc) {
return pc - kCallTargetAddressOffset;
}
void Assembler::deserialization_set_special_target_at(
Address instruction_payload, Code code, Address target) {
set_target_address_at(instruction_payload,
......
......@@ -365,10 +365,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
Address pc, Address constant_pool, Address target,
ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
// Return the code target address at a call site from the return address
// of that call in the instruction stream.
static inline Address target_address_from_return_address(Address pc);
// This sets the branch destination (which is in the instruction on x64).
// This is for calls and branches within generated code.
inline static void deserialization_set_special_target_at(
......@@ -389,9 +385,6 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Number of bytes taken up by the branch target in the code.
static constexpr int kSpecialTargetSize = 4; // 32-bit displacement.
// Distance between the address of the code target in the call instruction
// and the return address pushed on the stack.
static constexpr int kCallTargetAddressOffset = 4; // 32-bit displacement.
// One byte opcode for test eax,0xXXXXXXXX.
static constexpr byte kTestEaxByte = 0xA9;
......
// Copyright 2019 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.
// Flags: --trace-ic --logfile=test/mjsunit/tools/trace-ic-test.log
// Flags: --allow-natives-syntax
// The idea behind this test is to make sure we do not crash when using the
// --trace-ic flag.
(function testLoadIC() {
function loadIC(obj) {
return obj.field;
}
%EnsureFeedbackVectorForFunction(loadIC);
var obj = {field: 'hello'};
loadIC(obj);
loadIC(obj);
loadIC(obj);
})();
(function testStoreIC() {
function storeIC(obj, value) {
return obj.field = value;
}
%EnsureFeedbackVectorForFunction(storeIC);
var obj = {field: 'hello'};
storeIC(obj, 'world');
storeIC(obj, 'world');
storeIC(obj, 'world');
})();
(function testKeyedLoadIC() {
function keyedLoadIC(obj, field) {
return obj[field];
}
%EnsureFeedbackVectorForFunction(keyedLoadIC);
var obj = {field: 'hello'};
keyedLoadIC(obj, 'field');
keyedLoadIC(obj, 'field');
keyedLoadIC(obj, 'field');
})();
(function testKeyedStoreIC() {
function keyedStoreIC(obj, field, value) {
return obj[field] = value;
}
%EnsureFeedbackVectorForFunction(keyedStoreIC);
var obj = {field: 'hello'};
keyedStoreIC(obj, 'field', 'world');
keyedStoreIC(obj, 'field', 'world');
keyedStoreIC(obj, 'field', 'world');
})();
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