From 19418d76dfba7a48b3ab5760e2295a425b50590b Mon Sep 17 00:00:00 2001 From: Benedikt Meurer Date: Wed, 10 Dec 2014 08:48:57 +0100 Subject: [PATCH] [x64] Fix optimization for certain checked load/stores. 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 | 301 ++++++++++++++---- src/compiler/x64/instruction-selector-x64.cc | 34 +- .../asm/float32array-negative-offset.js | 42 +++ .../asm/float64array-negative-offset.js | 42 +++ .../mjsunit/asm/int16array-negative-offset.js | 42 +++ .../mjsunit/asm/int32array-negative-offset.js | 42 +++ test/mjsunit/asm/int8array-negative-offset.js | 42 +++ .../{regress-lena.js => regress-439743.js} | 0 8 files changed, 480 insertions(+), 65 deletions(-) create mode 100644 test/mjsunit/asm/float32array-negative-offset.js create mode 100644 test/mjsunit/asm/float64array-negative-offset.js create mode 100644 test/mjsunit/asm/int16array-negative-offset.js create mode 100644 test/mjsunit/asm/int32array-negative-offset.js create mode 100644 test/mjsunit/asm/int8array-negative-offset.js rename test/mjsunit/compiler/{regress-lena.js => regress-439743.js} (100%) diff --git a/src/compiler/x64/code-generator-x64.cc b/src/compiler/x64/code-generator-x64.cc index ec4d5b6c9..84b88f249 100644 --- a/src/compiler/x64/code-generator-x64.cc +++ b/src/compiler/x64/code-generator-x64.cc @@ -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) diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index a8a9f9d58..4c32255ba 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -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 index 000000000..524bdc840 --- /dev/null +++ b/test/mjsunit/asm/float32array-negative-offset.js @@ -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 index 000000000..154bd82cd --- /dev/null +++ b/test/mjsunit/asm/float64array-negative-offset.js @@ -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 index 000000000..5d33115ad --- /dev/null +++ b/test/mjsunit/asm/int16array-negative-offset.js @@ -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 index 000000000..d1a8efa67 --- /dev/null +++ b/test/mjsunit/asm/int32array-negative-offset.js @@ -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 index 000000000..47dbc1bfe --- /dev/null +++ b/test/mjsunit/asm/int8array-negative-offset.js @@ -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)); diff --git a/test/mjsunit/compiler/regress-lena.js b/test/mjsunit/compiler/regress-439743.js similarity index 100% rename from test/mjsunit/compiler/regress-lena.js rename to test/mjsunit/compiler/regress-439743.js -- 2.34.1