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-inl.h" 8 #include "src/interpreter/bytecode-jump-table.h" 9 #include "src/interpreter/bytecode-label.h" 10 #include "src/interpreter/bytecode-node.h" 11 #include "src/interpreter/bytecode-register.h" 12 #include "src/interpreter/bytecode-source-info.h" 13 #include "src/interpreter/constant-array-builder.h" 14 #include "src/log.h" 15 #include "src/objects-inl.h" 16 17 namespace v8 { 18 namespace internal { 19 namespace interpreter { 20 21 STATIC_CONST_MEMBER_DEFINITION const size_t 22 BytecodeArrayWriter::kMaxSizeOfPackedBytecode; 23 24 BytecodeArrayWriter::BytecodeArrayWriter( 25 Zone* zone, ConstantArrayBuilder* constant_array_builder, 26 SourcePositionTableBuilder::RecordingMode source_position_mode) 27 : bytecodes_(zone), 28 unbound_jumps_(0), 29 source_position_table_builder_(source_position_mode), 30 constant_array_builder_(constant_array_builder), 31 last_bytecode_(Bytecode::kIllegal), 32 last_bytecode_offset_(0), 33 last_bytecode_had_source_info_(false), 34 elide_noneffectful_bytecodes_(FLAG_ignition_elide_noneffectful_bytecodes), 35 exit_seen_in_block_(false) { 36 bytecodes_.reserve(512); // Derived via experimentation. 37 } 38 39 Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray( 40 Isolate* isolate, int register_count, int parameter_count, 41 Handle<ByteArray> handler_table) { 42 DCHECK_EQ(0, unbound_jumps_); 43 44 int bytecode_size = static_cast<int>(bytecodes()->size()); 45 int frame_size = register_count * kPointerSize; 46 Handle<FixedArray> constant_pool = 47 constant_array_builder()->ToFixedArray(isolate); 48 Handle<ByteArray> source_position_table = 49 source_position_table_builder()->ToSourcePositionTable(isolate); 50 Handle<BytecodeArray> bytecode_array = isolate->factory()->NewBytecodeArray( 51 bytecode_size, &bytecodes()->front(), frame_size, parameter_count, 52 constant_pool); 53 bytecode_array->set_handler_table(*handler_table); 54 bytecode_array->set_source_position_table(*source_position_table); 55 LOG_CODE_EVENT(isolate, CodeLinePosInfoRecordEvent( 56 bytecode_array->GetFirstBytecodeAddress(), 57 *source_position_table)); 58 return bytecode_array; 59 } 60 61 void BytecodeArrayWriter::Write(BytecodeNode* node) { 62 DCHECK(!Bytecodes::IsJump(node->bytecode())); 63 64 if (exit_seen_in_block_) return; // Don't emit dead code. 65 UpdateExitSeenInBlock(node->bytecode()); 66 MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid()); 67 68 UpdateSourcePositionTable(node); 69 EmitBytecode(node); 70 } 71 72 void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) { 73 DCHECK(Bytecodes::IsJump(node->bytecode())); 74 75 // TODO(rmcilroy): For forward jumps we could also mark the label as dead, 76 // thereby avoiding emitting dead code when we bind the label. 77 if (exit_seen_in_block_) return; // Don't emit dead code. 78 UpdateExitSeenInBlock(node->bytecode()); 79 MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid()); 80 81 UpdateSourcePositionTable(node); 82 EmitJump(node, label); 83 } 84 85 void BytecodeArrayWriter::WriteSwitch(BytecodeNode* node, 86 BytecodeJumpTable* jump_table) { 87 DCHECK(Bytecodes::IsSwitch(node->bytecode())); 88 89 // TODO(rmcilroy): For jump tables we could also mark the table as dead, 90 // thereby avoiding emitting dead code when we bind the entries. 91 if (exit_seen_in_block_) return; // Don't emit dead code. 92 UpdateExitSeenInBlock(node->bytecode()); 93 MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid()); 94 95 UpdateSourcePositionTable(node); 96 EmitSwitch(node, jump_table); 97 } 98 99 void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) { 100 size_t current_offset = bytecodes()->size(); 101 if (label->is_forward_target()) { 102 // An earlier jump instruction refers to this label. Update it's location. 103 PatchJump(current_offset, label->offset()); 104 // Now treat as if the label will only be back referred to. 105 } 106 label->bind_to(current_offset); 107 InvalidateLastBytecode(); 108 exit_seen_in_block_ = false; // Starting a new basic block. 109 } 110 111 void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target, 112 BytecodeLabel* label) { 113 DCHECK(!label->is_bound()); 114 DCHECK(target.is_bound()); 115 if (label->is_forward_target()) { 116 // An earlier jump instruction refers to this label. Update it's location. 117 PatchJump(target.offset(), label->offset()); 118 // Now treat as if the label will only be back referred to. 119 } 120 label->bind_to(target.offset()); 121 InvalidateLastBytecode(); 122 // exit_seen_in_block_ was reset when target was bound, so shouldn't be 123 // changed here. 124 } 125 126 void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table, 127 int case_value) { 128 DCHECK(!jump_table->is_bound(case_value)); 129 130 size_t current_offset = bytecodes()->size(); 131 size_t relative_jump = current_offset - jump_table->switch_bytecode_offset(); 132 133 constant_array_builder()->SetJumpTableSmi( 134 jump_table->ConstantPoolEntryFor(case_value), 135 Smi::FromInt(static_cast<int>(relative_jump))); 136 jump_table->mark_bound(case_value); 137 138 InvalidateLastBytecode(); 139 exit_seen_in_block_ = false; // Starting a new basic block. 140 } 141 142 void BytecodeArrayWriter::UpdateSourcePositionTable( 143 const BytecodeNode* const node) { 144 int bytecode_offset = static_cast<int>(bytecodes()->size()); 145 const BytecodeSourceInfo& source_info = node->source_info(); 146 if (source_info.is_valid()) { 147 source_position_table_builder()->AddPosition( 148 bytecode_offset, SourcePosition(source_info.source_position()), 149 source_info.is_statement()); 150 } 151 } 152 153 void BytecodeArrayWriter::UpdateExitSeenInBlock(Bytecode bytecode) { 154 switch (bytecode) { 155 case Bytecode::kReturn: 156 case Bytecode::kThrow: 157 case Bytecode::kReThrow: 158 case Bytecode::kAbort: 159 case Bytecode::kJump: 160 case Bytecode::kJumpConstant: 161 case Bytecode::kSuspendGenerator: 162 exit_seen_in_block_ = true; 163 break; 164 default: 165 break; 166 } 167 } 168 169 void BytecodeArrayWriter::MaybeElideLastBytecode(Bytecode next_bytecode, 170 bool has_source_info) { 171 if (!elide_noneffectful_bytecodes_) return; 172 173 // If the last bytecode loaded the accumulator without any external effect, 174 // and the next bytecode clobbers this load without reading the accumulator, 175 // then the previous bytecode can be elided as it has no effect. 176 if (Bytecodes::IsAccumulatorLoadWithoutEffects(last_bytecode_) && 177 Bytecodes::GetAccumulatorUse(next_bytecode) == AccumulatorUse::kWrite && 178 (!last_bytecode_had_source_info_ || !has_source_info)) { 179 DCHECK_GT(bytecodes()->size(), last_bytecode_offset_); 180 bytecodes()->resize(last_bytecode_offset_); 181 // If the last bytecode had source info we will transfer the source info 182 // to this bytecode. 183 has_source_info |= last_bytecode_had_source_info_; 184 } 185 last_bytecode_ = next_bytecode; 186 last_bytecode_had_source_info_ = has_source_info; 187 last_bytecode_offset_ = bytecodes()->size(); 188 } 189 190 void BytecodeArrayWriter::InvalidateLastBytecode() { 191 last_bytecode_ = Bytecode::kIllegal; 192 } 193 194 void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) { 195 DCHECK_NE(node->bytecode(), Bytecode::kIllegal); 196 197 Bytecode bytecode = node->bytecode(); 198 OperandScale operand_scale = node->operand_scale(); 199 200 if (operand_scale != OperandScale::kSingle) { 201 Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale); 202 bytecodes()->push_back(Bytecodes::ToByte(prefix)); 203 } 204 bytecodes()->push_back(Bytecodes::ToByte(bytecode)); 205 206 const uint32_t* const operands = node->operands(); 207 const int operand_count = node->operand_count(); 208 const OperandSize* operand_sizes = 209 Bytecodes::GetOperandSizes(bytecode, operand_scale); 210 for (int i = 0; i < operand_count; ++i) { 211 switch (operand_sizes[i]) { 212 case OperandSize::kNone: 213 UNREACHABLE(); 214 break; 215 case OperandSize::kByte: 216 bytecodes()->push_back(static_cast<uint8_t>(operands[i])); 217 break; 218 case OperandSize::kShort: { 219 uint16_t operand = static_cast<uint16_t>(operands[i]); 220 const uint8_t* raw_operand = reinterpret_cast<const uint8_t*>(&operand); 221 bytecodes()->push_back(raw_operand[0]); 222 bytecodes()->push_back(raw_operand[1]); 223 break; 224 } 225 case OperandSize::kQuad: { 226 const uint8_t* raw_operand = 227 reinterpret_cast<const uint8_t*>(&operands[i]); 228 bytecodes()->push_back(raw_operand[0]); 229 bytecodes()->push_back(raw_operand[1]); 230 bytecodes()->push_back(raw_operand[2]); 231 bytecodes()->push_back(raw_operand[3]); 232 break; 233 } 234 } 235 } 236 } 237 238 // static 239 Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) { 240 switch (jump_bytecode) { 241 case Bytecode::kJump: 242 return Bytecode::kJumpConstant; 243 case Bytecode::kJumpIfTrue: 244 return Bytecode::kJumpIfTrueConstant; 245 case Bytecode::kJumpIfFalse: 246 return Bytecode::kJumpIfFalseConstant; 247 case Bytecode::kJumpIfToBooleanTrue: 248 return Bytecode::kJumpIfToBooleanTrueConstant; 249 case Bytecode::kJumpIfToBooleanFalse: 250 return Bytecode::kJumpIfToBooleanFalseConstant; 251 case Bytecode::kJumpIfNull: 252 return Bytecode::kJumpIfNullConstant; 253 case Bytecode::kJumpIfNotNull: 254 return Bytecode::kJumpIfNotNullConstant; 255 case Bytecode::kJumpIfUndefined: 256 return Bytecode::kJumpIfUndefinedConstant; 257 case Bytecode::kJumpIfNotUndefined: 258 return Bytecode::kJumpIfNotUndefinedConstant; 259 case Bytecode::kJumpIfJSReceiver: 260 return Bytecode::kJumpIfJSReceiverConstant; 261 default: 262 UNREACHABLE(); 263 } 264 } 265 266 void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location, 267 int delta) { 268 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 269 DCHECK(Bytecodes::IsForwardJump(jump_bytecode)); 270 DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode)); 271 DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm); 272 DCHECK_GT(delta, 0); 273 size_t operand_location = jump_location + 1; 274 DCHECK_EQ(bytecodes()->at(operand_location), k8BitJumpPlaceholder); 275 if (Bytecodes::ScaleForUnsignedOperand(delta) == OperandScale::kSingle) { 276 // The jump fits within the range of an UImm8 operand, so cancel 277 // the reservation and jump directly. 278 constant_array_builder()->DiscardReservedEntry(OperandSize::kByte); 279 bytecodes()->at(operand_location) = static_cast<uint8_t>(delta); 280 } else { 281 // The jump does not fit within the range of an UImm8 operand, so 282 // commit reservation putting the offset into the constant pool, 283 // and update the jump instruction and operand. 284 size_t entry = constant_array_builder()->CommitReservedEntry( 285 OperandSize::kByte, Smi::FromInt(delta)); 286 DCHECK_EQ(Bytecodes::SizeForUnsignedOperand(static_cast<uint32_t>(entry)), 287 OperandSize::kByte); 288 jump_bytecode = GetJumpWithConstantOperand(jump_bytecode); 289 bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode); 290 bytecodes()->at(operand_location) = static_cast<uint8_t>(entry); 291 } 292 } 293 294 void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location, 295 int delta) { 296 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 297 DCHECK(Bytecodes::IsForwardJump(jump_bytecode)); 298 DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode)); 299 DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm); 300 DCHECK_GT(delta, 0); 301 size_t operand_location = jump_location + 1; 302 uint8_t operand_bytes[2]; 303 if (Bytecodes::ScaleForUnsignedOperand(delta) <= OperandScale::kDouble) { 304 // The jump fits within the range of an Imm16 operand, so cancel 305 // the reservation and jump directly. 306 constant_array_builder()->DiscardReservedEntry(OperandSize::kShort); 307 WriteUnalignedUInt16(reinterpret_cast<Address>(operand_bytes), 308 static_cast<uint16_t>(delta)); 309 } else { 310 // The jump does not fit within the range of an Imm16 operand, so 311 // commit reservation putting the offset into the constant pool, 312 // and update the jump instruction and operand. 313 size_t entry = constant_array_builder()->CommitReservedEntry( 314 OperandSize::kShort, Smi::FromInt(delta)); 315 jump_bytecode = GetJumpWithConstantOperand(jump_bytecode); 316 bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode); 317 WriteUnalignedUInt16(reinterpret_cast<Address>(operand_bytes), 318 static_cast<uint16_t>(entry)); 319 } 320 DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder && 321 bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder); 322 bytecodes()->at(operand_location++) = operand_bytes[0]; 323 bytecodes()->at(operand_location) = operand_bytes[1]; 324 } 325 326 void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location, 327 int delta) { 328 DCHECK(Bytecodes::IsJumpImmediate( 329 Bytecodes::FromByte(bytecodes()->at(jump_location)))); 330 constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad); 331 uint8_t operand_bytes[4]; 332 WriteUnalignedUInt32(reinterpret_cast<Address>(operand_bytes), 333 static_cast<uint32_t>(delta)); 334 size_t operand_location = jump_location + 1; 335 DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder && 336 bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder && 337 bytecodes()->at(operand_location + 2) == k8BitJumpPlaceholder && 338 bytecodes()->at(operand_location + 3) == k8BitJumpPlaceholder); 339 bytecodes()->at(operand_location++) = operand_bytes[0]; 340 bytecodes()->at(operand_location++) = operand_bytes[1]; 341 bytecodes()->at(operand_location++) = operand_bytes[2]; 342 bytecodes()->at(operand_location) = operand_bytes[3]; 343 } 344 345 void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) { 346 Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location)); 347 int delta = static_cast<int>(jump_target - jump_location); 348 int prefix_offset = 0; 349 OperandScale operand_scale = OperandScale::kSingle; 350 if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) { 351 // If a prefix scaling bytecode is emitted the target offset is one 352 // less than the case of no prefix scaling bytecode. 353 delta -= 1; 354 prefix_offset = 1; 355 operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode); 356 jump_bytecode = 357 Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset)); 358 } 359 360 DCHECK(Bytecodes::IsJump(jump_bytecode)); 361 switch (operand_scale) { 362 case OperandScale::kSingle: 363 PatchJumpWith8BitOperand(jump_location, delta); 364 break; 365 case OperandScale::kDouble: 366 PatchJumpWith16BitOperand(jump_location + prefix_offset, delta); 367 break; 368 case OperandScale::kQuadruple: 369 PatchJumpWith32BitOperand(jump_location + prefix_offset, delta); 370 break; 371 default: 372 UNREACHABLE(); 373 } 374 unbound_jumps_--; 375 } 376 377 void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) { 378 DCHECK(Bytecodes::IsJump(node->bytecode())); 379 DCHECK_EQ(0u, node->operand(0)); 380 381 size_t current_offset = bytecodes()->size(); 382 383 if (label->is_bound()) { 384 CHECK_GE(current_offset, label->offset()); 385 CHECK_LE(current_offset, static_cast<size_t>(kMaxUInt32)); 386 // Label has been bound already so this is a backwards jump. 387 uint32_t delta = static_cast<uint32_t>(current_offset - label->offset()); 388 OperandScale operand_scale = Bytecodes::ScaleForUnsignedOperand(delta); 389 if (operand_scale > OperandScale::kSingle) { 390 // Adjust for scaling byte prefix for wide jump offset. 391 delta += 1; 392 } 393 DCHECK_EQ(Bytecode::kJumpLoop, node->bytecode()); 394 node->update_operand0(delta); 395 } else { 396 // The label has not yet been bound so this is a forward reference 397 // that will be patched when the label is bound. We create a 398 // reservation in the constant pool so the jump can be patched 399 // when the label is bound. The reservation means the maximum size 400 // of the operand for the constant is known and the jump can 401 // be emitted into the bytecode stream with space for the operand. 402 unbound_jumps_++; 403 label->set_referrer(current_offset); 404 OperandSize reserved_operand_size = 405 constant_array_builder()->CreateReservedEntry(); 406 DCHECK_NE(Bytecode::kJumpLoop, node->bytecode()); 407 switch (reserved_operand_size) { 408 case OperandSize::kNone: 409 UNREACHABLE(); 410 break; 411 case OperandSize::kByte: 412 node->update_operand0(k8BitJumpPlaceholder); 413 break; 414 case OperandSize::kShort: 415 node->update_operand0(k16BitJumpPlaceholder); 416 break; 417 case OperandSize::kQuad: 418 node->update_operand0(k32BitJumpPlaceholder); 419 break; 420 } 421 } 422 EmitBytecode(node); 423 } 424 425 void BytecodeArrayWriter::EmitSwitch(BytecodeNode* node, 426 BytecodeJumpTable* jump_table) { 427 DCHECK(Bytecodes::IsSwitch(node->bytecode())); 428 429 size_t current_offset = bytecodes()->size(); 430 if (node->operand_scale() > OperandScale::kSingle) { 431 // Adjust for scaling byte prefix. 432 current_offset += 1; 433 } 434 jump_table->set_switch_bytecode_offset(current_offset); 435 436 EmitBytecode(node); 437 } 438 439 } // namespace interpreter 440 } // namespace internal 441 } // namespace v8 442