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