[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