7d8a3028dd
From asm.js code we might get an empty ArrayBuffer as heap memory. In this case, both the old memory start and the new memory start will be nullptr. The size however has to be patched from default_size to 0. This CL changes code specialization to be able to either patch memory references, or patch memory sizes or both. R=titzer@chromium.org, ahaas@chromium.org BUG=chromium:698587 Change-Id: I4d9d811d75cb83842f23df317e8e7fc02aeb5146 Reviewed-on: https://chromium-review.googlesource.com/450257 Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#43613}
214 lines
8.1 KiB
C++
214 lines
8.1 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved. Use of this
|
|
// source code is governed by a BSD-style license that can be found in the
|
|
// LICENSE file.
|
|
|
|
#include <cmath>
|
|
#include <functional>
|
|
#include <limits>
|
|
|
|
#include "src/base/bits.h"
|
|
#include "src/base/utils/random-number-generator.h"
|
|
#include "src/codegen.h"
|
|
#include "src/objects-inl.h"
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/compiler/codegen-tester.h"
|
|
#include "test/cctest/compiler/graph-builder-tester.h"
|
|
#include "test/cctest/compiler/value-helper.h"
|
|
|
|
using namespace v8::internal;
|
|
using namespace v8::internal::compiler;
|
|
|
|
static void UpdateMemoryReferences(Handle<Code> code, Address old_base,
|
|
Address new_base, uint32_t old_size,
|
|
uint32_t new_size) {
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
bool modified = false;
|
|
int mode_mask = RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_REFERENCE) |
|
|
RelocInfo::ModeMask(RelocInfo::WASM_MEMORY_SIZE_REFERENCE);
|
|
for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
|
|
RelocInfo::Mode mode = it.rinfo()->rmode();
|
|
if (RelocInfo::IsWasmMemoryReference(mode)) {
|
|
it.rinfo()->update_wasm_memory_reference(old_base, new_base);
|
|
} else {
|
|
DCHECK(RelocInfo::IsWasmMemorySizeReference(mode));
|
|
it.rinfo()->update_wasm_memory_size(old_size, new_size);
|
|
}
|
|
modified = true;
|
|
}
|
|
if (modified) {
|
|
Assembler::FlushICache(isolate, code->instruction_start(),
|
|
code->instruction_size());
|
|
}
|
|
}
|
|
|
|
static void UpdateFunctionTableSizeReferences(Handle<Code> code,
|
|
uint32_t old_size,
|
|
uint32_t new_size) {
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
bool modified = false;
|
|
int mode_mask =
|
|
RelocInfo::ModeMask(RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE);
|
|
for (RelocIterator it(*code, mode_mask); !it.done(); it.next()) {
|
|
RelocInfo::Mode mode = it.rinfo()->rmode();
|
|
if (RelocInfo::IsWasmFunctionTableSizeReference(mode)) {
|
|
it.rinfo()->update_wasm_function_table_size_reference(old_size, new_size);
|
|
modified = true;
|
|
}
|
|
}
|
|
if (modified) {
|
|
Assembler::FlushICache(isolate, code->instruction_start(),
|
|
code->instruction_size());
|
|
}
|
|
}
|
|
|
|
template <typename CType>
|
|
static void RunLoadStoreRelocation(MachineType rep) {
|
|
const int kNumElems = 2;
|
|
CType buffer[kNumElems];
|
|
CType new_buffer[kNumElems];
|
|
byte* raw = reinterpret_cast<byte*>(buffer);
|
|
byte* new_raw = reinterpret_cast<byte*>(new_buffer);
|
|
for (size_t i = 0; i < sizeof(buffer); i++) {
|
|
raw[i] = static_cast<byte>((i + sizeof(CType)) ^ 0xAA);
|
|
new_raw[i] = static_cast<byte>((i + sizeof(CType)) ^ 0xAA);
|
|
}
|
|
uint32_t OK = 0x29000;
|
|
RawMachineAssemblerTester<uint32_t> m;
|
|
Node* base = m.RelocatableIntPtrConstant(reinterpret_cast<intptr_t>(raw),
|
|
RelocInfo::WASM_MEMORY_REFERENCE);
|
|
Node* base1 = m.RelocatableIntPtrConstant(
|
|
reinterpret_cast<intptr_t>(raw + sizeof(CType)),
|
|
RelocInfo::WASM_MEMORY_REFERENCE);
|
|
Node* index = m.Int32Constant(0);
|
|
Node* load = m.Load(rep, base, index);
|
|
m.Store(rep.representation(), base1, index, load, kNoWriteBarrier);
|
|
m.Return(m.Int32Constant(OK));
|
|
CHECK(buffer[0] != buffer[1]);
|
|
CHECK_EQ(OK, m.Call());
|
|
CHECK(buffer[0] == buffer[1]);
|
|
m.GenerateCode();
|
|
Handle<Code> code = m.GetCode();
|
|
UpdateMemoryReferences(code, raw, new_raw, sizeof(buffer),
|
|
sizeof(new_buffer));
|
|
CHECK(new_buffer[0] != new_buffer[1]);
|
|
CHECK_EQ(OK, m.Call());
|
|
CHECK(new_buffer[0] == new_buffer[1]);
|
|
}
|
|
|
|
TEST(RunLoadStoreRelocation) {
|
|
RunLoadStoreRelocation<int8_t>(MachineType::Int8());
|
|
RunLoadStoreRelocation<uint8_t>(MachineType::Uint8());
|
|
RunLoadStoreRelocation<int16_t>(MachineType::Int16());
|
|
RunLoadStoreRelocation<uint16_t>(MachineType::Uint16());
|
|
RunLoadStoreRelocation<int32_t>(MachineType::Int32());
|
|
RunLoadStoreRelocation<uint32_t>(MachineType::Uint32());
|
|
RunLoadStoreRelocation<void*>(MachineType::AnyTagged());
|
|
RunLoadStoreRelocation<float>(MachineType::Float32());
|
|
RunLoadStoreRelocation<double>(MachineType::Float64());
|
|
}
|
|
|
|
template <typename CType>
|
|
static void RunLoadStoreRelocationOffset(MachineType rep) {
|
|
RawMachineAssemblerTester<int32_t> r(MachineType::Int32());
|
|
const int kNumElems = 4;
|
|
CType buffer[kNumElems];
|
|
CType new_buffer[kNumElems + 1];
|
|
|
|
for (int32_t x = 0; x < kNumElems; x++) {
|
|
int32_t y = kNumElems - x - 1;
|
|
// initialize the buffer with raw data.
|
|
byte* raw = reinterpret_cast<byte*>(buffer);
|
|
for (size_t i = 0; i < sizeof(buffer); i++) {
|
|
raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
|
|
}
|
|
|
|
RawMachineAssemblerTester<int32_t> m;
|
|
int32_t OK = 0x29000 + x;
|
|
Node* base = m.RelocatableIntPtrConstant(reinterpret_cast<intptr_t>(buffer),
|
|
RelocInfo::WASM_MEMORY_REFERENCE);
|
|
Node* index0 = m.IntPtrConstant(x * sizeof(buffer[0]));
|
|
Node* load = m.Load(rep, base, index0);
|
|
Node* index1 = m.IntPtrConstant(y * sizeof(buffer[0]));
|
|
m.Store(rep.representation(), base, index1, load, kNoWriteBarrier);
|
|
m.Return(m.Int32Constant(OK));
|
|
|
|
CHECK(buffer[x] != buffer[y]);
|
|
CHECK_EQ(OK, m.Call());
|
|
CHECK(buffer[x] == buffer[y]);
|
|
m.GenerateCode();
|
|
|
|
// Initialize new buffer and set old_buffer to 0
|
|
byte* new_raw = reinterpret_cast<byte*>(new_buffer);
|
|
for (size_t i = 0; i < sizeof(buffer); i++) {
|
|
raw[i] = 0;
|
|
new_raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
|
|
}
|
|
|
|
// Perform relocation on generated code
|
|
Handle<Code> code = m.GetCode();
|
|
UpdateMemoryReferences(code, raw, new_raw, sizeof(buffer),
|
|
sizeof(new_buffer));
|
|
|
|
CHECK(new_buffer[x] != new_buffer[y]);
|
|
CHECK_EQ(OK, m.Call());
|
|
CHECK(new_buffer[x] == new_buffer[y]);
|
|
}
|
|
}
|
|
|
|
TEST(RunLoadStoreRelocationOffset) {
|
|
RunLoadStoreRelocationOffset<int8_t>(MachineType::Int8());
|
|
RunLoadStoreRelocationOffset<uint8_t>(MachineType::Uint8());
|
|
RunLoadStoreRelocationOffset<int16_t>(MachineType::Int16());
|
|
RunLoadStoreRelocationOffset<uint16_t>(MachineType::Uint16());
|
|
RunLoadStoreRelocationOffset<int32_t>(MachineType::Int32());
|
|
RunLoadStoreRelocationOffset<uint32_t>(MachineType::Uint32());
|
|
RunLoadStoreRelocationOffset<void*>(MachineType::AnyTagged());
|
|
RunLoadStoreRelocationOffset<float>(MachineType::Float32());
|
|
RunLoadStoreRelocationOffset<double>(MachineType::Float64());
|
|
}
|
|
|
|
TEST(Uint32LessThanMemoryRelocation) {
|
|
RawMachineAssemblerTester<uint32_t> m;
|
|
RawMachineLabel within_bounds, out_of_bounds;
|
|
Node* index = m.Int32Constant(0x200);
|
|
Node* limit =
|
|
m.RelocatableInt32Constant(0x200, RelocInfo::WASM_MEMORY_SIZE_REFERENCE);
|
|
Node* cond = m.AddNode(m.machine()->Uint32LessThan(), index, limit);
|
|
m.Branch(cond, &within_bounds, &out_of_bounds);
|
|
m.Bind(&within_bounds);
|
|
m.Return(m.Int32Constant(0xaced));
|
|
m.Bind(&out_of_bounds);
|
|
m.Return(m.Int32Constant(0xdeadbeef));
|
|
// Check that index is out of bounds with current size
|
|
CHECK_EQ(0xdeadbeef, m.Call());
|
|
m.GenerateCode();
|
|
|
|
Handle<Code> code = m.GetCode();
|
|
UpdateMemoryReferences(code, reinterpret_cast<Address>(1234),
|
|
reinterpret_cast<Address>(1234), 0x200, 0x400);
|
|
// Check that after limit is increased, index is within bounds.
|
|
CHECK_EQ(0xacedu, m.Call());
|
|
}
|
|
|
|
TEST(Uint32LessThanFunctionTableRelocation) {
|
|
RawMachineAssemblerTester<uint32_t> m;
|
|
RawMachineLabel within_bounds, out_of_bounds;
|
|
Node* index = m.Int32Constant(0x200);
|
|
Node* limit = m.RelocatableInt32Constant(
|
|
0x200, RelocInfo::WASM_FUNCTION_TABLE_SIZE_REFERENCE);
|
|
Node* cond = m.AddNode(m.machine()->Uint32LessThan(), index, limit);
|
|
m.Branch(cond, &within_bounds, &out_of_bounds);
|
|
m.Bind(&within_bounds);
|
|
m.Return(m.Int32Constant(0xaced));
|
|
m.Bind(&out_of_bounds);
|
|
m.Return(m.Int32Constant(0xdeadbeef));
|
|
// Check that index is out of bounds with current size
|
|
CHECK_EQ(0xdeadbeef, m.Call());
|
|
m.GenerateCode();
|
|
|
|
Handle<Code> code = m.GetCode();
|
|
UpdateFunctionTableSizeReferences(code, 0x200, 0x400);
|
|
// Check that after limit is increased, index is within bounds.
|
|
CHECK_EQ(0xaced, m.Call());
|
|
}
|