Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CodeGen][X86] Improving robustness of PATCHABLE_OP wrapper instruction #59039

Closed
sylvain-audi opened this issue Nov 16, 2022 · 13 comments
Closed

Comments

@sylvain-audi
Copy link
Contributor

When activating -fms-hotpatch in clang, the "patchable-function" pass replaces the first machine instruction with a wrapper PATCHABLE_OP.

This wrapping loses some information about the wrapped instruction: when a PATCHABLE_OP instruction is handled by X86AsmPrinter::emitInstruction, the wrapped instruction simply gets lowered without going through X86AsmPrinter::emitInstruction itself.

Here is an example in C/C++, where a tail call doesn't get lowered properly: https://godbolt.org/z/1Pjcbx87n

The source of the issue seems to be the loss of information in PatchableFunction::runOnMachineFunction when replacing a MachineInstr with the PATCHABLE_OP one: It only keeps the OpCode and operands of the wrapped instruction, and X86AsmPrinter::emitInstruction can't be called from it.

We are looking for a clean way to achieve this. Any suggestions?

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 16, 2022

@llvm/issue-subscribers-backend-x86

@tru
Copy link
Collaborator

tru commented Nov 17, 2022

Turns out that this is not only windows related. This happens for all X86 platforms when -fms-hotpatch is being used.

@tru
Copy link
Collaborator

tru commented Nov 17, 2022

Ping @aganea

@sylvain-audi
Copy link
Contributor Author

Ping!

@tru
Copy link
Collaborator

tru commented Nov 24, 2022

Maybe @phoebewang or @RKSimon can help out here since it's very much a complicated X86 backend issue.

@phoebewang
Copy link
Contributor

I'm not familiar with hotpatch. Is the patch https://reviews.llvm.org/D137642 will solve the problem here too?

@sylvain-audi
Copy link
Contributor Author

I'm not familiar with hotpatch. Is the patch https://reviews.llvm.org/D137642 will solve the problem here too?

That patch fixes the selection of the function's instruction to make patchable (guarantee it's at least 2 bytes long or is preceded by a 2-byte nop).
This issue is about the lowering part, which I coudn't figure out how to do without losing information.

sylvain-audi added a commit that referenced this issue Nov 30, 2022
This patch fixes crashes related with how PatchableFunction selects the instruction to make patchable:
- Ensure PatchableFunction skips all instructions that don't generate actual machine instructions.
- Handle the case where the first MachineBasicBlock is empty
- Removed support for 16 bit x86 architectures.

Note: another issue remains related with PatchableFunction, in the lowering part.
See #59039

Differential Revision: https://reviews.llvm.org/D137642
@sylvain-audi
Copy link
Contributor Author

Ping!

I'm currently stuck, none of my attempts seem sustainable.
Currently in our fork we have a hack that simply inserts a 2-byte nop at the beginning of the function, disabling the opcode modification. This results is a lot of useless nops.

Note that MSVC seems to do this differently, as in MSVC blog: "most of the time, the compiler can juggle things so that you don’t even notice that it arranged for the first instruction of a function to be a multi-byte instruction. ". But it seems that doing that in clang could be pretty involved.

@phoebewang
Copy link
Contributor

cc @KanRobert

@tru
Copy link
Collaborator

tru commented Aug 1, 2023

Ping on this - anyone know if someone that can help us progress on this?

@MolecularMatters
Copy link

MolecularMatters commented Jan 4, 2024

Pinging this once again since it was linked into an issue with tailcall optimizations today (#76958).

It would be great if somebody could look into this, since using -fms-hotpatch is a requirement for making clang-cl work with Live++. The list of companies relying on clang-cl & Live++ for stable C++ hot-reload is growing, so a fix for this underlying issue would be very welcome.

Since most developers/companies probably don't use the -fms-hotpatch switch in their shipping builds, but rather in their development, debug, and optimized builds, I suppose it would not be a problem if compiling with the -fms-hotpatch option does not generate the most optimal code.

I'd therefore like to propose the following compromise as a fix for this issue:

  • Run all code generation and optimization passes
  • For functions compiled with -fms-hotpatch, check whether the first instruction is at least 2 bytes long
  • If not, insert a simple 2-byte NOP (e.g. xchg ax, ax) as first instruction, leaving the rest of the compiled code as it is

This would only add an unnecessary 2-byte NOP for functions which are only one byte in size, which should be very, very few.

However, I'm not familiar with code generation and intermediate passes in LLVM, so the proposed solution might not make sense in the context of LLVM.

@shafik
Copy link
Collaborator

shafik commented Jan 4, 2024

Also see: #76879

@KanRobert KanRobert self-assigned this Jan 5, 2024
@aganea
Copy link
Member

aganea commented Jan 7, 2024

I've posted a fix in #77245

aganea added a commit that referenced this issue Jan 22, 2024
…77245)

Previously, tail jump pseudo-opcodes were skipped by the
`encodeInstruction()` call inside `X86AsmPrinter::LowerPATCHABLE_OP`.
This caused emission of a 2-byte NOP and dropping of the tail jump.

With this PR, we change `PATCHABLE_OP` to not wrap the first
`MachineInstr` anymore, but inserting itself before,
leaving the instruction unaltered. At lowering time in `X86AsmPrinter`,
we now "look ahead" for the next non-pseudo `MachineInstr` and
lower+encode it, to inspect its size. If the size is below what
`PATCHABLE_OP` expects, it inserts NOPs; otherwise it does nothing. That
way, now the first `MachineInstr` is always lowered as usual even if
`"patchable-function"="prologue-short-redirect"` is used.

Fixes #76879,
#76958 and
#59039
@aganea aganea closed this as completed Jan 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants