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 }
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(printf(
347 "About to add a new reference to object %d with min location = %d, max "
348 "location = %d, alignment = %d, size = %d\n",
349 id,
350 min_offset + pc,
351 max_offset + pc,
352 alignment,
353 size));
354 return new ForwardReference<int32_t>(pc,
355 size,
356 min_offset + pc,
357 max_offset + pc,
358 alignment);
359 }
360
361 // Fuzz test that uses literal-like objects, that get deleted when they are
362 // placed.
TEST(FuzzObjectDeletedWhenPlaced)363 TEST(FuzzObjectDeletedWhenPlaced) {
364 TestMacroAssembler masm;
365 PoolManager<int32_t> pool_manager(4 /*header_size*/,
366 2 /*header_alignment*/,
367 BUFFER_ALIGNMENT);
368
369 const int kObjectNum = 100;
370 std::vector<TestObject *> objects;
371
372 // Create objects.
373 for (int i = 0; i < kObjectNum; ++i) {
374 objects.push_back(TestObject::CreateRandom(i));
375 }
376
377 int32_t pc = 0;
378 for (int i = 0; !objects.empty(); ++i) {
379 IF_VERBOSE(printf("PC = 0x%x (%d)\n", pc, pc));
380 int32_t pc_increment = RandomPCIncrement();
381 IF_VERBOSE(printf("Attempting to increment PC by %d\n", pc_increment));
382 if (pool_manager.MustEmit(pc, pc_increment)) {
383 pc = pool_manager.Emit(&masm, pc, pc_increment);
384 }
385 pc += pc_increment;
386 // Pick an object, randomly.
387 TestObject *object = objects[RandomObjectID(objects.size())];
388 int32_t min_offset = RandomMinOffset();
389 int32_t max_offset = RandomMaxOffset();
390 int32_t size = RandomReferenceSize();
391 int32_t alignment =
392 RandomObjectAlignment(object->GetPoolObjectSizeInBytes());
393 ForwardReference<int32_t> *ref = CreateReference(object->GetID(),
394 pc,
395 size,
396 min_offset,
397 max_offset,
398 alignment);
399 if (pool_manager.MustEmit(pc, size, ref, object)) {
400 pc = pool_manager.Emit(&masm, pc, size, ref, object);
401 delete ref;
402 // We must recreate the reference, the PC has changed, but only if
403 // it still is a forward reference.
404 if (!object->IsBound()) {
405 ref = CreateReference(object->GetID(),
406 pc,
407 size,
408 min_offset,
409 max_offset,
410 alignment);
411 }
412 }
413 IF_VERBOSE(printf("Incrementing PC by size of reference (%d).\n", size));
414 pc += size;
415 // We only need to track the reference if it's a forward reference.
416 if (!object->IsBound()) {
417 object->AddReference(ref);
418 pool_manager.AddObjectReference(ref, object);
419 }
420 VIXL_ASSERT(!pool_manager.MustEmit(pc - 1));
421 // Remove bound objects.
422 for (std::vector<TestObject *>::iterator iter = objects.begin();
423 iter != objects.end();) {
424 TestObject *object = *iter;
425 if (object->IsBound()) {
426 delete object;
427 iter = objects.erase(iter);
428 } else {
429 ++iter;
430 }
431 }
432 }
433
434 pool_manager.Emit(&masm, pc);
435 }
436
437 // Fuzz test that uses veneer-like objects, that get updated when they are
438 // placed and get deleted when they are bound by the user.
TEST(FuzzObjectUpdatedWhenPlaced)439 TEST(FuzzObjectUpdatedWhenPlaced) {
440 TestMacroAssembler masm;
441 PoolManager<int32_t> pool_manager(4 /*header_size*/,
442 2 /*header_alignment*/,
443 BUFFER_ALIGNMENT);
444 const int kObjectNum = 1000;
445 std::vector<TestBranchObject *> objects;
446
447 // Create objects.
448 for (int i = 0; i < kObjectNum; ++i) {
449 objects.push_back(TestBranchObject::CreateRandom(i));
450 }
451
452 int32_t pc = 0;
453 for (int i = 0; !objects.empty(); ++i) {
454 IF_VERBOSE(printf("PC = 0x%x (%d)\n", pc, pc));
455
456 int32_t pc_increment = RandomPCIncrement();
457 IF_VERBOSE(printf("Attempting to increment PC by %d\n", pc_increment));
458
459 if (pool_manager.MustEmit(pc, pc_increment)) {
460 pc = pool_manager.Emit(&masm, pc, pc_increment);
461 }
462 pc += pc_increment;
463
464 // Pick a random object.
465 TestBranchObject *object = objects[RandomObjectID(objects.size())];
466 int32_t min_offset = RandomMinOffset();
467 int32_t max_offset = RandomBranchMaxOffset();
468 int32_t size = RandomReferenceSize();
469 int32_t alignment =
470 RandomObjectAlignment(object->GetPoolObjectSizeInBytes());
471 ForwardReference<int32_t> *ref = CreateReference(object->GetID(),
472 pc,
473 size,
474 min_offset,
475 max_offset,
476 alignment);
477 if (pool_manager.MustEmit(pc, size, ref, object)) {
478 pc = pool_manager.Emit(&masm, pc, size);
479 delete ref;
480 // We must recreate the reference, the PC has changed.
481 ref = CreateReference(object->GetID(),
482 pc,
483 size,
484 min_offset,
485 max_offset,
486 alignment);
487 }
488 IF_VERBOSE(printf("Incrementing PC by size of reference (%d).\n", size));
489 pc += size;
490 object->AddReference(ref);
491 pool_manager.AddObjectReference(ref, object);
492 VIXL_ASSERT(!pool_manager.MustEmit(pc - 1));
493
494 // Pick another random label to bind.
495 const int kProbabilityToBind = 20;
496 if ((Random() % 100) < kProbabilityToBind) {
497 TestBranchObject *object = objects[RandomObjectID(objects.size())];
498 // Binding can cause the pool emission, so check if we need to emit
499 // the pools. The actual backends will know the max alignment we
500 // might need here, so can simplify the check (won't need to check
501 // the object references).
502 int max_padding = object->GetMaxAlignment() - 1;
503 if (pool_manager.MustEmit(pc, max_padding)) {
504 pc = pool_manager.Emit(&masm, pc, max_padding);
505 }
506 pc = pool_manager.Bind(&masm, object, pc);
507 }
508
509 // Remove bound objects.
510 for (std::vector<TestBranchObject *>::iterator iter = objects.begin();
511 iter != objects.end();) {
512 TestBranchObject *object = *iter;
513 if (object->IsBound()) {
514 delete object;
515 iter = objects.erase(iter);
516 } else {
517 ++iter;
518 }
519 }
520 }
521
522 pool_manager.Emit(&masm, pc);
523 }
524
525 // Test that binding an unused label works.
TEST(BindUnusedLabel)526 TEST(BindUnusedLabel) {
527 TestMacroAssembler masm;
528
529 PoolManager<int32_t> pool_manager(4 /*header_size*/,
530 2 /*header_alignment*/,
531 BUFFER_ALIGNMENT);
532 TestBranchObject *object = new TestBranchObject(4 /*size*/, 4 /*alignment*/);
533 int32_t pc = 0;
534 pool_manager.Bind(&masm, object, pc);
535 delete object;
536 }
537
538 // Test that binding a label adds necessary padding.
TEST(BindLabelNeedsPadding)539 TEST(BindLabelNeedsPadding) {
540 TestMacroAssembler masm;
541
542 PoolManager<int32_t> pool_manager(4 /*header_size*/,
543 2 /*header_alignment*/,
544 BUFFER_ALIGNMENT);
545
546 // Label that needs padding because of the minimum location of the reference.
547 TestBranchObject *object = new TestBranchObject(4 /*size*/, 2 /*alignment*/);
548 ForwardReference<int32_t> *ref =
549 new ForwardReference<int32_t>(0 /*location*/,
550 2 /*size*/,
551 4 /*min_location*/,
552 500 /*max_location*/);
553 object->AddReference(ref);
554 pool_manager.AddObjectReference(ref, object);
555 int32_t pc = 2;
556 pc = pool_manager.Bind(&masm, object, pc);
557 VIXL_ASSERT(pc == 4);
558 delete object;
559
560 // Label that needs padding because of the alignment of the object.
561 object = new TestBranchObject(4 /*size*/, 4 /*alignment*/);
562 ref = new ForwardReference<int32_t>(0 /*location*/,
563 2 /*size*/,
564 0 /*min_location*/,
565 500 /*max_location*/);
566 object->AddReference(ref);
567 pool_manager.AddObjectReference(ref, object);
568
569 pc = 2;
570 pc = pool_manager.Bind(&masm, object, pc);
571 VIXL_ASSERT(pc == 4);
572 delete object;
573
574 // Label that needs padding because of the alignment of the reference.
575 object = new TestBranchObject(4 /*size*/, 1 /*alignment*/);
576 ref = new ForwardReference<int32_t>(0 /*location*/,
577 2 /*size*/,
578 0 /*min_location*/,
579 500 /*max_location*/,
580 4 /*alignment*/);
581 object->AddReference(ref);
582 pool_manager.AddObjectReference(ref, object);
583
584 pc = 2;
585 pc = pool_manager.Bind(&masm, object, pc);
586 VIXL_ASSERT(pc == 4);
587 delete object;
588 }
589
590 // This test checks that when we omit the pool header, we insert any padding
591 // needed in order to meet the minimum location of the first object.
TEST(PoolWithoutHeaderMinLocation)592 TEST(PoolWithoutHeaderMinLocation) {
593 TestMacroAssembler masm;
594
595 PoolManager<int32_t> pool_manager(4 /*header_size*/,
596 2 /*header_alignment*/,
597 BUFFER_ALIGNMENT);
598 int object_size = 4;
599 int object_alignment = 1; // Do not restrict alignment for this test.
600 int min_location = 4; // We emit the pool at location 2, so need padding.
601 int max_location = 500;
602 TestObject object(object_size, object_alignment);
603 ForwardReference<int32_t> *ref = new ForwardReference<int32_t>(0 /*location*/,
604 2 /*size*/,
605 min_location,
606 max_location);
607 object.AddReference(ref);
608 pool_manager.AddObjectReference(ref, &object);
609
610 int32_t new_pc = pool_manager.Emit(&masm,
611 2,
612 0, /* no new code added */
613 NULL,
614 NULL,
615 PoolManager<int32_t>::kNoBranchRequired);
616 USE(new_pc);
617 VIXL_ASSERT(new_pc == min_location + object_size);
618 }
619
620 // This test checks that when we omit the pool header, we insert any padding
621 // needed in order to meet the alignment of the first object.
TEST(PoolWithoutHeaderAlignment)622 TEST(PoolWithoutHeaderAlignment) {
623 TestMacroAssembler masm;
624
625 PoolManager<int32_t> pool_manager(4 /*header_size*/,
626 2 /*header_alignment*/,
627 BUFFER_ALIGNMENT);
628 int object_size = 4;
629 int object_alignment = 4; // We emit the pool at location 2, so need padding.
630 int min_location = 0; // Do not restrict this for this test.
631 int max_location = 500;
632 TestObject object(object_size, object_alignment);
633 ForwardReference<int32_t> *ref = new ForwardReference<int32_t>(0 /*location*/,
634 2 /*size*/,
635 min_location,
636 max_location);
637 object.AddReference(ref);
638 pool_manager.AddObjectReference(ref, &object);
639
640 int32_t pc = 2;
641 int32_t new_pc = pool_manager.Emit(&masm,
642 pc,
643 0, /* no new code added */
644 NULL,
645 NULL,
646 PoolManager<int32_t>::kNoBranchRequired);
647 USE(pc);
648 USE(new_pc);
649 VIXL_ASSERT(new_pc == AlignUp(pc, object_alignment) + object_size);
650 }
651
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)652 static int32_t AddNBranches(PoolManager<int32_t> *pool_manager,
653 int32_t pc,
654 TestBranchObject *labels[],
655 int num_branches,
656 int branch_size,
657 int veneer_size,
658 int veneer_alignment,
659 int branch_range) {
660 for (int i = 0; i < num_branches; ++i) {
661 labels[i] = new TestBranchObject(veneer_size, veneer_alignment);
662 int32_t min_location = pc;
663 int32_t max_location = pc + branch_range;
664 ForwardReference<int32_t> *ref =
665 new ForwardReference<int32_t>(pc,
666 branch_size,
667 min_location,
668 max_location);
669 labels[i]->AddReference(ref);
670 // We have picked the object sizes so that we do not need to emit now.
671 VIXL_ASSERT(!pool_manager->MustEmit(pc, branch_size, ref, labels[i]));
672 pool_manager->AddObjectReference(ref, labels[i]);
673 pc += branch_size;
674 }
675 return pc;
676 }
677
TEST(MustEmitNewReferenceDueToRange)678 TEST(MustEmitNewReferenceDueToRange) {
679 const int kHeaderSize = 4;
680 const int kHeaderAlignment = 2;
681 const int kNumBranches = 550;
682 const int kBranchSize = 4;
683 const int kVeneerSize = 4;
684 const int kVeneerAlignment = 2;
685 const int kBranchRange = 1 * MBytes;
686 int32_t pc = 0;
687
688 TestMacroAssembler masm;
689 TestBranchObject *labels[kNumBranches];
690 PoolManager<int32_t> pool_manager(kHeaderSize,
691 kHeaderAlignment,
692 BUFFER_ALIGNMENT);
693 pc = AddNBranches(&pool_manager,
694 pc,
695 labels,
696 kNumBranches,
697 kBranchSize,
698 kVeneerSize,
699 kVeneerAlignment,
700 kBranchRange);
701
702 // Increment PC to close to the checkpoint of the pools.
703 TestPoolManager test(&pool_manager);
704 pc = test.GetPoolCheckpoint() - 4;
705 VIXL_ASSERT(!pool_manager.MustEmit(pc));
706
707 // Now, attempt to add a reference that would make the problem impossible.
708 // We need to emit the pool immediately after this new instruction, and
709 // the current size of the pool is kVeneerSize * kNumBranches, so adding a
710 // short-range (smaller than the pool size) reference should trigger pool
711 // emission.
712 const int kPoolSize = kVeneerSize * kNumBranches + kHeaderSize;
713
714 const int kNewObjectSize = 2;
715 TestObject new_object(kNewObjectSize, 1);
716
717 ForwardReference<int32_t> temp_ref(pc,
718 kBranchSize,
719 pc,
720 pc + kPoolSize + kBranchSize - 1);
721 VIXL_ASSERT(pool_manager.MustEmit(pc, kBranchSize, &temp_ref, &new_object));
722
723 // Before actually emitting the pool, try a few different references to make
724 // sure that this works as expected.
725 {
726 // This reference has a large enough range, so should not force pool
727 // emission.
728 ForwardReference<int32_t> far_ref(pc,
729 kBranchSize,
730 pc,
731 pc + kPoolSize + kBranchSize);
732 VIXL_ASSERT(!pool_manager.MustEmit(pc, kBranchSize, &far_ref, &new_object));
733
734 // This reference had a large enough range but will be restricted by
735 // alignment so should force pool emission.
736 int alignment = 16;
737 VIXL_ASSERT((pc & (alignment - 1)) != 0);
738 ForwardReference<int32_t> aligned_ref(pc,
739 kBranchSize,
740 pc,
741 pc + kPoolSize + kBranchSize,
742 alignment);
743 VIXL_ASSERT(
744 pool_manager.MustEmit(pc, kBranchSize, &aligned_ref, &new_object));
745 }
746
747 // Emit the pool and check its size.
748 int32_t new_pc =
749 pool_manager.Emit(&masm, pc, kBranchSize, &temp_ref, &new_object);
750 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
751 VIXL_ASSERT(new_pc == pc + kPoolSize);
752 pc = new_pc;
753
754 // Add the new reference, safely.
755 ForwardReference<int32_t> *ref =
756 new ForwardReference<int32_t>(pc, 4 /*size*/, pc, pc + kBranchRange);
757 new_object.AddReference(ref);
758 pool_manager.AddObjectReference(ref, &new_object);
759 pc += 4;
760
761 // Emit the pool again.
762 new_pc = pool_manager.Emit(&masm, pc);
763 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
764 VIXL_ASSERT(new_pc == pc + kNewObjectSize + kHeaderSize);
765 pc = new_pc;
766
767 // Finally, bind the labels.
768 for (int i = 0; i < kNumBranches; ++i) {
769 pc = pool_manager.Bind(&masm, labels[i], pc);
770 delete labels[i];
771 }
772 }
773
TEST(MustEmitNewReferenceDueToSizeOfObject)774 TEST(MustEmitNewReferenceDueToSizeOfObject) {
775 const int kHeaderSize = 4;
776 const int kHeaderAlignment = 2;
777 const int kNumBranches = 550;
778 const int kBranchSize = 4;
779 const int kVeneerSize = 4;
780 const int kVeneerAlignment = 2;
781 const int kBranchRange = 1 * MBytes;
782 int32_t pc = 0;
783
784 TestMacroAssembler masm;
785 PoolManager<int32_t> pool_manager(kHeaderSize,
786 kHeaderAlignment,
787 BUFFER_ALIGNMENT);
788 TestBranchObject *labels[kNumBranches];
789 pc = AddNBranches(&pool_manager,
790 pc,
791 labels,
792 kNumBranches,
793 kBranchSize,
794 kVeneerSize,
795 kVeneerAlignment,
796 kBranchRange);
797
798
799 // Increment PC to close to the checkpoint of the pools minus a known
800 // thershold.
801 const int kBigObjectSize = 1024;
802 TestPoolManager test(&pool_manager);
803 pc = test.GetPoolCheckpoint() - kBigObjectSize;
804 VIXL_ASSERT(!pool_manager.MustEmit(pc));
805
806 // Now, attempt to add a reference that would make the problem impossible.
807 // If we add a short-range (smaller than the pool size) reference with a
808 // large size (larger than the margin we have until pool emission), pool
809 // emission should be triggered.
810 const int kPoolSize = kVeneerSize * kNumBranches + kHeaderSize;
811
812 TestObject new_object(kBigObjectSize, 1);
813 ForwardReference<int32_t> temp_ref(pc, kBranchSize, pc, pc + kPoolSize);
814 VIXL_ASSERT(pool_manager.MustEmit(pc, kBranchSize, &temp_ref, &new_object));
815
816 // Before actually emitting the pool, try a few different references to make
817 // sure that this works as expected.
818 {
819 // If the object is smaller, we can emit the reference.
820 TestObject smaller_object(kBigObjectSize - 4, 1);
821 ForwardReference<int32_t> temp_ref(pc, kBranchSize, pc, pc + kPoolSize);
822 VIXL_ASSERT(
823 !pool_manager.MustEmit(pc, kBranchSize, &temp_ref, &smaller_object));
824
825 // If the reference is going to be added after the current objects in the
826 // pool, we can still emit it.
827 ForwardReference<int32_t> far_ref(pc, kBranchSize, pc, pc + kBranchRange);
828 VIXL_ASSERT(!pool_manager.MustEmit(pc, kBranchSize, &far_ref, &new_object));
829 }
830
831 // Emit the pool and check its size.
832 int32_t new_pc =
833 pool_manager.Emit(&masm, pc, kBranchSize, &temp_ref, &new_object);
834 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
835 VIXL_ASSERT(new_pc == pc + kPoolSize);
836 pc = new_pc;
837
838 // Add the new reference, safely.
839 ForwardReference<int32_t> *ref =
840 new ForwardReference<int32_t>(pc, 4 /*size*/, pc, pc + kBranchRange);
841 new_object.AddReference(ref);
842 pool_manager.AddObjectReference(ref, &new_object);
843 pc += 4;
844
845 // Emit the pool again.
846 new_pc = pool_manager.Emit(&masm, pc);
847 VIXL_ASSERT(pc % kHeaderAlignment == 0); // No need for padding.
848 VIXL_ASSERT(new_pc == pc + kBigObjectSize + kHeaderSize);
849 pc = new_pc;
850
851 // Finally, bind the labels.
852 for (int i = 0; i < kNumBranches; ++i) {
853 pc = pool_manager.Bind(&masm, labels[i], pc);
854 delete labels[i];
855 }
856 }
857
858 template <typename ObjectType>
ManagedLocationBaseTestHelper()859 void ManagedLocationBaseTestHelper() {
860 TestMacroAssembler masm;
861
862 PoolManager<int32_t> pool_manager(4 /*header_size*/,
863 2 /*header_alignment*/,
864 BUFFER_ALIGNMENT);
865 ObjectType *object1 = new ObjectType();
866 ObjectType *object2 = new ObjectType();
867 ForwardReference<int32_t> *ref_obj1 =
868 new ForwardReference<int32_t>(0 /*location*/, 2 /*size*/, 0, 200);
869 object1->AddReference(ref_obj1);
870 ForwardReference<int32_t> *ref_obj2 =
871 new ForwardReference<int32_t>(8 /*location*/, 2 /*size*/, 8, 500);
872 object2->AddReference(ref_obj2);
873
874 pool_manager.AddObjectReference(ref_obj1, object1);
875 pool_manager.AddObjectReference(ref_obj2, object2);
876
877 pool_manager.Emit(&masm, 20);
878 }
879
880 class TestObjectDeletedOnPlacement : public TestObject {
881 public:
TestObjectDeletedOnPlacement()882 TestObjectDeletedOnPlacement() : TestObject(4 /*size*/, 4 /*alignment*/) {}
883 // After passing ownership of this type of object to the pool manager, it is
884 // not safe to use it anymore.
ShouldBeDeletedOnPlacementByPoolManager() const885 virtual bool ShouldBeDeletedOnPlacementByPoolManager() const VIXL_OVERRIDE {
886 return true;
887 }
888 };
889
TEST(DeleteLocationBaseOnPlacement)890 TEST(DeleteLocationBaseOnPlacement) {
891 ManagedLocationBaseTestHelper<TestObjectDeletedOnPlacement>();
892 }
893
894 class TestObjectDeletedOnPoolManagerDestruction : public TestObject {
895 public:
TestObjectDeletedOnPoolManagerDestruction()896 TestObjectDeletedOnPoolManagerDestruction()
897 : TestObject(4 /*size*/, 4 /*alignment*/) {}
898 // We can continue using this type of object after passing its ownership to
899 // the pool manager, as it will be deleted only when the pool manager is
900 // destroyed.
ShouldBeDeletedOnPoolManagerDestruction() const901 virtual bool ShouldBeDeletedOnPoolManagerDestruction() const VIXL_OVERRIDE {
902 return true;
903 }
904 };
905
906
TEST(DeleteLocationBaseOnPoolManagerDestruction)907 TEST(DeleteLocationBaseOnPoolManagerDestruction) {
908 ManagedLocationBaseTestHelper<TestObjectDeletedOnPoolManagerDestruction>();
909 }
910