[CHR] Skip region containing llvm.coro.id

When a block containing llvm.coro.id is cloned during CHR, it inserts an invalid
PHI node with token type to the beginning of the block containing llvm.coro.begin.
To avoid such case, we exclude regions with llvm.coro.id.

Reviewed By: ChuanqiXu

Differential Revision: https://reviews.llvm.org/D124418
This commit is contained in:
Wei Wang 2022-04-25 14:00:41 -07:00
parent ccd047cba4
commit 26a0d53b15
2 changed files with 129 additions and 1 deletions

View File

@ -26,6 +26,7 @@
#include "llvm/IR/CFG.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/PassManager.h"
#include "llvm/InitializePasses.h"
@ -769,9 +770,21 @@ CHRScope * CHR::findScope(Region *R) {
return nullptr;
// If any of the basic blocks have address taken, we must skip this region
// because we cannot clone basic blocks that have address taken.
for (BasicBlock *BB : R->blocks())
for (BasicBlock *BB : R->blocks()) {
if (BB->hasAddressTaken())
return nullptr;
// If we encounter llvm.coro.id, skip this region because if the basic block
// is cloned, we end up inserting a token type PHI node to the block with
// llvm.coro.begin.
// FIXME: This could lead to less optimal codegen, because the region is
// excluded, it can prevent CHR from merging adjacent regions into bigger
// scope and hoisting more branches.
for (Instruction &I : *BB)
if (auto *II = dyn_cast<IntrinsicInst>(&I))
if (II->getIntrinsicID() == Intrinsic::coro_id)
return nullptr;
}
if (Exit) {
// Try to find an if-then block (check if R is an if-then).
// if (cond) {

View File

@ -0,0 +1,115 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
;RUN: opt < %s -passes='require<profile-summary>,function(chr,instcombine,simplifycfg)' -S | FileCheck %s
declare void @foo()
declare void @bar()
declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare noalias i8* @malloc(i32)
%f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1 }
; resume part of the coroutine
define fastcc void @f.resume(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
tail call void @bar()
ret void
}
; destroy part of the coroutine
define fastcc void @f.destroy(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
tail call void @bar()
ret void
}
; cleanup part of the coroutine
define fastcc void @f.cleanup(%f.Frame* noalias nonnull align 8 dereferenceable(24) %FramePtr) {
tail call void @bar()
ret void
}
@f.resumers = private constant [3 x void (%f.Frame*)*] [void (%f.Frame*)* @f.resume, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup]
; Test that chr will skip block containing llvm.coro.id.
define i8* @test_chr_with_coro_id(i32* %i) !prof !14 {
; CHECK-LABEL: @test_chr_with_coro_id(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[I:%.*]], align 4
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[TMP0]], 3
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 3
; CHECK-NEXT: br i1 [[TMP2]], label %[[BB0:.*]], label %[[ENTRY_SPLIT_NONCHR:.*]], !prof !15
; CHECK: [[BB0]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB_CORO_ID:.*]]
; CHECK: bb1.nonchr:
; CHECK-NEXT: [[TMP4:%.*]] = and i32 [[TMP0]], 2
; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i32 [[TMP4]], 0
; CHECK-NEXT: br i1 [[TMP5]], label %[[BB2_NONCHR:.*]], label %[[BB_CORO_ID]], !prof !16
; CHECK: [[BB2_NONCHR]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[BB_CORO_ID]]
; CHECK: [[BB_CORO_ID]]:
; CHECK-NEXT: [[ID:%.*]] = call token @llvm.coro.id
; CHECK-NEXT: [[NEED_DYN_ALLOC:%.*]] = call i1 @llvm.coro.alloc(token [[ID]])
; CHECK-NEXT: br i1 [[NEED_DYN_ALLOC]], label %[[BB_CORO_DYN_ALLOC:.*]], label %[[BB_CORO_BEGIN:.*]]
; CHECK: [[BB_CORO_BEGIN]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i8* [ null, %[[BB_CORO_ID]] ], [ %alloc, %[[BB_CORO_DYN_ALLOC]] ]
; CHECK-NEXT: [[HDL:%.*]] = call noalias nonnull i8* @llvm.coro.begin(token [[ID]], i8* [[PHI]])
;
entry:
%0 = load i32, i32* %i
%1 = and i32 %0, 1
%2 = icmp eq i32 %1, 0
br i1 %2, label %bb1, label %bb0, !prof !15
bb0:
call void @foo()
br label %bb1
bb1:
%3 = and i32 %0, 2
%4 = icmp eq i32 %3, 0
br i1 %4, label %bb2, label %bb.coro.id, !prof !15
bb2:
call void @foo()
br label %bb.coro.id
bb.coro.id:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* bitcast ([3 x void (%f.Frame*)*]* @f.resumers to i8*))
%need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
br i1 %need.dyn.alloc, label %bb.coro.dyn.alloc, label %bb.coro.begin
bb.coro.dyn.alloc:
%alloc = call i8* @malloc(i32 24)
br label %bb.coro.begin
bb.coro.begin:
%phi = phi i8* [ null, %bb.coro.id ], [ %alloc, %bb.coro.dyn.alloc ]
%hdl = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %phi)
ret i8* %hdl
}
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"ProfileSummary", !1}
!1 = !{!2, !3, !4, !5, !6, !7, !8, !9}
!2 = !{!"ProfileFormat", !"InstrProf"}
!3 = !{!"TotalCount", i64 10000}
!4 = !{!"MaxCount", i64 10}
!5 = !{!"MaxInternalCount", i64 1}
!6 = !{!"MaxFunctionCount", i64 1000}
!7 = !{!"NumCounts", i64 3}
!8 = !{!"NumFunctions", i64 3}
!9 = !{!"DetailedSummary", !10}
!10 = !{!11, !12, !13}
!11 = !{i32 10000, i64 100, i32 1}
!12 = !{i32 999000, i64 100, i32 1}
!13 = !{i32 999999, i64 1, i32 2}
!14 = !{!"function_entry_count", i64 100}
!15 = !{!"branch_weights", i32 0, i32 1}
!16 = !{!"branch_weights", i32 1, i32 1}
!17 = !{!"branch_weights", i32 0, i32 0}
; CHECK: !15 = !{!"branch_weights", i32 1000, i32 0}
; CHECK: !16 = !{!"branch_weights", i32 0, i32 1}