[turbofan] Bailout if LoadBuffer typing assumption doesn't hold.

The LoadBuffer operator that is used for asm.js heap access claims to
return only the appropriate typed array type, but out of bounds access
could make it return undefined. So far we tried to "repair" the graph
later if we see that our assumption was wrong, and for various reasons
that worked for some time. But now that wrong type information that is
propagated earlier is picked up appropriately and thus we generate wrong
code, i.e. we in the repro case we feed NaN into ChangeFloat64Uint32 and
thus get 2147483648 instead of 0 (with proper JS truncation).

This was always considered a temporary hack until we have a proper
asm.js pipeline, but since we still run asm.js through the generic
JavaScript pipeline, we have to address this now. Quickfix is to just
bailout from the pipeline when we see that the LoadBuffer type was
wrong, i.e. the result of LoadBuffer is not properly truncated and thus
undefined or NaN would be observable.

R=mstarzinger@chromium.org, jarin@chromium.org
BUG=chromium:589792
LOG=y

Review URL: https://codereview.chromium.org/1740123002

Cr-Commit-Position: refs/heads/master@{#34322}
This commit is contained in:
bmeurer 2016-02-26 03:05:22 -08:00 committed by Commit bot
parent cb29f9cdbc
commit 58ab990aa8
6 changed files with 43 additions and 143 deletions

View File

@ -697,6 +697,13 @@ struct SimplifiedLoweringPhase {
SimplifiedLowering lowering(data->jsgraph(), temp_zone,
data->source_positions());
lowering.LowerAllNodes();
// TODO(bmeurer): See comment on SimplifiedLowering::abort_compilation_.
if (lowering.abort_compilation_) {
data->set_compilation_failed();
return;
}
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common());
@ -1217,6 +1224,9 @@ Handle<Code> Pipeline::GenerateCode() {
// Kill the Typer and thereby uninstall the decorator (if any).
typer.Reset(nullptr);
// TODO(bmeurer): See comment on SimplifiedLowering::abort_compilation_.
if (data.compilation_failed()) return Handle<Code>::null();
return ScheduleAndGenerateCode(
Linkage::ComputeIncoming(data.instruction_zone(), info()));
}

View File

@ -1199,10 +1199,18 @@ class RepresentationSelector {
NodeOutputInfo(access.machine_type().representation(),
NodeProperties::GetType(node));
} else {
if (access.machine_type().representation() !=
MachineRepresentation::kFloat64) {
// TODO(bmeurer): See comment on abort_compilation_.
if (lower()) lowering->abort_compilation_ = true;
}
output_info = NodeOutputInfo::Float64();
}
}
} else {
// TODO(bmeurer): See comment on abort_compilation_.
if (lower()) lowering->abort_compilation_ = true;
// If undefined is not truncated away, we need to have the tagged
// representation.
output_info = NodeOutputInfo::AnyTagged();

View File

@ -41,6 +41,11 @@ class SimplifiedLowering final {
void DoStringLessThan(Node* node);
void DoStringLessThanOrEqual(Node* node);
// TODO(bmeurer): This is a gigantic hack to support the gigantic LoadBuffer
// typing hack to support the gigantic "asm.js should be fast without proper
// verifier"-hack, ... Kill this! Soon! Really soon! I'm serious!
bool abort_compilation_ = false;
private:
JSGraph* const jsgraph_;
Zone* const zone_;

View File

@ -81,7 +81,6 @@
'compiler/test-run-jsops.cc',
'compiler/test-run-machops.cc',
'compiler/test-run-native-calls.cc',
'compiler/test-run-properties.cc',
'compiler/test-run-stackcheck.cc',
'compiler/test-run-stubs.cc',
'compiler/test-run-variables.cc',

View File

@ -1,142 +0,0 @@
// Copyright 2014 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 "test/cctest/compiler/function-tester.h"
namespace v8 {
namespace internal {
namespace compiler {
template <typename U>
static void TypedArrayLoadHelper(const char* array_type) {
static const uint32_t kValues[] = {
0x00000000, 0x00000001, 0x00000023, 0x00000042, 0x12345678, 0x87654321,
0x0000003f, 0x0000007f, 0x00003fff, 0x00007fff, 0x3fffffff, 0x7fffffff,
0x000000ff, 0x00000080, 0x0000ffff, 0x00008000, 0xffffffff, 0x80000000};
EmbeddedVector<char, 1024> values_buffer;
StringBuilder values_builder(values_buffer.start(), values_buffer.length());
for (size_t i = 0; i < arraysize(kValues); ++i) {
values_builder.AddFormatted("a[%d] = 0x%08x;", i, kValues[i]);
}
// Note that below source creates two different typed arrays with the same
// elements kind to get coverage for both (on heap / with external backing
// store) access patterns.
const char* source =
"(function(a) {"
" var x = (a = new %sArray(%d)); %s;"
" var y = (a = new %sArray(%d)); %s; %%TypedArrayGetBuffer(y);"
" if (!%%HasFixed%sElements(x)) %%AbortJS('x');"
" if (!%%HasFixed%sElements(y)) %%AbortJS('y');"
" function f(a,b) {"
" a = a | 0; b = b | 0;"
" return x[a] + y[b];"
" }"
" return f;"
"})()";
EmbeddedVector<char, 1024> source_buffer;
SNPrintF(source_buffer, source, array_type, arraysize(kValues),
values_buffer.start(), array_type, arraysize(kValues),
values_buffer.start(), array_type, array_type);
FunctionTester T(source_buffer.start(),
CompilationInfo::kFunctionContextSpecializing |
CompilationInfo::kTypingEnabled);
for (size_t i = 0; i < arraysize(kValues); ++i) {
for (size_t j = 0; j < arraysize(kValues); ++j) {
volatile U value_a = static_cast<U>(kValues[i]);
volatile U value_b = static_cast<U>(kValues[j]);
double expected =
static_cast<double>(value_a) + static_cast<double>(value_b);
T.CheckCall(T.Val(expected), T.Val(static_cast<double>(i)),
T.Val(static_cast<double>(j)));
}
}
}
TEST(TypedArrayLoad) {
FLAG_typed_array_max_size_in_heap = 256;
TypedArrayLoadHelper<int8_t>("Int8");
TypedArrayLoadHelper<uint8_t>("Uint8");
TypedArrayLoadHelper<int16_t>("Int16");
TypedArrayLoadHelper<uint16_t>("Uint16");
TypedArrayLoadHelper<int32_t>("Int32");
TypedArrayLoadHelper<uint32_t>("Uint32");
TypedArrayLoadHelper<float>("Float32");
TypedArrayLoadHelper<double>("Float64");
// TODO(mstarzinger): Add tests for ClampedUint8.
}
template <typename U>
static void TypedArrayStoreHelper(const char* array_type) {
static const uint32_t kValues[] = {
0x00000000, 0x00000001, 0x00000023, 0x00000042, 0x12345678, 0x87654321,
0x0000003f, 0x0000007f, 0x00003fff, 0x00007fff, 0x3fffffff, 0x7fffffff,
0x000000ff, 0x00000080, 0x0000ffff, 0x00008000, 0xffffffff, 0x80000000};
EmbeddedVector<char, 1024> values_buffer;
StringBuilder values_builder(values_buffer.start(), values_buffer.length());
for (size_t i = 0; i < arraysize(kValues); ++i) {
values_builder.AddFormatted("a[%d] = 0x%08x;", i, kValues[i]);
}
// Note that below source creates two different typed arrays with the same
// elements kind to get coverage for both (on heap/with external backing
// store) access patterns.
const char* source =
"(function(a) {"
" var x = (a = new %sArray(%d)); %s;"
" var y = (a = new %sArray(%d)); %s; %%TypedArrayGetBuffer(y);"
" if (!%%HasFixed%sElements(x)) %%AbortJS('x');"
" if (!%%HasFixed%sElements(y)) %%AbortJS('y');"
" function f(a,b) {"
" a = a | 0; b = b | 0;"
" var t = x[a];"
" x[a] = y[b];"
" y[b] = t;"
" t = y[b];"
" y[b] = x[a];"
" x[a] = t;"
" return x[a] + y[b];"
" }"
" return f;"
"})()";
EmbeddedVector<char, 2048> source_buffer;
SNPrintF(source_buffer, source, array_type, arraysize(kValues),
values_buffer.start(), array_type, arraysize(kValues),
values_buffer.start(), array_type, array_type);
FunctionTester T(source_buffer.start(),
CompilationInfo::kFunctionContextSpecializing |
CompilationInfo::kTypingEnabled);
for (size_t i = 0; i < arraysize(kValues); ++i) {
for (size_t j = 0; j < arraysize(kValues); ++j) {
volatile U value_a = static_cast<U>(kValues[i]);
volatile U value_b = static_cast<U>(kValues[j]);
double expected =
static_cast<double>(value_a) + static_cast<double>(value_b);
T.CheckCall(T.Val(expected), T.Val(static_cast<double>(i)),
T.Val(static_cast<double>(j)));
}
}
}
TEST(TypedArrayStore) {
FLAG_typed_array_max_size_in_heap = 256;
TypedArrayStoreHelper<int8_t>("Int8");
TypedArrayStoreHelper<uint8_t>("Uint8");
TypedArrayStoreHelper<int16_t>("Int16");
TypedArrayStoreHelper<uint16_t>("Uint16");
TypedArrayStoreHelper<int32_t>("Int32");
TypedArrayStoreHelper<uint32_t>("Uint32");
TypedArrayStoreHelper<float>("Float32");
TypedArrayStoreHelper<double>("Float64");
// TODO(mstarzinger): Add tests for ClampedUint8.
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,20 @@
// 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.
// Flags: --allow-natives-syntax
var boom = (function(stdlib, foreign, heap) {
"use asm";
var MEM8 = new stdlib.Uint8Array(heap);
var MEM32 = new stdlib.Int32Array(heap);
function foo(i, j) {
j = MEM8[256];
// This following value '10' determines the value of 'rax'
MEM32[j >> 10] = 0xabcdefaa;
return MEM32[j >> 2] + j
}
return foo
})(this, 0, new ArrayBuffer(256));
%OptimizeFunctionOnNextCall(boom);
boom(0, 0x1000);