banach-space / llvm-tutor

A collection of out-of-tree LLVM passes for teaching and learning
MIT License
3k stars 397 forks source link

Add Windows pass plugin support #101

Open jvstech opened 1 year ago

jvstech commented 1 year ago

Windows pass plugins are supported in LLVM 14+, and llvm-tutor is set up in such a way that enabling them would be a fairly simple process.

Requirements:

@banach-space: What do you think for the symbol exporting options? I prefer module definition files, but I leave that decision up to you. We can discuss it in further detail what would be needed for each option.

banach-space commented 1 year ago

Thank you so much for looking into this!

You are the expert here and I am happy to follow your lead/recommendation. I would only suggest that we go for the most canonical approach :)

OwenTrokeBillard commented 10 months ago

Hi @banach-space @jvstech. Thank you for your work on this tutor.

Enzyme is an established open source tool gaining prominence in the scientific computing community. It is an LLVM plugin that uses an out-of-tree pass to automatically differentiate arbitrary functions.

We are trying to get it to compile on Windows with Clang + MSVC but there are some stubborn linker errors. See https://github.com/EnzymeAD/Enzyme/issues/1607. The errors look related to the current issue and this discussion.

We would be grateful if either of you could peek at the error and see if the cause is obvious given your expertise.

jvstech commented 10 months ago

Hi @banach-space @jvstech. Thank you for your work on this tutor.

Enzyme is an established open source tool gaining prominence in the scientific computing community. It is an LLVM plugin that uses an out-of-tree pass to automatically differentiate arbitrary functions.

We are trying to get it to compile on Windows with Clang + MSVC but there are some stubborn linker errors. See EnzymeAD/Enzyme#1607. The errors look related to the current issue and this discussion.

We would be grateful if either of you could peek at the error and see if the cause is obvious given your expertise.

Just wanted to point out that the problem was identified, and I believe there is a way forward.

jvstech commented 10 months ago

I've had some more time to work on this, and I've run into an issue that's been discussed before elsewhere.

Each pass is its own library. The transforms need to link against their related analysis passes in order to use them. That doesn't sound like that big of a deal, except that the pass manager and pass CRTP mixin classes are completely implemented as templates, giving them inline linkage. If the linker doesn't see the symbols being used, it drops them in a cascading manner, and then there aren't any symbols to export for other pass libraries to link against -- even if they're manually decorated with __declspec(dllexport) or listed in a module definitions file.

For example:

Here's where the problem manifests in this scenario:

I haven't run into this issue before because every pass library I've ever written to this point has had all its passes -- both analysis and transform -- contained within that library, and they don't call out to other libraries. To be honest, I'm not sure why this works on ELF or Mach-O builds given what I've seen in LLVM's pass manager code, but that's probably due to a lack of knowledge on my part regarding how linking is handled for those formats.

To get everything building and linking properly, I believe we're going to have to change how passes are defined.

  1. We're probably going to have to build every library as an object library and a shared library -- with the shared libraries "linking" against the object libraries (via target_link_libraries, like target_link_libraries(RIV PUBLIC obj.RIV), for example). This ensures that even symbols the linker doesn't think are used will still be added to the library.
  2. We're also going to have to explicitly instantiate at least the ID() function for each of the analysis passes:
    extern template static llvm::AnalysisKey *llvm::AnalysisInfoMixin<RIV>::ID();

    I think, perhaps the cleanest way to do this is via X-macros, sort of how LLVM uses Passes.def.

Let me know what you think. This is a pretty big change and I don't want to tear up existing code without a consensus.