I have written a ModulePass that calculates various things and adds custom metadata attributes to certain instructions. Depending on the attributes, I would like to change the machine code of these instructions. For example, I would like to replace certain calls with jumps, but as far as I can tell the IR metadata is not accessible anymore on the level of machine instructions (e.g. in the AsmPrinter). What is the best way to pass the information calculated by the pass (e.g the attributes) to the part where the target specific instructions are emitted?
I think an intrinsic would be ideal, but I couldn’t figure out how to replace calls with an arbitrary number of arguments with a call to an intrinsic. In particular, I would like to call a trampoline instead of the original function. The trampoline performs certain actions and then jumps to the original callee. Would I have to call the intrinsic with the original arguments and then perform a jump to the original function in the intrinsic?
Inlining is not possible unfortunately. I tried to use a IR branch instruction to jump the trampoline, but then the first basic block of the trampoline has a predecessor which is not allowed.
I would try to replicate your call with an intrinsic and jump to the trampoline. There’s a vararg_ty that can be used for your intrinsic type declaration, but I haven’t used it myself.
I would encourage you to look at the “cfguard” bundle added for Windows Control Flow Integrity. AsmPrinter is probably far too late for the transform you want to do. You probably need to implement it somewhere in call lowering, so assuming this is for x86, you would implement this in X86ISelLowering::LowerCall.
I think it would be less invasive to attach your metadata to the existing call instructions, and then change code generation later, rather than replacing the IR instructions completely.
The one thing I can‘t seem to figure out is how to lower a jump to another function at any stage before AsmPrinter. The methods to construct an unconditional branch always seem to expect a (machine) basic block and I don’t know how to get the first basic block of another function.
Is DAG.getNode(ISD::BR, dl, MVT::Other, ???) the right approach?
And why isn’t there a X86::BR, but only a X86::BRCOND?
I’d recommend looking at how tail call lowering is implemented. The TAILJMP* family of pseudo instructions are modelled as calls throughout CodeGen, but later in X86MCInstLower.cpp they are translated to plain JMP instructions. This avoids branch folding and the like from assuming things about the control flow of blocks ending in JMP_1. Similarly, you probably need to define a family of such call-like pseudo instructions, and lower them later.
Alternatively, you could try to get by without new instructions, and instead staple a bunch of extra special operands to the regular CALL* family of instructions. Later, you would change the way such instructions are “printed” depending on the presence of your operand. CALL* is a variadic instruction, so you can put more or less anything in the operand list, such as perhaps metadata. This approach runs the risk of rewriting CALL instructions that didn’t carry your special annotations, or having other CodeGen passes fold call instructions in ways that are incompatible with your transformation.
Felix Berlakovich via llvm-dev <llvm-dev@lists.llvm.org> writes:
I have written a ModulePass that calculates various things and adds
custom metadata attributes to certain instructions. Depending on the
attributes, I would like to change the machine code of these
instructions. For example, I would like to replace certain calls with
jumps, but as far as I can tell the IR metadata is not accessible
anymore on the level of machine instructions (e.g. in the
AsmPrinter). What is the best way to pass the information calculated
by the pass (e.g the attributes) to the part where the target specific
instructions are emitted?
This is something I have wanted for quite some time. It would be really
useful to have a Machine IR metadata facility. In the past I have
hacked things in in various ways but a more general solution would be
ideal.
Would others be interested in such a facility? I can't promise that I
can devote a ton of time to it right now but it is something I might
look at in the future if there is enough interest and/or it would be
accepted upstream. If it's not likely to be accepted upstream I don't
want to spend a lot of time on it, obviously.