v8/test/unittests/interpreter/bytecode-array-builder-unittest.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

857 lines
30 KiB
C++
Raw Normal View History

// 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.
#include "src/v8.h"
#include "src/ast/scopes.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/objects-inl.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
public:
BytecodeArrayBuilderTest() {}
~BytecodeArrayBuilderTest() override {}
};
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 131);
Factory* factory = isolate()->factory();
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
isolate()->heap()->HashSeed());
DeclarationScope scope(zone(), &ast_factory);
CHECK_EQ(builder.locals_count(), 131);
CHECK_EQ(builder.fixed_register_count(), 131);
Register reg(0);
Register other(reg.index() + 1);
Register wide(128);
RegisterList reg_list(0, 10);
RegisterList empty, single(0, 1), pair(0, 2), triple(0, 3);
[runtime] Optimize and unify rest parameters. Replace the somewhat awkward RestParamAccessStub, which would always call into the runtime anyway with a proper FastNewRestParameterStub, which is basically based on the code that was already there for strict arguments object materialization. But for rest parameters we could optimize even further (leading to 8-10x improvements for functions with rest parameters), by fixing the internal formal parameter count: Every SharedFunctionInfo has a formal_parameter_count field, which specifies the number of formal parameters, and is used to decide whether we need to create an arguments adaptor frame when calling a function (i.e. if there's a mismatch between the actual and expected parameters). Previously the formal_parameter_count included the rest parameter, which was sort of unfortunate, as that meant that calling a function with only the non-rest parameters still required an arguments adaptor (plus some other oddities). Now with this CL we fix, so that we do no longer include the rest parameter in that count. Thereby checking for rest parameters is very efficient, as we only need to check whether there is an arguments adaptor frame, and if not create an empty array, otherwise check whether the arguments adaptor frame has more parameters than specified by the formal_parameter_count. The FastNewRestParameterStub is written in a way that it can be directly used by Ignition as well, and with some tweaks to the TurboFan backends and the CodeStubAssembler, we should be able to rewrite it as TurboFanCodeStub in the near future. Drive-by-fix: Refactor and unify the CreateArgumentsType which was different in TurboFan and Ignition; now we have a single enum class which is used in both TurboFan and Ignition. R=jarin@chromium.org, rmcilroy@chromium.org TBR=rossberg@chromium.org BUG=v8:2159 LOG=n Review URL: https://codereview.chromium.org/1676883002 Cr-Commit-Position: refs/heads/master@{#33809}
2016-02-08 10:08:21 +00:00
// Emit argument creation operations.
builder.CreateArguments(CreateArgumentsType::kMappedArguments)
.CreateArguments(CreateArgumentsType::kUnmappedArguments)
[runtime] Optimize and unify rest parameters. Replace the somewhat awkward RestParamAccessStub, which would always call into the runtime anyway with a proper FastNewRestParameterStub, which is basically based on the code that was already there for strict arguments object materialization. But for rest parameters we could optimize even further (leading to 8-10x improvements for functions with rest parameters), by fixing the internal formal parameter count: Every SharedFunctionInfo has a formal_parameter_count field, which specifies the number of formal parameters, and is used to decide whether we need to create an arguments adaptor frame when calling a function (i.e. if there's a mismatch between the actual and expected parameters). Previously the formal_parameter_count included the rest parameter, which was sort of unfortunate, as that meant that calling a function with only the non-rest parameters still required an arguments adaptor (plus some other oddities). Now with this CL we fix, so that we do no longer include the rest parameter in that count. Thereby checking for rest parameters is very efficient, as we only need to check whether there is an arguments adaptor frame, and if not create an empty array, otherwise check whether the arguments adaptor frame has more parameters than specified by the formal_parameter_count. The FastNewRestParameterStub is written in a way that it can be directly used by Ignition as well, and with some tweaks to the TurboFan backends and the CodeStubAssembler, we should be able to rewrite it as TurboFanCodeStub in the near future. Drive-by-fix: Refactor and unify the CreateArgumentsType which was different in TurboFan and Ignition; now we have a single enum class which is used in both TurboFan and Ignition. R=jarin@chromium.org, rmcilroy@chromium.org TBR=rossberg@chromium.org BUG=v8:2159 LOG=n Review URL: https://codereview.chromium.org/1676883002 Cr-Commit-Position: refs/heads/master@{#33809}
2016-02-08 10:08:21 +00:00
.CreateArguments(CreateArgumentsType::kRestParameter);
// Emit constant loads.
builder.LoadLiteral(Smi::kZero)
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(8))
.CompareOperation(Token::Value::EQ, reg,
1) // Prevent peephole optimization
// LdaSmi, Star -> LdrSmi.
.StoreAccumulatorInRegister(reg)
.LoadLiteral(Smi::FromInt(10000000))
.StoreAccumulatorInRegister(reg)
.LoadLiteral(
ast_factory.NewString(ast_factory.GetOneByteString("A constant")))
.StoreAccumulatorInRegister(reg)
.LoadUndefined()
.StoreAccumulatorInRegister(reg)
.LoadNull()
.StoreAccumulatorInRegister(reg)
.LoadTheHole()
.StoreAccumulatorInRegister(reg)
.LoadTrue()
.StoreAccumulatorInRegister(reg)
.LoadFalse()
.StoreAccumulatorInRegister(wide);
// Emit Ldar and Star taking care to foil the register optimizer.
builder.StackCheck(0)
.LoadAccumulatorWithRegister(other)
.BinaryOperation(Token::ADD, reg, 1)
.StoreAccumulatorInRegister(reg)
.LoadNull();
// Emit register-register transfer.
builder.MoveRegister(reg, other);
builder.MoveRegister(reg, wide);
// Emit global load / store operations.
const AstRawString* name = ast_factory.GetOneByteString("var_name");
builder.LoadGlobal(name, 1, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadGlobal(name, 1, TypeofMode::INSIDE_TYPEOF)
.StoreGlobal(name, 1, LanguageMode::SLOPPY)
.StoreGlobal(name, 1, LanguageMode::STRICT);
// Emit context operations.
builder.PushContext(reg)
.PopContext(reg)
Reland of Thread maybe-assigned through the bytecodes. (patchset #1 id:1 of https://codereview.chromium.org/2680923003/ ) Reason for revert: False alarm, bot hiccup Original issue's description: > Revert of Thread maybe-assigned through the bytecodes. (patchset #5 id:80001 of https://codereview.chromium.org/2655733003/ ) > > Reason for revert: > needed for properly reverting https://chromium.googlesource.com/v8/v8/+/f3ae5ccf57690d8c2d87c4fe1d10b103ad6a4ab3 > > Original issue's description: > > Thread maybe-assigned through the bytecodes. > > > > This introduces LoadImmutableContextSlot and LoadImmutableCurrentContextSlot > > bytecodes, which are emitted when reading from never-assigned context slot. > > > > There is a subtlety here: the slot are not immutable, the meaning is > > actually undefined-or-hole-or-immutable. > > > > Review-Url: https://codereview.chromium.org/2655733003 > > Cr-Commit-Position: refs/heads/master@{#43000} > > Committed: https://chromium.googlesource.com/v8/v8/+/17c2dd388697da626224c371c8181d20d2016d82 > > TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > > Review-Url: https://codereview.chromium.org/2680923003 > Cr-Commit-Position: refs/heads/master@{#43011} > Committed: https://chromium.googlesource.com/v8/v8/+/ece4e54a31ac76ee9c450270d65270562db716e1 TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2679953003 Cr-Commit-Position: refs/heads/master@{#43012}
2017-02-07 20:42:03 +00:00
.LoadContextSlot(reg, 1, 0, BytecodeArrayBuilder::kMutableSlot)
.StoreContextSlot(reg, 1, 0)
.LoadContextSlot(reg, 2, 0, BytecodeArrayBuilder::kImmutableSlot)
.StoreContextSlot(reg, 3, 0);
// Emit context operations which operate on the local context.
Reland of Thread maybe-assigned through the bytecodes. (patchset #1 id:1 of https://codereview.chromium.org/2680923003/ ) Reason for revert: False alarm, bot hiccup Original issue's description: > Revert of Thread maybe-assigned through the bytecodes. (patchset #5 id:80001 of https://codereview.chromium.org/2655733003/ ) > > Reason for revert: > needed for properly reverting https://chromium.googlesource.com/v8/v8/+/f3ae5ccf57690d8c2d87c4fe1d10b103ad6a4ab3 > > Original issue's description: > > Thread maybe-assigned through the bytecodes. > > > > This introduces LoadImmutableContextSlot and LoadImmutableCurrentContextSlot > > bytecodes, which are emitted when reading from never-assigned context slot. > > > > There is a subtlety here: the slot are not immutable, the meaning is > > actually undefined-or-hole-or-immutable. > > > > Review-Url: https://codereview.chromium.org/2655733003 > > Cr-Commit-Position: refs/heads/master@{#43000} > > Committed: https://chromium.googlesource.com/v8/v8/+/17c2dd388697da626224c371c8181d20d2016d82 > > TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > > Review-Url: https://codereview.chromium.org/2680923003 > Cr-Commit-Position: refs/heads/master@{#43011} > Committed: https://chromium.googlesource.com/v8/v8/+/ece4e54a31ac76ee9c450270d65270562db716e1 TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2679953003 Cr-Commit-Position: refs/heads/master@{#43012}
2017-02-07 20:42:03 +00:00
builder
.LoadContextSlot(Register::current_context(), 1, 0,
BytecodeArrayBuilder::kMutableSlot)
.StoreContextSlot(Register::current_context(), 1, 0)
.LoadContextSlot(Register::current_context(), 2, 0,
BytecodeArrayBuilder::kImmutableSlot)
.StoreContextSlot(Register::current_context(), 3, 0);
// Emit load / store property operations.
builder.LoadNamedProperty(reg, name, 0)
.LoadKeyedProperty(reg, 0)
.StoreNamedProperty(reg, name, 0, LanguageMode::SLOPPY)
.StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY)
.StoreNamedProperty(reg, name, 0, LanguageMode::STRICT)
.StoreKeyedProperty(reg, reg, 0, LanguageMode::STRICT)
.StoreNamedOwnProperty(reg, name, 0);
// Emit load / store lookup slots.
builder.LoadLookupSlot(name, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadLookupSlot(name, TypeofMode::INSIDE_TYPEOF)
.StoreLookupSlot(name, LanguageMode::SLOPPY, LookupHoistingMode::kNormal)
.StoreLookupSlot(name, LanguageMode::SLOPPY,
LookupHoistingMode::kLegacySloppy)
.StoreLookupSlot(name, LanguageMode::STRICT, LookupHoistingMode::kNormal);
// Emit load / store lookup slots with context fast paths.
builder.LoadLookupContextSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
.LoadLookupContextSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
// Emit load / store lookup slots with global fast paths.
builder.LoadLookupGlobalSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
.LoadLookupGlobalSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
// Emit closure operations.
builder.CreateClosure(0, 1, NOT_TENURED);
// Emit create context operation.
builder.CreateBlockContext(&scope);
builder.CreateCatchContext(reg, name, &scope);
builder.CreateFunctionContext(1);
builder.CreateEvalContext(1);
builder.CreateWithContext(reg, &scope);
// Emit literal creation operations.
builder.CreateRegExpLiteral(ast_factory.GetOneByteString("a"), 0, 0);
builder.CreateArrayLiteral(0, 0, 0);
builder.CreateObjectLiteral(0, 0, 0, reg);
[es2015] Introduce dedicated GetTemplateObject bytecode. Tagged templates were previously desugared during parsing using some combination of runtime support written in JavaScript and C++, which prevented some optimizations from happening, namely the constant folding of the template object in TurboFan optimized code. This CL adds a new bytecode GetTemplateObject (with a corresponding GetTemplateObject AST node), which represents the abstract operation in the ES6 specification and allows TurboFan to simply constant-fold template objects at compile time (which is explicitly supported by the specification). This also pays down some technical debt by removing the template.js runtime support and therefore should reduce the size of the native context (snapshot) a bit. With this change in-place the ES6 version microbenchmark in the referenced tracking bug is now faster than the transpiled Babel code, it goes from templateStringTagES5: 4552 ms. templateStringTagES6: 14185 ms. templateStringTagBabel: 7626 ms. to templateStringTagES5: 4515 ms. templateStringTagES6: 7491 ms. templateStringTagBabel: 7639 ms. which corresponds to a solid 45% reduction in execution time. With some further optimizations the ES6 version should be able to outperform the ES5 version. This micro-benchmark should be fairly representative of the six-speed-templatestringtag-es6 benchmark, and as such that benchmark should also improve by around 50%. Bug: v8:6819,v8:6820 Tbr: mlippautz@chromium.org Change-Id: I821085e3794717fc7f52b5c306fcb93ba03345dc Reviewed-on: https://chromium-review.googlesource.com/677462 Reviewed-by: Mythri Alle <mythria@chromium.org> Reviewed-by: Caitlin Potter <caitp@igalia.com> Reviewed-by: Adam Klein <adamk@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#48126}
2017-09-22 09:57:29 +00:00
// Emit tagged template operations.
builder.GetTemplateObject(0);
// Call operations.
builder.CallAnyReceiver(reg, reg_list, 1)
.CallProperty(reg, reg_list, 1)
.CallProperty(reg, single, 1)
.CallProperty(reg, pair, 1)
.CallProperty(reg, triple, 1)
.CallUndefinedReceiver(reg, reg_list, 1)
.CallUndefinedReceiver(reg, empty, 1)
.CallUndefinedReceiver(reg, single, 1)
.CallUndefinedReceiver(reg, pair, 1)
.CallRuntime(Runtime::kIsArray, reg)
.CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
.CallJSRuntime(Context::SPREAD_ITERABLE_INDEX, reg_list)
.CallWithSpread(reg, reg_list, 1);
// Emit binary operator invocations.
builder.BinaryOperation(Token::Value::ADD, reg, 1)
.BinaryOperation(Token::Value::SUB, reg, 2)
.BinaryOperation(Token::Value::MUL, reg, 3)
.BinaryOperation(Token::Value::DIV, reg, 4)
.BinaryOperation(Token::Value::MOD, reg, 5);
// Emit bitwise operator invocations
builder.BinaryOperation(Token::Value::BIT_OR, reg, 6)
.BinaryOperation(Token::Value::BIT_XOR, reg, 7)
.BinaryOperation(Token::Value::BIT_AND, reg, 8);
// Emit shift operator invocations
builder.BinaryOperation(Token::Value::SHL, reg, 9)
.BinaryOperation(Token::Value::SAR, reg, 10)
.BinaryOperation(Token::Value::SHR, reg, 11);
Reland: [Interpreter] Move BinaryOp Smi transformation into BytecodeGenerator."" This relands commit d3e9aade0ff24e100621ab451e83f703439ace9e. The original CL was reverted speculatively but didn't cause the buildbot failure. Original change's description: > [Interpreter] Move BinaryOp Smi transformation into BytecodeGenerator. > > Perform the transformation to <BinaryOp>Smi for Binary ops which take Smi > literals in the BytecodeGenerator. This enables us to perform the > transformation for literals on either side for commutative operations, and > Avoids having to do the check on every bytecode in the peephole optimizer. > > In the process, adds Smi bytecode variants for all binary operations, adding > - MulSmi > - DivSmi > - ModSmi > - BitwiseXorSmi > - ShiftRightLogical > > BUG=v8:6194 > > Change-Id: If1484252f5385c16957004b9cac8bfbb1f209219 > Reviewed-on: https://chromium-review.googlesource.com/466246 > Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> > Reviewed-by: Yang Guo <yangguo@chromium.org> > Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> > Reviewed-by: Igor Sheludko <ishell@chromium.org> > Cr-Commit-Position: refs/heads/master@{#44477} TBR=rmcilroy@chromium.org,machenbach@chromium.org,yangguo@chromium.org,mstarzinger@chromium.org,mythria@chromium.org,v8-reviews@googlegroups.com,ishell@chromium.org # Not skipping CQ checks because original CL landed > 1 day ago. BUG=v8:6194 Change-Id: I2ccaefa1ce58d3885f5c2648755985c06f25c1d8 Reviewed-on: https://chromium-review.googlesource.com/472746 Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Commit-Queue: Ross McIlroy <rmcilroy@chromium.org> Cr-Commit-Position: refs/heads/master@{#44511}
2017-04-10 09:30:51 +00:00
// Emit Smi binary operations.
builder.BinaryOperationSmiLiteral(Token::Value::ADD, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::SUB, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::MUL, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::DIV, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::MOD, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::BIT_OR, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::BIT_XOR, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::BIT_AND, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::SHL, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::SAR, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::SHR, Smi::FromInt(42), 2);
// Emit unary and count operator invocations.
builder.UnaryOperation(Token::Value::INC, 1)
.UnaryOperation(Token::Value::DEC, 1)
.UnaryOperation(Token::Value::ADD, 1)
.UnaryOperation(Token::Value::SUB, 1)
.UnaryOperation(Token::Value::BIT_NOT, 1);
// Emit unary operator invocations.
builder.LogicalNot(ToBooleanMode::kConvertToBoolean)
.LogicalNot(ToBooleanMode::kAlreadyBoolean)
.TypeOf();
// Emit delete
builder.Delete(reg, LanguageMode::SLOPPY).Delete(reg, LanguageMode::STRICT);
// Emit construct.
builder.Construct(reg, reg_list, 1).ConstructWithSpread(reg, reg_list, 1);
// Emit test operator invocations.
builder.CompareOperation(Token::Value::EQ, reg, 1)
.CompareOperation(Token::Value::EQ_STRICT, reg, 2)
.CompareOperation(Token::Value::EQ_STRICT, reg)
.CompareOperation(Token::Value::LT, reg, 3)
.CompareOperation(Token::Value::GT, reg, 4)
.CompareOperation(Token::Value::LTE, reg, 5)
.CompareOperation(Token::Value::GTE, reg, 6)
.CompareTypeOf(TestTypeOfFlags::LiteralFlag::kNumber)
.CompareOperation(Token::Value::INSTANCEOF, reg)
.CompareOperation(Token::Value::IN, reg)
.CompareUndetectable()
.CompareUndefined()
.CompareNull();
// Emit conversion operator invocations.
builder.ToNumber(1).ToObject(reg).ToName(reg);
// Emit GetSuperConstructor.
builder.GetSuperConstructor(reg);
// Hole checks.
builder.ThrowReferenceErrorIfHole(name)
.ThrowSuperAlreadyCalledIfNotHole()
.ThrowSuperNotCalledIfHole();
// Short jumps with Imm8 operands
{
[ignition] desugar GetIterator() via bytecode rather than via AST Introduces: - a new AST node representing the GetIterator() algorithm in the specification, to be used by ForOfStatement, YieldExpression (in the case of delegating yield*), and the future `for-await-of` loop proposed in http://tc39.github.io/proposal-async-iteration/#sec-async-iterator-value-unwrap-functions. - a new opcode (JumpIfJSReceiver), which is useful for `if Type(object) is not Object` checks which are common throughout the specification. This node is easily eliminated by TurboFan. The AST node is desugared specially in bytecode, rather than manually when building the AST. The benefit of this is that desugaring in the BytecodeGenerator is much simpler and easier to understand than desugaring the AST. This also reduces parse time very slightly, and allows us to use LoadIC rather than KeyedLoadIC, which seems to have better baseline performance. This results in a ~20% improvement in test/js-perf-test/Iterators micro-benchmarks, which I believe owes to the use of the slightly faster LoadIC as opposed to the KeyedLoadIC in the baseline case. Both produce identical optimized code via TurboFan when the type check can be eliminated, and the load can be replaced with a constant value. BUG=v8:4280 R=bmeurer@chromium.org, rmcilroy@chromium.org, adamk@chromium.org, neis@chromium.org, jarin@chromium.org TBR=rossberg@chromium.org Review-Url: https://codereview.chromium.org/2557593004 Cr-Commit-Position: refs/heads/master@{#41555}
2016-12-07 15:19:52 +00:00
BytecodeLabel start, after_jump1, after_jump2, after_jump3, after_jump4,
after_jump5, after_jump6, after_jump7, after_jump8, after_jump9,
after_jump10;
builder.Bind(&start)
.Jump(&after_jump1)
.Bind(&after_jump1)
.JumpIfNull(&after_jump2)
.Bind(&after_jump2)
.JumpIfNotNull(&after_jump3)
.Bind(&after_jump3)
.JumpIfUndefined(&after_jump4)
.Bind(&after_jump4)
.JumpIfNotUndefined(&after_jump5)
[ignition] desugar GetIterator() via bytecode rather than via AST Introduces: - a new AST node representing the GetIterator() algorithm in the specification, to be used by ForOfStatement, YieldExpression (in the case of delegating yield*), and the future `for-await-of` loop proposed in http://tc39.github.io/proposal-async-iteration/#sec-async-iterator-value-unwrap-functions. - a new opcode (JumpIfJSReceiver), which is useful for `if Type(object) is not Object` checks which are common throughout the specification. This node is easily eliminated by TurboFan. The AST node is desugared specially in bytecode, rather than manually when building the AST. The benefit of this is that desugaring in the BytecodeGenerator is much simpler and easier to understand than desugaring the AST. This also reduces parse time very slightly, and allows us to use LoadIC rather than KeyedLoadIC, which seems to have better baseline performance. This results in a ~20% improvement in test/js-perf-test/Iterators micro-benchmarks, which I believe owes to the use of the slightly faster LoadIC as opposed to the KeyedLoadIC in the baseline case. Both produce identical optimized code via TurboFan when the type check can be eliminated, and the load can be replaced with a constant value. BUG=v8:4280 R=bmeurer@chromium.org, rmcilroy@chromium.org, adamk@chromium.org, neis@chromium.org, jarin@chromium.org TBR=rossberg@chromium.org Review-Url: https://codereview.chromium.org/2557593004 Cr-Commit-Position: refs/heads/master@{#41555}
2016-12-07 15:19:52 +00:00
.Bind(&after_jump5)
.JumpIfJSReceiver(&after_jump6)
.Bind(&after_jump6)
.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &after_jump7)
.Bind(&after_jump7)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &after_jump8)
.Bind(&after_jump8)
.JumpIfFalse(ToBooleanMode::kConvertToBoolean, &after_jump9)
.Bind(&after_jump9)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &after_jump10)
.Bind(&after_jump10)
.JumpLoop(&start, 0);
}
// Longer jumps with constant operands
BytecodeLabel end[10];
{
BytecodeLabel after_jump;
builder.Jump(&end[0])
.Bind(&after_jump)
.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &end[1])
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &end[2])
.JumpIfFalse(ToBooleanMode::kConvertToBoolean, &end[3])
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &end[4])
.JumpIfNull(&end[5])
.JumpIfNotNull(&end[6])
.JumpIfUndefined(&end[7])
.JumpIfNotUndefined(&end[8])
.LoadLiteral(ast_factory.prototype_string())
.JumpIfJSReceiver(&end[9]);
}
// Emit Smi table switch bytecode.
BytecodeJumpTable* jump_table = builder.AllocateJumpTable(1, 0);
builder.SwitchOnSmiNoFeedback(jump_table).Bind(jump_table, 0);
// Emit set pending message bytecode.
builder.SetPendingMessage();
// Emit stack check bytecode.
builder.StackCheck(0);
// Emit throw and re-throw in it's own basic block so that the rest of the
// code isn't omitted due to being dead.
BytecodeLabel after_throw;
builder.Throw().Bind(&after_throw);
BytecodeLabel after_rethrow;
builder.ReThrow().Bind(&after_rethrow);
[turbofan] Optimize fast enum cache driven for..in. This CL adds support to optimize for..in in fast enum-cache mode to the same degree that it was optimized in Crankshaft, without adding the same deoptimization loop that Crankshaft had with missing enum cache indices. That means code like for (var k in o) { var v = o[k]; // ... } and code like for (var k in o) { if (Object.prototype.hasOwnProperty.call(o, k)) { var v = o[k]; // ... } } which follows the https://eslint.org/docs/rules/guard-for-in linter rule, can now utilize the enum cache indices if o has only fast properties on the receiver, which speeds up the access o[k] significantly and reduces the pollution of the global megamorphic stub cache. For example the micro-benchmark in the tracking bug v8:6702 now runs faster than ever before: forIn: 1516 ms. forInHasOwnProperty: 1674 ms. forInHasOwnPropertySafe: 1595 ms. forInSum: 2051 ms. forInSumSafe: 2215 ms. Compared to numbers from V8 5.8 which is the last version running with Crankshaft forIn: 1641 ms. forInHasOwnProperty: 1719 ms. forInHasOwnPropertySafe: 1802 ms. forInSum: 2226 ms. forInSumSafe: 2409 ms. and V8 6.0 which is the current stable version with TurboFan: forIn: 1713 ms. forInHasOwnProperty: 5417 ms. forInHasOwnPropertySafe: 5324 ms. forInSum: 7556 ms. forInSumSafe: 11067 ms. It also improves the throughput on the string-fasta benchmark by around 7-10%, and there seems to be a ~5% improvement on the Speedometer/React benchmark locally. For this to work, the ForInPrepare bytecode was split into ForInEnumerate and ForInPrepare, which is very similar to how it was handled in Fullcodegen initially. In TurboFan we introduce a new operator LoadFieldByIndex that does the dynamic property load. This also removes the CheckMapValue operator again in favor of just using LoadField, ReferenceEqual and CheckIf, which work automatically with the EscapeAnalysis and the BranchConditionElimination. Bug: v8:6702 Change-Id: I91235413eea478ba77ace7bd14bb2f62e155dd9a Reviewed-on: https://chromium-review.googlesource.com/645949 Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Cr-Commit-Position: refs/heads/master@{#47768}
2017-09-01 10:49:06 +00:00
builder.ForInEnumerate(reg)
.ForInPrepare(triple, 1)
.ForInContinue(reg, reg)
.ForInNext(reg, reg, pair, 1)
.ForInStep(reg);
// Wide constant pool loads
for (int i = 0; i < 256; i++) {
// Emit junk in constant pool to force wide constant pool index.
builder.LoadLiteral(ast_factory.NewNumber(2.5321 + i));
}
builder.LoadLiteral(Smi::FromInt(20000000));
const AstRawString* wide_name = ast_factory.GetOneByteString("var_wide_name");
// Emit wide global load / store operations.
builder.LoadGlobal(name, 1024, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadGlobal(name, 1024, TypeofMode::INSIDE_TYPEOF)
.LoadGlobal(name, 1024, TypeofMode::INSIDE_TYPEOF)
.StoreGlobal(name, 1024, LanguageMode::SLOPPY)
.StoreGlobal(wide_name, 1, LanguageMode::STRICT);
// Emit extra wide global load.
builder.LoadGlobal(name, 1024 * 1024, TypeofMode::NOT_INSIDE_TYPEOF);
// Emit wide load / store property operations.
builder.LoadNamedProperty(reg, wide_name, 0)
.LoadKeyedProperty(reg, 2056)
.StoreNamedProperty(reg, wide_name, 0, LanguageMode::SLOPPY)
.StoreKeyedProperty(reg, reg, 2056, LanguageMode::SLOPPY)
.StoreNamedProperty(reg, wide_name, 0, LanguageMode::STRICT)
.StoreKeyedProperty(reg, reg, 2056, LanguageMode::STRICT)
.StoreNamedOwnProperty(reg, wide_name, 0);
builder.StoreDataPropertyInLiteral(reg, reg,
DataPropertyInLiteralFlag::kNoFlags, 0);
// Emit wide context operations.
Reland of Thread maybe-assigned through the bytecodes. (patchset #1 id:1 of https://codereview.chromium.org/2680923003/ ) Reason for revert: False alarm, bot hiccup Original issue's description: > Revert of Thread maybe-assigned through the bytecodes. (patchset #5 id:80001 of https://codereview.chromium.org/2655733003/ ) > > Reason for revert: > needed for properly reverting https://chromium.googlesource.com/v8/v8/+/f3ae5ccf57690d8c2d87c4fe1d10b103ad6a4ab3 > > Original issue's description: > > Thread maybe-assigned through the bytecodes. > > > > This introduces LoadImmutableContextSlot and LoadImmutableCurrentContextSlot > > bytecodes, which are emitted when reading from never-assigned context slot. > > > > There is a subtlety here: the slot are not immutable, the meaning is > > actually undefined-or-hole-or-immutable. > > > > Review-Url: https://codereview.chromium.org/2655733003 > > Cr-Commit-Position: refs/heads/master@{#43000} > > Committed: https://chromium.googlesource.com/v8/v8/+/17c2dd388697da626224c371c8181d20d2016d82 > > TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org > # Skipping CQ checks because original CL landed less than 1 days ago. > NOPRESUBMIT=true > NOTREECHECKS=true > NOTRY=true > > Review-Url: https://codereview.chromium.org/2680923003 > Cr-Commit-Position: refs/heads/master@{#43011} > Committed: https://chromium.googlesource.com/v8/v8/+/ece4e54a31ac76ee9c450270d65270562db716e1 TBR=rmcilroy@chromium.org,bmeurer@chromium.org,neis@chromium.org,jarin@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2679953003 Cr-Commit-Position: refs/heads/master@{#43012}
2017-02-07 20:42:03 +00:00
builder.LoadContextSlot(reg, 1024, 0, BytecodeArrayBuilder::kMutableSlot)
.StoreContextSlot(reg, 1024, 0);
// Emit wide load / store lookup slots.
builder.LoadLookupSlot(wide_name, TypeofMode::NOT_INSIDE_TYPEOF)
.LoadLookupSlot(wide_name, TypeofMode::INSIDE_TYPEOF)
.StoreLookupSlot(wide_name, LanguageMode::SLOPPY,
LookupHoistingMode::kNormal)
.StoreLookupSlot(wide_name, LanguageMode::SLOPPY,
LookupHoistingMode::kLegacySloppy)
.StoreLookupSlot(wide_name, LanguageMode::STRICT,
LookupHoistingMode::kNormal);
// CreateClosureWide
builder.CreateClosure(1000, 321, NOT_TENURED);
// Emit wide variant of literal creation operations.
builder
.CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0)
.CreateArrayLiteral(0, 0, 0)
.CreateEmptyArrayLiteral(0)
.CreateObjectLiteral(0, 0, 0, reg)
.CreateEmptyObjectLiteral();
// Emit load and store operations for module variables.
builder.LoadModuleVariable(-1, 42)
.LoadModuleVariable(0, 42)
.LoadModuleVariable(1, 42)
.StoreModuleVariable(-1, 42)
.StoreModuleVariable(0, 42)
.StoreModuleVariable(1, 42);
// Emit generator operations.
builder.SuspendGenerator(reg, reg_list, 0)
.RestoreGeneratorState(reg)
.RestoreGeneratorRegisters(reg, reg_list);
// Intrinsics handled by the interpreter.
builder.CallRuntime(Runtime::kInlineIsArray, reg_list);
// Emit debugger bytecode.
builder.Debugger();
// Emit abort bytecode.
{
BytecodeLabel after;
builder.Abort(kGenerator).Bind(&after);
}
// Insert dummy ops to force longer jumps.
for (int i = 0; i < 256; i++) {
builder.Debugger();
}
[coverage] Block coverage with support for IfStatements This CL implements general infrastructure for block coverage together with initial support for if-statements. Coverage output can be generated in lcov format by d8 as follows: $ d8 --block-coverage --lcov=$(echo ~/simple-if.lcov) ~/simple-if.js $ genhtml ~/simple-if.lcov -o ~/simple-if $ chrome ~/simple-if/index.html A high level overview of the implementation follows: The parser now collects source ranges unconditionally for relevant AST nodes. Memory overhead is very low and this seemed like the cleanest and simplest alternative. Bytecode generation uses these ranges to allocate coverage slots and insert IncBlockCounter instructions (e.g. at the beginning of then- and else blocks for if-statements). The slot-range mapping is generated here and passed on through CompilationInfo, and is later accessible through the SharedFunctionInfo. The IncBlockCounter bytecode fetches the slot-range mapping (called CoverageInfo) from the shared function info and simply increments the counter. We don't collect native-context-specific counts as they are irrelevant to our use-cases. Coverage information is finally generated on-demand through Coverage::Collect. The only current consumer is a d8 front-end with lcov-style output, but the short-term goal is to expose this through the inspector protocol. BUG=v8:6000 CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_chromium_rel_ng Review-Url: https://codereview.chromium.org/2882973002 Cr-Commit-Position: refs/heads/master@{#45737}
2017-06-06 15:44:55 +00:00
// Emit block counter increments.
builder.IncBlockCounter(0);
// Bind labels for long jumps at the very end.
for (size_t i = 0; i < arraysize(end); i++) {
builder.Bind(&end[i]);
}
// Return must be the last instruction.
builder.Return();
// Generate BytecodeArray.
scope.SetScriptScopeInfo(factory->NewScopeInfo(1));
ast_factory.Internalize(isolate());
Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
CHECK_EQ(the_array->frame_size(),
builder.total_register_count() * kPointerSize);
// Build scorecard of bytecodes encountered in the BytecodeArray.
std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
Bytecode final_bytecode = Bytecode::kLdaZero;
int i = 0;
while (i < the_array->length()) {
uint8_t code = the_array->get(i);
scorecard[code] += 1;
final_bytecode = Bytecodes::FromByte(code);
OperandScale operand_scale = OperandScale::kSingle;
int prefix_offset = 0;
if (Bytecodes::IsPrefixScalingBytecode(final_bytecode)) {
operand_scale = Bytecodes::PrefixBytecodeToOperandScale(final_bytecode);
prefix_offset = 1;
code = the_array->get(i + 1);
final_bytecode = Bytecodes::FromByte(code);
}
i += prefix_offset + Bytecodes::Size(final_bytecode, operand_scale);
}
// Insert entry for illegal bytecode as this is never willingly emitted.
scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1;
[type-profile] Incorporate into inspector protocol. JavaScript is a dynamically typed language. But most code is written with fixed types in mind. When debugging JavaScript, it is helpful to know the types of variables and parameters at runtime. It is often hard to infer types for complex code. Type profiling provides this information at runtime. Node.js uses the inspector protocol. This CL allows Node.js users to access and analyse type profile for via Node modules or the in-procress api. Type Profile helps developers to analyze their code for correctness and performance. Design doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing Add `takeTypeProfile` to the inspector protocol. It returns a list of TypeProfileForScripts, which in turn contains the type profile for each function. We can use TypeProfile data to annotate JavaScript code. Sample script with data from TypeProfile: function f(/*Object, number, undefined*/a, /*Array, number, null*/b, /*boolean, Object, symbol*/c) { return 'bye'; /*string*/}; f({}, [], true); f(3, 2.3, {a: 42}); f(undefined, null, Symbol('hello'));/*string*/ Bug: v8:5933 Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I626bfb886b752f90b9c86cc6953601558b18b60d Reviewed-on: https://chromium-review.googlesource.com/508588 Commit-Queue: Franziska Hinkelmann <franzih@chromium.org> Reviewed-by: Pavel Feldman <pfeldman@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#47920}
2017-09-08 08:28:29 +00:00
// Bytecode for CollectTypeProfile is only emitted when
// Type Information for DevTools is turned on.
scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
Collect type profile for DevTools Collect type information for JavaScript variables and display it in Chrome DevTools. Design Doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing When debugging JavaScript, it’s helpful to know the type of a variable, parameter, and return values. JavaScript is dynamically typed, and for complex source code it’s often hard to infer types. With type profiling, we can provide type information to JavaScript developers. This CL is a proof of concept. It collects type profile for assignments and simply prints the types to stdout. The output looks something like this: #my_var1 #Object #number #string #number #undefined #string #Object #Object We use an extra slot in the feedback vector of assignments to carry the list of types for that assignment. The extra slot is only added when the flag --type-profile is given. Missing work: * Collect data for parameters and return values (currently only assignments). * Remove duplicates from the list of collected types and use a common base class. * Add line numbers or source position instead of the variable name. For now, has a test that compares the stdout of --type-profile in test/message. We will remove this test when --type-profile is fully integrated in the debugger protocol. Adding the test in test/inspector does not work, because the inspector test itself consists of JavaScript code that would convolute the output and be non-deterministic under stress. BUG=v8:5935 Review-Url: https://codereview.chromium.org/2707873002 Cr-Commit-Position: refs/heads/master@{#43866}
2017-03-16 15:01:31 +00:00
// Check return occurs at the end and only once in the BytecodeArray.
CHECK_EQ(final_bytecode, Bytecode::kReturn);
CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
#define CHECK_BYTECODE_PRESENT(Name, ...) \
/* Check Bytecode is marked in scorecard, unless it's a debug break */ \
if (!Bytecodes::IsDebugBreak(Bytecode::k##Name)) { \
CHECK_GE(scorecard[Bytecodes::ToByte(Bytecode::k##Name)], 1); \
}
BYTECODE_LIST(CHECK_BYTECODE_PRESENT)
#undef CHECK_BYTECODE_PRESENT
}
TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
for (int locals = 0; locals < 5; locals++) {
for (int temps = 0; temps < 3; temps++) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, locals);
BytecodeRegisterAllocator* allocator(builder.register_allocator());
for (int i = 0; i < locals; i++) {
builder.LoadLiteral(Smi::kZero);
builder.StoreAccumulatorInRegister(Register(i));
}
for (int i = 0; i < temps; i++) {
Register temp = allocator->NewRegister();
builder.LoadLiteral(Smi::kZero);
builder.StoreAccumulatorInRegister(temp);
// Ensure temporaries are used so not optimized away by the
// register optimizer.
builder.ToName(temp);
}
builder.Return();
Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
int total_registers = locals + temps;
CHECK_EQ(the_array->frame_size(), total_registers * kPointerSize);
}
}
}
TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
int index = 1;
Register the_register(index);
CHECK_EQ(the_register.index(), index);
int actual_operand = the_register.ToOperand();
int actual_index = Register::FromOperand(actual_operand).index();
CHECK_EQ(actual_index, index);
}
TEST_F(BytecodeArrayBuilderTest, Parameters) {
BytecodeArrayBuilder builder(isolate(), zone(), 10, 0);
Register receiver(builder.Receiver());
Register param8(builder.Parameter(8));
CHECK_EQ(param8.index() - receiver.index(), 9);
}
TEST_F(BytecodeArrayBuilderTest, Constants) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 0);
AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
isolate()->heap()->HashSeed());
const AstValue* heap_num_1 = ast_factory.NewNumber(3.14);
const AstValue* heap_num_2 = ast_factory.NewNumber(5.2);
const AstValue* string =
ast_factory.NewString(ast_factory.GetOneByteString("foo"));
const AstValue* string_copy =
ast_factory.NewString(ast_factory.GetOneByteString("foo"));
builder.LoadLiteral(heap_num_1)
.LoadLiteral(heap_num_2)
.LoadLiteral(string)
.LoadLiteral(heap_num_1)
.LoadLiteral(heap_num_1)
.LoadLiteral(string_copy)
.Return();
ast_factory.Internalize(isolate());
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
// Should only have one entry for each identical constant.
CHECK_EQ(array->constant_pool()->length(), 3);
}
TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
static const int kFarJumpDistance = 256 + 20;
BytecodeArrayBuilder builder(isolate(), zone(), 1, 1);
Register reg(0);
BytecodeLabel far0, far1, far2, far3, far4;
BytecodeLabel near0, near1, near2, near3, near4;
BytecodeLabel after_jump0, after_jump1;
builder.Jump(&near0)
.Bind(&after_jump0)
.CompareOperation(Token::Value::EQ, reg, 1)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &near1)
.CompareOperation(Token::Value::EQ, reg, 2)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &near2)
.BinaryOperation(Token::Value::ADD, reg, 1)
.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &near3)
.BinaryOperation(Token::Value::ADD, reg, 2)
.JumpIfFalse(ToBooleanMode::kConvertToBoolean, &near4)
.Bind(&near0)
.Bind(&near1)
.Bind(&near2)
.Bind(&near3)
.Bind(&near4)
.Jump(&far0)
.Bind(&after_jump1)
.CompareOperation(Token::Value::EQ, reg, 3)
.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &far1)
.CompareOperation(Token::Value::EQ, reg, 4)
.JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &far2)
.BinaryOperation(Token::Value::ADD, reg, 3)
.JumpIfTrue(ToBooleanMode::kConvertToBoolean, &far3)
.BinaryOperation(Token::Value::ADD, reg, 4)
.JumpIfFalse(ToBooleanMode::kConvertToBoolean, &far4);
for (int i = 0; i < kFarJumpDistance - 22; i++) {
builder.Debugger();
}
builder.Bind(&far0).Bind(&far1).Bind(&far2).Bind(&far3).Bind(&far4);
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
DCHECK_EQ(array->length(), 44 + kFarJumpDistance - 22 + 1);
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 22);
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 17);
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 12);
iterator.Advance();
// Ignore add operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 7);
iterator.Advance();
// Ignore add operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance));
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 5));
iterator.Advance();
// Ignore compare operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 10));
iterator.Advance();
// Ignore add operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 15));
iterator.Advance();
// Ignore add operation.
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(),
Bytecode::kJumpIfToBooleanFalseConstant);
CHECK_EQ(*iterator.GetConstantForIndexOperand(0),
Smi::FromInt(kFarJumpDistance - 20));
iterator.Advance();
}
TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 1);
Register reg(0);
BytecodeLabel label0;
builder.Bind(&label0).JumpLoop(&label0, 0);
for (int i = 0; i < 42; i++) {
BytecodeLabel after_jump;
builder.JumpLoop(&label0, 0).Bind(&after_jump);
}
// Add padding to force wide backwards jumps.
for (int i = 0; i < 256; i++) {
builder.Debugger();
}
builder.JumpLoop(&label0, 0);
BytecodeLabel end;
builder.Bind(&end);
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
for (unsigned i = 0; i < 42; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
// offset of 3 (because kJumpLoop takes two immediate operands)
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), i * 3 + 3);
iterator.Advance();
}
// Check padding to force wide backwards jumps.
for (int i = 0; i < 256; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
iterator.Advance();
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 386);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, SmallSwitch) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 1);
// Small jump table that fits into the single-size constant pool
int small_jump_table_size = 5;
int small_jump_table_base = -2;
BytecodeJumpTable* small_jump_table =
builder.AllocateJumpTable(small_jump_table_size, small_jump_table_base);
builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(small_jump_table);
for (int i = 0; i < small_jump_table_size; i++) {
builder.Bind(small_jump_table, small_jump_table_base + i).Debugger();
}
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
{
int i = 0;
int switch_end =
iterator.current_offset() + iterator.current_bytecode_size();
for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
CHECK_EQ(entry.case_value, small_jump_table_base + i);
CHECK_EQ(entry.target_offset, switch_end + i);
i++;
}
CHECK_EQ(i, small_jump_table_size);
}
iterator.Advance();
for (int i = 0; i < small_jump_table_size; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
iterator.Advance();
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, WideSwitch) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 1);
// Large jump table that requires a wide Switch bytecode.
int large_jump_table_size = 256;
int large_jump_table_base = -10;
BytecodeJumpTable* large_jump_table =
builder.AllocateJumpTable(large_jump_table_size, large_jump_table_base);
builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(large_jump_table);
for (int i = 0; i < large_jump_table_size; i++) {
builder.Bind(large_jump_table, large_jump_table_base + i).Debugger();
}
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
{
int i = 0;
int switch_end =
iterator.current_offset() + iterator.current_bytecode_size();
for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
CHECK_EQ(entry.case_value, large_jump_table_base + i);
CHECK_EQ(entry.target_offset, switch_end + i);
i++;
}
CHECK_EQ(i, large_jump_table_size);
}
iterator.Advance();
for (int i = 0; i < large_jump_table_size; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
iterator.Advance();
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
BytecodeArrayBuilder builder(isolate(), zone(), 1, 0);
// Labels can only have 1 forward reference, but
// can be referred to mulitple times once bound.
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.JumpLoop(&label, 0)
.Bind(&after_jump0)
.JumpLoop(&label, 0)
.Bind(&after_jump1)
.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
static const int kRepeats = 3;
BytecodeArrayBuilder builder(isolate(), zone(), 1, 0);
for (int i = 0; i < kRepeats; i++) {
BytecodeLabel label, after_jump0, after_jump1;
builder.Jump(&label)
.Bind(&label)
.JumpLoop(&label, 0)
.Bind(&after_jump0)
.JumpLoop(&label, 0)
.Bind(&after_jump1);
}
builder.Return();
Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
BytecodeArrayIterator iterator(array);
for (int i = 0; i < kRepeats; i++) {
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
iterator.Advance();
}
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
iterator.Advance();
CHECK(iterator.done());
}
} // namespace interpreter
} // namespace internal
} // namespace v8