Skip to content

Cross-compile target modules and dispatch to executor#3033

Open
mehrdad2m wants to merge 14 commits into
isolate-remote-kernelsfrom
cross-compile-targets
Open

Cross-compile target modules and dispatch to executor#3033
mehrdad2m wants to merge 14 commits into
isolate-remote-kernelsfrom
cross-compile-targets

Conversation

@mehrdad2m

Copy link
Copy Markdown
Contributor

Context:
This PR adds a compile path that turns a catalyst.target-tagged QNode into a standalone object and either statically links it (local) or ships it to a remote executor.

Description of the Change:

  • Adds cross-compile-targets pass which lowers each catalyst.target nested module and emits a standalone .o. Then it records the path as catalyst.object_file, and reduce the module to external declarations. Local targets are also recorded for static linking.
  • Adds dispatch-executor-targets pass which ships the remote binaries and rewrites their host calls into executor.launch.
  • Modifies the compiler driver to run both passes after the bufferization stage and writes the local objects to a .objects manifest for the linker.

Benefits:

Possible Drawbacks:

Related GitHub Issues:

@github-actions

Copy link
Copy Markdown
Contributor

Hello. You may have forgotten to update the changelog!
Please edit doc/releases/changelog-dev.md on your branch with:

  • A one-to-two sentence description of the change. You may include a small working example for new features.
  • A link back to this PR.
  • Your name (or GitHub username) in the contributors section.

@maliasadi maliasadi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mehrdad2m! Will you fix the build issues on this PR or on the base branch/PR?

Comment thread mlir/Makefile
Comment on lines +79 to 81
-DLLVM_TARGETS_TO_BUILD="${LLVM_TARGETS_TO_BUILD}" \
-DLLVM_ENABLE_PROJECTS="$(LLVM_PROJECTS)" \
-DLLVM_ENABLE_ASSERTIONS=ON \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is consistent with other places:

Suggested change
-DLLVM_TARGETS_TO_BUILD="${LLVM_TARGETS_TO_BUILD}" \
-DLLVM_ENABLE_PROJECTS="$(LLVM_PROJECTS)" \
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_TARGETS_TO_BUILD=${LLVM_TARGETS_TO_BUILD} \
-DLLVM_ENABLE_PROJECTS=$(LLVM_PROJECTS) \
-DLLVM_ENABLE_ASSERTIONS=ON \

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added to allow multiple targets/project to be parsed correctly. e.g. without the quotation, cmake can not parse DLLVM_TARGETS_TO_BUILD=X86;AArch64 correctly. Let me know if you know a cleaner way to achieve this.

Comment on lines +23 to +24
// CHECK: executor.send_binary("ADDR:PORT", "/tmp/target_compute.o")
// CHECK-NOT: executor.send_binary

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do CHECK and CHECK-NOT executor.send_binary check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added to ensure we only send the binary once even if we have multiple launch_kernels on the same object file. I'll add a comment to clarify that

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread mlir/include/Executor/Transforms/Passes.td Outdated

1. Injects `executor.open` into `setup()` (once per unique address) and `executor.send_binary`
into `setup()` (once per module).
2. Replaces every host-side `func.call` to the module's entry functions with a

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the strategy to replace host func.call with executor.launch? What does filter the host-side func.calls which need to run on the host from those need to be handled with executor?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good catch. This docstring is outdated. I updated it in 2aafda2. the func.calls are unaffected by this pass. It only acts on nested modules with catalyst.dispatch attribute.

Comment thread mlir/lib/Executor/Transforms/DispatchExecutorTargets.cpp Outdated

// Open a session (once per address), ship the object recorded in
// `catalyst.object_file`, and rewrite each host-side func.call into a `executor.launch`.
LogicalResult dispatchViaOrcExecutor(ModuleOp host, ModuleOp nested,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this function do? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It handles one dispatch module which involves opening the session, shipping the object, and rewriting that module's launch_kernels into executor.launch. I just noticed though that the name carries an implementation detail it shouldn't, I renamed it to dispatchTargetModule. 350665b

Comment thread mlir/lib/Catalyst/Transforms/CrossCompileTargets.cpp Outdated
dumpLLVMIR(*llvmModule, kernelDir, name.str() + ".ll");
}
std::string objPath =
emitObjectFile(std::move(llvmModule), name, kernelDir, *targetMachine);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a guard before the compile loop to fix the object-file name collision issue? as it generates {workspace}/{name}/{name}.o and for two targets with the same sym name, this will silently fail.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Although having two targets with the same sym name would not be a valid IR to begin with, we default to "unnamed" for modules with no name and that could trigger this issue. I added a guard e22b237

Comment thread mlir/lib/Driver/CompilerDriver.cpp Outdated
let summary = "Apply the passes scheduled with the transform dialect.";
}

def CrossCompileTargetsPass : Pass<"cross-compile-targets", "mlir::ModuleOp"> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤩

mehrdad2m and others added 11 commits July 22, 2026 12:09
Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com>
Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com>
Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com>
Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com>
@mehrdad2m
mehrdad2m requested a review from maliasadi July 22, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants