[x64] Fix optimization for certain checked load/stores.
authorBenedikt Meurer <bmeurer@chromium.org>
Wed, 10 Dec 2014 07:48:57 +0000 (08:48 +0100)
committerBenedikt Meurer <bmeurer@chromium.org>
Wed, 10 Dec 2014 07:49:12 +0000 (07:49 +0000)
BUG=chromium:439743
LOG=y
TEST=mjsunit
R=svenpanne@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#25731}

src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-selector-x64.cc
test/mjsunit/asm/float32array-negative-offset.js [new file with mode: 0644]
test/mjsunit/asm/float64array-negative-offset.js [new file with mode: 0644]
test/mjsunit/asm/int16array-negative-offset.js [new file with mode: 0644]
test/mjsunit/asm/int32array-negative-offset.js [new file with mode: 0644]
test/mjsunit/asm/int8array-negative-offset.js [new file with mode: 0644]
test/mjsunit/compiler/regress-439743.js [moved from test/mjsunit/compiler/regress-lena.js with 100% similarity]

index ec4d5b6..84b88f2 100644 (file)
@@ -138,9 +138,9 @@ bool HasImmediateInput(Instruction* instr, int index) {
 }
 
 
-class OutOfLineLoadInteger FINAL : public OutOfLineCode {
+class OutOfLineLoadZero FINAL : public OutOfLineCode {
  public:
-  OutOfLineLoadInteger(CodeGenerator* gen, Register result)
+  OutOfLineLoadZero(CodeGenerator* gen, Register result)
       : OutOfLineCode(gen), result_(result) {}
 
   void Generate() FINAL { __ xorl(result_, result_); }
@@ -150,9 +150,9 @@ class OutOfLineLoadInteger FINAL : public OutOfLineCode {
 };
 
 
-class OutOfLineLoadFloat FINAL : public OutOfLineCode {
+class OutOfLineLoadNaN FINAL : public OutOfLineCode {
  public:
-  OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result)
+  OutOfLineLoadNaN(CodeGenerator* gen, XMMRegister result)
       : OutOfLineCode(gen), result_(result) {}
 
   void Generate() FINAL { __ pcmpeqd(result_, result_); }
@@ -272,69 +272,254 @@ class OutOfLineTruncateDoubleToI FINAL : public OutOfLineCode {
   } while (0)
 
 
-#define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr)                          \
-  do {                                                                  \
-    auto result = i.OutputDoubleRegister();                             \
-    auto offset = i.InputRegister(0);                                   \
-    if (instr->InputAt(1)->IsRegister()) {                              \
-      __ cmpl(offset, i.InputRegister(1));                              \
-    } else {                                                            \
-      __ cmpl(offset, i.InputImmediate(1));                             \
-    }                                                                   \
-    OutOfLineCode* ool = new (zone()) OutOfLineLoadFloat(this, result); \
-    __ j(above_equal, ool->entry());                                    \
-    __ asm_instr(result, i.MemoryOperand(2));                           \
-    __ bind(ool->exit());                                               \
+#define ASSEMBLE_CHECKED_LOAD_FLOAT(asm_instr)                                 \
+  do {                                                                         \
+    auto result = i.OutputDoubleRegister();                                    \
+    auto buffer = i.InputRegister(0);                                          \
+    auto index1 = i.InputRegister(1);                                          \
+    auto index2 = i.InputInt32(2);                                             \
+    OutOfLineCode* ool;                                                        \
+    if (instr->InputAt(3)->IsRegister()) {                                     \
+      auto length = i.InputRegister(3);                                        \
+      DCHECK_EQ(0, index2);                                                    \
+      __ cmpl(index1, length);                                                 \
+      ool = new (zone()) OutOfLineLoadNaN(this, result);                       \
+    } else {                                                                   \
+      auto length = i.InputInt32(3);                                           \
+      DCHECK_LE(index2, length);                                               \
+      __ cmpl(index1, Immediate(length - index2));                             \
+      if (index2 == 0) {                                                       \
+        ool = new (zone()) OutOfLineLoadNaN(this, result);                     \
+      } else {                                                                 \
+        class OutOfLineLoadFloat FINAL : public OutOfLineCode {                \
+         public:                                                               \
+          OutOfLineLoadFloat(CodeGenerator* gen, XMMRegister result,           \
+                             Register buffer, Register index1, int32_t index2, \
+                             int32_t length)                                   \
+              : OutOfLineCode(gen),                                            \
+                result_(result),                                               \
+                buffer_(buffer),                                               \
+                index1_(index1),                                               \
+                index2_(index2),                                               \
+                length_(length) {}                                             \
+                                                                               \
+          void Generate() FINAL {                                              \
+            DCHECK_NE(0, index2_);                                             \
+            __ leal(kScratchRegister, Operand(index1_, index2_));              \
+            __ pcmpeqd(result_, result_);                                      \
+            __ cmpl(kScratchRegister, Immediate(length_));                     \
+            __ j(above_equal, exit());                                         \
+            __ asm_instr(result_,                                              \
+                         Operand(buffer_, kScratchRegister, times_1, 0));      \
+          }                                                                    \
+                                                                               \
+         private:                                                              \
+          XMMRegister const result_;                                           \
+          Register const buffer_;                                              \
+          Register const index1_;                                              \
+          int32_t const index2_;                                               \
+          int32_t const length_;                                               \
+        };                                                                     \
+        ool = new (zone())                                                     \
+            OutOfLineLoadFloat(this, result, buffer, index1, index2, length);  \
+      }                                                                        \
+    }                                                                          \
+    __ j(above_equal, ool->entry());                                           \
+    __ asm_instr(result, Operand(buffer, index1, times_1, index2));            \
+    __ bind(ool->exit());                                                      \
+  } while (false)
+
+
+#define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr)                              \
+  do {                                                                        \
+    auto result = i.OutputRegister();                                         \
+    auto buffer = i.InputRegister(0);                                         \
+    auto index1 = i.InputRegister(1);                                         \
+    auto index2 = i.InputInt32(2);                                            \
+    OutOfLineCode* ool;                                                       \
+    if (instr->InputAt(3)->IsRegister()) {                                    \
+      auto length = i.InputRegister(3);                                       \
+      DCHECK_EQ(0, index2);                                                   \
+      __ cmpl(index1, length);                                                \
+      ool = new (zone()) OutOfLineLoadZero(this, result);                     \
+    } else {                                                                  \
+      auto length = i.InputInt32(3);                                          \
+      DCHECK_LE(index2, length);                                              \
+      __ cmpl(index1, Immediate(length - index2));                            \
+      if (index2 == 0) {                                                      \
+        ool = new (zone()) OutOfLineLoadZero(this, result);                   \
+      } else {                                                                \
+        class OutOfLineLoadInteger FINAL : public OutOfLineCode {             \
+         public:                                                              \
+          OutOfLineLoadInteger(CodeGenerator* gen, Register result,           \
+                               Register buffer, Register index1,              \
+                               int32_t index2, int32_t length)                \
+              : OutOfLineCode(gen),                                           \
+                result_(result),                                              \
+                buffer_(buffer),                                              \
+                index1_(index1),                                              \
+                index2_(index2),                                              \
+                length_(length) {}                                            \
+                                                                              \
+          void Generate() FINAL {                                             \
+            DCHECK_NE(0, index2_);                                            \
+            __ leal(kScratchRegister, Operand(index1_, index2_));             \
+            __ xorl(result_, result_);                                        \
+            __ cmpl(kScratchRegister, Immediate(length_));                    \
+            __ j(above_equal, exit());                                        \
+            __ asm_instr(result_,                                             \
+                         Operand(buffer_, kScratchRegister, times_1, 0));     \
+          }                                                                   \
+                                                                              \
+         private:                                                             \
+          Register const result_;                                             \
+          Register const buffer_;                                             \
+          Register const index1_;                                             \
+          int32_t const index2_;                                              \
+          int32_t const length_;                                              \
+        };                                                                    \
+        ool = new (zone()) OutOfLineLoadInteger(this, result, buffer, index1, \
+                                                index2, length);              \
+      }                                                                       \
+    }                                                                         \
+    __ j(above_equal, ool->entry());                                          \
+    __ asm_instr(result, Operand(buffer, index1, times_1, index2));           \
+    __ bind(ool->exit());                                                     \
   } while (false)
 
 
-#define ASSEMBLE_CHECKED_LOAD_INTEGER(asm_instr)                          \
-  do {                                                                    \
-    auto result = i.OutputRegister();                                     \
-    auto offset = i.InputRegister(0);                                     \
-    if (instr->InputAt(1)->IsRegister()) {                                \
-      __ cmpl(offset, i.InputRegister(1));                                \
-    } else {                                                              \
-      __ cmpl(offset, i.InputImmediate(1));                               \
-    }                                                                     \
-    OutOfLineCode* ool = new (zone()) OutOfLineLoadInteger(this, result); \
-    __ j(above_equal, ool->entry());                                      \
-    __ asm_instr(result, i.MemoryOperand(2));                             \
-    __ bind(ool->exit());                                                 \
+#define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr)                                \
+  do {                                                                         \
+    auto buffer = i.InputRegister(0);                                          \
+    auto index1 = i.InputRegister(1);                                          \
+    auto index2 = i.InputInt32(2);                                             \
+    auto value = i.InputDoubleRegister(4);                                     \
+    if (instr->InputAt(3)->IsRegister()) {                                     \
+      auto length = i.InputRegister(3);                                        \
+      DCHECK_EQ(0, index2);                                                    \
+      Label done;                                                              \
+      __ cmpl(index1, length);                                                 \
+      __ j(above_equal, &done, Label::kNear);                                  \
+      __ asm_instr(Operand(buffer, index1, times_1, index2), value);           \
+      __ bind(&done);                                                          \
+    } else {                                                                   \
+      auto length = i.InputInt32(3);                                           \
+      DCHECK_LE(index2, length);                                               \
+      __ cmpl(index1, Immediate(length - index2));                             \
+      if (index2 == 0) {                                                       \
+        Label done;                                                            \
+        __ j(above_equal, &done, Label::kNear);                                \
+        __ asm_instr(Operand(buffer, index1, times_1, index2), value);         \
+        __ bind(&done);                                                        \
+      } else {                                                                 \
+        class OutOfLineStoreFloat FINAL : public OutOfLineCode {               \
+         public:                                                               \
+          OutOfLineStoreFloat(CodeGenerator* gen, Register buffer,             \
+                              Register index1, int32_t index2, int32_t length, \
+                              XMMRegister value)                               \
+              : OutOfLineCode(gen),                                            \
+                buffer_(buffer),                                               \
+                index1_(index1),                                               \
+                index2_(index2),                                               \
+                length_(length),                                               \
+                value_(value) {}                                               \
+                                                                               \
+          void Generate() FINAL {                                              \
+            DCHECK_NE(0, index2_);                                             \
+            __ leal(kScratchRegister, Operand(index1_, index2_));              \
+            __ cmpl(kScratchRegister, Immediate(length_));                     \
+            __ j(above_equal, exit());                                         \
+            __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0),       \
+                         value_);                                              \
+          }                                                                    \
+                                                                               \
+         private:                                                              \
+          Register const buffer_;                                              \
+          Register const index1_;                                              \
+          int32_t const index2_;                                               \
+          int32_t const length_;                                               \
+          XMMRegister const value_;                                            \
+        };                                                                     \
+        auto ool = new (zone())                                                \
+            OutOfLineStoreFloat(this, buffer, index1, index2, length, value);  \
+        __ j(above_equal, ool->entry());                                       \
+        __ asm_instr(Operand(buffer, index1, times_1, index2), value);         \
+        __ bind(ool->exit());                                                  \
+      }                                                                        \
+    }                                                                          \
   } while (false)
 
 
-#define ASSEMBLE_CHECKED_STORE_FLOAT(asm_instr)                 \
-  do {                                                          \
-    auto offset = i.InputRegister(0);                           \
-    if (instr->InputAt(1)->IsRegister()) {                      \
-      __ cmpl(offset, i.InputRegister(1));                      \
-    } else {                                                    \
-      __ cmpl(offset, i.InputImmediate(1));                     \
-    }                                                           \
-    Label done;                                                 \
-    __ j(above_equal, &done, Label::kNear);                     \
-    __ asm_instr(i.MemoryOperand(3), i.InputDoubleRegister(2)); \
-    __ bind(&done);                                             \
+#define ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Value)                 \
+  do {                                                                        \
+    auto buffer = i.InputRegister(0);                                         \
+    auto index1 = i.InputRegister(1);                                         \
+    auto index2 = i.InputInt32(2);                                            \
+    if (instr->InputAt(3)->IsRegister()) {                                    \
+      auto length = i.InputRegister(3);                                       \
+      DCHECK_EQ(0, index2);                                                   \
+      Label done;                                                             \
+      __ cmpl(index1, length);                                                \
+      __ j(above_equal, &done, Label::kNear);                                 \
+      __ asm_instr(Operand(buffer, index1, times_1, index2), value);          \
+      __ bind(&done);                                                         \
+    } else {                                                                  \
+      auto length = i.InputInt32(3);                                          \
+      DCHECK_LE(index2, length);                                              \
+      __ cmpl(index1, Immediate(length - index2));                            \
+      if (index2 == 0) {                                                      \
+        Label done;                                                           \
+        __ j(above_equal, &done, Label::kNear);                               \
+        __ asm_instr(Operand(buffer, index1, times_1, index2), value);        \
+        __ bind(&done);                                                       \
+      } else {                                                                \
+        class OutOfLineStoreInteger FINAL : public OutOfLineCode {            \
+         public:                                                              \
+          OutOfLineStoreInteger(CodeGenerator* gen, Register buffer,          \
+                                Register index1, int32_t index2,              \
+                                int32_t length, Value value)                  \
+              : OutOfLineCode(gen),                                           \
+                buffer_(buffer),                                              \
+                index1_(index1),                                              \
+                index2_(index2),                                              \
+                length_(length),                                              \
+                value_(value) {}                                              \
+                                                                              \
+          void Generate() FINAL {                                             \
+            DCHECK_NE(0, index2_);                                            \
+            __ leal(kScratchRegister, Operand(index1_, index2_));             \
+            __ cmpl(kScratchRegister, Immediate(length_));                    \
+            __ j(above_equal, exit());                                        \
+            __ asm_instr(Operand(buffer_, kScratchRegister, times_1, 0),      \
+                         value_);                                             \
+          }                                                                   \
+                                                                              \
+         private:                                                             \
+          Register const buffer_;                                             \
+          Register const index1_;                                             \
+          int32_t const index2_;                                              \
+          int32_t const length_;                                              \
+          Value const value_;                                                 \
+        };                                                                    \
+        auto ool = new (zone()) OutOfLineStoreInteger(this, buffer, index1,   \
+                                                      index2, length, value); \
+        __ j(above_equal, ool->entry());                                      \
+        __ asm_instr(Operand(buffer, index1, times_1, index2), value);        \
+        __ bind(ool->exit());                                                 \
+      }                                                                       \
+    }                                                                         \
   } while (false)
 
 
-#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr)            \
-  do {                                                       \
-    auto offset = i.InputRegister(0);                        \
-    if (instr->InputAt(1)->IsRegister()) {                   \
-      __ cmpl(offset, i.InputRegister(1));                   \
-    } else {                                                 \
-      __ cmpl(offset, i.InputImmediate(1));                  \
-    }                                                        \
-    Label done;                                              \
-    __ j(above_equal, &done, Label::kNear);                  \
-    if (instr->InputAt(2)->IsRegister()) {                   \
-      __ asm_instr(i.MemoryOperand(3), i.InputRegister(2));  \
-    } else {                                                 \
-      __ asm_instr(i.MemoryOperand(3), i.InputImmediate(2)); \
-    }                                                        \
-    __ bind(&done);                                          \
+#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr)                \
+  do {                                                           \
+    if (instr->InputAt(4)->IsRegister()) {                       \
+      Register value = i.InputRegister(4);                       \
+      ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Register);  \
+    } else {                                                     \
+      Immediate value = i.InputImmediate(4);                     \
+      ASSEMBLE_CHECKED_STORE_INTEGER_IMPL(asm_instr, Immediate); \
+    }                                                            \
   } while (false)
 
 
index a8a9f9d..4c32255 100644 (file)
@@ -230,12 +230,21 @@ void InstructionSelector::VisitCheckedLoad(Node* node) {
       UNREACHABLE();
       return;
   }
-  InstructionOperand* offset_operand = g.UseRegister(offset);
+  if (offset->opcode() == IrOpcode::kInt32Add && CanCover(node, offset)) {
+    Int32Matcher mlength(length);
+    Int32BinopMatcher moffset(offset);
+    if (mlength.HasValue() && moffset.right().HasValue() &&
+        mlength.Value() >= moffset.right().Value()) {
+      Emit(opcode, g.DefineAsRegister(node), g.UseRegister(buffer),
+           g.UseRegister(moffset.left().node()),
+           g.UseImmediate(moffset.right().node()), g.UseImmediate(length));
+      return;
+    }
+  }
   InstructionOperand* length_operand =
       g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length);
-  Emit(opcode | AddressingModeField::encode(kMode_MR1),
-       g.DefineAsRegister(node), offset_operand, length_operand,
-       g.UseRegister(buffer), offset_operand);
+  Emit(opcode, g.DefineAsRegister(node), g.UseRegister(buffer),
+       g.UseRegister(offset), g.TempImmediate(0), length_operand);
 }
 
 
@@ -269,11 +278,22 @@ void InstructionSelector::VisitCheckedStore(Node* node) {
   }
   InstructionOperand* value_operand =
       g.CanBeImmediate(value) ? g.UseImmediate(value) : g.UseRegister(value);
-  InstructionOperand* offset_operand = g.UseRegister(offset);
+  if (offset->opcode() == IrOpcode::kInt32Add && CanCover(node, offset)) {
+    Int32Matcher mlength(length);
+    Int32BinopMatcher moffset(offset);
+    if (mlength.HasValue() && moffset.right().HasValue() &&
+        mlength.Value() >= moffset.right().Value()) {
+      Emit(opcode, nullptr, g.UseRegister(buffer),
+           g.UseRegister(moffset.left().node()),
+           g.UseImmediate(moffset.right().node()), g.UseImmediate(length),
+           value_operand);
+      return;
+    }
+  }
   InstructionOperand* length_operand =
       g.CanBeImmediate(length) ? g.UseImmediate(length) : g.UseRegister(length);
-  Emit(opcode | AddressingModeField::encode(kMode_MR1), nullptr, offset_operand,
-       length_operand, value_operand, g.UseRegister(buffer), offset_operand);
+  Emit(opcode, nullptr, g.UseRegister(buffer), g.UseRegister(offset),
+       g.TempImmediate(0), length_operand, value_operand);
 }
 
 
diff --git a/test/mjsunit/asm/float32array-negative-offset.js b/test/mjsunit/asm/float32array-negative-offset.js
new file mode 100644 (file)
index 0000000..524bdc8
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+var stdlib = this;
+var buffer = new ArrayBuffer(64 * 1024);
+var foreign = {}
+
+
+var m = (function Module(stdlib, foreign, heap) {
+  "use asm";
+  var MEM32 = new stdlib.Float32Array(heap);
+  function load(i) {
+    i = i|0;
+    i = +MEM32[i >> 2];
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = +v;
+    MEM32[i >> 2] = v;
+  }
+  function load8(i) {
+    i = i|0;
+    i = +MEM32[i + 8 >> 2];
+    return i;
+  }
+  function store8(i, v) {
+    i = i|0;
+    v = +v;
+    MEM32[i + 8 >> 2] = v;
+  }
+  return { load: load, store: store, load8: load8, store8: store8 };
+})(stdlib, foreign, buffer);
+
+assertEquals(NaN, m.load(-8));
+assertEquals(NaN, m.load8(-16));
+m.store(0, 42.0);
+assertEquals(42.0, m.load8(-8));
+m.store8(-8, 99.0);
+assertEquals(99.0, m.load(0));
+assertEquals(99.0, m.load8(-8));
diff --git a/test/mjsunit/asm/float64array-negative-offset.js b/test/mjsunit/asm/float64array-negative-offset.js
new file mode 100644 (file)
index 0000000..154bd82
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+var stdlib = this;
+var buffer = new ArrayBuffer(64 * 1024);
+var foreign = {}
+
+
+var m = (function Module(stdlib, foreign, heap) {
+  "use asm";
+  var MEM64 = new stdlib.Float64Array(heap);
+  function load(i) {
+    i = i|0;
+    i = +MEM64[i >> 3];
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = +v;
+    MEM64[i >> 3] = v;
+  }
+  function load8(i) {
+    i = i|0;
+    i = +MEM64[i + 8 >> 3];
+    return i;
+  }
+  function store8(i, v) {
+    i = i|0;
+    v = +v;
+    MEM64[i + 8 >> 3] = v;
+  }
+  return { load: load, store: store, load8: load8, store8: store8 };
+})(stdlib, foreign, buffer);
+
+assertEquals(NaN, m.load(-8));
+assertEquals(NaN, m.load8(-16));
+m.store(0, 42.0);
+assertEquals(42.0, m.load8(-8));
+m.store8(-8, 99.0);
+assertEquals(99.0, m.load(0));
+assertEquals(99.0, m.load8(-8));
diff --git a/test/mjsunit/asm/int16array-negative-offset.js b/test/mjsunit/asm/int16array-negative-offset.js
new file mode 100644 (file)
index 0000000..5d33115
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+var stdlib = this;
+var buffer = new ArrayBuffer(64 * 1024);
+var foreign = {}
+
+
+var m = (function Module(stdlib, foreign, heap) {
+  "use asm";
+  var MEM16 = new stdlib.Int16Array(heap);
+  function load(i) {
+    i = i|0;
+    i = MEM16[i >> 1]|0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM16[i >> 1] = v;
+  }
+  function load8(i) {
+    i = i|0;
+    i = MEM16[i + 8 >> 1]|0;
+    return i;
+  }
+  function store8(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM16[i + 8 >> 1] = v;
+  }
+  return { load: load, store: store, load8: load8, store8: store8 };
+})(stdlib, foreign, buffer);
+
+assertEquals(0, m.load(-8));
+assertEquals(0, m.load8(-16));
+m.store(0, 42);
+assertEquals(42, m.load8(-8));
+m.store8(-8, 99);
+assertEquals(99, m.load(0));
+assertEquals(99, m.load8(-8));
diff --git a/test/mjsunit/asm/int32array-negative-offset.js b/test/mjsunit/asm/int32array-negative-offset.js
new file mode 100644 (file)
index 0000000..d1a8efa
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+var stdlib = this;
+var buffer = new ArrayBuffer(64 * 1024);
+var foreign = {}
+
+
+var m = (function Module(stdlib, foreign, heap) {
+  "use asm";
+  var MEM32 = new stdlib.Int32Array(heap);
+  function load(i) {
+    i = i|0;
+    i = MEM32[i >> 2]|0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM32[i >> 2] = v;
+  }
+  function load8(i) {
+    i = i|0;
+    i = MEM32[i + 8 >> 2]|0;
+    return i;
+  }
+  function store8(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM32[i + 8 >> 2] = v;
+  }
+  return { load: load, store: store, load8: load8, store8: store8 };
+})(stdlib, foreign, buffer);
+
+assertEquals(0, m.load(-8));
+assertEquals(0, m.load8(-16));
+m.store(0, 42);
+assertEquals(42, m.load8(-8));
+m.store8(-8, 99);
+assertEquals(99, m.load(0));
+assertEquals(99, m.load8(-8));
diff --git a/test/mjsunit/asm/int8array-negative-offset.js b/test/mjsunit/asm/int8array-negative-offset.js
new file mode 100644 (file)
index 0000000..47dbc1b
--- /dev/null
@@ -0,0 +1,42 @@
+// 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.
+
+var stdlib = this;
+var buffer = new ArrayBuffer(64 * 1024);
+var foreign = {}
+
+
+var m = (function Module(stdlib, foreign, heap) {
+  "use asm";
+  var MEM8 = new stdlib.Int8Array(heap);
+  function load(i) {
+    i = i|0;
+    i = MEM8[i >> 0]|0;
+    return i;
+  }
+  function store(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM8[i >> 0] = v;
+  }
+  function load8(i) {
+    i = i|0;
+    i = MEM8[i + 8 >> 0]|0;
+    return i;
+  }
+  function store8(i, v) {
+    i = i|0;
+    v = v|0;
+    MEM8[i + 8 >> 0] = v;
+  }
+  return { load: load, store: store, load8: load8, store8: store8 };
+})(stdlib, foreign, buffer);
+
+assertEquals(0, m.load(-8));
+assertEquals(0, m.load8(-16));
+m.store(0, 42);
+assertEquals(42, m.load8(-8));
+m.store8(-8, 99);
+assertEquals(99, m.load(0));
+assertEquals(99, m.load8(-8));