Commit 5361c57b authored by Pierre Langlois's avatar Pierre Langlois Committed by Commit Bot

[arm] Restrict usage of pc-relative LDR.

Disallow using the PC as a base in LDR and instead provide a dedicated assembler
method for pc-relative loads. The reason for this is that the generic
`Assembler::ldr` method may decide to generate more instructions if the offset
is out of range, and if the PC was the base, we would get surprising
results. For example:

~~~
ldr r0, [pc, #0xcabba9e]
~~~

is not equivalent to:

~~~
movw ip, #0xba9e
movt ip, #0xcab
ldr r0, [pc, ip]
~~~

since the reference to the PC has moved down two instructions!

We could teach the assembler to handle those cases correctly, but pc-relative
loads are used in specific cases only so that's not necessary.

As a drive-by, remove a reference to code aging.

Bug: 
Change-Id: I586d83a418db52cf28d3b524f889bf40f077998a
Reviewed-on: https://chromium-review.googlesource.com/847008Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Pierre Langlois <pierre.langlois@arm.com>
Cr-Commit-Position: refs/heads/master@{#50475}
parent 93e04fd3
......@@ -1186,7 +1186,7 @@ void Assembler::Move32BitImmediate(Register rd, const Operand& x,
immediate = x.immediate();
}
ConstantPoolAddEntry(pc_offset(), x.rmode_, immediate);
ldr(rd, MemOperand(pc, 0), cond);
ldr_pcrel(rd, 0, cond);
}
}
......@@ -1293,6 +1293,9 @@ bool Assembler::AddrMode1TryEncodeOperand(Instr* instr, const Operand& x) {
void Assembler::AddrMode2(Instr instr, Register rd, const MemOperand& x) {
DCHECK((instr & ~(kCondMask | B | L)) == B26);
// This method does not handle pc-relative addresses. ldr_pcrel() should be
// used instead.
DCHECK(x.rn_ != pc);
int am = x.am_;
if (!x.rm_.is_valid()) {
// Immediate offset.
......@@ -1330,6 +1333,9 @@ void Assembler::AddrMode2(Instr instr, Register rd, const MemOperand& x) {
void Assembler::AddrMode3(Instr instr, Register rd, const MemOperand& x) {
DCHECK((instr & ~(kCondMask | L | S6 | H)) == (B4 | B7));
DCHECK(x.rn_.is_valid());
// This method does not handle pc-relative addresses. ldr_pcrel() should be
// used instead.
DCHECK(x.rn_ != pc);
int am = x.am_;
bool is_load = (instr & L) == L;
if (!x.rm_.is_valid()) {
......@@ -2158,6 +2164,16 @@ void Assembler::strd(Register src1, Register src2,
AddrMode3(cond | B7 | B6 | B5 | B4, src1, dst);
}
void Assembler::ldr_pcrel(Register dst, int imm12, Condition cond) {
AddrMode am = Offset;
if (imm12 < 0) {
imm12 = -imm12;
am = NegOffset;
}
DCHECK(is_uint12(imm12));
emit(cond | B26 | am | L | pc.code() * B16 | dst.code() * B12 | imm12);
}
// Load/Store exclusive instructions.
void Assembler::ldrex(Register dst, Register src, Condition cond) {
// Instruction details available in ARM DDI 0406C.b, A8.8.75.
......
......@@ -908,6 +908,9 @@ class Assembler : public AssemblerBase {
Register src2,
const MemOperand& dst, Condition cond = al);
// Load literal from a pc relative address.
void ldr_pcrel(Register dst, int imm12, Condition cond = al);
// Load/Store exclusive instructions
void ldrex(Register dst, Register src, Condition cond = al);
void strex(Register src1, Register src2, Register dst, Condition cond = al);
......
......@@ -34,9 +34,6 @@ inline int DecodeConstantPoolLength(int instr) {
return ((instr >> 4) & 0xfff0) | (instr & 0xf);
}
// Used in code age prologue - ldr(pc, MemOperand(pc, -4))
const int kCodeAgeJumpInstruction = 0xe51ff004;
// Number of registers in normal ARM mode.
const int kNumRegisters = 16;
......
......@@ -2601,14 +2601,6 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
"constant pool begin (length %d)",
DecodeConstantPoolLength(instruction_bits));
return Instruction::kInstrSize;
} else if (instruction_bits == kCodeAgeJumpInstruction) {
// The code age prologue has a constant immediately following the jump
// instruction.
Instruction* target = Instruction::At(instr_ptr + Instruction::kInstrSize);
DecodeType2(instr);
SNPrintF(out_buffer_ + out_buffer_pos_,
" (0x%08x)", target->InstructionBits());
return 2 * Instruction::kInstrSize;
}
switch (instr->TypeValue()) {
case 0:
......
......@@ -574,17 +574,18 @@ void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
// Check if the code object is marked for deoptimization. If it is, then it
// jumps to the CompileLazyDeoptimizedCode builtin. In order to do this we need
// to:
// 1. load the address of the current instruction;
// 1. compute the offset of the {CodeDataContainer} from our current location
// and load it.
// 2. read from memory the word that contains that bit, which can be found in
// the flags in the referenced {CodeDataContainer} object;
// 3. test kMarkedForDeoptimizationBit in those flags; and
// 4. if it is not zero then it jumps to the builtin.
void CodeGenerator::BailoutIfDeoptimized() {
int pc_offset = __ pc_offset();
int offset =
Code::kCodeDataContainerOffset - (Code::kHeaderSize + pc_offset + 8);
int offset = Code::kCodeDataContainerOffset -
(Code::kHeaderSize + pc_offset + TurboAssembler::kPcLoadDelta);
// We can use the register pc - 8 for the address of the current instruction.
__ ldr(ip, MemOperand(pc, offset));
__ ldr_pcrel(ip, offset);
__ ldr(ip, FieldMemOperand(ip, CodeDataContainer::kKindSpecificFlagsOffset));
__ tst(ip, Operand(1 << Code::kMarkedForDeoptimizationBit));
Handle<Code> code = isolate()->builtins()->builtin_handle(
......
......@@ -1478,7 +1478,7 @@ static void TestLoadLiteral(byte* buffer, Assembler* assm, bool* failure,
int offset) {
int pc_offset = assm->pc_offset();
byte *progcounter = &buffer[pc_offset];
assm->ldr(r0, MemOperand(pc, offset));
assm->ldr_pcrel(r0, offset);
const char *expected_string_template =
(offset >= 0) ?
......
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