@@ -259,6 +259,10 @@ void MethodGenerationContext::removeLastBytecodes(size_t numBytecodes) {
259
259
bytecode.erase (bytecode.end () - bytesToRemove, bytecode.end ());
260
260
}
261
261
262
+ bool MethodGenerationContext::hasOneLiteralBlockArgument () {
263
+ return lastBytecodeIs (0 , BC_PUSH_BLOCK);
264
+ }
265
+
262
266
bool MethodGenerationContext::hasTwoLiteralBlockArguments () {
263
267
if (!lastBytecodeIs (0 , BC_PUSH_BLOCK)) {
264
268
return false ;
@@ -280,6 +284,16 @@ vm_oop_t MethodGenerationContext::getLastBlockMethodAndFreeLiteral(
280
284
return block;
281
285
}
282
286
287
+ vm_oop_t MethodGenerationContext::extractBlockMethodAndRemoveBytecode () {
288
+ uint8_t blockLitIdx = bytecode.at (bytecode.size () - 1 );
289
+
290
+ vm_oop_t toBeInlined = getLastBlockMethodAndFreeLiteral (blockLitIdx);
291
+
292
+ removeLastBytecodes (1 );
293
+
294
+ return toBeInlined;
295
+ }
296
+
283
297
std::tuple<vm_oop_t , vm_oop_t >
284
298
MethodGenerationContext::extractBlockMethodsAndRemoveBytecodes () {
285
299
uint8_t block1LitIdx = bytecode.at (bytecode.size () - 3 );
@@ -294,6 +308,74 @@ MethodGenerationContext::extractBlockMethodsAndRemoveBytecodes() {
294
308
return {toBeInlined1, toBeInlined2};
295
309
}
296
310
311
+ bool MethodGenerationContext::InlineIfTrueOrIfFalse (bool isIfTrue) {
312
+ // HACK: We do assume that the receiver on the stack is a boolean,
313
+ // HACK: similar to the IfTrueIfFalseNode.
314
+ // HACK: We don't support anything but booleans at the moment.
315
+ assert (Bytecode::GetBytecodeLength (BC_PUSH_BLOCK) == 2 );
316
+ if (!hasOneLiteralBlockArgument ()) {
317
+ return false ;
318
+ }
319
+
320
+ VMMethod* toBeInlined =
321
+ static_cast <VMMethod*>(extractBlockMethodAndRemoveBytecode ());
322
+
323
+ size_t jumpOffsetIdxToSkipBody =
324
+ EmitJumpOnBoolWithDummyOffset (*this , isIfTrue, false );
325
+
326
+ isCurrentlyInliningABlock = true ;
327
+
328
+ toBeInlined->InlineInto (*this );
329
+ PatchJumpOffsetToPointToNextInstruction (jumpOffsetIdxToSkipBody);
330
+
331
+ // with the jumping, it's best to prevent any subsequent optimizations here
332
+ // otherwise we may not have the correct jump target
333
+ resetLastBytecodeBuffer ();
334
+
335
+ return true ;
336
+ }
337
+
338
+ bool MethodGenerationContext::InlineIfTrueFalse (bool isIfTrue) {
339
+ // HACK: We do assume that the receiver on the stack is a boolean,
340
+ // HACK: similar to the IfTrueIfFalseNode.
341
+ // HACK: We don't support anything but booleans at the moment.
342
+
343
+ if (!hasTwoLiteralBlockArguments ()) {
344
+ return false ;
345
+ }
346
+
347
+ assert (Bytecode::GetBytecodeLength (BC_PUSH_BLOCK) == 2 );
348
+
349
+ std::tuple<vm_oop_t , vm_oop_t > methods =
350
+ extractBlockMethodsAndRemoveBytecodes ();
351
+ VMMethod* condMethod = static_cast <VMMethod*>(std::get<0 >(methods));
352
+ VMMethod* bodyMethod = static_cast <VMMethod*>(std::get<1 >(methods));
353
+
354
+ size_t jumpOffsetIdxToSkipTrueBranch =
355
+ EmitJumpOnBoolWithDummyOffset (*this , isIfTrue, true );
356
+
357
+ isCurrentlyInliningABlock = true ;
358
+ condMethod->InlineInto (*this );
359
+
360
+ size_t jumpOffsetIdxToSkipFalseBranch = EmitJumpWithDumyOffset (*this );
361
+
362
+ PatchJumpOffsetToPointToNextInstruction (jumpOffsetIdxToSkipTrueBranch);
363
+
364
+ // prevent optimizations between blocks to avoid issues with jump targets
365
+ resetLastBytecodeBuffer ();
366
+
367
+ bodyMethod->InlineInto (*this );
368
+
369
+ isCurrentlyInliningABlock = false ;
370
+
371
+ PatchJumpOffsetToPointToNextInstruction (jumpOffsetIdxToSkipFalseBranch);
372
+
373
+ // prevent optimizations messing with the final jump target
374
+ resetLastBytecodeBuffer ();
375
+
376
+ return true ;
377
+ }
378
+
297
379
bool MethodGenerationContext::InlineWhile (Parser& parser, bool isWhileTrue) {
298
380
if (!hasTwoLiteralBlockArguments ()) {
299
381
return false ;
@@ -324,6 +406,39 @@ bool MethodGenerationContext::InlineWhile(Parser& parser, bool isWhileTrue) {
324
406
return true ;
325
407
}
326
408
409
+ bool MethodGenerationContext::InlineAndOr (bool isOr) {
410
+ // HACK: We do assume that the receiver on the stack is a boolean,
411
+ // HACK: similar to the IfTrueIfFalseNode.
412
+ // HACK: We don't support anything but booleans at the moment.
413
+
414
+ assert (Bytecode::GetBytecodeLength (BC_PUSH_BLOCK) == 2 );
415
+ if (!hasOneLiteralBlockArgument ()) {
416
+ return false ;
417
+ }
418
+
419
+ VMMethod* toBeInlined =
420
+ static_cast <VMMethod*>(extractBlockMethodAndRemoveBytecode ());
421
+
422
+ size_t jumpOffsetIdxToSkipBranch =
423
+ EmitJumpOnBoolWithDummyOffset (*this , !isOr, true );
424
+
425
+ isCurrentlyInliningABlock = true ;
426
+ toBeInlined->InlineInto (*this );
427
+ isCurrentlyInliningABlock = false ;
428
+
429
+ size_t jumpOffsetIdxToSkipPushTrue = EmitJumpWithDumyOffset (*this );
430
+
431
+ PatchJumpOffsetToPointToNextInstruction (jumpOffsetIdxToSkipBranch);
432
+ EmitPUSHCONSTANT (*this ,
433
+ isOr ? load_ptr (trueObject) : load_ptr (falseObject));
434
+
435
+ PatchJumpOffsetToPointToNextInstruction (jumpOffsetIdxToSkipPushTrue);
436
+
437
+ resetLastBytecodeBuffer ();
438
+
439
+ return true ;
440
+ }
441
+
327
442
void MethodGenerationContext::CompleteLexicalScope () {
328
443
lexicalScope = new LexicalScope (
329
444
outerGenc == nullptr ? nullptr : outerGenc->lexicalScope , arguments,
0 commit comments