This is an archive of the discontinued LLVM Phabricator instance.

CodeGen: Emit globals with a constant initializer but no definition as available_externally.
Needs ReviewPublic

Authored by bkramer on Oct 19 2013, 8:49 AM.

Details

Reviewers
rjmccall
rsmith
Summary

This can happen with static const members (PR17612). It has the advantage
over just making constant folding smarter that it also enables the optimizer
to fold more complicated patterns, such as passing the static const member
by reference.

We only do this at -O1 or higher.

Diff Detail

Event Timeline

bkramer updated this revision to Unknown Object (????).Oct 20 2013, 4:31 AM

Fixed a crasher during selfhost and tightened up asserts.

rnk added inline comments.Oct 23 2013, 2:35 PM
lib/CodeGen/CodeGenModule.cpp
1258–1261

This seems more intuitive as:

if (isAvailableExternally)
  return CGO.OptLevel != 0;
return true;

Also, how do we know isConstant is true? Can you assert that it is like you do below?

1909

What if the variable is thread local, or fits any of the below special cases involving templates? This probably goes towards the end of the if/else chain, not first, considering that it's for an optimization.

bkramer added inline comments.Oct 24 2013, 12:16 AM
lib/CodeGen/CodeGenModule.cpp
1258–1261

Will fix the order. The isConstant flag has no effect for this path currently, but I can add the assert here too.

1909

The problem is that we now allow certain types of globals to hit the method that didn't get here before, such as explicit template instantiations. For example std::string is explicitly instantiated in libc++, but std::string::npos is still a good fit for this optimization. GetGVALinkageForVariable asserts in that case, so the options are

  • replicating the checks from GetGVALinkageForVariable here.
  • leaving the check where it is. I'm not aware of any valid C++11 fragment that this code would fail for, but that's of course a weak argument when C++ is moving on :/

Any preferences?

bkramer updated this revision to Unknown Object (????).Oct 24 2013, 12:19 AM

Add some clarification and address review comments.

rnk added inline comments.Oct 24 2013, 10:37 AM
lib/CodeGen/CodeGenModule.cpp
1909

I guess there's no GVALinkage value for available_externally and we probably don't want to add one, so it might make sense to me to keep this here.

Can you add a test case for something like std::string::npos where it should be considered available_externally?

I can also come up with weird test cases along the lines of:

constexpr int foo() { return 1; }
struct Holder {
  static const int thread_local f = foo();
};
const int *bar() {
  return &Holder::f;
}

I'm not sure what the linkage of 'f' should actually be, but having this as a test case would be good. :)

bkramer updated this revision to Unknown Object (????).Oct 26 2013, 3:24 AM

Extended test case for explicit instantiations and thread_locals.

rsmith added a comment.Nov 3 2013, 8:51 PM

Seems like a great idea to me.

lib/CodeGen/CodeGenModule.cpp
1591

This is incorrect. The following is valid:

struct S {
  static constexpr volatile int n = 0;
};
1909–1910

This is incorrect. The following is valid:

struct A {
  mutable int n;
};
struct B {
  static constexpr A a = { 0 };
};
bkramer updated this revision to Unknown Object (????).Nov 16 2013, 12:29 PM

Turned assertions into checks.

Ping. The patch is old but still useful :)

The final LGTM should probably wait for a C++ expert, but I agree this would be really nice to have :-)

test/CodeGenCXX/constexpr-available-externally.cpp
6

Can you move this closer to the code that causes it to be output? Maybe with CHECK-DAG?

37

Could you also add a testcase with a explicit template instantiation declaration?

Please also add a test showing we don't emit a available_externally for a member if just the class is used:

class foo {

static const int X = 1;
void bar();

}
void f(foo *x) {x->bar(); }

LGTM with that.

bkramer updated this revision to Unknown Object (????).Feb 14 2014, 4:04 AM

Update patch. Add a test that we don't emit unused variables.

I don't like that parts of the test here are duplicated in three different places; can you refactor to improve that? Maybe factor out a CanEmitAsAvailableExternally from GetOrCreateLLVMGlobal and GetLLVMLinkageVarDefinition, call isTypeConstant in there rather than asserting it, and remove the assert from shouldEmitGlobalVariable?

It also seems weird to me that shouldEmitGlobalVariable would return true when GetLLVMLinkageVarDefinition returns llvm::GlobalVariable::ExternalLinkage (even though in practice this never actually happens).

lib/CodeGen/CodeGenModule.cpp
1909–1910

Why is this an assert rather than a test?

bkramer updated this revision to Unknown Object (????).Feb 20 2014, 4:26 AM

Factored duplicated checks into a helper method.

bkramer added inline comments.Feb 20 2014, 4:39 AM
lib/CodeGen/CodeGenModule.cpp
1909–1910

The idea was that this code path could never be triggered if the check failed earlier. But that's obsolete with the latest update.

Patch looks good to me. Just one more thing (hopefully...): does this behave correctly if the global variable lifetime-extends a temporary? Given:

struct Q { int &&r; };
struct S {
  static constexpr Q q = { 0 };
};
int k = S::q.r;

... how do we emit @_ZN1S1qE and @_ZGRN1S1qE?

lib/CodeGen/CodeGenModule.cpp
1263–1268

Can you find a better name for this function? It returns true for variables that we shouldn't emit at all, which makes me uneasy. Maybe just inline it into its only call site (which already has a comment describing exactly what this code does)?

@_ZN1S1qE = available_externally constant %struct.Q { i32* @_ZGRN1S1qE }, align 8
@_ZGRN1S1qE = private global i32 0, align 4

That doesn't look good :|

This seems to be a pre-existing and essentially unrelated bug. The same problem occurs for:

constexpr int &p = S::q.r;

I think we should be looking at the linkage of the extending declaration when picking the linkage for a lifetime-extended temporary. (If the extending declaration is externally visible, I think the temporary should be linkonce_odr.)

{F51457}
Could this small change be useful for solving the problem?

I don't think the available_externally check is right, but that looks like the right direction.