Skip to content

Commit 6b69a06

Browse files
neildharfacebook-github-bot
authored andcommitted
Recycle block local registers in fast pass
Summary: The register allocator has the ability to honour a memory limit that is proportional to the product of the number of instructions and basic blocks in the function being allocated. Unfortunately, functions that hit this limit by definition have a lot of instructions Even in the most degenerate case where every block has one instruction, you need 4000 instructions to hit the 10M limit. This diff tries to improve the quality of generated code in cases where most values are used within the basic block they are defined in. In such cases, we currently make the register available after the end of the block. With this diff, the registers become available after their last use in the block. This is useful for functions with extremely large basic blocks, where the current approach would end up allocating a huge number of registers since the registers cannot be used within the same block. Reviewed By: avp Differential Revision: D59072005 fbshipit-source-id: 83109ad366ac1a0a5445806fce407d3a42ef2232
1 parent 23cdae6 commit 6b69a06

File tree

10 files changed

+753
-736
lines changed

10 files changed

+753
-736
lines changed

include/hermes/BCGen/RegAlloc.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,9 @@ class RegisterAllocator {
381381
/// predecessor blocks.
382382
void lowerPhis(ArrayRef<BasicBlock *> order);
383383

384-
/// Allocate the registers for the instructions in the function. The order of
385-
/// the block needs to match the order which we'll use for instruction
386-
/// selection.
384+
/// Allocate the registers for the instructions in the function. The blocks
385+
/// must be in reverse-post-order, and must match the order which we'll use
386+
/// for instruction selection.
387387
void allocate(ArrayRef<BasicBlock *> order);
388388

389389
/// Reserves consecutive registers that will be manually managed by the user.

lib/BCGen/RegAlloc.cpp

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -512,19 +512,6 @@ void RegisterAllocator::coalesce(
512512
}
513513
}
514514

515-
namespace {
516-
/// Determines whether the Instruction is ever used outside its BasicBlock.
517-
bool isBlockLocal(Instruction *inst) {
518-
BasicBlock *parent = inst->getParent();
519-
for (auto user : inst->getUsers()) {
520-
if (parent != user->getParent()) {
521-
return false;
522-
}
523-
}
524-
return true;
525-
}
526-
} // namespace
527-
528515
void RegisterAllocator::allocateFastPass(ArrayRef<BasicBlock *> order) {
529516
// Make sure Phis and related Movs get the same register
530517
for (auto *bb : order) {
@@ -540,26 +527,69 @@ void RegisterAllocator::allocateFastPass(ArrayRef<BasicBlock *> order) {
540527
}
541528
}
542529

543-
llvh::SmallVector<Register, 16> blockLocals;
530+
// Bit vector indicating whether a register with a given index is being used
531+
// as a block local register.
532+
llvh::BitVector blockLocals;
533+
534+
// List of free block local registers. We have to maintain this outside the
535+
// file because we cannot determine interference between local and global
536+
// registers. So we have to ensure that the local registers are only reused
537+
// for other block-local instructions.
538+
llvh::SmallVector<Register, 8> freeBlockLocals;
539+
540+
// A dummy register used for all instructions that have no users.
541+
Register deadReg = file.allocateRegister();
542+
543+
// Iterate in reverse, so we can cheaply determine whether an instruction
544+
// is local, and assign it a register accordingly.
545+
for (auto *bb : llvh::reverse(order)) {
546+
for (auto &inst : llvh::reverse(*bb)) {
547+
if (isAllocated(&inst)) {
548+
// If this is using a local register, we know the register is free after
549+
// we visit the definition.
550+
auto reg = getRegister(&inst);
551+
auto idx = reg.getIndex();
552+
if (idx < blockLocals.size() && blockLocals.test(idx))
553+
freeBlockLocals.push_back(reg);
554+
} else {
555+
// Unallocated instruction means the result is dead, because all users
556+
// are visited first. Allocate a temporary register.
557+
assert(!inst.hasUsers());
558+
updateRegister(&inst, deadReg);
559+
}
544560

545-
// Then just allocate the rest sequentially, while optimizing the case
546-
// where an inst is only ever used in its own block.
547-
for (auto *bb : order) {
548-
for (auto &inst : *bb) {
549-
if (!isAllocated(&inst)) {
550-
Register R = file.allocateRegister();
551-
updateRegister(&inst, R);
552-
if (inst.getNumUsers() == 0) {
553-
file.killRegister(R);
554-
} else if (isBlockLocal(&inst)) {
555-
blockLocals.push_back(R);
561+
// Allocate a register to unallocated operands.
562+
for (size_t i = 0, e = inst.getNumOperands(); i < e; ++i) {
563+
auto *op = llvh::dyn_cast<Instruction>(inst.getOperand(i));
564+
565+
// Skip if op is not an instruction or already has a register.
566+
if (!op || isAllocated(op))
567+
continue;
568+
569+
if (op->getParent() != bb) {
570+
// Live across blocks, allocate a global regigster.
571+
updateRegister(op, file.allocateRegister());
572+
continue;
573+
}
574+
575+
// We know this operand is local because:
576+
// 1. The operand is in the same block as this one.
577+
// 2. All blocks dominated by this block have been visited.
578+
// 3. All users must be dominated by their def, since Phis are
579+
// allocated beforehand.
580+
if (!freeBlockLocals.empty()) {
581+
updateRegister(op, freeBlockLocals.pop_back_val());
582+
continue;
556583
}
584+
585+
// No free local register, allocate another one.
586+
Register reg = file.allocateRegister();
587+
if (blockLocals.size() <= reg.getIndex())
588+
blockLocals.resize(reg.getIndex() + 1);
589+
blockLocals.set(reg.getIndex());
590+
updateRegister(op, reg);
557591
}
558592
}
559-
for (auto &reg : blockLocals) {
560-
file.killRegister(reg);
561-
}
562-
blockLocals.clear();
563593
}
564594
}
565595

test/BCGen/HBC/RA/assign_to_argument.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ function foo(a) {
1717

1818
// CHECK:function global(): any
1919
// CHECK-NEXT:%BB0:
20-
// CHECK-NEXT: $Reg0 = CreateScopeInst (:environment) %VS0: any, empty: any
21-
// CHECK-NEXT: $Reg1 = DeclareGlobalVarInst "foo": string
22-
// CHECK-NEXT: $Reg1 = CreateFunctionInst (:object) $Reg0, %foo(): functionCode
20+
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS0: any, empty: any
21+
// CHECK-NEXT: $Reg0 = DeclareGlobalVarInst "foo": string
22+
// CHECK-NEXT: $Reg1 = CreateFunctionInst (:object) $Reg1, %foo(): functionCode
2323
// CHECK-NEXT: $Reg2 = HBCGetGlobalObjectInst (:object)
24-
// CHECK-NEXT: $Reg3 = StorePropertyLooseInst $Reg1, $Reg2, "foo": string
25-
// CHECK-NEXT: $Reg3 = AllocStackInst (:any) $?anon_0_ret: any
26-
// CHECK-NEXT: $Reg4 = HBCLoadConstInst (:undefined) undefined: undefined
27-
// CHECK-NEXT: $Reg5 = StoreStackInst $Reg4, $Reg3
28-
// CHECK-NEXT: $Reg5 = LoadStackInst (:any) $Reg3
29-
// CHECK-NEXT: $Reg6 = ReturnInst $Reg5
24+
// CHECK-NEXT: $Reg0 = StorePropertyLooseInst $Reg1, $Reg2, "foo": string
25+
// CHECK-NEXT: $Reg1 = AllocStackInst (:any) $?anon_0_ret: any
26+
// CHECK-NEXT: $Reg2 = HBCLoadConstInst (:undefined) undefined: undefined
27+
// CHECK-NEXT: $Reg0 = StoreStackInst $Reg2, $Reg1
28+
// CHECK-NEXT: $Reg1 = LoadStackInst (:any) $Reg1
29+
// CHECK-NEXT: $Reg0 = ReturnInst $Reg1
3030
// CHECK-NEXT:function_end
3131

3232
// CHECK:scope %VS0 []
@@ -35,12 +35,12 @@ function foo(a) {
3535

3636
// CHECK:function foo(a: any): any
3737
// CHECK-NEXT:%BB0:
38-
// CHECK-NEXT: $Reg0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
39-
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS1: any, $Reg0
38+
// CHECK-NEXT: $Reg1 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
39+
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS1: any, $Reg1
4040
// CHECK-NEXT: $Reg2 = LoadParamInst (:any) %a: any
41-
// CHECK-NEXT: $Reg3 = StoreFrameInst $Reg1, $Reg2, [%VS1.a]: any
42-
// CHECK-NEXT: $Reg3 = LoadFrameInst (:any) $Reg1, [%VS1.a]: any
43-
// CHECK-NEXT: $Reg4 = StoreFrameInst $Reg1, $Reg3, [%VS1.a]: any
44-
// CHECK-NEXT: $Reg4 = HBCLoadConstInst (:undefined) undefined: undefined
45-
// CHECK-NEXT: $Reg5 = ReturnInst $Reg4
41+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg1, $Reg2, [%VS1.a]: any
42+
// CHECK-NEXT: $Reg2 = LoadFrameInst (:any) $Reg1, [%VS1.a]: any
43+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg1, $Reg2, [%VS1.a]: any
44+
// CHECK-NEXT: $Reg1 = HBCLoadConstInst (:undefined) undefined: undefined
45+
// CHECK-NEXT: $Reg0 = ReturnInst $Reg1
4646
// CHECK-NEXT:function_end

test/BCGen/HBC/RA/callee.js

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ function foo(x) {
1919

2020
// CHECK:function global(): any
2121
// CHECK-NEXT:%BB0:
22-
// CHECK-NEXT: $Reg0 = CreateScopeInst (:environment) %VS0: any, empty: any
23-
// CHECK-NEXT: $Reg1 = DeclareGlobalVarInst "sink": string
24-
// CHECK-NEXT: $Reg1 = DeclareGlobalVarInst "foo": string
25-
// CHECK-NEXT: $Reg1 = CreateFunctionInst (:object) $Reg0, %sink(): functionCode
22+
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS0: any, empty: any
23+
// CHECK-NEXT: $Reg0 = DeclareGlobalVarInst "sink": string
24+
// CHECK-NEXT: $Reg0 = DeclareGlobalVarInst "foo": string
25+
// CHECK-NEXT: $Reg2 = CreateFunctionInst (:object) $Reg1, %sink(): functionCode
26+
// CHECK-NEXT: $Reg3 = HBCGetGlobalObjectInst (:object)
27+
// CHECK-NEXT: $Reg0 = StorePropertyLooseInst $Reg2, $Reg3, "sink": string
28+
// CHECK-NEXT: $Reg1 = CreateFunctionInst (:object) $Reg1, %foo(): functionCode
2629
// CHECK-NEXT: $Reg2 = HBCGetGlobalObjectInst (:object)
27-
// CHECK-NEXT: $Reg3 = StorePropertyLooseInst $Reg1, $Reg2, "sink": string
28-
// CHECK-NEXT: $Reg3 = CreateFunctionInst (:object) $Reg0, %foo(): functionCode
29-
// CHECK-NEXT: $Reg4 = HBCGetGlobalObjectInst (:object)
30-
// CHECK-NEXT: $Reg5 = StorePropertyLooseInst $Reg3, $Reg4, "foo": string
31-
// CHECK-NEXT: $Reg5 = AllocStackInst (:any) $?anon_0_ret: any
32-
// CHECK-NEXT: $Reg6 = HBCLoadConstInst (:undefined) undefined: undefined
33-
// CHECK-NEXT: $Reg7 = StoreStackInst $Reg6, $Reg5
34-
// CHECK-NEXT: $Reg7 = LoadStackInst (:any) $Reg5
35-
// CHECK-NEXT: $Reg8 = ReturnInst $Reg7
30+
// CHECK-NEXT: $Reg0 = StorePropertyLooseInst $Reg1, $Reg2, "foo": string
31+
// CHECK-NEXT: $Reg1 = AllocStackInst (:any) $?anon_0_ret: any
32+
// CHECK-NEXT: $Reg2 = HBCLoadConstInst (:undefined) undefined: undefined
33+
// CHECK-NEXT: $Reg0 = StoreStackInst $Reg2, $Reg1
34+
// CHECK-NEXT: $Reg1 = LoadStackInst (:any) $Reg1
35+
// CHECK-NEXT: $Reg0 = ReturnInst $Reg1
3636
// CHECK-NEXT:function_end
3737

3838
// CHECK:scope %VS0 []
@@ -41,16 +41,16 @@ function foo(x) {
4141

4242
// CHECK:function sink(x: any, y: any, z: any): any
4343
// CHECK-NEXT:%BB0:
44-
// CHECK-NEXT: $Reg0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
45-
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS1: any, $Reg0
44+
// CHECK-NEXT: $Reg1 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
45+
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS1: any, $Reg1
4646
// CHECK-NEXT: $Reg2 = LoadParamInst (:any) %x: any
47-
// CHECK-NEXT: $Reg3 = StoreFrameInst $Reg1, $Reg2, [%VS1.x]: any
48-
// CHECK-NEXT: $Reg3 = LoadParamInst (:any) %y: any
49-
// CHECK-NEXT: $Reg4 = StoreFrameInst $Reg1, $Reg3, [%VS1.y]: any
50-
// CHECK-NEXT: $Reg4 = LoadParamInst (:any) %z: any
51-
// CHECK-NEXT: $Reg5 = StoreFrameInst $Reg1, $Reg4, [%VS1.z]: any
52-
// CHECK-NEXT: $Reg5 = HBCLoadConstInst (:undefined) undefined: undefined
53-
// CHECK-NEXT: $Reg6 = ReturnInst $Reg5
47+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg1, $Reg2, [%VS1.x]: any
48+
// CHECK-NEXT: $Reg2 = LoadParamInst (:any) %y: any
49+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg1, $Reg2, [%VS1.y]: any
50+
// CHECK-NEXT: $Reg2 = LoadParamInst (:any) %z: any
51+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg1, $Reg2, [%VS1.z]: any
52+
// CHECK-NEXT: $Reg1 = HBCLoadConstInst (:undefined) undefined: undefined
53+
// CHECK-NEXT: $Reg0 = ReturnInst $Reg1
5454
// CHECK-NEXT:function_end
5555

5656
// CHECK:scope %VS0 []
@@ -59,17 +59,17 @@ function foo(x) {
5959

6060
// CHECK:function foo(x: any): any
6161
// CHECK-NEXT:%BB0:
62-
// CHECK-NEXT: $Reg0 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
63-
// CHECK-NEXT: $Reg1 = CreateScopeInst (:environment) %VS1: any, $Reg0
64-
// CHECK-NEXT: $Reg2 = LoadParamInst (:any) %x: any
65-
// CHECK-NEXT: $Reg3 = StoreFrameInst $Reg1, $Reg2, [%VS1.x]: any
66-
// CHECK-NEXT: $Reg3 = LoadFrameInst (:any) $Reg1, [%VS1.x]: any
67-
// CHECK-NEXT: $Reg4 = LoadPropertyInst (:any) $Reg3, "sink": string
68-
// CHECK-NEXT: $Reg5 = HBCLoadConstInst (:undefined) undefined: undefined
69-
// CHECK-NEXT: $Reg6 = HBCLoadConstInst (:number) 1: number
70-
// CHECK-NEXT: $Reg7 = HBCLoadConstInst (:number) 2: number
71-
// CHECK-NEXT: $Reg8 = HBCLoadConstInst (:number) 3: number
72-
// CHECK-NEXT: $Reg9 = HBCCallNInst (:any) $Reg4, empty: any, empty: any, $Reg5, $Reg3, $Reg6, $Reg7, $Reg8
73-
// CHECK-NEXT: $Reg9 = HBCLoadConstInst (:undefined) undefined: undefined
74-
// CHECK-NEXT: $Reg10 = ReturnInst $Reg9
62+
// CHECK-NEXT: $Reg3 = GetParentScopeInst (:environment) %VS0: any, %parentScope: environment
63+
// CHECK-NEXT: $Reg3 = CreateScopeInst (:environment) %VS1: any, $Reg3
64+
// CHECK-NEXT: $Reg1 = LoadParamInst (:any) %x: any
65+
// CHECK-NEXT: $Reg0 = StoreFrameInst $Reg3, $Reg1, [%VS1.x]: any
66+
// CHECK-NEXT: $Reg3 = LoadFrameInst (:any) $Reg3, [%VS1.x]: any
67+
// CHECK-NEXT: $Reg1 = LoadPropertyInst (:any) $Reg3, "sink": string
68+
// CHECK-NEXT: $Reg2 = HBCLoadConstInst (:undefined) undefined: undefined
69+
// CHECK-NEXT: $Reg4 = HBCLoadConstInst (:number) 1: number
70+
// CHECK-NEXT: $Reg5 = HBCLoadConstInst (:number) 2: number
71+
// CHECK-NEXT: $Reg6 = HBCLoadConstInst (:number) 3: number
72+
// CHECK-NEXT: $Reg0 = HBCCallNInst (:any) $Reg1, empty: any, empty: any, $Reg2, $Reg3, $Reg4, $Reg5, $Reg6
73+
// CHECK-NEXT: $Reg1 = HBCLoadConstInst (:undefined) undefined: undefined
74+
// CHECK-NEXT: $Reg0 = ReturnInst $Reg1
7575
// CHECK-NEXT:function_end

0 commit comments

Comments
 (0)