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