Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llvm][IR] Add per-global code model attribute #72077

Merged
merged 4 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,13 @@ information. Attaching section information to an external declaration is an
assertion that its definition is located in the specified section. If the
definition is located in a different section, the behavior is undefined.

LLVM allows an explicit code model to be specified for globals. If the
target supports it, it will emit globals in the code model specified,
overriding the code model used to compile the translation unit.
The allowed values are "tiny", "small", "kernel", "medium", "large".
This may be extended in the future to specify global data layout that
doesn't cleanly fit into a specific code model.

By default, global initializers are optimized by assuming that global
variables defined within the module are not modified from their
initial values before the start of the global initializer. This is
Expand Down Expand Up @@ -757,6 +764,7 @@ Syntax::
<global | constant> <Type> [<InitializerConstant>]
[, section "name"] [, partition "name"]
[, comdat [($name)]] [, align <Alignment>]
[, code_model "model"]
heiher marked this conversation as resolved.
Show resolved Hide resolved
[, no_sanitize_address] [, no_sanitize_hwaddress]
[, sanitize_address_dyninit] [, sanitize_memtag]
(, !name !N)*
Expand All @@ -774,6 +782,13 @@ The following example just declares a global variable

@G = external global i32

The following example defines a global variable with the
``large`` code model:

.. code-block:: llvm

@G = internal global i32 0, code_model "large"

The following example defines a thread-local global with the
``initialexec`` TLS model:

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ namespace llvm {
bool parseOptionalCallingConv(unsigned &CC);
bool parseOptionalAlignment(MaybeAlign &Alignment,
bool AllowParens = false);
bool parseOptionalCodeModel(CodeModel::Model &model);
bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
bool parseOptionalUWTableKind(UWTableKind &Kind);
bool parseAllocKind(AllocFnKind &Kind);
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ enum Kind {
kw_addrspace,
kw_section,
kw_partition,
kw_code_model,
kw_alias,
kw_ifunc,
kw_module,
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/GlobalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class GlobalObject : public GlobalValue {
Comdat *ObjComdat = nullptr;
enum {
LastAlignmentBit = 5,
LastCodeModelBit = 8,
HasSectionHashEntryBit,

GlobalObjectBits,
Expand Down
27 changes: 27 additions & 0 deletions llvm/include/llvm/IR/GlobalVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {
// global initializers are run?
bool isExternallyInitializedConstant : 1;

private:
static const unsigned CodeModelBits = LastCodeModelBit - LastAlignmentBit;
static const unsigned CodeModelMask = (1 << CodeModelBits) - 1;
static const unsigned CodeModelShift = LastAlignmentBit + 1;

public:
/// GlobalVariable ctor - If a parent module is specified, the global is
/// automatically inserted into the end of the specified modules global list.
Expand Down Expand Up @@ -247,6 +252,28 @@ class GlobalVariable : public GlobalObject, public ilist_node<GlobalVariable> {
getAttributes().hasAttribute("rodata-section");
}

/// Get the custom code model raw value of this global.
///
unsigned getCodeModelRaw() const {
unsigned Data = getGlobalValueSubClassData();
return (Data >> CodeModelShift) & CodeModelMask;
}

/// Get the custom code model of this global if it has one.
///
/// If this global does not have a custom code model, the empty instance
/// will be returned.
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 global.
///
void setCodeModel(CodeModel::Model CM);

// Methods for support type inquiry through isa, cast, and dyn_cast:
static bool classof(const Value *V) {
return V->getValueID() == Value::GlobalVariableVal;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(addrspace);
KEYWORD(section);
KEYWORD(partition);
KEYWORD(code_model);
KEYWORD(alias);
KEYWORD(ifunc);
KEYWORD(module);
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,11 @@ bool LLParser::parseGlobal(const std::string &Name, LocTy NameLoc,
return true;
if (Alignment)
GV->setAlignment(*Alignment);
} else if (Lex.getKind() == lltok::kw_code_model) {
CodeModel::Model CodeModel;
if (parseOptionalCodeModel(CodeModel))
return true;
GV->setCodeModel(CodeModel);
} else if (Lex.getKind() == lltok::MetadataVar) {
if (parseGlobalObjectMetadataAttachment(*GV))
return true;
Expand Down Expand Up @@ -2166,6 +2171,30 @@ bool LLParser::parseOptionalAlignment(MaybeAlign &Alignment, bool AllowParens) {
return false;
}

/// parseOptionalCodeModel
/// ::= /* empty */
/// ::= 'code_model' "large"
bool LLParser::parseOptionalCodeModel(CodeModel::Model &model) {
Lex.Lex();
auto StrVal = Lex.getStrVal();
auto ErrMsg = "expected global code model string";
if (StrVal == "tiny")
model = CodeModel::Tiny;
else if (StrVal == "small")
model = CodeModel::Small;
else if (StrVal == "kernel")
model = CodeModel::Kernel;
else if (StrVal == "medium")
model = CodeModel::Medium;
else if (StrVal == "large")
model = CodeModel::Large;
else
return tokError(ErrMsg);
if (parseToken(lltok::StringConstant, ErrMsg))
return true;
return false;
}

/// parseOptionalDerefAttrBytes
/// ::= /* empty */
/// ::= AttrKind '(' 4 ')'
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,23 @@ static bool getDecodedDSOLocal(unsigned Val) {
}
}

static std::optional<CodeModel::Model> getDecodedCodeModel(unsigned Val) {
switch (Val) {
case 1:
return CodeModel::Tiny;
case 2:
return CodeModel::Small;
case 3:
return CodeModel::Kernel;
case 4:
return CodeModel::Medium;
case 5:
return CodeModel::Large;
}

return {};
}

static GlobalVariable::ThreadLocalMode getDecodedThreadLocalMode(unsigned Val) {
switch (Val) {
case 0: return GlobalVariable::NotThreadLocal;
Expand Down Expand Up @@ -3809,6 +3826,7 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> Record) {
// dllstorageclass, comdat, attributes, preemption specifier,
// partition strtab offset, partition strtab size] (name in VST)
// v2: [strtab_offset, strtab_size, v1]
// v3: [v2, code_model]
StringRef Name;
std::tie(Name, Record) = readNameFromStrtab(Record);

Expand Down Expand Up @@ -3917,6 +3935,13 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> Record) {
NewGV->setSanitizerMetadata(Meta);
}

if (Record.size() > 17 && Record[17]) {
aeubanks marked this conversation as resolved.
Show resolved Hide resolved
if (auto CM = getDecodedCodeModel(Record[17]))
NewGV->setCodeModel(*CM);
else
return error("Invalid global variable code model");
}

return Error::success();
}

Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
// GLOBALVAR: [strtab offset, strtab size, type, isconst, initid,
// linkage, alignment, section, visibility, threadlocal,
// unnamed_addr, externally_initialized, dllstorageclass,
// comdat, attributes, DSO_Local, GlobalSanitizer]
// comdat, attributes, DSO_Local, GlobalSanitizer, code_model]
Vals.push_back(addToStrtab(GV.getName()));
Vals.push_back(GV.getName().size());
Vals.push_back(VE.getTypeID(GV.getValueType()));
Expand All @@ -1421,7 +1421,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
GV.isExternallyInitialized() ||
GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass ||
GV.hasComdat() || GV.hasAttributes() || GV.isDSOLocal() ||
GV.hasPartition() || GV.hasSanitizerMetadata()) {
GV.hasPartition() || GV.hasSanitizerMetadata() || GV.getCodeModel()) {
Vals.push_back(getEncodedVisibility(GV));
Vals.push_back(getEncodedThreadLocalMode(GV));
Vals.push_back(getEncodedUnnamedAddr(GV));
Expand All @@ -1439,6 +1439,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
Vals.push_back((GV.hasSanitizerMetadata() ? serializeSanitizerMetadata(
GV.getSanitizerMetadata())
: 0));
Vals.push_back(GV.getCodeModelRaw());
} else {
AbbrevToUse = SimpleGVarAbbrev;
}
Expand Down
21 changes: 21 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3647,6 +3647,27 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
printEscapedString(GV->getPartition(), Out);
Out << '"';
}
if (auto CM = GV->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 << '"';
}

using SanitizerMetadata = llvm::GlobalValue::SanitizerMetadata;
if (GV->hasSanitizerMetadata()) {
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/IR/Globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,24 @@ void GlobalVariable::copyAttributesFrom(const GlobalVariable *Src) {
GlobalObject::copyAttributesFrom(Src);
setExternallyInitialized(Src->isExternallyInitialized());
setAttributes(Src->getAttributes());
if (auto CM = Src->getCodeModel())
setCodeModel(*CM);
}

void GlobalVariable::dropAllReferences() {
User::dropAllReferences();
clearMetadata();
}

void GlobalVariable::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!");
}

//===----------------------------------------------------------------------===//
// GlobalAlias Implementation
//===----------------------------------------------------------------------===//
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/Assembler/globalvariable-attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
@g7 = global i32 2, sanitize_address_dyninit, align 4
@g8 = global i32 2, sanitize_memtag, align 4
@g9 = global i32 2, no_sanitize_address, no_sanitize_hwaddress, sanitize_memtag, align 4
@g10 = global i32 2, code_model "tiny"
@g11 = global i32 2, code_model "small"
@g12 = global i32 2, code_model "kernel"
@g13 = global i32 2, code_model "medium"
@g14 = global i32 2, code_model "large"

attributes #0 = { "string" = "value" nobuiltin norecurse }

Expand All @@ -21,6 +26,11 @@ attributes #0 = { "string" = "value" nobuiltin norecurse }
; CHECK: @g7 = global i32 2, sanitize_address_dyninit, align 4
; CHECK: @g8 = global i32 2, sanitize_memtag, align 4
; CHECK: @g9 = global i32 2, no_sanitize_address, no_sanitize_hwaddress, sanitize_memtag, align 4
; CHECK: @g10 = global i32 2, code_model "tiny"
; CHECK: @g11 = global i32 2, code_model "small"
; CHECK: @g12 = global i32 2, code_model "kernel"
; CHECK: @g13 = global i32 2, code_model "medium"
; CHECK: @g14 = global i32 2, code_model "large"

; CHECK: attributes #0 = { "key"="value" "key2"="value2" }
; CHECK: attributes #1 = { "key3"="value3" }
Expand Down
11 changes: 11 additions & 0 deletions llvm/test/Transforms/GlobalOpt/globalvar-code-model.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
; RUN: opt -passes=globalopt -S < %s | FileCheck %s

@G = internal global i32 5, code_model "large"

define i32 @test() norecurse {
%a = load i32, ptr @G
store i32 4, ptr @G
ret i32 %a
}

; CHECK: @G = internal unnamed_addr global i1 false, code_model "large"
Loading