This is an archive of the discontinued LLVM Phabricator instance.

[X86] Introduce a large data threshold for the medium code model
ClosedPublic

Authored by aeubanks on Apr 26 2023, 1:40 PM.

Details

Summary

Currently clang's medium code model treats all data as large, putting them in a large data section and using more expensive instruction sequences to access them.

Following gcc's -mlarge-data-threshold, which allows putting data under a certain size in a normal data section as opposed to a large data section. This allows using cheaper code sequences to access some portion of data in the binary (which will be implemented in LLVM in a future patch).

And under the medium codel mode, only put data above the large data threshold into large data sections, not all data.

Diff Detail

Event Timeline

aeubanks created this revision.Apr 26 2023, 1:40 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 26 2023, 1:40 PM
aeubanks requested review of this revision.Apr 26 2023, 1:40 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 26 2023, 1:40 PM

large-data-threshold is ABI, and needs to have a default value which is the same as GCC, 65535. (And users should be discouraged from changing it.)

In many common cases you can get away with using different values in different object files, because cross-object references are going via PLT/GOT when the compiler doesn't know the definition is in-DSO. But that's definitely not a 100% solution -- e.g. __attribute__((visibility("hidden"))) or ODR-data defined in a -fPIE object is known to be defined in-DSO, so will use a pc-relative reference.

llvm/lib/Target/TargetMachine.cpp
47

W also must handle unknown-sized objects as large here.

E.g. clang -mcmodel=medium -fPIC on

__attribute__((visibility("hidden"))) extern int x[];
int bar(void) { return x[0]; }

must refer to x via GOTOFF not pc-relative, because it _could_ be defined as large.

aeubanks updated this revision to Diff 524471.May 22 2023, 1:50 PM

check size 0

large-data-threshold is ABI, and needs to have a default value which is the same as GCC, 65535. (And users should be discouraged from changing it.)

In many common cases you can get away with using different values in different object files, because cross-object references are going via PLT/GOT when the compiler doesn't know the definition is in-DSO. But that's definitely not a 100% solution -- e.g. __attribute__((visibility("hidden"))) or ODR-data defined in a -fPIE object is known to be defined in-DSO, so will use a pc-relative reference.

Yes, changing the default value and adding a clang flag will come in a future patch; this patch preserves the current behavior by default

llvm/lib/Target/TargetMachine.cpp
47

ah ok, although that's not observable in this patch since we only use this for determining which section to place a symbol in, not how we reference a symbol (that's D150297). I'll make the change here but add tests in the other patch

MaskRay added inline comments.May 25 2023, 8:27 AM
llvm/lib/CodeGen/CommandFlags.cpp
169

To match GCC, we can use 65536

Consider applying this to large code model as well.

https://gcc.gnu.org/pipermail/gcc-patches/2023-May/619695.html

llvm/lib/Target/TargetMachine.cpp
51

I think GCC's condition is > instead of >=.

tkoeppe added inline comments.May 25 2023, 8:40 AM
llvm/tools/llc/llc.cpp
594

Can we avoid the implicit check for 0 here and spell out ; LDT > 0? Does LLVM require C++17?

MaskRay added inline comments.May 25 2023, 1:12 PM
llvm/tools/llc/llc.cpp
594

Building llvm-project requires c++17 (libomptarget may be an exception).

We can use if (auto LDT = ...; LDT && LDT > 0) only if the default is 0.

aeubanks updated this revision to Diff 525770.May 25 2023, 1:23 PM

instead of >=

llvm/lib/CodeGen/CommandFlags.cpp
169

changes to this value will come in a later patch, and same for making the large code model respect this value

llvm/tools/llc/llc.cpp
594

yeah LLVM uses C++17

codegen::getExplicitLargeDataThreshold returns a std::optional, so we're checking if the user explicitly overrode the value, not if the value is 0. we would want to set the value to 0 if the user explicitly requested it

tkoeppe added inline comments.May 25 2023, 1:30 PM
llvm/tools/llc/llc.cpp
594

Ah yes, sorry, I forgot that it's an optional<int>, not just an int. In that case all I meant was for the check to not be implicit, i.e. I'd recommend:

for (auto LDT = ...; LDT.has_vaiue()) {
  // use *LDT
}

Just so there's one less implicit thing one needs to keep in mind when reading this.

MaskRay added inline comments.May 25 2023, 1:49 PM
llvm/lib/CodeGen/CommandFlags.cpp
169

This seems strange. Introducing this variable and setting it to an appropriate should come in the same patch, no ?

I don't want clang -mcmodel=medium emitted data sections for small objects to change from .data to .ldata, then to .data again.

aeubanks updated this revision to Diff 525790.May 25 2023, 2:06 PM

use std::optional<uint64_t> instead of auto

llvm/lib/CodeGen/CommandFlags.cpp
169

this patch doesn't change the current behavior of the medium code model in clang since a large data threshold of zero treats all data as large, which is the current behavior. I'd rather have the implementation in this patch then a separate small patch to actually change the value, especially since not all of the large data threshold functionality is done yet (e.g. D150297)

llvm/tools/llc/llc.cpp
594

I think this is the standard idiom for checking if a std::optional has a value, not calling has_value().
I did change the type to be explicit though, instead of auto

rnk added a comment.Jun 1 2023, 1:58 PM

What about storing some kind of "large data attribute" on the global itself in the IR? That would help make sure the medium code model flags don't have to be passed to ThinLTO backend action compilations, for example, and give intuitive semantics to linking together objects compiled in the large, medium, and small code models.

The other consideration is making this feature easy for other frontends (Rust) to use, and your solution does make that easy. The best I can come up with is an LLVM pass or helper function in LLVM that annotates IR globals appropriately given a size threshold from the frontend.

aeubanks added a comment.EditedSep 12 2023, 10:21 PM

What about storing some kind of "large data attribute" on the global itself in the IR? That would help make sure the medium code model flags don't have to be passed to ThinLTO backend action compilations, for example, and give intuitive semantics to linking together objects compiled in the large, medium, and small code models.

apparently a module metadata for the code model for passing along to LTO backends was already done a while back in https://reviews.llvm.org/D52322

and I verified that you don't need to pass -mcmodel to lld with ThinLTO

Right, the model should be encoded in the IR for LTO. So as long as we consider the threshold an ABI constraint, and we can accurately compute whether a global has indefinite size, we don't need to tag individual globals.

Tagging individual globals could be useful as an extension, I guess, but it doesn't need to be in this patch.

(Note there's a weird interaction between -mcmodel=medium and the gcc extension that allows initializing flexible arrays: it's possible to define a "small" global that's actually larger than the threshold, and gcc will still treat it as small. But the interaction is consistent with the way this patch works.)

rnk added a comment.Sep 13 2023, 12:43 PM

One of our use cases for having a "large" attribute for global data is to have a way to mark instrumentation data as large. We would use this for ASan global metadata and PGO name data, which are not accessed frequently during program execution, but contribute significantly to binary data size.

Regardless, I'm in favor of using a simple module flag for this. It makes it easy for non-Clang frontends. The explicit "large" attribute can come later.

I have a patch that introduces the large data threshold module metadata, but I'd like to separate that patch to keep patches smallish. It has some dependencies so I need to do stacked patches but we're deprecating new patches on phab and GH doesn't support stacked patch reviews yet. Can we move forward with this one first?

rnk added inline comments.Sep 13 2023, 3:56 PM
llvm/tools/llc/llc.cpp
494

Should the threshold be a TargetOption? Would that avoid the need for the new TargetMachine field?

aeubanks added inline comments.Sep 13 2023, 5:20 PM
llvm/tools/llc/llc.cpp
494

it should go along with the code model since they're tied together. I do think putting all these options (code model, relocation model) in TargetOptions makes sense, it fits with lots of existing options there. if we put all these options into TargetOptions it'd simplify the TargetMachine constructors. if people think this makes sense I can do that cleanup first

rnk accepted this revision.Sep 14 2023, 10:01 AM

lgtm

llvm/tools/llc/llc.cpp
494

I think what I'd like to do is to minimize the number of changes to the createTargetMachine prototype, since it's called in many places. It has many optional parameters, and I suspect that most callers accept the defaults:

TargetMachine *createTargetMachine(
    StringRef TT, StringRef CPU, StringRef Features,
    const TargetOptions &Options,
    std::optional<Reloc::Model> RM, // No default, but is std::optional
    std::optional<CodeModel::Model> CM = std::nullopt, // Optional, with a default.
    CodeGenOpt::Level OL = CodeGenOpt::Default, // has a default, this will change to an enum class
    bool JIT = false // Boolean optional parameters are unreadable.
) const {

Maybe the first change is to move the relocation and code model into target options. We probably have to leave the optimization level alone, since there are multiple callers of TargetMachine::setOptLevel, and it's not really an immutable option, it varies from function to function.

That said, if you want to land this now, and defer that work, I'm OK with that, this change doesn't affect the createTargetMachine prototype, it's the CodeGenLevel one that does.

This revision is now accepted and ready to land.Sep 14 2023, 10:01 AM
MaskRay accepted this revision.Sep 14 2023, 11:11 AM
MaskRay added inline comments.
llvm/lib/CodeGen/CommandFlags.cpp
168

Perhaps we should say x86-64 medium/large code model.
My https://gcc.gnu.org/pipermail/gcc-patches/2023-September/630207.html is still pending after many pings, but I think we can go ahead as the obvious choice.

aeubanks added inline comments.Sep 14 2023, 3:05 PM
llvm/lib/CodeGen/CommandFlags.cpp
168

we can change this to also say "large" once LLVM actually respects it in the large code model

This revision was landed with ongoing or failed builds.Sep 14 2023, 3:09 PM
This revision was automatically updated to reflect the committed changes.