Skip to content

Commit f581704

Browse files
committed
[MC/DC][Coverage] Make tvbitmapupdate capable of atomic write
This also introduces "Test and conditional Read-Modify-Write". The flow to `atomicrmw or` is marked as `unlikely`. This includes llvm#96040.
1 parent 7ef2bd4 commit f581704

File tree

2 files changed

+92
-15
lines changed

2 files changed

+92
-15
lines changed

llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "llvm/IR/Instruction.h"
3939
#include "llvm/IR/Instructions.h"
4040
#include "llvm/IR/IntrinsicInst.h"
41+
#include "llvm/IR/MDBuilder.h"
4142
#include "llvm/IR/Module.h"
4243
#include "llvm/IR/Type.h"
4344
#include "llvm/InitializePasses.h"
@@ -237,6 +238,10 @@ class InstrLowerer final {
237238
GlobalVariable *NamesVar = nullptr;
238239
size_t NamesSize = 0;
239240

241+
/// The instance of [[forceinline]] rmw_or(ptr, i8).
242+
/// This is name-insensitive.
243+
Function *RMWOrFunc = nullptr;
244+
240245
// vector of counter load/store pairs to be register promoted.
241246
std::vector<LoadStorePair> PromotionCandidates;
242247

@@ -297,6 +302,14 @@ class InstrLowerer final {
297302
StringRef Name,
298303
GlobalValue::LinkageTypes Linkage);
299304

305+
/// Create [[forceinline]] rmw_or(ptr, i8).
306+
/// This doesn't update `RMWOrFunc`.
307+
Function *createRMWOrFunc();
308+
309+
/// Get the call to `rmw_or`.
310+
/// Create the instance if it is unknown.
311+
CallInst *getRMWOrCall(Value *Addr, Value *Val);
312+
300313
/// Compute the address of the test vector bitmap that this profiling
301314
/// instruction acts on.
302315
Value *getBitmapAddress(InstrProfMCDCTVBitmapUpdate *I);
@@ -937,6 +950,67 @@ Value *InstrLowerer::getCounterAddress(InstrProfCntrInstBase *I) {
937950
return Builder.CreateIntToPtr(Add, Addr->getType());
938951
}
939952

953+
Function *InstrLowerer::createRMWOrFunc() {
954+
auto &Ctx = M.getContext();
955+
auto *Int8Ty = Type::getInt8Ty(Ctx);
956+
// void alwaysinline rmw_or(ptr, i8)
957+
Function *Fn = Function::Create(
958+
FunctionType::get(Type::getVoidTy(Ctx),
959+
{PointerType::getUnqual(Ctx), Int8Ty}, false),
960+
Function::LinkageTypes::PrivateLinkage, "rmw_or", M);
961+
Fn->addFnAttr(Attribute::AlwaysInline);
962+
auto *ArgAddr = Fn->getArg(0);
963+
auto *ArgVal = Fn->getArg(1);
964+
IRBuilder<> Builder(BasicBlock::Create(Ctx, "", Fn));
965+
966+
// Load profile bitmap byte.
967+
// %mcdc.bits = load i8, ptr %4, align 1
968+
auto *Bitmap = Builder.CreateLoad(Int8Ty, ArgAddr, "mcdc.bits");
969+
970+
if (Options.Atomic || AtomicCounterUpdateAll) {
971+
// If ((Bitmap & Val) != Val), then execute atomic (Bitmap |= Val).
972+
// Note, just-loaded Bitmap might not be up-to-date. Use it just for
973+
// early testing.
974+
auto *Masked = Builder.CreateAnd(Bitmap, ArgVal);
975+
auto *ShouldStore = Builder.CreateICmpNE(Masked, ArgVal);
976+
auto *ThenTerm = BasicBlock::Create(Ctx, "", Fn);
977+
auto *ElseTerm = BasicBlock::Create(Ctx, "", Fn);
978+
// Assume updating will be rare.
979+
auto *Unlikely = MDBuilder(Ctx).createUnlikelyBranchWeights();
980+
Builder.CreateCondBr(ShouldStore, ThenTerm, ElseTerm, Unlikely);
981+
982+
IRBuilder<> ThenBuilder(ThenTerm);
983+
ThenBuilder.CreateAtomicRMW(AtomicRMWInst::Or, ArgAddr, ArgVal,
984+
MaybeAlign(), AtomicOrdering::Monotonic);
985+
ThenBuilder.CreateRetVoid();
986+
987+
IRBuilder<> ElseBuilder(ElseTerm);
988+
ElseBuilder.CreateRetVoid();
989+
990+
return Fn;
991+
}
992+
993+
// Perform logical OR of profile bitmap byte and shifted bit offset.
994+
// %8 = or i8 %mcdc.bits, %7
995+
auto *Result = Builder.CreateOr(Bitmap, ArgVal);
996+
997+
// Store the updated profile bitmap byte.
998+
// store i8 %8, ptr %3, align 1
999+
Builder.CreateStore(Result, ArgAddr);
1000+
1001+
// Terminator
1002+
Builder.CreateRetVoid();
1003+
1004+
return Fn;
1005+
}
1006+
1007+
CallInst *InstrLowerer::getRMWOrCall(Value *Addr, Value *Val) {
1008+
if (!RMWOrFunc)
1009+
RMWOrFunc = createRMWOrFunc();
1010+
1011+
return CallInst::Create(RMWOrFunc, {Addr, Val});
1012+
}
1013+
9401014
Value *InstrLowerer::getBitmapAddress(InstrProfMCDCTVBitmapUpdate *I) {
9411015
auto *Bitmaps = getOrCreateRegionBitmaps(I);
9421016
IRBuilder<> Builder(I);
@@ -1044,17 +1118,7 @@ void InstrLowerer::lowerMCDCTestVectorBitmapUpdate(
10441118
// %7 = shl i8 1, %6
10451119
auto *ShiftedVal = Builder.CreateShl(Builder.getInt8(0x1), BitToSet);
10461120

1047-
// Load profile bitmap byte.
1048-
// %mcdc.bits = load i8, ptr %4, align 1
1049-
auto *Bitmap = Builder.CreateLoad(Int8Ty, BitmapByteAddr, "mcdc.bits");
1050-
1051-
// Perform logical OR of profile bitmap byte and shifted bit offset.
1052-
// %8 = or i8 %mcdc.bits, %7
1053-
auto *Result = Builder.CreateOr(Bitmap, ShiftedVal);
1054-
1055-
// Store the updated profile bitmap byte.
1056-
// store i8 %8, ptr %3, align 1
1057-
Builder.CreateStore(Result, BitmapByteAddr);
1121+
Builder.Insert(getRMWOrCall(BitmapByteAddr, ShiftedVal));
10581122
Update->eraseFromParent();
10591123
}
10601124

llvm/test/Instrumentation/InstrProfiling/mcdc.ll

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
; Check that MC/DC intrinsics are properly lowered
2-
; RUN: opt < %s -passes=instrprof -S | FileCheck %s
2+
; RUN: opt < %s -passes=instrprof -S | FileCheck %s --check-prefixes=CHECK,BASIC
3+
; RUN: opt < %s -passes=instrprof -S -instrprof-atomic-counter-update-all | FileCheck %s --check-prefixes=CHECK,ATOMIC
34
; RUN: opt < %s -passes=instrprof -runtime-counter-relocation -S 2>&1 | FileCheck %s --check-prefix RELOC
45

56
; RELOC: Runtime counter relocation is presently not supported for MC/DC bitmaps
@@ -30,12 +31,24 @@ entry:
3031
; CHECK-NEXT: %[[LAB8:[0-9]+]] = and i32 %[[TEMP]], 7
3132
; CHECK-NEXT: %[[LAB9:[0-9]+]] = trunc i32 %[[LAB8]] to i8
3233
; CHECK-NEXT: %[[LAB10:[0-9]+]] = shl i8 1, %[[LAB9]]
33-
; CHECK-NEXT: %[[BITS:mcdc.*]] = load i8, ptr %[[LAB7]], align 1
34-
; CHECK-NEXT: %[[LAB11:[0-9]+]] = or i8 %[[BITS]], %[[LAB10]]
35-
; CHECK-NEXT: store i8 %[[LAB11]], ptr %[[LAB7]], align 1
34+
; CHECK-NEXT: call void @[[RMW_OR:.+]](ptr %[[LAB7]], i8 %[[LAB10]])
3635
ret void
3736
}
3837

38+
; CHECK: define private void @[[RMW_OR]](ptr %[[ARGPTR:.+]], i8 %[[ARGVAL:.+]])
39+
; CHECK: %[[BITS:.+]] = load i8, ptr %[[ARGPTR]], align 1
40+
; BASIC-NEXT: %[[LAB11:[0-9]+]] = or i8 %[[BITS]], %[[ARGVAL]]
41+
; BASIC-NEXT: store i8 %[[LAB11]], ptr %[[ARGPTR]], align 1
42+
; ATOMIC-NEXT: %[[MASKED:.+]] = and i8 %[[BITS]], %[[ARGVAL]]
43+
; ATOMIC-NEXT: %[[SHOULDWRITE:.+]] = icmp ne i8 %[[MASKED]], %[[ARGVAL]]
44+
; ATOMIC-NEXT: br i1 %[[SHOULDWRITE]], label %[[WRITE:.+]], label %[[SKIP:.+]], !prof ![[MDPROF:[0-9]+]]
45+
; ATOMIC: [[WRITE]]:
46+
; ATOMIC-NEXT: %{{.+}} = atomicrmw or ptr %[[ARGPTR]], i8 %[[ARGVAL]] monotonic, align 1
47+
; ATOMIC-NEXT: ret void
48+
; ATOMIC: [[SKIP]]:
49+
; CHECK-NEXT: ret void
50+
; ATOMIC: ![[MDPROF]] = !{!"branch_weights", i32 1, i32 1048575}
51+
3952
declare void @llvm.instrprof.cover(ptr, i64, i32, i32)
4053

4154
declare void @llvm.instrprof.mcdc.parameters(ptr, i64, i32)

0 commit comments

Comments
 (0)