diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 7b50b01049..0901df2b27 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -242,7 +242,7 @@ class CodeGenerator: public AstVisitor { void LoadReference(Reference* ref); void UnloadReference(Reference* ref); - MemOperand ContextOperand(Register context, int index) const { + static MemOperand ContextOperand(Register context, int index) { return MemOperand(context, Context::SlotOffset(index)); } @@ -254,7 +254,7 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - MemOperand GlobalObject() const { + static MemOperand GlobalObject() { return ContextOperand(cp, Context::GLOBAL_INDEX); } @@ -425,6 +425,7 @@ class CodeGenerator: public AstVisitor { friend class VirtualFrame; friend class JumpTarget; friend class Reference; + friend class FastCodeGenerator; DISALLOW_COPY_AND_ASSIGN(CodeGenerator); }; diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 6b485474da..c99e051310 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -160,9 +160,10 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { Expression* rhs = expr->value(); Visit(rhs); - // Left-hand side is always a (parameter or local) slot. + // Left-hand side can only be a global or a (parameter or local) slot. Variable* var = expr->target()->AsVariableProxy()->AsVariable(); - ASSERT(var != NULL && var->slot() != NULL); + ASSERT(var != NULL); + ASSERT(var->is_global() || var->slot() != NULL); // Complete the assignment based on the location of the right-hand-side // value and the desired location of the assignment value. @@ -171,27 +172,53 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { ASSERT(!destination.is_constant()); ASSERT(!source.is_nowhere()); - if (source.is_temporary()) { + if (var->is_global()) { + // Assignment to a global variable, use inline caching. Right-hand-side + // value is passed in r0, variable name in r2, and the global object on + // the stack. + if (source.is_temporary()) { + __ pop(r0); + } else { + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + __ mov(r0, Operand(rhs->AsLiteral()->handle())); + } + __ mov(r2, Operand(var->name())); + __ ldr(ip, CodeGenerator::GlobalObject()); + __ push(ip); + Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + // Overwrite the global object on the stack with the result if needed. if (destination.is_temporary()) { - // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary - // on the stack. - __ ldr(ip, MemOperand(sp)); + __ str(r0, MemOperand(sp)); } else { ASSERT(destination.is_nowhere()); - // Case 'var = temp'. Discard right-hand-side temporary. - __ pop(ip); + __ pop(); } - __ str(ip, MemOperand(fp, SlotOffset(var->slot()))); + } else { - ASSERT(source.is_constant()); - ASSERT(rhs->AsLiteral() != NULL); - // Two cases: 'temp <- (var = constant)', or 'var = constant' with a - // discarded result. Always perform the assignment. - __ mov(ip, Operand(rhs->AsLiteral()->handle())); - __ str(ip, MemOperand(fp, SlotOffset(var->slot()))); - if (destination.is_temporary()) { - // Case 'temp <- (var = constant)'. Save result. - __ push(ip); + if (source.is_temporary()) { + if (destination.is_temporary()) { + // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side + // temporary on the stack. + __ ldr(ip, MemOperand(sp)); + } else { + ASSERT(destination.is_nowhere()); + // Case 'var = temp'. Discard right-hand-side temporary. + __ pop(ip); + } + __ str(ip, MemOperand(fp, SlotOffset(var->slot()))); + } else { + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + // Two cases: 'temp <- (var = constant)', or 'var = constant' with a + // discarded result. Always perform the assignment. + __ mov(ip, Operand(rhs->AsLiteral()->handle())); + __ str(ip, MemOperand(fp, SlotOffset(var->slot()))); + if (destination.is_temporary()) { + // Case 'temp <- (var = constant)'. Save result. + __ push(ip); + } } } } diff --git a/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc index 2d5b1406c2..97d164eef9 100644 --- a/src/arm/virtual-frame-arm.cc +++ b/src/arm/virtual-frame-arm.cc @@ -255,7 +255,7 @@ void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, void VirtualFrame::RawCallCodeObject(Handle code, - RelocInfo::Mode rmode) { + RelocInfo::Mode rmode) { ASSERT(cgen()->HasValidEntryRegisters()); __ Call(code, rmode); } diff --git a/src/compiler.cc b/src/compiler.cc index 64b2a4a322..7fb7021fff 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -649,12 +649,14 @@ void CodeGenSelector::VisitAssignment(Assignment* expr) { } Variable* var = expr->target()->AsVariableProxy()->AsVariable(); - if (var == NULL || var->is_global()) BAILOUT("non-variable assignment"); + if (var == NULL) BAILOUT("non-variable assignment"); - ASSERT(var->slot() != NULL); - Slot::Type type = var->slot()->type(); - if (type != Slot::PARAMETER && type != Slot::LOCAL) { - BAILOUT("non-parameter/non-local slot assignment"); + if (!var->is_global()) { + ASSERT(var->slot() != NULL); + Slot::Type type = var->slot()->type(); + if (type != Slot::PARAMETER && type != Slot::LOCAL) { + BAILOUT("non-parameter/non-local slot assignment"); + } } Visit(expr->value()); diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index ec4a8be16d..8c500433b6 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -396,7 +396,7 @@ class CodeGenerator: public AstVisitor { void LoadReference(Reference* ref); void UnloadReference(Reference* ref); - Operand ContextOperand(Register context, int index) const { + static Operand ContextOperand(Register context, int index) { return Operand(context, Context::SlotOffset(index)); } @@ -407,7 +407,7 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - Operand GlobalObject() const { + static Operand GlobalObject() { return ContextOperand(esi, Context::GLOBAL_INDEX); } @@ -616,6 +616,7 @@ class CodeGenerator: public AstVisitor { friend class JumpTarget; friend class Reference; friend class Result; + friend class FastCodeGenerator; friend class CodeGeneratorPatcher; // Used in test-log-stack-tracer.cc diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index 5e5d7c17a0..1304535357 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -149,9 +149,10 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { Expression* rhs = expr->value(); Visit(rhs); - // Left-hand side is always a (parameter or local) slot. + // Left-hand side can only be a global or a (parameter or local) slot. Variable* var = expr->target()->AsVariableProxy()->AsVariable(); - ASSERT(var != NULL && var->slot() != NULL); + ASSERT(var != NULL); + ASSERT(var->is_global() || var->slot() != NULL); // Complete the assignment based on the location of the right-hand-side // value and the desired location of the assignment value. @@ -160,27 +161,53 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { ASSERT(!destination.is_constant()); ASSERT(!source.is_nowhere()); - if (source.is_temporary()) { + if (var->is_global()) { + // Assignment to a global variable, use inline caching. Right-hand-side + // value is passed in eax, variable name in ecx, and the global object + // on the stack. + if (source.is_temporary()) { + __ pop(eax); + } else { + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + __ mov(eax, rhs->AsLiteral()->handle()); + } + __ mov(ecx, var->name()); + __ push(CodeGenerator::GlobalObject()); + Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ call(ic, RelocInfo::CODE_TARGET); + // Overwrite the global object on the stack with the result if needed. if (destination.is_temporary()) { - // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary - // on the stack. - __ mov(eax, Operand(esp, 0)); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + __ mov(Operand(esp, 0), eax); } else { ASSERT(destination.is_nowhere()); - // Case 'var = temp'. Discard right-hand-side temporary. - __ pop(Operand(ebp, SlotOffset(var->slot()))); + __ pop(eax); } + } else { - ASSERT(source.is_constant()); - ASSERT(rhs->AsLiteral() != NULL); - // Two cases: 'temp <- (var = constant)', or 'var = constant' with a - // discarded result. Always perform the assignment. - __ mov(eax, rhs->AsLiteral()->handle()); - __ mov(Operand(ebp, SlotOffset(var->slot())), eax); - if (destination.is_temporary()) { - // Case 'temp <- (var = constant)'. Save result. - __ push(eax); + // Local or parameter assignment. + if (source.is_temporary()) { + if (destination.is_temporary()) { + // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side + // temporary on the stack. + __ mov(eax, Operand(esp, 0)); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + } else { + ASSERT(destination.is_nowhere()); + // Case 'var = temp'. Discard right-hand-side temporary. + __ pop(Operand(ebp, SlotOffset(var->slot()))); + } + } else { + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + // Two cases: 'temp <- (var = constant)', or 'var = constant' with a + // discarded result. Always perform the assignment. + __ mov(eax, rhs->AsLiteral()->handle()); + __ mov(Operand(ebp, SlotOffset(var->slot())), eax); + if (destination.is_temporary()) { + // Case 'temp <- (var = constant)'. Save result. + __ push(eax); + } } } } diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 5fa6583a31..ddb571db48 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -398,7 +398,7 @@ class CodeGenerator: public AstVisitor { void LoadReference(Reference* ref); void UnloadReference(Reference* ref); - Operand ContextOperand(Register context, int index) const { + static Operand ContextOperand(Register context, int index) { return Operand(context, Context::SlotOffset(index)); } @@ -409,7 +409,7 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - Operand GlobalObject() const { + static Operand GlobalObject() { return ContextOperand(rsi, Context::GLOBAL_INDEX); } @@ -616,6 +616,7 @@ class CodeGenerator: public AstVisitor { friend class JumpTarget; friend class Reference; friend class Result; + friend class FastCodeGenerator; friend class CodeGeneratorPatcher; // Used in test-log-stack-tracer.cc diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index 9dcdf8e8b8..7867b6fb2e 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -166,9 +166,10 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { Expression* rhs = expr->value(); Visit(rhs); - // Left-hand side is always a (parameter or local) slot. + // Left-hand side can only be a global or a (parameter or local) slot. Variable* var = expr->target()->AsVariableProxy()->AsVariable(); - ASSERT(var != NULL && var->slot() != NULL); + ASSERT(var != NULL); + ASSERT(var->is_global() || var->slot() != NULL); // Complete the assignment based on the location of the right-hand-side // value and the desired location of the assignment value. @@ -177,27 +178,50 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { ASSERT(!destination.is_constant()); ASSERT(!source.is_nowhere()); - if (source.is_temporary()) { - if (destination.is_temporary()) { - // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary - // on the stack. - __ movq(kScratchRegister, Operand(rsp, 0)); - __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); + if (var->is_global()) { + // Assignment to a global variable, use inline caching. Right-hand-side + // value is passed in rax, variable name in rcx, and the global object + // on the stack. + if (source.is_temporary()) { + __ pop(rax); } else { - ASSERT(destination.is_nowhere()); - // Case 'var = temp'. Discard right-hand-side temporary. - __ pop(Operand(rbp, SlotOffset(var->slot()))); + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + __ Move(rax, rhs->AsLiteral()->handle()); + } + __ Move(rcx, var->name()); + __ push(CodeGenerator::GlobalObject()); + Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + __ Call(ic, RelocInfo::CODE_TARGET); + // Overwrite the global object on the stack with the result if needed. + if (destination.is_temporary()) { + __ movq(Operand(rsp, 0), rax); + } else { + __ pop(rax); } } else { - ASSERT(source.is_constant()); - ASSERT(rhs->AsLiteral() != NULL); - // Two cases: 'temp <- (var = constant)', or 'var = constant' with a - // discarded result. Always perform the assignment. - __ Move(kScratchRegister, rhs->AsLiteral()->handle()); - __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); - if (destination.is_temporary()) { - // Case 'temp <- (var = constant)'. Save result. - __ push(kScratchRegister); + if (source.is_temporary()) { + if (destination.is_temporary()) { + // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary + // on the stack. + __ movq(kScratchRegister, Operand(rsp, 0)); + __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); + } else { + ASSERT(destination.is_nowhere()); + // Case 'var = temp'. Discard right-hand-side temporary. + __ pop(Operand(rbp, SlotOffset(var->slot()))); + } + } else { + ASSERT(source.is_constant()); + ASSERT(rhs->AsLiteral() != NULL); + // Two cases: 'temp <- (var = constant)', or 'var = constant' with a + // discarded result. Always perform the assignment. + __ Move(kScratchRegister, rhs->AsLiteral()->handle()); + __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); + if (destination.is_temporary()) { + // Case 'temp <- (var = constant)'. Save result. + __ push(kScratchRegister); + } } } } diff --git a/test/mjsunit/compiler/globals.js b/test/mjsunit/compiler/globals.js new file mode 100644 index 0000000000..0be772befa --- /dev/null +++ b/test/mjsunit/compiler/globals.js @@ -0,0 +1,44 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test references and assignments to global variables. +var g = 0; + +// Test compilation of a global variable store. +assertEquals(1, eval('g = 1')); +// Test that the store worked. +assertEquals(1, g); + +// Test that patching the IC in the compiled code works. +assertEquals(1, eval('g = 1')); +assertEquals(1, g); +assertEquals(1, eval('g = 1')); +assertEquals(1, g); + +// Test a second store. +assertEquals("2", eval('g = "2"')); +assertEquals("2", g);