Line data Source code
1 : // Copyright 2014 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 <limits>
6 :
7 : #include "src/v8.h"
8 :
9 : #include "src/ast/scopes.h"
10 : #include "src/interpreter/bytecode-array-builder.h"
11 : #include "src/interpreter/bytecode-array-iterator.h"
12 : #include "src/interpreter/bytecode-jump-table.h"
13 : #include "src/interpreter/bytecode-label.h"
14 : #include "src/interpreter/bytecode-register-allocator.h"
15 : #include "src/objects-inl.h"
16 : #include "src/objects/smi.h"
17 : #include "test/unittests/interpreter/bytecode-utils.h"
18 : #include "test/unittests/test-utils.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 : namespace interpreter {
23 :
24 : class BytecodeArrayBuilderTest : public TestWithIsolateAndZone {
25 : public:
26 11 : BytecodeArrayBuilderTest() = default;
27 11 : ~BytecodeArrayBuilderTest() override = default;
28 : };
29 :
30 : using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
31 :
32 15129 : TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
33 1 : FeedbackVectorSpec feedback_spec(zone());
34 2 : BytecodeArrayBuilder builder(zone(), 1, 131, &feedback_spec);
35 : Factory* factory = isolate()->factory();
36 : AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
37 2 : isolate()->heap()->HashSeed());
38 1 : DeclarationScope scope(zone(), &ast_factory);
39 :
40 1 : CHECK_EQ(builder.locals_count(), 131);
41 1 : CHECK_EQ(builder.fixed_register_count(), 131);
42 :
43 : Register reg(0);
44 : Register other(reg.index() + 1);
45 : Register wide(128);
46 : RegisterList empty;
47 1 : RegisterList single = BytecodeUtils::NewRegisterList(0, 1);
48 1 : RegisterList pair = BytecodeUtils::NewRegisterList(0, 2);
49 1 : RegisterList triple = BytecodeUtils::NewRegisterList(0, 3);
50 1 : RegisterList reg_list = BytecodeUtils::NewRegisterList(0, 10);
51 :
52 : // Emit argument creation operations.
53 1 : builder.CreateArguments(CreateArgumentsType::kMappedArguments)
54 1 : .CreateArguments(CreateArgumentsType::kUnmappedArguments)
55 1 : .CreateArguments(CreateArgumentsType::kRestParameter);
56 :
57 : // Emit constant loads.
58 1 : builder.LoadLiteral(Smi::zero())
59 1 : .StoreAccumulatorInRegister(reg)
60 1 : .LoadLiteral(Smi::FromInt(8))
61 : .CompareOperation(Token::Value::EQ, reg,
62 1 : 1) // Prevent peephole optimization
63 : // LdaSmi, Star -> LdrSmi.
64 1 : .StoreAccumulatorInRegister(reg)
65 1 : .LoadLiteral(Smi::FromInt(10000000))
66 1 : .StoreAccumulatorInRegister(reg)
67 2 : .LoadLiteral(ast_factory.GetOneByteString("A constant"))
68 1 : .StoreAccumulatorInRegister(reg)
69 1 : .LoadUndefined()
70 1 : .StoreAccumulatorInRegister(reg)
71 1 : .LoadNull()
72 1 : .StoreAccumulatorInRegister(reg)
73 1 : .LoadTheHole()
74 1 : .StoreAccumulatorInRegister(reg)
75 1 : .LoadTrue()
76 1 : .StoreAccumulatorInRegister(reg)
77 1 : .LoadFalse()
78 1 : .StoreAccumulatorInRegister(wide);
79 :
80 : // Emit Ldar and Star taking care to foil the register optimizer.
81 1 : builder.StackCheck(0)
82 1 : .LoadAccumulatorWithRegister(other)
83 1 : .BinaryOperation(Token::ADD, reg, 1)
84 1 : .StoreAccumulatorInRegister(reg)
85 1 : .LoadNull();
86 :
87 : // Emit register-register transfer.
88 1 : builder.MoveRegister(reg, other);
89 1 : builder.MoveRegister(reg, wide);
90 :
91 : FeedbackSlot load_global_slot =
92 : feedback_spec.AddLoadGlobalICSlot(NOT_INSIDE_TYPEOF);
93 : FeedbackSlot load_global_typeof_slot =
94 : feedback_spec.AddLoadGlobalICSlot(INSIDE_TYPEOF);
95 : FeedbackSlot sloppy_store_global_slot =
96 : feedback_spec.AddStoreGlobalICSlot(LanguageMode::kSloppy);
97 : FeedbackSlot load_slot = feedback_spec.AddLoadICSlot();
98 : FeedbackSlot keyed_load_slot = feedback_spec.AddKeyedLoadICSlot();
99 : FeedbackSlot sloppy_store_slot =
100 : feedback_spec.AddStoreICSlot(LanguageMode::kSloppy);
101 : FeedbackSlot strict_store_slot =
102 : feedback_spec.AddStoreICSlot(LanguageMode::kStrict);
103 : FeedbackSlot sloppy_keyed_store_slot =
104 : feedback_spec.AddKeyedStoreICSlot(LanguageMode::kSloppy);
105 : FeedbackSlot strict_keyed_store_slot =
106 : feedback_spec.AddKeyedStoreICSlot(LanguageMode::kStrict);
107 : FeedbackSlot store_own_slot = feedback_spec.AddStoreOwnICSlot();
108 : FeedbackSlot store_array_element_slot =
109 : feedback_spec.AddStoreInArrayLiteralICSlot();
110 :
111 : // Emit global load / store operations.
112 1 : const AstRawString* name = ast_factory.GetOneByteString("var_name");
113 : builder
114 1 : .LoadGlobal(name, load_global_slot.ToInt(), TypeofMode::NOT_INSIDE_TYPEOF)
115 : .LoadGlobal(name, load_global_typeof_slot.ToInt(),
116 1 : TypeofMode::INSIDE_TYPEOF)
117 1 : .StoreGlobal(name, sloppy_store_global_slot.ToInt());
118 :
119 : // Emit context operations.
120 1 : builder.PushContext(reg)
121 1 : .PopContext(reg)
122 1 : .LoadContextSlot(reg, 1, 0, BytecodeArrayBuilder::kMutableSlot)
123 1 : .StoreContextSlot(reg, 1, 0)
124 1 : .LoadContextSlot(reg, 2, 0, BytecodeArrayBuilder::kImmutableSlot)
125 1 : .StoreContextSlot(reg, 3, 0);
126 :
127 : // Emit context operations which operate on the local context.
128 : builder
129 : .LoadContextSlot(Register::current_context(), 1, 0,
130 1 : BytecodeArrayBuilder::kMutableSlot)
131 2 : .StoreContextSlot(Register::current_context(), 1, 0)
132 : .LoadContextSlot(Register::current_context(), 2, 0,
133 2 : BytecodeArrayBuilder::kImmutableSlot)
134 2 : .StoreContextSlot(Register::current_context(), 3, 0);
135 :
136 : // Emit load / store property operations.
137 1 : builder.LoadNamedProperty(reg, name, load_slot.ToInt())
138 1 : .LoadNamedPropertyNoFeedback(reg, name)
139 1 : .LoadKeyedProperty(reg, keyed_load_slot.ToInt())
140 : .StoreNamedProperty(reg, name, sloppy_store_slot.ToInt(),
141 1 : LanguageMode::kSloppy)
142 1 : .StoreNamedPropertyNoFeedback(reg, name, LanguageMode::kStrict)
143 1 : .StoreNamedPropertyNoFeedback(reg, name, LanguageMode::kSloppy)
144 : .StoreKeyedProperty(reg, reg, sloppy_keyed_store_slot.ToInt(),
145 1 : LanguageMode::kSloppy)
146 : .StoreNamedProperty(reg, name, strict_store_slot.ToInt(),
147 1 : LanguageMode::kStrict)
148 : .StoreKeyedProperty(reg, reg, strict_keyed_store_slot.ToInt(),
149 1 : LanguageMode::kStrict)
150 1 : .StoreNamedOwnProperty(reg, name, store_own_slot.ToInt())
151 1 : .StoreInArrayLiteral(reg, reg, store_array_element_slot.ToInt());
152 :
153 : // Emit load / store lookup slots.
154 1 : builder.LoadLookupSlot(name, TypeofMode::NOT_INSIDE_TYPEOF)
155 1 : .LoadLookupSlot(name, TypeofMode::INSIDE_TYPEOF)
156 1 : .StoreLookupSlot(name, LanguageMode::kSloppy, LookupHoistingMode::kNormal)
157 : .StoreLookupSlot(name, LanguageMode::kSloppy,
158 1 : LookupHoistingMode::kLegacySloppy)
159 : .StoreLookupSlot(name, LanguageMode::kStrict,
160 1 : LookupHoistingMode::kNormal);
161 :
162 : // Emit load / store lookup slots with context fast paths.
163 1 : builder.LoadLookupContextSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
164 1 : .LoadLookupContextSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
165 :
166 : // Emit load / store lookup slots with global fast paths.
167 1 : builder.LoadLookupGlobalSlot(name, TypeofMode::NOT_INSIDE_TYPEOF, 1, 0)
168 1 : .LoadLookupGlobalSlot(name, TypeofMode::INSIDE_TYPEOF, 1, 0);
169 :
170 : // Emit closure operations.
171 1 : builder.CreateClosure(0, 1, NOT_TENURED);
172 :
173 : // Emit create context operation.
174 1 : builder.CreateBlockContext(&scope);
175 1 : builder.CreateCatchContext(reg, &scope);
176 1 : builder.CreateFunctionContext(&scope, 1);
177 1 : builder.CreateEvalContext(&scope, 1);
178 1 : builder.CreateWithContext(reg, &scope);
179 :
180 : // Emit literal creation operations.
181 1 : builder.CreateRegExpLiteral(ast_factory.GetOneByteString("a"), 0, 0);
182 1 : builder.CreateArrayLiteral(0, 0, 0);
183 1 : builder.CreateObjectLiteral(0, 0, 0);
184 :
185 : // Emit tagged template operations.
186 1 : builder.GetTemplateObject(0, 0);
187 :
188 : // Call operations.
189 1 : builder.CallAnyReceiver(reg, reg_list, 1)
190 1 : .CallProperty(reg, reg_list, 1)
191 1 : .CallProperty(reg, single, 1)
192 1 : .CallProperty(reg, pair, 1)
193 1 : .CallProperty(reg, triple, 1)
194 1 : .CallUndefinedReceiver(reg, reg_list, 1)
195 1 : .CallUndefinedReceiver(reg, empty, 1)
196 1 : .CallUndefinedReceiver(reg, single, 1)
197 1 : .CallUndefinedReceiver(reg, pair, 1)
198 1 : .CallRuntime(Runtime::kIsArray, reg)
199 1 : .CallRuntimeForPair(Runtime::kLoadLookupSlotForCall, reg_list, pair)
200 1 : .CallJSRuntime(Context::OBJECT_CREATE, reg_list)
201 1 : .CallWithSpread(reg, reg_list, 1)
202 1 : .CallNoFeedback(reg, reg_list);
203 :
204 : // Emit binary operator invocations.
205 1 : builder.BinaryOperation(Token::Value::ADD, reg, 1)
206 1 : .BinaryOperation(Token::Value::SUB, reg, 2)
207 1 : .BinaryOperation(Token::Value::MUL, reg, 3)
208 1 : .BinaryOperation(Token::Value::DIV, reg, 4)
209 1 : .BinaryOperation(Token::Value::MOD, reg, 5)
210 1 : .BinaryOperation(Token::Value::EXP, reg, 6);
211 :
212 : // Emit bitwise operator invocations
213 1 : builder.BinaryOperation(Token::Value::BIT_OR, reg, 6)
214 1 : .BinaryOperation(Token::Value::BIT_XOR, reg, 7)
215 1 : .BinaryOperation(Token::Value::BIT_AND, reg, 8);
216 :
217 : // Emit shift operator invocations
218 1 : builder.BinaryOperation(Token::Value::SHL, reg, 9)
219 1 : .BinaryOperation(Token::Value::SAR, reg, 10)
220 1 : .BinaryOperation(Token::Value::SHR, reg, 11);
221 :
222 : // Emit Smi binary operations.
223 1 : builder.BinaryOperationSmiLiteral(Token::Value::ADD, Smi::FromInt(42), 2)
224 1 : .BinaryOperationSmiLiteral(Token::Value::SUB, Smi::FromInt(42), 2)
225 1 : .BinaryOperationSmiLiteral(Token::Value::MUL, Smi::FromInt(42), 2)
226 1 : .BinaryOperationSmiLiteral(Token::Value::DIV, Smi::FromInt(42), 2)
227 1 : .BinaryOperationSmiLiteral(Token::Value::MOD, Smi::FromInt(42), 2)
228 1 : .BinaryOperationSmiLiteral(Token::Value::EXP, Smi::FromInt(42), 2)
229 1 : .BinaryOperationSmiLiteral(Token::Value::BIT_OR, Smi::FromInt(42), 2)
230 1 : .BinaryOperationSmiLiteral(Token::Value::BIT_XOR, Smi::FromInt(42), 2)
231 1 : .BinaryOperationSmiLiteral(Token::Value::BIT_AND, Smi::FromInt(42), 2)
232 1 : .BinaryOperationSmiLiteral(Token::Value::SHL, Smi::FromInt(42), 2)
233 1 : .BinaryOperationSmiLiteral(Token::Value::SAR, Smi::FromInt(42), 2)
234 1 : .BinaryOperationSmiLiteral(Token::Value::SHR, Smi::FromInt(42), 2);
235 :
236 : // Emit unary and count operator invocations.
237 1 : builder.UnaryOperation(Token::Value::INC, 1)
238 1 : .UnaryOperation(Token::Value::DEC, 1)
239 1 : .UnaryOperation(Token::Value::ADD, 1)
240 1 : .UnaryOperation(Token::Value::SUB, 1)
241 1 : .UnaryOperation(Token::Value::BIT_NOT, 1);
242 :
243 : // Emit unary operator invocations.
244 1 : builder.LogicalNot(ToBooleanMode::kConvertToBoolean)
245 1 : .LogicalNot(ToBooleanMode::kAlreadyBoolean)
246 1 : .TypeOf();
247 :
248 : // Emit delete
249 1 : builder.Delete(reg, LanguageMode::kSloppy).Delete(reg, LanguageMode::kStrict);
250 :
251 : // Emit construct.
252 1 : builder.Construct(reg, reg_list, 1).ConstructWithSpread(reg, reg_list, 1);
253 :
254 : // Emit test operator invocations.
255 1 : builder.CompareOperation(Token::Value::EQ, reg, 1)
256 1 : .CompareOperation(Token::Value::EQ_STRICT, reg, 2)
257 1 : .CompareOperation(Token::Value::LT, reg, 3)
258 1 : .CompareOperation(Token::Value::GT, reg, 4)
259 1 : .CompareOperation(Token::Value::LTE, reg, 5)
260 1 : .CompareOperation(Token::Value::GTE, reg, 6)
261 1 : .CompareTypeOf(TestTypeOfFlags::LiteralFlag::kNumber)
262 1 : .CompareOperation(Token::Value::INSTANCEOF, reg, 7)
263 1 : .CompareOperation(Token::Value::IN, reg)
264 1 : .CompareReference(reg)
265 1 : .CompareUndetectable()
266 1 : .CompareUndefined()
267 1 : .CompareNull();
268 :
269 : // Emit conversion operator invocations.
270 1 : builder.ToNumber(1).ToNumeric(1).ToObject(reg).ToName(reg).ToString();
271 :
272 : // Emit GetSuperConstructor.
273 1 : builder.GetSuperConstructor(reg);
274 :
275 : // Hole checks.
276 1 : builder.ThrowReferenceErrorIfHole(name)
277 1 : .ThrowSuperAlreadyCalledIfNotHole()
278 1 : .ThrowSuperNotCalledIfHole();
279 :
280 : // Short jumps with Imm8 operands
281 : {
282 : BytecodeLabel start, after_jump1, after_jump2, after_jump3, after_jump4,
283 : after_jump5, after_jump6, after_jump7, after_jump8, after_jump9,
284 : after_jump10;
285 1 : builder.Bind(&start)
286 1 : .Jump(&after_jump1)
287 1 : .Bind(&after_jump1)
288 1 : .JumpIfNull(&after_jump2)
289 1 : .Bind(&after_jump2)
290 1 : .JumpIfNotNull(&after_jump3)
291 1 : .Bind(&after_jump3)
292 1 : .JumpIfUndefined(&after_jump4)
293 1 : .Bind(&after_jump4)
294 1 : .JumpIfNotUndefined(&after_jump5)
295 1 : .Bind(&after_jump5)
296 1 : .JumpIfJSReceiver(&after_jump6)
297 1 : .Bind(&after_jump6)
298 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &after_jump7)
299 1 : .Bind(&after_jump7)
300 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &after_jump8)
301 1 : .Bind(&after_jump8)
302 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &after_jump9)
303 1 : .Bind(&after_jump9)
304 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &after_jump10)
305 1 : .Bind(&after_jump10)
306 1 : .JumpLoop(&start, 0);
307 : }
308 :
309 : // Longer jumps with constant operands
310 11 : BytecodeLabel end[10];
311 : {
312 : BytecodeLabel after_jump;
313 1 : builder.Jump(&end[0])
314 1 : .Bind(&after_jump)
315 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &end[1])
316 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &end[2])
317 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &end[3])
318 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &end[4])
319 1 : .JumpIfNull(&end[5])
320 1 : .JumpIfNotNull(&end[6])
321 1 : .JumpIfUndefined(&end[7])
322 1 : .JumpIfNotUndefined(&end[8])
323 2 : .LoadLiteral(ast_factory.prototype_string())
324 1 : .JumpIfJSReceiver(&end[9]);
325 : }
326 :
327 : // Emit Smi table switch bytecode.
328 1 : BytecodeJumpTable* jump_table = builder.AllocateJumpTable(1, 0);
329 1 : builder.SwitchOnSmiNoFeedback(jump_table).Bind(jump_table, 0);
330 :
331 : // Emit set pending message bytecode.
332 1 : builder.SetPendingMessage();
333 :
334 : // Emit stack check bytecode.
335 1 : builder.StackCheck(0);
336 :
337 : // Emit throw and re-throw in it's own basic block so that the rest of the
338 : // code isn't omitted due to being dead.
339 : BytecodeLabel after_throw;
340 1 : builder.Throw().Bind(&after_throw);
341 : BytecodeLabel after_rethrow;
342 1 : builder.ReThrow().Bind(&after_rethrow);
343 :
344 1 : builder.ForInEnumerate(reg)
345 1 : .ForInPrepare(triple, 1)
346 1 : .ForInContinue(reg, reg)
347 1 : .ForInNext(reg, reg, pair, 1)
348 1 : .ForInStep(reg);
349 :
350 : // Wide constant pool loads
351 257 : for (int i = 0; i < 256; i++) {
352 : // Emit junk in constant pool to force wide constant pool index.
353 256 : builder.LoadLiteral(2.5321 + i);
354 : }
355 1 : builder.LoadLiteral(Smi::FromInt(20000000));
356 1 : const AstRawString* wide_name = ast_factory.GetOneByteString("var_wide_name");
357 :
358 : builder.StoreDataPropertyInLiteral(reg, reg,
359 1 : DataPropertyInLiteralFlag::kNoFlags, 0);
360 :
361 : // Emit wide context operations.
362 1 : builder.LoadContextSlot(reg, 1024, 0, BytecodeArrayBuilder::kMutableSlot)
363 1 : .StoreContextSlot(reg, 1024, 0);
364 :
365 : // Emit wide load / store lookup slots.
366 1 : builder.LoadLookupSlot(wide_name, TypeofMode::NOT_INSIDE_TYPEOF)
367 1 : .LoadLookupSlot(wide_name, TypeofMode::INSIDE_TYPEOF)
368 : .StoreLookupSlot(wide_name, LanguageMode::kSloppy,
369 1 : LookupHoistingMode::kNormal)
370 : .StoreLookupSlot(wide_name, LanguageMode::kSloppy,
371 1 : LookupHoistingMode::kLegacySloppy)
372 : .StoreLookupSlot(wide_name, LanguageMode::kStrict,
373 1 : LookupHoistingMode::kNormal);
374 :
375 : // CreateClosureWide
376 1 : builder.CreateClosure(1000, 321, NOT_TENURED);
377 :
378 : // Emit wide variant of literal creation operations.
379 : builder
380 1 : .CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0)
381 1 : .CreateArrayLiteral(0, 0, 0)
382 1 : .CreateEmptyArrayLiteral(0)
383 1 : .CreateArrayFromIterable()
384 1 : .CreateObjectLiteral(0, 0, 0)
385 1 : .CreateEmptyObjectLiteral()
386 1 : .CloneObject(reg, 0, 0);
387 :
388 : // Emit load and store operations for module variables.
389 1 : builder.LoadModuleVariable(-1, 42)
390 1 : .LoadModuleVariable(0, 42)
391 1 : .LoadModuleVariable(1, 42)
392 1 : .StoreModuleVariable(-1, 42)
393 1 : .StoreModuleVariable(0, 42)
394 1 : .StoreModuleVariable(1, 42);
395 :
396 : // Emit generator operations.
397 : {
398 : // We have to skip over suspend because it returns and marks the remaining
399 : // bytecode dead.
400 : BytecodeLabel after_suspend;
401 1 : builder.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &after_suspend)
402 1 : .SuspendGenerator(reg, reg_list, 0)
403 1 : .Bind(&after_suspend)
404 1 : .ResumeGenerator(reg, reg_list);
405 : }
406 1 : BytecodeJumpTable* gen_jump_table = builder.AllocateJumpTable(1, 0);
407 1 : builder.SwitchOnGeneratorState(reg, gen_jump_table).Bind(gen_jump_table, 0);
408 :
409 : // Intrinsics handled by the interpreter.
410 1 : builder.CallRuntime(Runtime::kInlineIsArray, reg_list);
411 :
412 : // Emit debugger bytecode.
413 1 : builder.Debugger();
414 :
415 : // Emit abort bytecode.
416 : {
417 : BytecodeLabel after;
418 1 : builder.Abort(AbortReason::kOperandIsASmi).Bind(&after);
419 : }
420 :
421 : // Insert dummy ops to force longer jumps.
422 257 : for (int i = 0; i < 256; i++) {
423 256 : builder.Debugger();
424 : }
425 :
426 : // Emit block counter increments.
427 1 : builder.IncBlockCounter(0);
428 :
429 : // Bind labels for long jumps at the very end.
430 11 : for (size_t i = 0; i < arraysize(end); i++) {
431 10 : builder.Bind(&end[i]);
432 : }
433 :
434 : // Return must be the last instruction.
435 1 : builder.Return();
436 :
437 : // Generate BytecodeArray.
438 1 : scope.SetScriptScopeInfo(factory->NewScopeInfo(1));
439 1 : ast_factory.Internalize(isolate());
440 1 : Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
441 2 : CHECK_EQ(the_array->frame_size(),
442 : builder.total_register_count() * kPointerSize);
443 :
444 : // Build scorecard of bytecodes encountered in the BytecodeArray.
445 1 : std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
446 :
447 : Bytecode final_bytecode = Bytecode::kLdaZero;
448 : int i = 0;
449 466 : while (i < the_array->length()) {
450 : uint8_t code = the_array->get(i);
451 928 : scorecard[code] += 1;
452 : final_bytecode = Bytecodes::FromByte(code);
453 : OperandScale operand_scale = OperandScale::kSingle;
454 : int prefix_offset = 0;
455 464 : if (Bytecodes::IsPrefixScalingBytecode(final_bytecode)) {
456 15 : operand_scale = Bytecodes::PrefixBytecodeToOperandScale(final_bytecode);
457 : prefix_offset = 1;
458 15 : code = the_array->get(i + 1);
459 30 : scorecard[code] += 1;
460 : final_bytecode = Bytecodes::FromByte(code);
461 : }
462 464 : i += prefix_offset + Bytecodes::Size(final_bytecode, operand_scale);
463 : }
464 :
465 : // Insert entry for illegal bytecode as this is never willingly emitted.
466 1 : scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1;
467 :
468 : // Bytecode for CollectTypeProfile is only emitted when
469 : // Type Information for DevTools is turned on.
470 1 : scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
471 :
472 : // Check return occurs at the end and only once in the BytecodeArray.
473 1 : CHECK_EQ(final_bytecode, Bytecode::kReturn);
474 2 : CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
475 :
476 : #define CHECK_BYTECODE_PRESENT(Name, ...) \
477 : /* Check Bytecode is marked in scorecard, unless it's a debug break */ \
478 : if (!Bytecodes::IsDebugBreak(Bytecode::k##Name)) { \
479 : EXPECT_GE(scorecard[Bytecodes::ToByte(Bytecode::k##Name)], 1); \
480 : }
481 180 : BYTECODE_LIST(CHECK_BYTECODE_PRESENT)
482 : #undef CHECK_BYTECODE_PRESENT
483 1 : }
484 :
485 :
486 15129 : TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
487 6 : for (int locals = 0; locals < 5; locals++) {
488 15 : for (int temps = 0; temps < 3; temps++) {
489 15 : BytecodeArrayBuilder builder(zone(), 1, locals);
490 : BytecodeRegisterAllocator* allocator(builder.register_allocator());
491 45 : for (int i = 0; i < locals; i++) {
492 30 : builder.LoadLiteral(Smi::zero());
493 30 : builder.StoreAccumulatorInRegister(Register(i));
494 : }
495 15 : for (int i = 0; i < temps; i++) {
496 15 : Register temp = allocator->NewRegister();
497 15 : builder.LoadLiteral(Smi::zero());
498 15 : builder.StoreAccumulatorInRegister(temp);
499 : // Ensure temporaries are used so not optimized away by the
500 : // register optimizer.
501 15 : builder.ToName(temp);
502 : }
503 15 : builder.Return();
504 :
505 15 : Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
506 15 : int total_registers = locals + temps;
507 30 : CHECK_EQ(the_array->frame_size(), total_registers * kPointerSize);
508 15 : }
509 : }
510 1 : }
511 :
512 :
513 15129 : TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
514 : int index = 1;
515 :
516 : Register the_register(index);
517 1 : CHECK_EQ(the_register.index(), index);
518 :
519 : int actual_operand = the_register.ToOperand();
520 : int actual_index = Register::FromOperand(actual_operand).index();
521 1 : CHECK_EQ(actual_index, index);
522 1 : }
523 :
524 :
525 15129 : TEST_F(BytecodeArrayBuilderTest, Parameters) {
526 1 : BytecodeArrayBuilder builder(zone(), 10, 0);
527 :
528 1 : Register receiver(builder.Receiver());
529 1 : Register param8(builder.Parameter(8));
530 1 : CHECK_EQ(param8.index() - receiver.index(), 9);
531 1 : }
532 :
533 :
534 15129 : TEST_F(BytecodeArrayBuilderTest, Constants) {
535 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
536 : AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
537 2 : isolate()->heap()->HashSeed());
538 :
539 : double heap_num_1 = 3.14;
540 : double heap_num_2 = 5.2;
541 : double nan = std::numeric_limits<double>::quiet_NaN();
542 1 : const AstRawString* string = ast_factory.GetOneByteString("foo");
543 1 : const AstRawString* string_copy = ast_factory.GetOneByteString("foo");
544 :
545 1 : builder.LoadLiteral(heap_num_1)
546 1 : .LoadLiteral(heap_num_2)
547 1 : .LoadLiteral(string)
548 1 : .LoadLiteral(heap_num_1)
549 1 : .LoadLiteral(heap_num_1)
550 1 : .LoadLiteral(nan)
551 1 : .LoadLiteral(string_copy)
552 1 : .LoadLiteral(heap_num_2)
553 1 : .LoadLiteral(nan)
554 1 : .Return();
555 :
556 1 : ast_factory.Internalize(isolate());
557 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
558 : // Should only have one entry for each identical constant.
559 4 : EXPECT_EQ(4, array->constant_pool()->length());
560 1 : }
561 :
562 15129 : TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
563 : static const int kFarJumpDistance = 256 + 20;
564 :
565 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
566 :
567 : Register reg(0);
568 : BytecodeLabel far0, far1, far2, far3, far4;
569 : BytecodeLabel near0, near1, near2, near3, near4;
570 : BytecodeLabel after_jump0, after_jump1;
571 :
572 1 : builder.Jump(&near0)
573 1 : .Bind(&after_jump0)
574 1 : .CompareOperation(Token::Value::EQ, reg, 1)
575 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &near1)
576 1 : .CompareOperation(Token::Value::EQ, reg, 2)
577 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &near2)
578 1 : .BinaryOperation(Token::Value::ADD, reg, 1)
579 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &near3)
580 1 : .BinaryOperation(Token::Value::ADD, reg, 2)
581 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &near4)
582 1 : .Bind(&near0)
583 1 : .Bind(&near1)
584 1 : .Bind(&near2)
585 1 : .Bind(&near3)
586 1 : .Bind(&near4)
587 1 : .Jump(&far0)
588 1 : .Bind(&after_jump1)
589 1 : .CompareOperation(Token::Value::EQ, reg, 3)
590 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &far1)
591 1 : .CompareOperation(Token::Value::EQ, reg, 4)
592 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &far2)
593 1 : .BinaryOperation(Token::Value::ADD, reg, 3)
594 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &far3)
595 1 : .BinaryOperation(Token::Value::ADD, reg, 4)
596 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &far4);
597 255 : for (int i = 0; i < kFarJumpDistance - 22; i++) {
598 254 : builder.Debugger();
599 : }
600 1 : builder.Bind(&far0).Bind(&far1).Bind(&far2).Bind(&far3).Bind(&far4);
601 1 : builder.Return();
602 :
603 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
604 : DCHECK_EQ(array->length(), 44 + kFarJumpDistance - 22 + 1);
605 :
606 1 : BytecodeArrayIterator iterator(array);
607 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
608 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 22);
609 1 : iterator.Advance();
610 :
611 : // Ignore compare operation.
612 1 : iterator.Advance();
613 :
614 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
615 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 17);
616 1 : iterator.Advance();
617 :
618 : // Ignore compare operation.
619 1 : iterator.Advance();
620 :
621 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
622 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 12);
623 1 : iterator.Advance();
624 :
625 : // Ignore add operation.
626 1 : iterator.Advance();
627 :
628 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
629 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 7);
630 1 : iterator.Advance();
631 :
632 : // Ignore add operation.
633 1 : iterator.Advance();
634 :
635 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
636 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
637 1 : iterator.Advance();
638 :
639 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
640 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
641 : Smi::FromInt(kFarJumpDistance));
642 1 : iterator.Advance();
643 :
644 : // Ignore compare operation.
645 1 : iterator.Advance();
646 :
647 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
648 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
649 : Smi::FromInt(kFarJumpDistance - 5));
650 1 : iterator.Advance();
651 :
652 : // Ignore compare operation.
653 1 : iterator.Advance();
654 :
655 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
656 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
657 : Smi::FromInt(kFarJumpDistance - 10));
658 1 : iterator.Advance();
659 :
660 : // Ignore add operation.
661 1 : iterator.Advance();
662 :
663 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
664 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
665 : Smi::FromInt(kFarJumpDistance - 15));
666 1 : iterator.Advance();
667 :
668 : // Ignore add operation.
669 1 : iterator.Advance();
670 :
671 1 : CHECK_EQ(iterator.current_bytecode(),
672 : Bytecode::kJumpIfToBooleanFalseConstant);
673 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
674 : Smi::FromInt(kFarJumpDistance - 20));
675 1 : iterator.Advance();
676 1 : }
677 :
678 :
679 15129 : TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
680 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
681 :
682 : Register reg(0);
683 :
684 : BytecodeLabel label0;
685 1 : builder.Bind(&label0).JumpLoop(&label0, 0);
686 43 : for (int i = 0; i < 42; i++) {
687 : BytecodeLabel after_jump;
688 42 : builder.JumpLoop(&label0, 0).Bind(&after_jump);
689 : }
690 :
691 : // Add padding to force wide backwards jumps.
692 256 : for (int i = 0; i < 256; i++) {
693 256 : builder.Debugger();
694 : }
695 :
696 1 : builder.JumpLoop(&label0, 0);
697 : BytecodeLabel end;
698 1 : builder.Bind(&end);
699 1 : builder.Return();
700 :
701 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
702 1 : BytecodeArrayIterator iterator(array);
703 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
704 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
705 1 : iterator.Advance();
706 44 : for (unsigned i = 0; i < 42; i++) {
707 42 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
708 42 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
709 : // offset of 3 (because kJumpLoop takes two immediate operands)
710 42 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), i * 3 + 3);
711 42 : iterator.Advance();
712 : }
713 : // Check padding to force wide backwards jumps.
714 256 : for (int i = 0; i < 256; i++) {
715 256 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
716 256 : iterator.Advance();
717 : }
718 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
719 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
720 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 386);
721 1 : iterator.Advance();
722 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
723 1 : iterator.Advance();
724 1 : CHECK(iterator.done());
725 1 : }
726 :
727 15129 : TEST_F(BytecodeArrayBuilderTest, SmallSwitch) {
728 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
729 :
730 : // Small jump table that fits into the single-size constant pool
731 : int small_jump_table_size = 5;
732 : int small_jump_table_base = -2;
733 : BytecodeJumpTable* small_jump_table =
734 1 : builder.AllocateJumpTable(small_jump_table_size, small_jump_table_base);
735 :
736 1 : builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(small_jump_table);
737 6 : for (int i = 0; i < small_jump_table_size; i++) {
738 5 : builder.Bind(small_jump_table, small_jump_table_base + i).Debugger();
739 : }
740 1 : builder.Return();
741 :
742 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
743 1 : BytecodeArrayIterator iterator(array);
744 :
745 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
746 1 : iterator.Advance();
747 :
748 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
749 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
750 : {
751 : int i = 0;
752 : int switch_end =
753 1 : iterator.current_offset() + iterator.current_bytecode_size();
754 :
755 6 : for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
756 5 : CHECK_EQ(entry.case_value, small_jump_table_base + i);
757 5 : CHECK_EQ(entry.target_offset, switch_end + i);
758 :
759 5 : i++;
760 : }
761 1 : CHECK_EQ(i, small_jump_table_size);
762 : }
763 1 : iterator.Advance();
764 :
765 6 : for (int i = 0; i < small_jump_table_size; i++) {
766 5 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
767 5 : iterator.Advance();
768 : }
769 :
770 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
771 1 : iterator.Advance();
772 1 : CHECK(iterator.done());
773 1 : }
774 :
775 15129 : TEST_F(BytecodeArrayBuilderTest, WideSwitch) {
776 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
777 :
778 : // Large jump table that requires a wide Switch bytecode.
779 : int large_jump_table_size = 256;
780 : int large_jump_table_base = -10;
781 : BytecodeJumpTable* large_jump_table =
782 1 : builder.AllocateJumpTable(large_jump_table_size, large_jump_table_base);
783 :
784 1 : builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(large_jump_table);
785 257 : for (int i = 0; i < large_jump_table_size; i++) {
786 256 : builder.Bind(large_jump_table, large_jump_table_base + i).Debugger();
787 : }
788 1 : builder.Return();
789 :
790 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
791 1 : BytecodeArrayIterator iterator(array);
792 :
793 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
794 1 : iterator.Advance();
795 :
796 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
797 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
798 : {
799 : int i = 0;
800 : int switch_end =
801 1 : iterator.current_offset() + iterator.current_bytecode_size();
802 :
803 257 : for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
804 256 : CHECK_EQ(entry.case_value, large_jump_table_base + i);
805 256 : CHECK_EQ(entry.target_offset, switch_end + i);
806 :
807 256 : i++;
808 : }
809 1 : CHECK_EQ(i, large_jump_table_size);
810 : }
811 1 : iterator.Advance();
812 :
813 257 : for (int i = 0; i < large_jump_table_size; i++) {
814 256 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
815 256 : iterator.Advance();
816 : }
817 :
818 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
819 1 : iterator.Advance();
820 1 : CHECK(iterator.done());
821 1 : }
822 :
823 15129 : TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
824 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
825 :
826 : // Labels can only have 1 forward reference, but
827 : // can be referred to mulitple times once bound.
828 : BytecodeLabel label, after_jump0, after_jump1;
829 :
830 1 : builder.Jump(&label)
831 1 : .Bind(&label)
832 1 : .JumpLoop(&label, 0)
833 1 : .Bind(&after_jump0)
834 1 : .JumpLoop(&label, 0)
835 1 : .Bind(&after_jump1)
836 1 : .Return();
837 :
838 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
839 1 : BytecodeArrayIterator iterator(array);
840 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
841 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
842 1 : iterator.Advance();
843 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
844 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
845 1 : iterator.Advance();
846 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
847 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
848 1 : iterator.Advance();
849 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
850 1 : iterator.Advance();
851 1 : CHECK(iterator.done());
852 1 : }
853 :
854 :
855 15129 : TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
856 : static const int kRepeats = 3;
857 :
858 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
859 4 : for (int i = 0; i < kRepeats; i++) {
860 : BytecodeLabel label, after_jump0, after_jump1;
861 3 : builder.Jump(&label)
862 3 : .Bind(&label)
863 3 : .JumpLoop(&label, 0)
864 3 : .Bind(&after_jump0)
865 3 : .JumpLoop(&label, 0)
866 3 : .Bind(&after_jump1);
867 : }
868 1 : builder.Return();
869 :
870 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
871 1 : BytecodeArrayIterator iterator(array);
872 4 : for (int i = 0; i < kRepeats; i++) {
873 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
874 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
875 3 : iterator.Advance();
876 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
877 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
878 3 : iterator.Advance();
879 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
880 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
881 3 : iterator.Advance();
882 : }
883 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
884 1 : iterator.Advance();
885 1 : CHECK(iterator.done());
886 1 : }
887 :
888 : } // namespace interpreter
889 : } // namespace internal
890 9075 : } // namespace v8
|