1 // Copyright 2016 the V8 project authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file.
4
5 #include <cmath>
6 #include <functional>
7 #include <limits>
8
9 #include "src/base/bits.h"
10 #include "src/base/utils/random-number-generator.h"
11 #include "src/codegen.h"
12 #include "test/cctest/cctest.h"
13 #include "test/cctest/compiler/codegen-tester.h"
14 #include "test/cctest/compiler/graph-builder-tester.h"
15 #include "test/cctest/compiler/value-helper.h"
16
17 using namespace v8::base;
18
19 namespace {
20 template <typename Type>
CheckOobValue(Type val)21 void CheckOobValue(Type val) {
22 UNREACHABLE();
23 }
24
25 template <>
CheckOobValue(int32_t val)26 void CheckOobValue(int32_t val) {
27 CHECK_EQ(0, val);
28 }
29
30 template <>
CheckOobValue(int64_t val)31 void CheckOobValue(int64_t val) {
32 CHECK_EQ(0, val);
33 }
34
35 template <>
CheckOobValue(float val)36 void CheckOobValue(float val) {
37 CHECK(std::isnan(val));
38 }
39
40 template <>
CheckOobValue(double val)41 void CheckOobValue(double val) {
42 CHECK(std::isnan(val));
43 }
44 } // namespace
45
46 namespace v8 {
47 namespace internal {
48 namespace compiler {
49
50 // This is a America!
51 #define A_BILLION 1000000000ULL
52 #define A_GIG (1024ULL * 1024ULL * 1024ULL)
53
TEST(RunLoadInt32)54 TEST(RunLoadInt32) {
55 RawMachineAssemblerTester<int32_t> m;
56
57 int32_t p1 = 0; // loads directly from this location.
58 m.Return(m.LoadFromPointer(&p1, MachineType::Int32()));
59
60 FOR_INT32_INPUTS(i) {
61 p1 = *i;
62 CHECK_EQ(p1, m.Call());
63 }
64 }
65
TEST(RunLoadInt32Offset)66 TEST(RunLoadInt32Offset) {
67 int32_t p1 = 0; // loads directly from this location.
68
69 int32_t offsets[] = {-2000000, -100, -101, 1, 3,
70 7, 120, 2000, 2000000000, 0xff};
71
72 for (size_t i = 0; i < arraysize(offsets); i++) {
73 RawMachineAssemblerTester<int32_t> m;
74 int32_t offset = offsets[i];
75 byte* pointer = reinterpret_cast<byte*>(&p1) - offset;
76 // generate load [#base + #index]
77 m.Return(m.LoadFromPointer(pointer, MachineType::Int32(), offset));
78
79 FOR_INT32_INPUTS(j) {
80 p1 = *j;
81 CHECK_EQ(p1, m.Call());
82 }
83 }
84 }
85
TEST(RunLoadStoreFloat32Offset)86 TEST(RunLoadStoreFloat32Offset) {
87 float p1 = 0.0f; // loads directly from this location.
88 float p2 = 0.0f; // and stores directly into this location.
89
90 FOR_INT32_INPUTS(i) {
91 int32_t magic = 0x2342aabb + *i * 3;
92 RawMachineAssemblerTester<int32_t> m;
93 int32_t offset = *i;
94 byte* from = reinterpret_cast<byte*>(&p1) - offset;
95 byte* to = reinterpret_cast<byte*>(&p2) - offset;
96 // generate load [#base + #index]
97 Node* load = m.Load(MachineType::Float32(), m.PointerConstant(from),
98 m.IntPtrConstant(offset));
99 m.Store(MachineRepresentation::kFloat32, m.PointerConstant(to),
100 m.IntPtrConstant(offset), load, kNoWriteBarrier);
101 m.Return(m.Int32Constant(magic));
102
103 FOR_FLOAT32_INPUTS(j) {
104 p1 = *j;
105 p2 = *j - 5;
106 CHECK_EQ(magic, m.Call());
107 CheckDoubleEq(p1, p2);
108 }
109 }
110 }
111
TEST(RunLoadStoreFloat64Offset)112 TEST(RunLoadStoreFloat64Offset) {
113 double p1 = 0; // loads directly from this location.
114 double p2 = 0; // and stores directly into this location.
115
116 FOR_INT32_INPUTS(i) {
117 int32_t magic = 0x2342aabb + *i * 3;
118 RawMachineAssemblerTester<int32_t> m;
119 int32_t offset = *i;
120 byte* from = reinterpret_cast<byte*>(&p1) - offset;
121 byte* to = reinterpret_cast<byte*>(&p2) - offset;
122 // generate load [#base + #index]
123 Node* load = m.Load(MachineType::Float64(), m.PointerConstant(from),
124 m.IntPtrConstant(offset));
125 m.Store(MachineRepresentation::kFloat64, m.PointerConstant(to),
126 m.IntPtrConstant(offset), load, kNoWriteBarrier);
127 m.Return(m.Int32Constant(magic));
128
129 FOR_FLOAT64_INPUTS(j) {
130 p1 = *j;
131 p2 = *j - 5;
132 CHECK_EQ(magic, m.Call());
133 CheckDoubleEq(p1, p2);
134 }
135 }
136 }
137
138 namespace {
139 template <typename Type>
RunLoadImmIndex(MachineType rep)140 void RunLoadImmIndex(MachineType rep) {
141 const int kNumElems = 3;
142 Type buffer[kNumElems];
143
144 // initialize the buffer with some raw data.
145 byte* raw = reinterpret_cast<byte*>(buffer);
146 for (size_t i = 0; i < sizeof(buffer); i++) {
147 raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
148 }
149
150 // Test with various large and small offsets.
151 for (int offset = -1; offset <= 200000; offset *= -5) {
152 for (int i = 0; i < kNumElems; i++) {
153 BufferedRawMachineAssemblerTester<Type> m;
154 Node* base = m.PointerConstant(buffer - offset);
155 Node* index = m.Int32Constant((offset + i) * sizeof(buffer[0]));
156 m.Return(m.Load(rep, base, index));
157
158 volatile Type expected = buffer[i];
159 volatile Type actual = m.Call();
160 CHECK_EQ(expected, actual);
161 }
162 }
163 }
164
165 template <typename CType>
RunLoadStore(MachineType rep)166 void RunLoadStore(MachineType rep) {
167 const int kNumElems = 4;
168 CType buffer[kNumElems];
169
170 for (int32_t x = 0; x < kNumElems; x++) {
171 int32_t y = kNumElems - x - 1;
172 // initialize the buffer with raw data.
173 byte* raw = reinterpret_cast<byte*>(buffer);
174 for (size_t i = 0; i < sizeof(buffer); i++) {
175 raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
176 }
177
178 RawMachineAssemblerTester<int32_t> m;
179 int32_t OK = 0x29000 + x;
180 Node* base = m.PointerConstant(buffer);
181 Node* index0 = m.IntPtrConstant(x * sizeof(buffer[0]));
182 Node* load = m.Load(rep, base, index0);
183 Node* index1 = m.IntPtrConstant(y * sizeof(buffer[0]));
184 m.Store(rep.representation(), base, index1, load, kNoWriteBarrier);
185 m.Return(m.Int32Constant(OK));
186
187 CHECK(buffer[x] != buffer[y]);
188 CHECK_EQ(OK, m.Call());
189 CHECK(buffer[x] == buffer[y]);
190 }
191 }
192 } // namespace
193
TEST(RunLoadImmIndex)194 TEST(RunLoadImmIndex) {
195 RunLoadImmIndex<int8_t>(MachineType::Int8());
196 RunLoadImmIndex<uint8_t>(MachineType::Uint8());
197 RunLoadImmIndex<int16_t>(MachineType::Int16());
198 RunLoadImmIndex<uint16_t>(MachineType::Uint16());
199 RunLoadImmIndex<int32_t>(MachineType::Int32());
200 RunLoadImmIndex<uint32_t>(MachineType::Uint32());
201 RunLoadImmIndex<int32_t*>(MachineType::AnyTagged());
202 RunLoadImmIndex<float>(MachineType::Float32());
203 RunLoadImmIndex<double>(MachineType::Float64());
204 #if V8_TARGET_ARCH_64_BIT
205 RunLoadImmIndex<int64_t>(MachineType::Int64());
206 #endif
207 // TODO(titzer): test various indexing modes.
208 }
209
TEST(RunLoadStore)210 TEST(RunLoadStore) {
211 RunLoadStore<int8_t>(MachineType::Int8());
212 RunLoadStore<uint8_t>(MachineType::Uint8());
213 RunLoadStore<int16_t>(MachineType::Int16());
214 RunLoadStore<uint16_t>(MachineType::Uint16());
215 RunLoadStore<int32_t>(MachineType::Int32());
216 RunLoadStore<uint32_t>(MachineType::Uint32());
217 RunLoadStore<void*>(MachineType::AnyTagged());
218 RunLoadStore<float>(MachineType::Float32());
219 RunLoadStore<double>(MachineType::Float64());
220 #if V8_TARGET_ARCH_64_BIT
221 RunLoadStore<int64_t>(MachineType::Int64());
222 #endif
223 }
224
225 #if V8_TARGET_LITTLE_ENDIAN
226 #define LSB(addr, bytes) addr
227 #elif V8_TARGET_BIG_ENDIAN
228 #define LSB(addr, bytes) reinterpret_cast<byte*>(addr + 1) - bytes
229 #else
230 #error "Unknown Architecture"
231 #endif
232
TEST(RunLoadStoreSignExtend32)233 TEST(RunLoadStoreSignExtend32) {
234 int32_t buffer[4];
235 RawMachineAssemblerTester<int32_t> m;
236 Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Int8());
237 Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Int16());
238 Node* load32 = m.LoadFromPointer(&buffer[0], MachineType::Int32());
239 m.StoreToPointer(&buffer[1], MachineRepresentation::kWord32, load8);
240 m.StoreToPointer(&buffer[2], MachineRepresentation::kWord32, load16);
241 m.StoreToPointer(&buffer[3], MachineRepresentation::kWord32, load32);
242 m.Return(load8);
243
244 FOR_INT32_INPUTS(i) {
245 buffer[0] = *i;
246
247 CHECK_EQ(static_cast<int8_t>(*i & 0xff), m.Call());
248 CHECK_EQ(static_cast<int8_t>(*i & 0xff), buffer[1]);
249 CHECK_EQ(static_cast<int16_t>(*i & 0xffff), buffer[2]);
250 CHECK_EQ(*i, buffer[3]);
251 }
252 }
253
TEST(RunLoadStoreZeroExtend32)254 TEST(RunLoadStoreZeroExtend32) {
255 uint32_t buffer[4];
256 RawMachineAssemblerTester<uint32_t> m;
257 Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Uint8());
258 Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Uint16());
259 Node* load32 = m.LoadFromPointer(&buffer[0], MachineType::Uint32());
260 m.StoreToPointer(&buffer[1], MachineRepresentation::kWord32, load8);
261 m.StoreToPointer(&buffer[2], MachineRepresentation::kWord32, load16);
262 m.StoreToPointer(&buffer[3], MachineRepresentation::kWord32, load32);
263 m.Return(load8);
264
265 FOR_UINT32_INPUTS(i) {
266 buffer[0] = *i;
267
268 CHECK_EQ((*i & 0xff), m.Call());
269 CHECK_EQ((*i & 0xff), buffer[1]);
270 CHECK_EQ((*i & 0xffff), buffer[2]);
271 CHECK_EQ(*i, buffer[3]);
272 }
273 }
274
275 #if V8_TARGET_ARCH_64_BIT
TEST(RunCheckedLoadInt64)276 TEST(RunCheckedLoadInt64) {
277 int64_t buffer[] = {0x66bbccddeeff0011LL, 0x1122334455667788LL};
278 RawMachineAssemblerTester<int64_t> m(MachineType::Int32());
279 Node* base = m.PointerConstant(buffer);
280 Node* index = m.Parameter(0);
281 Node* length = m.Int32Constant(16);
282 Node* load = m.AddNode(m.machine()->CheckedLoad(MachineType::Int64()), base,
283 index, length);
284 m.Return(load);
285
286 CHECK_EQ(buffer[0], m.Call(0));
287 CHECK_EQ(buffer[1], m.Call(8));
288 CheckOobValue(m.Call(16));
289 }
290
TEST(RunLoadStoreSignExtend64)291 TEST(RunLoadStoreSignExtend64) {
292 if (true) return; // TODO(titzer): sign extension of loads to 64-bit.
293 int64_t buffer[5];
294 RawMachineAssemblerTester<int64_t> m;
295 Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Int8());
296 Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Int16());
297 Node* load32 = m.LoadFromPointer(LSB(&buffer[0], 4), MachineType::Int32());
298 Node* load64 = m.LoadFromPointer(&buffer[0], MachineType::Int64());
299 m.StoreToPointer(&buffer[1], MachineRepresentation::kWord64, load8);
300 m.StoreToPointer(&buffer[2], MachineRepresentation::kWord64, load16);
301 m.StoreToPointer(&buffer[3], MachineRepresentation::kWord64, load32);
302 m.StoreToPointer(&buffer[4], MachineRepresentation::kWord64, load64);
303 m.Return(load8);
304
305 FOR_INT64_INPUTS(i) {
306 buffer[0] = *i;
307
308 CHECK_EQ(static_cast<int8_t>(*i & 0xff), m.Call());
309 CHECK_EQ(static_cast<int8_t>(*i & 0xff), buffer[1]);
310 CHECK_EQ(static_cast<int16_t>(*i & 0xffff), buffer[2]);
311 CHECK_EQ(static_cast<int32_t>(*i & 0xffffffff), buffer[3]);
312 CHECK_EQ(*i, buffer[4]);
313 }
314 }
315
TEST(RunLoadStoreZeroExtend64)316 TEST(RunLoadStoreZeroExtend64) {
317 if (kPointerSize < 8) return;
318 uint64_t buffer[5];
319 RawMachineAssemblerTester<int64_t> m;
320 Node* load8 = m.LoadFromPointer(LSB(&buffer[0], 1), MachineType::Uint8());
321 Node* load16 = m.LoadFromPointer(LSB(&buffer[0], 2), MachineType::Uint16());
322 Node* load32 = m.LoadFromPointer(LSB(&buffer[0], 4), MachineType::Uint32());
323 Node* load64 = m.LoadFromPointer(&buffer[0], MachineType::Uint64());
324 m.StoreToPointer(&buffer[1], MachineRepresentation::kWord64, load8);
325 m.StoreToPointer(&buffer[2], MachineRepresentation::kWord64, load16);
326 m.StoreToPointer(&buffer[3], MachineRepresentation::kWord64, load32);
327 m.StoreToPointer(&buffer[4], MachineRepresentation::kWord64, load64);
328 m.Return(load8);
329
330 FOR_UINT64_INPUTS(i) {
331 buffer[0] = *i;
332
333 CHECK_EQ((*i & 0xff), m.Call());
334 CHECK_EQ((*i & 0xff), buffer[1]);
335 CHECK_EQ((*i & 0xffff), buffer[2]);
336 CHECK_EQ((*i & 0xffffffff), buffer[3]);
337 CHECK_EQ(*i, buffer[4]);
338 }
339 }
340
TEST(RunCheckedStoreInt64)341 TEST(RunCheckedStoreInt64) {
342 const int64_t write = 0x5566778899aabbLL;
343 const int64_t before = 0x33bbccddeeff0011LL;
344 int64_t buffer[] = {before, before};
345 RawMachineAssemblerTester<int32_t> m(MachineType::Int32());
346 Node* base = m.PointerConstant(buffer);
347 Node* index = m.Parameter(0);
348 Node* length = m.Int32Constant(16);
349 Node* value = m.Int64Constant(write);
350 Node* store =
351 m.AddNode(m.machine()->CheckedStore(MachineRepresentation::kWord64), base,
352 index, length, value);
353 USE(store);
354 m.Return(m.Int32Constant(11));
355
356 CHECK_EQ(11, m.Call(16));
357 CHECK_EQ(before, buffer[0]);
358 CHECK_EQ(before, buffer[1]);
359
360 CHECK_EQ(11, m.Call(0));
361 CHECK_EQ(write, buffer[0]);
362 CHECK_EQ(before, buffer[1]);
363
364 CHECK_EQ(11, m.Call(8));
365 CHECK_EQ(write, buffer[0]);
366 CHECK_EQ(write, buffer[1]);
367 }
368 #endif
369
370 namespace {
371 template <typename IntType>
LoadStoreTruncation(MachineType kRepresentation)372 void LoadStoreTruncation(MachineType kRepresentation) {
373 IntType input;
374
375 RawMachineAssemblerTester<int32_t> m;
376 Node* a = m.LoadFromPointer(&input, kRepresentation);
377 Node* ap1 = m.Int32Add(a, m.Int32Constant(1));
378 m.StoreToPointer(&input, kRepresentation.representation(), ap1);
379 m.Return(ap1);
380
381 const IntType max = std::numeric_limits<IntType>::max();
382 const IntType min = std::numeric_limits<IntType>::min();
383
384 // Test upper bound.
385 input = max;
386 CHECK_EQ(max + 1, m.Call());
387 CHECK_EQ(min, input);
388
389 // Test lower bound.
390 input = min;
391 CHECK_EQ(static_cast<IntType>(max + 2), m.Call());
392 CHECK_EQ(min + 1, input);
393
394 // Test all one byte values that are not one byte bounds.
395 for (int i = -127; i < 127; i++) {
396 input = i;
397 int expected = i >= 0 ? i + 1 : max + (i - min) + 2;
398 CHECK_EQ(static_cast<IntType>(expected), m.Call());
399 CHECK_EQ(static_cast<IntType>(i + 1), input);
400 }
401 }
402 } // namespace
403
TEST(RunLoadStoreTruncation)404 TEST(RunLoadStoreTruncation) {
405 LoadStoreTruncation<int8_t>(MachineType::Int8());
406 LoadStoreTruncation<int16_t>(MachineType::Int16());
407 }
408
TestRunOobCheckedLoad(bool length_is_immediate)409 void TestRunOobCheckedLoad(bool length_is_immediate) {
410 USE(CheckOobValue<int32_t>);
411 USE(CheckOobValue<int64_t>);
412 USE(CheckOobValue<float>);
413 USE(CheckOobValue<double>);
414
415 RawMachineAssemblerTester<int32_t> m(MachineType::Int32(),
416 MachineType::Int32());
417 MachineOperatorBuilder machine(m.zone());
418 const int32_t kNumElems = 27;
419 const int32_t kLength = kNumElems * 4;
420
421 int32_t buffer[kNumElems];
422 Node* base = m.PointerConstant(buffer);
423 Node* offset = m.Parameter(0);
424 Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1);
425 Node* node =
426 m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len);
427 m.Return(node);
428
429 {
430 // randomize memory.
431 v8::base::RandomNumberGenerator rng;
432 rng.SetSeed(100);
433 rng.NextBytes(&buffer[0], sizeof(buffer));
434 }
435
436 // in-bounds accesses.
437 for (int32_t i = 0; i < kNumElems; i++) {
438 int32_t offset = static_cast<int32_t>(i * sizeof(int32_t));
439 int32_t expected = buffer[i];
440 CHECK_EQ(expected, m.Call(offset, kLength));
441 }
442
443 // slightly out-of-bounds accesses.
444 for (int32_t i = kLength; i < kNumElems + 30; i++) {
445 int32_t offset = static_cast<int32_t>(i * sizeof(int32_t));
446 CheckOobValue(m.Call(offset, kLength));
447 }
448
449 // way out-of-bounds accesses.
450 for (int32_t offset = -2000000000; offset <= 2000000000;
451 offset += 100000000) {
452 if (offset == 0) continue;
453 CheckOobValue(m.Call(offset, kLength));
454 }
455 }
456
TEST(RunOobCheckedLoad)457 TEST(RunOobCheckedLoad) { TestRunOobCheckedLoad(false); }
458
TEST(RunOobCheckedLoadImm)459 TEST(RunOobCheckedLoadImm) { TestRunOobCheckedLoad(true); }
460
TestRunOobCheckedStore(bool length_is_immediate)461 void TestRunOobCheckedStore(bool length_is_immediate) {
462 RawMachineAssemblerTester<int32_t> m(MachineType::Int32(),
463 MachineType::Int32());
464 MachineOperatorBuilder machine(m.zone());
465 const int32_t kNumElems = 29;
466 const int32_t kValue = -78227234;
467 const int32_t kLength = kNumElems * 4;
468
469 int32_t buffer[kNumElems + kNumElems];
470 Node* base = m.PointerConstant(buffer);
471 Node* offset = m.Parameter(0);
472 Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1);
473 Node* val = m.Int32Constant(kValue);
474 m.AddNode(machine.CheckedStore(MachineRepresentation::kWord32), base, offset,
475 len, val);
476 m.Return(val);
477
478 // in-bounds accesses.
479 for (int32_t i = 0; i < kNumElems; i++) {
480 memset(buffer, 0, sizeof(buffer));
481 int32_t offset = static_cast<int32_t>(i * sizeof(int32_t));
482 CHECK_EQ(kValue, m.Call(offset, kLength));
483 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
484 if (i == j) {
485 CHECK_EQ(kValue, buffer[j]);
486 } else {
487 CHECK_EQ(0, buffer[j]);
488 }
489 }
490 }
491
492 memset(buffer, 0, sizeof(buffer));
493
494 // slightly out-of-bounds accesses.
495 for (int32_t i = kLength; i < kNumElems + 30; i++) {
496 int32_t offset = static_cast<int32_t>(i * sizeof(int32_t));
497 CHECK_EQ(kValue, m.Call(offset, kLength));
498 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
499 CHECK_EQ(0, buffer[j]);
500 }
501 }
502
503 // way out-of-bounds accesses.
504 for (int32_t offset = -2000000000; offset <= 2000000000;
505 offset += 100000000) {
506 if (offset == 0) continue;
507 CHECK_EQ(kValue, m.Call(offset, kLength));
508 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
509 CHECK_EQ(0, buffer[j]);
510 }
511 }
512 }
513
TEST(RunOobCheckedStore)514 TEST(RunOobCheckedStore) { TestRunOobCheckedStore(false); }
515
TEST(RunOobCheckedStoreImm)516 TEST(RunOobCheckedStoreImm) { TestRunOobCheckedStore(true); }
517
518 // TODO(titzer): CheckedLoad/CheckedStore don't support 64-bit offsets.
519 #define ALLOW_64_BIT_OFFSETS 0
520
521 #if V8_TARGET_ARCH_64_BIT && ALLOW_64_BIT_OFFSETS
522
TestRunOobCheckedLoad64(uint32_t pseudo_base,bool length_is_immediate)523 void TestRunOobCheckedLoad64(uint32_t pseudo_base, bool length_is_immediate) {
524 RawMachineAssemblerTester<int32_t> m(MachineType::Uint64(),
525 MachineType::Uint64());
526 MachineOperatorBuilder machine(m.zone());
527 const uint32_t kNumElems = 25;
528 const uint32_t kLength = kNumElems * 4;
529 int32_t real_buffer[kNumElems];
530
531 // Simulate the end of a large buffer.
532 int32_t* buffer = real_buffer - (pseudo_base / 4);
533 uint64_t length = kLength + pseudo_base;
534
535 Node* base = m.PointerConstant(buffer);
536 Node* offset = m.Parameter(0);
537 Node* len = length_is_immediate ? m.Int64Constant(length) : m.Parameter(1);
538 Node* node =
539 m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len);
540 m.Return(node);
541
542 {
543 // randomize memory.
544 v8::base::RandomNumberGenerator rng;
545 rng.SetSeed(100);
546 rng.NextBytes(&real_buffer[0], sizeof(real_buffer));
547 }
548
549 // in-bounds accesses.
550 for (uint32_t i = 0; i < kNumElems; i++) {
551 uint64_t offset = pseudo_base + i * 4;
552 int32_t expected = real_buffer[i];
553 CHECK_EQ(expected, m.Call(offset, length));
554 }
555
556 // in-bounds accesses w.r.t lower 32-bits, but upper bits set.
557 for (uint64_t i = 0x100000000ULL; i != 0; i <<= 1) {
558 uint64_t offset = pseudo_base + i;
559 CheckOobValue(m.Call(offset, length));
560 }
561
562 // slightly out-of-bounds accesses.
563 for (uint32_t i = kLength; i < kNumElems + 30; i++) {
564 uint64_t offset = pseudo_base + i * 4;
565 CheckOobValue(0, m.Call(offset, length));
566 }
567
568 // way out-of-bounds accesses.
569 for (uint64_t offset = length; offset < 100 * A_BILLION; offset += A_GIG) {
570 if (offset < length) continue;
571 CheckOobValue(0, m.Call(offset, length));
572 }
573 }
574
TEST(RunOobCheckedLoad64_0)575 TEST(RunOobCheckedLoad64_0) {
576 TestRunOobCheckedLoad64(0, false);
577 TestRunOobCheckedLoad64(0, true);
578 }
579
TEST(RunOobCheckedLoad64_1)580 TEST(RunOobCheckedLoad64_1) {
581 TestRunOobCheckedLoad64(1 * A_BILLION, false);
582 TestRunOobCheckedLoad64(1 * A_BILLION, true);
583 }
584
TEST(RunOobCheckedLoad64_2)585 TEST(RunOobCheckedLoad64_2) {
586 TestRunOobCheckedLoad64(2 * A_BILLION, false);
587 TestRunOobCheckedLoad64(2 * A_BILLION, true);
588 }
589
TEST(RunOobCheckedLoad64_3)590 TEST(RunOobCheckedLoad64_3) {
591 TestRunOobCheckedLoad64(3 * A_BILLION, false);
592 TestRunOobCheckedLoad64(3 * A_BILLION, true);
593 }
594
TEST(RunOobCheckedLoad64_4)595 TEST(RunOobCheckedLoad64_4) {
596 TestRunOobCheckedLoad64(4 * A_BILLION, false);
597 TestRunOobCheckedLoad64(4 * A_BILLION, true);
598 }
599
TestRunOobCheckedStore64(uint32_t pseudo_base,bool length_is_immediate)600 void TestRunOobCheckedStore64(uint32_t pseudo_base, bool length_is_immediate) {
601 RawMachineAssemblerTester<int32_t> m(MachineType::Uint64(),
602 MachineType::Uint64());
603 MachineOperatorBuilder machine(m.zone());
604 const uint32_t kNumElems = 21;
605 const uint32_t kLength = kNumElems * 4;
606 const uint32_t kValue = 897234987;
607 int32_t real_buffer[kNumElems + kNumElems];
608
609 // Simulate the end of a large buffer.
610 int32_t* buffer = real_buffer - (pseudo_base / 4);
611 uint64_t length = kLength + pseudo_base;
612
613 Node* base = m.PointerConstant(buffer);
614 Node* offset = m.Parameter(0);
615 Node* len = length_is_immediate ? m.Int64Constant(length) : m.Parameter(1);
616 Node* val = m.Int32Constant(kValue);
617 m.AddNode(machine.CheckedStore(MachineRepresentation::kWord32), base, offset,
618 len, val);
619 m.Return(val);
620
621 // in-bounds accesses.
622 for (uint32_t i = 0; i < kNumElems; i++) {
623 memset(real_buffer, 0, sizeof(real_buffer));
624 uint64_t offset = pseudo_base + i * 4;
625 CHECK_EQ(kValue, m.Call(offset, length));
626 for (uint32_t j = 0; j < kNumElems + kNumElems; j++) {
627 if (i == j) {
628 CHECK_EQ(kValue, real_buffer[j]);
629 } else {
630 CHECK_EQ(0, real_buffer[j]);
631 }
632 }
633 }
634
635 memset(real_buffer, 0, sizeof(real_buffer));
636
637 // in-bounds accesses w.r.t lower 32-bits, but upper bits set.
638 for (uint64_t i = 0x100000000ULL; i != 0; i <<= 1) {
639 uint64_t offset = pseudo_base + i;
640 CHECK_EQ(kValue, m.Call(offset, length));
641 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
642 CHECK_EQ(0, real_buffer[j]);
643 }
644 }
645
646 // slightly out-of-bounds accesses.
647 for (uint32_t i = kLength; i < kNumElems + 30; i++) {
648 uint64_t offset = pseudo_base + i * 4;
649 CHECK_EQ(kValue, m.Call(offset, length));
650 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
651 CHECK_EQ(0, real_buffer[j]);
652 }
653 }
654
655 // way out-of-bounds accesses.
656 for (uint64_t offset = length; offset < 100 * A_BILLION; offset += A_GIG) {
657 if (offset < length) continue;
658 CHECK_EQ(kValue, m.Call(offset, length));
659 for (int32_t j = 0; j < kNumElems + kNumElems; j++) {
660 CHECK_EQ(0, real_buffer[j]);
661 }
662 }
663 }
664
TEST(RunOobCheckedStore64_0)665 TEST(RunOobCheckedStore64_0) {
666 TestRunOobCheckedStore64(0, false);
667 TestRunOobCheckedStore64(0, true);
668 }
669
TEST(RunOobCheckedStore64_1)670 TEST(RunOobCheckedStore64_1) {
671 TestRunOobCheckedStore64(1 * A_BILLION, false);
672 TestRunOobCheckedStore64(1 * A_BILLION, true);
673 }
674
TEST(RunOobCheckedStore64_2)675 TEST(RunOobCheckedStore64_2) {
676 TestRunOobCheckedStore64(2 * A_BILLION, false);
677 TestRunOobCheckedStore64(2 * A_BILLION, true);
678 }
679
TEST(RunOobCheckedStore64_3)680 TEST(RunOobCheckedStore64_3) {
681 TestRunOobCheckedStore64(3 * A_BILLION, false);
682 TestRunOobCheckedStore64(3 * A_BILLION, true);
683 }
684
TEST(RunOobCheckedStore64_4)685 TEST(RunOobCheckedStore64_4) {
686 TestRunOobCheckedStore64(4 * A_BILLION, false);
687 TestRunOobCheckedStore64(4 * A_BILLION, true);
688 }
689
690 #endif
691
TestRunOobCheckedLoad_pseudo(uint64_t x,bool length_is_immediate)692 void TestRunOobCheckedLoad_pseudo(uint64_t x, bool length_is_immediate) {
693 RawMachineAssemblerTester<int32_t> m(MachineType::Uint32(),
694 MachineType::Uint32());
695
696 uint32_t pseudo_base = static_cast<uint32_t>(x);
697 MachineOperatorBuilder machine(m.zone());
698 const uint32_t kNumElems = 29;
699 const uint32_t kLength = pseudo_base + kNumElems * 4;
700
701 int32_t buffer[kNumElems];
702 Node* base = m.PointerConstant(reinterpret_cast<byte*>(buffer) - pseudo_base);
703 Node* offset = m.Parameter(0);
704 Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1);
705 Node* node =
706 m.AddNode(machine.CheckedLoad(MachineType::Int32()), base, offset, len);
707 m.Return(node);
708
709 {
710 // randomize memory.
711 v8::base::RandomNumberGenerator rng;
712 rng.SetSeed(100);
713 rng.NextBytes(&buffer[0], sizeof(buffer));
714 }
715
716 // in-bounds accesses.
717 for (uint32_t i = 0; i < kNumElems; i++) {
718 uint32_t offset = static_cast<uint32_t>(i * sizeof(int32_t));
719 uint32_t expected = buffer[i];
720 CHECK_EQ(expected, m.Call(offset + pseudo_base, kLength));
721 }
722
723 // slightly out-of-bounds accesses.
724 for (int32_t i = kNumElems; i < kNumElems + 30; i++) {
725 uint32_t offset = static_cast<uint32_t>(i * sizeof(int32_t));
726 CheckOobValue(m.Call(offset + pseudo_base, kLength));
727 }
728
729 // way out-of-bounds accesses.
730 for (uint64_t i = pseudo_base + sizeof(buffer); i < 0xFFFFFFFF;
731 i += A_BILLION) {
732 uint32_t offset = static_cast<uint32_t>(i);
733 CheckOobValue(m.Call(offset, kLength));
734 }
735 }
736
TEST(RunOobCheckedLoad_pseudo0)737 TEST(RunOobCheckedLoad_pseudo0) {
738 TestRunOobCheckedLoad_pseudo(0, false);
739 TestRunOobCheckedLoad_pseudo(0, true);
740 }
741
TEST(RunOobCheckedLoad_pseudo1)742 TEST(RunOobCheckedLoad_pseudo1) {
743 TestRunOobCheckedLoad_pseudo(100000, false);
744 TestRunOobCheckedLoad_pseudo(100000, true);
745 }
746
TEST(RunOobCheckedLoad_pseudo2)747 TEST(RunOobCheckedLoad_pseudo2) {
748 TestRunOobCheckedLoad_pseudo(A_BILLION, false);
749 TestRunOobCheckedLoad_pseudo(A_BILLION, true);
750 }
751
TEST(RunOobCheckedLoad_pseudo3)752 TEST(RunOobCheckedLoad_pseudo3) {
753 TestRunOobCheckedLoad_pseudo(A_GIG, false);
754 TestRunOobCheckedLoad_pseudo(A_GIG, true);
755 }
756
TEST(RunOobCheckedLoad_pseudo4)757 TEST(RunOobCheckedLoad_pseudo4) {
758 TestRunOobCheckedLoad_pseudo(2 * A_BILLION, false);
759 TestRunOobCheckedLoad_pseudo(2 * A_BILLION, true);
760 }
761
TEST(RunOobCheckedLoad_pseudo5)762 TEST(RunOobCheckedLoad_pseudo5) {
763 TestRunOobCheckedLoad_pseudo(2 * A_GIG, false);
764 TestRunOobCheckedLoad_pseudo(2 * A_GIG, true);
765 }
766
TEST(RunOobCheckedLoad_pseudo6)767 TEST(RunOobCheckedLoad_pseudo6) {
768 TestRunOobCheckedLoad_pseudo(3 * A_BILLION, false);
769 TestRunOobCheckedLoad_pseudo(3 * A_BILLION, true);
770 }
771
TEST(RunOobCheckedLoad_pseudo7)772 TEST(RunOobCheckedLoad_pseudo7) {
773 TestRunOobCheckedLoad_pseudo(3 * A_GIG, false);
774 TestRunOobCheckedLoad_pseudo(3 * A_GIG, true);
775 }
776
TEST(RunOobCheckedLoad_pseudo8)777 TEST(RunOobCheckedLoad_pseudo8) {
778 TestRunOobCheckedLoad_pseudo(4 * A_BILLION, false);
779 TestRunOobCheckedLoad_pseudo(4 * A_BILLION, true);
780 }
781
782 template <typename MemType>
TestRunOobCheckedLoadT_pseudo(uint64_t x,bool length_is_immediate)783 void TestRunOobCheckedLoadT_pseudo(uint64_t x, bool length_is_immediate) {
784 const int32_t kReturn = 11999;
785 const uint32_t kNumElems = 29;
786 MemType buffer[kNumElems];
787 uint32_t pseudo_base = static_cast<uint32_t>(x);
788 const uint32_t kLength = static_cast<uint32_t>(pseudo_base + sizeof(buffer));
789
790 MemType result;
791
792 RawMachineAssemblerTester<int32_t> m(MachineType::Uint32(),
793 MachineType::Uint32());
794 MachineOperatorBuilder machine(m.zone());
795 Node* base = m.PointerConstant(reinterpret_cast<byte*>(buffer) - pseudo_base);
796 Node* offset = m.Parameter(0);
797 Node* len = length_is_immediate ? m.Int32Constant(kLength) : m.Parameter(1);
798 Node* node = m.AddNode(machine.CheckedLoad(MachineTypeForC<MemType>()), base,
799 offset, len);
800 Node* store = m.StoreToPointer(
801 &result, MachineTypeForC<MemType>().representation(), node);
802 USE(store);
803 m.Return(m.Int32Constant(kReturn));
804
805 {
806 // randomize memory.
807 v8::base::RandomNumberGenerator rng;
808 rng.SetSeed(103);
809 rng.NextBytes(&buffer[0], sizeof(buffer));
810 }
811
812 // in-bounds accesses.
813 for (uint32_t i = 0; i < kNumElems; i++) {
814 uint32_t offset = static_cast<uint32_t>(i * sizeof(MemType));
815 MemType expected = buffer[i];
816 CHECK_EQ(kReturn, m.Call(offset + pseudo_base, kLength));
817 CHECK_EQ(expected, result);
818 }
819
820 // slightly out-of-bounds accesses.
821 for (int32_t i = kNumElems; i < kNumElems + 30; i++) {
822 uint32_t offset = static_cast<uint32_t>(i * sizeof(MemType));
823 CHECK_EQ(kReturn, m.Call(offset + pseudo_base, kLength));
824 CheckOobValue(result);
825 }
826
827 // way out-of-bounds accesses.
828 for (uint64_t i = pseudo_base + sizeof(buffer); i < 0xFFFFFFFF;
829 i += A_BILLION) {
830 uint32_t offset = static_cast<uint32_t>(i);
831 CHECK_EQ(kReturn, m.Call(offset, kLength));
832 CheckOobValue(result);
833 }
834 }
835
TEST(RunOobCheckedLoadT_pseudo0)836 TEST(RunOobCheckedLoadT_pseudo0) {
837 TestRunOobCheckedLoadT_pseudo<int32_t>(0, false);
838 TestRunOobCheckedLoadT_pseudo<int32_t>(0, true);
839 TestRunOobCheckedLoadT_pseudo<float>(0, false);
840 TestRunOobCheckedLoadT_pseudo<float>(0, true);
841 TestRunOobCheckedLoadT_pseudo<double>(0, false);
842 TestRunOobCheckedLoadT_pseudo<double>(0, true);
843 }
844
TEST(RunOobCheckedLoadT_pseudo1)845 TEST(RunOobCheckedLoadT_pseudo1) {
846 TestRunOobCheckedLoadT_pseudo<int32_t>(100000, false);
847 TestRunOobCheckedLoadT_pseudo<int32_t>(100000, true);
848 TestRunOobCheckedLoadT_pseudo<float>(100000, false);
849 TestRunOobCheckedLoadT_pseudo<float>(100000, true);
850 TestRunOobCheckedLoadT_pseudo<double>(100000, false);
851 TestRunOobCheckedLoadT_pseudo<double>(100000, true);
852 }
853
TEST(RunOobCheckedLoadT_pseudo2)854 TEST(RunOobCheckedLoadT_pseudo2) {
855 TestRunOobCheckedLoadT_pseudo<int32_t>(A_BILLION, false);
856 TestRunOobCheckedLoadT_pseudo<int32_t>(A_BILLION, true);
857 TestRunOobCheckedLoadT_pseudo<float>(A_BILLION, false);
858 TestRunOobCheckedLoadT_pseudo<float>(A_BILLION, true);
859 TestRunOobCheckedLoadT_pseudo<double>(A_BILLION, false);
860 TestRunOobCheckedLoadT_pseudo<double>(A_BILLION, true);
861 }
862
TEST(RunOobCheckedLoadT_pseudo3)863 TEST(RunOobCheckedLoadT_pseudo3) {
864 TestRunOobCheckedLoadT_pseudo<int32_t>(A_GIG, false);
865 TestRunOobCheckedLoadT_pseudo<int32_t>(A_GIG, true);
866 TestRunOobCheckedLoadT_pseudo<float>(A_GIG, false);
867 TestRunOobCheckedLoadT_pseudo<float>(A_GIG, true);
868 TestRunOobCheckedLoadT_pseudo<double>(A_GIG, false);
869 TestRunOobCheckedLoadT_pseudo<double>(A_GIG, true);
870 }
871
TEST(RunOobCheckedLoadT_pseudo4)872 TEST(RunOobCheckedLoadT_pseudo4) {
873 TestRunOobCheckedLoadT_pseudo<int32_t>(2 * A_BILLION, false);
874 TestRunOobCheckedLoadT_pseudo<int32_t>(2 * A_BILLION, true);
875 TestRunOobCheckedLoadT_pseudo<float>(2 * A_BILLION, false);
876 TestRunOobCheckedLoadT_pseudo<float>(2 * A_BILLION, true);
877 TestRunOobCheckedLoadT_pseudo<double>(2 * A_BILLION, false);
878 TestRunOobCheckedLoadT_pseudo<double>(2 * A_BILLION, true);
879 }
880
TEST(RunOobCheckedLoadT_pseudo5)881 TEST(RunOobCheckedLoadT_pseudo5) {
882 TestRunOobCheckedLoadT_pseudo<int32_t>(2 * A_GIG, false);
883 TestRunOobCheckedLoadT_pseudo<int32_t>(2 * A_GIG, true);
884 TestRunOobCheckedLoadT_pseudo<float>(2 * A_GIG, false);
885 TestRunOobCheckedLoadT_pseudo<float>(2 * A_GIG, true);
886 TestRunOobCheckedLoadT_pseudo<double>(2 * A_GIG, false);
887 TestRunOobCheckedLoadT_pseudo<double>(2 * A_GIG, true);
888 }
889
TEST(RunOobCheckedLoadT_pseudo6)890 TEST(RunOobCheckedLoadT_pseudo6) {
891 TestRunOobCheckedLoadT_pseudo<int32_t>(3 * A_BILLION, false);
892 TestRunOobCheckedLoadT_pseudo<int32_t>(3 * A_BILLION, true);
893 TestRunOobCheckedLoadT_pseudo<float>(3 * A_BILLION, false);
894 TestRunOobCheckedLoadT_pseudo<float>(3 * A_BILLION, true);
895 TestRunOobCheckedLoadT_pseudo<double>(3 * A_BILLION, false);
896 TestRunOobCheckedLoadT_pseudo<double>(3 * A_BILLION, true);
897 }
898
TEST(RunOobCheckedLoadT_pseudo7)899 TEST(RunOobCheckedLoadT_pseudo7) {
900 TestRunOobCheckedLoadT_pseudo<int32_t>(3 * A_GIG, false);
901 TestRunOobCheckedLoadT_pseudo<int32_t>(3 * A_GIG, true);
902 TestRunOobCheckedLoadT_pseudo<float>(3 * A_GIG, false);
903 TestRunOobCheckedLoadT_pseudo<float>(3 * A_GIG, true);
904 TestRunOobCheckedLoadT_pseudo<double>(3 * A_GIG, false);
905 TestRunOobCheckedLoadT_pseudo<double>(3 * A_GIG, true);
906 }
907
TEST(RunOobCheckedLoadT_pseudo8)908 TEST(RunOobCheckedLoadT_pseudo8) {
909 TestRunOobCheckedLoadT_pseudo<int32_t>(4 * A_BILLION, false);
910 TestRunOobCheckedLoadT_pseudo<int32_t>(4 * A_BILLION, true);
911 TestRunOobCheckedLoadT_pseudo<float>(4 * A_BILLION, false);
912 TestRunOobCheckedLoadT_pseudo<float>(4 * A_BILLION, true);
913 TestRunOobCheckedLoadT_pseudo<double>(4 * A_BILLION, false);
914 TestRunOobCheckedLoadT_pseudo<double>(4 * A_BILLION, true);
915 }
916
917 } // namespace compiler
918 } // namespace internal
919 } // namespace v8
920