Coverage Report

Created: 2025-06-24 06:43

/src/hermes/lib/BCGen/HBC/Passes.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include "hermes/BCGen/HBC/Passes.h"
9
10
#include "hermes/BCGen/BCOpt.h"
11
#include "hermes/BCGen/HBC/BytecodeGenerator.h"
12
#include "hermes/BCGen/HBC/BytecodeStream.h"
13
#include "hermes/BCGen/HBC/HBC.h"
14
#include "hermes/BCGen/HBC/ISel.h"
15
#include "hermes/BCGen/Lowering.h"
16
17
#include "llvh/ADT/SetVector.h"
18
19
#define DEBUG_TYPE "hbc-backend"
20
21
namespace hermes {
22
namespace hbc {
23
24
namespace {
25
/// In blockToFix, change all incoming Phi values from previousBlock to instead
26
/// come from newBlock.
27
void updateIncomingPhiValues(
28
    BasicBlock *blockToFix,
29
    BasicBlock *previousBlock,
30
0
    BasicBlock *newBlock) {
31
0
  for (auto &inst : *blockToFix) {
32
0
    auto *phi = llvh::dyn_cast<PhiInst>(&inst);
33
0
    if (!phi)
34
0
      return;
35
36
0
    for (int i = 0, e = phi->getNumEntries(); i < e; i++) {
37
0
      auto entry = phi->getEntry(i);
38
0
      if (entry.second == previousBlock) {
39
0
        phi->updateEntry(i, entry.first, newBlock);
40
0
      }
41
0
    }
42
0
  }
43
0
}
44
// Get the next instruction(s) after this Instruction. Creates branches as
45
// necessary.
46
llvh::SmallVector<Instruction *, 4> getInsertionPointsAfter(
47
    IRBuilder &builder,
48
292k
    Instruction *inst) {
49
292k
  llvh::SmallVector<Instruction *, 4> points;
50
51
292k
  if (!llvh::isa<TerminatorInst>(inst)) {
52
    // Easy case for non-terminators. Just return the next inst.
53
292k
    auto pos = inst->getIterator();
54
292k
    pos++;
55
292k
    points.push_back(&*pos);
56
292k
    return points;
57
292k
  }
58
59
0
  for (int i = 0, e = inst->getNumOperands(); i < e; i++) {
60
0
    BasicBlock *target = llvh::dyn_cast<BasicBlock>(inst->getOperand(i));
61
0
    if (!target)
62
0
      continue;
63
64
0
    auto *detour = builder.createBasicBlock(target->getParent());
65
0
    builder.setInsertionBlock(detour);
66
0
    auto *bounce = builder.createBranchInst(target);
67
0
    inst->setOperand(detour, i);
68
0
    updateIncomingPhiValues(target, inst->getParent(), detour);
69
0
    points.push_back(bounce);
70
0
  }
71
0
  return points;
72
292k
}
73
74
/// Update the insertion point of the builder to the "entry insertion point"
75
/// of the function, which is where we insert new lowered instructions that must
76
/// execute on entry.
77
56.1k
void updateToEntryInsertionPoint(IRBuilder &builder, Function *F) {
78
56.1k
  auto &BB = F->front();
79
56.1k
  auto it = BB.begin();
80
56.1k
  auto end = BB.end();
81
  // Skip all HBCCreateEnvironmentInst.
82
112k
  while (it != end && llvh::isa<HBCCreateEnvironmentInst>(*it))
83
56.1k
    ++it;
84
85
56.1k
  builder.setInsertionPoint(&*it);
86
56.1k
}
87
88
} // namespace
89
90
921k
bool LoadConstants::operandMustBeLiteral(Instruction *Inst, unsigned opIndex) {
91
  // HBCLoadConstInst is meant to load a constant
92
921k
  if (llvh::isa<HBCLoadConstInst>(Inst))
93
116
    return true;
94
95
  // The operand of HBCLoadParamInst is a literal index.
96
921k
  if (llvh::isa<HBCLoadParamInst>(Inst))
97
0
    return true;
98
99
921k
  if (llvh::isa<HBCAllocObjectFromBufferInst>(Inst))
100
0
    return true;
101
102
  // All operands of AllocArrayInst are literals.
103
921k
  if (llvh::isa<AllocArrayInst>(Inst))
104
109k
    return true;
105
106
812k
  if (llvh::isa<AllocObjectInst>(Inst)) {
107
    // The AllocObjectInst::SizeIdx is a literal.
108
215
    if (opIndex == AllocObjectInst::SizeIdx)
109
215
      return true;
110
    // AllocObjectInst::ParentObjectIdx is a literal if it is the EmptySentinel.
111
0
    if (opIndex == AllocObjectInst::ParentObjectIdx &&
112
0
        llvh::isa<EmptySentinel>(Inst->getOperand(opIndex)))
113
0
      return true;
114
115
0
    return false;
116
0
  }
117
118
  // SwitchInst's rest of the operands are case values,
119
  // hence they will stay as constant.
120
812k
  if (llvh::isa<SwitchInst>(Inst) && opIndex > 0)
121
0
    return true;
122
123
  // StoreOwnPropertyInst and StoreNewOwnPropertyInst.
124
812k
  if (auto *SOP = llvh::dyn_cast<StoreOwnPropertyInst>(Inst)) {
125
306k
    if (opIndex == StoreOwnPropertyInst::PropertyIdx) {
126
102k
      if (llvh::isa<StoreNewOwnPropertyInst>(Inst)) {
127
        // In StoreNewOwnPropertyInst the property name must be a literal.
128
0
        return true;
129
0
      }
130
131
      // If the propery is a LiteralNumber, the property is enumerable, and it
132
      // is a valid array index, it is coming from an array initialization and
133
      // we will emit it as PutByIndex.
134
102k
      if (auto *LN = llvh::dyn_cast<LiteralNumber>(Inst->getOperand(opIndex))) {
135
102k
        if (SOP->getIsEnumerable() && LN->convertToArrayIndex().hasValue())
136
102k
          return true;
137
102k
      }
138
102k
    }
139
140
    // StoreOwnPropertyInst's isEnumerable is a boolean constant.
141
204k
    if (opIndex == StoreOwnPropertyInst::IsEnumerableIdx)
142
102k
      return true;
143
144
102k
    return false;
145
204k
  }
146
147
  // If StorePropertyInst's property ID is a LiteralString, we will keep it
148
  // untouched and emit try_put_by_id eventually.
149
506k
  if (llvh::isa<StorePropertyInst>(Inst) &&
150
506k
      opIndex == StorePropertyInst::PropertyIdx &&
151
506k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
152
1.08k
    return true;
153
154
  // If LoadPropertyInst's property ID is a LiteralString, we will keep it
155
  // untouched and emit try_put_by_id eventually.
156
504k
  if (llvh::isa<LoadPropertyInst>(Inst) &&
157
504k
      opIndex == LoadPropertyInst::PropertyIdx &&
158
504k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
159
94.4k
    return true;
160
161
  // If DeletePropertyInst's property ID is a LiteralString, we will keep it
162
  // untouched and emit try_put_by_id eventually.
163
410k
  if (llvh::isa<DeletePropertyInst>(Inst) &&
164
410k
      opIndex == DeletePropertyInst::PropertyIdx &&
165
410k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
166
0
    return true;
167
168
  // StoreGetterSetterInst's isEnumerable is a boolean constant.
169
410k
  if (llvh::isa<StoreGetterSetterInst>(Inst) &&
170
410k
      opIndex == StoreGetterSetterInst::IsEnumerableIdx)
171
0
    return true;
172
173
  // Both pattern and flags operands of the CreateRegExpInst
174
  // are literal strings.
175
410k
  if (llvh::isa<CreateRegExpInst>(Inst))
176
46
    return true;
177
178
410k
  if (llvh::isa<SwitchImmInst>(Inst) &&
179
410k
      (opIndex == SwitchImmInst::MinValueIdx ||
180
0
       opIndex == SwitchImmInst::SizeIdx ||
181
0
       opIndex >= SwitchImmInst::FirstCaseIdx))
182
0
    return true;
183
184
  /// CallBuiltin's callee and "this" should always be literals.
185
410k
  if (llvh::isa<CallBuiltinInst>(Inst) &&
186
410k
      (opIndex == CallBuiltinInst::CalleeIdx ||
187
123k
       opIndex == CallBuiltinInst::ThisIdx))
188
1.03k
    return true;
189
190
  /// Call's new.target must be literal if it is undefined (well, it doesn't,
191
  /// but an undefined new.target won't be emitted to bytecode, hence it doesn't
192
  /// need to be loaded).
193
409k
  if (llvh::isa<CallInst>(Inst) && opIndex == CallInst::NewTargetIdx)
194
1.05k
    return true;
195
196
  /// GetBuiltinClosureInst's builtin index is always literal.
197
408k
  if (llvh::isa<GetBuiltinClosureInst>(Inst) &&
198
408k
      opIndex == GetBuiltinClosureInst::BuiltinIndexIdx)
199
0
    return true;
200
201
#ifdef HERMES_RUN_WASM
202
  /// CallIntrinsic's IntrinsicIndexIdx should always be literals.
203
  if (llvh::isa<CallIntrinsicInst>(Inst) &&
204
      (opIndex == CallIntrinsicInst::IntrinsicIndexIdx))
205
    return true;
206
#endif
207
208
408k
  if (llvh::isa<IteratorCloseInst>(Inst) &&
209
408k
      opIndex == IteratorCloseInst::IgnoreInnerExceptionIdx) {
210
2.73k
    return true;
211
2.73k
  }
212
213
405k
  if (llvh::isa<ThrowIfHasRestrictedGlobalPropertyInst>(Inst) &&
214
405k
      opIndex == ThrowIfHasRestrictedGlobalPropertyInst::PropertyIdx) {
215
0
    return true;
216
0
  }
217
  // DirectEvalInst's isStrict is a boolean constant.
218
405k
  if (llvh::isa<DirectEvalInst>(Inst) &&
219
405k
      opIndex == DirectEvalInst::IsStrictIdx) {
220
0
    return true;
221
0
  }
222
223
405k
  return false;
224
405k
}
225
226
28.0k
bool LoadConstants::runOnFunction(Function *F) {
227
28.0k
  IRBuilder builder(F);
228
28.0k
  bool changed = false;
229
230
  /// Inserts and returns a load instruction for \p literal before \p where.
231
507k
  auto createLoadLiteral = [&builder](Literal *literal, Instruction *where) {
232
507k
    builder.setInsertionPoint(where);
233
507k
    return llvh::isa<GlobalObject>(literal)
234
507k
        ? cast<Instruction>(builder.createHBCGetGlobalObjectInst())
235
507k
        : cast<Instruction>(builder.createHBCLoadConstInst(literal));
236
507k
  };
237
238
129k
  for (BasicBlock &BB : *F) {
239
590k
    for (auto &I : BB) {
240
590k
      if (auto *phi = llvh::dyn_cast<PhiInst>(&I)) {
241
        // Since PhiInsts must always be at the start of a basic block, we have
242
        // to insert the load instruction in the predecessor. This lowering is
243
        // sub-optimal: for conditional branches, the load constant operation
244
        // will be performed before the branch decides which path to take.
245
2.40k
        for (unsigned i = 0, e = phi->getNumEntries(); i < e; ++i) {
246
1.60k
          auto [val, bb] = phi->getEntry(i);
247
1.60k
          if (auto *literal = llvh::dyn_cast<Literal>(val)) {
248
116
            auto *load = createLoadLiteral(literal, bb->getTerminator());
249
116
            phi->updateEntry(i, load, bb);
250
116
            changed = true;
251
116
          }
252
1.60k
        }
253
800
        continue;
254
800
      }
255
      // For all other instructions, insert load constants right before the they
256
      // are needed. This minimizes their live range and therefore reduces
257
      // register pressure. CodeMotion and CSE can later hoist and deduplicate
258
      // them.
259
2.17M
      for (unsigned i = 0, e = I.getNumOperands(); i < e; ++i) {
260
1.58M
        if (auto *literal = llvh::dyn_cast<Literal>(I.getOperand(i))) {
261
921k
          if (!operandMustBeLiteral(&I, i)) {
262
507k
            auto *load = createLoadLiteral(literal, &I);
263
507k
            I.setOperand(load, i);
264
507k
            changed = true;
265
507k
          }
266
921k
        }
267
1.58M
      }
268
589k
    }
269
129k
  }
270
28.0k
  return changed;
271
28.0k
}
272
273
28.0k
bool LoadParameters::runOnFunction(Function *F) {
274
28.0k
  IRBuilder builder(F);
275
28.0k
  bool changed = false;
276
277
28.0k
  updateToEntryInsertionPoint(builder, F);
278
279
  // Index of 0 is the "this" parameter.
280
28.0k
  unsigned index = 1;
281
28.0k
  for (Parameter *p : F->getParameters()) {
282
27.6k
    auto *load =
283
27.6k
        builder.createHBCLoadParamInst(builder.getLiteralNumber(index));
284
27.6k
    p->replaceAllUsesWith(load);
285
27.6k
    index++;
286
27.6k
    changed = true;
287
27.6k
  }
288
289
  // Lower accesses to "this".
290
28.0k
  auto *thisParam = F->getThisParameter();
291
28.0k
  if (thisParam && thisParam->hasUsers()) {
292
    // In strict mode just use param 0 directly. In non-strict, we must coerce
293
    // it to an object.
294
6
    Value *getThisInst = F->isStrictMode()
295
6
        ? cast<Value>(
296
0
              builder.createHBCLoadParamInst(builder.getLiteralNumber(0)))
297
6
        : cast<Value>(builder.createHBCGetThisNSInst());
298
6
    thisParam->replaceAllUsesWith(getThisInst);
299
6
    changed = true;
300
6
  }
301
28.0k
  return changed;
302
28.0k
}
303
304
ScopeCreationInst *LowerLoadStoreFrameInst::getScope(
305
    IRBuilder &builder,
306
    Variable *var,
307
28.0k
    ScopeCreationInst *environment) {
308
28.0k
  if (var->getParent() == environment->getCreatedScopeDesc()) {
309
    // Var's scope belongs to the current function.
310
28.0k
    assert(
311
28.0k
        var->getParent()->getFunction() == builder.getFunction() &&
312
28.0k
        "Scope should only be found if var is not captured from another "
313
28.0k
        "function");
314
28.0k
    return environment;
315
28.0k
  }
316
317
37
  auto *environmentWithParent =
318
37
      llvh::dyn_cast<NestedScopeCreationInst>(environment);
319
37
  if (!environmentWithParent) {
320
    // Failed to find the variable in the current Function -- the first scope in
321
    // a Function is always an non-NestedScopeCreationInst.
322
37
    assert(
323
37
        var->getParent()->getFunction() != builder.getFunction() &&
324
37
        "Failed to find scope in current function for local variable.");
325
37
    return builder.createHBCResolveEnvironment(
326
37
        environment->getCreatedScopeDesc(), var->getParent());
327
37
  }
328
329
  // Keep looking.
330
0
  return getScope(builder, var, environmentWithParent->getParentScope());
331
37
}
332
333
28.0k
bool LowerLoadStoreFrameInst::runOnFunction(Function *F) {
334
28.0k
  IRBuilder builder(F);
335
28.0k
  bool changed = false;
336
337
28.0k
  bool fnScopeCreated = false;
338
129k
  for (BasicBlock &BB : F->getBasicBlockList()) {
339
720k
    for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) {
340
590k
      Instruction *Inst = &*I;
341
590k
      ++I;
342
590k
      if (auto *csi = llvh::dyn_cast<CreateScopeInst>(Inst)) {
343
28.0k
        fnScopeCreated |=
344
28.0k
            csi->getCreatedScopeDesc() == F->getFunctionScopeDesc();
345
28.0k
        builder.setInsertionPoint(csi);
346
28.0k
        Instruction *llInst =
347
28.0k
            builder.createHBCCreateEnvironmentInst(csi->getCreatedScopeDesc());
348
28.0k
        Inst->replaceAllUsesWith(llInst);
349
28.0k
        Inst->eraseFromParent();
350
28.0k
        changed = true;
351
562k
      } else if (auto cisi = llvh::dyn_cast<CreateInnerScopeInst>(Inst)) {
352
0
        builder.setInsertionPoint(cisi);
353
0
        assert(
354
0
            llvh::isa<HBCCreateEnvironmentInst>(cisi->getParentScope()) ||
355
0
            llvh::isa<HBCCreateInnerEnvironmentInst>(cisi->getParentScope()));
356
357
0
        Instruction *llInst = builder.createHBCCreateInnerEnvironmentInst(
358
0
            cisi->getParentScope(), cisi->getCreatedScopeDesc());
359
0
        Inst->replaceAllUsesWith(llInst);
360
0
        Inst->eraseFromParent();
361
0
        changed = true;
362
0
      }
363
590k
    }
364
129k
  }
365
366
  // At this point all scopes used in F should be materialized. However, not
367
  // materializing F's scope potentially breaks lazy compilation as the compiler
368
  // always assumes all "external" scopes (i.e., those that have already been
369
  // compiled) have been materialized. Therefore, materialize the function scope
370
  // now. This instruction will be optimized out if unused in optimized builds.
371
28.0k
  assert(fnScopeCreated && "Function body scope not materialized.");
372
28.0k
  (void)fnScopeCreated;
373
374
129k
  for (BasicBlock &BB : F->getBasicBlockList()) {
375
720k
    for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) {
376
      // Keep the reference and increment iterator first.
377
590k
      Instruction *Inst = &*I;
378
590k
      ++I;
379
380
590k
      builder.setLocation(Inst->getLocation());
381
590k
      builder.setCurrentSourceLevelScope(Inst->getSourceLevelScope());
382
383
590k
      switch (Inst->getKind()) {
384
39
        case ValueKind::LoadFrameInstKind: {
385
39
          auto *LFI = cast<LoadFrameInst>(Inst);
386
39
          auto *var = LFI->getLoadVariable();
387
39
          auto *environment = LFI->getEnvironment();
388
389
39
          builder.setInsertionPoint(Inst);
390
39
          ScopeCreationInst *scope = getScope(builder, var, environment);
391
39
          Instruction *newInst =
392
39
              builder.createHBCLoadFromEnvironmentInst(scope, var);
393
394
39
          Inst->replaceAllUsesWith(newInst);
395
39
          Inst->eraseFromParent();
396
39
          changed = true;
397
39
          break;
398
0
        }
399
28.0k
        case ValueKind::StoreFrameInstKind: {
400
28.0k
          auto *SFI = cast<StoreFrameInst>(Inst);
401
28.0k
          auto *var = SFI->getVariable();
402
28.0k
          auto *val = SFI->getValue();
403
28.0k
          auto *environment = SFI->getEnvironment();
404
405
28.0k
          builder.setInsertionPoint(Inst);
406
28.0k
          ScopeCreationInst *scope = getScope(builder, var, environment);
407
28.0k
          builder.createHBCStoreToEnvironmentInst(scope, val, var);
408
409
28.0k
          Inst->eraseFromParent();
410
28.0k
          changed = true;
411
28.0k
          break;
412
0
        }
413
27.8k
        case ValueKind::CreateFunctionInstKind: {
414
27.8k
          auto *CFI = cast<CreateFunctionInst>(Inst);
415
27.8k
          auto *environment = CFI->getEnvironment();
416
417
27.8k
          builder.setInsertionPoint(Inst);
418
27.8k
          auto *newInst = builder.createHBCCreateFunctionInst(
419
27.8k
              CFI->getFunctionCode(), environment);
420
421
27.8k
          Inst->replaceAllUsesWith(newInst);
422
27.8k
          Inst->eraseFromParent();
423
27.8k
          changed = true;
424
27.8k
          break;
425
0
        }
426
2
        case ValueKind::CreateGeneratorInstKind: {
427
2
          auto *CFI = cast<CreateGeneratorInst>(Inst);
428
2
          auto *environment = CFI->getEnvironment();
429
430
2
          builder.setInsertionPoint(Inst);
431
2
          auto *newInst = builder.createHBCCreateGeneratorInst(
432
2
              CFI->getFunctionCode(), environment);
433
434
2
          Inst->replaceAllUsesWith(newInst);
435
2
          Inst->eraseFromParent();
436
2
          changed = true;
437
2
          break;
438
0
        }
439
534k
        default:
440
534k
          break;
441
590k
      }
442
590k
    }
443
129k
  }
444
28.0k
  return changed;
445
28.0k
}
446
447
28.0k
CreateArgumentsInst *LowerArgumentsArray::getCreateArgumentsInst(Function *F) {
448
  // CreateArgumentsInst is always in the first block in normal functions,
449
  // but is in the second block in GeneratorInnerFunctions.
450
28.0k
  if (llvh::isa<GeneratorInnerFunction>(F)) {
451
4
    for (BasicBlock *succ : F->front().getTerminator()->successors()) {
452
4
      for (auto &inst : *succ) {
453
4
        if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) {
454
0
          return target;
455
0
        }
456
4
      }
457
4
    }
458
28.0k
  } else {
459
306k
    for (auto &inst : F->front()) {
460
306k
      if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) {
461
2
        return target;
462
2
      }
463
306k
    }
464
28.0k
  }
465
28.0k
  return nullptr;
466
28.0k
}
467
468
28.0k
bool LowerArgumentsArray::runOnFunction(Function *F) {
469
28.0k
  IRBuilder builder(F);
470
28.0k
  updateToEntryInsertionPoint(builder, F);
471
472
28.0k
  CreateArgumentsInst *createArguments = getCreateArgumentsInst(F);
473
28.0k
  if (!createArguments) {
474
28.0k
    return false;
475
28.0k
  }
476
477
2
  builder.setInsertionPoint(createArguments);
478
2
  AllocStackInst *lazyReg = builder.createAllocStackInst("arguments");
479
2
  builder.createStoreStackInst(builder.getLiteralUndefined(), lazyReg);
480
481
  // Process all LoadPropertyInst's first because they may add another user
482
  // to the list of users of createArguments.
483
  // Specifically the case when `arguments[arguments]` is accessed.
484
  // Note that in such a case, a single LoadPropertyInst will appear twice in
485
  // the use list. Use a set so we only remove it once.
486
2
  llvh::SmallSetVector<Instruction *, 16> uniqueUsers;
487
2
  uniqueUsers.insert(
488
2
      createArguments->getUsers().begin(), createArguments->getUsers().end());
489
11
  for (Value *user : uniqueUsers) {
490
11
    auto *load = llvh::dyn_cast<LoadPropertyInst>(user);
491
11
    if (load && load->getObject() == createArguments) {
492
0
      builder.setInsertionPoint(load);
493
0
      builder.setLocation(load->getLocation());
494
0
      builder.setCurrentSourceLevelScope(load->getSourceLevelScope());
495
0
      auto *propertyString = llvh::dyn_cast<LiteralString>(load->getProperty());
496
0
      if (propertyString && propertyString->getValue().str() == "length") {
497
        // For `arguments.length`, get the length.
498
0
        auto *length = builder.createHBCGetArgumentsLengthInst(lazyReg);
499
0
        load->replaceAllUsesWith(length);
500
0
        load->eraseFromParent();
501
0
      } else {
502
        // For all other property loads, get by index.
503
0
        auto *get = builder.createHBCGetArgumentsPropByValInst(
504
0
            load->getProperty(), lazyReg);
505
0
        load->replaceAllUsesWith(get);
506
0
        load->eraseFromParent();
507
0
      }
508
0
    }
509
11
  }
510
511
2
  uniqueUsers.clear();
512
2
  uniqueUsers.insert(
513
2
      createArguments->getUsers().begin(), createArguments->getUsers().end());
514
11
  for (Value *user : uniqueUsers) {
515
11
    if (auto *phi = llvh::dyn_cast<PhiInst>(user)) {
516
      // We have to insert another branch where we can reify the value.
517
0
      for (int i = 0, n = phi->getNumEntries(); i < n; i++) {
518
0
        auto entry = phi->getEntry(i);
519
0
        if (entry.first != createArguments)
520
0
          continue;
521
522
0
        auto *previousBlock = cast<BasicBlock>(entry.second);
523
0
        auto *thisBlock = phi->getParent();
524
525
0
        auto *newBlock = builder.createBasicBlock(F);
526
0
        builder.setInsertionBlock(newBlock);
527
0
        builder.createHBCReifyArgumentsInst(lazyReg);
528
0
        auto *reifiedValue = builder.createLoadStackInst(lazyReg);
529
0
        builder.createBranchInst(thisBlock);
530
531
0
        phi->updateEntry(i, reifiedValue, newBlock);
532
        // Update all other PHI nodes in thisBlock that currently reference
533
        // previousBlock so they instead reference newBlock.
534
0
        updateIncomingPhiValues(thisBlock, previousBlock, newBlock);
535
536
0
        auto *branch = previousBlock->getTerminator();
537
0
        for (int j = 0, m = branch->getNumOperands(); j < m; j++)
538
0
          if (branch->getOperand(j) == thisBlock)
539
0
            branch->setOperand(newBlock, j);
540
0
      }
541
11
    } else if (auto *inst = llvh::dyn_cast<Instruction>(user)) {
542
      // For other users, insert a reification so we can replace
543
      // the usage with this array.
544
11
      builder.setInsertionPoint(inst);
545
11
      builder.setLocation(inst->getLocation());
546
11
      builder.setCurrentSourceLevelScope(inst->getSourceLevelScope());
547
11
      builder.createHBCReifyArgumentsInst(lazyReg);
548
11
      auto *array = builder.createLoadStackInst(lazyReg);
549
33
      for (int i = 0, n = inst->getNumOperands(); i < n; i++) {
550
22
        if (inst->getOperand(i) == createArguments) {
551
11
          inst->setOperand(array, i);
552
11
        }
553
22
      }
554
11
    } else {
555
0
      hermes_fatal("CreateArguments used for a non-Instruction.");
556
0
    }
557
11
  }
558
559
2
  createArguments->eraseFromParent();
560
2
  return true;
561
2
}
562
563
28.0k
bool DedupReifyArguments::runOnFunction(Function *F) {
564
28.0k
  bool changed = false;
565
566
  // Check if there are any HBCReifyArgumentsInst in the function before
567
  // calculating dominator tree and reverse post order to save compile time.
568
28.0k
  bool hasRAI = false;
569
100k
  for (auto &BB : *F) {
570
473k
    for (auto &inst : BB.getInstList()) {
571
473k
      if (llvh::isa<HBCReifyArgumentsInst>(&inst)) {
572
2
        hasRAI = true;
573
2
        break;
574
2
      }
575
473k
    }
576
100k
    if (hasRAI)
577
2
      break;
578
100k
  }
579
580
28.0k
  if (!hasRAI)
581
28.0k
    return false;
582
583
2
  DominanceInfo domInfo{F};
584
2
  PostOrderAnalysis PO(F);
585
2
  IRBuilder::InstructionDestroyer destroyer;
586
587
2
  llvh::SmallVector<BasicBlock *, 16> reversePO(PO.rbegin(), PO.rend());
588
2
  llvh::SmallVector<HBCReifyArgumentsInst *, 4> reifications;
589
590
33.8k
  for (auto *BB : reversePO) {
591
133k
    for (auto &inst : BB->getInstList()) {
592
133k
      if (auto *reify = llvh::dyn_cast<HBCReifyArgumentsInst>(&inst)) {
593
11
        HBCReifyArgumentsInst *dominator = nullptr;
594
11
        for (auto *possibleParent : reifications) {
595
9
          if (domInfo.properlyDominates(possibleParent, reify)) {
596
9
            dominator = possibleParent;
597
9
            break;
598
9
          }
599
9
        }
600
601
11
        if (dominator) {
602
9
          destroyer.add(reify);
603
9
          changed = true;
604
9
        } else {
605
2
          reifications.push_back(reify);
606
2
        }
607
11
      }
608
133k
    }
609
33.8k
  }
610
2
  return changed;
611
28.0k
}
612
613
28.0k
bool LowerConstruction::runOnFunction(Function *F) {
614
28.0k
  IRBuilder builder(F);
615
28.0k
  auto *prototypeString = builder.getLiteralString("prototype");
616
617
129k
  for (BasicBlock &BB : F->getBasicBlockList()) {
618
129k
    IRBuilder::InstructionDestroyer destroyer;
619
590k
    for (Instruction &I : BB) {
620
590k
      if (auto *constructor = llvh::dyn_cast<ConstructInst>(&I)) {
621
0
        builder.setInsertionPoint(constructor);
622
0
        builder.setLocation(constructor->getLocation());
623
0
        builder.setCurrentSourceLevelScope(constructor->getSourceLevelScope());
624
0
        auto closure = constructor->getCallee();
625
0
        auto prototype =
626
0
            builder.createLoadPropertyInst(closure, prototypeString);
627
0
        auto thisObject = builder.createHBCCreateThisInst(prototype, closure);
628
629
0
        llvh::SmallVector<Value *, 8> args;
630
0
        for (int i = 1, n = constructor->getNumArguments(); i < n; i++) {
631
0
          args.push_back(constructor->getArgument(i));
632
0
        }
633
0
        auto newConstructor = builder.createHBCConstructInst(
634
0
            closure, constructor->getNewTarget(), thisObject, args);
635
0
        auto finalValue = builder.createHBCGetConstructedObjectInst(
636
0
            thisObject, newConstructor);
637
0
        constructor->replaceAllUsesWith(finalValue);
638
0
        destroyer.add(constructor);
639
0
      }
640
590k
    }
641
129k
  }
642
28.0k
  return true;
643
28.0k
}
644
645
28.0k
bool LowerCalls::runOnFunction(Function *F) {
646
28.0k
  IRBuilder builder(F);
647
28.0k
  bool changed = false;
648
649
129k
  for (auto &BB : *F) {
650
1.12M
    for (auto &I : BB) {
651
1.12M
      auto *call = llvh::dyn_cast<CallInst>(&I);
652
      // This also matches constructors.
653
1.12M
      if (!call)
654
1.12M
        continue;
655
1.05k
      builder.setInsertionPoint(call);
656
1.05k
      changed = true;
657
658
1.05k
      auto reg = RA_.getLastRegister().getIndex() -
659
1.05k
          HVMRegisterAllocator::CALL_EXTRA_REGISTERS;
660
661
244k
      for (int i = 0, e = call->getNumArguments(); i < e; i++, --reg) {
662
        // If this is a Call instruction, emit explicit Movs to the argument
663
        // registers. If this is a CallN instruction, emit ImplicitMovs
664
        // instead, to express that these registers get written to by the CallN,
665
        // even though they are not the destination.
666
        // Lastly, if this is argument 0 of CallBuiltinInst emit ImplicitMov to
667
        // encode that the "this" register is implicitly set to undefined.
668
243k
        Value *arg = call->getArgument(i);
669
243k
        if (llvh::isa<HBCCallNInst>(call) ||
670
243k
            (i == 0 && llvh::isa<CallBuiltinInst>(call))) {
671
519
          auto *imov = builder.createImplicitMovInst(arg);
672
519
          RA_.updateRegister(imov, Register(reg));
673
243k
        } else {
674
243k
          auto *mov = builder.createMovInst(arg);
675
243k
          RA_.updateRegister(mov, Register(reg));
676
243k
          call->setArgument(mov, i);
677
243k
        }
678
243k
      }
679
1.05k
    }
680
129k
  }
681
28.0k
  return changed;
682
28.0k
}
683
684
0
bool RecreateCheapValues::runOnFunction(Function *F) {
685
0
  IRBuilder builder(F);
686
0
  llvh::SmallPtrSet<Instruction *, 4> potentiallyUnused;
687
0
  bool changed = false;
688
689
0
  for (auto &BB : *F) {
690
0
    IRBuilder::InstructionDestroyer destroyer;
691
0
    for (auto &I : BB) {
692
0
      auto *mov = llvh::dyn_cast<MovInst>(&I);
693
0
      if (!mov)
694
0
        continue;
695
0
      auto *load = llvh::dyn_cast<HBCLoadConstInst>(mov->getSingleOperand());
696
0
      if (!load)
697
0
        continue;
698
0
      Literal *literal = load->getConst();
699
700
0
      switch (literal->getKind()) {
701
0
        case ValueKind::LiteralUndefinedKind:
702
0
        case ValueKind::LiteralNullKind:
703
0
        case ValueKind::LiteralBoolKind:
704
0
          break;
705
0
        case ValueKind::LiteralNumberKind:
706
0
          if (cast<LiteralNumber>(literal)->isPositiveZero()) {
707
0
            break;
708
0
          }
709
0
          continue;
710
0
        default:
711
0
          continue;
712
0
      }
713
714
0
      builder.setInsertionPoint(mov);
715
0
      auto *recreation = builder.createHBCLoadConstInst(literal);
716
0
      RA_.updateRegister(recreation, RA_.getRegister(mov));
717
0
      mov->replaceAllUsesWith(recreation);
718
0
      destroyer.add(mov);
719
0
      potentiallyUnused.insert(load);
720
0
      changed = true;
721
0
    }
722
0
  }
723
724
0
  {
725
0
    IRBuilder::InstructionDestroyer destroyer;
726
0
    for (auto *inst : potentiallyUnused) {
727
0
      if (!inst->hasUsers()) {
728
0
        destroyer.add(inst);
729
0
      }
730
0
    }
731
0
  }
732
0
  return changed;
733
0
}
734
735
0
bool LoadConstantValueNumbering::runOnFunction(Function *F) {
736
0
  IRBuilder builder(F);
737
0
  bool changed = false;
738
739
0
  for (auto &BB : *F) {
740
0
    IRBuilder::InstructionDestroyer destroyer;
741
    // Maps a register number to the instruction that last modified it.
742
    // Every Instruction is either an HBCLoadConstInst or a Mov whose
743
    // operand is a HBCLoadConstInst
744
0
    llvh::DenseMap<unsigned, Instruction *> regToInstMap{};
745
0
    for (auto &I : BB) {
746
0
      HBCLoadConstInst *loadI{nullptr};
747
      // Value numbering currently only tracks the values of registers that
748
      // have a constant in them, or that have had a constant moved in them.
749
0
      if (!(loadI = llvh::dyn_cast<HBCLoadConstInst>(&I))) {
750
0
        if (auto *movI = llvh::dyn_cast<MovInst>(&I)) {
751
0
          loadI = llvh::dyn_cast<HBCLoadConstInst>(movI->getSingleOperand());
752
0
        }
753
0
      }
754
755
0
      if (RA_.isAllocated(&I)) {
756
0
        unsigned reg = RA_.getRegister(&I).getIndex();
757
0
        if (loadI) {
758
0
          auto it = regToInstMap.find(reg);
759
0
          if (it != regToInstMap.end()) {
760
0
            auto prevI = it->second;
761
0
            HBCLoadConstInst *prevLoad{nullptr};
762
            // If the key is found, the instruction must be either an
763
            // HBCLoadConstInst, or a Mov whose operand is an HBCLoadConstInst.
764
0
            if (!(prevLoad = llvh::dyn_cast<HBCLoadConstInst>(prevI))) {
765
0
              prevLoad = llvh::dyn_cast<HBCLoadConstInst>(prevI->getOperand(0));
766
0
            }
767
0
            if (prevLoad->isIdenticalTo(loadI)) {
768
0
              I.replaceAllUsesWith(prevI);
769
0
              destroyer.add(&I);
770
0
              changed = true;
771
0
              continue;
772
0
            }
773
0
          }
774
0
          regToInstMap[reg] = &I;
775
0
          continue;
776
0
        }
777
0
        regToInstMap.erase(reg);
778
0
      }
779
780
0
      for (auto index : I.getChangedOperands()) {
781
0
        auto *operand = I.getOperand(index);
782
0
        unsigned reg = RA_.getRegister(cast<Instruction>(operand)).getIndex();
783
0
        regToInstMap.erase(reg);
784
0
      }
785
0
    }
786
0
  }
787
0
  return changed;
788
0
}
789
790
950k
bool SpillRegisters::requiresShortOutput(Instruction *I) {
791
950k
  if (llvh::isa<TerminatorInst>(I)) {
792
    // None of our terminators produce results at all
793
    // (though GetNextPNameInst modifies operands).
794
33.8k
    return false;
795
33.8k
  }
796
797
  // Instructions that produce no output, don't use the register, even when
798
  // allocated.
799
917k
  if (I->getType().isNoType())
800
40
    return false;
801
802
917k
  switch (I->getKind()) {
803
    // Some instructions become Movs or other opcodes with long variants:
804
295k
    case ValueKind::HBCSpillMovInstKind:
805
302k
    case ValueKind::LoadStackInstKind:
806
573k
    case ValueKind::MovInstKind:
807
574k
    case ValueKind::PhiInstKind:
808
    // Some instructions aren't actually encoded at all:
809
582k
    case ValueKind::AllocStackInstKind:
810
584k
    case ValueKind::TryEndInstKind:
811
584k
    case ValueKind::TryStartInstKind:
812
584k
      return false;
813
332k
    default:
814
332k
      return true;
815
917k
  }
816
917k
}
817
818
891k
bool SpillRegisters::requiresShortOperand(Instruction *I, int op) {
819
891k
  switch (I->getKind()) {
820
706
    case ValueKind::PhiInstKind:
821
271k
    case ValueKind::MovInstKind:
822
566k
    case ValueKind::HBCSpillMovInstKind:
823
574k
    case ValueKind::LoadStackInstKind:
824
574k
    case ValueKind::StoreStackInstKind:
825
574k
      return false;
826
120k
    case ValueKind::CallInstKind:
827
120k
    case ValueKind::ConstructInstKind:
828
241k
    case ValueKind::CallBuiltinInstKind:
829
241k
    case ValueKind::HBCConstructInstKind:
830
241k
    case ValueKind::HBCCallDirectInstKind:
831
241k
      return op == CallInst::CalleeIdx;
832
75.4k
    default:
833
75.4k
      return true;
834
891k
  }
835
891k
}
836
837
48.0k
bool SpillRegisters::modifiesOperandRegister(Instruction *I, int op) {
838
48.0k
  return I->getChangedOperands().at((unsigned)op);
839
48.0k
}
840
841
28.0k
bool SpillRegisters::runOnFunction(Function *F) {
842
28.0k
  if (RA_.getMaxRegisterUsage() < boundary_) {
843
28.0k
    return false;
844
28.0k
  }
845
8
  reserveLowRegisters(F);
846
847
8
  IRBuilder builder(F);
848
8
  llvh::SmallVector<std::pair<Instruction *, Register>, 2> toSpill;
849
850
33.8k
  for (BasicBlock &BB : F->getBasicBlockList()) {
851
950k
    for (Instruction &inst : BB) {
852
950k
      if (!RA_.isAllocated(&inst)) {
853
        // This instruction is dead. Don't bother spilling.
854
0
        continue;
855
0
      }
856
857
950k
      int tempReg = 0;
858
950k
      toSpill.clear();
859
950k
      bool replaceWithFirstSpill = false;
860
950k
      builder.setLocation(inst.getLocation());
861
950k
      builder.setCurrentSourceLevelScope(inst.getSourceLevelScope());
862
863
950k
      auto myRegister = RA_.getRegister(&inst);
864
950k
      if (requiresShortOutput(&inst) && !isShort(myRegister)) {
865
292k
        auto temp = getReserved(tempReg++);
866
292k
        RA_.updateRegister(&inst, temp);
867
292k
        toSpill.push_back(
868
292k
            std::pair<Instruction *, Register>(&inst, myRegister));
869
292k
        replaceWithFirstSpill = true;
870
292k
      }
871
872
2.17M
      for (int i = 0, e = inst.getNumOperands(); i < e; i++) {
873
1.22M
        auto *op = llvh::dyn_cast<Instruction>(inst.getOperand(i));
874
1.22M
        if (!op || !RA_.isAllocated(op)) {
875
          // This is either not an instruction, or a dead instruction.
876
          // Either way, we don't have to do anything.
877
331k
          continue;
878
331k
        }
879
891k
        auto opRegister = RA_.getRegister(op);
880
881
891k
        if (requiresShortOperand(&inst, i) && !isShort(opRegister)) {
882
48.0k
          auto temp = getReserved(tempReg++);
883
884
48.0k
          builder.setInsertionPoint(&inst);
885
48.0k
          auto *load = builder.createHBCSpillMovInst(op);
886
48.0k
          RA_.updateRegister(load, temp);
887
48.0k
          inst.setOperand(load, i);
888
889
48.0k
          if (modifiesOperandRegister(&inst, i)) {
890
2.63k
            toSpill.push_back(
891
2.63k
                std::pair<Instruction *, Register>(load, opRegister));
892
2.63k
          }
893
48.0k
        }
894
891k
      }
895
896
950k
      if (toSpill.size()) {
897
292k
        auto spillPoints = getInsertionPointsAfter(builder, &inst);
898
899
292k
        assert(
900
292k
            (!replaceWithFirstSpill || spillPoints.size() <= 1) &&
901
292k
            "Asked to spill the value of a TerminatorInst. Our terminators "
902
292k
            "shouldn't produce values. It wouldn't have mattered, but it "
903
292k
            "also has multiple branches so users might need PhiInsts.");
904
905
292k
        for (auto *point : spillPoints) {
906
292k
          builder.setInsertionPoint(point);
907
295k
          for (auto store : toSpill) {
908
295k
            auto *storeInst = builder.createHBCSpillMovInst(store.first);
909
295k
            RA_.updateRegister(storeInst, store.second);
910
911
295k
            if (!replaceWithFirstSpill)
912
2.63k
              continue;
913
            // Replace all uses of the inst with the spilling inst
914
292k
            inst.replaceAllUsesWith(storeInst);
915
            // Except for the actual spill of course
916
292k
            storeInst->setOperand(&inst, 0);
917
            // Disable now that the job is done.
918
292k
            replaceWithFirstSpill = false;
919
292k
          }
920
292k
        }
921
292k
      }
922
950k
    }
923
33.8k
  }
924
8
  return true;
925
8
}
926
927
28.0k
bool LowerSwitchIntoJumpTables::runOnFunction(Function *F) {
928
28.0k
  bool changed = false;
929
28.0k
  llvh::SmallVector<SwitchInst *, 4> switches;
930
  // Collect all switch instructions.
931
28.0k
  for (BasicBlock &BB : *F)
932
590k
    for (auto &it : BB) {
933
590k
      if (auto *S = llvh::dyn_cast<SwitchInst>(&it))
934
0
        switches.push_back(S);
935
590k
    }
936
937
28.0k
  for (auto *S : switches) {
938
0
    if (lowerIntoJumpTable(S))
939
0
      changed = true;
940
0
  }
941
942
28.0k
  return changed;
943
28.0k
}
944
945
0
bool LowerSwitchIntoJumpTables::lowerIntoJumpTable(SwitchInst *switchInst) {
946
  // if its a constant switch don't bother
947
0
  if (llvh::isa<Literal>(switchInst->getInputValue())) {
948
0
    return false;
949
0
  }
950
0
  IRBuilder builder(switchInst->getParent()->getParent());
951
0
  unsigned numCases = switchInst->getNumCasePair();
952
0
  uint32_t minValue = 0;
953
0
  uint32_t maxValue = 0;
954
955
0
  llvh::SmallVector<LiteralNumber *, 8> values;
956
0
  llvh::SmallVector<BasicBlock *, 8> blocks;
957
958
0
  for (unsigned i = 0; i != numCases; ++i) {
959
0
    auto casePair = switchInst->getCasePair(i);
960
0
    auto *lit = casePair.first;
961
0
    auto *num = llvh::dyn_cast<LiteralNumber>(lit);
962
0
    if (!num)
963
0
      return false;
964
965
    // Check whether it is representable as uint32.
966
0
    if (auto ival = num->isIntTypeRepresentible<uint32_t>()) {
967
0
      values.push_back(num);
968
0
      blocks.push_back(casePair.second);
969
970
0
      if (i == 0) {
971
0
        minValue = maxValue = ival.getValue();
972
0
      } else {
973
0
        minValue = std::min(minValue, ival.getValue());
974
0
        maxValue = std::max(maxValue, ival.getValue());
975
0
      }
976
0
    } else {
977
0
      return false;
978
0
    }
979
0
  }
980
981
0
  assert(minValue <= maxValue && "Minimum cannot exceed maximum");
982
0
  uint32_t range = maxValue - minValue;
983
  // We can't generate a table for a zero-sized range.
984
0
  if (range == 0)
985
0
    return false;
986
987
  // The number of cases is range + 1, which must fit in a uint32.
988
0
  if (range == std::numeric_limits<uint32_t>::max())
989
0
    return false;
990
991
  // Check the "denseness" of the cases.
992
  // Don't convert small switches.
993
0
  if (range / numCases > 5 || numCases < 10)
994
0
    return false;
995
996
0
  builder.setInsertionPoint(switchInst);
997
0
  auto *switchImmInst = builder.createSwitchImmInst(
998
0
      switchInst->getInputValue(),
999
0
      switchInst->getDefaultDestination(),
1000
0
      builder.getLiteralNumber(minValue),
1001
0
      builder.getLiteralNumber(range + 1),
1002
0
      values,
1003
0
      blocks);
1004
1005
0
  switchInst->replaceAllUsesWith(switchImmInst);
1006
0
  switchInst->eraseFromParent();
1007
0
  return true;
1008
0
}
1009
1010
} // namespace hbc
1011
} // namespace hermes
1012
1013
#undef DEBUG_TYPE