Skip to content

Split PTX LLVM emission into common and per-kernel modules - #662

Open
0rlych1kk4 wants to merge 8 commits into
vosen:masterfrom
0rlych1kk4:investigate/ptx-compilation-split
Open

Split PTX LLVM emission into common and per-kernel modules#662
0rlych1kk4 wants to merge 8 commits into
vosen:masterfrom
0rlych1kk4:investigate/ptx-compilation-split

Conversation

@0rlych1kk4

@0rlych1kk4 0rlych1kk4 commented Jul 23, 2026

Copy link
Copy Markdown

What this changes

This PR splits PTX LLVM emission into a common module and separate modules for each kernel.

The common module keeps the global variables and helper function definitions. Each kernel module contains the kernel itself, declarations for the helper functions it calls, and extern declarations for the globals it may reference. The kernel modules are then linked back into the common module.

Why

The current emission path builds one LLVM module from all transformed PTX directives. This change introduces the structure needed to compile kernels separately without cloning the transformed instruction bodies.

Implementation notes

  • tracks direct and transitive helper dependencies for each kernel;
  • creates body-less declarations for reachable helper functions;
  • builds an ownership-based compilation plan so definitions are moved only once;
  • creates extern global declarations for kernel modules;
  • emits each kernel module separately and links it into the common module.

The monolithic reconstruction path is kept only for tests.

Validation

I ran:

  • cargo check -p ptx
  • cargo check -p ptx --tests
  • cargo build -p compiler --bin zoc

I also compiled these PTX files through LLVM linking and ELF generation:

  • ptx/src/test/spirv_run/add.ptx
  • ptx/src/test/spirv_run/call.ptx
  • ptx/src/test/spirv_run/global_array.ptx
  • ptx/src/test/spirv_run/extern_shared_call.ptx
  • ptx/src/test/spirv_run/32/add.ptx

The generated LLVM IR retained the helper definitions, global initializers, external shared declarations, and kernel calls as expected.
Opening this as a draft to get feedback on the module ownership and linking approach before expanding the test coverage.

Part of #652

This implements the compilation-unit splitting and linking groundwork for #652.

Each kernel is now emitted as a separate LLVM module with the required helper and global declarations, then linked into the common module. The current implementation still emits the kernel modules sequentially. Parallel scheduling of those compilation units is not included in this PR.

Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
Signed-off-by: 0rlych1kk4 <orlychikka@gmail.com>
@0rlych1kk4

Copy link
Copy Markdown
Author

Hi @vosen separately from the PR, I’m currently looking for paid work involving Rust, LLVM, GPU tooling, or compiler development. I’ve enjoyed working on this part of ZLUDA and would be interested in contributing professionally if there is a suitable opportunity, including contract, part-time, or full-time work. I’d also appreciate any referral or direction you may have. Thank you.

@vosen vosen left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Code in kernel_dependencies.rs has to be heavily reworked

  • it ignores the dependencies in global variables:

    const  .u32 foo = 42;
    .global .u32 p2 = foo;
    
  • It seems to ignore existence of declarations (going by FxHashMap<SpirvWord, &Function<ast::Instruction<SpirvWord>, SpirvWord>>):

    .extern .func foobar(.b64 x );
    
    ...
    
    .func foobar(.param .b64 x) {
        ...
    }
    

    Every function can be identified by SpirvWord, but there might be multiple Function objects corresponding to a single function (because of declarations)

  • The style is weird, why does each iterator one-liner need its own function? And why the function ordering is C++ style?

The code should first build a reachability graph (petgraph is already a dependency) and then split it into separate modules and compile

I did not read tests in detail, but they can be simplified too. Either by introducing some helpers or by extending existing text assertions code (see tests in ptx/src/pass/test)

@vosen

vosen commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Hi @vosen separately from the PR, I’m currently looking for paid work involving Rust, LLVM, GPU tooling, or compiler development. I’ve enjoyed working on this part of ZLUDA and would be interested in contributing professionally if there is a suitable opportunity, including contract, part-time, or full-time work. I’d also appreciate any referral or direction you may have. Thank you.

Sorry, but I know of no such opportunities currently

@0rlych1kk4

Copy link
Copy Markdown
Author

Thanks for the detailed feedback. I understand the concerns around global-variable dependencies, multiple declarations for the same function, the current structure of kernel_dependencies.rs, and the test style.
I’ll rework this around a proper reachability graph using petgraph, account for declarations and global dependencies, and simplify the tests using the existing test helpers where possible. I’ll update the PR once the revised approach is ready.
And thank you as well for answering my question about work opportunities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants