Commit 886b259e authored by jacob.bramley's avatar jacob.bramley Committed by Commit bot

[arm] Fix test failures on old architectures.

This mostly affects ARMv6, but also fixes some failures on ARMv7 when
hardware integer division is not available.

- Fix a case where a CodePatcher accumulates literal pool entries, but
  does not actually emit them.
- Don't treat division as safe if we can't use the hardware instruction.
  Our fallback implementation returns the wrong result if the divisor is
  zero.
- Support deoptimization tables bigger than 8 bits. (kMaxNumberOfEntries
  requires a 16-bit table index.)
- Correct a TurboFan instruction encoding to encode the Operand2 mode.

BUG=

Review-Url: https://codereview.chromium.org/2021343002
Cr-Commit-Position: refs/heads/master@{#36636}
parent a478bcb8
......@@ -848,6 +848,19 @@ void Assembler::target_at_put(int pos, int target_pos) {
// Load the position of the label relative to the generated code object
// pointer in a register.
// The existing code must be a single 24-bit label chain link, followed by
// nops encoding the destination register. See mov_label_offset.
// Extract the destination register from the first nop instructions.
Register dst =
Register::from_code(Instruction::RmValue(instr_at(pos + kInstrSize)));
// In addition to the 24-bit label chain link, we expect to find one nop for
// ARMv7 and above, or two nops for ARMv6. See mov_label_offset.
DCHECK(IsNop(instr_at(pos + kInstrSize), dst.code()));
if (!CpuFeatures::IsSupported(ARMv7)) {
DCHECK(IsNop(instr_at(pos + 2 * kInstrSize), dst.code()));
}
// Here are the instructions we need to emit:
// For ARMv7: target24 => target16_1:target16_0
// movw dst, #target16_0
......@@ -857,10 +870,6 @@ void Assembler::target_at_put(int pos, int target_pos) {
// orr dst, dst, #target8_1 << 8
// orr dst, dst, #target8_2 << 16
// We extract the destination register from the emitted nop instruction.
Register dst = Register::from_code(
Instruction::RmValue(instr_at(pos + kInstrSize)));
DCHECK(IsNop(instr_at(pos + kInstrSize), dst.code()));
uint32_t target24 = target_pos + (Code::kHeaderSize - kHeapObjectTag);
DCHECK(is_uint24(target24));
if (is_uint8(target24)) {
......
......@@ -66,15 +66,12 @@ void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
// We need calls to have a predictable size in the unoptimized code, but
// this is optimized code, so we don't have to have a predictable size.
int call_size_in_bytes =
MacroAssembler::CallSizeNotPredictableCodeSize(isolate,
deopt_entry,
RelocInfo::NONE32);
int call_size_in_bytes = MacroAssembler::CallDeoptimizerSize();
int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
DCHECK(call_size_in_bytes % Assembler::kInstrSize == 0);
DCHECK(call_size_in_bytes <= patch_size());
CodePatcher patcher(isolate, call_address, call_size_in_words);
patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
patcher.masm()->CallDeoptimizer(deopt_entry);
DCHECK(prev_call_address == NULL ||
call_address >= prev_call_address + patch_size());
DCHECK(call_address + patch_size() <= code->instruction_end());
......@@ -307,15 +304,50 @@ void Deoptimizer::TableEntryGenerator::Generate() {
void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
// Create a sequence of deoptimization entries.
// Note that registers are still live when jumping to an entry.
Label done;
for (int i = 0; i < count(); i++) {
int start = masm()->pc_offset();
USE(start);
__ mov(ip, Operand(i));
__ b(&done);
DCHECK(masm()->pc_offset() - start == table_entry_size_);
// We need to be able to generate immediates up to kMaxNumberOfEntries. On
// ARMv7, we can use movw (with a maximum immediate of 0xffff). On ARMv6, we
// need two instructions.
STATIC_ASSERT((kMaxNumberOfEntries - 1) <= 0xffff);
if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatureScope scope(masm(), ARMv7);
Label done;
for (int i = 0; i < count(); i++) {
int start = masm()->pc_offset();
USE(start);
__ movw(ip, i);
__ b(&done);
DCHECK_EQ(table_entry_size_, masm()->pc_offset() - start);
}
__ bind(&done);
} else {
// We want to keep table_entry_size_ == 8 (since this is the common case),
// but we need two instructions to load most immediates over 0xff. To handle
// this, we set the low byte in the main table, and then set the high byte
// in a separate table if necessary.
Label high_fixes[256];
int high_fix_max = (count() - 1) >> 8;
DCHECK_GT(arraysize(high_fixes), high_fix_max);
for (int i = 0; i < count(); i++) {
int start = masm()->pc_offset();
USE(start);
__ mov(ip, Operand(i & 0xff)); // Set the low byte.
__ b(&high_fixes[i >> 8]); // Jump to the secondary table.
DCHECK_EQ(table_entry_size_, masm()->pc_offset() - start);
}
// Generate the secondary table, to set the high byte.
for (int high = 1; high <= high_fix_max; high++) {
__ bind(&high_fixes[high]);
__ orr(ip, ip, Operand(high << 8));
// If this isn't the last entry, emit a branch to the end of the table.
// The last entry can just fall through.
if (high < high_fix_max) __ b(&high_fixes[0]);
}
// Bind high_fixes[0] last, for indices like 0x00**. This case requires no
// fix-up, so for (common) small tables we can jump here, then just fall
// through with no additional branch.
__ bind(&high_fixes[0]);
}
__ bind(&done);
__ push(ip);
}
......
......@@ -89,17 +89,6 @@ int MacroAssembler::CallStubSize(
}
int MacroAssembler::CallSizeNotPredictableCodeSize(Isolate* isolate,
Address target,
RelocInfo::Mode rmode,
Condition cond) {
Instr mov_instr = cond | MOV | LeaveCC;
Operand mov_operand = Operand(reinterpret_cast<intptr_t>(target), rmode);
return kInstrSize +
mov_operand.instructions_required(NULL, mov_instr) * kInstrSize;
}
void MacroAssembler::Call(Address target,
RelocInfo::Mode rmode,
Condition cond,
......@@ -173,6 +162,40 @@ void MacroAssembler::Call(Handle<Code> code,
Call(reinterpret_cast<Address>(code.location()), rmode, cond, mode);
}
void MacroAssembler::CallDeoptimizer(Address target) {
BlockConstPoolScope block_const_pool(this);
uintptr_t target_raw = reinterpret_cast<uintptr_t>(target);
// We use blx, like a call, but it does not return here. The link register is
// used by the deoptimizer to work out what called it.
if (CpuFeatures::IsSupported(ARMv7)) {
CpuFeatureScope scope(this, ARMv7);
movw(ip, target_raw & 0xffff);
movt(ip, (target_raw >> 16) & 0xffff);
blx(ip);
} else {
// We need to load a literal, but we can't use the usual constant pool
// because we call this from a patcher, and cannot afford the guard
// instruction and other administrative overhead.
ldr(ip, MemOperand(pc, (2 * kInstrSize) - kPcLoadDelta));
blx(ip);
dd(target_raw);
}
}
int MacroAssembler::CallDeoptimizerSize() {
// ARMv7+:
// movw ip, ...
// movt ip, ...
// blx ip @ This never returns.
//
// ARMv6:
// ldr ip, =address
// blx ip @ This never returns.
// .word address
return 3 * kInstrSize;
}
void MacroAssembler::Ret(Condition cond) {
bx(lr, cond);
......@@ -3958,6 +3981,10 @@ CodePatcher::~CodePatcher() {
Assembler::FlushICache(masm_.isolate(), address_, size_);
}
// Check that we don't have any pending constant pools.
DCHECK(masm_.num_pending_32_bit_constants_ == 0);
DCHECK(masm_.num_pending_64_bit_constants_ == 0);
// Check that the code was patched as expected.
DCHECK(masm_.pc_ == address_ + size_);
DCHECK(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
......
......@@ -101,10 +101,6 @@ class MacroAssembler: public Assembler {
int CallStubSize(CodeStub* stub,
TypeFeedbackId ast_id = TypeFeedbackId::None(),
Condition cond = al);
static int CallSizeNotPredictableCodeSize(Isolate* isolate,
Address target,
RelocInfo::Mode rmode,
Condition cond = al);
// Jump, Call, and Ret pseudo instructions implementing inter-working.
void Jump(Register target, Condition cond = al);
......@@ -114,17 +110,19 @@ class MacroAssembler: public Assembler {
void Call(Address target, RelocInfo::Mode rmode,
Condition cond = al,
TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS);
void Call(Handle<Code> code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
TypeFeedbackId ast_id = TypeFeedbackId::None(), Condition cond = al,
TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS);
int CallSize(Handle<Code> code,
RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
TypeFeedbackId ast_id = TypeFeedbackId::None(),
Condition cond = al);
void Call(Handle<Code> code,
RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
TypeFeedbackId ast_id = TypeFeedbackId::None(),
Condition cond = al,
TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS);
void Ret(Condition cond = al);
// Used for patching in calls to the deoptimizer.
void CallDeoptimizer(Address target);
static int CallDeoptimizerSize();
// Emit code to discard a non-negative number of pointer-sized elements
// from the stack, clobbering only the sp register.
void Drop(int count, Condition cond = al);
......
......@@ -326,7 +326,8 @@ void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode div_opcode,
} else {
InstructionOperand mul_operand = g.TempRegister();
selector->Emit(kArmMul, mul_operand, div_operand, right_operand);
selector->Emit(kArmSub, result_operand, left_operand, mul_operand);
selector->Emit(kArmSub | AddressingModeField::encode(kMode_Operand2_R),
result_operand, left_operand, mul_operand);
}
}
......@@ -1951,9 +1952,13 @@ void InstructionSelector::VisitAtomicStore(Node* node) {
// static
MachineOperatorBuilder::Flags
InstructionSelector::SupportedMachineOperatorFlags() {
MachineOperatorBuilder::Flags flags =
MachineOperatorBuilder::kInt32DivIsSafe |
MachineOperatorBuilder::kUint32DivIsSafe;
MachineOperatorBuilder::Flags flags;
if (CpuFeatures::IsSupported(SUDIV)) {
// The sdiv and udiv instructions correctly return 0 if the divisor is 0,
// but the fall-back implementation does not.
flags |= MachineOperatorBuilder::kInt32DivIsSafe |
MachineOperatorBuilder::kUint32DivIsSafe;
}
if (CpuFeatures::IsSupported(ARMv7)) {
flags |= MachineOperatorBuilder::kWord32ReverseBits;
}
......
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