Coverage Report

Created: 2025-09-04 06:34

/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
352k
    Instruction *inst) {
49
352k
  llvh::SmallVector<Instruction *, 4> points;
50
51
352k
  if (!llvh::isa<TerminatorInst>(inst)) {
52
    // Easy case for non-terminators. Just return the next inst.
53
352k
    auto pos = inst->getIterator();
54
352k
    pos++;
55
352k
    points.push_back(&*pos);
56
352k
    return points;
57
352k
  }
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
352k
}
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
2.67k
void updateToEntryInsertionPoint(IRBuilder &builder, Function *F) {
78
2.67k
  auto &BB = F->front();
79
2.67k
  auto it = BB.begin();
80
2.67k
  auto end = BB.end();
81
  // Skip all HBCCreateEnvironmentInst.
82
5.34k
  while (it != end && llvh::isa<HBCCreateEnvironmentInst>(*it))
83
2.67k
    ++it;
84
85
2.67k
  builder.setInsertionPoint(&*it);
86
2.67k
}
87
88
} // namespace
89
90
963k
bool LoadConstants::operandMustBeLiteral(Instruction *Inst, unsigned opIndex) {
91
  // HBCLoadConstInst is meant to load a constant
92
963k
  if (llvh::isa<HBCLoadConstInst>(Inst))
93
5
    return true;
94
95
  // The operand of HBCLoadParamInst is a literal index.
96
963k
  if (llvh::isa<HBCLoadParamInst>(Inst))
97
0
    return true;
98
99
963k
  if (llvh::isa<HBCAllocObjectFromBufferInst>(Inst))
100
0
    return true;
101
102
  // All operands of AllocArrayInst are literals.
103
963k
  if (llvh::isa<AllocArrayInst>(Inst))
104
8.73k
    return true;
105
106
954k
  if (llvh::isa<AllocObjectInst>(Inst)) {
107
    // The AllocObjectInst::SizeIdx is a literal.
108
0
    if (opIndex == AllocObjectInst::SizeIdx)
109
0
      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
954k
  if (llvh::isa<SwitchInst>(Inst) && opIndex > 0)
121
0
    return true;
122
123
  // StoreOwnPropertyInst and StoreNewOwnPropertyInst.
124
954k
  if (auto *SOP = llvh::dyn_cast<StoreOwnPropertyInst>(Inst)) {
125
212k
    if (opIndex == StoreOwnPropertyInst::PropertyIdx) {
126
70.7k
      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
70.7k
      if (auto *LN = llvh::dyn_cast<LiteralNumber>(Inst->getOperand(opIndex))) {
135
70.7k
        if (SOP->getIsEnumerable() && LN->convertToArrayIndex().hasValue())
136
70.7k
          return true;
137
70.7k
      }
138
70.7k
    }
139
140
    // StoreOwnPropertyInst's isEnumerable is a boolean constant.
141
141k
    if (opIndex == StoreOwnPropertyInst::IsEnumerableIdx)
142
70.7k
      return true;
143
144
70.6k
    return false;
145
141k
  }
146
147
  // If StorePropertyInst's property ID is a LiteralString, we will keep it
148
  // untouched and emit try_put_by_id eventually.
149
742k
  if (llvh::isa<StorePropertyInst>(Inst) &&
150
742k
      opIndex == StorePropertyInst::PropertyIdx &&
151
742k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
152
1.37k
    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
740k
  if (llvh::isa<LoadPropertyInst>(Inst) &&
157
740k
      opIndex == LoadPropertyInst::PropertyIdx &&
158
740k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
159
245k
    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
495k
  if (llvh::isa<DeletePropertyInst>(Inst) &&
164
495k
      opIndex == DeletePropertyInst::PropertyIdx &&
165
495k
      llvh::isa<LiteralString>(Inst->getOperand(opIndex)))
166
18.2k
    return true;
167
168
  // StoreGetterSetterInst's isEnumerable is a boolean constant.
169
477k
  if (llvh::isa<StoreGetterSetterInst>(Inst) &&
170
477k
      opIndex == StoreGetterSetterInst::IsEnumerableIdx)
171
0
    return true;
172
173
  // Both pattern and flags operands of the CreateRegExpInst
174
  // are literal strings.
175
477k
  if (llvh::isa<CreateRegExpInst>(Inst))
176
646
    return true;
177
178
476k
  if (llvh::isa<SwitchImmInst>(Inst) &&
179
476k
      (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
476k
  if (llvh::isa<CallBuiltinInst>(Inst) &&
186
476k
      (opIndex == CallBuiltinInst::CalleeIdx ||
187
102k
       opIndex == CallBuiltinInst::ThisIdx))
188
33.6k
    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
443k
  if (llvh::isa<CallInst>(Inst) && opIndex == CallInst::NewTargetIdx)
194
21.2k
    return true;
195
196
  /// GetBuiltinClosureInst's builtin index is always literal.
197
421k
  if (llvh::isa<GetBuiltinClosureInst>(Inst) &&
198
421k
      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
421k
  if (llvh::isa<IteratorCloseInst>(Inst) &&
209
421k
      opIndex == IteratorCloseInst::IgnoreInnerExceptionIdx) {
210
522
    return true;
211
522
  }
212
213
421k
  if (llvh::isa<ThrowIfHasRestrictedGlobalPropertyInst>(Inst) &&
214
421k
      opIndex == ThrowIfHasRestrictedGlobalPropertyInst::PropertyIdx) {
215
0
    return true;
216
0
  }
217
  // DirectEvalInst's isStrict is a boolean constant.
218
421k
  if (llvh::isa<DirectEvalInst>(Inst) &&
219
421k
      opIndex == DirectEvalInst::IsStrictIdx) {
220
0
    return true;
221
0
  }
222
223
421k
  return false;
224
421k
}
225
226
1.33k
bool LoadConstants::runOnFunction(Function *F) {
227
1.33k
  IRBuilder builder(F);
228
1.33k
  bool changed = false;
229
230
  /// Inserts and returns a load instruction for \p literal before \p where.
231
491k
  auto createLoadLiteral = [&builder](Literal *literal, Instruction *where) {
232
491k
    builder.setInsertionPoint(where);
233
491k
    return llvh::isa<GlobalObject>(literal)
234
491k
        ? cast<Instruction>(builder.createHBCGetGlobalObjectInst())
235
491k
        : cast<Instruction>(builder.createHBCLoadConstInst(literal));
236
491k
  };
237
238
59.6k
  for (BasicBlock &BB : *F) {
239
749k
    for (auto &I : BB) {
240
749k
      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
13.5k
        for (unsigned i = 0, e = phi->getNumEntries(); i < e; ++i) {
246
9.05k
          auto [val, bb] = phi->getEntry(i);
247
9.05k
          if (auto *literal = llvh::dyn_cast<Literal>(val)) {
248
5
            auto *load = createLoadLiteral(literal, bb->getTerminator());
249
5
            phi->updateEntry(i, load, bb);
250
5
            changed = true;
251
5
          }
252
9.05k
        }
253
4.52k
        continue;
254
4.52k
      }
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.46M
      for (unsigned i = 0, e = I.getNumOperands(); i < e; ++i) {
260
1.72M
        if (auto *literal = llvh::dyn_cast<Literal>(I.getOperand(i))) {
261
963k
          if (!operandMustBeLiteral(&I, i)) {
262
491k
            auto *load = createLoadLiteral(literal, &I);
263
491k
            I.setOperand(load, i);
264
491k
            changed = true;
265
491k
          }
266
963k
        }
267
1.72M
      }
268
744k
    }
269
59.6k
  }
270
1.33k
  return changed;
271
1.33k
}
272
273
1.33k
bool LoadParameters::runOnFunction(Function *F) {
274
1.33k
  IRBuilder builder(F);
275
1.33k
  bool changed = false;
276
277
1.33k
  updateToEntryInsertionPoint(builder, F);
278
279
  // Index of 0 is the "this" parameter.
280
1.33k
  unsigned index = 1;
281
1.33k
  for (Parameter *p : F->getParameters()) {
282
1.30k
    auto *load =
283
1.30k
        builder.createHBCLoadParamInst(builder.getLiteralNumber(index));
284
1.30k
    p->replaceAllUsesWith(load);
285
1.30k
    index++;
286
1.30k
    changed = true;
287
1.30k
  }
288
289
  // Lower accesses to "this".
290
1.33k
  auto *thisParam = F->getThisParameter();
291
1.33k
  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
5
    Value *getThisInst = F->isStrictMode()
295
5
        ? cast<Value>(
296
0
              builder.createHBCLoadParamInst(builder.getLiteralNumber(0)))
297
5
        : cast<Value>(builder.createHBCGetThisNSInst());
298
5
    thisParam->replaceAllUsesWith(getThisInst);
299
5
    changed = true;
300
5
  }
301
1.33k
  return changed;
302
1.33k
}
303
304
ScopeCreationInst *LowerLoadStoreFrameInst::getScope(
305
    IRBuilder &builder,
306
    Variable *var,
307
5.94k
    ScopeCreationInst *environment) {
308
5.94k
  if (var->getParent() == environment->getCreatedScopeDesc()) {
309
    // Var's scope belongs to the current function.
310
5.85k
    assert(
311
5.85k
        var->getParent()->getFunction() == builder.getFunction() &&
312
5.85k
        "Scope should only be found if var is not captured from another "
313
5.85k
        "function");
314
5.85k
    return environment;
315
5.85k
  }
316
317
88
  auto *environmentWithParent =
318
88
      llvh::dyn_cast<NestedScopeCreationInst>(environment);
319
88
  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
88
    assert(
323
88
        var->getParent()->getFunction() != builder.getFunction() &&
324
88
        "Failed to find scope in current function for local variable.");
325
88
    return builder.createHBCResolveEnvironment(
326
88
        environment->getCreatedScopeDesc(), var->getParent());
327
88
  }
328
329
  // Keep looking.
330
0
  return getScope(builder, var, environmentWithParent->getParentScope());
331
88
}
332
333
1.33k
bool LowerLoadStoreFrameInst::runOnFunction(Function *F) {
334
1.33k
  IRBuilder builder(F);
335
1.33k
  bool changed = false;
336
337
1.33k
  bool fnScopeCreated = false;
338
59.6k
  for (BasicBlock &BB : F->getBasicBlockList()) {
339
808k
    for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) {
340
749k
      Instruction *Inst = &*I;
341
749k
      ++I;
342
749k
      if (auto *csi = llvh::dyn_cast<CreateScopeInst>(Inst)) {
343
1.33k
        fnScopeCreated |=
344
1.33k
            csi->getCreatedScopeDesc() == F->getFunctionScopeDesc();
345
1.33k
        builder.setInsertionPoint(csi);
346
1.33k
        Instruction *llInst =
347
1.33k
            builder.createHBCCreateEnvironmentInst(csi->getCreatedScopeDesc());
348
1.33k
        Inst->replaceAllUsesWith(llInst);
349
1.33k
        Inst->eraseFromParent();
350
1.33k
        changed = true;
351
748k
      } 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
749k
    }
364
59.6k
  }
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
1.33k
  assert(fnScopeCreated && "Function body scope not materialized.");
372
1.33k
  (void)fnScopeCreated;
373
374
59.6k
  for (BasicBlock &BB : F->getBasicBlockList()) {
375
808k
    for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) {
376
      // Keep the reference and increment iterator first.
377
749k
      Instruction *Inst = &*I;
378
749k
      ++I;
379
380
749k
      builder.setLocation(Inst->getLocation());
381
749k
      builder.setCurrentSourceLevelScope(Inst->getSourceLevelScope());
382
383
749k
      switch (Inst->getKind()) {
384
4.60k
        case ValueKind::LoadFrameInstKind: {
385
4.60k
          auto *LFI = cast<LoadFrameInst>(Inst);
386
4.60k
          auto *var = LFI->getLoadVariable();
387
4.60k
          auto *environment = LFI->getEnvironment();
388
389
4.60k
          builder.setInsertionPoint(Inst);
390
4.60k
          ScopeCreationInst *scope = getScope(builder, var, environment);
391
4.60k
          Instruction *newInst =
392
4.60k
              builder.createHBCLoadFromEnvironmentInst(scope, var);
393
394
4.60k
          Inst->replaceAllUsesWith(newInst);
395
4.60k
          Inst->eraseFromParent();
396
4.60k
          changed = true;
397
4.60k
          break;
398
0
        }
399
1.33k
        case ValueKind::StoreFrameInstKind: {
400
1.33k
          auto *SFI = cast<StoreFrameInst>(Inst);
401
1.33k
          auto *var = SFI->getVariable();
402
1.33k
          auto *val = SFI->getValue();
403
1.33k
          auto *environment = SFI->getEnvironment();
404
405
1.33k
          builder.setInsertionPoint(Inst);
406
1.33k
          ScopeCreationInst *scope = getScope(builder, var, environment);
407
1.33k
          builder.createHBCStoreToEnvironmentInst(scope, val, var);
408
409
1.33k
          Inst->eraseFromParent();
410
1.33k
          changed = true;
411
1.33k
          break;
412
0
        }
413
1.30k
        case ValueKind::CreateFunctionInstKind: {
414
1.30k
          auto *CFI = cast<CreateFunctionInst>(Inst);
415
1.30k
          auto *environment = CFI->getEnvironment();
416
417
1.30k
          builder.setInsertionPoint(Inst);
418
1.30k
          auto *newInst = builder.createHBCCreateFunctionInst(
419
1.30k
              CFI->getFunctionCode(), environment);
420
421
1.30k
          Inst->replaceAllUsesWith(newInst);
422
1.30k
          Inst->eraseFromParent();
423
1.30k
          changed = true;
424
1.30k
          break;
425
0
        }
426
0
        case ValueKind::CreateGeneratorInstKind: {
427
0
          auto *CFI = cast<CreateGeneratorInst>(Inst);
428
0
          auto *environment = CFI->getEnvironment();
429
430
0
          builder.setInsertionPoint(Inst);
431
0
          auto *newInst = builder.createHBCCreateGeneratorInst(
432
0
              CFI->getFunctionCode(), environment);
433
434
0
          Inst->replaceAllUsesWith(newInst);
435
0
          Inst->eraseFromParent();
436
0
          changed = true;
437
0
          break;
438
0
        }
439
742k
        default:
440
742k
          break;
441
749k
      }
442
749k
    }
443
59.6k
  }
444
1.33k
  return changed;
445
1.33k
}
446
447
1.33k
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
1.33k
  if (llvh::isa<GeneratorInnerFunction>(F)) {
451
0
    for (BasicBlock *succ : F->front().getTerminator()->successors()) {
452
0
      for (auto &inst : *succ) {
453
0
        if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) {
454
0
          return target;
455
0
        }
456
0
      }
457
0
    }
458
1.33k
  } else {
459
144k
    for (auto &inst : F->front()) {
460
144k
      if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) {
461
6
        return target;
462
6
      }
463
144k
    }
464
1.33k
  }
465
1.33k
  return nullptr;
466
1.33k
}
467
468
1.33k
bool LowerArgumentsArray::runOnFunction(Function *F) {
469
1.33k
  IRBuilder builder(F);
470
1.33k
  updateToEntryInsertionPoint(builder, F);
471
472
1.33k
  CreateArgumentsInst *createArguments = getCreateArgumentsInst(F);
473
1.33k
  if (!createArguments) {
474
1.33k
    return false;
475
1.33k
  }
476
477
6
  builder.setInsertionPoint(createArguments);
478
6
  AllocStackInst *lazyReg = builder.createAllocStackInst("arguments");
479
6
  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
6
  llvh::SmallSetVector<Instruction *, 16> uniqueUsers;
487
6
  uniqueUsers.insert(
488
6
      createArguments->getUsers().begin(), createArguments->getUsers().end());
489
12
  for (Value *user : uniqueUsers) {
490
12
    auto *load = llvh::dyn_cast<LoadPropertyInst>(user);
491
12
    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
12
  }
510
511
6
  uniqueUsers.clear();
512
6
  uniqueUsers.insert(
513
6
      createArguments->getUsers().begin(), createArguments->getUsers().end());
514
12
  for (Value *user : uniqueUsers) {
515
12
    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
12
    } 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
12
      builder.setInsertionPoint(inst);
545
12
      builder.setLocation(inst->getLocation());
546
12
      builder.setCurrentSourceLevelScope(inst->getSourceLevelScope());
547
12
      builder.createHBCReifyArgumentsInst(lazyReg);
548
12
      auto *array = builder.createLoadStackInst(lazyReg);
549
36
      for (int i = 0, n = inst->getNumOperands(); i < n; i++) {
550
24
        if (inst->getOperand(i) == createArguments) {
551
12
          inst->setOperand(array, i);
552
12
        }
553
24
      }
554
12
    } else {
555
0
      hermes_fatal("CreateArguments used for a non-Instruction.");
556
0
    }
557
12
  }
558
559
6
  createArguments->eraseFromParent();
560
6
  return true;
561
6
}
562
563
1.33k
bool DedupReifyArguments::runOnFunction(Function *F) {
564
1.33k
  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
1.33k
  bool hasRAI = false;
569
57.3k
  for (auto &BB : *F) {
570
598k
    for (auto &inst : BB.getInstList()) {
571
598k
      if (llvh::isa<HBCReifyArgumentsInst>(&inst)) {
572
6
        hasRAI = true;
573
6
        break;
574
6
      }
575
598k
    }
576
57.3k
    if (hasRAI)
577
6
      break;
578
57.3k
  }
579
580
1.33k
  if (!hasRAI)
581
1.33k
    return false;
582
583
6
  DominanceInfo domInfo{F};
584
6
  PostOrderAnalysis PO(F);
585
6
  IRBuilder::InstructionDestroyer destroyer;
586
587
6
  llvh::SmallVector<BasicBlock *, 16> reversePO(PO.rbegin(), PO.rend());
588
6
  llvh::SmallVector<HBCReifyArgumentsInst *, 4> reifications;
589
590
5.64k
  for (auto *BB : reversePO) {
591
307k
    for (auto &inst : BB->getInstList()) {
592
307k
      if (auto *reify = llvh::dyn_cast<HBCReifyArgumentsInst>(&inst)) {
593
12
        HBCReifyArgumentsInst *dominator = nullptr;
594
12
        for (auto *possibleParent : reifications) {
595
6
          if (domInfo.properlyDominates(possibleParent, reify)) {
596
6
            dominator = possibleParent;
597
6
            break;
598
6
          }
599
6
        }
600
601
12
        if (dominator) {
602
6
          destroyer.add(reify);
603
6
          changed = true;
604
6
        } else {
605
6
          reifications.push_back(reify);
606
6
        }
607
12
      }
608
307k
    }
609
5.64k
  }
610
6
  return changed;
611
1.33k
}
612
613
1.33k
bool LowerConstruction::runOnFunction(Function *F) {
614
1.33k
  IRBuilder builder(F);
615
1.33k
  auto *prototypeString = builder.getLiteralString("prototype");
616
617
59.6k
  for (BasicBlock &BB : F->getBasicBlockList()) {
618
59.6k
    IRBuilder::InstructionDestroyer destroyer;
619
749k
    for (Instruction &I : BB) {
620
749k
      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
749k
    }
641
59.6k
  }
642
1.33k
  return true;
643
1.33k
}
644
645
1.33k
bool LowerCalls::runOnFunction(Function *F) {
646
1.33k
  IRBuilder builder(F);
647
1.33k
  bool changed = false;
648
649
59.6k
  for (auto &BB : *F) {
650
1.25M
    for (auto &I : BB) {
651
1.25M
      auto *call = llvh::dyn_cast<CallInst>(&I);
652
      // This also matches constructors.
653
1.25M
      if (!call)
654
1.23M
        continue;
655
21.2k
      builder.setInsertionPoint(call);
656
21.2k
      changed = true;
657
658
21.2k
      auto reg = RA_.getLastRegister().getIndex() -
659
21.2k
          HVMRegisterAllocator::CALL_EXTRA_REGISTERS;
660
661
160k
      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
139k
        Value *arg = call->getArgument(i);
669
139k
        if (llvh::isa<HBCCallNInst>(call) ||
670
139k
            (i == 0 && llvh::isa<CallBuiltinInst>(call))) {
671
16.8k
          auto *imov = builder.createImplicitMovInst(arg);
672
16.8k
          RA_.updateRegister(imov, Register(reg));
673
122k
        } else {
674
122k
          auto *mov = builder.createMovInst(arg);
675
122k
          RA_.updateRegister(mov, Register(reg));
676
122k
          call->setArgument(mov, i);
677
122k
        }
678
139k
      }
679
21.2k
    }
680
59.6k
  }
681
1.33k
  return changed;
682
1.33k
}
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
873k
bool SpillRegisters::requiresShortOutput(Instruction *I) {
791
873k
  if (llvh::isa<TerminatorInst>(I)) {
792
    // None of our terminators produce results at all
793
    // (though GetNextPNameInst modifies operands).
794
17.3k
    return false;
795
17.3k
  }
796
797
  // Instructions that produce no output, don't use the register, even when
798
  // allocated.
799
856k
  if (I->getType().isNoType())
800
231
    return false;
801
802
856k
  switch (I->getKind()) {
803
    // Some instructions become Movs or other opcodes with long variants:
804
353k
    case ValueKind::HBCSpillMovInstKind:
805
354k
    case ValueKind::LoadStackInstKind:
806
493k
    case ValueKind::MovInstKind:
807
498k
    case ValueKind::PhiInstKind:
808
    // Some instructions aren't actually encoded at all:
809
499k
    case ValueKind::AllocStackInstKind:
810
500k
    case ValueKind::TryEndInstKind:
811
500k
    case ValueKind::TryStartInstKind:
812
500k
      return false;
813
356k
    default:
814
356k
      return true;
815
856k
  }
816
856k
}
817
818
809k
bool SpillRegisters::requiresShortOperand(Instruction *I, int op) {
819
809k
  switch (I->getKind()) {
820
8.94k
    case ValueKind::PhiInstKind:
821
147k
    case ValueKind::MovInstKind:
822
501k
    case ValueKind::HBCSpillMovInstKind:
823
502k
    case ValueKind::LoadStackInstKind:
824
502k
    case ValueKind::StoreStackInstKind:
825
502k
      return false;
826
38.1k
    case ValueKind::CallInstKind:
827
38.1k
    case ValueKind::ConstructInstKind:
828
87.4k
    case ValueKind::CallBuiltinInstKind:
829
87.4k
    case ValueKind::HBCConstructInstKind:
830
87.4k
    case ValueKind::HBCCallDirectInstKind:
831
87.4k
      return op == CallInst::CalleeIdx;
832
219k
    default:
833
219k
      return true;
834
809k
  }
835
809k
}
836
837
217k
bool SpillRegisters::modifiesOperandRegister(Instruction *I, int op) {
838
217k
  return I->getChangedOperands().at((unsigned)op);
839
217k
}
840
841
1.33k
bool SpillRegisters::runOnFunction(Function *F) {
842
1.33k
  if (RA_.getMaxRegisterUsage() < boundary_) {
843
1.33k
    return false;
844
1.33k
  }
845
7
  reserveLowRegisters(F);
846
847
7
  IRBuilder builder(F);
848
7
  llvh::SmallVector<std::pair<Instruction *, Register>, 2> toSpill;
849
850
17.3k
  for (BasicBlock &BB : F->getBasicBlockList()) {
851
873k
    for (Instruction &inst : BB) {
852
873k
      if (!RA_.isAllocated(&inst)) {
853
        // This instruction is dead. Don't bother spilling.
854
0
        continue;
855
0
      }
856
857
873k
      int tempReg = 0;
858
873k
      toSpill.clear();
859
873k
      bool replaceWithFirstSpill = false;
860
873k
      builder.setLocation(inst.getLocation());
861
873k
      builder.setCurrentSourceLevelScope(inst.getSourceLevelScope());
862
863
873k
      auto myRegister = RA_.getRegister(&inst);
864
873k
      if (requiresShortOutput(&inst) && !isShort(myRegister)) {
865
352k
        auto temp = getReserved(tempReg++);
866
352k
        RA_.updateRegister(&inst, temp);
867
352k
        toSpill.push_back(
868
352k
            std::pair<Instruction *, Register>(&inst, myRegister));
869
352k
        replaceWithFirstSpill = true;
870
352k
      }
871
872
1.92M
      for (int i = 0, e = inst.getNumOperands(); i < e; i++) {
873
1.05M
        auto *op = llvh::dyn_cast<Instruction>(inst.getOperand(i));
874
1.05M
        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
241k
          continue;
878
241k
        }
879
809k
        auto opRegister = RA_.getRegister(op);
880
881
809k
        if (requiresShortOperand(&inst, i) && !isShort(opRegister)) {
882
217k
          auto temp = getReserved(tempReg++);
883
884
217k
          builder.setInsertionPoint(&inst);
885
217k
          auto *load = builder.createHBCSpillMovInst(op);
886
217k
          RA_.updateRegister(load, temp);
887
217k
          inst.setOperand(load, i);
888
889
217k
          if (modifiesOperandRegister(&inst, i)) {
890
507
            toSpill.push_back(
891
507
                std::pair<Instruction *, Register>(load, opRegister));
892
507
          }
893
217k
        }
894
809k
      }
895
896
873k
      if (toSpill.size()) {
897
352k
        auto spillPoints = getInsertionPointsAfter(builder, &inst);
898
899
352k
        assert(
900
352k
            (!replaceWithFirstSpill || spillPoints.size() <= 1) &&
901
352k
            "Asked to spill the value of a TerminatorInst. Our terminators "
902
352k
            "shouldn't produce values. It wouldn't have mattered, but it "
903
352k
            "also has multiple branches so users might need PhiInsts.");
904
905
352k
        for (auto *point : spillPoints) {
906
352k
          builder.setInsertionPoint(point);
907
353k
          for (auto store : toSpill) {
908
353k
            auto *storeInst = builder.createHBCSpillMovInst(store.first);
909
353k
            RA_.updateRegister(storeInst, store.second);
910
911
353k
            if (!replaceWithFirstSpill)
912
507
              continue;
913
            // Replace all uses of the inst with the spilling inst
914
352k
            inst.replaceAllUsesWith(storeInst);
915
            // Except for the actual spill of course
916
352k
            storeInst->setOperand(&inst, 0);
917
            // Disable now that the job is done.
918
352k
            replaceWithFirstSpill = false;
919
352k
          }
920
352k
        }
921
352k
      }
922
873k
    }
923
17.3k
  }
924
7
  return true;
925
7
}
926
927
1.33k
bool LowerSwitchIntoJumpTables::runOnFunction(Function *F) {
928
1.33k
  bool changed = false;
929
1.33k
  llvh::SmallVector<SwitchInst *, 4> switches;
930
  // Collect all switch instructions.
931
1.33k
  for (BasicBlock &BB : *F)
932
749k
    for (auto &it : BB) {
933
749k
      if (auto *S = llvh::dyn_cast<SwitchInst>(&it))
934
0
        switches.push_back(S);
935
749k
    }
936
937
1.33k
  for (auto *S : switches) {
938
0
    if (lowerIntoJumpTable(S))
939
0
      changed = true;
940
0
  }
941
942
1.33k
  return changed;
943
1.33k
}
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