This is an archive of the discontinued LLVM Phabricator instance.

[TRE] Do not mark alloca's as escaped if the function only writes to argmem
AbandonedPublic

Authored by caojoshua on Apr 30 2023, 5:10 PM.

Details

Summary

TRE currently reasons that if a function does not write to any memory, a
passed alloca cannot escape. We strengthen this by not escaping for functions
that write only to argmem memory. An alloca is escaped if its address is
written to a location outside of the function, such as a global. If the
function only writes to arguments, there is no escaping. For example:

unsigned *foo(unsigned *n) {
    *n = 12;
    return n;
}

foo() writes to memory, but only to argmem memory. If n is a passed
alloca, it does not escape.

Diff Detail

Event Timeline

caojoshua created this revision.Apr 30 2023, 5:10 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 30 2023, 5:10 PM
caojoshua updated this revision to Diff 518372.Apr 30 2023, 5:19 PM
caojoshua edited the summary of this revision. (Show Details)

Update commit message

As a side note, while looking through the code, I didn't understand this:

// If it can write to memory, it can leak the alloca value.

Even if an alloca is written to in a called function, I don't see why the callsite needs to be escaped. I also don't see why we care about the alloca's value, we only care about the pointer to the alloca. I think maybe the comment is concerned about the called function writing copying the alloca pointer to a global for example, but that we should not be concerned because we already checked for nocapture

caojoshua published this revision for review.Apr 30 2023, 5:28 PM
Herald added a project: Restricted Project. · View Herald TranscriptApr 30 2023, 5:28 PM
efriedma requested changes to this revision.Apr 30 2023, 8:09 PM

A call cannot be marked tail if it may read or write to an alloca in the same function. There are basically two ways to to "read or write": either the pointer is passed as an argument, or it's captured.

For the function @test16 in the given testcase, neither call can be marked tail. The call to @use_readonly can obviously read the alloca. The call to @use_readonly can also capture the address of the alloca, so the call to @noarg can also read the alloca.

This revision now requires changes to proceed.Apr 30 2023, 8:09 PM
caojoshua retitled this revision from [TRE] don't escape callsite if its alloca argument is readonly to [TRE] Mark alloca's as escaped if their call does not write memory.
caojoshua edited the summary of this revision. (Show Details)

Update this patch to not mark alloca's as escaped even if their call does not write to memory.

I added some more pre-committed tests for memory(none|read) functions. After this patch, they are no longer marked as tail calls

There are basically two ways a pointer can be "captured": it can be written to memory, or it can be returned.

The code here is trying to reason more deeply about memory(none) functions: the memory(none) implies no writes to memory, so the only way a pointer can be captured is by returning it. The code follows the return value to figure out if it's actually used. So the tail markings in test17 and test18 should be fine, I think.

There are basically two ways a pointer can be "captured": it can be written to memory, or it can be returned.

The code here is trying to reason more deeply about memory(none) functions: the memory(none) implies no writes to memory, so the only way a pointer can be captured is by returning it. The code follows the return value to figure out if it's actually used. So the tail markings in test17 and test18 should be fine, I think.

OK, I think I'm starting to understand this better. Looking at the function attributes doc, I think we can slightly strengthen this to not escape for memory(read, argmem: readwrite). Right not, we always escape if the callee can write to memory, but we actually don't need to escape if the callee is only writing to argmems. Please see these examples: https://godbolt.org/z/qzj76rhah. Does this make sense?

caojoshua updated this revision to Diff 520231.May 7 2023, 6:25 PM
caojoshua retitled this revision from [TRE] Mark alloca's as escaped if their call does not write memory to [TRE] Do not mark alloca's as escaped if the function only writes to argmem.
caojoshua edited the summary of this revision. (Show Details)

Rework the patch and commit message completey based on discussion. Don't escape allocas that write only to argmems.

I don't understand the distinction you're trying to draw for argument vs. non-argument memory. Writing a pointer into memory passed as an argument counts as "captured" for the purpose of this analysis; it doesn't track memory, only values in registers.

Stepping back, the purpose of the analysis is to determine which allocas 'escape'. If an alloca does not escape, we cannot mark a call as a tail call. The definition of escaped is not defined in the source. For the purpose of deciding which calls can be tail called, I would say an alloca is escaped if its address is accessible from outside of the function, i.e. a global. In this case, we would not be able to make a tail call, because the alloca would be lost when the caller's frame is overwritten.

In general, I think there is a lot of missed opportunities for tail calls in TRE. In this example:

unsigned *foo(unsigned *n) { // define dso_local ptr @foo(ptr noundef returned writeonly %n) memory(argmem: write)
    *n = 12;
    return n;
}

void bar() {
    unsigned n; // lets say this is an alloca, and does not become a register
    foo(&n);
    could_be_tail_called();

n is captured when calling foo() because n is returned. Since foo() writes to memory, TRE marks n as escaped, and no functions can be marked as a tail call. Looking at the function, I don't see how the n alloca escapes, and it should not prevent any tail calls.

I think my current assumptions regarding function attributes in this patch are incorrect. I'll abandon this patch. If I revisit this idea, I'll back it up with data so I can make a stronger case.

caojoshua abandoned this revision.May 9 2023, 1:14 AM