guessInstructionProperties, X86, mayLoad/Store and Predicates

Hello!

As part of our project, we are trying to configure the LLVM X86
back-end to not select or schedule certain classes of instructions. I
believe I can easily do that for some categories by using the platform
feature predicates in X86.td . But I would like to disable larger
classes of instructions - say instructions that read from memory or
instructions that write to memory or have side-effects. My initial
approach was an attempt to modify X86InstrFormat and, perhaps
appending a feature predicate to the Predicates field contingent on
mayLoad or mayStore being set.

class X86Inst<...> {
...
let Predicates = !if(!or(mayLoad,!mayStore), [UseLoadStores], []);
}

Now I get the clash between predicates and Requires and how the Mips
folks solved that and I can work around that issue just fine.
Unfortunately, I can't seem to access the mayLoad or mayStore bits in
time to use them in the predicate. They come back as ? which causes
tablegen to error out when evaluating the !if expression.

Those bits seem to be generated in tablegen with a fall-back path (for
guessInstructionProperties = 1) where it simply clears both mayLoad
and mayStore and sets hasSideEffects. Turning off
guessInstructionProperties for X86 seems to result in tens of
thousands of failures where patters can't infer these effects - most
of which seem to indeed depend on the behavior that mayStore = 0 and
mayLoad = 0 by default unless it can be inferred otherwise.

Do you know of any way I could actually acquire the inferred
mayLoad/mayStore properties for an instruction from within the
tablegen language? Alternatively, does it make sense to hack into
tablegen at CodeGenDAGPatterns::CodeGenDAGPatterns and have a step
after InferInstructionFlags() that then rewrites the predicate list of
all patterns based on the instructions I wish to include/exclude? I
cannot quite figure out how the various tablegen backends maintain the
relationship between the parsed instruction record and the many
different patterns it generates from it. Would simply iterating over
PatternsToMatch in CodeGenDAGPatterns and appending an extra Predicate
work well enough for all the back-ends? Any smarter way to pull this
all off?

Of course I also understand there's the "painful" way to do this,
which is to actually go into all the X86Instr*.td files and manually
write mayLoad and mayStore entries for the instructions I care about
so those values are indeed available to be referenced in the X86Inst
record. Is there any plan to actually move X86 .td files towards such
explicit annotation such as RISC-V with it actually setting mayLoad
and mayStore explicitly? Would even partially doing that be valuable
to y'all?

Thank you!

Kartik

I’m not sure why you want to disable all load and stores. I could see wanting to disable load folding into arithmetic operations, but disabling the basic load and store to/from register will just cause isel to fail. Can you elaborate more on your goal?

Didn’t mean all of them :). It is pretty much what you are imagining. I am attempting to disable all the “do-something-but-also-load-and-store” instructions and I don’t full understand dag patterns and the selection dag code yet. I can add an extra field to X86Instr that is something akin to “AllowMemoryAccess”. That way, I can selectively turn on and off specific instruction classes or multiclasses but it seemed tedious to manually tag every single instruction that read or wrote to memory.

I primarily care only for the move, arithmetic, extension, control, and conditional instructions and not the ones that are for packed fused vector shuffles that cook you pizza. Maybe there’s a more sensible way for me to correctly attach a predicate to all those instructions? Or somehow write a predicate that would let me reference mayLoad or mayStore at from the C++ code (I couldn’t figure out how to get to the instruction given just a SubTarget object)?

Thanks!

Kartik

All of the only load and only store instruction also have mayLoad and mayStore set. So that doesn’t seem like an effective way to disable just the “do-something-but-also-load-and-store”.

I think you can stop most load folding by making X86DAGToDAGISel::IsProfitableToFold return false. I don’t have a good answer for stores at the moment. But there are a lot fewer of those.

The Peephole pass will try to fold loads after isel and the register allocator can fold stack spills and reloads. So you’ll need to disable the foldMemoryOperand* functions in X86InstrInfo.cpp.

I suppose you could put an AddedComplexity = 300 on all the plain store instructions. That will artificially give them priority over all the patterns that include a store plus and another operation. Normally patterns are prioritized by counting how many nodes they will match, but AddedComplexity can make a pattern look like it matches more nodes to inflate the priority