1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/interpreter/bytecode-array-writer.h" 6 7#include "src/api.h" 8#include "src/interpreter/bytecode-label.h" 9#include "src/interpreter/bytecode-register.h" 10#include "src/interpreter/constant-array-builder.h" 11#include "src/log.h" 12 13namespace v8 { 14namespace internal { 15namespace interpreter { 16 17STATIC_CONST_MEMBER_DEFINITION const size_t 18 BytecodeArrayWriter::kMaxSizeOfPackedBytecode; 19 20BytecodeArrayWriter::BytecodeArrayWriter( 21 Zone* zone, ConstantArrayBuilder* constant_array_builder, 22 SourcePositionTableBuilder::RecordingMode source_position_mode) 23 : bytecodes_(zone), 24 unbound_jumps_(0), 25 source_position_table_builder_(zone, source_position_mode), 26 constant_array_builder_(constant_array_builder) { 27 bytecodes_.reserve(512); // Derived via experimentation. 28} 29 30// override 31BytecodeArrayWriter::~BytecodeArrayWriter() {} 32 33// override 34Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray( 35 Isolate* isolate, int register_count, int parameter_count, 36 Handle<FixedArray> handler_table) { 37 DCHECK_EQ(0, unbound_jumps_); 38 39 int bytecode_size = static_cast<int>(bytecodes()->size()); 40 int frame_size = register_count * kPointerSize; 41 Handle<FixedArray> constant_pool = 42 constant_array_builder()->ToFixedArray(isolate); 43 Handle<BytecodeArray> bytecode_array = isolate->factory()->NewBytecodeArray( 44 bytecode_size, &bytecodes()->front(), frame_size, parameter_count, 45 constant_pool); 46 bytecode_array->set_handler_table(*handler_table); 47 Handle<ByteArray> source_position_table = 48 source_position_table_builder()->ToSourcePositionTable( 49 isolate, Handle<AbstractCode>::cast(bytecode_array)); 50 bytecode_array->set_source_position_table(*source_position_table); 51 return bytecode_array; 52} 53 54// override 55void BytecodeArrayWriter::Write(BytecodeNode* node) { 56 DCHECK(!Bytecodes::IsJump(node->bytecode())); 57 UpdateSourcePositionTable(node); 58 EmitBytecode(node); 59} 60 61// override 62void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) { 63 DCHECK(Bytecodes::IsJump(node->bytecode())); 64 UpdateSourcePositionTable(node); 65 EmitJump(node, label); 66} 67 68// override 69void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) { 70 size_t current_offset = bytecodes()->size(); 71 if (label->is_forward_target()) { 72 // An earlier jump instruction refers to this label. Update it's location. 73 PatchJump(current_offset, label->offset()); 74 // Now treat as if the label will only be back referred to. 75 } 76 label->bind_to(current_offset); 77} 78 79// override 80void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target, 81 BytecodeLabel* label) { 82 DCHECK(!label->is_bound()); 83 DCHECK(target.is_bound()); 84 if (label->is_forward_target()) { 85 // An earlier jump instruction refers to this label. Update it's location. 86 PatchJump(target.offset(), label->offset()); 87 // Now treat as if the label will only be back referred to. 88 } 89 label->bind_to(target.offset()); 90} 91 92void BytecodeArrayWriter::UpdateSourcePositionTable( 93 const BytecodeNode* const node) { 94 int bytecode_offset = static_cast<int>(bytecodes()->size()); 95 const BytecodeSourceInfo& source_info = node->source_info(); 96 if (source_info.is_valid()) { 97 source_position_table_builder()->AddPosition( 98 bytecode_offset, SourcePosition(source_info.source_position()), 99 source_info.is_statement()); 100 } 101} 102 103void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) { 104 DCHECK_NE(node->bytecode(), Bytecode::kIllegal); 105 106 Bytecode bytecode = node->bytecode(); 107 OperandScale operand_scale = node->operand_scale(); 108 109 if (operand_scale != OperandScale::kSingle) { 110 Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale); 111 bytecodes()->push_back(Bytecodes::ToByte(prefix)); 112 } 113 bytecodes()->push_back(Bytecodes::ToByte(bytecode)); 114 115 const uint32_t* const operands = node->operands(); 116 const int operand_count = node->operand_count(); 117 const OperandSize* operand_sizes = 118 Bytecodes::GetOperandSizes(bytecode, operand_scale); 119 for (int i = 0; i < operand_count; ++i) { 120 switch (operand_sizes[i]) { 121 case OperandSize::kNone: 122 UNREACHABLE(); 123 break; 124 case OperandSize::kByte: 125 bytecodes()->push_back(static_cast<uint8_t>(operands[i])); 126 break; 127 case OperandSize::kShort: { 128 uint16_t operand = static_cast<uint16_t>(operands[i]); 129 const uint8_t* raw_operand = reinterpret_cast<const uint8_t*>(&operand); 130 bytecodes()->push_back(raw_operand[0]); 131 bytecodes()->push_back(raw_operand[1]); 132 break; 133 } 134 case OperandSize::kQuad: { 135 const uint8_t* raw_operand = 136 reinterpret_cast<const uint8_t*>(&operands[i]); 137 bytecodes()->push_back(raw_operand[0]); 138 bytecodes()->push_back(raw_operand[1]); 139 bytecodes()->push_back(raw_operand[2]); 140 bytecodes()->push_back(raw_operand[3]); 141 break; 142 } 143 } 144 } 145} 146 147// static 148Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) { 149 switch (jump_bytecode) { 150 case Bytecode::kJump: 151 return Bytecode::kJumpConstant; 152 case Bytecode::kJumpIfTrue: 153 return Bytecode::kJumpIfTrueConstant; 154 case Bytecode::kJumpIfFalse: 155 return Bytecode::kJumpIfFalseConstant; 156 case Bytecode::kJumpIfToBooleanTrue: 157 return Bytecode::kJumpIfToBooleanTrueConstant; 158 case Bytecode::kJumpIfToBooleanFalse: 159 return Bytecode::kJumpIfToBooleanFalseConstant; 160 case Bytecode::kJumpIfNotHole: 161 return Bytecode::kJumpIfNotHoleConstant; 162 case Bytecode::kJumpIfNull: 163 return Bytecode::kJumpIfNullConstant; 164 case Bytecode::kJumpIfUndefined: 165 return Bytecode::kJumpIfUndefinedConstant; 166 default: 167 UNREACHABLE(); 168 return Bytecode::kIllegal; 169 } 170} 171 172void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location, 173 int delta) { 174 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 175 DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode)); 176 size_t operand_location = jump_location + 1; 177 DCHECK_EQ(bytecodes()->at(operand_location), k8BitJumpPlaceholder); 178 if (Bytecodes::ScaleForSignedOperand(delta) == OperandScale::kSingle) { 179 // The jump fits within the range of an Imm8 operand, so cancel 180 // the reservation and jump directly. 181 constant_array_builder()->DiscardReservedEntry(OperandSize::kByte); 182 bytecodes()->at(operand_location) = static_cast<uint8_t>(delta); 183 } else { 184 // The jump does not fit within the range of an Imm8 operand, so 185 // commit reservation putting the offset into the constant pool, 186 // and update the jump instruction and operand. 187 size_t entry = constant_array_builder()->CommitReservedEntry( 188 OperandSize::kByte, Smi::FromInt(delta)); 189 DCHECK_EQ(Bytecodes::SizeForUnsignedOperand(static_cast<uint32_t>(entry)), 190 OperandSize::kByte); 191 jump_bytecode = GetJumpWithConstantOperand(jump_bytecode); 192 bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode); 193 bytecodes()->at(operand_location) = static_cast<uint8_t>(entry); 194 } 195} 196 197void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location, 198 int delta) { 199 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 200 DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode)); 201 size_t operand_location = jump_location + 1; 202 uint8_t operand_bytes[2]; 203 if (Bytecodes::ScaleForSignedOperand(delta) <= OperandScale::kDouble) { 204 // The jump fits within the range of an Imm16 operand, so cancel 205 // the reservation and jump directly. 206 constant_array_builder()->DiscardReservedEntry(OperandSize::kShort); 207 WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(delta)); 208 } else { 209 // The jump does not fit within the range of an Imm16 operand, so 210 // commit reservation putting the offset into the constant pool, 211 // and update the jump instruction and operand. 212 size_t entry = constant_array_builder()->CommitReservedEntry( 213 OperandSize::kShort, Smi::FromInt(delta)); 214 jump_bytecode = GetJumpWithConstantOperand(jump_bytecode); 215 bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode); 216 WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry)); 217 } 218 DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder && 219 bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder); 220 bytecodes()->at(operand_location++) = operand_bytes[0]; 221 bytecodes()->at(operand_location) = operand_bytes[1]; 222} 223 224void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location, 225 int delta) { 226 DCHECK(Bytecodes::IsJumpImmediate( 227 Bytecodes::FromByte(bytecodes()->at(jump_location)))); 228 constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad); 229 uint8_t operand_bytes[4]; 230 WriteUnalignedUInt32(operand_bytes, static_cast<uint32_t>(delta)); 231 size_t operand_location = jump_location + 1; 232 DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder && 233 bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder && 234 bytecodes()->at(operand_location + 2) == k8BitJumpPlaceholder && 235 bytecodes()->at(operand_location + 3) == k8BitJumpPlaceholder); 236 bytecodes()->at(operand_location++) = operand_bytes[0]; 237 bytecodes()->at(operand_location++) = operand_bytes[1]; 238 bytecodes()->at(operand_location++) = operand_bytes[2]; 239 bytecodes()->at(operand_location) = operand_bytes[3]; 240} 241 242void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) { 243 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 244 int delta = static_cast<int>(jump_target - jump_location); 245 int prefix_offset = 0; 246 OperandScale operand_scale = OperandScale::kSingle; 247 if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) { 248 // If a prefix scaling bytecode is emitted the target offset is one 249 // less than the case of no prefix scaling bytecode. 250 delta -= 1; 251 prefix_offset = 1; 252 operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode); 253 jump_bytecode = 254 Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset)); 255 } 256 257 DCHECK(Bytecodes::IsJump(jump_bytecode)); 258 switch (operand_scale) { 259 case OperandScale::kSingle: 260 PatchJumpWith8BitOperand(jump_location, delta); 261 break; 262 case OperandScale::kDouble: 263 PatchJumpWith16BitOperand(jump_location + prefix_offset, delta); 264 break; 265 case OperandScale::kQuadruple: 266 PatchJumpWith32BitOperand(jump_location + prefix_offset, delta); 267 break; 268 default: 269 UNREACHABLE(); 270 } 271 unbound_jumps_--; 272} 273 274void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) { 275 DCHECK(Bytecodes::IsJump(node->bytecode())); 276 DCHECK_EQ(0u, node->operand(0)); 277 278 size_t current_offset = bytecodes()->size(); 279 280 if (label->is_bound()) { 281 CHECK_GE(current_offset, label->offset()); 282 CHECK_LE(current_offset, static_cast<size_t>(kMaxInt)); 283 // Label has been bound already so this is a backwards jump. 284 size_t abs_delta = current_offset - label->offset(); 285 int delta = -static_cast<int>(abs_delta); 286 OperandScale operand_scale = Bytecodes::ScaleForSignedOperand(delta); 287 if (operand_scale > OperandScale::kSingle) { 288 // Adjust for scaling byte prefix for wide jump offset. 289 DCHECK_LE(delta, 0); 290 delta -= 1; 291 } 292 DCHECK_EQ(Bytecode::kJumpLoop, node->bytecode()); 293 node->set_bytecode(node->bytecode(), delta, node->operand(1)); 294 } else { 295 // The label has not yet been bound so this is a forward reference 296 // that will be patched when the label is bound. We create a 297 // reservation in the constant pool so the jump can be patched 298 // when the label is bound. The reservation means the maximum size 299 // of the operand for the constant is known and the jump can 300 // be emitted into the bytecode stream with space for the operand. 301 unbound_jumps_++; 302 label->set_referrer(current_offset); 303 OperandSize reserved_operand_size = 304 constant_array_builder()->CreateReservedEntry(); 305 DCHECK_NE(Bytecode::kJumpLoop, node->bytecode()); 306 switch (reserved_operand_size) { 307 case OperandSize::kNone: 308 UNREACHABLE(); 309 break; 310 case OperandSize::kByte: 311 node->set_bytecode(node->bytecode(), k8BitJumpPlaceholder); 312 break; 313 case OperandSize::kShort: 314 node->set_bytecode(node->bytecode(), k16BitJumpPlaceholder); 315 break; 316 case OperandSize::kQuad: 317 node->set_bytecode(node->bytecode(), k32BitJumpPlaceholder); 318 break; 319 } 320 } 321 EmitBytecode(node); 322} 323 324} // namespace interpreter 325} // namespace internal 326} // namespace v8 327