Commit 905d7aaf authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[Interpreter] Add StringConcat bytecode.

Special cases addition expressions where one of the sides is known to be a
string to enable chains of string additions to be transformed into a series
of ToPrimitiveToString operations followed by a single string concatenation 
at the end of the chain of additions. This should avoid creating temporary
strings for each of the string additions (in essence this is an automated
string builder).

BUG=v8:6243

Change-Id: I44977d6dad00ee906f251c4bd9cab27e160c09d1
Reviewed-on: https://chromium-review.googlesource.com/493966
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45453}
parent d813f46e
......@@ -3878,23 +3878,48 @@ Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
return fake_sequential_string;
}
void CodeStubAssembler::MaybeDerefIndirectString(Variable* var_string,
Node* instance_type,
Variable* var_did_something) {
Label deref(this), done(this, var_did_something);
void CodeStubAssembler::BranchIfCanDerefIndirectString(Node* string,
Node* instance_type,
Label* can_deref,
Label* cannot_deref) {
CSA_ASSERT(this, IsString(string));
Node* representation =
Word32And(instance_type, Int32Constant(kStringRepresentationMask));
GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), &deref);
GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)), &done);
GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), can_deref);
GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)),
cannot_deref);
// Cons string.
Node* rhs = LoadObjectField(var_string->value(), ConsString::kSecondOffset);
GotoIf(WordEqual(rhs, EmptyStringConstant()), &deref);
Goto(&done);
Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
GotoIf(IsEmptyString(rhs), can_deref);
Goto(cannot_deref);
}
void CodeStubAssembler::DerefIndirectString(Variable* var_string,
Node* instance_type) {
#ifdef DEBUG
Label can_deref(this), cannot_deref(this);
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &can_deref,
&cannot_deref);
BIND(&cannot_deref);
DebugBreak(); // Should be able to dereference string.
Goto(&can_deref);
BIND(&can_deref);
#endif // DEBUG
BIND(&deref);
STATIC_ASSERT(ThinString::kActualOffset == ConsString::kFirstOffset);
var_string->Bind(
LoadObjectField(var_string->value(), ThinString::kActualOffset));
}
void CodeStubAssembler::MaybeDerefIndirectString(Variable* var_string,
Node* instance_type,
Variable* var_did_something) {
Label deref(this), done(this, var_did_something);
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &deref,
&done);
BIND(&deref);
DerefIndirectString(var_string, instance_type);
var_did_something->Bind(IntPtrConstant(1));
Goto(&done);
......@@ -3939,6 +3964,10 @@ Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right,
CSA_ASSERT(this, TaggedIsSmi(left_length));
CSA_ASSERT(this, TaggedIsSmi(right_length));
Node* new_length = SmiAdd(left_length, right_length);
// If new length is greater than String::kMaxLength, goto runtime to
// throw. Note: we also need to invalidate the string length protector, so
// can't just throw here directly.
GotoIf(SmiAboveOrEqual(new_length, SmiConstant(String::kMaxLength)),
&runtime);
......@@ -4456,10 +4485,8 @@ Node* CodeStubAssembler::ToUint32(Node* context, Node* input) {
Node* CodeStubAssembler::ToString(Node* context, Node* input) {
Label is_number(this);
Label runtime(this, Label::kDeferred);
Label runtime(this, Label::kDeferred), done(this);
VARIABLE(result, MachineRepresentation::kTagged);
Label done(this, &result);
GotoIf(TaggedIsSmi(input), &is_number);
Node* input_map = LoadMap(input);
......
......@@ -435,6 +435,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* LoadWeakCellValueUnchecked(Node* weak_cell);
Node* LoadWeakCellValue(Node* weak_cell, Label* if_cleared = nullptr);
// Get the offset of an element in a fixed array.
Node* GetFixedArrayElementOffset(
Node* index_node, int additional_offset = 0,
ParameterMode parameter_mode = INTPTR_PARAMETERS);
// Load an array element from a FixedArray.
Node* LoadFixedArrayElement(Node* object, Node* index,
int additional_offset = 0,
......@@ -840,6 +845,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* TryDerefExternalString(Node* const string, Node* const instance_type,
Label* if_bailout);
// Check if |string| is an indirect (thin or flat cons) string type that can
// be dereferenced by DerefIndirectString.
void BranchIfCanDerefIndirectString(Node* string, Node* instance_type,
Label* can_deref, Label* cannot_deref);
// Unpack an indirect (thin or flat cons) string type.
void DerefIndirectString(Variable* var_string, Node* instance_type);
// Check if |var_string| has an indirect (thin or flat cons) string type,
// and unpack it if so.
void MaybeDerefIndirectString(Variable* var_string, Node* instance_type,
......
......@@ -2091,6 +2091,14 @@ void BytecodeGraphBuilder::VisitToNumber() {
Environment::kAttachFrameState);
}
void BytecodeGraphBuilder::VisitToPrimitiveToString() {
UNREACHABLE(); // TODO(rmcilroy): Implement this.
}
void BytecodeGraphBuilder::VisitStringConcat() {
UNREACHABLE(); // TODO(rmcilroy): Implement this.
}
void BytecodeGraphBuilder::VisitJump() { BuildJump(); }
void BytecodeGraphBuilder::VisitJumpConstant() { BuildJump(); }
......
......@@ -327,6 +327,8 @@ DEFINE_BOOL(ignition_elide_noneffectful_bytecodes, true,
DEFINE_BOOL(ignition_reo, true, "use ignition register equivalence optimizer")
DEFINE_BOOL(ignition_filter_expression_positions, true,
"filter expression positions before the bytecode pipeline")
DEFINE_BOOL(ignition_string_concat, false,
"translate string add chains into string concatenations")
DEFINE_BOOL(print_bytecode, false,
"print bytecode generated by ignition interpreter")
DEFINE_STRING(print_bytecode_filter, "*",
......
......@@ -1242,6 +1242,15 @@ inline uint32_t ObjectHash(Address address) {
kPointerSizeLog2);
}
// Type feedback is encoded in such a way that, we can combine the feedback
// at different points by performing an 'OR' operation. Type feedback moves
// to a more generic type when we combine feedback.
// kString -> kAny
class ToPrimitiveToStringFeedback {
public:
enum { kNone = 0x0, kString = 0x1, kAny = 0x3 };
};
// Type feedback is encoded in such a way that, we can combine the feedback
// at different points by performing an 'OR' operation. Type feedback moves
// to a more generic type when we combine feedback.
......
......@@ -990,24 +990,34 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToObject(
Register out) {
BytecodeArrayBuilder& BytecodeArrayBuilder::ToObject(Register out) {
OutputToObject(out);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToName(
Register out) {
BytecodeArrayBuilder& BytecodeArrayBuilder::ToName(Register out) {
OutputToName(out);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToNumber(
Register out, int feedback_slot) {
BytecodeArrayBuilder& BytecodeArrayBuilder::ToNumber(Register out,
int feedback_slot) {
OutputToNumber(out, feedback_slot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ToPrimitiveToString(
Register out, int feedback_slot) {
OutputToPrimitiveToString(out, feedback_slot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StringConcat(
RegisterList operand_registers) {
OutputStringConcat(operand_registers, operand_registers.register_count());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
// Flush the register optimizer when binding a label to ensure all
// expected registers are valid when jumping to this label.
......
......@@ -352,10 +352,16 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
TestTypeOfFlags::LiteralFlag literal_flag);
// Converts accumulator and stores result in register |out|.
BytecodeArrayBuilder& ConvertAccumulatorToObject(Register out);
BytecodeArrayBuilder& ConvertAccumulatorToName(Register out);
BytecodeArrayBuilder& ConvertAccumulatorToNumber(Register out,
int feedback_slot);
BytecodeArrayBuilder& ToObject(Register out);
BytecodeArrayBuilder& ToName(Register out);
BytecodeArrayBuilder& ToNumber(Register out, int feedback_slot);
// Converts accumulator to a primitive and then to a string, and stores result
// in register |out|.
BytecodeArrayBuilder& ToPrimitiveToString(Register out, int feedback_slot);
// Concatenate all the string values in |operand_registers| into a string
// and store result in the accumulator.
BytecodeArrayBuilder& StringConcat(RegisterList operand_registers);
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
......
This diff is collapsed.
......@@ -39,6 +39,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitStatements(ZoneList<Statement*>* statments);
private:
class AdditionResultScope;
class ContextScope;
class ControlScope;
class ControlScopeForBreakable;
......@@ -57,7 +58,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
enum class TestFallthrough { kThen, kElse, kNone };
enum class TypeHint { kAny, kBoolean };
enum class TypeHint { kAny, kString, kBoolean };
void GenerateBytecodeBody();
void AllocateDeferredConstants(Isolate* isolate);
......@@ -159,6 +160,13 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitForInAssignment(Expression* expr, FeedbackSlot slot);
void VisitModuleNamespaceImports();
// Builds an addition expression. If the result is a known string addition,
// then rather than emitting the add, the operands will converted to
// primitive, then to string and stored in registers in the
// |operand_registers| list for later concatenation.
void BuildAddExpression(BinaryOperation* expr,
RegisterList* operand_registers);
// Visit the header/body of a loop iteration.
void VisitIterationHeader(IterationStatement* stmt,
LoopBuilder* loop_builder);
......@@ -182,13 +190,18 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
void VisitForEffect(Expression* expr);
void VisitForTest(Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
TypeHint VisitForAddOperand(Expression* expr, RegisterList* operand_registers,
Register* out_register);
// Returns the runtime function id for a store to super for the function's
// language mode.
inline Runtime::FunctionId StoreToSuperRuntimeId();
inline Runtime::FunctionId StoreKeyedToSuperRuntimeId();
ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint);
static constexpr ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint) {
return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean
: ToBooleanMode::kConvertToBoolean;
}
inline BytecodeArrayBuilder* builder() const { return builder_; }
inline Zone* zone() const { return zone_; }
......
......@@ -73,6 +73,22 @@ class BytecodeRegisterAllocator final {
return reg;
}
// Releases the last register in |reg_list|, decreasing it's count by one and
// returning the register released.
//
// Note: no other new registers must be currently allocated since the register
// list was originally allocated or grown.
Register ShrinkRegisterList(RegisterList* reg_list) {
// If the following CHECK fails then a register was allocated (and not
// freed) between the creation of the RegisterList and this call to release
// the Register.
Register last_reg = reg_list->last_register();
CHECK_EQ(last_reg.index(), next_register_index_ - 1);
reg_list->DecrementRegisterCount();
ReleaseRegisters(next_register_index_ - 1);
return last_reg;
}
// Release all registers above |register_index|.
void ReleaseRegisters(int register_index) {
if (observer_) {
......
......@@ -105,8 +105,9 @@ class RegisterList {
RegisterList(int first_reg_index, int register_count)
: first_reg_index_(first_reg_index), register_count_(register_count) {}
// Increases the size of the register list by one.
// Increases/decreases the size of the register list by one.
void IncrementRegisterCount() { register_count_++; }
void DecrementRegisterCount() { register_count_--; }
// Returns a new RegisterList which is a truncated version of this list, with
// |count| registers.
......
......@@ -224,6 +224,12 @@ namespace interpreter {
V(ToNumber, AccumulatorUse::kRead, OperandType::kRegOut, OperandType::kIdx) \
V(ToObject, AccumulatorUse::kRead, OperandType::kRegOut) \
\
/* String concatenation */ \
V(ToPrimitiveToString, AccumulatorUse::kRead, OperandType::kRegOut, \
OperandType::kIdx) \
V(StringConcat, AccumulatorUse::kWrite, OperandType::kRegList, \
OperandType::kRegCount) \
\
/* Literals */ \
V(CreateRegExpLiteral, AccumulatorUse::kWrite, OperandType::kIdx, \
OperandType::kIdx, OperandType::kFlag8) \
......
......@@ -945,8 +945,10 @@ Node* InterpreterAssembler::ConstructWithSpread(Node* constructor,
Node* InterpreterAssembler::CallRuntimeN(Node* function_id, Node* context,
Node* first_arg, Node* arg_count,
int result_size) {
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
DCHECK(Bytecodes::IsCallRuntime(bytecode_));
DCHECK_IMPLIES(Bytecodes::IsCallRuntime(bytecode_),
Bytecodes::MakesCallAlongCriticalPath(bytecode_));
DCHECK(Bytecodes::IsCallRuntime(bytecode_) ||
bytecode_ == Bytecode::kStringConcat);
Callable callable = CodeFactory::InterpreterCEntry(isolate(), result_size);
Node* code_target = HeapConstant(callable.code());
......
This diff is collapsed.
......@@ -15,6 +15,7 @@
#include "src/interpreter/bytecodes.h"
#include "src/isolate-inl.h"
#include "src/ostreams.h"
#include "src/string-builder.h"
namespace v8 {
namespace internal {
......@@ -34,6 +35,23 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
static_cast<PretenureFlag>(pretenured_flag));
}
RUNTIME_FUNCTION(Runtime_InterpreterStringConcat) {
HandleScope scope(isolate);
DCHECK_LE(2, args.length());
int const argc = args.length();
ScopedVector<Handle<Object>> argv(argc);
isolate->counters()->string_add_runtime()->Increment();
IncrementalStringBuilder builder(isolate);
for (int i = 0; i < argc; ++i) {
Handle<String> str = Handle<String>::cast(args.at(i));
if (str->length() != 0) {
builder.AppendString(str);
}
}
RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
}
#ifdef V8_TRACE_IGNITION
namespace {
......
......@@ -224,9 +224,10 @@ namespace internal {
#define FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F)
#endif
#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F) \
F(InterpreterNewClosure, 4, 1) \
#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F) \
F(InterpreterNewClosure, 4, 1) \
F(InterpreterStringConcat, -1 /* >= 2 */, 1) \
F(InterpreterAdvanceBytecodeOffset, 2, 1)
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
......
#
# Autogenerated by generate-bytecode-expectations.
#
---
wrap: yes
---
snippet: "
var a = 1;
var b = 2;
return a + b + 'string';
"
frame size: 3
parameter count: 1
bytecode array length: 22
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(Ldar), R(1),
/* 65 E> */ B(Add), R(0), U8(3),
B(Star), R(2),
B(LdaConstant), U8(0),
/* 69 E> */ B(Add), R(2), U8(4),
/* 81 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["string"],
]
handlers: [
]
---
snippet: "
var a = 1;
var b = 2;
return 'string' + a + b;
"
frame size: 3
parameter count: 1
bytecode array length: 26
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
B(Star), R(2),
B(Ldar), R(0),
/* 72 E> */ B(Add), R(2), U8(3),
B(Star), R(2),
B(Ldar), R(1),
/* 76 E> */ B(Add), R(2), U8(4),
/* 81 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["string"],
]
handlers: [
]
---
snippet: "
var a = 1;
var b = 2;
return a + 'string' + b;
"
frame size: 3
parameter count: 1
bytecode array length: 22
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
/* 65 E> */ B(Add), R(0), U8(3),
B(Star), R(2),
B(Ldar), R(1),
/* 76 E> */ B(Add), R(2), U8(4),
/* 81 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["string"],
]
handlers: [
]
---
snippet: "
var a = 1;
var b = 2;
return 'foo' + a + 'bar' + b + 'baz' + 1;
"
frame size: 3
parameter count: 1
bytecode array length: 43
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
B(Star), R(2),
B(Ldar), R(0),
/* 69 E> */ B(Add), R(2), U8(3),
B(Star), R(2),
B(LdaConstant), U8(1),
/* 73 E> */ B(Add), R(2), U8(4),
B(Star), R(2),
B(Ldar), R(1),
/* 81 E> */ B(Add), R(2), U8(5),
B(Star), R(2),
B(LdaConstant), U8(2),
/* 85 E> */ B(Add), R(2), U8(6),
/* 93 E> */ B(AddSmi), I8(1), U8(7),
/* 98 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["foo"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["bar"],
ONE_BYTE_INTERNALIZED_STRING_TYPE ["baz"],
]
handlers: [
]
---
snippet: "
var a = 1;
var b = 2;
return (a + 'string') + ('string' + b);
"
frame size: 4
parameter count: 1
bytecode array length: 29
bytecodes: [
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 56 S> */ B(LdaConstant), U8(0),
/* 66 E> */ B(Add), R(0), U8(3),
B(Star), R(2),
B(LdaConstant), U8(0),
B(Star), R(3),
B(Ldar), R(1),
/* 90 E> */ B(Add), R(3), U8(4),
/* 78 E> */ B(Add), R(2), U8(5),
/* 96 S> */ B(Return),
]
constant pool: [
ONE_BYTE_INTERNALIZED_STRING_TYPE ["string"],
]
handlers: [
]
---
snippet: "
var a = 1;
var b = 2;
function foo(a, b) { };
return 'string' + foo(a, b) + a + b;
"
frame size: 4
parameter count: 1
bytecode array length: 42
bytecodes: [
B(CreateClosure), U8(0), U8(3), U8(2),
B(Star), R(2),
/* 30 E> */ B(StackCheck),
/* 42 S> */ B(LdaSmi), I8(1),
B(Star), R(0),
/* 53 S> */ B(LdaSmi), I8(2),
B(Star), R(1),
/* 80 S> */ B(LdaConstant), U8(1),
B(Star), R(3),
/* 98 E> */ B(CallUndefinedReceiver2), R(2), R(0), R(1), U8(4),
/* 96 E> */ B(Add), R(3), U8(6),
B(Star), R(3),
B(Ldar), R(0),
/* 108 E> */ B(Add), R(3), U8(7),
B(Star), R(3),
B(Ldar), R(1),
/* 112 E> */ B(Add), R(3), U8(8),
/* 117 S> */ B(Return),
]
constant pool: [
SHARED_FUNCTION_INFO_TYPE,
ONE_BYTE_INTERNALIZED_STRING_TYPE ["string"],
]
handlers: [
]
......@@ -2629,6 +2629,41 @@ TEST(ForOfLoop) {
LoadGolden("ForOfLoop.golden")));
}
TEST(StringConcat) {
InitializedIgnitionHandleScope scope;
BytecodeExpectationsPrinter printer(CcTest::isolate());
const char* snippets[] = {
"var a = 1;\n"
"var b = 2;\n"
"return a + b + 'string';\n",
"var a = 1;\n"
"var b = 2;\n"
"return 'string' + a + b;\n",
"var a = 1;\n"
"var b = 2;\n"
"return a + 'string' + b;\n",
"var a = 1;\n"
"var b = 2;\n"
"return 'foo' + a + 'bar' + b + 'baz' + 1;\n",
"var a = 1;\n"
"var b = 2;\n"
"return (a + 'string') + ('string' + b);\n",
"var a = 1;\n"
"var b = 2;\n"
"function foo(a, b) { };\n"
"return 'string' + foo(a, b) + a + b;\n",
};
CHECK(CompareTexts(BuildActual(printer, snippets),
LoadGolden("StringConcat.golden")));
}
} // namespace interpreter
} // namespace internal
} // namespace v8
This diff is collapsed.
......@@ -190,6 +190,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.BinaryOperationSmiLiteral(Token::Value::SAR, Smi::FromInt(42), 2)
.BinaryOperationSmiLiteral(Token::Value::SHR, Smi::FromInt(42), 2);
// Emit StringConcat operations.
builder.ToPrimitiveToString(reg, 1).StringConcat(pair);
// Emit count operatior invocations
builder.CountOperation(Token::Value::ADD, 1)
.CountOperation(Token::Value::SUB, 1);
......@@ -221,9 +224,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.CompareNull();
// Emit conversion operator invocations.
builder.ConvertAccumulatorToNumber(reg, 1)
.ConvertAccumulatorToObject(reg)
.ConvertAccumulatorToName(reg);
builder.ToNumber(reg, 1).ToObject(reg).ToName(reg);
// Emit GetSuperConstructor.
builder.GetSuperConstructor(reg);
......@@ -448,7 +449,7 @@ TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
builder.StoreAccumulatorInRegister(temp);
// Ensure temporaries are used so not optimized away by the
// register optimizer.
builder.ConvertAccumulatorToName(temp);
builder.ToName(temp);
}
builder.Return();
......
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