ARM: remove the regexp specific literal pool.
It is replaced by a mov_label_offset(Register, Label*) instruction. BUG=none TEST=test/cctest/test-assembler-arm.cc R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/23515007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16676 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
e6d10e4243
commit
6a558d107a
@ -39,6 +39,7 @@
|
||||
#if V8_TARGET_ARCH_ARM
|
||||
|
||||
#include "arm/assembler-arm-inl.h"
|
||||
#include "macro-assembler.h"
|
||||
#include "serialize.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -775,9 +776,9 @@ int Assembler::GetCmpImmediateRawImmediate(Instr instr) {
|
||||
|
||||
int Assembler::target_at(int pos) {
|
||||
Instr instr = instr_at(pos);
|
||||
if ((instr & ~kImm24Mask) == 0) {
|
||||
// Emitted label constant, not part of a branch.
|
||||
return instr - (Code::kHeaderSize - kHeapObjectTag);
|
||||
if (is_uint24(instr)) {
|
||||
// Emitted link to a label, not part of a branch.
|
||||
return instr;
|
||||
}
|
||||
ASSERT((instr & 7*B25) == 5*B25); // b, bl, or blx imm24
|
||||
int imm26 = ((instr & kImm24Mask) << 8) >> 6;
|
||||
@ -792,11 +793,72 @@ int Assembler::target_at(int pos) {
|
||||
|
||||
void Assembler::target_at_put(int pos, int target_pos) {
|
||||
Instr instr = instr_at(pos);
|
||||
if ((instr & ~kImm24Mask) == 0) {
|
||||
if (is_uint24(instr)) {
|
||||
ASSERT(target_pos == pos || target_pos >= 0);
|
||||
// Emitted label constant, not part of a branch.
|
||||
// Make label relative to Code* of generated Code object.
|
||||
instr_at_put(pos, target_pos + (Code::kHeaderSize - kHeapObjectTag));
|
||||
// Emitted link to a label, not part of a branch.
|
||||
// Load the position of the label relative to the generated code object
|
||||
// pointer in a register.
|
||||
|
||||
// Here are the instructions we need to emit:
|
||||
// For ARMv7: target24 => target16_1:target16_0
|
||||
// movw dst, #target16_0
|
||||
// movt dst, #target16_1
|
||||
// For ARMv6: target24 => target8_2:target8_1:target8_0
|
||||
// mov dst, #target8_0
|
||||
// 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)));
|
||||
ASSERT(IsNop(instr_at(pos + kInstrSize), dst.code()));
|
||||
uint32_t target24 = target_pos + (Code::kHeaderSize - kHeapObjectTag);
|
||||
ASSERT(is_uint24(target24));
|
||||
if (is_uint8(target24)) {
|
||||
// If the target fits in a byte then only patch with a mov
|
||||
// instruction.
|
||||
CodePatcher patcher(reinterpret_cast<byte*>(buffer_ + pos),
|
||||
1,
|
||||
CodePatcher::DONT_FLUSH);
|
||||
patcher.masm()->mov(dst, Operand(target24));
|
||||
} else {
|
||||
uint16_t target16_0 = target24 & kImm16Mask;
|
||||
uint16_t target16_1 = target24 >> 16;
|
||||
if (CpuFeatures::IsSupported(ARMv7)) {
|
||||
// Patch with movw/movt.
|
||||
if (target16_1 == 0) {
|
||||
CodePatcher patcher(reinterpret_cast<byte*>(buffer_ + pos),
|
||||
1,
|
||||
CodePatcher::DONT_FLUSH);
|
||||
patcher.masm()->movw(dst, target16_0);
|
||||
} else {
|
||||
CodePatcher patcher(reinterpret_cast<byte*>(buffer_ + pos),
|
||||
2,
|
||||
CodePatcher::DONT_FLUSH);
|
||||
patcher.masm()->movw(dst, target16_0);
|
||||
patcher.masm()->movt(dst, target16_1);
|
||||
}
|
||||
} else {
|
||||
// Patch with a sequence of mov/orr/orr instructions.
|
||||
uint8_t target8_0 = target16_0 & kImm8Mask;
|
||||
uint8_t target8_1 = target16_0 >> 8;
|
||||
uint8_t target8_2 = target16_1 & kImm8Mask;
|
||||
if (target8_2 == 0) {
|
||||
CodePatcher patcher(reinterpret_cast<byte*>(buffer_ + pos),
|
||||
2,
|
||||
CodePatcher::DONT_FLUSH);
|
||||
patcher.masm()->mov(dst, Operand(target8_0));
|
||||
patcher.masm()->orr(dst, dst, Operand(target8_1 << 8));
|
||||
} else {
|
||||
CodePatcher patcher(reinterpret_cast<byte*>(buffer_ + pos),
|
||||
3,
|
||||
CodePatcher::DONT_FLUSH);
|
||||
patcher.masm()->mov(dst, Operand(target8_0));
|
||||
patcher.masm()->orr(dst, dst, Operand(target8_1 << 8));
|
||||
patcher.masm()->orr(dst, dst, Operand(target8_2 << 16));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
int imm26 = target_pos - (pos + kPcLoadDelta);
|
||||
@ -1229,21 +1291,6 @@ int Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
|
||||
}
|
||||
|
||||
|
||||
void Assembler::label_at_put(Label* L, int at_offset) {
|
||||
int target_pos;
|
||||
ASSERT(!L->is_bound());
|
||||
if (L->is_linked()) {
|
||||
// Point to previous instruction that uses the link.
|
||||
target_pos = L->pos();
|
||||
} else {
|
||||
// First entry of the link chain points to itself.
|
||||
target_pos = at_offset;
|
||||
}
|
||||
L->link_to(at_offset);
|
||||
instr_at_put(at_offset, target_pos + (Code::kHeaderSize - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
// Branch instructions.
|
||||
void Assembler::b(int branch_offset, Condition cond) {
|
||||
ASSERT((branch_offset & 3) == 0);
|
||||
@ -1386,6 +1433,45 @@ void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) {
|
||||
}
|
||||
|
||||
|
||||
void Assembler::mov_label_offset(Register dst, Label* label) {
|
||||
if (label->is_bound()) {
|
||||
mov(dst, Operand(label->pos() + (Code::kHeaderSize - kHeapObjectTag)));
|
||||
} else {
|
||||
// Emit the link to the label in the code stream followed by extra nop
|
||||
// instructions.
|
||||
// If the label is not linked, then start a new link chain by linking it to
|
||||
// itself, emitting pc_offset().
|
||||
int link = label->is_linked() ? label->pos() : pc_offset();
|
||||
label->link_to(pc_offset());
|
||||
|
||||
// When the label is bound, these instructions will be patched with a
|
||||
// sequence of movw/movt or mov/orr/orr instructions. They will load the
|
||||
// destination register with the position of the label from the beginning
|
||||
// of the code.
|
||||
//
|
||||
// The link will be extracted from the first instruction and the destination
|
||||
// register from the second.
|
||||
// For ARMv7:
|
||||
// link
|
||||
// mov dst, dst
|
||||
// For ARMv6:
|
||||
// link
|
||||
// mov dst, dst
|
||||
// mov dst, dst
|
||||
//
|
||||
// When the label gets bound: target_at extracts the link and target_at_put
|
||||
// patches the instructions.
|
||||
ASSERT(is_uint24(link));
|
||||
BlockConstPoolScope block_const_pool(this);
|
||||
emit(link);
|
||||
nop(dst.code());
|
||||
if (!CpuFeatures::IsSupported(ARMv7)) {
|
||||
nop(dst.code());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Assembler::movw(Register reg, uint32_t immediate, Condition cond) {
|
||||
ASSERT(immediate < 0x10000);
|
||||
// May use movw if supported, but on unsupported platforms will try to use
|
||||
|
@ -748,10 +748,6 @@ class Assembler : public AssemblerBase {
|
||||
// Manages the jump elimination optimization if the second parameter is true.
|
||||
int branch_offset(Label* L, bool jump_elimination_allowed);
|
||||
|
||||
// Puts a labels target address at the given position.
|
||||
// The high 8 bits are set to zero.
|
||||
void label_at_put(Label* L, int at_offset);
|
||||
|
||||
// Return the address in the constant pool of the code target address used by
|
||||
// the branch/call instruction at pc, or the object in a mov.
|
||||
INLINE(static Address target_pointer_address_at(Address pc));
|
||||
@ -903,6 +899,10 @@ class Assembler : public AssemblerBase {
|
||||
mov(dst, Operand(src), s, cond);
|
||||
}
|
||||
|
||||
// Load the position of the label relative to the generated code object
|
||||
// pointer in a register.
|
||||
void mov_label_offset(Register dst, Label* label);
|
||||
|
||||
// ARMv7 instructions for loading a 32 bit immediate in two instructions.
|
||||
// This may actually emit a different mov instruction, but on an ARMv7 it
|
||||
// is guaranteed to only emit one instruction.
|
||||
@ -1561,7 +1561,6 @@ class Assembler : public AssemblerBase {
|
||||
void RecordRelocInfo(double data);
|
||||
void RecordRelocInfoConstantPoolEntryHelper(const RelocInfo& rinfo);
|
||||
|
||||
friend class RegExpMacroAssemblerARM;
|
||||
friend class RelocInfo;
|
||||
friend class CodePatcher;
|
||||
friend class BlockConstPoolScope;
|
||||
|
@ -220,6 +220,8 @@ enum {
|
||||
kCoprocessorMask = 15 << 8,
|
||||
kOpCodeMask = 15 << 21, // In data-processing instructions.
|
||||
kImm24Mask = (1 << 24) - 1,
|
||||
kImm16Mask = (1 << 16) - 1,
|
||||
kImm8Mask = (1 << 8) - 1,
|
||||
kOff12Mask = (1 << 12) - 1,
|
||||
kOff8Mask = (1 << 8) - 1
|
||||
};
|
||||
|
@ -3845,10 +3845,13 @@ bool AreAliased(Register reg1,
|
||||
#endif
|
||||
|
||||
|
||||
CodePatcher::CodePatcher(byte* address, int instructions)
|
||||
CodePatcher::CodePatcher(byte* address,
|
||||
int instructions,
|
||||
FlushICache flush_cache)
|
||||
: address_(address),
|
||||
size_(instructions * Assembler::kInstrSize),
|
||||
masm_(NULL, address, size_ + Assembler::kGap) {
|
||||
masm_(NULL, address, size_ + Assembler::kGap),
|
||||
flush_cache_(flush_cache) {
|
||||
// Create a new macro assembler pointing to the address of the code to patch.
|
||||
// The size is adjusted with kGap on order for the assembler to generate size
|
||||
// bytes of instructions without failing with buffer size constraints.
|
||||
@ -3858,7 +3861,9 @@ CodePatcher::CodePatcher(byte* address, int instructions)
|
||||
|
||||
CodePatcher::~CodePatcher() {
|
||||
// Indicate that code has changed.
|
||||
CPU::FlushICache(address_, size_);
|
||||
if (flush_cache_ == FLUSH) {
|
||||
CPU::FlushICache(address_, size_);
|
||||
}
|
||||
|
||||
// Check that the code was patched as expected.
|
||||
ASSERT(masm_.pc_ == address_ + size_);
|
||||
|
@ -1429,7 +1429,14 @@ class MacroAssembler: public Assembler {
|
||||
// an assertion to fail.
|
||||
class CodePatcher {
|
||||
public:
|
||||
CodePatcher(byte* address, int instructions);
|
||||
enum FlushICache {
|
||||
FLUSH,
|
||||
DONT_FLUSH
|
||||
};
|
||||
|
||||
CodePatcher(byte* address,
|
||||
int instructions,
|
||||
FlushICache flush_cache = FLUSH);
|
||||
virtual ~CodePatcher();
|
||||
|
||||
// Macro assembler to emit code.
|
||||
@ -1449,6 +1456,7 @@ class CodePatcher {
|
||||
byte* address_; // The address of the code being patched.
|
||||
int size_; // Number of bytes of the expected patch size.
|
||||
MacroAssembler masm_; // Macro assembler used to generate the code.
|
||||
FlushICache flush_cache_; // Whether to flush the I cache after patching.
|
||||
};
|
||||
|
||||
|
||||
|
@ -134,7 +134,6 @@ RegExpMacroAssemblerARM::RegExpMacroAssemblerARM(
|
||||
exit_label_() {
|
||||
ASSERT_EQ(0, registers_to_save % 2);
|
||||
__ jmp(&entry_label_); // We'll write the entry code later.
|
||||
EmitBacktrackConstantPool();
|
||||
__ bind(&start_label_); // And then continue from here.
|
||||
}
|
||||
|
||||
@ -938,37 +937,8 @@ void RegExpMacroAssemblerARM::PopRegister(int register_index) {
|
||||
}
|
||||
|
||||
|
||||
static bool is_valid_memory_offset(int value) {
|
||||
if (value < 0) value = -value;
|
||||
return value < (1<<12);
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::PushBacktrack(Label* label) {
|
||||
if (label->is_bound()) {
|
||||
int target = label->pos();
|
||||
__ mov(r0, Operand(target + Code::kHeaderSize - kHeapObjectTag));
|
||||
} else {
|
||||
int constant_offset = GetBacktrackConstantPoolEntry();
|
||||
masm_->label_at_put(label, constant_offset);
|
||||
// Reading pc-relative is based on the address 8 bytes ahead of
|
||||
// the current opcode.
|
||||
unsigned int offset_of_pc_register_read =
|
||||
masm_->pc_offset() + Assembler::kPcLoadDelta;
|
||||
int pc_offset_of_constant =
|
||||
constant_offset - offset_of_pc_register_read;
|
||||
ASSERT(pc_offset_of_constant < 0);
|
||||
if (is_valid_memory_offset(pc_offset_of_constant)) {
|
||||
Assembler::BlockConstPoolScope block_const_pool(masm_);
|
||||
__ ldr(r0, MemOperand(pc, pc_offset_of_constant));
|
||||
} else {
|
||||
// Not a 12-bit offset, so it needs to be loaded from the constant
|
||||
// pool.
|
||||
Assembler::BlockConstPoolScope block_const_pool(masm_);
|
||||
__ mov(r0, Operand(pc_offset_of_constant + Assembler::kInstrSize));
|
||||
__ ldr(r0, MemOperand(pc, r0));
|
||||
}
|
||||
}
|
||||
__ mov_label_offset(r0, label);
|
||||
Push(r0);
|
||||
CheckStackLimit();
|
||||
}
|
||||
@ -1279,38 +1249,6 @@ void RegExpMacroAssemblerARM::CheckStackLimit() {
|
||||
}
|
||||
|
||||
|
||||
void RegExpMacroAssemblerARM::EmitBacktrackConstantPool() {
|
||||
__ CheckConstPool(false, false);
|
||||
Assembler::BlockConstPoolScope block_const_pool(masm_);
|
||||
backtrack_constant_pool_offset_ = masm_->pc_offset();
|
||||
for (int i = 0; i < kBacktrackConstantPoolSize; i++) {
|
||||
__ emit(0);
|
||||
}
|
||||
|
||||
backtrack_constant_pool_capacity_ = kBacktrackConstantPoolSize;
|
||||
}
|
||||
|
||||
|
||||
int RegExpMacroAssemblerARM::GetBacktrackConstantPoolEntry() {
|
||||
while (backtrack_constant_pool_capacity_ > 0) {
|
||||
int offset = backtrack_constant_pool_offset_;
|
||||
backtrack_constant_pool_offset_ += kPointerSize;
|
||||
backtrack_constant_pool_capacity_--;
|
||||
if (masm_->pc_offset() - offset < 2 * KB) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
Label new_pool_skip;
|
||||
__ jmp(&new_pool_skip);
|
||||
EmitBacktrackConstantPool();
|
||||
__ bind(&new_pool_skip);
|
||||
int offset = backtrack_constant_pool_offset_;
|
||||
backtrack_constant_pool_offset_ += kPointerSize;
|
||||
backtrack_constant_pool_capacity_--;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
bool RegExpMacroAssemblerARM::CanReadUnaligned() {
|
||||
return CpuFeatures::IsSupported(UNALIGNED_ACCESSES) && !slow_safe();
|
||||
}
|
||||
|
@ -160,9 +160,6 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler {
|
||||
// Check whether we are exceeding the stack limit on the backtrack stack.
|
||||
void CheckStackLimit();
|
||||
|
||||
void EmitBacktrackConstantPool();
|
||||
int GetBacktrackConstantPoolEntry();
|
||||
|
||||
|
||||
// Generate a call to CheckStackGuardState.
|
||||
void CallCheckStackGuardState(Register scratch);
|
||||
|
@ -196,7 +196,6 @@ class Label BASE_EMBEDDED {
|
||||
}
|
||||
|
||||
friend class Assembler;
|
||||
friend class RegexpAssembler;
|
||||
friend class Displacement;
|
||||
friend class RegExpMacroAssemblerIrregexp;
|
||||
};
|
||||
|
@ -1439,4 +1439,75 @@ TEST(17) {
|
||||
}
|
||||
|
||||
|
||||
TEST(code_relative_offset) {
|
||||
// Test extracting the offset of a label from the beginning of the code
|
||||
// in a register.
|
||||
CcTest::InitializeVM();
|
||||
Isolate* isolate = Isolate::Current();
|
||||
HandleScope scope(isolate);
|
||||
// Initialize a code object that will contain the code.
|
||||
Handle<Object> code_object(isolate->heap()->undefined_value(), isolate);
|
||||
|
||||
Assembler assm(isolate, NULL, 0);
|
||||
|
||||
Label start, target_away, target_faraway;
|
||||
|
||||
__ stm(db_w, sp, r4.bit() | r5.bit() | lr.bit());
|
||||
|
||||
// r3 is used as the address zero, the test will crash when we load it.
|
||||
__ mov(r3, Operand::Zero());
|
||||
|
||||
// r5 will be a pointer to the start of the code.
|
||||
__ mov(r5, Operand(code_object));
|
||||
__ mov_label_offset(r4, &start);
|
||||
|
||||
__ mov_label_offset(r1, &target_faraway);
|
||||
__ str(r1, MemOperand(sp, kPointerSize, NegPreIndex));
|
||||
|
||||
__ mov_label_offset(r1, &target_away);
|
||||
|
||||
// Jump straight to 'target_away' the first time and use the relative
|
||||
// position the second time. This covers the case when extracting the
|
||||
// position of a label which is linked.
|
||||
__ mov(r2, Operand::Zero());
|
||||
__ bind(&start);
|
||||
__ cmp(r2, Operand::Zero());
|
||||
__ b(eq, &target_away);
|
||||
__ add(pc, r5, r1);
|
||||
// Emit invalid instructions to push the label between 2^8 and 2^16
|
||||
// instructions away. The test will crash if they are reached.
|
||||
for (int i = 0; i < (1 << 10); i++) {
|
||||
__ ldr(r3, MemOperand(r3));
|
||||
}
|
||||
__ bind(&target_away);
|
||||
// This will be hit twice: r0 = r0 + 5 + 5.
|
||||
__ add(r0, r0, Operand(5));
|
||||
|
||||
__ ldr(r1, MemOperand(sp, kPointerSize, PostIndex), ne);
|
||||
__ add(pc, r5, r4, LeaveCC, ne);
|
||||
|
||||
__ mov(r2, Operand(1));
|
||||
__ b(&start);
|
||||
// Emit invalid instructions to push the label between 2^16 and 2^24
|
||||
// instructions away. The test will crash if they are reached.
|
||||
for (int i = 0; i < (1 << 21); i++) {
|
||||
__ ldr(r3, MemOperand(r3));
|
||||
}
|
||||
__ bind(&target_faraway);
|
||||
// r0 = r0 + 5 + 5 + 11
|
||||
__ add(r0, r0, Operand(11));
|
||||
|
||||
__ ldm(ia_w, sp, r4.bit() | r5.bit() | pc.bit());
|
||||
|
||||
CodeDesc desc;
|
||||
assm.GetCode(&desc);
|
||||
Handle<Code> code = isolate->factory()->NewCode(desc,
|
||||
Code::ComputeFlags(Code::STUB), code_object);
|
||||
CHECK(code->IsCode());
|
||||
F1 f = FUNCTION_CAST<F1>(code->entry());
|
||||
int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 21, 0, 0, 0, 0));
|
||||
::printf("f() = %d\n", res);
|
||||
CHECK_EQ(42, res);
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
Loading…
Reference in New Issue
Block a user