Skip to content

[CIR][WIP] Add ABI lowering pass #1471

New issue

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

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

Already on GitHub? # to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Lancern
Copy link
Member

@Lancern Lancern commented Mar 12, 2025

This PR attempts to add a new pass cir-abi-lowering to the CIR dialect. This pass runs before the CallConvLowering pass, and it expands all ABI-dependent types and operations inside a function to their ABI-independent equivalences according to the ABI specification.

The patch also moves the lowering code of the following types and operations from the LLVM lowering conversion to the new pass:

  • The pointer-to-data-member type cir.data_member;
  • The pointer-to-member-function type cir.method;
  • All operations working on operands of the above types.

@Lancern
Copy link
Member Author

Lancern commented Mar 12, 2025

The direct motivation for this new pass is the proper CallConvLowering of the !cir.method type. Currently, this type is lowered during LLVM lowering, which is too late to achieve a proper lowering. Consider the following CIR code:

cir.func @test(%arg0: !cir.method) {
  // ...
}

Following the current lowering approach, which keeps !cir.method until we arrive at LLVM lowering, we would eventually get the following LLVM IR:

define dso_local @test({ i64, i64 } %0) {
  ; ...
}

But we actually expect the following LLVM IR (note the differences on the function signatures):

define dso_local @test(i64 %0, i64 %1) {
  ; ...
}

To achieve this, I have 3 choices:

  1. Teach the CallConvLowering pass about the !cir.method type.
  2. Move the lowering of !cir.method to the LoweringPrepare pass, which runs before CallConvLowering.
  3. Add a new pass before CallConvLowering that lowers !cir.method to !cir.struct.

At the beginning I thought option 1 would be the easiest way. But as I dig through the rabbit hole I found some tricky stuff behind the scene. The problem comes from the CodeGen of function prologue and epilogue. In the prologue, each argument is assigned a stack slot and stored there. For an argument of type !cir.method, after CallConvLowering it would expands into two arguments of type !s64i. Thus in the function prologue I would have to come up a way to store two !s64i values into the stack slot allocated for a !cir.method value, which is tricky. Similar problems also exist in the epilogue.

The problem of option 3 is that the LoweringPrepare pass is not a conversion pass, which could be really tricky if you want to do type conversion stuff in it. In my case I have to convert every appearances of !cir.method to !cir.struct and this kind of job is better suited for a conversion pass.

Anyway, this PR is still very incomplete and under construction, I'd like to hear some early comments about this from the community.

@lanza lanza force-pushed the main branch 2 times, most recently from d2c4ab8 to 8f89224 Compare July 23, 2025 17:04
@Lancern Lancern force-pushed the abi-lowering-pass branch from 352959f to fc67ccf Compare August 4, 2025 16:07
@Lancern Lancern marked this pull request as ready for review August 4, 2025 16:08
@Lancern Lancern requested review from xlauko and andykaylor August 4, 2025 16:08
@Lancern
Copy link
Member Author

Lancern commented Aug 4, 2025

Sorry for the delay here, I finally got the bandwidth to update this PR. The primary goal of this PR is to introduce the cir-abi-lowering pass which performs ABI lowering work for globals and inside each function.

The cir-abi-lowering pass converts ABI dependent types to more "fundamental" CIR types, and replaces operations that act on these ABI dependent types with more "fundamental" CIR operations. The idea of cir-abi-lowering is to avoid mixing such ABI lowering logic in LLVM lowering code, which could be confusing and make the code more complex. With the introduction of cir-abi-lowering, we got the following benefits:

  • The LLVM lowering pass becomes more simple and easy to comprehend. It only has to deal with "fundamental" CIR operations and types, and does not have to deal with complex ABI details.
  • The lower to MLIR path could potentially benefit from the ABI lowering pass in the future. We don't need to implement ABI lowering again in the MLIR lowering path.
  • As stated in [CIR][WIP] Add ABI lowering pass #1471 (comment), some ABI types are better lowered to more "fundamental" types before CallConvLowering, which is impossible if we put their lowering code inline in the LLVM lowering pass.

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

I like the direction overall, thanks for working on this. I feel it's a bit confusing to have both cir-abi-lowering and cir-call-conv-lowering being distinct passes. Can we merge cir-call-conv-lowering into the former while keeping a switch to enable/disable the call conv lowering part (should be default to off for now)? It's fine if you do that in a follow up PR, but I'd like to have a plan before we land this.

@Lancern
Copy link
Member Author

Lancern commented Aug 10, 2025

I feel it's a bit confusing to have both cir-abi-lowering and cir-call-conv-lowering being distinct passes. Can we merge cir-call-conv-lowering into the former while keeping a switch to enable/disable the call conv lowering part (should be default to off for now)?

That makes sense to me, and I'll do that in a follow-up PR. Actually in short term I also plan to migrate part of LoweringPrepare as well, as some ABI lowering code is put there for now (e.g. the handling of dynamic_cast). The long-term goal would be to put all ABI-related code into this pass, although I'm not quite sure whether this is actually achievable under current CIR design. For example, some ABI-related code is necessary during CIRGen for now because the CIR emitted from there is already ABI-dependent.

@bcardosolopes
Copy link
Member

The long-term goal would be to put all ABI-related code into this pass, although I'm not quite sure whether this is actually achievable under current CIR design. For example, some ABI-related code is necessary during CIRGen for now because the CIR emitted from there is already ABI-dependent.

Yea, since we have no need for postponing some of the ABI decisions we just do it right away, avoiding duplicating AST information to only when necessary

This patch adds a new pass cir-abi-lowering to the CIR dialect. This pass runs
before the CallConvLowering pass, and it expands all ABI-dependent types and
operations inside a function to their ABI-independent equivalences according to
the ABI specification.

This patch also moves the lowering code of the following types and operations
from the LLVM lowering conversion to the new pass:
  - The pointer-to-data-member type `cir.data_member`;
  - The pointer-to-member-function type `cir.method`;
  - All operations working on operands of the above types.
@Lancern Lancern force-pushed the abi-lowering-pass branch from fc67ccf to 57f037d Compare August 18, 2025 15:29
@Lancern
Copy link
Member Author

Lancern commented Aug 18, 2025

Rebased.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants