llvm / llvm/circt

[DC] Connecting up DC during incremental lowering

Open
#5,696 5 comments 0 reactions 1 assignee Claimed by @mortbopet View on GitHub
DC
Dominant language
C++
Stars
2.2k
Forks
524
Avg merge
3d 2h
Merged PRs (30d)
46

Description

I'm working on lowering to DC and I'd like to make a set of passes which do so incrementally rather than having to do it monolithically in one shot.

A real example:

```mlir
// Original code
ibis.class @C {
ibis.method @math(%a: i32, %b: i32) -> i32 {
%1 = comb.add bin %a, %b : i32
ibis.return %1 : i32
}
}

ibis.class @User {
ibis.instance @c, @C
ibis.method @wrapper(%a: i32, %b: i32) -> i32 {
%x = ibis.call @c::@math(%a, %b): (i32, i32) -> i32
ibis.return %x : i32
}
}
```

The `ibis-call-pass` as a first step converts the arguments into a single struct:

```mlir
ibis.class @C {
ibis.method @math(%arg: !hw.struct) -> i32 {
%a, %b = hw.struct_explode %arg : !hw.struct
%0 = comb.add bin %a, %b : i32
ibis.return %0 : i32
}
}
ibis.class @User {
ibis.instance @c, @C
ibis.method @wrapper(%arg: !hw.struct) -> i32 {
%a, %b = hw.struct_explode %arg : !hw.struct
%0 = hw.struct_create (%a, %b) : !hw.struct
%1 = ibis.call @c::@math(%0) : (!hw.struct) -> i32
ibis.return %1 : i32
}
}
```

Notice that it only affects the method boundaries -- doesn't touch or analyze the `comb.add`.

The next step is to DC-ify the calls/methods, meaning wrap the method arguments with `dc.value`s. Ideally, I would like to do this incrementally like I do with the last step -- changing the method signatures and add `dc.pack`/`dc.unpack`, so we don't have to monolithically convert all the control flow over to DC. Those two operations, however, consume/produce a `token`. Wiring the tokens together is not necessarily the right thing to do -- the control flow may dictate something different. Also, where do I get the `token` for the call's `pack`/`unpack`?

To demonstrate:

```mlir
// class C omitted
ibis.class @User {
ibis.instance @c, @C
ibis.method @wrapper(%arg: !dc.value>) -> !dc.value {
// Unwrap the args.
%inputToken, %unwrapped = dc.unpack %arg : !dc.value>
// What should I do with %inputToken?
%a, %b = hw.struct_explode %unwrapped : !hw.struct

// Wrap for the method call.
%0 = hw.struct_create (%a, %b) : !hw.struct
%tokenForCall = ... // Where should I get this?
%valueForCall = dc.pack %tokenForCall, %0 : !hw.struct
%1 = ibis.call @c::@math(%0) : (!dc.value>) -> !dc.value
// Unpack the result.
%callReturnToken, %retData = dc.unpack %1 : !dc.value
// What should I do with %callReturnToken?

// Pack the result.
%methodReturnToken = ... // where should I get this Value?
%ret = dc.pack %methodReturnToken, %retData : i32

ibis.return %ret : !dc.value
}
}
```

Suggestions? Perhaps the answer is to do all the control flow to DC in one pass. I'd rather not do that since it requires that one pass have knowledge of all the control flow constructs (and we're planning to add some custom control flow to ibis) so it's not just CF.

I'll post my suggested options as comments below so as to not frame the discussion as a selection between my options.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.