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 {
bool is_anonymous_expression() const {
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; }
LanguageMode language_mode() const;
......@@ -2333,7 +2339,7 @@ class FunctionLiteral final : public Expression {
kHasDuplicateParameters) |
DontOptimizeReasonField::encode(BailoutReason::kNoReason) |
RequiresInstanceFieldsInitializer::encode(false) |
HasBracesField::encode(has_braces);
HasBracesField::encode(has_braces) | IIFEBit::encode(false);
if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile();
DCHECK_EQ(body == nullptr, expected_property_count < 0);
}
......@@ -2348,6 +2354,7 @@ class FunctionLiteral final : public Expression {
: public BitField<bool, DontOptimizeReasonField::kNext, 1> {};
class HasBracesField
: public BitField<bool, RequiresInstanceFieldsInitializer::kNext, 1> {};
class IIFEBit : public BitField<bool, HasBracesField::kNext, 1> {};
int expected_property_count_;
int parameter_count_;
......
......@@ -277,6 +277,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(IsTypedArray) \
/* Loads */ \
V(LoadLookupSlotForCall) \
V(GetProperty) \
/* Arrays */ \
V(ArraySpeciesConstructor) \
V(EstimateNumberOfElements) \
......
......@@ -315,6 +315,11 @@ DEFINE_BOOL(optimize_for_size, false,
"Enables optimizations which favor memory size over execution "
"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)
// Flags for data representation optimizations
......
......@@ -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) {
size_t class_boilerplate_entry =
builder()->AllocateDeferredConstantPoolEntry();
......@@ -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) {
DCHECK(expr->target()->IsValidReferenceExpression() ||
(expr->op() == Token::INIT && expr->target()->IsVariableProxy() &&
......@@ -2837,8 +2893,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break;
}
case NAMED_PROPERTY: {
FeedbackSlot slot = GetCachedLoadICSlot(property->obj(), name);
builder()->LoadNamedProperty(object, name, feedback_index(slot));
BuildLoadNamedProperty(property, object, name);
break;
}
case KEYED_PROPERTY: {
......@@ -2888,17 +2943,7 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
break;
}
case NAMED_PROPERTY: {
FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), 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);
}
BuildStoreNamedProperty(property, object, name);
break;
}
case KEYED_PROPERTY: {
......@@ -3364,11 +3409,9 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
UNREACHABLE();
case NAMED_PROPERTY: {
builder()->SetExpressionPosition(property);
builder()->LoadNamedProperty(
obj, property->key()->AsLiteral()->AsRawPropertyName(),
feedback_index(GetCachedLoadICSlot(
property->obj(),
property->key()->AsLiteral()->AsRawPropertyName())));
const AstRawString* name =
property->key()->AsLiteral()->AsRawPropertyName();
BuildLoadNamedProperty(property, obj, name);
break;
}
case KEYED_PROPERTY: {
......
......@@ -120,6 +120,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitPropertyLoadForRegister(Register obj, Property* expr,
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,
TypeofMode typeof_mode = NOT_INSIDE_TYPEOF);
void BuildVariableLoadForAccumulatorValue(
......@@ -277,6 +282,11 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
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) {
return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean
: ToBooleanMode::kConvertToBoolean;
......
......@@ -224,6 +224,12 @@ Node* IntrinsicsGenerator::HasProperty(
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(
const InterpreterAssembler::RegListNodePair& args, Node* context) {
return IntrinsicAsStubCall(
......
......@@ -26,6 +26,7 @@ namespace interpreter {
V(CreateIterResultObject, create_iter_result_object, 2) \
V(CreateAsyncFromSyncIterator, create_async_from_sync_iterator, 1) \
V(HasProperty, has_property, 2) \
V(GetProperty, get_property, 2) \
V(IsArray, is_array, 1) \
V(IsJSProxy, is_js_proxy, 1) \
V(IsJSReceiver, is_js_receiver, 1) \
......
......@@ -3378,6 +3378,7 @@ ParserBase<Impl>::ParseLeftHandSideExpression(bool* ok) {
// function literal eagerly, we can also compile it eagerly.
if (result->IsFunctionLiteral()) {
result->AsFunctionLiteral()->SetShouldEagerCompile();
result->AsFunctionLiteral()->mark_as_iife();
}
}
Scanner::Location spread_pos;
......
......@@ -341,6 +341,7 @@ class PreParserExpression {
// More dummy implementations of things PreParser doesn't need to track:
void SetShouldEagerCompile() {}
void mark_as_iife() {}
int position() const { return kNoSourcePosition; }
void set_function_token_position(int position) {}
......
......@@ -21,6 +21,7 @@
#include "src/objects/module-inl.h"
#include "src/runtime/runtime.h"
#include "src/source-position-table.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
......@@ -116,6 +117,17 @@ BytecodeExpectationsPrinter::GetBytecodeArrayForScript(
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(
std::ostream& stream, const std::string& string) const {
for (char c : string) {
......@@ -372,11 +384,14 @@ void BytecodeExpectationsPrinter::PrintExpectation(
wrap_ ? WrapCodeInFunction(test_function_name_.c_str(), snippet)
: snippet;
i::FLAG_enable_one_shot_optimization = oneshot_opt_;
i::Handle<i::BytecodeArray> bytecode_array;
if (module_) {
CHECK(top_level_ && !wrap_);
v8::Local<v8::Module> module = CompileModule(source_code.c_str());
bytecode_array = GetBytecodeArrayForModule(module);
} else if (print_callee_) {
bytecode_array = GetBytecodeArrayOfCallee(source_code.c_str());
} else {
v8::Local<v8::Script> script = CompileScript(source_code.c_str());
if (top_level_) {
......
......@@ -32,6 +32,8 @@ class BytecodeExpectationsPrinter final {
module_(false),
wrap_(true),
top_level_(false),
print_callee_(false),
oneshot_opt_(true),
test_function_name_(kDefaultTopFunctionName) {}
void PrintExpectation(std::ostream& stream, // NOLINT
......@@ -46,6 +48,12 @@ class BytecodeExpectationsPrinter final {
void set_top_level(bool top_level) { top_level_ = 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) {
test_function_name_ = test_function_name;
}
......@@ -94,6 +102,8 @@ class BytecodeExpectationsPrinter final {
v8::Local<v8::Module> module) const;
i::Handle<v8::internal::BytecodeArray> GetBytecodeArrayForScript(
v8::Local<v8::Script> script) const;
i::Handle<i::BytecodeArray> GetBytecodeArrayOfCallee(
const char* source_code) const;
i::Isolate* i_isolate() const {
return reinterpret_cast<i::Isolate*>(isolate_);
......@@ -103,6 +113,8 @@ class BytecodeExpectationsPrinter final {
bool module_;
bool wrap_;
bool top_level_;
bool print_callee_;
bool oneshot_opt_;
std::string test_function_name_;
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: "
import * as foo from \"bar\"
foo.f(foo, foo.x);
"
frame size: 8
frame size: 10
parameter count: 2
bytecode array length: 89
bytecode array length: 103
bytecodes: [
B(SwitchOnGeneratorState), R(0), U8(0), U8(1),
B(LdaConstant), U8(1),
......@@ -537,11 +537,17 @@ bytecodes: [
/* 0 E> */ B(Throw),
B(Ldar), R(4),
/* 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),
/* 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),
/* 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),
/* 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 {
wrap_(true),
module_(false),
top_level_(false),
print_callee_(false),
oneshot_opt_(true),
do_expressions_(false),
async_iteration_(false),
public_fields_(false),
......@@ -64,6 +66,8 @@ class ProgramOptions final {
bool wrap() const { return wrap_; }
bool module() const { return module_; }
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 async_iteration() const { return async_iteration_; }
bool public_fields() const { return public_fields_; }
......@@ -84,6 +88,8 @@ class ProgramOptions final {
bool wrap_;
bool module_;
bool top_level_;
bool print_callee_;
bool oneshot_opt_;
bool do_expressions_;
bool async_iteration_;
bool public_fields_;
......@@ -174,6 +180,10 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
options.module_ = true;
} else if (strcmp(argv[i], "--top-level") == 0) {
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) {
options.do_expressions_ = true;
} else if (strcmp(argv[i], "--async-iteration") == 0) {
......@@ -269,6 +279,8 @@ bool ProgramOptions::Validate() const {
void ProgramOptions::UpdateFromHeader(std::istream& stream) {
std::string line;
const char* kPrintCallee = "print callee: ";
const char* kOneshotOpt = "oneshot opt: ";
// Skip to the beginning of the options header
while (std::getline(stream, line)) {
......@@ -284,6 +296,10 @@ void ProgramOptions::UpdateFromHeader(std::istream& stream) {
test_function_name_ = line.c_str() + 20;
} else if (line.compare(0, 11, "top level: ") == 0) {
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) {
do_expressions_ = ParseBoolean(line.c_str() + 16);
} else if (line.compare(0, 17, "async iteration: ") == 0) {
......@@ -315,6 +331,8 @@ void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT
if (module_) stream << "\nmodule: 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 (async_iteration_) stream << "\nasync iteration: yes";
if (public_fields_) stream << "\npublic fields: yes";
......@@ -364,6 +382,10 @@ bool ReadNextSnippet(std::istream& stream, std::string* string_out) { // NOLINT
}
if (!found_begin_snippet) continue;
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
string_out->append(line.begin() + 2, line.end());
*string_out += '\n';
......@@ -418,6 +440,8 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
printer.set_wrap(options.wrap());
printer.set_module(options.module());
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()) {
printer.set_test_function_name(options.test_function_name());
}
......@@ -478,6 +502,9 @@ void PrintUsage(const char* exec_path) {
" --stdin Read from standard input instead of file.\n"
" --rebaseline Rebaseline input snippet file.\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"
" --test-function-name=foo "
"Specify the name of the test function.\n"
......
......@@ -129,6 +129,26 @@ std::string BuildActual(const BytecodeExpectationsPrinter& printer,
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) {
std::istringstream generated_stream(generated);
std::istringstream expected_stream(expected);
......@@ -157,7 +177,7 @@ bool CompareTexts(const std::string& generated, const std::string& expected) {
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 << " Generated: '" << generated_line << "'\n";
std::cerr << " Expected: '" << expected_line << "'\n";
......@@ -411,6 +431,219 @@ TEST(PropertyLoads) {
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) {
InitializedIgnitionHandleScope scope;
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