[llvm] [llvm][IR] Add per-function code model attribute (PR #222678)
Leonardo Román Carrillo via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 10 07:53:40 PDT 2026
https://github.com/leonardo-roman-carrillo updated https://github.com/llvm/llvm-project/pull/222678
>From 3a73946ec02bc64594c9ae6aeeb74cece79afc7b Mon Sep 17 00:00:00 2001
From: Leonardo Roman <leonardoroman at google.com>
Date: Thu, 10 Sep 2026 14:43:18 +0000
Subject: [PATCH] [llvm][IR] Add per-function code model attribute
---
llvm/docs/LangRef.md | 9 ++++----
llvm/include/llvm/AsmParser/LLParser.h | 2 +-
llvm/include/llvm/IR/Function.h | 21 +++++++++++++++++
llvm/lib/AsmParser/LLParser.cpp | 19 +++++++++++-----
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 7 ++++++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 3 ++-
llvm/lib/IR/AsmWriter.cpp | 11 +++++++++
llvm/lib/IR/Function.cpp | 11 +++++++++
llvm/test/Assembler/function-code-model.ll | 26 ++++++++++++++++++++++
9 files changed, 97 insertions(+), 12 deletions(-)
create mode 100644 llvm/test/Assembler/function-code-model.ll
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 14caff88243c1..2a28076dae610 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -988,6 +988,7 @@ attributes <paramattrs>`), optional {ref}`function attributes <fnattrs>`,
an optional address space, an optional section, an optional partition,
an optional minimum alignment,
an optional preferred alignment,
+an optional code model,
an optional {ref}`comdat <langref_comdats>`,
an optional {ref}`garbage collector name <gc>`, an optional {ref}`prefix <prefixdata>`,
an optional {ref}`prologue <prologuedata>`,
@@ -1002,7 +1003,7 @@ define [linkage] [PreemptionSpecifier] [visibility] [DLLStorageClass]
<ResultType> @<FunctionName> ([argument list])
[(unnamed_addr|local_unnamed_addr)] [AddrSpace] [fn Attrs]
[section "name"] [partition "name"] [comdat [($name)]] [align N]
- [prefalign(N)] [gc] [prefix Constant] [prologue Constant]
+ [prefalign(N)] [code_model "model"] [gc] [prefix Constant] [prologue Constant]
[personality Constant] (!name !N)* { ... }
```
@@ -1021,8 +1022,8 @@ optional {ref}`linkage type <linkage>`, an optional {ref}`visibility style
optional {ref}`calling convention <callingconv>`, an optional `unnamed_addr`
or `local_unnamed_addr` attribute, an optional address space, a return type,
an optional {ref}`parameter attribute <paramattrs>` for the return type, a function name, a possibly
-empty list of arguments, an optional alignment, an optional {ref}`garbage
-collector name <gc>`, an optional {ref}`prefix <prefixdata>`, and an optional
+empty list of arguments, an optional alignment, an optional code model,
+an optional {ref}`garbage collector name <gc>`, an optional {ref}`prefix <prefixdata>`, and an optional
{ref}`prologue <prologuedata>`.
Syntax:
@@ -1031,7 +1032,7 @@ Syntax:
declare [linkage] [visibility] [DLLStorageClass]
[cconv] [ret attrs]
<ResultType> @<FunctionName> ([argument list])
- [(unnamed_addr|local_unnamed_addr)] [align N] [gc]
+ [(unnamed_addr|local_unnamed_addr)] [align N] [code_model "model"] [gc]
[prefix Constant] [prologue Constant]
```
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 7cc8e9877bbd0..80d92e384d961 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -346,7 +346,7 @@ namespace llvm {
bool parseOptionalAlignment(MaybeAlign &Alignment,
bool AllowParens = false);
bool parseOptionalPrefAlignment(MaybeAlign &Alignment);
- bool parseOptionalCodeModel(CodeModel::Model &model);
+ bool parseOptionalCodeModel(std::optional<CodeModel::Model> &model);
bool parseOptionalAttrBytes(lltok::Kind AttrKind,
std::optional<uint64_t> &Bytes,
bool ErrorNoBytes = true);
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index 17fa5efd01a9d..d5b3c40a55a9c 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -1040,7 +1040,28 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
/// unknown.
unsigned getVScaleValue() const;
+ /// Get the custom code model raw value of this function.
+ unsigned getCodeModelRaw() const {
+ unsigned Data = getGlobalValueSubClassData();
+ return (Data >> CodeModelShift) & CodeModelMask;
+ }
+
+ /// Get the custom code model of this function if it has one.
+ std::optional<CodeModel::Model> getCodeModel() const {
+ unsigned CodeModelData = getCodeModelRaw();
+ if (CodeModelData > 0)
+ return static_cast<CodeModel::Model>(CodeModelData - 1);
+ return {};
+ }
+
+ /// Change the code model for this function.
+ void setCodeModel(CodeModel::Model CM);
+
private:
+ static const unsigned CodeModelBits = LastCodeModelBit - LastAlignmentBit;
+ static const unsigned CodeModelMask = (1 << CodeModelBits) - 1;
+ static const unsigned CodeModelShift = LastAlignmentBit + 1;
+
void allocHungoffUselist();
template<int Idx> void setHungoffOperand(Constant *C);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index cc7633894fb74..94aba73972ae8 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1618,10 +1618,11 @@ bool LLParser::parseGlobal(const std::string &Name, unsigned NameID,
if (Alignment)
GV->setAlignment(*Alignment);
} else if (Lex.getKind() == lltok::kw_code_model) {
- CodeModel::Model CodeModel;
+ std::optional<CodeModel::Model> CodeModel;
if (parseOptionalCodeModel(CodeModel))
return true;
- GV->setCodeModel(CodeModel);
+ if (CodeModel)
+ GV->setCodeModel(*CodeModel);
} else if (Lex.getKind() == lltok::MetadataVar) {
if (parseGlobalObjectMetadataAttachment(*GV))
return true;
@@ -2644,10 +2645,12 @@ bool LLParser::parseOptionalPrefAlignment(MaybeAlign &Alignment) {
/// parseOptionalCodeModel
/// ::= /* empty */
/// ::= 'code_model' "large"
-bool LLParser::parseOptionalCodeModel(CodeModel::Model &model) {
- Lex.Lex();
+bool LLParser::parseOptionalCodeModel(std::optional<CodeModel::Model> &model) {
+ model = std::nullopt;
+ if (!EatIfPresent(lltok::kw_code_model))
+ return false;
auto StrVal = Lex.getStrVal();
- auto ErrMsg = "expected global code model string";
+ auto ErrMsg = "expected code model string";
if (StrVal == "tiny")
model = CodeModel::Tiny;
else if (StrVal == "small")
@@ -7241,6 +7244,7 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
std::string Section;
std::string Partition;
MaybeAlign Alignment, PrefAlignment;
+ std::optional<CodeModel::Model> FnCodeModel;
std::string GC;
GlobalValue::UnnamedAddr UnnamedAddr = GlobalValue::UnnamedAddr::None;
unsigned AddrSpace = 0;
@@ -7259,6 +7263,7 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
parseOptionalComdat(FunctionName, C) ||
parseOptionalAlignment(Alignment) ||
parseOptionalPrefAlignment(PrefAlignment) ||
+ parseOptionalCodeModel(FnCodeModel) ||
(EatIfPresent(lltok::kw_gc) && parseStringConstant(GC)) ||
(EatIfPresent(lltok::kw_prefix) && parseGlobalTypeAndValue(Prefix)) ||
(EatIfPresent(lltok::kw_prologue) && parseGlobalTypeAndValue(Prologue)) ||
@@ -7309,7 +7314,7 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
"function '" +
FunctionName +
"' with wrong type: "
- "expected '" +
+ "expected '" +
getTypeString(PFT) + "' but was '" +
getTypeString(FwdFn->getType()) + "'");
ForwardRefVals.erase(FRVI);
@@ -7361,6 +7366,8 @@ bool LLParser::parseFunctionHeader(Function *&Fn, bool IsDefine,
if (Alignment)
Fn->setAlignment(*Alignment);
Fn->setPreferredAlignment(PrefAlignment);
+ if (FnCodeModel)
+ Fn->setCodeModel(*FnCodeModel);
Fn->setSection(Section);
Fn->setPartition(Partition);
Fn->setComdat(C);
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index d75e85b0b0f9e..ef85a402a81b7 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -4512,6 +4512,13 @@ Error BitcodeReader::parseFunctionRecord(ArrayRef<uint64_t> Record) {
Func->setPreferredAlignment(PrefAlignment);
}
+ if (Record.size() > 20 && Record[20]) {
+ if (auto CM = getDecodedCodeModel(Record[20]))
+ Func->setCodeModel(*CM);
+ else
+ return error("Invalid function code model");
+ }
+
ValueList.push_back(Func, getVirtualTypeID(Func->getType(), FTyID));
if (OperandInfo.PersonalityFn || OperandInfo.Prefix || OperandInfo.Prologue)
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index e33b6e0050318..ff37b0c62688a 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1726,7 +1726,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
// linkage, paramattrs, alignment, section, visibility, gc,
// unnamed_addr, prologuedata, dllstorageclass, comdat,
// prefixdata, personalityfn, DSO_Local, addrspace,
- // partition_strtab, partition_size, prefalign]
+ // partition_strtab, partition_size, prefalign, code_model]
Vals.push_back(addToStrtab(F.getName()));
Vals.push_back(F.getName().size());
Vals.push_back(VE.getTypeID(F.getFunctionType()));
@@ -1754,6 +1754,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
Vals.push_back(addToStrtab(F.getPartition()));
Vals.push_back(F.getPartition().size());
Vals.push_back(getEncodedAlign(F.getPreferredAlignment()));
+ Vals.push_back(F.getCodeModelRaw());
unsigned AbbrevToUse = 0;
Stream.EmitRecord(bitc::MODULE_CODE_FUNCTION, Vals, AbbrevToUse);
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 8e57bff1d36c9..fa505ae2eba6b 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -4331,6 +4331,17 @@ void AssemblyWriter::printFunction(const Function *F) {
Out << " align " << A->value();
if (MaybeAlign A = F->getPreferredAlignment())
Out << " prefalign(" << A->value() << ')';
+ if (auto CM = F->getCodeModel()) {
+ Out << " code_model \"";
+ switch (*CM) {
+ case CodeModel::Tiny: Out << "tiny"; break;
+ case CodeModel::Small: Out << "small"; break;
+ case CodeModel::Kernel: Out << "kernel"; break;
+ case CodeModel::Medium: Out << "medium"; break;
+ case CodeModel::Large: Out << "large"; break;
+ }
+ Out << '"';
+ }
if (F->hasGC())
Out << " gc \"" << F->getGC() << '"';
if (F->hasPrefixData()) {
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 678dd93c96fe9..9c929018e29ac 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -856,6 +856,8 @@ void Function::copyAttributesFrom(const Function *Src) {
setPrefixData(Src->getPrefixData());
if (Src->hasPrologueData())
setPrologueData(Src->getPrologueData());
+ if (auto CM = Src->getCodeModel())
+ setCodeModel(*CM);
}
MemoryEffects Function::getMemoryEffects() const {
@@ -1233,3 +1235,12 @@ bool llvm::CallingConv::supportsNonVoidReturnType(CallingConv::ID CC) {
llvm_unreachable("covered callingconv switch");
}
+
+void Function::setCodeModel(CodeModel::Model CM) {
+ unsigned CodeModelData = static_cast<unsigned>(CM) + 1;
+ unsigned OldData = getGlobalValueSubClassData();
+ unsigned NewData = (OldData & ~(CodeModelMask << CodeModelShift)) |
+ (CodeModelData << CodeModelShift);
+ setGlobalValueSubClassData(NewData);
+ assert(getCodeModel() == CM && "Code model representation error!");
+}
diff --git a/llvm/test/Assembler/function-code-model.ll b/llvm/test/Assembler/function-code-model.ll
new file mode 100644
index 0000000000000..ae0bed5770ebb
--- /dev/null
+++ b/llvm/test/Assembler/function-code-model.ll
@@ -0,0 +1,26 @@
+; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
+
+; CHECK: define void @f1() code_model "tiny" {
+define void @f1() code_model "tiny" {
+ ret void
+}
+
+; CHECK: define void @f2() code_model "small" {
+define void @f2() code_model "small" {
+ ret void
+}
+
+; CHECK: define void @f3() code_model "kernel" {
+define void @f3() code_model "kernel" {
+ ret void
+}
+
+; CHECK: define void @f4() code_model "medium" {
+define void @f4() code_model "medium" {
+ ret void
+}
+
+; CHECK: define void @f5() code_model "large" {
+define void @f5() code_model "large" {
+ ret void
+}
More information about the llvm-commits
mailing list