Commit 690bda84 authored by Creddy's avatar Creddy Committed by Commit Bot

[Interpreter] Do not use IC slots for property load/stores in an IIFE and top-level code

An IIFE or top-level code is executed only once hence, there is no need to collect
type feedback. We can save some memory by not using IC slots for property Loads/Stores
within a IIFE/top-level code. This CL emits Runtime Get/Set property calls instead of LdaNamedProperty
/StaNamedProperty for the property loads within a IIFE and top-level code.

Change-Id: I3e0ce26d05d82bb3648cb9262c4e112a2c4556c9
Reviewed-on: https://chromium-review.googlesource.com/1146579Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Chandan Reddy <chandanreddy@google.com>
Cr-Commit-Position: refs/heads/master@{#54949}
parent 46952216
...@@ -2201,6 +2201,12 @@ class FunctionLiteral final : public Expression { ...@@ -2201,6 +2201,12 @@ class FunctionLiteral final : public Expression {
bool is_anonymous_expression() const { bool is_anonymous_expression() const {
return function_type() == kAnonymousExpression; return function_type() == kAnonymousExpression;
} }
void mark_as_iife() { bit_field_ = IIFEBit::update(bit_field_, true); }
bool is_iife() const { return IIFEBit::decode(bit_field_); }
bool is_top_level() const {
return function_literal_id() == FunctionLiteral::kIdTypeTopLevel;
}
bool is_wrapped() const { return function_type() == kWrapped; } bool is_wrapped() const { return function_type() == kWrapped; }
LanguageMode language_mode() const; LanguageMode language_mode() const;
...@@ -2333,7 +2339,7 @@ class FunctionLiteral final : public Expression { ...@@ -2333,7 +2339,7 @@ class FunctionLiteral final : public Expression {
kHasDuplicateParameters) | kHasDuplicateParameters) |
DontOptimizeReasonField::encode(BailoutReason::kNoReason) | DontOptimizeReasonField::encode(BailoutReason::kNoReason) |
RequiresInstanceFieldsInitializer::encode(false) | RequiresInstanceFieldsInitializer::encode(false) |
HasBracesField::encode(has_braces); HasBracesField::encode(has_braces) | IIFEBit::encode(false);
if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile(); if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile();
DCHECK_EQ(body == nullptr, expected_property_count < 0); DCHECK_EQ(body == nullptr, expected_property_count < 0);
} }
...@@ -2348,6 +2354,7 @@ class FunctionLiteral final : public Expression { ...@@ -2348,6 +2354,7 @@ class FunctionLiteral final : public Expression {
: public BitField<bool, DontOptimizeReasonField::kNext, 1> {}; : public BitField<bool, DontOptimizeReasonField::kNext, 1> {};
class HasBracesField class HasBracesField
: public BitField<bool, RequiresInstanceFieldsInitializer::kNext, 1> {}; : public BitField<bool, RequiresInstanceFieldsInitializer::kNext, 1> {};
class IIFEBit : public BitField<bool, HasBracesField::kNext, 1> {};
int expected_property_count_; int expected_property_count_;
int parameter_count_; int parameter_count_;
......
...@@ -277,6 +277,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -277,6 +277,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(IsTypedArray) \ V(IsTypedArray) \
/* Loads */ \ /* Loads */ \
V(LoadLookupSlotForCall) \ V(LoadLookupSlotForCall) \
V(GetProperty) \
/* Arrays */ \ /* Arrays */ \
V(ArraySpeciesConstructor) \ V(ArraySpeciesConstructor) \
V(EstimateNumberOfElements) \ V(EstimateNumberOfElements) \
......
...@@ -315,6 +315,11 @@ DEFINE_BOOL(optimize_for_size, false, ...@@ -315,6 +315,11 @@ DEFINE_BOOL(optimize_for_size, false,
"Enables optimizations which favor memory size over execution " "Enables optimizations which favor memory size over execution "
"speed") "speed")
// Flag for one shot optimiztions.
DEFINE_BOOL(enable_one_shot_optimization, true,
"Enable size optimizations for the code that will "
"only be executed once")
DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, 1) DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, 1)
// Flags for data representation optimizations // Flags for data representation optimizations
......
...@@ -1815,6 +1815,14 @@ void BytecodeGenerator::AddToEagerLiteralsIfEager(FunctionLiteral* literal) { ...@@ -1815,6 +1815,14 @@ void BytecodeGenerator::AddToEagerLiteralsIfEager(FunctionLiteral* literal) {
} }
} }
bool BytecodeGenerator::ShouldOptimizeAsOneShot() const {
if (!FLAG_enable_one_shot_optimization) return false;
if (loop_depth_ > 0) return false;
return info()->literal()->is_top_level() || info()->literal()->is_iife();
}
void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) { void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
size_t class_boilerplate_entry = size_t class_boilerplate_entry =
builder()->AllocateDeferredConstantPoolEntry(); builder()->AllocateDeferredConstantPoolEntry();
...@@ -2776,6 +2784,54 @@ void BytecodeGenerator::BuildVariableAssignment( ...@@ -2776,6 +2784,54 @@ void BytecodeGenerator::BuildVariableAssignment(
} }
} }
void BytecodeGenerator::BuildLoadNamedProperty(Property* property,
Register object,
const AstRawString* name) {
if (ShouldOptimizeAsOneShot()) {
RegisterList args = register_allocator()->NewRegisterList(2);
size_t name_index = builder()->GetConstantPoolEntry(name);
builder()
->MoveRegister(object, args[0])
.LoadConstantPoolEntry(name_index)
.StoreAccumulatorInRegister(args[1])
.CallRuntime(Runtime::kInlineGetProperty, args);
} else {
FeedbackSlot slot = GetCachedLoadICSlot(property->obj(), name);
builder()->LoadNamedProperty(object, name, feedback_index(slot));
}
}
void BytecodeGenerator::BuildStoreNamedProperty(Property* property,
Register object,
const AstRawString* name) {
Register value;
if (!execution_result()->IsEffect()) {
value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
}
if (ShouldOptimizeAsOneShot()) {
RegisterList args = register_allocator()->NewRegisterList(4);
size_t name_index = builder()->GetConstantPoolEntry(name);
builder()
->MoveRegister(object, args[0])
.StoreAccumulatorInRegister(args[2])
.LoadConstantPoolEntry(name_index)
.StoreAccumulatorInRegister(args[1])
.LoadLiteral(Smi::FromEnum(language_mode()))
.StoreAccumulatorInRegister(args[3])
.CallRuntime(Runtime::kSetProperty, args);
} else {
FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), name);
builder()->StoreNamedProperty(object, name, feedback_index(slot),
language_mode());
}
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
}
void BytecodeGenerator::VisitAssignment(Assignment* expr) { void BytecodeGenerator::VisitAssignment(Assignment* expr) {
DCHECK(expr->target()->IsValidReferenceExpression() || DCHECK(expr->target()->IsValidReferenceExpression() ||
(expr->op() == Token::INIT && expr->target()->IsVariableProxy() && (expr->op() == Token::INIT && expr->target()->IsVariableProxy() &&
...@@ -2837,8 +2893,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2837,8 +2893,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break; break;
} }
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
FeedbackSlot slot = GetCachedLoadICSlot(property->obj(), name); BuildLoadNamedProperty(property, object, name);
builder()->LoadNamedProperty(object, name, feedback_index(slot));
break; break;
} }
case KEYED_PROPERTY: { case KEYED_PROPERTY: {
...@@ -2888,17 +2943,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { ...@@ -2888,17 +2943,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break; break;
} }
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), name); BuildStoreNamedProperty(property, object, name);
Register value;
if (!execution_result()->IsEffect()) {
value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
}
builder()->StoreNamedProperty(object, name, feedback_index(slot),
language_mode());
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
break; break;
} }
case KEYED_PROPERTY: { case KEYED_PROPERTY: {
...@@ -3364,11 +3409,9 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { ...@@ -3364,11 +3409,9 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
UNREACHABLE(); UNREACHABLE();
case NAMED_PROPERTY: { case NAMED_PROPERTY: {
builder()->SetExpressionPosition(property); builder()->SetExpressionPosition(property);
builder()->LoadNamedProperty( const AstRawString* name =
obj, property->key()->AsLiteral()->AsRawPropertyName(), property->key()->AsLiteral()->AsRawPropertyName();
feedback_index(GetCachedLoadICSlot( BuildLoadNamedProperty(property, obj, name);
property->obj(),
property->key()->AsLiteral()->AsRawPropertyName())));
break; break;
} }
case KEYED_PROPERTY: { case KEYED_PROPERTY: {
......
...@@ -120,6 +120,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -120,6 +120,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitPropertyLoadForRegister(Register obj, Property* expr, void VisitPropertyLoadForRegister(Register obj, Property* expr,
Register destination); Register destination);
void BuildLoadNamedProperty(Property* property, Register object,
const AstRawString* name);
void BuildStoreNamedProperty(Property* property, Register object,
const AstRawString* name);
void BuildVariableLoad(Variable* variable, HoleCheckMode hole_check_mode, void BuildVariableLoad(Variable* variable, HoleCheckMode hole_check_mode,
TypeofMode typeof_mode = NOT_INSIDE_TYPEOF); TypeofMode typeof_mode = NOT_INSIDE_TYPEOF);
void BuildVariableLoadForAccumulatorValue( void BuildVariableLoadForAccumulatorValue(
...@@ -277,6 +282,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> { ...@@ -277,6 +282,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void AddToEagerLiteralsIfEager(FunctionLiteral* literal); void AddToEagerLiteralsIfEager(FunctionLiteral* literal);
// Checks if the visited expression is one shot, i.e executed only once. Any
// expression either in a top level code or an IIFE that is not within a loop
// is eligible for one shot optimizations.
inline bool ShouldOptimizeAsOneShot() const;
static constexpr ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint) { static constexpr ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint) {
return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean
: ToBooleanMode::kConvertToBoolean; : ToBooleanMode::kConvertToBoolean;
......
...@@ -224,6 +224,12 @@ Node* IntrinsicsGenerator::HasProperty( ...@@ -224,6 +224,12 @@ Node* IntrinsicsGenerator::HasProperty(
args, context, Builtins::CallableFor(isolate(), Builtins::kHasProperty)); args, context, Builtins::CallableFor(isolate(), Builtins::kHasProperty));
} }
Node* IntrinsicsGenerator::GetProperty(
const InterpreterAssembler::RegListNodePair& args, Node* context) {
return IntrinsicAsStubCall(
args, context, Builtins::CallableFor(isolate(), Builtins::kGetProperty));
}
Node* IntrinsicsGenerator::RejectPromise( Node* IntrinsicsGenerator::RejectPromise(
const InterpreterAssembler::RegListNodePair& args, Node* context) { const InterpreterAssembler::RegListNodePair& args, Node* context) {
return IntrinsicAsStubCall( return IntrinsicAsStubCall(
......
...@@ -26,6 +26,7 @@ namespace interpreter { ...@@ -26,6 +26,7 @@ namespace interpreter {
V(CreateIterResultObject, create_iter_result_object, 2) \ V(CreateIterResultObject, create_iter_result_object, 2) \
V(CreateAsyncFromSyncIterator, create_async_from_sync_iterator, 1) \ V(CreateAsyncFromSyncIterator, create_async_from_sync_iterator, 1) \
V(HasProperty, has_property, 2) \ V(HasProperty, has_property, 2) \
V(GetProperty, get_property, 2) \
V(IsArray, is_array, 1) \ V(IsArray, is_array, 1) \
V(IsJSProxy, is_js_proxy, 1) \ V(IsJSProxy, is_js_proxy, 1) \
V(IsJSReceiver, is_js_receiver, 1) \ V(IsJSReceiver, is_js_receiver, 1) \
......
...@@ -3378,6 +3378,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) { ...@@ -3378,6 +3378,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
// function literal eagerly, we can also compile it eagerly. // function literal eagerly, we can also compile it eagerly.
if (result->IsFunctionLiteral()) { if (result->IsFunctionLiteral()) {
result->AsFunctionLiteral()->SetShouldEagerCompile(); result->AsFunctionLiteral()->SetShouldEagerCompile();
result->AsFunctionLiteral()->mark_as_iife();
} }
} }
Scanner::Location spread_pos; Scanner::Location spread_pos;
......
...@@ -341,6 +341,7 @@ class PreParserExpression { ...@@ -341,6 +341,7 @@ class PreParserExpression {
// More dummy implementations of things PreParser doesn't need to track: // More dummy implementations of things PreParser doesn't need to track:
void SetShouldEagerCompile() {} void SetShouldEagerCompile() {}
void mark_as_iife() {}
int position() const { return kNoSourcePosition; } int position() const { return kNoSourcePosition; }
void set_function_token_position(int position) {} void set_function_token_position(int position) {}
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "src/objects/module-inl.h" #include "src/objects/module-inl.h"
#include "src/runtime/runtime.h" #include "src/runtime/runtime.h"
#include "src/source-position-table.h" #include "src/source-position-table.h"
#include "test/cctest/cctest.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -116,6 +117,17 @@ BytecodeExpectationsPrinter::GetBytecodeArrayForScript( ...@@ -116,6 +117,17 @@ BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
return i::handle(js_function->shared()->GetBytecodeArray(), i_isolate()); return i::handle(js_function->shared()->GetBytecodeArray(), i_isolate());
} }
i::Handle<i::BytecodeArray>
BytecodeExpectationsPrinter::GetBytecodeArrayOfCallee(
const char* source_code) const {
i::Handle<i::Object> i_object =
v8::Utils::OpenHandle(*CompileRun(source_code));
i::Handle<i::JSFunction> js_function =
i::Handle<i::JSFunction>::cast(i_object);
CHECK(js_function->shared()->HasBytecodeArray());
return i::handle(js_function->shared()->GetBytecodeArray(), i_isolate());
}
void BytecodeExpectationsPrinter::PrintEscapedString( void BytecodeExpectationsPrinter::PrintEscapedString(
std::ostream& stream, const std::string& string) const { std::ostream& stream, const std::string& string) const {
for (char c : string) { for (char c : string) {
...@@ -372,11 +384,14 @@ void BytecodeExpectationsPrinter::PrintExpectation( ...@@ -372,11 +384,14 @@ void BytecodeExpectationsPrinter::PrintExpectation(
wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet) wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
: snippet; : snippet;
i::FLAG_enable_one_shot_optimization = oneshot_opt_;
i::Handle<i::BytecodeArray> bytecode_array; i::Handle<i::BytecodeArray> bytecode_array;
if (module_) { if (module_) {
CHECK(top_level_ && !wrap_); CHECK(top_level_ && !wrap_);
v8::Local<v8::Module> module = CompileModule(source_code.c_str()); v8::Local<v8::Module> module = CompileModule(source_code.c_str());
bytecode_array = GetBytecodeArrayForModule(module); bytecode_array = GetBytecodeArrayForModule(module);
} else if (print_callee_) {
bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str());
} else { } else {
v8::Local<v8::Script> script = CompileScript(source_code.c_str()); v8::Local<v8::Script> script = CompileScript(source_code.c_str());
if (top_level_) { if (top_level_) {
......
...@@ -32,6 +32,8 @@ class BytecodeExpectationsPrinter final { ...@@ -32,6 +32,8 @@ class BytecodeExpectationsPrinter final {
module_(false), module_(false),
wrap_(true), wrap_(true),
top_level_(false), top_level_(false),
print_callee_(false),
oneshot_opt_(true),
test_function_name_(kDefaultTopFunctionName) {} test_function_name_(kDefaultTopFunctionName) {}
void PrintExpectation(std::ostream& stream, // NOLINT void PrintExpectation(std::ostream& stream, // NOLINT
...@@ -46,6 +48,12 @@ class BytecodeExpectationsPrinter final { ...@@ -46,6 +48,12 @@ class BytecodeExpectationsPrinter final {
void set_top_level(bool top_level) { top_level_ = top_level; } void set_top_level(bool top_level) { top_level_ = top_level; }
bool top_level() const { return top_level_; } bool top_level() const { return top_level_; }
void set_print_callee(bool print_callee) { print_callee_ = print_callee; }
bool print_callee() { return print_callee_; }
void set_oneshot_opt(bool oneshot_opt) { oneshot_opt_ = oneshot_opt; }
bool oneshot_opt() { return oneshot_opt_; }
void set_test_function_name(const std::string& test_function_name) { void set_test_function_name(const std::string& test_function_name) {
test_function_name_ = test_function_name; test_function_name_ = test_function_name;
} }
...@@ -94,6 +102,8 @@ class BytecodeExpectationsPrinter final { ...@@ -94,6 +102,8 @@ class BytecodeExpectationsPrinter final {
v8::Local<v8::Module> module) const; v8::Local<v8::Module> module) const;
i::Handle<v8::internal::BytecodeArray> GetBytecodeArrayForScript( i::Handle<v8::internal::BytecodeArray> GetBytecodeArrayForScript(
v8::Local<v8::Script> script) const; v8::Local<v8::Script> script) const;
i::Handle<i::BytecodeArray> GetBytecodeArrayOfCallee(
const char* source_code) const;
i::Isolate* i_isolate() const { i::Isolate* i_isolate() const {
return reinterpret_cast<i::Isolate*>(isolate_); return reinterpret_cast<i::Isolate*>(isolate_);
...@@ -103,6 +113,8 @@ class BytecodeExpectationsPrinter final { ...@@ -103,6 +113,8 @@ class BytecodeExpectationsPrinter final {
bool module_; bool module_;
bool wrap_; bool wrap_;
bool top_level_; bool top_level_;
bool print_callee_;
bool oneshot_opt_;
std::string test_function_name_; std::string test_function_name_;
static const char* const kDefaultTopFunctionName; static const char* const kDefaultTopFunctionName;
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: no
top level: yes
print callee: yes
oneshot opt: no
---
snippet: "
(function() {
l = {};
l.a = 2;
l.b = l.a;
return arguments.callee;
})();
"
frame size: 3
parameter count: 1
bytecode array length: 42
bytecodes: [
B(CreateMappedArguments),
B(Star), R(0),
/* 16 E> */ B(StackCheck),
/* 29 S> */ B(CreateEmptyObjectLiteral),
/* 31 E> */ B(StaGlobal), U8(0), U8(0),
/* 45 S> */ B(LdaGlobal), U8(0), U8(2),
B(Star), R(1),
B(LdaSmi), I8(2),
/* 49 E> */ B(StaNamedProperty), R(1), U8(1), U8(4),
/* 62 S> */ B(LdaGlobal), U8(0), U8(2),
B(Star), R(1),
/* 68 E> */ B(LdaGlobal), U8(0), U8(2),
B(Star), R(2),
/* 70 E> */ B(LdaNamedProperty), R(2), U8(1), U8(6),
/* 66 E> */ B(StaNamedProperty), R(1), U8(2), U8(8),
/* 98 S> */ B(LdaNamedProperty), R(0), U8(3), U8(10),
/* 105 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["l"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["a"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["b"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["callee"],
]
handlers: [
]
---
snippet: "
(function() {
l = {
'a': 3.3,
'b': 4.4
};
if (l.a < 3) {
l.a = 3;
} else {
l.a = l.b;
}
return arguments.callee;
})();
"
frame size: 3
parameter count: 1
bytecode array length: 68
bytecodes: [
B(CreateMappedArguments),
B(Star), R(0),
/* 16 E> */ B(StackCheck),
/* 29 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(1),
B(Ldar), R(1),
/* 31 E> */ B(StaGlobal), U8(1), U8(1),
/* 93 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
/* 99 E> */ B(LdaNamedProperty), R(1), U8(2), U8(5),
B(Star), R(1),
B(LdaSmi), I8(3),
/* 101 E> */ B(TestLessThan), R(1), U8(7),
B(JumpIfFalse), U8(15),
/* 118 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
B(LdaSmi), I8(3),
/* 122 E> */ B(StaNamedProperty), R(1), U8(2), U8(8),
B(Jump), U8(20),
/* 154 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
/* 160 E> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(2),
/* 162 E> */ B(LdaNamedProperty), R(2), U8(3), U8(10),
/* 158 E> */ B(StaNamedProperty), R(1), U8(2), U8(8),
/* 200 S> */ B(LdaNamedProperty), R(0), U8(4), U8(12),
/* 207 S> */ B(Return),
]
constant pool: [
OBJECT_BOILERPLATE_DESCRIPTION_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["l"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["a"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["b"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["callee"],
]
handlers: [
]
...@@ -508,9 +508,9 @@ snippet: " ...@@ -508,9 +508,9 @@ snippet: "
import * as foo from \"bar\" import * as foo from \"bar\"
foo.f(foo, foo.x); foo.f(foo, foo.x);
" "
frame size: 8 frame size: 10
parameter count: 2 parameter count: 2
bytecode array length: 89 bytecode array length: 103
bytecodes: [ bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1), B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(LdaConstant), U8(1), B(LdaConstant), U8(1),
...@@ -537,11 +537,17 @@ bytecodes: [ ...@@ -537,11 +537,17 @@ bytecodes: [
/* 0 E> */ B(Throw), /* 0 E> */ B(Throw),
B(Ldar), R(4), B(Ldar), R(4),
/* 46 S> */ B(Return), /* 46 S> */ B(Return),
/* 31 S> */ B(LdaNamedProperty), R(1), U8(4), U8(0), /* 27 S> */ B(LdaConstant), U8(4),
B(Star), R(7),
B(Mov), R(1), R(6),
/* 31 E> */ B(InvokeIntrinsic), U8(Runtime::k_GetProperty), R(6), U8(2),
B(Star), R(4), B(Star), R(4),
/* 42 E> */ B(LdaNamedProperty), R(1), U8(5), U8(2), B(LdaConstant), U8(5),
B(Star), R(9),
B(Mov), R(1), R(8),
/* 42 E> */ B(InvokeIntrinsic), U8(Runtime::k_GetProperty), R(8), U8(2),
B(Star), R(7), B(Star), R(7),
/* 31 E> */ B(CallProperty2), R(4), R(1), R(1), R(7), U8(4), /* 31 E> */ B(CallProperty2), R(4), R(1), R(1), R(7), U8(0),
B(Star), R(2), B(Star), R(2),
/* 46 S> */ B(Return), /* 46 S> */ B(Return),
] ]
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: no
top level: yes
oneshot opt: no
---
snippet: "
l = {
'a': 1.1,
'b': 2.2
};
v = l['a'] + l['b'];
l['b'] = 7;
l['a'] = l['b'];
"
frame size: 3
parameter count: 1
bytecode array length: 74
bytecodes: [
/* 0 E> */ B(StackCheck),
/* 7 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(1),
B(Ldar), R(1),
/* 9 E> */ B(StaGlobal), U8(1), U8(1),
/* 64 S> */ B(LdaGlobal), U8(1), U8(4),
B(Star), R(1),
/* 69 E> */ B(LdaNamedProperty), R(1), U8(2), U8(6),
B(Star), R(1),
/* 77 E> */ B(LdaGlobal), U8(1), U8(4),
B(Star), R(2),
/* 78 E> */ B(LdaNamedProperty), R(2), U8(3), U8(8),
/* 75 E> */ B(Add), R(1), U8(3),
/* 66 E> */ B(StaGlobal), U8(4), U8(10),
/* 91 S> */ B(LdaGlobal), U8(1), U8(4),
B(Star), R(1),
B(LdaSmi), I8(7),
/* 98 E> */ B(StaNamedProperty), R(1), U8(3), U8(12),
/* 109 S> */ B(LdaGlobal), U8(1), U8(4),
B(Star), R(1),
/* 118 E> */ B(LdaGlobal), U8(1), U8(4),
B(Star), R(2),
/* 119 E> */ B(LdaNamedProperty), R(2), U8(3), U8(8),
B(Star), R(2),
/* 116 E> */ B(StaNamedProperty), R(1), U8(2), U8(14),
B(Mov), R(2), R(0),
B(Ldar), R(0),
/* 127 S> */ B(Return),
]
constant pool: [
OBJECT_BOILERPLATE_DESCRIPTION_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["l"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["a"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["b"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["v"],
]
handlers: [
]
---
snippet: "
l = {
'cc': 1.1,
'dd': 2.2
};
if (l['cc'] < 3) {
l['cc'] = 3;
} else {
l['dd'] = 3;
}
"
frame size: 3
parameter count: 1
bytecode array length: 70
bytecodes: [
/* 0 E> */ B(StackCheck),
/* 7 S> */ B(CreateObjectLiteral), U8(0), U8(0), U8(41), R(1),
B(Ldar), R(1),
/* 9 E> */ B(StaGlobal), U8(1), U8(1),
/* 65 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
/* 70 E> */ B(LdaNamedProperty), R(1), U8(2), U8(5),
B(Star), R(1),
B(LdaSmi), I8(3),
/* 77 E> */ B(TestLessThan), R(1), U8(7),
B(JumpIfFalse), U8(22),
/* 92 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
B(LdaSmi), I8(3),
B(Star), R(2),
/* 100 E> */ B(StaNamedProperty), R(1), U8(2), U8(8),
B(Mov), R(2), R(0),
B(Ldar), R(2),
B(Jump), U8(20),
/* 128 S> */ B(LdaGlobal), U8(1), U8(3),
B(Star), R(1),
B(LdaSmi), I8(3),
B(Star), R(2),
/* 136 E> */ B(StaNamedProperty), R(1), U8(3), U8(10),
B(Mov), R(2), R(0),
B(Ldar), R(2),
B(Ldar), R(0),
/* 156 S> */ B(Return),
]
constant pool: [
OBJECT_BOILERPLATE_DESCRIPTION_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["l"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["cc"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["dd"],
]
handlers: [
]
...@@ -42,6 +42,8 @@ class ProgramOptions final { ...@@ -42,6 +42,8 @@ class ProgramOptions final {
wrap_(true), wrap_(true),
module_(false), module_(false),
top_level_(false), top_level_(false),
print_callee_(false),
oneshot_opt_(true),
do_expressions_(false), do_expressions_(false),
async_iteration_(false), async_iteration_(false),
public_fields_(false), public_fields_(false),
...@@ -64,6 +66,8 @@ class ProgramOptions final { ...@@ -64,6 +66,8 @@ class ProgramOptions final {
bool wrap() const { return wrap_; } bool wrap() const { return wrap_; }
bool module() const { return module_; } bool module() const { return module_; }
bool top_level() const { return top_level_; } bool top_level() const { return top_level_; }
bool print_callee() const { return print_callee_; }
bool oneshot_opt() const { return oneshot_opt_; }
bool do_expressions() const { return do_expressions_; } bool do_expressions() const { return do_expressions_; }
bool async_iteration() const { return async_iteration_; } bool async_iteration() const { return async_iteration_; }
bool public_fields() const { return public_fields_; } bool public_fields() const { return public_fields_; }
...@@ -84,6 +88,8 @@ class ProgramOptions final { ...@@ -84,6 +88,8 @@ class ProgramOptions final {
bool wrap_; bool wrap_;
bool module_; bool module_;
bool top_level_; bool top_level_;
bool print_callee_;
bool oneshot_opt_;
bool do_expressions_; bool do_expressions_;
bool async_iteration_; bool async_iteration_;
bool public_fields_; bool public_fields_;
...@@ -174,6 +180,10 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) { ...@@ -174,6 +180,10 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
options.module_ = true; options.module_ = true;
} else if (strcmp(argv[i], "--top-level") == 0) { } else if (strcmp(argv[i], "--top-level") == 0) {
options.top_level_ = true; options.top_level_ = true;
} else if (strcmp(argv[i], "--print-callee") == 0) {
options.print_callee_ = true;
} else if (strcmp(argv[i], "--disable-oneshot-opt") == 0) {
options.oneshot_opt_ = false;
} else if (strcmp(argv[i], "--do-expressions") == 0) { } else if (strcmp(argv[i], "--do-expressions") == 0) {
options.do_expressions_ = true; options.do_expressions_ = true;
} else if (strcmp(argv[i], "--async-iteration") == 0) { } else if (strcmp(argv[i], "--async-iteration") == 0) {
...@@ -269,6 +279,8 @@ bool ProgramOptions::Validate() const { ...@@ -269,6 +279,8 @@ bool ProgramOptions::Validate() const {
void ProgramOptions::UpdateFromHeader(std::istream& stream) { void ProgramOptions::UpdateFromHeader(std::istream& stream) {
std::string line; std::string line;
const char* kPrintCallee = "print callee: ";
const char* kOneshotOpt = "oneshot opt: ";
// Skip to the beginning of the options header // Skip to the beginning of the options header
while (std::getline(stream, line)) { while (std::getline(stream, line)) {
...@@ -284,6 +296,10 @@ void ProgramOptions::UpdateFromHeader(std::istream& stream) { ...@@ -284,6 +296,10 @@ void ProgramOptions::UpdateFromHeader(std::istream& stream) {
test_function_name_ = line.c_str() + 20; test_function_name_ = line.c_str() + 20;
} else if (line.compare(0, 11, "top level: ") == 0) { } else if (line.compare(0, 11, "top level: ") == 0) {
top_level_ = ParseBoolean(line.c_str() + 11); top_level_ = ParseBoolean(line.c_str() + 11);
} else if (line.compare(0, strlen(kPrintCallee), kPrintCallee) == 0) {
print_callee_ = ParseBoolean(line.c_str() + strlen(kPrintCallee));
} else if (line.compare(0, strlen(kOneshotOpt), kOneshotOpt) == 0) {
oneshot_opt_ = ParseBoolean(line.c_str() + strlen(kOneshotOpt));
} else if (line.compare(0, 16, "do expressions: ") == 0) { } else if (line.compare(0, 16, "do expressions: ") == 0) {
do_expressions_ = ParseBoolean(line.c_str() + 16); do_expressions_ = ParseBoolean(line.c_str() + 16);
} else if (line.compare(0, 17, "async iteration: ") == 0) { } else if (line.compare(0, 17, "async iteration: ") == 0) {
...@@ -315,6 +331,8 @@ void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT ...@@ -315,6 +331,8 @@ void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT
if (module_) stream << "\nmodule: yes"; if (module_) stream << "\nmodule: yes";
if (top_level_) stream << "\ntop level: yes"; if (top_level_) stream << "\ntop level: yes";
if (print_callee_) stream << "\nprint callee: yes";
if (!oneshot_opt_) stream << "\noneshot opt: no";
if (do_expressions_) stream << "\ndo expressions: yes"; if (do_expressions_) stream << "\ndo expressions: yes";
if (async_iteration_) stream << "\nasync iteration: yes"; if (async_iteration_) stream << "\nasync iteration: yes";
if (public_fields_) stream << "\npublic fields: yes"; if (public_fields_) stream << "\npublic fields: yes";
...@@ -364,6 +382,10 @@ bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT ...@@ -364,6 +382,10 @@ bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT
} }
if (!found_begin_snippet) continue; if (!found_begin_snippet) continue;
if (line == "\"") return true; if (line == "\"") return true;
if (line.size() == 0) {
string_out->append("\n"); // consume empty line
continue;
}
CHECK_GE(line.size(), 2u); // We should have the indent CHECK_GE(line.size(), 2u); // We should have the indent
string_out->append(line.begin() + 2, line.end()); string_out->append(line.begin() + 2, line.end());
*string_out += '\n'; *string_out += '\n';
...@@ -418,6 +440,8 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT ...@@ -418,6 +440,8 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
printer.set_wrap(options.wrap()); printer.set_wrap(options.wrap());
printer.set_module(options.module()); printer.set_module(options.module());
printer.set_top_level(options.top_level()); printer.set_top_level(options.top_level());
printer.set_print_callee(options.print_callee());
printer.set_oneshot_opt(options.oneshot_opt());
if (!options.test_function_name().empty()) { if (!options.test_function_name().empty()) {
printer.set_test_function_name(options.test_function_name()); printer.set_test_function_name(options.test_function_name());
} }
...@@ -478,6 +502,9 @@ void PrintUsage(const char* exec_path) { ...@@ -478,6 +502,9 @@ void PrintUsage(const char* exec_path) {
" --stdin Read from standard input instead of file.\n" " --stdin Read from standard input instead of file.\n"
" --rebaseline Rebaseline input snippet file.\n" " --rebaseline Rebaseline input snippet file.\n"
" --no-wrap Do not wrap the snippet in a function.\n" " --no-wrap Do not wrap the snippet in a function.\n"
" --disable-oneshot-opt Disable Oneshot Optimization.\n"
" --print-callee Print bytecode of callee, function should "
"return arguments.callee.\n"
" --module Compile as JavaScript module.\n" " --module Compile as JavaScript module.\n"
" --test-function-name=foo " " --test-function-name=foo "
"Specify the name of the test function.\n" "Specify the name of the test function.\n"
......
...@@ -129,6 +129,26 @@ std::string BuildActual(const BytecodeExpectationsPrinter& printer, ...@@ -129,6 +129,26 @@ std::string BuildActual(const BytecodeExpectationsPrinter& printer,
return actual_stream.str(); return actual_stream.str();
} }
// inplace left trim
static inline void ltrim(std::string& str) {
str.erase(str.begin(),
std::find_if(str.begin(), str.end(),
[](unsigned char ch) { return !std::isspace(ch); }));
}
// inplace right trim
static inline void rtrim(std::string& str) {
str.erase(std::find_if(str.begin(), str.end(),
[](unsigned char ch) { return !std::isspace(ch); }),
str.end());
}
static inline std::string trim(std::string& str) {
ltrim(str);
rtrim(str);
return str;
}
bool CompareTexts(const std::string& generated, const std::string& expected) { bool CompareTexts(const std::string& generated, const std::string& expected) {
std::istringstream generated_stream(generated); std::istringstream generated_stream(generated);
std::istringstream expected_stream(expected); std::istringstream expected_stream(expected);
...@@ -157,7 +177,7 @@ bool CompareTexts(const std::string& generated, const std::string& expected) { ...@@ -157,7 +177,7 @@ bool CompareTexts(const std::string& generated, const std::string& expected) {
return false; return false;
} }
if (generated_line != expected_line) { if (trim(generated_line) != trim(expected_line)) {
std::cerr << "Inputs differ at line " << line_number << "\n"; std::cerr << "Inputs differ at line " << line_number << "\n";
std::cerr << " Generated: '" << generated_line << "'\n"; std::cerr << " Generated: '" << generated_line << "'\n";
std::cerr << " Expected: '" << expected_line << "'\n"; std::cerr << " Expected: '" << expected_line << "'\n";
...@@ -411,6 +431,219 @@ TEST(PropertyLoads) { ...@@ -411,6 +431,219 @@ TEST(PropertyLoads) {
LoadGolden("PropertyLoads.golden"))); LoadGolden("PropertyLoads.golden")));
} }
TEST(PropertyLoadStoreOneShot) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
printer.set_wrap(false);
printer.set_top_level(true);
const char* snippets[] = {
R"(
l = {
'a': 1,
'b': 2
};
v = l['a'] + l['b'];
l['b'] = 7;
l['a'] = l['b'];
)",
R"(
l = {
'a': 1.1,
'b': 2.2
};
for (i = 0; i < 5; ++i) {
l['a'] = l['a'] + l['b'];
l['b'] = l['a'] + l['b'];
}
)",
R"(
l = {
'a': 1.1,
'b': 2.2
};
while (s > 0) {
l['a'] = l['a'] - l['b'];
l['b'] = l['b'] - l['a'];
}
)",
R"(
l = {
'a': 1.1,
'b': 2.2
};
s = 10;
do {
l['a'] = l['b'] - l['a'];
} while (s < 10);
)",
R"(
l = {
'c': 1.1,
'd': 2.2
};
if (l['c'] < 3) {
l['c'] = 3;
} else {
l['d'] = 3;
}
)",
};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PropertyLoadStoreOneShot.golden")));
}
TEST(PropertyLoadStoreWithoutOneShot) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
printer.set_wrap(false);
printer.set_top_level(true);
printer.set_oneshot_opt(false);
const char* snippets[] = {
R"(
l = {
'aa': 1.1,
'bb': 2.2
};
v = l['aa'] + l['bb'];
l['bb'] = 7;
l['aa'] = l['bb'];
)",
R"(
l = {
'cc': 3.1,
'dd': 4.2
};
if (l['cc'] < 3) {
l['cc'] = 3;
} else {
l['dd'] = 3;
}
)",
};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("PropertyLoadStoreWithoutOneShot.golden")));
}
TEST(IIFEWithOneshotOpt) {
InitializedIgnitionHandleScope scope;
v8::Isolate* isolate = CcTest::isolate();
BytecodeExpectationsPrinter printer(isolate);
printer.set_wrap(false);
printer.set_top_level(true);
printer.set_print_callee(true);
const char* snippets[] = {
// No feedback vectors for top-level loads/store named property in an IIFE
R"(
(function() {
l = {};
l.aa = 2;
l.bb = l.aa;
return arguments.callee;
})();
)",
// Normal load/store within loops of an IIFE
R"(
(function() {
l = {};
for (i = 0; i < 5; ++i) {
l.aa = 2;
l.bb = l.aa;
}
return arguments.callee;
})();
)",
R"(
(function() {
l = {};
c = 4;
while(c > 4) {
l.aa = 2;
l.bb = l.aa;
c--;
}
return arguments.callee;
})();
)",
R"(
(function() {
l = {};
c = 4;
do {
l.aa = 2;
l.bb = l.aa;
c--;
} while(c > 4)
return arguments.callee;
})();
)",
// No feedback vectors for loads/stores in conditionals
R"(
(function() {
l = {
'aa': 3.3,
'bb': 4.4
};
if (l.aa < 3) {
l.aa = 3;
} else {
l.aa = l.bb;
}
return arguments.callee;
})();
)",
};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("IIFEWithOneshotOpt.golden")));
}
TEST(IIFEWithoutOneshotOpt) {
InitializedIgnitionHandleScope scope;
v8::Isolate* isolate = CcTest::isolate();
BytecodeExpectationsPrinter printer(isolate);
printer.set_wrap(false);
printer.set_top_level(true);
printer.set_print_callee(true);
printer.set_oneshot_opt(false);
const char* snippets[] = {
R"(
(function() {
l = {};
l.a = 2;
l.b = l.a;
return arguments.callee;
})();
)",
R"(
(function() {
l = {
'a': 4.3,
'b': 3.4
};
if (l.a < 3) {
l.a = 3;
} else {
l.a = l.b;
}
return arguments.callee;
})();
)",
};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("IIFEWithoutOneshotOpt.golden")));
}
TEST(PropertyStores) { TEST(PropertyStores) {
InitializedIgnitionHandleScope scope; InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate()); BytecodeExpectationsPrinter printer(CcTest::isolate());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment