Skip to content

Commit 1f00e9a

Browse files
committed
[opt] Teach mem2reg about begin_access.
Mem2reg can now follow, project, and remove being_access instructions.
1 parent 40723a1 commit 1f00e9a

File tree

2 files changed

+141
-25
lines changed

2 files changed

+141
-25
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class MemoryToRegisters {
156156
SILBuilder B;
157157

158158
/// Check if the AllocStackInst \p ASI is only written into.
159-
bool isWriteOnlyAllocation(AllocStackInst *ASI);
159+
bool isWriteOnlyAllocation(SingleValueInstruction *addr);
160160

161161
/// Promote all of the AllocStacks in a single basic block in one
162162
/// linear scan. Note: This function deletes all of the users of the
@@ -227,7 +227,7 @@ static bool isAddressForLoad(SILInstruction *I, SILBasicBlock *&singleBlock,
227227
/// Returns true if \p I is a dead struct_element_addr or tuple_element_addr.
228228
static bool isDeadAddrProjection(SILInstruction *I) {
229229
if (!isa<UncheckedAddrCastInst>(I) && !isa<StructElementAddrInst>(I) &&
230-
!isa<TupleElementAddrInst>(I))
230+
!isa<TupleElementAddrInst>(I) && !isa<BeginAccessInst>(I))
231231
return false;
232232

233233
// Recursively search for uses which are dead themselves.
@@ -241,8 +241,8 @@ static bool isDeadAddrProjection(SILInstruction *I) {
241241

242242
/// Returns true if this AllocStacks is captured.
243243
/// Sets \p inSingleBlock to true if all uses of \p ASI are in a single block.
244-
static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
245-
244+
static bool isCaptured(SingleValueInstruction *ASI, bool &inSingleBlock) {
245+
246246
SILBasicBlock *singleBlock = ASI->getParent();
247247

248248
// For all users of the AllocStack instruction.
@@ -262,9 +262,10 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
262262
if (SI->getDest() == ASI)
263263
continue;
264264

265-
// Deallocation is also okay, as are DebugValueAddr. We will turn
266-
// the latter into DebugValue.
267-
if (isa<DeallocStackInst>(II) || isa<DebugValueAddrInst>(II))
265+
// Deallocation is also okay, as are EndAccess and DebugValueAddr. We will
266+
// turn the latter into DebugValue.
267+
if (isa<DeallocStackInst>(II) || isa<DebugValueAddrInst>(II) ||
268+
isa<EndAccessInst>(II))
268269
continue;
269270

270271
// Destroys of loadable types can be rewritten as releases, so
@@ -273,6 +274,13 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
273274
if (DAI->getOperand()->getType().isLoadable(*DAI->getFunction()))
274275
continue;
275276

277+
// Begin access is OK if it isn't captured.
278+
if (auto *beginAccess = dyn_cast<BeginAccessInst>(II)) {
279+
if (isCaptured(beginAccess, inSingleBlock))
280+
return true;
281+
continue;
282+
}
283+
276284
// Other instructions are assumed to capture the AllocStack.
277285
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack is captured by: " << *II);
278286
return true;
@@ -284,9 +292,9 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
284292
}
285293

286294
/// Returns true if the AllocStack is only stored into.
287-
bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI) {
295+
bool MemoryToRegisters::isWriteOnlyAllocation(SingleValueInstruction *addr) {
288296
// For all users of the AllocStack:
289-
for (auto UI = ASI->use_begin(), E = ASI->use_end(); UI != E; ++UI) {
297+
for (auto UI = addr->use_begin(), E = addr->use_end(); UI != E; ++UI) {
290298
SILInstruction *II = UI->getUser();
291299

292300
// It is okay to store into this AllocStack.
@@ -300,11 +308,16 @@ bool MemoryToRegisters::isWriteOnlyAllocation(AllocStackInst *ASI) {
300308

301309
// If we haven't already promoted the AllocStack, we may see
302310
// DebugValueAddr uses.
303-
if (isa<DebugValueAddrInst>(II))
311+
if (isa<DebugValueAddrInst>(II) || isDeadAddrProjection(II) ||
312+
isa<EndAccessInst>(II))
304313
continue;
305314

306-
if (isDeadAddrProjection(II))
307-
continue;
315+
// Begin access is OK if it is write only.
316+
if (auto *beginAccess = dyn_cast<BeginAccessInst>(II)) {
317+
if (isWriteOnlyAllocation(beginAccess))
318+
continue;
319+
return false;
320+
}
308321

309322
// Can't do anything else with it.
310323
LLVM_DEBUG(llvm::dbgs() << "*** AllocStack has non-write use: " << *II);
@@ -343,7 +356,7 @@ static bool isLoadFromStack(SILInstruction *I, AllocStackInst *ASI) {
343356
ValueBase *op = I->getOperand(0);
344357
while (op != ASI) {
345358
if (!isa<UncheckedAddrCastInst>(op) && !isa<StructElementAddrInst>(op) &&
346-
!isa<TupleElementAddrInst>(op))
359+
!isa<TupleElementAddrInst>(op) && !isa<BeginAccessInst>(op))
347360
return false;
348361

349362
op = cast<SingleValueInstruction>(op)->getOperand(0);
@@ -358,7 +371,7 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads)
358371
return;
359372
}
360373
if (!isa<UncheckedAddrCastInst>(I) && !isa<StructElementAddrInst>(I) &&
361-
!isa<TupleElementAddrInst>(I))
374+
!isa<TupleElementAddrInst>(I) && !isa<BeginAccessInst>(I))
362375
return;
363376

364377
// Recursively search for other loads in the instruction's uses.
@@ -375,9 +388,13 @@ static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) {
375388

376389
while (op != ASI) {
377390
assert(isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
378-
isa<TupleElementAddrInst>(op));
391+
isa<TupleElementAddrInst>(op) || isa<BeginAccessInst>(op));
379392
auto *Inst = cast<SingleValueInstruction>(op);
380-
projections.push_back(Projection(Inst));
393+
394+
// We don't want to project these instructions but we do want to follow
395+
// their operand.
396+
if (!isa<BeginAccessInst>(op))
397+
projections.push_back(Projection(Inst));
381398
op = Inst->getOperand(0);
382399
}
383400

@@ -421,11 +438,17 @@ static void replaceLoad(LoadInst *LI, SILValue val, AllocStackInst *ASI) {
421438

422439
while (op != ASI && op->use_empty()) {
423440
assert(isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
424-
isa<TupleElementAddrInst>(op));
441+
isa<TupleElementAddrInst>(op) || isa<BeginAccessInst>(op));
425442
auto *Inst = cast<SingleValueInstruction>(op);
426-
SILValue next = Inst->getOperand(0);
427-
Inst->eraseFromParent();
428-
op = next;
443+
op = Inst->getOperand(0);
444+
if (Inst->use_empty())
445+
Inst->eraseFromParent();
446+
if (auto singleUse = Inst->getSingleUse()) {
447+
if (isa<EndAccessInst>(singleUse->getUser())) {
448+
singleUse->getUser()->eraseFromParent();
449+
Inst->eraseFromParent();
450+
}
451+
}
429452
}
430453
}
431454

@@ -596,10 +619,10 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) {
596619
SILValue RunningVal = SILValue();
597620

598621
// For all instructions in the block.
599-
for (auto BBI = BB->begin(), E = BB->end(); BBI != E;) {
600-
SILInstruction *Inst = &*BBI;
601-
++BBI;
602-
622+
SmallVector<SILInstruction *, 64> allInstInBlock;
623+
for (auto &i : *BB)
624+
allInstInBlock.push_back(&i);
625+
for (auto *Inst : allInstInBlock) {
603626
// Remove instructions that we are loading from. Replace the loaded value
604627
// with our running value.
605628
if (isLoadFromStack(Inst, ASI)) {
@@ -665,7 +688,7 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) {
665688
SILNode *Node = Inst;
666689
while (isa<StructElementAddrInst>(Node) ||
667690
isa<TupleElementAddrInst>(Node) ||
668-
isa<UncheckedAddrCastInst>(Node)) {
691+
isa<UncheckedAddrCastInst>(Node) || isa<BeginAccessInst>(Node)) {
669692
auto *I = cast<SingleValueInstruction>(Node);
670693
if (!I->use_empty()) break;
671694
Node = I->getOperand(0);

test/SILOptimizer/mem2reg.sil

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,96 @@ bb3:
492492
%11 = tuple ()
493493
return %11 : $()
494494
}
495+
496+
// CHECK-LABEL: sil @single_block_with_access
497+
// CHECK: bb0
498+
// CHECK: [[IL:%.*]] = integer_literal
499+
// CHECK: [[X:%.*]] = struct $Int ([[IL]]
500+
// CHECK: return [[X]]
501+
// CHECK-LABEL: end sil function 'single_block_with_access'
502+
sil @single_block_with_access : $@convention(thin) () -> Int {
503+
bb0:
504+
%1 = integer_literal $Builtin.Int64, 0 // user: %2
505+
%2 = struct $Int (%1 : $Builtin.Int64) // user: %4
506+
%3 = alloc_stack $Int // users: %8, %5, %4
507+
store %2 to %3 : $*Int // id: %4
508+
%5 = begin_access [read] [dynamic] [no_nested_conflict] %3 : $*Int // users: %6, %7
509+
%6 = load %5 : $*Int // user: %9
510+
end_access %5 : $*Int // id: %7
511+
dealloc_stack %3 : $*Int // id: %8
512+
return %6 : $Int // id: %9
513+
}
514+
515+
// CHECK-LABEL: sil @multi_block_with_access
516+
// CHECK-NOT: alloc_stack
517+
// CHECK-NOT: begin_access
518+
519+
// CHECK: bb0(%0 : $Bool):
520+
// CHECK: [[IL:%.*]] = integer_literal
521+
// CHECK-NEXT: [[X:%.*]] = struct $Int ([[IL]]
522+
// CHECK-NEXT: [[COND:%.*]] = struct_extract %0
523+
// CHECK-NEXT: cond_br [[COND]]
524+
525+
// CHECK: bb2:
526+
// CHECK-NEXT: [[IL2:%.*]] = integer_literal
527+
// CHECK-NEXT: [[XVAL:%.*]] = struct_extract [[X]]
528+
// CHECK-NEXT: integer_literal
529+
// CHECK-NEXT: [[ADD1:%.*]] = builtin "sadd_with_overflow_Int64"([[XVAL]] : {{.*}}, [[IL2]]
530+
// CHECK-NEXT: tuple_extract
531+
// CHECK-NEXT: cond_fail
532+
// CHECK-NEXT: br
533+
534+
// CHECK: bb3:
535+
// CHECK-NEXT: [[XVAL2:%.*]] = struct_extract [[X]]
536+
// CHECK-NEXT: [[IL3:%.*]] = integer_literal
537+
// CHECK-NEXT: integer_literal
538+
// CHECK-NEXT: [[ADD2:%.*]] = builtin "sadd_with_overflow_Int64"([[XVAL2]] : {{.*}}, [[IL3]]
539+
// CHECK-NEXT: [[SUM_VAL:%.*]] = tuple_extract [[ADD2]]
540+
// CHECK-NEXT: tuple_extract
541+
// CHECK-NEXT: cond_fail
542+
// CHECK-NEXT: [[OUT:%.*]] = struct $Int ([[SUM_VAL]]
543+
// CHECK-NEXT: return [[OUT]]
544+
545+
// CHECK: } // end sil function 'multi_block_with_access'
546+
sil @multi_block_with_access : $@convention(thin) (Bool) -> Int {
547+
bb0(%0 : $Bool):
548+
%2 = integer_literal $Builtin.Int64, 0 // user: %3
549+
%3 = struct $Int (%2 : $Builtin.Int64) // user: %5
550+
%4 = alloc_stack $Int // users: %26, %10, %22, %5
551+
store %3 to %4 : $*Int // id: %5
552+
%6 = struct_extract %0 : $Bool, #Bool._value // user: %7
553+
cond_br %6, bb2, bb1 // id: %7
554+
555+
bb1: // Preds: bb0
556+
br bb3 // id: %8
557+
558+
bb2: // Preds: bb0
559+
%9 = integer_literal $Builtin.Int64, 1 // user: %14
560+
%10 = begin_access [modify] [static] [no_nested_conflict] %4 : $*Int // users: %20, %19, %11
561+
%11 = struct_element_addr %10 : $*Int, #Int._value // user: %12
562+
%12 = load %11 : $*Builtin.Int64 // user: %14
563+
%13 = integer_literal $Builtin.Int1, -1 // user: %14
564+
%14 = builtin "sadd_with_overflow_Int64"(%12 : $Builtin.Int64, %9 : $Builtin.Int64, %13 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %16, %15
565+
%15 = tuple_extract %14 : $(Builtin.Int64, Builtin.Int1), 0 // user: %18
566+
%16 = tuple_extract %14 : $(Builtin.Int64, Builtin.Int1), 1 // user: %17
567+
cond_fail %16 : $Builtin.Int1, "arithmetic overflow" // id: %17
568+
%18 = struct $Int (%15 : $Builtin.Int64) // user: %19
569+
store %18 to %10 : $*Int // id: %19
570+
end_access %10 : $*Int // id: %20
571+
br bb3 // id: %21
572+
573+
bb3: // Preds: bb1 bb2
574+
%22 = begin_access [read] [static] [no_nested_conflict] %4 : $*Int // users: %25, %23
575+
%23 = struct_element_addr %22 : $*Int, #Int._value // user: %24
576+
%24 = load %23 : $*Builtin.Int64 // user: %29
577+
end_access %22 : $*Int // id: %25
578+
dealloc_stack %4 : $*Int // id: %26
579+
%27 = integer_literal $Builtin.Int64, 2 // user: %29
580+
%28 = integer_literal $Builtin.Int1, -1 // user: %29
581+
%29 = builtin "sadd_with_overflow_Int64"(%24 : $Builtin.Int64, %27 : $Builtin.Int64, %28 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // users: %31, %30
582+
%30 = tuple_extract %29 : $(Builtin.Int64, Builtin.Int1), 0 // user: %33
583+
%31 = tuple_extract %29 : $(Builtin.Int64, Builtin.Int1), 1 // user: %32
584+
cond_fail %31 : $Builtin.Int1, "arithmetic overflow" // id: %32
585+
%33 = struct $Int (%30 : $Builtin.Int64) // user: %34
586+
return %33 : $Int // id: %34
587+
}

0 commit comments

Comments
 (0)