Skip to content

Commit bc81ff2

Browse files
committed
opt: 内联与 LoopTiling 重复调用循环折叠协作,保留可折叠调用点
LoopTiling 的 collapseRepeatedInvariantCallLoop 能把"每轮先用不变 store 恢复数组再调用同一函数"的常量重复循环折叠为单次执行,但该 折叠依赖循环体内的调用形态. 新增 shouldPreserveForRepeatedCallLoopFold 协作检查:callee 可能 经指针形参写内存、调用点位于循环内、且循环体存在覆盖指针实参根 对象的 store 时跳过内联。折叠后循环被展平,调用点不再处于循环中, LateInline 仍会正常内联,折叠与内联收益兼得
1 parent a27b837 commit bc81ff2

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

ir/passes/modulePass/SmallFunctionInline.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,106 @@ bool shouldPreserveForPureCallLoopCache(CallInst * call)
283283
return true;
284284
}
285285

286+
/// @brief 剥离 GEP 链获取指针的根对象,仅识别全局变量与 alloca
287+
/// @param value 待剥离的指针值
288+
/// @return 根对象(全局变量或 alloca),无法识别时返回 nullptr
289+
Value * stripWritableRoot(Value * value)
290+
{
291+
std::unordered_set<Value *> visited;
292+
while (value && visited.insert(value).second) {
293+
if (auto * gep = dynamic_cast<GetElementPtrInst *>(value)) {
294+
value = gep->getBasePointer();
295+
continue;
296+
}
297+
break;
298+
}
299+
if (dynamic_cast<GlobalVariable *>(value) != nullptr || dynamic_cast<AllocaInst *>(value) != nullptr) {
300+
return value;
301+
}
302+
return nullptr;
303+
}
304+
305+
/// @brief 判断 callee 是否可能通过指针形参写入调用方内存
306+
///
307+
/// store 地址的根既不是 callee 自身的 alloca 也不是全局变量时,
308+
/// 视为可能经形参指针写入调用方内存(mem2reg 前后均成立的保守判定)
309+
/// @param callee 被调用函数
310+
/// @return true 表示可能写入调用方传入的内存
311+
bool calleeMayWriteParamMemory(Function * callee)
312+
{
313+
for (auto * bb : callee->getBlocks()) {
314+
for (auto * inst : bb->getInstructions()) {
315+
auto * store = dynamic_cast<StoreInst *>(inst);
316+
if (store != nullptr && stripWritableRoot(store->getPointerOperand()) == nullptr) {
317+
return true;
318+
}
319+
}
320+
}
321+
return false;
322+
}
323+
324+
/// @brief 若调用匹配 LoopTiling 可折叠的重复不变调用循环模式,保留给折叠 pass
325+
///
326+
/// LoopTiling 的 collapseRepeatedInvariantCallLoop 依赖循环体内
327+
/// "覆盖性 store + 唯一用户调用"的形态将常量重复循环折叠为单次执行,
328+
/// 提前内联该调用会破坏形态使折叠永久失效。这里用保守的充分信号判定:
329+
/// callee 可能经指针形参写内存,且调用点所在循环体内存在对某个
330+
/// 指针实参根对象的 store(即每轮先恢复数组再调用的模式)
331+
/// @param call 待判断的调用点
332+
/// @return true 表示应跳过内联,把机会留给折叠 pass
333+
bool shouldPreserveForRepeatedCallLoopFold(CallInst * call)
334+
{
335+
Function * caller = call ? call->getFunction() : nullptr;
336+
Function * callee = call ? call->getCallee() : nullptr;
337+
if (!caller || !callee || !call->getParentBlock()) {
338+
return false;
339+
}
340+
341+
if (!calleeMayWriteParamMemory(callee)) {
342+
return false;
343+
}
344+
345+
// 收集调用的指针实参根对象(全局变量或调用方数组 alloca)
346+
std::unordered_set<Value *> argRoots;
347+
for (int32_t arg = 0; arg < call->getArgCount(); ++arg) {
348+
if (Value * root = stripWritableRoot(call->getArg(arg))) {
349+
argRoots.insert(root);
350+
}
351+
}
352+
if (argRoots.empty()) {
353+
return false;
354+
}
355+
356+
DominatorTree domTree(caller);
357+
LoopInfo loopInfo(caller, &domTree);
358+
BasicBlock * callBlock = call->getParentBlock();
359+
360+
// 任一包含调用点的循环体内若存在覆盖实参根对象的 store 则保留
361+
for (auto * header : caller->getBlocks()) {
362+
if (!loopInfo.isLoopHeader(header)) {
363+
continue;
364+
}
365+
const auto * loopBody = loopInfo.getLoopBody(header);
366+
if (!loopBody || loopBody->find(callBlock) == loopBody->end()) {
367+
continue;
368+
}
369+
for (auto * bb : *loopBody) {
370+
for (auto * inst : bb->getInstructions()) {
371+
auto * store = dynamic_cast<StoreInst *>(inst);
372+
if (!store) {
373+
continue;
374+
}
375+
Value * root = stripWritableRoot(store->getPointerOperand());
376+
if (root != nullptr && argRoots.count(root) > 0) {
377+
return true;
378+
}
379+
}
380+
}
381+
}
382+
383+
return false;
384+
}
385+
286386
/// @brief 判断指令是否属于内联支持的指令类型
287387
/// @param inst 待检查的指令
288388
/// @return true 表示该指令可以被安全地克隆和内联
@@ -452,6 +552,11 @@ bool SmallFunctionInline::shouldInlineCallee(Function * caller, CallInst * call)
452552
return false;
453553
}
454554

555+
// 与 LoopTiling 的协作:保留可折叠的重复不变调用循环中的调用
556+
if (shouldPreserveForRepeatedCallLoopFold(call)) {
557+
return false;
558+
}
559+
455560
// 防止极端栈帧膨胀
456561
if (getAllocaBytes(callee) > kMaxAllocaBytes) {
457562
return false;

0 commit comments

Comments
 (0)