Index: src/interpreter/bytecode-array-builder.cc |
diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc |
index 2d06765cd1cb1e12592f9cb8a213e63ea0ef8619..9bc634ef94cd538b541186177e19c56c5b30e53e 100644 |
--- a/src/interpreter/bytecode-array-builder.cc |
+++ b/src/interpreter/bytecode-array-builder.cc |
@@ -12,6 +12,9 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) |
: isolate_(isolate), |
bytecodes_(zone), |
bytecode_generated_(false), |
+ last_block_end_(0), |
+ last_bytecode_start_(~0), |
+ return_seen_in_block_(false), |
constants_map_(isolate->heap(), zone), |
constants_(zone), |
parameter_count_(-1), |
@@ -37,14 +40,6 @@ void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) { |
int BytecodeArrayBuilder::parameter_count() const { return parameter_count_; } |
-bool BytecodeArrayBuilder::HasExplicitReturn() { |
- // TODO(rmcilroy): When we have control flow we should return false here if |
- // there is an outstanding jump target, even if the last bytecode is kReturn. |
- return !bytecodes_.empty() && |
- bytecodes_.back() == Bytecodes::ToByte(Bytecode::kReturn); |
-} |
- |
- |
Register BytecodeArrayBuilder::Parameter(int parameter_index) { |
DCHECK_GE(parameter_index, 0); |
DCHECK_LT(parameter_index, parameter_count_); |
@@ -56,6 +51,9 @@ Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() { |
DCHECK_EQ(bytecode_generated_, false); |
DCHECK_GE(parameter_count_, 0); |
DCHECK_GE(local_register_count_, 0); |
+ |
+ EnsureReturn(); |
+ |
int bytecode_size = static_cast<int>(bytecodes_.size()); |
int register_count = local_register_count_ + temporary_register_count_; |
int frame_size = register_count * kPointerSize; |
@@ -76,9 +74,57 @@ Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() { |
} |
-BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value binop, |
+template <size_t N> |
+void BytecodeArrayBuilder::Output(uint8_t(&bytes)[N]) { |
+ DCHECK_EQ(Bytecodes::NumberOfOperands(Bytecodes::FromByte(bytes[0])), N - 1); |
+ last_bytecode_start_ = bytecodes()->size(); |
+ for (int i = 1; i < static_cast<int>(N); i++) { |
+ DCHECK(OperandIsValid(Bytecodes::FromByte(bytes[0]), i - 1, bytes[i])); |
+ } |
+ bytecodes()->insert(bytecodes()->end(), bytes, bytes + N); |
+} |
+ |
+ |
+void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, |
+ uint8_t operand1, uint8_t operand2) { |
+ uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0, operand1, operand2}; |
+ Output(bytes); |
+} |
+ |
+ |
+void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, |
+ uint8_t operand1) { |
+ uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0, operand1}; |
+ Output(bytes); |
+} |
+ |
+ |
+void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0) { |
+ uint8_t bytes[] = {Bytecodes::ToByte(bytecode), operand0}; |
+ Output(bytes); |
+} |
+ |
+ |
+void BytecodeArrayBuilder::Output(Bytecode bytecode) { |
+ uint8_t bytes[] = {Bytecodes::ToByte(bytecode)}; |
+ Output(bytes); |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op, |
Register reg) { |
- Output(BytecodeForBinaryOperation(binop), reg.ToOperand()); |
+ Output(BytecodeForBinaryOperation(op), reg.ToOperand()); |
+ return *this; |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation( |
+ Token::Value op, Register reg, LanguageMode language_mode) { |
+ if (!is_sloppy(language_mode)) { |
+ UNIMPLEMENTED(); |
+ } |
+ |
+ Output(BytecodeForCompareOperation(op), reg.ToOperand()); |
return *this; |
} |
@@ -99,7 +145,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( |
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) { |
size_t entry = GetConstantPoolEntry(object); |
- if (FitsInByteOperand(entry)) { |
+ if (FitsInIdxOperand(entry)) { |
Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry)); |
} else { |
UNIMPLEMENTED(); |
@@ -154,7 +200,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( |
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) { |
DCHECK(slot_index >= 0); |
- if (FitsInByteOperand(slot_index)) { |
+ if (FitsInIdxOperand(slot_index)) { |
Output(Bytecode::kLdaGlobal, static_cast<uint8_t>(slot_index)); |
} else { |
UNIMPLEMENTED(); |
@@ -168,7 +214,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( |
UNIMPLEMENTED(); |
} |
- if (FitsInByteOperand(feedback_slot)) { |
+ if (FitsInIdxOperand(feedback_slot)) { |
Output(Bytecode::kLoadIC, object.ToOperand(), |
static_cast<uint8_t>(feedback_slot)); |
} else { |
@@ -184,7 +230,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( |
UNIMPLEMENTED(); |
} |
- if (FitsInByteOperand(feedback_slot)) { |
+ if (FitsInIdxOperand(feedback_slot)) { |
Output(Bytecode::kKeyedLoadIC, object.ToOperand(), |
static_cast<uint8_t>(feedback_slot)); |
} else { |
@@ -201,7 +247,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( |
UNIMPLEMENTED(); |
} |
- if (FitsInByteOperand(feedback_slot)) { |
+ if (FitsInIdxOperand(feedback_slot)) { |
Output(Bytecode::kStoreIC, object.ToOperand(), name.ToOperand(), |
static_cast<uint8_t>(feedback_slot)); |
} else { |
@@ -218,7 +264,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( |
UNIMPLEMENTED(); |
} |
- if (FitsInByteOperand(feedback_slot)) { |
+ if (FitsInIdxOperand(feedback_slot)) { |
Output(Bytecode::kKeyedStoreIC, object.ToOperand(), key.ToOperand(), |
static_cast<uint8_t>(feedback_slot)); |
} else { |
@@ -228,16 +274,179 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( |
} |
+BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToBoolean() { |
+ if (LastBytecodeInSameBlock()) { |
+ // If the previous bytecode puts a boolean in the accumulator |
+ // there is no need to emit an instruction. |
+ switch (Bytecodes::FromByte(bytecodes()->at(last_bytecode_start_))) { |
+ case Bytecode::kToBoolean: |
+ UNREACHABLE(); |
+ case Bytecode::kLdaTrue: |
+ case Bytecode::kLdaFalse: |
+ case Bytecode::kTestEqual: |
+ case Bytecode::kTestNotEqual: |
+ case Bytecode::kTestEqualStrict: |
+ case Bytecode::kTestNotEqualStrict: |
+ case Bytecode::kTestLessThan: |
+ case Bytecode::kTestLessThanEqual: |
+ case Bytecode::kTestGreaterThan: |
+ case Bytecode::kTestGreaterThanEqual: |
+ case Bytecode::kTestInstanceOf: |
+ case Bytecode::kTestIn: |
+ break; |
+ default: |
+ Output(Bytecode::kToBoolean); |
+ } |
+ } |
+ return *this; |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) { |
+ if (label->is_forward_target()) { |
+ // An earlier jump instruction refers to this label. Update it's location. |
+ PatchJump(bytecodes()->end(), bytecodes()->begin() + label->offset()); |
+ // Now treat as if the label will only be back referred to. |
+ } |
+ label->bind_to(bytecodes()->size()); |
+ return *this; |
+} |
+ |
+ |
+// static |
+bool BytecodeArrayBuilder::IsJumpWithImm8Operand(Bytecode jump_bytecode) { |
+ return jump_bytecode == Bytecode::kJump || |
+ jump_bytecode == Bytecode::kJumpIfTrue || |
+ jump_bytecode == Bytecode::kJumpIfFalse; |
+} |
+ |
+ |
+// static |
+Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand( |
+ Bytecode jump_bytecode) { |
+ switch (jump_bytecode) { |
+ case Bytecode::kJump: |
+ return Bytecode::kJumpConstant; |
+ case Bytecode::kJumpIfTrue: |
+ return Bytecode::kJumpIfTrueConstant; |
+ case Bytecode::kJumpIfFalse: |
+ return Bytecode::kJumpIfFalseConstant; |
+ default: |
+ UNREACHABLE(); |
+ return Bytecode::kJumpConstant; |
+ } |
+} |
+ |
+ |
+void BytecodeArrayBuilder::PatchJump( |
+ const ZoneVector<uint8_t>::iterator& jump_target, |
+ ZoneVector<uint8_t>::iterator jump_location) { |
+ Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location); |
+ int delta = static_cast<int>(jump_target - jump_location); |
+ |
+ DCHECK(IsJumpWithImm8Operand(jump_bytecode)); |
+ DCHECK_EQ(Bytecodes::Size(jump_bytecode), 2); |
+ DCHECK_GE(delta, 0); |
+ |
+ if (FitsInImm8Operand(delta)) { |
+ // Just update the operand |
+ jump_location++; |
+ *jump_location = static_cast<uint8_t>(delta); |
+ } else { |
+ // Update the jump type and operand |
+ size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate())); |
+ if (FitsInIdxOperand(entry)) { |
+ *jump_location++ = |
+ Bytecodes::ToByte(GetJumpWithConstantOperand(jump_bytecode)); |
+ *jump_location = static_cast<uint8_t>(entry); |
+ } else { |
+ // TODO(oth): OutputJump should reserve a constant pool entry |
+ // when jump is written. The reservation should be used here if |
+ // needed, or cancelled if not. This is due to the patch needing |
+ // to match the size of the code it's replacing. In future, |
+ // there will probably be a jump with 32-bit operand for cases |
+ // when constant pool is full, but that needs to be emitted in |
+ // OutputJump too. |
+ UNIMPLEMENTED(); |
+ } |
+ } |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode, |
+ BytecodeLabel* label) { |
+ int delta; |
+ if (label->is_bound()) { |
+ // Label has been bound already so this is a backwards jump. |
+ CHECK_GE(bytecodes()->size(), label->offset()); |
+ CHECK_LE(bytecodes()->size(), static_cast<size_t>(kMaxInt)); |
+ size_t abs_delta = bytecodes()->size() - label->offset(); |
+ delta = -static_cast<int>(abs_delta); |
+ } else { |
+ // Label has not yet been bound so this is a forward reference |
+ // that will be patched when the label is bound. |
+ label->set_referrer(bytecodes()->size()); |
+ delta = 0; |
+ } |
+ |
+ if (FitsInImm8Operand(delta)) { |
+ Output(jump_bytecode, static_cast<uint8_t>(delta)); |
+ } else { |
+ size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate())); |
+ if (FitsInIdxOperand(entry)) { |
+ Output(GetJumpWithConstantOperand(jump_bytecode), |
+ static_cast<uint8_t>(entry)); |
+ } else { |
+ UNIMPLEMENTED(); |
+ } |
+ } |
+ return *this; |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) { |
+ return OutputJump(Bytecode::kJump, label); |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) { |
+ return OutputJump(Bytecode::kJumpIfTrue, label); |
+} |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) { |
+ return OutputJump(Bytecode::kJumpIfFalse, label); |
+} |
+ |
+ |
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { |
Output(Bytecode::kReturn); |
+ return_seen_in_block_ = true; |
return *this; |
} |
+BytecodeArrayBuilder& BytecodeArrayBuilder::EnterBlock() { return *this; } |
+ |
+ |
+BytecodeArrayBuilder& BytecodeArrayBuilder::LeaveBlock() { |
+ last_block_end_ = bytecodes()->size(); |
+ return_seen_in_block_ = false; |
+ return *this; |
+} |
+ |
+ |
+void BytecodeArrayBuilder::EnsureReturn() { |
+ if (!return_seen_in_block_) { |
+ LoadUndefined(); |
+ Return(); |
+ } |
+} |
+ |
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, |
Register receiver, |
size_t arg_count) { |
- if (FitsInByteOperand(arg_count)) { |
+ if (FitsInIdxOperand(arg_count)) { |
Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(), |
static_cast<uint8_t>(arg_count)); |
} else { |
@@ -308,42 +517,9 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index, |
return false; |
} |
- |
-void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, |
- uint8_t operand1, uint8_t operand2) { |
- DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 3); |
- DCHECK(OperandIsValid(bytecode, 0, operand0) && |
- OperandIsValid(bytecode, 1, operand1) && |
- OperandIsValid(bytecode, 2, operand2)); |
- bytecodes_.push_back(Bytecodes::ToByte(bytecode)); |
- bytecodes_.push_back(operand0); |
- bytecodes_.push_back(operand1); |
- bytecodes_.push_back(operand2); |
-} |
- |
- |
-void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0, |
- uint8_t operand1) { |
- DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 2); |
- DCHECK(OperandIsValid(bytecode, 0, operand0) && |
- OperandIsValid(bytecode, 1, operand1)); |
- bytecodes_.push_back(Bytecodes::ToByte(bytecode)); |
- bytecodes_.push_back(operand0); |
- bytecodes_.push_back(operand1); |
-} |
- |
- |
-void BytecodeArrayBuilder::Output(Bytecode bytecode, uint8_t operand0) { |
- DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 1); |
- DCHECK(OperandIsValid(bytecode, 0, operand0)); |
- bytecodes_.push_back(Bytecodes::ToByte(bytecode)); |
- bytecodes_.push_back(operand0); |
-} |
- |
- |
-void BytecodeArrayBuilder::Output(Bytecode bytecode) { |
- DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 0); |
- bytecodes_.push_back(Bytecodes::ToByte(bytecode)); |
+bool BytecodeArrayBuilder::LastBytecodeInSameBlock() const { |
+ return last_bytecode_start_ < bytecodes()->size() && |
+ last_bytecode_start_ >= last_block_end_; |
} |
@@ -361,21 +537,57 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) { |
case Token::Value::MOD: |
return Bytecode::kMod; |
default: |
- UNIMPLEMENTED(); |
+ UNREACHABLE(); |
return static_cast<Bytecode>(-1); |
} |
} |
// static |
-bool BytecodeArrayBuilder::FitsInByteOperand(int value) { |
- return 0 <= value && value <= 255; |
+Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) { |
+ switch (op) { |
+ case Token::Value::EQ: |
+ return Bytecode::kTestEqual; |
+ case Token::Value::NE: |
+ return Bytecode::kTestNotEqual; |
+ case Token::Value::EQ_STRICT: |
+ return Bytecode::kTestEqualStrict; |
+ case Token::Value::NE_STRICT: |
+ return Bytecode::kTestNotEqualStrict; |
+ case Token::Value::LT: |
+ return Bytecode::kTestLessThan; |
+ case Token::Value::GT: |
+ return Bytecode::kTestGreaterThan; |
+ case Token::Value::LTE: |
+ return Bytecode::kTestLessThanEqual; |
+ case Token::Value::GTE: |
+ return Bytecode::kTestGreaterThanEqual; |
+ case Token::Value::INSTANCEOF: |
+ return Bytecode::kTestInstanceOf; |
+ case Token::Value::IN: |
+ return Bytecode::kTestIn; |
+ default: |
+ UNREACHABLE(); |
+ return static_cast<Bytecode>(-1); |
+ } |
+} |
+ |
+ |
+// static |
+bool BytecodeArrayBuilder::FitsInIdxOperand(int value) { |
+ return kMinUInt8 <= value && value <= kMaxUInt8; |
+} |
+ |
+ |
+// static |
+bool BytecodeArrayBuilder::FitsInIdxOperand(size_t value) { |
+ return value <= static_cast<size_t>(kMaxUInt8); |
} |
// static |
-bool BytecodeArrayBuilder::FitsInByteOperand(size_t value) { |
- return value <= 255; |
+bool BytecodeArrayBuilder::FitsInImm8Operand(int value) { |
+ return kMinInt8 <= value && value < kMaxInt8; |
} |