Hi @rnk,
Adding metadata uses of void values may be premature optimization. It seems better to make forward progress first and optimize later.
SGTM - I hadn’t considered the performance benefits we’d get by using that alternative approach. Assignment tracking definitely needs looking at from a performance perspective, which I’ve not had the chance to do yet.
To go back to your earlier point:
What kind of assignment would clang produce for these assignments? What local variable are we updating, what goes on the left hand side? DSE fires here today, and the user can observe stale values in the debugger.
I think it’s reasonable to not try to present accurate information in both cases you and I have highlighted:
struct Bar { int b; };
Bar *wash(Bar *p) { return p; }
void foo() {
Bar o;
wash(&o)->b = 1; // can DSE
wash(&o)->b = 2;
}
__attribute__((__optnone__)) void step() {}
void useAddress(int *n);
void fun() {
int local = 5;
int* local_ptr = &local;
step();
*local_ptr = 6; // causes local = 5 to be eliminated
step();
useAddress(&local);
}
Both of these examples have indirect stores to an alloca which backs a variable. These stores don’t get tagged (the have no DIAssignID metadata, and no dbg.assign records their positions). I’m currently of the opinion that the back end should treat these stores as assignments to that local (i.e. we should use the memory location for the variables there).
The result is that the locations generated for your example looks exactly the same with and without the prototype (DSE is visible). For my example, we track the initial store and acknowledge the second (DSE not visible) - this is an improvement over turning off LowerDbgDeclare (DSE visible), and results in the same values being available (from different locations) with LowerDbgDeclare turned on.
I’d like to clarify what values we promise to be accurate, and I think our promise should focus on assignments where the LHS is a local variable or a field of one.
I agree and think that promise sounds reasonable. The current design, including what I mentioned above, may cause inaccurate locations when an indirect assignment to a local:
- is eliminated - the store wasn’t tracked; there’s no dbg.assign to mark that an assignment happened.
- is partially eliminated - the store wasn’t tracked so we aren’t able to “undef” the eliminated part.
- is moved - the assignment will be visible from where it occurs in the optimized program (no attempt to match to the unoptimized program position, since the assignment hasn’t been tracked).
I believe the promise graciously covers these cases, and I cannot currently think of any further situations that result in inaccurate locations.
It’s less clear to me how to incorporate the fact that inlined assignments are tracked, but only from the time of inlining. I.e. some specific indirect assignments are tracked. At the moment I feel that tracking inlined stores like this is useful, but I think we can decide on that later when we’re closer to a shippable implementation and have more experimental data.
As an aside, I tried comparing how GCC (12) handles variables in memory to clang with and without assignment tracking, but for the few examples I looked at it appears clang is more aggressive with DSE than GCC so a direct comparison doesn’t tell us much.
I’m now going to start attempting to solve the remaining design limitations.