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