add thrift endpoint for sending user notifications
[hiphop-php.git] / hphp / runtime / test / relocation-test.cpp
blobf9f25999bce5edbd77f90147477bda9a26bf9230
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
25 using namespace vixl;
27 namespace HPHP::jit {
29 namespace arm {
31 uint8_t* code_;
32 size_t blockSize_ = 4096;
34 void initBlocks(size_t size, CodeBlock& main, DataBlock& data) {
35 blockSize_ = size;
36 code_ = static_cast<uint8_t*>(mmap(nullptr, blockSize_,
37 PROT_READ | PROT_WRITE | PROT_EXEC,
38 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
39 auto dataSize = 100;
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");
49 void freeBlocks() {
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()
58 * b.cc <target>
59 * to
60 * b.!cc <past>
61 * movz/movk $tmp, <target>
62 * br $tmp
64 TEST(Relocation, RelocateBccImm2MovzMovkBccReg) {
65 if (arch() != Arch::ARM) {
66 SUCCEED();
67 return;
70 // 1. Init
71 CodeBlock main;
72 DataBlock data;
73 initBlocks(4096, main, data);
74 SCOPE_EXIT { freeBlocks(); };
76 CGMeta meta;
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());
83 a.b(-0x40000, eq);
84 auto bccOrig = Instruction::Cast(start);
85 auto const cond = static_cast<Condition>(bccOrig->ConditionBranch());
87 auto end = main.frontier();
89 // 3. Call relocate()
90 RelocationInfo rel;
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
96 auto bcc = instr;
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());
112 auto br = instr;
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()
123 * cbz $rt, <target>
124 * to
125 * cbnz $rt, <past>
126 * movz/movk $tmp, <target>
127 * br $tmp
129 TEST(Relocation, RelocateCbz2MovzMovkCbnzReg) {
130 if (arch() != Arch::ARM) {
131 SUCCEED();
132 return;
135 // 1. Init
136 CodeBlock main;
137 DataBlock data;
138 initBlocks(4096, main, data);
139 SCOPE_EXIT { freeBlocks(); };
141 CGMeta meta;
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());
148 a.cbz(x0, -0x40000);
149 auto cbzOrig = Instruction::Cast(start);
151 auto end = main.frontier();
153 // 3. Call relocate()
154 RelocationInfo rel;
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
160 auto cbnz = instr;
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());
177 auto br = instr;
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>
189 * to
190 * tbnz $rt, <it_pos>, <past>
191 * movz/movk $tmp, <target>
192 * br $tmp
194 TEST(Relocation, RelocateTbz2MovzMovkTbnzReg) {
195 if (arch() != Arch::ARM) {
196 SUCCEED();
197 return;
200 // 1. Init
201 CodeBlock main;
202 DataBlock data;
203 initBlocks(4096, main, data);
204 SCOPE_EXIT { freeBlocks(); };
206 CGMeta meta;
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()
219 RelocationInfo rel;
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
225 auto tbnz = instr;
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());
244 auto br = instr;
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) {
256 SUCCEED();
257 return;
260 // 1. Init
261 CodeBlock main;
262 DataBlock data;
263 initBlocks(4096, main, data);
264 SCOPE_EXIT { freeBlocks(); };
266 CGMeta meta;
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.
273 a.nop();
274 // The relocator won't know this is a litteral. This seems like a problem
275 // with this test.
276 a.dc64(0xdeadbeef);
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()
285 RelocationInfo rel;
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) {
302 SUCCEED();
303 return;
306 // 1. Init
307 CodeBlock main;
308 DataBlock data;
309 initBlocks(5 << 20, main, data);
310 SCOPE_EXIT { freeBlocks(); };
312 CGMeta meta;
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()
325 RelocationInfo rel;
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
349 * relocateImpl().
351 TEST(Relocation, RelocateInternalAdjustedMovzMovk) {
352 if (arch() != Arch::ARM) {
353 SUCCEED();
354 return;
357 // 1. Init
358 CodeBlock main;
359 DataBlock data;
360 initBlocks(9 << 20, main, data);
361 SCOPE_EXIT { freeBlocks(); };
363 CGMeta meta;
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()
376 RelocationInfo rel;
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
382 auto movz = instr;
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) {
401 SUCCEED();
402 return;
405 // 1. Init
406 CodeBlock main;
407 DataBlock data;
408 initBlocks(3 << 20, main, data);
409 SCOPE_EXIT { freeBlocks(); };
411 CGMeta meta;
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();
423 a.nop();
424 auto end = main.frontier();
426 // 3. Call relocate() and then adjust the mov/movk
427 RelocationInfo rel;
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));