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 11 : BytecodeArrayBuilderTest() = default;
28 11 : ~BytecodeArrayBuilderTest() override = default;
29 : };
30 :
31 : using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
32 :
33 15189 : TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
34 1 : 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 2 : 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, NOT_TENURED);
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)
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 : BytecodeLabel start, after_jump1, after_jump2, after_jump3, after_jump4,
284 : after_jump5, after_jump6, after_jump7, after_jump8, after_jump9,
285 : after_jump10;
286 1 : builder.Bind(&start)
287 1 : .Jump(&after_jump1)
288 1 : .Bind(&after_jump1)
289 1 : .JumpIfNull(&after_jump2)
290 1 : .Bind(&after_jump2)
291 1 : .JumpIfNotNull(&after_jump3)
292 1 : .Bind(&after_jump3)
293 1 : .JumpIfUndefined(&after_jump4)
294 1 : .Bind(&after_jump4)
295 1 : .JumpIfNotUndefined(&after_jump5)
296 1 : .Bind(&after_jump5)
297 1 : .JumpIfJSReceiver(&after_jump6)
298 1 : .Bind(&after_jump6)
299 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &after_jump7)
300 1 : .Bind(&after_jump7)
301 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &after_jump8)
302 1 : .Bind(&after_jump8)
303 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &after_jump9)
304 1 : .Bind(&after_jump9)
305 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &after_jump10)
306 1 : .Bind(&after_jump10)
307 1 : .JumpLoop(&start, 0);
308 : }
309 :
310 : // Longer jumps with constant operands
311 11 : BytecodeLabel end[10];
312 : {
313 : BytecodeLabel after_jump;
314 1 : builder.Jump(&end[0])
315 1 : .Bind(&after_jump)
316 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &end[1])
317 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &end[2])
318 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &end[3])
319 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &end[4])
320 1 : .JumpIfNull(&end[5])
321 1 : .JumpIfNotNull(&end[6])
322 1 : .JumpIfUndefined(&end[7])
323 1 : .JumpIfNotUndefined(&end[8])
324 2 : .LoadLiteral(ast_factory.prototype_string())
325 1 : .JumpIfJSReceiver(&end[9]);
326 : }
327 :
328 : // Emit Smi table switch bytecode.
329 1 : BytecodeJumpTable* jump_table = builder.AllocateJumpTable(1, 0);
330 1 : builder.SwitchOnSmiNoFeedback(jump_table).Bind(jump_table, 0);
331 :
332 : // Emit set pending message bytecode.
333 1 : builder.SetPendingMessage();
334 :
335 : // Emit stack check bytecode.
336 1 : builder.StackCheck(0);
337 :
338 : // Emit throw and re-throw in it's own basic block so that the rest of the
339 : // code isn't omitted due to being dead.
340 : BytecodeLabel after_throw;
341 1 : builder.Throw().Bind(&after_throw);
342 : BytecodeLabel after_rethrow;
343 1 : builder.ReThrow().Bind(&after_rethrow);
344 :
345 1 : builder.ForInEnumerate(reg)
346 1 : .ForInPrepare(triple, 1)
347 1 : .ForInContinue(reg, reg)
348 1 : .ForInNext(reg, reg, pair, 1)
349 1 : .ForInStep(reg);
350 :
351 : // Wide constant pool loads
352 257 : for (int i = 0; i < 256; i++) {
353 : // Emit junk in constant pool to force wide constant pool index.
354 256 : builder.LoadLiteral(2.5321 + i);
355 : }
356 1 : builder.LoadLiteral(Smi::FromInt(20000000));
357 1 : const AstRawString* wide_name = ast_factory.GetOneByteString("var_wide_name");
358 :
359 : builder.StoreDataPropertyInLiteral(reg, reg,
360 1 : DataPropertyInLiteralFlag::kNoFlags, 0);
361 :
362 : // Emit wide context operations.
363 1 : builder.LoadContextSlot(reg, 1024, 0, BytecodeArrayBuilder::kMutableSlot)
364 1 : .StoreContextSlot(reg, 1024, 0);
365 :
366 : // Emit wide load / store lookup slots.
367 1 : builder.LoadLookupSlot(wide_name, TypeofMode::NOT_INSIDE_TYPEOF)
368 1 : .LoadLookupSlot(wide_name, TypeofMode::INSIDE_TYPEOF)
369 : .StoreLookupSlot(wide_name, LanguageMode::kSloppy,
370 1 : LookupHoistingMode::kNormal)
371 : .StoreLookupSlot(wide_name, LanguageMode::kSloppy,
372 1 : LookupHoistingMode::kLegacySloppy)
373 : .StoreLookupSlot(wide_name, LanguageMode::kStrict,
374 1 : LookupHoistingMode::kNormal);
375 :
376 : // CreateClosureWide
377 1 : builder.CreateClosure(1000, 321, NOT_TENURED);
378 :
379 : // Emit wide variant of literal creation operations.
380 : builder
381 1 : .CreateRegExpLiteral(ast_factory.GetOneByteString("wide_literal"), 0, 0)
382 1 : .CreateArrayLiteral(0, 0, 0)
383 1 : .CreateEmptyArrayLiteral(0)
384 1 : .CreateArrayFromIterable()
385 1 : .CreateObjectLiteral(0, 0, 0)
386 1 : .CreateEmptyObjectLiteral()
387 1 : .CloneObject(reg, 0, 0);
388 :
389 : // Emit load and store operations for module variables.
390 1 : builder.LoadModuleVariable(-1, 42)
391 1 : .LoadModuleVariable(0, 42)
392 1 : .LoadModuleVariable(1, 42)
393 1 : .StoreModuleVariable(-1, 42)
394 1 : .StoreModuleVariable(0, 42)
395 1 : .StoreModuleVariable(1, 42);
396 :
397 : // Emit generator operations.
398 : {
399 : // We have to skip over suspend because it returns and marks the remaining
400 : // bytecode dead.
401 : BytecodeLabel after_suspend;
402 1 : builder.JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &after_suspend)
403 1 : .SuspendGenerator(reg, reg_list, 0)
404 1 : .Bind(&after_suspend)
405 1 : .ResumeGenerator(reg, reg_list);
406 : }
407 1 : BytecodeJumpTable* gen_jump_table = builder.AllocateJumpTable(1, 0);
408 1 : builder.SwitchOnGeneratorState(reg, gen_jump_table).Bind(gen_jump_table, 0);
409 :
410 : // Intrinsics handled by the interpreter.
411 1 : builder.CallRuntime(Runtime::kInlineIsArray, reg_list);
412 :
413 : // Emit debugger bytecode.
414 1 : builder.Debugger();
415 :
416 : // Emit abort bytecode.
417 : {
418 : BytecodeLabel after;
419 1 : builder.Abort(AbortReason::kOperandIsASmi).Bind(&after);
420 : }
421 :
422 : // Insert dummy ops to force longer jumps.
423 257 : for (int i = 0; i < 256; i++) {
424 256 : builder.Debugger();
425 : }
426 :
427 : // Emit block counter increments.
428 1 : builder.IncBlockCounter(0);
429 :
430 : // Bind labels for long jumps at the very end.
431 11 : for (size_t i = 0; i < arraysize(end); i++) {
432 10 : builder.Bind(&end[i]);
433 : }
434 :
435 : // Return must be the last instruction.
436 1 : builder.Return();
437 :
438 : // Generate BytecodeArray.
439 1 : scope.SetScriptScopeInfo(factory->NewScopeInfo(1));
440 1 : ast_factory.Internalize(isolate());
441 1 : Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
442 2 : CHECK_EQ(the_array->frame_size(),
443 : builder.total_register_count() * kSystemPointerSize);
444 :
445 : // Build scorecard of bytecodes encountered in the BytecodeArray.
446 1 : std::vector<int> scorecard(Bytecodes::ToByte(Bytecode::kLast) + 1);
447 :
448 : Bytecode final_bytecode = Bytecode::kLdaZero;
449 : int i = 0;
450 466 : while (i < the_array->length()) {
451 : uint8_t code = the_array->get(i);
452 928 : scorecard[code] += 1;
453 : final_bytecode = Bytecodes::FromByte(code);
454 : OperandScale operand_scale = OperandScale::kSingle;
455 : int prefix_offset = 0;
456 464 : if (Bytecodes::IsPrefixScalingBytecode(final_bytecode)) {
457 15 : operand_scale = Bytecodes::PrefixBytecodeToOperandScale(final_bytecode);
458 : prefix_offset = 1;
459 15 : code = the_array->get(i + 1);
460 30 : scorecard[code] += 1;
461 : final_bytecode = Bytecodes::FromByte(code);
462 : }
463 464 : i += prefix_offset + Bytecodes::Size(final_bytecode, operand_scale);
464 : }
465 :
466 : // Insert entry for illegal bytecode as this is never willingly emitted.
467 1 : scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1;
468 :
469 : // Bytecode for CollectTypeProfile is only emitted when
470 : // Type Information for DevTools is turned on.
471 1 : scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
472 :
473 : // Check return occurs at the end and only once in the BytecodeArray.
474 1 : CHECK_EQ(final_bytecode, Bytecode::kReturn);
475 2 : CHECK_EQ(scorecard[Bytecodes::ToByte(final_bytecode)], 1);
476 :
477 : #define CHECK_BYTECODE_PRESENT(Name, ...) \
478 : /* Check Bytecode is marked in scorecard, unless it's a debug break */ \
479 : if (!Bytecodes::IsDebugBreak(Bytecode::k##Name)) { \
480 : EXPECT_GE(scorecard[Bytecodes::ToByte(Bytecode::k##Name)], 1); \
481 : }
482 180 : BYTECODE_LIST(CHECK_BYTECODE_PRESENT)
483 : #undef CHECK_BYTECODE_PRESENT
484 1 : }
485 :
486 :
487 15189 : TEST_F(BytecodeArrayBuilderTest, FrameSizesLookGood) {
488 6 : for (int locals = 0; locals < 5; locals++) {
489 15 : for (int temps = 0; temps < 3; temps++) {
490 15 : BytecodeArrayBuilder builder(zone(), 1, locals);
491 : BytecodeRegisterAllocator* allocator(builder.register_allocator());
492 45 : for (int i = 0; i < locals; i++) {
493 30 : builder.LoadLiteral(Smi::zero());
494 30 : builder.StoreAccumulatorInRegister(Register(i));
495 : }
496 15 : for (int i = 0; i < temps; i++) {
497 15 : Register temp = allocator->NewRegister();
498 15 : builder.LoadLiteral(Smi::zero());
499 15 : builder.StoreAccumulatorInRegister(temp);
500 : // Ensure temporaries are used so not optimized away by the
501 : // register optimizer.
502 15 : builder.ToName(temp);
503 : }
504 15 : builder.Return();
505 :
506 15 : Handle<BytecodeArray> the_array = builder.ToBytecodeArray(isolate());
507 15 : int total_registers = locals + temps;
508 30 : CHECK_EQ(the_array->frame_size(), total_registers * kSystemPointerSize);
509 15 : }
510 : }
511 1 : }
512 :
513 :
514 15189 : TEST_F(BytecodeArrayBuilderTest, RegisterValues) {
515 : int index = 1;
516 :
517 : Register the_register(index);
518 1 : CHECK_EQ(the_register.index(), index);
519 :
520 : int actual_operand = the_register.ToOperand();
521 : int actual_index = Register::FromOperand(actual_operand).index();
522 1 : CHECK_EQ(actual_index, index);
523 1 : }
524 :
525 :
526 15189 : TEST_F(BytecodeArrayBuilderTest, Parameters) {
527 1 : BytecodeArrayBuilder builder(zone(), 10, 0);
528 :
529 1 : Register receiver(builder.Receiver());
530 1 : Register param8(builder.Parameter(8));
531 1 : CHECK_EQ(param8.index() - receiver.index(), 9);
532 1 : }
533 :
534 :
535 15189 : TEST_F(BytecodeArrayBuilderTest, Constants) {
536 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
537 : AstValueFactory ast_factory(zone(), isolate()->ast_string_constants(),
538 2 : HashSeed(isolate()));
539 :
540 : double heap_num_1 = 3.14;
541 : double heap_num_2 = 5.2;
542 : double nan = std::numeric_limits<double>::quiet_NaN();
543 1 : const AstRawString* string = ast_factory.GetOneByteString("foo");
544 1 : const AstRawString* string_copy = ast_factory.GetOneByteString("foo");
545 :
546 1 : builder.LoadLiteral(heap_num_1)
547 1 : .LoadLiteral(heap_num_2)
548 1 : .LoadLiteral(string)
549 1 : .LoadLiteral(heap_num_1)
550 1 : .LoadLiteral(heap_num_1)
551 1 : .LoadLiteral(nan)
552 1 : .LoadLiteral(string_copy)
553 1 : .LoadLiteral(heap_num_2)
554 1 : .LoadLiteral(nan)
555 1 : .Return();
556 :
557 1 : ast_factory.Internalize(isolate());
558 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
559 : // Should only have one entry for each identical constant.
560 4 : EXPECT_EQ(4, array->constant_pool()->length());
561 1 : }
562 :
563 15189 : TEST_F(BytecodeArrayBuilderTest, ForwardJumps) {
564 : static const int kFarJumpDistance = 256 + 20;
565 :
566 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
567 :
568 : Register reg(0);
569 : BytecodeLabel far0, far1, far2, far3, far4;
570 : BytecodeLabel near0, near1, near2, near3, near4;
571 : BytecodeLabel after_jump0, after_jump1;
572 :
573 1 : builder.Jump(&near0)
574 1 : .Bind(&after_jump0)
575 1 : .CompareOperation(Token::Value::EQ, reg, 1)
576 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &near1)
577 1 : .CompareOperation(Token::Value::EQ, reg, 2)
578 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &near2)
579 1 : .BinaryOperation(Token::Value::ADD, reg, 1)
580 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &near3)
581 1 : .BinaryOperation(Token::Value::ADD, reg, 2)
582 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &near4)
583 1 : .Bind(&near0)
584 1 : .Bind(&near1)
585 1 : .Bind(&near2)
586 1 : .Bind(&near3)
587 1 : .Bind(&near4)
588 1 : .Jump(&far0)
589 1 : .Bind(&after_jump1)
590 1 : .CompareOperation(Token::Value::EQ, reg, 3)
591 1 : .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &far1)
592 1 : .CompareOperation(Token::Value::EQ, reg, 4)
593 1 : .JumpIfFalse(ToBooleanMode::kAlreadyBoolean, &far2)
594 1 : .BinaryOperation(Token::Value::ADD, reg, 3)
595 1 : .JumpIfTrue(ToBooleanMode::kConvertToBoolean, &far3)
596 1 : .BinaryOperation(Token::Value::ADD, reg, 4)
597 1 : .JumpIfFalse(ToBooleanMode::kConvertToBoolean, &far4);
598 255 : for (int i = 0; i < kFarJumpDistance - 22; i++) {
599 254 : builder.Debugger();
600 : }
601 1 : builder.Bind(&far0).Bind(&far1).Bind(&far2).Bind(&far3).Bind(&far4);
602 1 : builder.Return();
603 :
604 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
605 : DCHECK_EQ(array->length(), 44 + kFarJumpDistance - 22 + 1);
606 :
607 1 : BytecodeArrayIterator iterator(array);
608 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
609 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 22);
610 1 : iterator.Advance();
611 :
612 : // Ignore compare operation.
613 1 : iterator.Advance();
614 :
615 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrue);
616 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 17);
617 1 : iterator.Advance();
618 :
619 : // Ignore compare operation.
620 1 : iterator.Advance();
621 :
622 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalse);
623 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 12);
624 1 : iterator.Advance();
625 :
626 : // Ignore add operation.
627 1 : iterator.Advance();
628 :
629 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrue);
630 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 7);
631 1 : iterator.Advance();
632 :
633 : // Ignore add operation.
634 1 : iterator.Advance();
635 :
636 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanFalse);
637 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
638 1 : iterator.Advance();
639 :
640 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpConstant);
641 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
642 : Smi::FromInt(kFarJumpDistance));
643 1 : iterator.Advance();
644 :
645 : // Ignore compare operation.
646 1 : iterator.Advance();
647 :
648 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfTrueConstant);
649 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
650 : Smi::FromInt(kFarJumpDistance - 5));
651 1 : iterator.Advance();
652 :
653 : // Ignore compare operation.
654 1 : iterator.Advance();
655 :
656 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfFalseConstant);
657 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
658 : Smi::FromInt(kFarJumpDistance - 10));
659 1 : iterator.Advance();
660 :
661 : // Ignore add operation.
662 1 : iterator.Advance();
663 :
664 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpIfToBooleanTrueConstant);
665 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
666 : Smi::FromInt(kFarJumpDistance - 15));
667 1 : iterator.Advance();
668 :
669 : // Ignore add operation.
670 1 : iterator.Advance();
671 :
672 1 : CHECK_EQ(iterator.current_bytecode(),
673 : Bytecode::kJumpIfToBooleanFalseConstant);
674 2 : CHECK_EQ(iterator.GetConstantForIndexOperand(0),
675 : Smi::FromInt(kFarJumpDistance - 20));
676 1 : iterator.Advance();
677 1 : }
678 :
679 :
680 15189 : TEST_F(BytecodeArrayBuilderTest, BackwardJumps) {
681 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
682 :
683 : Register reg(0);
684 :
685 : BytecodeLabel label0;
686 1 : builder.Bind(&label0).JumpLoop(&label0, 0);
687 43 : for (int i = 0; i < 42; i++) {
688 : BytecodeLabel after_jump;
689 42 : builder.JumpLoop(&label0, 0).Bind(&after_jump);
690 : }
691 :
692 : // Add padding to force wide backwards jumps.
693 256 : for (int i = 0; i < 256; i++) {
694 256 : builder.Debugger();
695 : }
696 :
697 1 : builder.JumpLoop(&label0, 0);
698 : BytecodeLabel end;
699 1 : builder.Bind(&end);
700 1 : builder.Return();
701 :
702 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
703 1 : BytecodeArrayIterator iterator(array);
704 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
705 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
706 1 : iterator.Advance();
707 44 : for (unsigned i = 0; i < 42; i++) {
708 42 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
709 42 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
710 : // offset of 3 (because kJumpLoop takes two immediate operands)
711 42 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), i * 3 + 3);
712 42 : iterator.Advance();
713 : }
714 : // Check padding to force wide backwards jumps.
715 256 : for (int i = 0; i < 256; i++) {
716 256 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
717 256 : iterator.Advance();
718 : }
719 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
720 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
721 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 386);
722 1 : iterator.Advance();
723 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
724 1 : iterator.Advance();
725 1 : CHECK(iterator.done());
726 1 : }
727 :
728 15189 : TEST_F(BytecodeArrayBuilderTest, SmallSwitch) {
729 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
730 :
731 : // Small jump table that fits into the single-size constant pool
732 : int small_jump_table_size = 5;
733 : int small_jump_table_base = -2;
734 : BytecodeJumpTable* small_jump_table =
735 1 : builder.AllocateJumpTable(small_jump_table_size, small_jump_table_base);
736 :
737 1 : builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(small_jump_table);
738 6 : for (int i = 0; i < small_jump_table_size; i++) {
739 5 : builder.Bind(small_jump_table, small_jump_table_base + i).Debugger();
740 : }
741 1 : builder.Return();
742 :
743 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
744 1 : BytecodeArrayIterator iterator(array);
745 :
746 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
747 1 : iterator.Advance();
748 :
749 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
750 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kSingle);
751 : {
752 : int i = 0;
753 : int switch_end =
754 1 : iterator.current_offset() + iterator.current_bytecode_size();
755 :
756 6 : for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
757 5 : CHECK_EQ(entry.case_value, small_jump_table_base + i);
758 5 : CHECK_EQ(entry.target_offset, switch_end + i);
759 :
760 5 : i++;
761 : }
762 1 : CHECK_EQ(i, small_jump_table_size);
763 : }
764 1 : iterator.Advance();
765 :
766 6 : for (int i = 0; i < small_jump_table_size; i++) {
767 5 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
768 5 : iterator.Advance();
769 : }
770 :
771 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
772 1 : iterator.Advance();
773 1 : CHECK(iterator.done());
774 1 : }
775 :
776 15189 : TEST_F(BytecodeArrayBuilderTest, WideSwitch) {
777 1 : BytecodeArrayBuilder builder(zone(), 1, 1);
778 :
779 : // Large jump table that requires a wide Switch bytecode.
780 : int large_jump_table_size = 256;
781 : int large_jump_table_base = -10;
782 : BytecodeJumpTable* large_jump_table =
783 1 : builder.AllocateJumpTable(large_jump_table_size, large_jump_table_base);
784 :
785 1 : builder.LoadLiteral(Smi::FromInt(7)).SwitchOnSmiNoFeedback(large_jump_table);
786 257 : for (int i = 0; i < large_jump_table_size; i++) {
787 256 : builder.Bind(large_jump_table, large_jump_table_base + i).Debugger();
788 : }
789 1 : builder.Return();
790 :
791 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
792 1 : BytecodeArrayIterator iterator(array);
793 :
794 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kLdaSmi);
795 1 : iterator.Advance();
796 :
797 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
798 1 : CHECK_EQ(iterator.current_operand_scale(), OperandScale::kDouble);
799 : {
800 : int i = 0;
801 : int switch_end =
802 1 : iterator.current_offset() + iterator.current_bytecode_size();
803 :
804 257 : for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
805 256 : CHECK_EQ(entry.case_value, large_jump_table_base + i);
806 256 : CHECK_EQ(entry.target_offset, switch_end + i);
807 :
808 256 : i++;
809 : }
810 1 : CHECK_EQ(i, large_jump_table_size);
811 : }
812 1 : iterator.Advance();
813 :
814 257 : for (int i = 0; i < large_jump_table_size; i++) {
815 256 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kDebugger);
816 256 : iterator.Advance();
817 : }
818 :
819 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
820 1 : iterator.Advance();
821 1 : CHECK(iterator.done());
822 1 : }
823 :
824 15189 : TEST_F(BytecodeArrayBuilderTest, LabelReuse) {
825 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
826 :
827 : // Labels can only have 1 forward reference, but
828 : // can be referred to mulitple times once bound.
829 : BytecodeLabel label, after_jump0, after_jump1;
830 :
831 1 : builder.Jump(&label)
832 1 : .Bind(&label)
833 1 : .JumpLoop(&label, 0)
834 1 : .Bind(&after_jump0)
835 1 : .JumpLoop(&label, 0)
836 1 : .Bind(&after_jump1)
837 1 : .Return();
838 :
839 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
840 1 : BytecodeArrayIterator iterator(array);
841 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
842 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
843 1 : iterator.Advance();
844 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
845 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
846 1 : iterator.Advance();
847 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
848 1 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
849 1 : iterator.Advance();
850 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
851 1 : iterator.Advance();
852 1 : CHECK(iterator.done());
853 1 : }
854 :
855 :
856 15189 : TEST_F(BytecodeArrayBuilderTest, LabelAddressReuse) {
857 : static const int kRepeats = 3;
858 :
859 1 : BytecodeArrayBuilder builder(zone(), 1, 0);
860 4 : for (int i = 0; i < kRepeats; i++) {
861 : BytecodeLabel label, after_jump0, after_jump1;
862 3 : builder.Jump(&label)
863 3 : .Bind(&label)
864 3 : .JumpLoop(&label, 0)
865 3 : .Bind(&after_jump0)
866 3 : .JumpLoop(&label, 0)
867 3 : .Bind(&after_jump1);
868 : }
869 1 : builder.Return();
870 :
871 1 : Handle<BytecodeArray> array = builder.ToBytecodeArray(isolate());
872 1 : BytecodeArrayIterator iterator(array);
873 4 : for (int i = 0; i < kRepeats; i++) {
874 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJump);
875 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 2);
876 3 : iterator.Advance();
877 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
878 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 0);
879 3 : iterator.Advance();
880 3 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
881 3 : CHECK_EQ(iterator.GetUnsignedImmediateOperand(0), 3);
882 3 : iterator.Advance();
883 : }
884 1 : CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
885 1 : iterator.Advance();
886 1 : CHECK(iterator.done());
887 1 : }
888 :
889 : } // namespace interpreter
890 : } // namespace internal
891 9111 : } // namespace v8
|