[IR] "modular-format" attribute for functions using format strings

A new InstCombine transform uses this attribute to rewrite calls to a
modular version of the implementation along with llvm.reloc.none
relocations against aspects of the implementation needed by the call.

This change only adds support for the 'float' aspect, but it also builds
the structure needed for others.

See issue #146159
This commit is contained in:
Daniel Thornburgh 2025-04-02 16:24:57 -07:00
parent 341dd6a3ca
commit 40bafe2ceb
2 changed files with 79 additions and 0 deletions

View File

@ -2635,6 +2635,23 @@ For example:
This attribute indicates that outlining passes should not modify the
function.
``"modular_format"="<string_idx>,<first_idx_to_check>,<modular_impl_fn>,<impl_name>,<aspects...>"``
This attribute indicates that the implementation is modular on a particular
format string argument . When the argument for a given call is constant, the
compiler may redirect the call to a modular implementation function
instead.
The compiler also emits relocations to report various aspects of the format
string and arguments that were present. The compiler reports an aspect by
issing a relocation for the symbol `<impl_name>_<aspect>``. This arranges
for code and data needed to support the aspect of the implementation to be
brought into the link to satisfy weak references in the modular
implemenation function.
The following aspects are currently supported:
- ``float``: The call has a floating point argument
Call Site Attributes
----------------------

View File

@ -19,6 +19,7 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumeBundleQueries.h"
#include "llvm/Analysis/AssumptionCache.h"
@ -4001,6 +4002,63 @@ Instruction *InstCombinerImpl::visitCallBrInst(CallBrInst &CBI) {
return visitCallBase(CBI);
}
static Value *optimizeModularFormat(CallInst *CI, IRBuilderBase &B) {
if (!CI->hasFnAttr("modular-format"))
return nullptr;
SmallVector<StringRef> Args(
llvm::split(CI->getFnAttr("modular-format").getValueAsString(), ','));
// TODO: Examine the format argument in Args[0].
// TODO: Error handling
unsigned FirstArgIdx;
if (!llvm::to_integer(Args[1], FirstArgIdx))
return nullptr;
if (FirstArgIdx == 0)
return nullptr;
--FirstArgIdx;
StringRef FnName = Args[2];
StringRef ImplName = Args[3];
DenseSet<StringRef> Aspects(llvm::from_range,
ArrayRef<StringRef>(Args).drop_front(4));
Module *M = CI->getModule();
Function *Callee = CI->getCalledFunction();
FunctionCallee ModularFn =
M->getOrInsertFunction(FnName, Callee->getFunctionType(),
Callee->getAttributes().removeFnAttribute(
M->getContext(), "modular-format"));
CallInst *New = cast<CallInst>(CI->clone());
New->setCalledFunction(ModularFn);
New->removeFnAttr("modular-format");
B.Insert(New);
const auto ReferenceAspect = [&](StringRef Aspect) {
SmallString<20> Name = ImplName;
Name += '_';
Name += Aspect;
Constant *Sym =
M->getOrInsertGlobal(Name, Type::getInt8Ty(M->getContext()));
Function *RelocNoneFn =
Intrinsic::getOrInsertDeclaration(M, Intrinsic::reloc_none);
B.CreateCall(RelocNoneFn, {Sym});
};
if (Aspects.contains("float")) {
Aspects.erase("float");
if (llvm::any_of(
llvm::make_range(std::next(CI->arg_begin(), FirstArgIdx),
CI->arg_end()),
[](Value *V) { return V->getType()->isFloatingPointTy(); }))
ReferenceAspect("float");
}
SmallVector<StringRef> UnknownAspects(Aspects.begin(), Aspects.end());
llvm::sort(UnknownAspects);
for (StringRef Request : UnknownAspects)
ReferenceAspect(Request);
return New;
}
Instruction *InstCombinerImpl::tryOptimizeCall(CallInst *CI) {
if (!CI->getCalledFunction()) return nullptr;
@ -4022,6 +4080,10 @@ Instruction *InstCombinerImpl::tryOptimizeCall(CallInst *CI) {
++NumSimplified;
return CI->use_empty() ? CI : replaceInstUsesWith(*CI, With);
}
if (Value *With = optimizeModularFormat(CI, Builder)) {
++NumSimplified;
return CI->use_empty() ? CI : replaceInstUsesWith(*CI, With);
}
return nullptr;
}