Address thread identification problems with coroutine

For concreteness, I was actually thinking that readnone means “does not access memory”, where “thread_id” is considered to not be part of memory. That is:

  1. Introduce new attribute noread_thread_id
  2. Emit noread_thread_id in addition to readnone in the frontend for functions that are known to not read the thread ID implicitly or explicitly.
  3. Mark the TLS intrinsic as only as readnone.
  4. Many checks for hasAttribute(ReadNone) (e.g. to guard CSE) have to become hasAttribute(ReadNone) && (!isCoroutine(CurrentFn) || !hasAttribute(NoReadThreadID). Though, note that this isn’t the first time that happens. For example, we have a bunch of places that need to check hasAttribute(ReadNone) && !hasAttribute(Convergent). So there’s precedent.

It ends up being functionally equivalent to how you interpreted what you wrote, but I’d argue that what I’m describing here is more composable. The way I like to think about it is that there are certain “capabilities” that code may be using: reading memory, writing memory, freeing memory, synchronization, accessing globals, indirect memory accesses; and attributes are used to express which of these capabilities code may be using. Capabilities can be thought of as (almost) a lattice, so defining the attributes in terms of “atomic” capabilities is better for composability.

I understand that there is a bit of tension here between what looks like raw software engineering pragmatism and formal semantics arguments. So I think I can mostly understand where you’re coming from, it’s just that personally, I’ve been burned too often by holes in semantics of IR which is why I fall more in the camp of “let’s please get the semantics right” :slight_smile:

1 Like