Skip to content

Conversation

@mdavis36
Copy link
Collaborator

No description provided.

Core Architecture Change:
  - Changed Fusion from inheritance (class Fusion : public IrContainer)
to composition (class Fusion { std::u
nique_ptr<IrContainer> container_; })
  - Added back-reference from IrContainer to owning Fusion via fusion()
method
  - All IrContainer public methods forwarded through Fusion
  - Added isA<T>() and as<T>() methods to Fusion (no longer inherits
from PolymorphicBase)
@github-actions
Copy link

github-actions bot commented Dec 16, 2025

Review updated until commit f3e849f

Description

  • Changed Fusion from inheritance to composition pattern with IrContainer

  • Added back-reference from IrContainer to owning Fusion via fusion() method

  • Fixed virtual dispatch for registerExpr by adding Fusion::registerStmt()

  • Updated IrBuilder::registerWithContainer() to route through owning Fusion

  • All IrContainer methods now forwarded through Fusion composition

Changes walkthrough

Relevant files
Enhancement
22 files
fusion.cpp
Core architectural change to composition pattern                 
+72/-20 
fusion.h
Fusion class definition with composition                                 
+106/-9 
container.cpp
IrContainer with back-reference to Fusion                               
+21/-2   
container.h
IrContainer interface with owning Fusion                                 
+31/-9   
builder.h
IrBuilder interface with registration helpers                       
+9/-3     
base_nodes.cpp
Updated fusion() and kernel() methods                                       
+13/-10 
validation.cpp
Updated container access pattern                                                 
+2/-2     
dynamic_transform.cpp
Updated container access pattern                                                 
+1/-2     
evaluator_common.cpp
Updated container access pattern                                                 
+1/-1     
grouped_reduction.cpp
Updated container access pattern                                                 
+1/-1     
container.cpp
Updated container access pattern                                                 
+1/-1     
ir.cpp
Updated container access pattern                                                 
+13/-13 
lower.cpp
Updated container access pattern                                                 
+1/-1     
schedule.cpp
Updated container access pattern                                                 
+1/-1     
internal_nodes.cpp
Updated container access pattern                                                 
+4/-4     
kernel_ir.cpp
Updated container access pattern                                                 
+32/-32 
resharding.cpp
Updated container access pattern                                                 
+1/-1     
move_gather.cpp
Updated container access pattern                                                 
+1/-1     
loop_domain_scheduler.cpp
Updated container access pattern                                                 
+3/-3     
utils.cpp
Updated container access pattern                                                 
+1/-0     
statement_guard.cpp
Updated container access pattern                                                 
+1/-1     
tensor_view.cpp
Updated container access pattern                                                 
+12/-12 
Bug_fix
1 files
builder.cpp
Registration routing through owning Fusion                             
+17/-0   
Tests
5 files
test_host_ir_evaluator.cpp
Updated container access pattern                                                 
+2/-2     
test_host_ir_jit.cpp
Updated container access pattern                                                 
+1/-1     
test_host_ir_stream_lowering.cpp
Updated container access pattern                                                 
+1/-1     
test_host_irs.cpp
Updated container access pattern                                                 
+7/-7     
test_multidevice_host_ir.cpp
Updated container access pattern                                                 
+2/-2     
Additional files
5 files
base_nodes.h +1/-0     
builder_passkey.h +1/-0     
cloner.cpp +1/-1     
cloner.h +3/-1     
kernel.h +0/-3     

PR Reviewer Guide

Here are some key observations to aid the review process:

🧪 PR contains tests
⚡ Recommended focus areas for review
Null pointer dereference risk

Multiple locations call container()->fusion() without null checks. If container() returns nullptr or fusion() returns nullptr, this will cause crashes. Need to verify all code paths handle null cases properly.

Fusion* Statement::fusion() const {
  Fusion* fusion = ir_container_->fusion();
  NVF_ERROR(fusion != nullptr, "Statement does not belong to a fusion.");
  return fusion;
}

kir::Kernel* Statement::kernel() const {
  Fusion* fusion = ir_container_->fusion();
  NVF_ERROR(
      fusion != nullptr && fusion->isA<kir::Kernel>(),
      "Statement does not belong to a kernel.");
  return fusion->as<kir::Kernel>();
}
Container ownership complexity

The new composition pattern introduces back-references (owning_fusion_) that could create circular dependencies or memory management issues. The updateAllStatementContainerPointers() function suggests complex state management that needs careful validation.

// the Fusion destructor and the scope initializer could call the
// constructor of Trace, which could throw an exception.
// FUSER_PERF_SCOPE("Fusion clear");

if (container_) {
  container_->clear();
}

inputs_.clear();
outputs_.clear();

io_alias_.clear();
Registration logic complexity

The conditional registration logic (through Fusion vs direct to container) creates two different code paths that must be kept in sync. This increases complexity and risk of inconsistencies in IR state management.

Fusion* fusion() const {
  return owning_fusion_;
}

// Set the owning fusion - should only be called by Fusion
void setOwningFusion(Fusion* fusion) {
  owning_fusion_ = fusion;
}

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 16, 2025

Greptile Overview

Greptile Summary

This PR refactors Fusion from an inheritance-based design to a composition pattern. Previously Fusion inherited from IrContainer; now it contains an IrContainer* member and forwards container methods. Similarly, IrContainer no longer inherits from PolymorphicBase, with Fusion maintaining that inheritance instead.

Key changes:

  • Fusion now owns an IrContainer via std::unique_ptr<IrContainer> container_ with forwarding methods
  • IrContainer adds owning_fusion_ back-reference to support bidirectional navigation
  • Type checking pattern changed: container()->isA<Kernel>() becomes container()->fusion()->isA<Kernel>()
  • Added updateAllStatementContainerPointers() to fix dangling pointers after container swaps
  • Registration now routes through Fusion::registerStmt() when container is owned by a Fusion

Issue identified:
The type checking logic in csrc/ir/base_nodes.cpp:82 appears incorrect. The code checks fusion->isA<kir::Kernel>() on a Fusion*, but since Kernel inherits from Fusion (not from IrContainer), this check should work correctly. However, the previous review comment suggests this is problematic - needs verification of the inheritance hierarchy.

Confidence Score: 3/5

  • This PR requires careful testing due to fundamental architectural changes in type checking and container ownership
  • Score reflects the complexity of the refactoring and potential issues with type checking logic. The composition pattern is well-implemented with proper back-references and pointer updates, but the type checking changes (especially fusion()->isA<Kernel>() pattern) need thorough validation across all edge cases
  • csrc/ir/base_nodes.cpp requires close attention for the type checking logic changes, and csrc/fusion.cpp needs validation of the swap and copy operations with container pointer updates

Important Files Changed

File Analysis

Filename Score Overview
csrc/fusion.h 4/5 Changed from inheritance to composition pattern: Fusion now contains an IrContainer* member instead of inheriting from it, forwarding all container methods
csrc/fusion.cpp 4/5 Updated to use composition pattern with back-reference management and proper container ownership; swap operations now update container pointers
csrc/ir/container.h 4/5 Removed PolymorphicBase inheritance, added owning_fusion_ back-reference and updateAllStatementContainerPointers() method for composition support
csrc/ir/container.cpp 4/5 Implemented updateAllStatementContainerPointers() to fix dangling pointers after container swaps; simplified registration during copy
csrc/ir/base_nodes.cpp 3/5 Changed type checks to use container()->fusion() indirection with null checks; logic assumes Kernel inheritance but checks may be incorrect
csrc/ir/builder.cpp 4/5 Added registerWithContainer() helper that routes registration through Fusion when container is owned by one
csrc/kernel_ir.cpp 4/5 Updated all type checks to use container()->fusion() indirection pattern throughout kernel IR constructors

Sequence Diagram

sequenceDiagram
    participant User
    participant Fusion
    participant IrContainer
    participant Statement
    participant IrBuilder
    
    Note over Fusion,IrContainer: Composition Pattern
    
    User->>Fusion: new Fusion()
    Fusion->>IrContainer: create unique_ptr<IrContainer>
    IrContainer-->>Fusion: container_
    Fusion->>IrContainer: setOwningFusion(this)
    IrContainer->>IrContainer: owning_fusion_ = this
    
    Note over User,Statement: Creating IR Nodes
    
    User->>IrBuilder: create<Val>(args...)
    IrBuilder->>Fusion: getActiveContainer()
    Fusion-->>IrBuilder: container()
    IrBuilder->>Val: new Val(passkey, args)
    Val->>Statement: ir_container_ = container
    IrBuilder->>IrBuilder: registerWithContainer(container, val)
    IrBuilder->>IrContainer: fusion()
    IrContainer-->>IrBuilder: owning_fusion_
    alt Container owned by Fusion
        IrBuilder->>Fusion: registerStmt(val)
        Fusion->>IrContainer: registerVal(passkey, val)
    else Standalone container
        IrBuilder->>IrContainer: registerStmt(passkey, val)
    end
    
    Note over User,Statement: Type Checking Pattern
    
    User->>Statement: kernel()
    Statement->>IrContainer: fusion()
    IrContainer-->>Statement: owning_fusion_
    Statement->>Fusion: isA<kir::Kernel>()
    Fusion-->>Statement: true/false
    Statement->>Fusion: as<kir::Kernel>()
    Fusion-->>Statement: kernel ptr
    
    Note over Fusion,IrContainer: Swap Operations
    
    User->>Fusion: swap(fusion_a, fusion_b)
    Fusion->>Fusion: swap(container_, container_)
    Fusion->>IrContainer: setOwningFusion(&fusion_a)
    Fusion->>IrContainer: updateAllStatementContainerPointers()
    loop For each Val/Expr
        IrContainer->>Statement: ir_container_ = this
    end
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

24 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 79 to +84
kir::Kernel* Statement::kernel() const {
Fusion* fusion = ir_container_->fusion();
NVF_ERROR(
ir_container_->isA<kir::Kernel>(),
fusion != nullptr && fusion->isA<kir::Kernel>(),
"Statement does not belong to a kernel.");
return ir_container_->as<kir::Kernel>();
return fusion->as<kir::Kernel>();
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Logic error: fusion->isA<kir::Kernel>() will always return false because Kernel inherits from Fusion, not from IrContainer.

Since Kernel extends Fusion, you should check if the fusion itself is a Kernel:

Suggested change
kir::Kernel* Statement::kernel() const {
Fusion* fusion = ir_container_->fusion();
NVF_ERROR(
ir_container_->isA<kir::Kernel>(),
fusion != nullptr && fusion->isA<kir::Kernel>(),
"Statement does not belong to a kernel.");
return ir_container_->as<kir::Kernel>();
return fusion->as<kir::Kernel>();
kir::Kernel* Statement::kernel() const {
Fusion* fusion = ir_container_->fusion();
NVF_ERROR(
fusion != nullptr && fusion->isA<kir::Kernel>(),
"Statement does not belong to a kernel.");
return static_cast<kir::Kernel*>(fusion);
}

Or more simply, since Kernel is a Fusion subclass, use dynamic_cast for type safety.

Major Fix Applied: The core issue was that when we changed Fusion from
inheriting IrContainer to using composition, the virtual dispatch for
registerExpr was broken.

1. Added Fusion::registerStmt(Statement*) method that dispatches to
registerVal or registerExpr
2. Modified IrBuilder::registerWithContainer() to check if the container
has an owning Fusion, and if so, call Fusion::registerStmt() instead of
directly calling container->registerStmt()
3. This ensures that Fusion::registerExpr is called, which properly sets
the definition on output Vals
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

33 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

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