1 // Copyright 2017, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of ARM Limited nor the names of its contributors may be
13 // used to endorse or promote products derived from this software without
14 // specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 #include "test-pool-manager.h"
28
29 #include <stdio.h>
30
31 #include "pool-manager-impl.h"
32 #include "pool-manager.h"
33 #include "test-runner.h"
34
35 #define TEST(Name) TEST_(POOL_MANAGER_##Name)
36
37 #define IF_VERBOSE(exp) \
38 if (Test::verbose()) exp
39
40 #define BUFFER_ALIGNMENT 16
41
42 using namespace vixl;
43
Random()44 static int Random() { return static_cast<int>(std::abs(mrand48())); }
45
RandomObjectID(size_t num_objects)46 static int RandomObjectID(size_t num_objects) { return Random() % num_objects; }
47
RandomObjectSize()48 static int RandomObjectSize() { return 1 + Random() % 256; }
49
RandomObjectAlignment(int size)50 static int RandomObjectAlignment(int size) {
51 const int limit = static_cast<int>(floor(log2(BUFFER_ALIGNMENT)));
52 int log2Size = static_cast<int>(floor(log2(size)));
53 // Restrict alignment due to buffer alignment.
54 log2Size = std::min(log2Size, limit);
55 return (1 << (Random() % (1 + log2Size)));
56 }
57
58 // The size of the instruction.
RandomReferenceSize()59 static int RandomReferenceSize() { return (Random() % 2) ? 2 : 4; }
60
61 // The alignment of an instruction is either 2 or 4.
RandomInstructionAlignment()62 static int RandomInstructionAlignment() { return (Random() % 2) ? 2 : 4; }
63
RandomMinOffset()64 static int32_t RandomMinOffset() {
65 const int N = 3;
66 static const int offsets[N] = {0, 2, 4};
67 return offsets[Random() % N];
68 }
69
RandomMaxOffset()70 static int32_t RandomMaxOffset() {
71 const int N = 5;
72 static const int offsets[N] = {255, 1020, 1024, 4096, 16384};
73 return offsets[Random() % N];
74 }
75
RandomBranchMaxOffset()76 static int32_t RandomBranchMaxOffset() {
77 const int N = 10;
78 // The maximum offsets used for testing are taken from A32 and T32.
79 static const int offsets[N] =
80 {126, 254, 255, 1020, 1024, 2046, 4095, 1048574, 16777214, 33554428};
81 return offsets[Random() % N];
82 }
83
RandomPCIncrement()84 static int RandomPCIncrement() {
85 // A multiple of two.
86 return 2 * (Random() % 4 + 1);
87 }
88
89 class TestObject : public LocationBase<int32_t> {
90 public:
TestObject(int size,int alignment,int id=0)91 TestObject(int size, int alignment, int id = 0)
92 : LocationBase(0 /*type*/, size, alignment), id_(id) {}
93
EmitPoolObject(MacroAssemblerInterface * masm)94 void EmitPoolObject(MacroAssemblerInterface *masm) VIXL_OVERRIDE {
95 USE(masm);
96 }
97
ShouldDeletePoolObjectOnPlacement() const98 bool ShouldDeletePoolObjectOnPlacement() const VIXL_OVERRIDE { return true; }
99
100 // Update the references to this object.
ResolveReferences(internal::AssemblerBase * assembler)101 void ResolveReferences(internal::AssemblerBase *assembler) VIXL_OVERRIDE {
102 int32_t location = GetLocation();
103 USE(assembler);
104 for (std::vector<ForwardReference<int32_t> *>::iterator iter =
105 references_.begin();
106 iter != references_.end();) {
107 ForwardReference<int32_t> *ref = *iter;
108 VIXL_ASSERT(ref->LocationIsEncodable(location));
109 delete ref;
110 iter = references_.erase(iter);
111 }
112 IF_VERBOSE(printf("Placed object %d at location: 0x%x (%u)\n",
113 id_,
114 location,
115 location));
116 }
117
AddReference(ForwardReference<int32_t> * ref)118 void AddReference(ForwardReference<int32_t> *ref) {
119 references_.push_back(ref);
120 }
121
GetID()122 int GetID() { return id_; }
123
CreateRandom(int id)124 static TestObject *CreateRandom(int id) {
125 int size = RandomObjectSize();
126 int alignment = RandomObjectAlignment(size);
127 IF_VERBOSE(printf("Object %d -> size = %d, alignment = %d\n",
128 id,
129 size,
130 alignment));
131 return new TestObject(size, alignment, id);
132 }
133
134 private:
135 // Store pointers to ForwardReference objects - TestObject is responsible
136 // for deleting them.
137 std::vector<ForwardReference<int32_t> *> references_;
138 // Object id used for debugging.
139 int id_;
140 };
141
142 class TestBranchObject : public LocationBase<int32_t> {
143 public:
TestBranchObject(int size,int alignment,int id=0)144 TestBranchObject(int size, int alignment, int id = 0)
145 : LocationBase(1 /* type */, size, alignment), id_(id) {}
146
UsePoolObjectEmissionMargin() const147 bool UsePoolObjectEmissionMargin() const VIXL_OVERRIDE { return true; }
GetPoolObjectEmissionMargin() const148 int32_t GetPoolObjectEmissionMargin() const VIXL_OVERRIDE {
149 return 1 * KBytes;
150 }
151
152 // Do nothing for now.
EmitPoolObject(MacroAssemblerInterface * masm)153 void EmitPoolObject(MacroAssemblerInterface *masm) VIXL_OVERRIDE {
154 USE(masm);
155 }
156
ShouldDeletePoolObjectOnPlacement() const157 bool ShouldDeletePoolObjectOnPlacement() const VIXL_OVERRIDE { return false; }
158
UpdatePoolObject(PoolObject<int32_t> * object)159 virtual void UpdatePoolObject(PoolObject<int32_t> *object) VIXL_OVERRIDE {
160 // Reference from the last emitted veneer:
161 int32_t min = location_ + min_offset_;
162 int32_t max = location_ + max_offset_;
163 // The alignment that the new "veneer" requires of the label.
164 int reference_alignment = RandomInstructionAlignment();
165 reference_alignment =
166 std::max(reference_alignment, GetPoolObjectAlignment());
167 ForwardReference<int32_t> *ref =
168 new ForwardReference<int32_t>(location_,
169 4 /*size*/,
170 min,
171 max,
172 reference_alignment);
173 AddReference(ref);
174 object->Update(min, max, reference_alignment);
175 }
176
177 // Update the references to this object.
ResolveReferences(internal::AssemblerBase * assembler)178 void ResolveReferences(internal::AssemblerBase *assembler) VIXL_OVERRIDE {
179 int32_t location = GetLocation();
180 USE(assembler);
181 for (std::vector<ForwardReference<int32_t> *>::iterator iter =
182 references_.begin();
183 iter != references_.end();) {
184 ForwardReference<int32_t> *ref = *iter;
185 VIXL_ASSERT(ref->LocationIsEncodable(location));
186 delete ref;
187 iter = references_.erase(iter);
188 }
189 IF_VERBOSE(printf("Veneer %d placed at location: 0x%x (%u)\n",
190 id_,
191 location,
192 location));
193 }
194
AddReference(ForwardReference<int32_t> * ref)195 void AddReference(ForwardReference<int32_t> *ref) {
196 references_.push_back(ref);
197 }
198
GetMaxAlignment() const199 virtual int GetMaxAlignment() const VIXL_OVERRIDE {
200 int max_alignment = GetPoolObjectAlignment();
201 for (std::vector<ForwardReference<int32_t> *>::const_iterator iter =
202 references_.begin();
203 iter != references_.end();
204 ++iter) {
205 const ForwardReference<int32_t> *ref = *iter;
206 if (ref->GetAlignment() > max_alignment)
207 max_alignment = ref->GetAlignment();
208 }
209 return max_alignment;
210 }
GetMinLocation() const211 virtual int32_t GetMinLocation() const VIXL_OVERRIDE {
212 int32_t min_location = 0;
213 for (std::vector<ForwardReference<int32_t> *>::const_iterator iter =
214 references_.begin();
215 iter != references_.end();
216 ++iter) {
217 const ForwardReference<int32_t> *ref = *iter;
218 if (ref->GetMinLocation() > min_location)
219 min_location = ref->GetMinLocation();
220 }
221 return min_location;
222 }
223
GetID()224 int GetID() { return id_; }
225
CreateRandom(int id)226 static TestBranchObject *CreateRandom(int id) {
227 int size = RandomReferenceSize();
228 int alignment = size;
229 IF_VERBOSE(printf("Object %d -> size = %d, alignment = %d\n",
230 id,
231 size,
232 alignment));
233 return new TestBranchObject(size, alignment, id);
234 }
235
236 private:
237 // Store pointers to ForwardReference objects - TestBranchObject is
238 // responsible for deleting them.
239 std::vector<ForwardReference<int32_t> *> references_;
240 // Object id used for debugging.
241 int id_;
242
243 // These are the min and max offsets of the type of branch used for the
244 // veneer.
245 static const int32_t min_offset_ = 0;
246 static const int32_t max_offset_ = 16 * 1024 * 1024;
247 };
248
249 // MacroAssembler implementation that does nothing but print in verbose mode.
250 class TestMacroAssembler : public MacroAssemblerInterface {
251 public:
TestMacroAssembler()252 TestMacroAssembler() : assembler_(128) {}
253
EmitPoolHeader()254 void EmitPoolHeader() VIXL_OVERRIDE {
255 IF_VERBOSE(printf("[MASM] Emitting pool header.\n"));
256 }
EmitPoolFooter()257 void EmitPoolFooter() VIXL_OVERRIDE {
258 IF_VERBOSE(printf("[MASM] Emitting pool footer.\n"));
259 }
EmitPaddingBytes(int n)260 void EmitPaddingBytes(int n) VIXL_OVERRIDE {
261 IF_VERBOSE(printf("[MASM] Added %d bytes of padding.\n", n));
262 }
EmitNopBytes(int n)263 void EmitNopBytes(int n) VIXL_OVERRIDE {
264 IF_VERBOSE(printf("[MASM] Added %d bytes of NOPs.\n", n));
265 }
ArePoolsBlocked() const266 bool ArePoolsBlocked() const VIXL_OVERRIDE { return false; }
AllowMacroInstructions() const267 bool AllowMacroInstructions() const VIXL_OVERRIDE { return false; }
SetAllowMacroInstructions(bool allow)268 void SetAllowMacroInstructions(bool allow) VIXL_OVERRIDE { USE(allow); }
269
BlockPools()270 void BlockPools() VIXL_OVERRIDE {}
ReleasePools()271 void ReleasePools() VIXL_OVERRIDE {}
EnsureEmitPoolsFor(size_t)272 void EnsureEmitPoolsFor(size_t) VIXL_OVERRIDE {}
AsAssemblerBase()273 internal::AssemblerBase *AsAssemblerBase() VIXL_OVERRIDE {
274 return &assembler_;
275 }
276
277 private:
278 internal::AssemblerBase assembler_;
279 };
280
281 // Used for debugging.
282 namespace vixl {
283 template <>
DumpCurrentState(int32_t pc) const284 void PoolManager<int32_t>::DumpCurrentState(int32_t pc) const {
285 IF_VERBOSE(
286 printf("Number of objects: %d\n", static_cast<int>(objects_.size())));
287 IF_VERBOSE(printf("Current pc = 0x%x (%d)\n", pc, pc));
288
289 for (int i = 0; i < static_cast<int>(objects_.size()); ++i) {
290 const PoolObject<int32_t> &object = objects_[i];
291 IF_VERBOSE(
292 printf("Object %d -> size = %d, alignment = %d, range = (%d,%d)\n",
293 i,
294 object.label_base_->GetPoolObjectSizeInBytes(),
295 object.alignment_,
296 object.min_location_,
297 object.max_location_));
298 }
299 }
300 } // namespace vixl
301
302 // Basic test - checks that emitting a very simple pool works.
TEST(Basic)303 TEST(Basic) {
304 TestMacroAssembler masm;
305
306 PoolManager<int32_t> pool_manager(4 /*header_size*/,
307 2 /*header_alignment*/,
308 BUFFER_ALIGNMENT);
309 TestObject object1(4 /*size*/, 4 /*alignment*/);
310 TestObject object2(128 /*size*/, 4 /*alignment*/);
311 ForwardReference<int32_t> *ref1_obj1 =
312 new ForwardReference<int32_t>(0 /*location*/, 2 /*size*/, 0, 200);
313 ForwardReference<int32_t> *ref2_obj1 =
314 new ForwardReference<int32_t>(2 /*location*/, 2 /*size*/, 2, 202);
315 ForwardReference<int32_t> *ref3_obj1 =
316 new ForwardReference<int32_t>(4 /*location*/, 2 /*size*/, 4, 204);
317 object1.AddReference(ref1_obj1);
318 object1.AddReference(ref2_obj1);
319 object1.AddReference(ref3_obj1);
320 ForwardReference<int32_t> *ref1_obj2 =
321 new ForwardReference<int32_t>(8 /*location*/, 2 /*size*/, 8, 500);
322 ForwardReference<int32_t> *ref2_obj2 =
323 new ForwardReference<int32_t>(12 /*location*/, 4 /*size*/, 12, 300);
324 ForwardReference<int32_t> *ref3_obj2 =
325 new ForwardReference<int32_t>(16 /*location*/, 4 /*size*/, 16, 400);
326 object2.AddReference(ref1_obj2);
327 object2.AddReference(ref2_obj2);
328 object2.AddReference(ref3_obj2);
329
330 pool_manager.AddObjectReference(ref1_obj1, &object1);
331 pool_manager.AddObjectReference(ref2_obj1, &object1);
332 pool_manager.AddObjectReference(ref3_obj1, &object1);
333 pool_manager.AddObjectReference(ref1_obj2, &object2);
334 pool_manager.AddObjectReference(ref2_obj2, &object2);
335 pool_manager.AddObjectReference(ref3_obj2, &object2);
336
337 pool_manager.Emit(&masm, 20);
338 }
339
CreateReference(int id,int32_t pc,int size,int32_t min_offset,int32_t max_offset,int alignment)340 static ForwardReference<int32_t> *CreateReference(int id,
341 int32_t pc,
342 int size,
343 int32_t min_offset,
344 int32_t max_offset,
345 int alignment) {
346 IF_VERBOSE(
347 printf("About to add a new reference to object %d with min location = "
348 "%d, max "
349 "location = %d, alignment = %d, size = %d\n",
350 id,
351 min_offset + pc,
352 max_offset + pc,
353 alignment,
354 size));
355 return new ForwardReference<int32_t>(pc,
356 size,
357 min_offset + pc,
358 max_offset + pc,
359 alignment);
360 }
361
362 // Fuzz test that uses literal-like objects, that get deleted when they are
363 // placed.
TEST(FuzzObjectDeletedWhenPlaced)364 TEST(FuzzObjectDeletedWhenPlaced) {
365 TestMacroAssembler masm;
366 PoolManager<int32_t> pool_manager(4 /*header_size*/,
367 2 /*header_alignment*/,
368 BUFFER_ALIGNMENT);
369
370 const int kObjectNum = 100;
371 std::vector<TestObject *> objects;
372
373 // Create objects.
374 for (int i = 0; i < kObjectNum; ++i) {
375 objects.push_back(TestObject::CreateRandom(i));
376 }
377
378 int32_t pc = 0;
379 while (!objects.empty()) {
380 IF_VERBOSE(printf("PC = 0x%x (%d)\n", pc, pc));
381 int32_t pc_increment = RandomPCIncrement();
382 IF_VERBOSE(printf("Attempting to increment PC by %d\n", pc_increment));
383 if (pool_manager.MustEmit(pc, pc_increment)) {
384 pc = pool_manager.Emit(&masm, pc, pc_increment);
385 }
386 pc += pc_increment;
387 // Pick an object, randomly.
388 TestObject *object = objects[RandomObjectID(objects.size())];
389 int32_t min_offset = RandomMinOffset();
390 int32_t max_offset = RandomMaxOffset();
391 int32_t size = RandomReferenceSize();
392 int32_t alignment =
393 RandomObjectAlignment(object->GetPoolObjectSizeInBytes());
394 ForwardReference<int32_t> *ref = CreateReference(object->GetID(),
395 pc,
396 size,
397 min_offset,
398 max_offset,
399 alignment);
400 if (pool_manager.MustEmit(pc, size, ref, object)) {
401 pc = pool_manager.Emit(&masm, pc, size, ref, object);
402 delete ref;
403 // We must recreate the reference, the PC has changed, but only if
404 // it still is a forward reference.
405 if (!object->IsBound()) {
406 ref = CreateReference(object->GetID(),
407 pc,
408 size,
409 min_offset,
410 max_offset,
411 alignment);
412 }
413 }
414 IF_VERBOSE(printf("Incrementing PC by size of reference (%d).\n", size));
415 pc += size;
416 // We only need to track the reference if it's a forward reference.
417 if (!object->IsBound()) {
418 object->AddReference(ref);
419 pool_manager.AddObjectReference(ref, object);
420 }
421 VIXL_ASSERT(!pool_manager.MustEmit(pc - 1));
422 // Remove bound objects.
423 for (std::vector<TestObject *>::iterator iter = objects.begin();
424 iter != objects.end();) {
425 TestObject *obj = *iter;
426 if (obj->IsBound()) {
427 delete obj;
428 iter = objects.erase(iter);
429 } else {
430 ++iter;
431 }
432 }
433 }
434
435 pool_manager.Emit(&masm, pc);
436 }
437
438 // Fuzz test that uses veneer-like objects, that get updated when they are
439 // placed and get deleted when they are bound by the user.
TEST(FuzzObjectUpdatedWhenPlaced)440 TEST(FuzzObjectUpdatedWhenPlaced) {
441 TestMacroAssembler masm;
442 PoolManager<int32_t> pool_manager(4 /*header_size*/,
443 2 /*header_alignment*/,
444 BUFFER_ALIGNMENT);
445 const int kObjectNum = 1000;
446 std::vector<TestBranchObject *> objects;
447
448 // Create objects.
449 for (int i = 0; i < kObjectNum; ++i) {
450 objects.push_back(TestBranchObject::CreateRandom(i));
451 }
452
453 int32_t pc = 0;
454 while (!objects.empty()) {
455 IF_VERBOSE(printf("PC = 0x%x (%d)\n", pc, pc));
456
457 int32_t pc_increment = RandomPCIncrement();
458 IF_VERBOSE(printf("Attempting to increment PC by %d\n", pc_increment));
459
460 if (pool_manager.MustEmit(pc, pc_increment)) {
461 pc = pool_manager.Emit(&masm, pc, pc_increment);
462 }
463 pc += pc_increment;
464
465 // Pick a random object.
466 TestBranchObject *object = objects[RandomObjectID(objects.size())];
467 int32_t min_offset = RandomMinOffset();
468 int32_t max_offset = RandomBranchMaxOffset();
469 int32_t size = RandomReferenceSize();
470 int32_t alignment =
471 RandomObjectAlignment(object->GetPoolObjectSizeInBytes());
472 ForwardReference<int32_t> *ref = CreateReference(object->GetID(),
473 pc,
474 size,
475 min_offset,
476 max_offset,
477 alignment);
478 if (pool_manager.MustEmit(pc, size, ref, object)) {
479 pc = pool_manager.Emit(&masm, pc, size);
480 delete ref;
481 // We must recreate the reference, the PC has changed.
482 ref = CreateReference(object->GetID(),
483 pc,
484 size,
485 min_offset,
486 max_offset,
487 alignment);
488 }
489 IF_VERBOSE(printf("Incrementing PC by size of reference (%d).\n", size));
490 pc += size;
491 object->AddReference(ref);
492 pool_manager.AddObjectReference(ref, object);
493 VIXL_ASSERT(!pool_manager.MustEmit(pc - 1));
494
495 // Pick another random label to bind.
496 const int kProbabilityToBind = 20;
497 if ((Random() % 100) < kProbabilityToBind) {
498 TestBranchObject *object2 = objects[RandomObjectID(objects.size())];
499 // Binding can cause the pool emission, so check if we need to emit
500 // the pools. The actual backends will know the max alignment we
501 // might need here, so can simplify the check (won't need to check
502 // the object references).
503 int max_padding = object->GetMaxAlignment() - 1;
504 if (pool_manager.MustEmit(pc, max_padding)) {
505 pc = pool_manager.Emit(&masm, pc, max_padding);
506 }
507 pc = pool_manager.Bind(&masm, object2, pc);
508 }
509
510 // Remove bound objects.
511 for (std::vector<TestBranchObject *>::iterator iter = objects.begin();
512 iter != objects.end();) {
513 TestBranchObject *obj = *iter;
514 if (obj->IsBound()) {
515 delete obj;
516 iter = objects.erase(iter);
517 } else {
518 ++iter;
519 }
520 }
521 }
522
523 pool_manager.Emit(&masm, pc);
524 }
525
526 // Test that binding an unused label works.
TEST(BindUnusedLabel)527 TEST(BindUnusedLabel) {
528 TestMacroAssembler masm;
529
530 PoolManager<int32_t> pool_manager(4 /*header_size*/,
531 2 /*header_alignment*/,
532 BUFFER_ALIGNMENT);
533 TestBranchObject *object = new TestBranchObject(4 /*size*/, 4 /*alignment*/);
534 int32_t pc = 0;
535 pool_manager.Bind(&masm, object, pc);
536 delete object;
537 }
538
539 // Test that binding a label adds necessary padding.
TEST(BindLabelNeedsPadding)540 TEST(BindLabelNeedsPadding) {
541 TestMacroAssembler masm;
542
543 PoolManager<int32_t> pool_manager(4 /*header_size*/,
544 2 /*header_alignment*/,
545 BUFFER_ALIGNMENT);
546
547 // Label that needs padding because of the minimum location of the reference.
548 TestBranchObject *object = new TestBranchObject(4 /*size*/, 2 /*alignment*/);
549 ForwardReference<int32_t> *ref =
550 new ForwardReference<int32_t>(0 /*location*/,
551 2 /*size*/,
552 4 /*min_location*/,
553 500 /*max_location*/);
554 object->AddReference(ref);
555 pool_manager.AddObjectReference(ref, object);
556 int32_t pc = 2;
557 pc = pool_manager.Bind(&masm, object, pc);
558 VIXL_ASSERT(pc == 4);
559 delete object;
560
561 // Label that needs padding because of the alignment of the object.
562 object = new TestBranchObject(4 /*size*/, 4 /*alignment*/);
563 ref = new ForwardReference<int32_t>(0 /*location*/,
564 2 /*size*/,
565 0 /*min_location*/,
566 500 /*max_location*/);
567 object->AddReference(ref);
568 pool_manager.AddObjectReference(ref, object);
569
570 pc = 2;
571 pc = pool_manager.Bind(&masm, object, pc);
572 VIXL_ASSERT(pc == 4);
573 delete object;
574
575 // Label that needs padding because of the alignment of the reference.
576 object = new TestBranchObject(4 /*size*/, 1 /*alignment*/);
577 ref = new ForwardReference<int32_t>(0 /*location*/,
578 2 /*size*/,
579 0 /*min_location*/,
580 500 /*max_location*/,
581 4 /*alignment*/);
582 object->AddReference(ref);
583 pool_manager.AddObjectReference(ref, object);
584
585 pc = 2;
586 pc = pool_manager.Bind(&masm, object, pc);
587 VIXL_ASSERT(pc == 4);
588 delete object;
589 }
590
591 // This test checks that when we omit the pool header, we insert any padding
592 // needed in order to meet the minimum location of the first object.
TEST(PoolWithoutHeaderMinLocation)593 TEST(PoolWithoutHeaderMinLocation) {
594 TestMacroAssembler masm;
595
596 PoolManager<int32_t> pool_manager(4 /*header_size*/,
597 2 /*header_alignment*/,
598 BUFFER_ALIGNMENT);
599 int object_size = 4;
600 int object_alignment = 1; // Do not restrict alignment for this test.
601 int min_location = 4; // We emit the pool at location 2, so need padding.
602 int max_location = 500;
603 TestObject object(object_size, object_alignment);
604 ForwardReference<int32_t> *ref = new ForwardReference<int32_t>(0 /*location*/,
605 2 /*size*/,
606 min_location,
607 max_location);
608 object.AddReference(ref);
609 pool_manager.AddObjectReference(ref, &object);
610
611 int32_t new_pc = pool_manager.Emit(&masm,
612 2,
613 0, /* no new code added */
614 NULL,
615 NULL,
616 PoolManager<int32_t>::kNoBranchRequired);
617 USE(new_pc);
618 VIXL_ASSERT(new_pc == min_location + object_size);
619 }
620
621 // This test checks that when we omit the pool header, we insert any padding
622 // needed in order to meet the alignment of the first object.
TEST(PoolWithoutHeaderAlignment)623 TEST(PoolWithoutHeaderAlignment) {
624 TestMacroAssembler masm;
625
626 PoolManager<int32_t> pool_manager(4 /*header_size*/,
627 2 /*header_alignment*/,
628 BUFFER_ALIGNMENT);
629 int object_size = 4;
630 int object_alignment = 4; // We emit the pool at location 2, so need padding.
631 int min_location = 0; // Do not restrict this for this test.
632 int max_location = 500;
633 TestObject object(object_size, object_alignment);
634 ForwardReference<int32_t> *ref = new ForwardReference<int32_t>(0 /*location*/,
635 2 /*size*/,
636 min_location,
637 max_location);
638 object.AddReference(ref);
639 pool_manager.AddObjectReference(ref, &object);
640
641 int32_t pc = 2;
642 int32_t new_pc = pool_manager.Emit(&masm,
643 pc,
644 0, /* no new code added */
645 NULL,
646 NULL,
647 PoolManager<int32_t>::kNoBranchRequired);
648 USE(pc);
649 USE(new_pc);
650 VIXL_ASSERT(new_pc == AlignUp(pc, object_alignment) + object_size);
651 }
652
AddNBranches(PoolManager<int32_t> * pool_manager,int32_t pc,TestBranchObject * labels[],int num_branches,int branch_size,int veneer_size,int veneer_alignment,int branch_range)653 static int32_t AddNBranches(PoolManager<int32_t> *pool_manager,
654 int32_t pc,
655 TestBranchObject *labels[],
656 int num_branches,
657 int branch_size,
658 int veneer_size,
659 int veneer_alignment,
660 int branch_range) {
661 for (int i = 0; i < num_branches; ++i) {
662 labels[i] = new TestBranchObject(veneer_size, veneer_alignment);
663 int32_t min_location = pc;
664 int32_t max_location = pc + branch_range;
665 ForwardReference<int32_t> *ref =
666 new ForwardReference<int32_t>(pc,
667 branch_size,
668 min_location,
669 max_location);
670 labels[i]->AddReference(ref);
671 // We have picked the object sizes so that we do not need to emit now.
672 VIXL_ASSERT(!pool_manager->MustEmit(pc, branch_size, ref, labels[i]));
673 pool_manager->AddObjectReference(ref, labels[i]);
674 pc += branch_size;
675 }
676 return pc;
677 }
678
TEST(MustEmitNewReferenceDueToRange)679 TEST(MustEmitNewReferenceDueToRange) {
680 const int kHeaderSize = 4;
681 const int kHeaderAlignment = 2;
682 const int kNumBranches = 550;
683 const int kBranchSize = 4;
684 const int kVeneerSize = 4;
685 const int kVeneerAlignment = 2;
686 const int kBranchRange = 1 * MBytes;
687 int32_t pc = 0;
688
689 TestMacroAssembler masm;
690 TestBranchObject *labels[kNumBranches];
691 PoolManager<int32_t> pool_manager(kHeaderSize,
692 kHeaderAlignment,
693 BUFFER_ALIGNMENT);
694 AddNBranches(&pool_manager,
695 pc,
696 labels,
697 kNumBranches,
698 kBranchSize,
699 kVeneerSize,
700 kVeneerAlignment,
701 kBranchRange);
702
703 // Increment PC to close to the checkpoint of the pools.
704 TestPoolManager test(&pool_manager);
705 pc = test.GetPoolCheckpoint() - 4;
706 VIXL_ASSERT(!pool_manager.MustEmit(pc));
707
708 // Now, attempt to add a reference that would make the problem impossible.
709 // We need to emit the pool immediately after this new instruction, and
710 // the current size of the pool is kVeneerSize * kNumBranches, so adding a
711 // short-range (smaller than the pool size) reference should trigger pool
712 // emission.
713 const int kPoolSize = kVeneerSize * kNumBranches + kHeaderSize;
714
715 const int kNewObjectSize = 2;
716 TestObject new_object(kNewObjectSize, 1);
717
718 ForwardReference<int32_t> temp_ref(pc,
719 kBranchSize,
720 pc,
721 pc + kPoolSize + kBranchSize - 1);
722 VIXL_ASSERT(pool_manager.MustEmit(pc, kBranchSize, &temp_ref, &new_object));
723
724 // Before actually emitting the pool, try a few different references to make
725 // sure that this works as expected.
726 {
727 // This reference has a large enough range, so should not force pool
728 // emission.
729 ForwardReference<int32_t> far_ref(pc,
730 kBranchSize,
731 pc,
732 pc + kPoolSize + kBranchSize);
733 VIXL_ASSERT(!pool_manager.MustEmit(pc, kBranchSize, &far_ref, &new_object));
734
735 // This reference had a large enough range but will be restricted by
736 // alignment so should force pool emission.
737 int alignment = 16;
738 VIXL_ASSERT((pc & (alignment - 1)) != 0);
739 ForwardReference<int32_t> aligned_ref(pc,
740 kBranchSize,
741 pc,
742 pc + kPoolSize + kBranchSize,
743 alignment);
744 VIXL_ASSERT(
745 pool_manager.MustEmit(pc, kBranchSize, &aligned_ref, &new_object));
746 }
747
748 // Emit the pool and check its size.
749 int32_t new_pc =
750 pool_manager.Emit(&masm, pc, kBranchSize, &temp_ref, &new_object);
751 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
752 VIXL_ASSERT(new_pc == pc + kPoolSize);
753 pc = new_pc;
754
755 // Add the new reference, safely.
756 ForwardReference<int32_t> *ref =
757 new ForwardReference<int32_t>(pc, 4 /*size*/, pc, pc + kBranchRange);
758 new_object.AddReference(ref);
759 pool_manager.AddObjectReference(ref, &new_object);
760 pc += 4;
761
762 // Emit the pool again.
763 new_pc = pool_manager.Emit(&masm, pc);
764 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
765 VIXL_ASSERT(new_pc == pc + kNewObjectSize + kHeaderSize);
766 pc = new_pc;
767
768 // Finally, bind the labels.
769 for (int i = 0; i < kNumBranches; ++i) {
770 pc = pool_manager.Bind(&masm, labels[i], pc);
771 delete labels[i];
772 }
773 }
774
TEST(MustEmitNewReferenceDueToSizeOfObject)775 TEST(MustEmitNewReferenceDueToSizeOfObject) {
776 const int kHeaderSize = 4;
777 const int kHeaderAlignment = 2;
778 const int kNumBranches = 550;
779 const int kBranchSize = 4;
780 const int kVeneerSize = 4;
781 const int kVeneerAlignment = 2;
782 const int kBranchRange = 1 * MBytes;
783 int32_t pc = 0;
784
785 TestMacroAssembler masm;
786 PoolManager<int32_t> pool_manager(kHeaderSize,
787 kHeaderAlignment,
788 BUFFER_ALIGNMENT);
789 TestBranchObject *labels[kNumBranches];
790 AddNBranches(&pool_manager,
791 pc,
792 labels,
793 kNumBranches,
794 kBranchSize,
795 kVeneerSize,
796 kVeneerAlignment,
797 kBranchRange);
798
799
800 // Increment PC to close to the checkpoint of the pools minus a known
801 // threshold.
802 const int kBigObjectSize = 1024;
803 TestPoolManager test(&pool_manager);
804 pc = test.GetPoolCheckpoint() - kBigObjectSize;
805 VIXL_ASSERT(!pool_manager.MustEmit(pc));
806
807 // Now, attempt to add a reference that would make the problem impossible.
808 // If we add a short-range (smaller than the pool size) reference with a
809 // large size (larger than the margin we have until pool emission), pool
810 // emission should be triggered.
811 const int kPoolSize = kVeneerSize * kNumBranches + kHeaderSize;
812
813 TestObject new_object(kBigObjectSize, 1);
814 ForwardReference<int32_t> temp_ref(pc, kBranchSize, pc, pc + kPoolSize);
815 VIXL_ASSERT(pool_manager.MustEmit(pc, kBranchSize, &temp_ref, &new_object));
816
817 // Before actually emitting the pool, try a few different references to make
818 // sure that this works as expected.
819 {
820 // If the object is smaller, we can emit the reference.
821 TestObject smaller_object(kBigObjectSize - 4, 1);
822 ForwardReference<int32_t> temp_ref2(pc, kBranchSize, pc, pc + kPoolSize);
823 VIXL_ASSERT(
824 !pool_manager.MustEmit(pc, kBranchSize, &temp_ref2, &smaller_object));
825
826 // If the reference is going to be added after the current objects in the
827 // pool, we can still emit it.
828 ForwardReference<int32_t> far_ref(pc, kBranchSize, pc, pc + kBranchRange);
829 VIXL_ASSERT(!pool_manager.MustEmit(pc, kBranchSize, &far_ref, &new_object));
830 }
831
832 // Emit the pool and check its size.
833 int32_t new_pc =
834 pool_manager.Emit(&masm, pc, kBranchSize, &temp_ref, &new_object);
835 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
836 VIXL_ASSERT(new_pc == pc + kPoolSize);
837 pc = new_pc;
838
839 // Add the new reference, safely.
840 ForwardReference<int32_t> *ref =
841 new ForwardReference<int32_t>(pc, 4 /*size*/, pc, pc + kBranchRange);
842 new_object.AddReference(ref);
843 pool_manager.AddObjectReference(ref, &new_object);
844 pc += 4;
845
846 // Emit the pool again.
847 new_pc = pool_manager.Emit(&masm, pc);
848 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
849 VIXL_ASSERT(new_pc == pc + kBigObjectSize + kHeaderSize);
850 pc = new_pc;
851
852 // Finally, bind the labels.
853 for (int i = 0; i < kNumBranches; ++i) {
854 pc = pool_manager.Bind(&masm, labels[i], pc);
855 delete labels[i];
856 }
857 }
858
859 template <typename ObjectType>
ManagedLocationBaseTestHelper()860 void ManagedLocationBaseTestHelper() {
861 TestMacroAssembler masm;
862
863 PoolManager<int32_t> pool_manager(4 /*header_size*/,
864 2 /*header_alignment*/,
865 BUFFER_ALIGNMENT);
866 ObjectType *object1 = new ObjectType();
867 ObjectType *object2 = new ObjectType();
868 ForwardReference<int32_t> *ref_obj1 =
869 new ForwardReference<int32_t>(0 /*location*/, 2 /*size*/, 0, 200);
870 object1->AddReference(ref_obj1);
871 ForwardReference<int32_t> *ref_obj2 =
872 new ForwardReference<int32_t>(8 /*location*/, 2 /*size*/, 8, 500);
873 object2->AddReference(ref_obj2);
874
875 pool_manager.AddObjectReference(ref_obj1, object1);
876 pool_manager.AddObjectReference(ref_obj2, object2);
877
878 pool_manager.Emit(&masm, 20);
879 }
880
881 class TestObjectDeletedOnPlacement : public TestObject {
882 public:
TestObjectDeletedOnPlacement()883 TestObjectDeletedOnPlacement() : TestObject(4 /*size*/, 4 /*alignment*/) {}
884 // After passing ownership of this type of object to the pool manager, it is
885 // not safe to use it anymore.
ShouldBeDeletedOnPlacementByPoolManager() const886 virtual bool ShouldBeDeletedOnPlacementByPoolManager() const VIXL_OVERRIDE {
887 return true;
888 }
889 };
890
TEST(DeleteLocationBaseOnPlacement)891 TEST(DeleteLocationBaseOnPlacement) {
892 ManagedLocationBaseTestHelper<TestObjectDeletedOnPlacement>();
893 }
894
895 class TestObjectDeletedOnPoolManagerDestruction : public TestObject {
896 public:
TestObjectDeletedOnPoolManagerDestruction()897 TestObjectDeletedOnPoolManagerDestruction()
898 : TestObject(4 /*size*/, 4 /*alignment*/) {}
899 // We can continue using this type of object after passing its ownership to
900 // the pool manager, as it will be deleted only when the pool manager is
901 // destroyed.
ShouldBeDeletedOnPoolManagerDestruction() const902 virtual bool ShouldBeDeletedOnPoolManagerDestruction() const VIXL_OVERRIDE {
903 return true;
904 }
905 };
906
907
TEST(DeleteLocationBaseOnPoolManagerDestruction)908 TEST(DeleteLocationBaseOnPoolManagerDestruction) {
909 ManagedLocationBaseTestHelper<TestObjectDeletedOnPoolManagerDestruction>();
910 }
911