Skip to content

Commit 5224a17

Browse files
authored
[FuzzMutate] Prevent the mutator from generating illegal memory operations (#144885)
This PR prevents the mutator from generating illegal memory operations for AMDGCN. In particular, direct store and load instructions on addrspace(8) are not permitted. This PR fixes that by properly introducing casts to addrspace(7) when required.
1 parent 32946eb commit 5224a17

File tree

2 files changed

+118
-20
lines changed

2 files changed

+118
-20
lines changed

llvm/lib/FuzzMutate/RandomIRBuilder.cpp

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,55 @@ Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB,
108108
return findOrCreateSource(BB, Insts, {}, anyType());
109109
}
110110

111+
// Adapts the current pointer for a legal mem operation on the target arch.
112+
static Value *buildTargetLegalPtr(Module *M, Value *Ptr, InsertPosition IP,
113+
const Twine &Name,
114+
SmallVector<Instruction *> *NewInsts) {
115+
if (M && M->getTargetTriple().isAMDGCN()) {
116+
// Check if we should perform an address space cast
117+
PointerType *pointerType = dyn_cast<PointerType>(Ptr->getType());
118+
if (pointerType && pointerType->getAddressSpace() == 8) {
119+
// Perform address space cast from address space 8 to address space 7
120+
auto NewPtr = new AddrSpaceCastInst(
121+
Ptr, PointerType::get(M->getContext(), 7), Name + ".ASC", IP);
122+
if (NewInsts)
123+
NewInsts->push_back(NewPtr);
124+
return NewPtr;
125+
}
126+
}
127+
128+
return Ptr;
129+
}
130+
131+
// Stores a value to memory, considering the target triple's restrictions.
132+
static Instruction *buildTargetLegalStore(Value *Val, Value *Ptr,
133+
InsertPosition IP, Module *M) {
134+
Value *StorePtr = buildTargetLegalPtr(M, Ptr, IP, "", nullptr);
135+
Instruction *Store = new StoreInst(Val, StorePtr, IP);
136+
return Store;
137+
}
138+
139+
// Loads a value from memory, considering the target triple's restrictions.
140+
static std::pair<Instruction *, SmallVector<Instruction *>>
141+
buildTargetLegalLoad(Type *AccessTy, Value *Ptr, InsertPosition IP, Module *M,
142+
const Twine &LoadName) {
143+
SmallVector<Instruction *> NewInsts;
144+
145+
Value *LoadPtr = buildTargetLegalPtr(M, Ptr, IP, LoadName, &NewInsts);
146+
147+
Instruction *Load = new LoadInst(AccessTy, LoadPtr, LoadName, IP);
148+
NewInsts.push_back(Load);
149+
150+
return std::make_pair(Load, NewInsts);
151+
}
152+
153+
static void eraseNewInstructions(SmallVector<Instruction *> &NewInsts) {
154+
// Remove in reverse order (uses before defs)
155+
for (auto it = NewInsts.rbegin(); it != NewInsts.rend(); ++it) {
156+
(*it)->eraseFromParent();
157+
}
158+
}
159+
111160
Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB,
112161
ArrayRef<Instruction *> Insts,
113162
ArrayRef<Value *> Srcs,
@@ -158,19 +207,20 @@ Value *RandomIRBuilder::findOrCreateSource(BasicBlock &BB,
158207
Module *M = BB.getParent()->getParent();
159208
auto [GV, DidCreate] = findOrCreateGlobalVariable(M, Srcs, Pred);
160209
Type *Ty = GV->getValueType();
161-
LoadInst *LoadGV = nullptr;
162-
if (BB.getTerminator()) {
163-
LoadGV = new LoadInst(Ty, GV, "LGV", BB.getFirstInsertionPt());
164-
} else {
165-
LoadGV = new LoadInst(Ty, GV, "LGV", &BB);
166-
}
210+
InsertPosition IP = BB.getTerminator()
211+
? InsertPosition(BB.getFirstInsertionPt())
212+
: InsertPosition(&BB);
213+
// Build a legal load and track new instructions in case a rollback is
214+
// needed.
215+
auto [LoadGV, NewInsts] = buildTargetLegalLoad(Ty, GV, IP, M, "LGV");
167216
// Because we might be generating new values, we have to check if it
168217
// matches again.
169218
if (DidCreate) {
170219
if (Pred.matches(Srcs, LoadGV)) {
171220
return LoadGV;
172221
}
173-
LoadGV->eraseFromParent();
222+
// Remove newly inserted instructions
223+
eraseNewInstructions(NewInsts);
174224
// If no one is using this GlobalVariable, delete it too.
175225
if (GV->use_empty()) {
176226
GV->eraseFromParent();
@@ -208,13 +258,18 @@ Value *RandomIRBuilder::newSource(BasicBlock &BB, ArrayRef<Instruction *> Insts,
208258
}
209259
// Pick the type independently.
210260
Type *AccessTy = RS.getSelection()->getType();
211-
auto *NewLoad = new LoadInst(AccessTy, Ptr, "L", IP);
261+
// Build a legal load and track new instructions in case a rollback is
262+
// needed.
263+
auto [NewLoad, NewInsts] =
264+
buildTargetLegalLoad(AccessTy, Ptr, IP, BB.getModule(), "L");
212265

213266
// Only sample this load if it really matches the descriptor
214267
if (Pred.matches(Srcs, NewLoad))
215268
RS.sample(NewLoad, RS.totalWeight());
216-
else
217-
NewLoad->eraseFromParent();
269+
else {
270+
// Remove newly inserted instructions
271+
eraseNewInstructions(NewInsts);
272+
}
218273
}
219274

220275
Value *newSrc = RS.getSelection();
@@ -320,8 +375,10 @@ Instruction *RandomIRBuilder::connectToSink(BasicBlock &BB,
320375
std::shuffle(Dominators.begin(), Dominators.end(), Rand);
321376
for (BasicBlock *Dom : Dominators) {
322377
for (Instruction &I : *Dom) {
323-
if (isa<PointerType>(I.getType()))
324-
return new StoreInst(V, &I, Insts.back()->getIterator());
378+
if (isa<PointerType>(I.getType())) {
379+
return buildTargetLegalStore(V, &I, Insts.back()->getIterator(),
380+
I.getModule());
381+
}
325382
}
326383
}
327384
break;
@@ -344,10 +401,10 @@ Instruction *RandomIRBuilder::connectToSink(BasicBlock &BB,
344401
/// TODO: allocate a new stack memory.
345402
return newSink(BB, Insts, V);
346403
case SinkToGlobalVariable: {
347-
Module *M = BB.getParent()->getParent();
404+
Module *M = BB.getModule();
348405
auto [GV, DidCreate] =
349406
findOrCreateGlobalVariable(M, {}, fuzzerop::onlyType(V->getType()));
350-
return new StoreInst(V, GV, Insts.back()->getIterator());
407+
return buildTargetLegalStore(V, GV, Insts.back()->getIterator(), M);
351408
}
352409
case EndOfValueSink:
353410
default:
@@ -369,7 +426,8 @@ Instruction *RandomIRBuilder::newSink(BasicBlock &BB,
369426
}
370427
}
371428

372-
return new StoreInst(V, Ptr, Insts.back()->getIterator());
429+
return buildTargetLegalStore(V, Ptr, Insts.back()->getIterator(),
430+
BB.getModule());
373431
}
374432

375433
Value *RandomIRBuilder::findPointer(BasicBlock &BB,

llvm/unittests/FuzzMutate/StrategiesTest.cpp

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,32 @@ void IterateOnSource(StringRef Source, IRMutator &Mutator) {
8989
}
9090
}
9191

92-
static void mutateAndVerifyModule(StringRef Source,
93-
std::unique_ptr<IRMutator> &Mutator,
94-
int repeat = 100) {
92+
using ModuleVerifier = std::function<void(Module &)>;
93+
94+
static void
95+
mutateAndVerifyModule(StringRef Source, std::unique_ptr<IRMutator> &Mutator,
96+
int repeat = 100,
97+
ArrayRef<ModuleVerifier> ExtraModuleVerifiers = {}) {
9598
LLVMContext Ctx;
9699
auto M = parseAssembly(Source.data(), Ctx);
97100
std::mt19937 mt(Seed);
98101
std::uniform_int_distribution<int> RandInt(INT_MIN, INT_MAX);
99102
for (int i = 0; i < repeat; i++) {
100103
Mutator->mutateModule(*M, RandInt(mt), IRMutator::getModuleSize(*M) + 1024);
101104
ASSERT_FALSE(verifyModule(*M, &errs()));
105+
for (auto &ModuleVerifier : ExtraModuleVerifiers) {
106+
ModuleVerifier(*M);
107+
}
102108
}
103109
}
110+
104111
template <class Strategy>
105-
static void mutateAndVerifyModule(StringRef Source, int repeat = 100) {
112+
static void
113+
mutateAndVerifyModule(StringRef Source, int repeat = 100,
114+
ArrayRef<ModuleVerifier> ExtraModuleVerifiers = {}) {
106115
auto Mutator = createMutator<Strategy>();
107116
ASSERT_TRUE(Mutator);
108-
mutateAndVerifyModule(Source, Mutator, repeat);
117+
mutateAndVerifyModule(Source, Mutator, repeat, ExtraModuleVerifiers);
109118
}
110119

111120
TEST(InjectorIRStrategyTest, EmptyModule) {
@@ -763,4 +772,35 @@ TEST(AllStrategies, SpecialTerminator) {
763772
mutateAndVerifyModule<SinkInstructionStrategy>(Source);
764773
}
765774

775+
TEST(AllStrategies, AMDGCNLegalAddrspace) {
776+
StringRef Source = "\n\
777+
target triple = \"amdgcn-amd-amdhsa\"\n\
778+
; minimum values required by the fuzzer (e.g., default addrspace for allocas and globals)\n\
779+
target datalayout = \"A5-G1\"\n\
780+
define amdgpu_gfx void @strict_wwm_amdgpu_cs_main(<4 x i32> inreg %desc, i32 %index) {\n\
781+
%desc.int = bitcast <4 x i32> %desc to i128\n\
782+
%desc.ptr = inttoptr i128 %desc.int to ptr addrspace(8)\n\
783+
ret void\n\
784+
}\n\
785+
";
786+
787+
ModuleVerifier AddrSpaceVerifier = [](Module &M) {
788+
Function *F = M.getFunction("strict_wwm_amdgpu_cs_main");
789+
EXPECT_TRUE(F != nullptr);
790+
for (BasicBlock &BB : *F) {
791+
for (Instruction &I : BB) {
792+
if (StoreInst *S = dyn_cast<StoreInst>(&I)) {
793+
EXPECT_TRUE(S->getPointerAddressSpace() != 8);
794+
} else if (LoadInst *L = dyn_cast<LoadInst>(&I)) {
795+
EXPECT_TRUE(L->getPointerAddressSpace() != 8);
796+
}
797+
}
798+
}
799+
};
800+
801+
int Repeat = 100;
802+
mutateAndVerifyModule<SinkInstructionStrategy>(Source, Repeat,
803+
{AddrSpaceVerifier});
804+
}
805+
766806
} // namespace

0 commit comments

Comments
 (0)