Skip to content

Commit 88c3902

Browse files
committed
Add nested loop CFG fuzzing
1 parent 1d6c697 commit 88c3902

2 files changed

Lines changed: 111 additions & 26 deletions

File tree

‎amdgpu/README.md‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ Coverage includes scalar integer arithmetic, bitwise ops, compares/selects,
1919
subexpressions reduced back to `i32`, explicit `i1` boolean subexpressions
2020
reduced back to `i32`, and LLVM bit, min/max, saturation, absolute-value, and
2121
funnel-shift intrinsics. The mutator can also wrap the current result in
22-
structured two-way branches, wider multi-way switches, and deeper bounded
23-
nested acyclic subgraphs with `i32` phi joins. It also generates counted loops
24-
with small bounded constant or dynamically masked trip counts whose bodies can
25-
contain nested diamonds and switches. Some generated loops carry two independent
26-
`i32` accumulator phis, combine them after the loop, or take a guarded early
27-
exit from the loop body through an exit phi, so corpus entries exercise both
28-
expression simplification and CFG and loop transforms. CFG arms include the same
29-
scalar integer, bit, boolean, narrowing, saturating, funnel-shift, and vector
30-
expression families as the linear mutator.
22+
structured two-way branches, wider multi-way switches, and deeper bounded CFG
23+
subgraphs with `i32` phi joins. Those subgraphs can nest more diamonds,
24+
switches, and small counted loops with optional guarded early exits. The mutator
25+
also generates top-level counted loops with small bounded constant or
26+
dynamically masked trip counts whose bodies can contain nested diamonds,
27+
switches, and inner loops. Some generated loops carry two independent `i32`
28+
accumulator phis, combine them after the loop, or take a guarded early exit from
29+
the loop body through an exit phi, so corpus entries exercise both expression
30+
simplification and CFG and loop transforms. CFG arms include the same scalar
31+
integer, bit, boolean, narrowing, saturating, funnel-shift, and vector expression
32+
families as the linear mutator.
3133
Corpus files can be inspected directly with `opt -S corpus-entry -o -`.
3234

3335
## Requirements

‎amdgpu/fuzzer/llvm_amdgpu_diff_fuzzer.cpp‎

Lines changed: 100 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ namespace {
6060

6161
constexpr unsigned ThreadsPerBlock = 256;
6262
constexpr unsigned InputCount = 256;
63-
constexpr unsigned MaxIRCFGBlocks = 512;
63+
constexpr unsigned MaxIRCFGBlocks = 768;
6464

6565
constexpr StringRef DataLayout =
6666
"e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-"
@@ -1960,13 +1960,13 @@ struct CFGFragment {
19601960
unsigned chooseCFGDepth(const Function &F, unsigned HardMax,
19611961
std::minstd_rand &Gen) {
19621962
unsigned MaxDepth = HardMax;
1963-
if (F.size() >= MaxIRCFGBlocks - 64)
1963+
if (F.size() >= MaxIRCFGBlocks - 96)
19641964
MaxDepth = 1;
1965-
else if (F.size() >= 384)
1965+
else if (F.size() >= 576)
19661966
MaxDepth = std::min(MaxDepth, 2u);
1967-
else if (F.size() >= 192)
1967+
else if (F.size() >= 384)
19681968
MaxDepth = std::min(MaxDepth, 3u);
1969-
else if (F.size() >= 96)
1969+
else if (F.size() >= 192)
19701970
MaxDepth = std::min(MaxDepth, 4u);
19711971

19721972
unsigned Depth = 1;
@@ -2068,6 +2068,80 @@ CFGFragment emitRandomNestedSwitch(IRBuilder<NoFolder> &B, Module &M,
20682068
return {Phi, Join};
20692069
}
20702070

2071+
CFGFragment emitRandomNestedCountedLoop(IRBuilder<NoFolder> &B, Module &M,
2072+
BasicBlock *InsertBefore,
2073+
Value *Current, Value *Other,
2074+
unsigned Depth,
2075+
std::minstd_rand &Gen) {
2076+
LLVMContext &Ctx = M.getContext();
2077+
Type *I32 = Type::getInt32Ty(Ctx);
2078+
Function *F = B.GetInsertBlock()->getParent();
2079+
BasicBlock *Preheader = B.GetInsertBlock();
2080+
BasicBlock *Header =
2081+
BasicBlock::Create(Ctx, "fuzz.nested.loop.header", F, InsertBefore);
2082+
BasicBlock *Body =
2083+
BasicBlock::Create(Ctx, "fuzz.nested.loop.body", F, InsertBefore);
2084+
BasicBlock *Exit =
2085+
BasicBlock::Create(Ctx, "fuzz.nested.loop.exit", F, InsertBefore);
2086+
bool UseEarlyExit = (Gen() % 5) == 0;
2087+
2088+
Value *TripCount = nullptr;
2089+
if ((Gen() % 2) == 0) {
2090+
TripCount = ci32(Ctx, 1 + (Gen() % 4));
2091+
} else {
2092+
Value *TripSeed =
2093+
(Gen() % 2) == 0 ? Current : chooseCFGValue(Ctx, Other, Gen);
2094+
Value *Masked =
2095+
B.CreateAnd(TripSeed, ci32(Ctx, 3), "fuzz.loop.trip.inner.mask");
2096+
TripCount = B.CreateAdd(Masked, ci32(Ctx, 1), "fuzz.loop.trip.inner");
2097+
}
2098+
B.CreateBr(Header);
2099+
2100+
IRBuilder<NoFolder> HeaderB(Header);
2101+
PHINode *Index = HeaderB.CreatePHI(I32, 2, "fuzz.loop.iv.inner");
2102+
PHINode *Acc = HeaderB.CreatePHI(I32, 2, "fuzz.loop.acc.inner");
2103+
Index->addIncoming(ci32(Ctx, 0), Preheader);
2104+
Acc->addIncoming(Current, Preheader);
2105+
Value *Cond = HeaderB.CreateICmpULT(Index, TripCount,
2106+
"fuzz.loop.cond.inner");
2107+
HeaderB.CreateCondBr(Cond, Body, Exit);
2108+
2109+
IRBuilder<NoFolder> BodyB(Body);
2110+
CFGFragment BodyFrag =
2111+
emitRandomCFGSubgraph(BodyB, M, Exit, Acc, Other, Depth - 1, Gen);
2112+
2113+
Value *NextAcc = BodyFrag.Result;
2114+
Value *ExitResult = Acc;
2115+
BasicBlock *Backedge = BodyFrag.Tail;
2116+
if (UseEarlyExit) {
2117+
IRBuilder<NoFolder> BreakB(BodyFrag.Tail);
2118+
Value *BreakCond =
2119+
BreakB.CreateICmp(randomICmpPredicate(Gen), NextAcc,
2120+
chooseCFGValue(Ctx, Other, Gen),
2121+
"fuzz.loop.break.inner");
2122+
Backedge =
2123+
BasicBlock::Create(Ctx, "fuzz.nested.loop.continue", F, Exit);
2124+
BreakB.CreateCondBr(BreakCond, Exit, Backedge);
2125+
2126+
PHINode *ExitPhi =
2127+
PHINode::Create(I32, 2, "fuzz.loop.exit.value.inner", Exit->begin());
2128+
ExitPhi->addIncoming(Acc, Header);
2129+
ExitPhi->addIncoming(NextAcc, BodyFrag.Tail);
2130+
ExitResult = ExitPhi;
2131+
}
2132+
2133+
IRBuilder<NoFolder> BackedgeB(Backedge);
2134+
if ((Gen() % 2) == 0)
2135+
NextAcc = BackedgeB.CreateXor(NextAcc, Index, "fuzz.loop.acc.inner.mix");
2136+
Value *NextIndex =
2137+
BackedgeB.CreateAdd(Index, ci32(Ctx, 1), "fuzz.loop.next.inner");
2138+
BackedgeB.CreateBr(Header);
2139+
Index->addIncoming(NextIndex, Backedge);
2140+
Acc->addIncoming(NextAcc, Backedge);
2141+
2142+
return {ExitResult, Exit};
2143+
}
2144+
20712145
CFGFragment emitRandomCFGSubgraph(IRBuilder<NoFolder> &B, Module &M,
20722146
BasicBlock *InsertBefore, Value *Current,
20732147
Value *Other, unsigned Depth,
@@ -2078,11 +2152,17 @@ CFGFragment emitRandomCFGSubgraph(IRBuilder<NoFolder> &B, Module &M,
20782152
return {Linear, B.GetInsertBlock()};
20792153

20802154
Value *NestedOther = chooseCFGValue(M.getContext(), Other, Gen);
2081-
if ((Gen() % 4) == 0)
2155+
switch (Gen() % 5) {
2156+
case 0:
20822157
return emitRandomNestedSwitch(B, M, InsertBefore, Linear, NestedOther,
20832158
Depth, Gen);
2084-
return emitRandomNestedDiamond(B, M, InsertBefore, Linear, NestedOther,
2085-
Depth, Gen);
2159+
case 1:
2160+
return emitRandomNestedCountedLoop(B, M, InsertBefore, Linear, NestedOther,
2161+
Depth, Gen);
2162+
default:
2163+
return emitRandomNestedDiamond(B, M, InsertBefore, Linear, NestedOther,
2164+
Depth, Gen);
2165+
}
20862166
}
20872167

20882168
void mutateIRAddDiamond(Module &M, std::minstd_rand &Gen) {
@@ -2109,13 +2189,13 @@ void mutateIRAddDiamond(Module &M, std::minstd_rand &Gen) {
21092189

21102190
IRBuilder<NoFolder> ThenB(Then);
21112191
CFGFragment ThenFrag = emitRandomCFGSubgraph(
2112-
ThenB, M, Join, Current, Other, chooseCFGDepth(*F, 6, Gen), Gen);
2192+
ThenB, M, Join, Current, Other, chooseCFGDepth(*F, 8, Gen), Gen);
21132193
IRBuilder<NoFolder> ThenTailB(ThenFrag.Tail);
21142194
ThenTailB.CreateBr(Join);
21152195

21162196
IRBuilder<NoFolder> ElseB(Else);
21172197
CFGFragment ElseFrag = emitRandomCFGSubgraph(
2118-
ElseB, M, Join, Current, Other, chooseCFGDepth(*F, 6, Gen), Gen);
2198+
ElseB, M, Join, Current, Other, chooseCFGDepth(*F, 8, Gen), Gen);
21192199
IRBuilder<NoFolder> ElseTailB(ElseFrag.Tail);
21202200
ElseTailB.CreateBr(Join);
21212201

@@ -2164,15 +2244,15 @@ void mutateIRAddSwitch(Module &M, std::minstd_rand &Gen) {
21642244
Value *CaseOther =
21652245
(I % 2) == 0 ? Other : ci32(Ctx, randomInteresting64(Gen));
21662246
CFGFragment CaseFrag = emitRandomCFGSubgraph(
2167-
CaseB, M, Join, Current, CaseOther, chooseCFGDepth(*F, 5, Gen), Gen);
2247+
CaseB, M, Join, Current, CaseOther, chooseCFGDepth(*F, 7, Gen), Gen);
21682248
IRBuilder<NoFolder> CaseTailB(CaseFrag.Tail);
21692249
CaseTailB.CreateBr(Join);
21702250
Phi->addIncoming(CaseFrag.Result, CaseFrag.Tail);
21712251
}
21722252

21732253
IRBuilder<NoFolder> DefaultB(Default);
21742254
CFGFragment DefaultFrag = emitRandomCFGSubgraph(
2175-
DefaultB, M, Join, Current, Other, chooseCFGDepth(*F, 5, Gen), Gen);
2255+
DefaultB, M, Join, Current, Other, chooseCFGDepth(*F, 7, Gen), Gen);
21762256
IRBuilder<NoFolder> DefaultTailB(DefaultFrag.Tail);
21772257
DefaultTailB.CreateBr(Join);
21782258
Phi->addIncoming(DefaultFrag.Result, DefaultFrag.Tail);
@@ -2353,7 +2433,7 @@ void mutateIRModule(Module &M, std::minstd_rand &Gen) {
23532433
while (NumMutations < 24 && (Gen() % 4) == 0)
23542434
++NumMutations;
23552435
for (unsigned I = 0; I < NumMutations; ++I) {
2356-
switch (Gen() % 15) {
2436+
switch (Gen() % 17) {
23572437
case 0:
23582438
case 1:
23592439
case 2:
@@ -2362,19 +2442,22 @@ void mutateIRModule(Module &M, std::minstd_rand &Gen) {
23622442
case 3:
23632443
case 4:
23642444
case 5:
2445+
case 6:
23652446
mutateIRAddDiamond(M, Gen);
23662447
break;
2367-
case 6:
23682448
case 7:
23692449
case 8:
2370-
mutateIRAddSwitch(M, Gen);
2371-
break;
23722450
case 9:
23732451
case 10:
2374-
mutateIRAddCountedLoop(M, Gen);
2452+
mutateIRAddSwitch(M, Gen);
23752453
break;
23762454
case 11:
23772455
case 12:
2456+
case 13:
2457+
mutateIRAddCountedLoop(M, Gen);
2458+
break;
2459+
case 14:
2460+
case 15:
23782461
mutateIRModifyConstant(M, Gen);
23792462
break;
23802463
default:

0 commit comments

Comments
 (0)