This is an archive of the discontinued LLVM Phabricator instance.

[TableGen] Check if input string of !isa is an instance of record type
AbandonedPublic

Authored by pcwang-thead on Jun 14 2022, 5:17 AM.

Details

Summary

We can cast a string to a record via !cast, but we have no mechanism
to check if it is valid and TableGen will raise an error if failed to
cast. Besides, we have no semantic null in TableGen (we have ? but
different backends handle uninitialized value differently), so operator
like dyn_cast<> is hard to implement.

In this patch, when input of !isa is a string and type to check is a
record, we will treat the input string as record name and loop up all
defined records with the type to see if we can find one. We do this
check until the final resolving step is done to match what !cast has
done.

By doing these, we can write code like:

class dyn_cast_to_record<string r> {
  R value = !if(!isa<R>(r), !cast<R>(r), default_value);
}
defvar v = dyn_cast_to_record<"R0">.value; // R0 or default_value.

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptJun 14 2022, 5:17 AM
pcwang-thead requested review of this revision.Jun 14 2022, 5:17 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 14 2022, 5:17 AM
tra added a comment.Jun 14 2022, 10:25 AM

Do you have a practical use case where this feature would be needed?

TableGen will raise an error if failed to cast

This may be considered to be a feature. If you attempt to cast to a record that does not exist, then arguably something went wrong.

While I generally do agree that we don't have a good way to handle non-existing casts, I'm not convinced that !isa is the right place to deal with the issue.
E.g. I may want !isa<R>(x) to be false for any 'x' that's not an instance of R, even if x is a string with a value that matches the name of an existing record of R?

If we want/need to check whether a record with a given name exists, then we'll probably need a new operator.
Or, perhaps, we should consider extending !cast to accept an additional value to be returned if the cast has failed.

Do you have a practical use case where this feature would be needed?

Yes.
In RISCV backend, we have defined some pseudos for Vector extension, and we construct a table which maps pseudos to real instructions. These mappings are constructed by pseudos' name (see https://github.com/llvm/llvm-project/blob/06c6758a98161262ac97fad42248139d78d39581/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td#L429). For example, we have a Pseudo named PseudoVADD_VV_M1 and an Instruction named VADD_VV, we can connect them together with string substitutions of pseudo name, that is, !subst("_M1", "", !subst("Pseudo", "", PseudoName)) (see also https://github.com/llvm/llvm-project/blob/06c6758a98161262ac97fad42248139d78d39581/llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td#L49), and then, a !cast will cast instruction name to real Instruction record. The casting works because corresponding instruction always exists.
Our downstream would like to map these pseudos to other instructions, but not all pseudos have corresponding instructions. We use almost the same substitutions to map pseudo name to new instruction name, that is, !subst("_M1", "", !subst("Pseudo", "New_", PseudoName)). So PseudoVADD_VV_M1 will be mapped to NEW_VADD_VV. The key point is that pseudos may have no corresponding new instruction and !cast may be failed. So we wanna check if !cast would success and set a default noop instruction (like KILL instruction) if failed.

TableGen will raise an error if failed to cast

This may be considered to be a feature. If you attempt to cast to a record that does not exist, then arguably something went wrong.

While I generally do agree that we don't have a good way to handle non-existing casts, I'm not convinced that !isa is the right place to deal with the issue.
If we want/need to check whether a record with a given name exists, then we'll probably need a new operator.

I have considered adding a new operator !instanceof<T>(s) to check if whether a record with type T and name s exists, but I think !isa have the same semantic meaning with !instanceof, just like what we have in C++ code (https://llvm.org/docs/ProgrammersManual.html#the-isa-cast-and-dyn-cast-templates):

isa<>:
The isa<> operator works exactly like the Java “instanceof” operator. It returns true or false depending on whether a reference or pointer points to an instance of the specified class. This can be very useful for constraint checking of various sorts (example below).

So I tried to extend !isa first.

E.g. I may want !isa<R>(x) to be false for any 'x' that's not an instance of R, even if x is a string with a value that matches the name of an existing record of R?

I don't know if this feature is needed, I haven't found any usage like this in upstream. The most common scenario where we need a !isa is, we pass a record of superclass and then check if it is an instance of some subclasses.

Or, perhaps, we should consider extending !cast to accept an additional value to be returned if the cast has failed.

Agree, this can be feasible. I will have a try, but I still think we need something like !instanceof. :-)

tra added a comment.Jun 15 2022, 10:47 AM

I have considered adding a new operator !instanceof<T>(s) to check if whether a record with type T and name s exists, but I think !isa have the same semantic meaning with !instanceof,

The difference is that for C++ (and AFAICT in Java, based on your description), std::string or StringRef is just another class, while here a string is distinct from a record and we may need to distinguish the two.

E.g. I may want !isa<R>(x) to be false for any 'x' that's not an instance of R, even if x is a string with a value that matches the name of an existing record of R?

I don't know if this feature is needed, I haven't found any usage like this in upstream. The most common scenario where we need a !isa is, we pass a record of superclass and then check if it is an instance of some subclasses.

I agree that being able to determine existance of a named record is useful.
What I have concerns about is whether !isa is the right mechanism for that.
We're considering adding new functionality to the tablegen language and need to consider how it can be used or abused once it's in place. As I've pointed above, distinguishing between a string and a record is something that !isa would need to handle.

Or, perhaps, we should consider extending !cast to accept an additional value to be returned if the cast has failed.

Agree, this can be feasible. I will have a try, but I still think we need something like !instanceof. :-)

I'm fine with !instanceof, too. It would be cleaner than 'overloading' !cast with an extra argument.

@nhaehnle - WDYT?