2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2017-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/cg-meta.h"
18 #include "hphp/runtime/vm/jit/relocation.h"
19 #include "hphp/util/arch.h"
20 #include "hphp/util/data-block.h"
21 #include "hphp/vixl/a64/macro-assembler-a64.h"
23 #include <gtest/gtest.h>
32 size_t blockSize_
= 4096;
34 void initBlocks(size_t size
, CodeBlock
& main
, DataBlock
& data
) {
36 code_
= static_cast<uint8_t*>(mmap(nullptr, blockSize_
,
37 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
38 MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0));
40 auto codeSize
= blockSize_
- dataSize
;
41 // None of these tests should use much data.
42 auto data_buffer
= code_
+ codeSize
;
44 main
.init(code_
, codeSize
, "test");
45 data
.init(data_buffer
, dataSize
, "data");
50 munmap(code_
, blockSize_
);
54 * Tests relocating PC relative conditional branches
55 * to a sequence of a conditional branch and an absolute
56 * branch to register. See arm::relocatePCRelativeHelper()
61 * movz/movk $tmp, <target>
64 TEST(Relocation
, RelocateBccImm2MovzMovkBccReg
) {
65 if (arch() != Arch::ARM
) {
73 initBlocks(4096, main
, data
);
74 SCOPE_EXIT
{ freeBlocks(); };
78 // 2. Emit b.cc <imm> of -0x40000 (19-bit int min)
79 auto start
= main
.frontier();
81 MacroAssembler a
{ main
};
82 meta
.addressImmediates
.insert(main
.frontier());
84 auto bccOrig
= Instruction::Cast(start
);
85 auto const cond
= static_cast<Condition
>(bccOrig
->ConditionBranch());
87 auto end
= main
.frontier();
91 AreaIndex ai
= AreaIndex::Main
;
92 auto instr
= Instruction::Cast(end
);
93 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
95 // 4. Expect !b.cc, movz/movk X and br X where with imm in X
97 EXPECT_TRUE(bcc
->IsCondBranchImm());
98 EXPECT_EQ(bcc
->ConditionBranch(), InvertCondition(cond
));
100 auto movz
= bcc
->NextInstruction();
101 EXPECT_TRUE(movz
->IsMovz());
102 const auto rd
= movz
->Rd();
103 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
104 instr
= movz
->NextInstruction();
105 while (instr
->IsMovk()) {
106 EXPECT_EQ(instr
->Rd(), rd
);
107 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
108 instr
= instr
->NextInstruction();
110 EXPECT_EQ(Instruction::Cast(target
), bccOrig
->ImmPCOffsetTarget());
113 EXPECT_TRUE(br
->IsUncondBranchReg());
114 EXPECT_EQ(br
->Rn(), rd
);
115 EXPECT_EQ(bcc
->ImmPCOffsetTarget(), br
->NextInstruction());
119 * Tests relocating PC relative compare and branch
120 * to a sequence of a compare and branch and an absolute
121 * branch to register. See arm::relocatePCRelativeHelper()
126 * movz/movk $tmp, <target>
129 TEST(Relocation
, RelocateCbz2MovzMovkCbnzReg
) {
130 if (arch() != Arch::ARM
) {
138 initBlocks(4096, main
, data
);
139 SCOPE_EXIT
{ freeBlocks(); };
143 // 2. Emit cbz <imm> of -0x40000 (19-bit int min)
144 auto start
= main
.frontier();
146 MacroAssembler a
{ main
};
147 meta
.addressImmediates
.insert(main
.frontier());
149 auto cbzOrig
= Instruction::Cast(start
);
151 auto end
= main
.frontier();
153 // 3. Call relocate()
155 AreaIndex ai
= AreaIndex::Main
;
156 auto instr
= Instruction::Cast(end
);
157 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
159 // 4. Expect cbnz, movz/movk X and br X where with imm in X
161 EXPECT_TRUE(cbnz
->Mask(LoadStoreUnsignedOffsetMask
) == CBNZ_x
);
162 const auto rt
= cbnz
->Rt();
163 EXPECT_EQ(cbzOrig
->Rt(), rt
);
165 auto movz
= cbnz
->NextInstruction();
166 EXPECT_TRUE(movz
->IsMovz());
167 const auto rd
= movz
->Rd();
168 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
169 instr
= movz
->NextInstruction();
170 while (instr
->IsMovk()) {
171 EXPECT_EQ(instr
->Rd(), rd
);
172 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
173 instr
= instr
->NextInstruction();
175 EXPECT_EQ(Instruction::Cast(target
), cbzOrig
->ImmPCOffsetTarget());
178 EXPECT_TRUE(br
->IsUncondBranchReg());
179 EXPECT_EQ(br
->Rn(), rd
);
180 EXPECT_EQ(cbnz
->ImmPCOffsetTarget(), br
->NextInstruction());
184 * Tests relocating PC relative test bit and branch
185 * to a sequence of a test bit and branch and an absolute
186 * branch to register. See arm::relocatePCRelativeHelper()
188 * tbz $rt, <bit_pos>, <target>
190 * tbnz $rt, <it_pos>, <past>
191 * movz/movk $tmp, <target>
194 TEST(Relocation
, RelocateTbz2MovzMovkTbnzReg
) {
195 if (arch() != Arch::ARM
) {
203 initBlocks(4096, main
, data
);
204 SCOPE_EXIT
{ freeBlocks(); };
208 // 2. Emit tbz <imm> of -0x2000 (14-bit int min)
209 auto start
= main
.frontier();
211 MacroAssembler a
{ main
};
212 meta
.addressImmediates
.insert(main
.frontier());
213 a
.tbz(x0
, 3, -0x2000);
214 auto tbzOrig
= Instruction::Cast(start
);
216 auto end
= main
.frontier();
218 // 3. Call relocate()
220 AreaIndex ai
= AreaIndex::Main
;
221 auto instr
= Instruction::Cast(end
);
222 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
224 // 4. Expect tbnz, movz/movk X and br X where with imm in X
226 EXPECT_TRUE(tbnz
->Mask(LoadStoreUnsignedOffsetMask
) == TBNZ
);
227 const auto rt
= tbnz
->Rt();
228 EXPECT_EQ(tbzOrig
->Rt(), rt
);
229 const auto bit_pos
= tbnz
->ImmTestBranchBit40();
230 EXPECT_EQ(bit_pos
, 3);
232 auto movz
= tbnz
->NextInstruction();
233 EXPECT_TRUE(movz
->IsMovz());
234 const auto rd
= movz
->Rd();
235 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
236 instr
= movz
->NextInstruction();
237 while (instr
->IsMovk()) {
238 EXPECT_EQ(instr
->Rd(), rd
);
239 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
240 instr
= instr
->NextInstruction();
242 EXPECT_EQ(Instruction::Cast(target
), tbzOrig
->ImmPCOffsetTarget());
245 EXPECT_TRUE(br
->IsUncondBranchReg());
246 EXPECT_EQ(br
->Rn(), rd
);
247 EXPECT_EQ(tbnz
->ImmPCOffsetTarget(), br
->NextInstruction());
251 * See arm::relocateImmediateHelper().
253 TEST(Relocation
, RelocateMovzMovkLdr2LdrLiteral
) {
255 if (arch() != Arch::ARM
) {
263 initBlocks(4096, main
, data
);
264 SCOPE_EXIT
{ freeBlocks(); };
268 // 2. Emit movz $x17 addr; movk $x17 addr; ldr $0, $x17
269 auto start
= main
.frontier();
271 MacroAssembler a
{ main
};
272 // We will kill this nop during relocation.
274 // The relocator won't know this is a litteral. This seems like a problem
277 meta
.addressImmediates
.insert(main
.frontier());
278 a
.Mov(x17
, start
+ 4);
280 a
.Ldr(x0
, MemOperand(x17
, 0));
282 auto end
= main
.frontier();
284 // 3. Call relocate()
286 AreaIndex ai
= AreaIndex::Main
;
287 auto instr
= Instruction::Cast(end
+ 8);
288 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
290 // 4. Expect a Ldr Literal to $x0 from addr
291 EXPECT_TRUE(instr
->IsLoadLiteral());
292 EXPECT_EQ(instr
->Rd(), 0);
293 EXPECT_EQ(reinterpret_cast<TCA
>(instr
->LiteralAddress()), end
);
297 * Tests the adjustment of a movz/movk target after that
298 * movz/movk is relocated. See arm::relocateImmediateHelper().
300 TEST(Relocation
, RelocateAdjustedMovzMovk
) {
301 if (arch() != Arch::ARM
) {
309 initBlocks(5 << 20, main
, data
);
310 SCOPE_EXIT
{ freeBlocks(); };
314 // 2. Emit Mov of start into x17 after padding out 2MB+.
315 auto start
= main
.frontier();
317 main
.setFrontier(start
+ (2 << 20) + 16);
318 MacroAssembler a
{ main
};
319 meta
.addressImmediates
.insert(main
.frontier());
320 a
.Mov(x17
, start
+ 8);
322 auto end
= main
.frontier();
324 // 3. Call relocate()
326 AreaIndex ai
= AreaIndex::Main
;
327 auto instr
= Instruction::Cast(end
);
328 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
330 // 4. Expect movz/movk of new target
331 auto movz
= instr
+ (2 << 20) + 16;
332 EXPECT_TRUE(movz
->IsMovz());
333 EXPECT_EQ(movz
->Rd(), 17);
334 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
335 instr
= movz
->NextInstruction();
336 while (instr
->IsMovk()) {
337 EXPECT_EQ(instr
->Rd(), 17);
338 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
339 instr
= instr
->NextInstruction();
341 EXPECT_EQ(Instruction::Cast(target
), Instruction::Cast(end
+ 8));
345 * Tests the adjustment of a movz/movk target after that
346 * movz/movk is relocated. This is a second adjustment of
347 * the internal reference triggered because the translation
348 * changed in size during relocation. See second portion of
351 TEST(Relocation
, RelocateInternalAdjustedMovzMovk
) {
352 if (arch() != Arch::ARM
) {
360 initBlocks(9 << 20, main
, data
);
361 SCOPE_EXIT
{ freeBlocks(); };
365 // 2. Emit Mov of start + 2MB into x17 and then pad out 4MB+.
366 auto start
= main
.frontier();
368 MacroAssembler a
{ main
};
369 meta
.addressImmediates
.insert(main
.frontier());
370 a
.Mov(x17
, start
+ (2 << 20) + 16);
371 main
.setFrontier(start
+ (4 << 20) + 32);
373 auto end
= main
.frontier();
375 // 3. Call relocate()
377 AreaIndex ai
= AreaIndex::Main
;
378 auto instr
= Instruction::Cast(end
);
379 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
381 // 4. Expect movz/movk of new target
383 EXPECT_TRUE(movz
->IsMovz());
384 EXPECT_EQ(movz
->Rd(), 17);
385 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
386 instr
= movz
->NextInstruction();
387 while (instr
->IsMovk()) {
388 EXPECT_EQ(instr
->Rd(), 17);
389 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
390 instr
= instr
->NextInstruction();
392 EXPECT_EQ(Instruction::Cast(target
), Instruction::Cast(end
+ (2 << 20) + 16));
396 * Tests the adjustment of a movz/movk target when its target
397 * is relocated. See arm::adjustInstruction().
399 TEST(Relocation
, AdjustMovzMovk
) {
400 if (arch() != Arch::ARM
) {
408 initBlocks(3 << 20, main
, data
);
409 SCOPE_EXIT
{ freeBlocks(); };
413 // 2. Emit Mov of start + 2MB+ into x17. This is not relocated, but
414 // its target at 2MB+ will be.
415 auto orig
= main
.frontier();
417 MacroAssembler a
{ main
};
418 meta
.addressImmediates
.insert(main
.frontier());
419 a
.Mov(x17
, orig
+ (2 << 20) + 16);
420 main
.setFrontier(orig
+ (2 << 20) + 16);
422 auto start
= main
.frontier();
424 auto end
= main
.frontier();
426 // 3. Call relocate() and then adjust the mov/movk
428 AreaIndex ai
= AreaIndex::Main
;
429 relocate(rel
, main
, start
, end
, main
, meta
, nullptr, ai
);
430 adjustForRelocation(rel
, orig
, start
);
432 // 4. Expect movz/movk of new target
433 auto movz
= Instruction::Cast(orig
);
434 EXPECT_TRUE(movz
->IsMovz());
435 EXPECT_EQ(movz
->Rd(), 17);
436 uint64_t target
= movz
->ImmMoveWide() << (16 * movz
->ShiftMoveWide());
437 auto instr
= movz
->NextInstruction();
438 while (instr
->IsMovk()) {
439 EXPECT_EQ(instr
->Rd(), 17);
440 target
|= instr
->ImmMoveWide() << (16 * instr
->ShiftMoveWide());
441 instr
= instr
->NextInstruction();
443 EXPECT_EQ(Instruction::Cast(target
), Instruction::Cast(end
));