Skip to content

Conversation

@klarasm
Copy link
Collaborator

@klarasm klarasm commented Dec 14, 2025

Allow delays to be connected to global inputs and outputs.

There are a few parts of this:

  1. SDFSchedule needs to find the correct rate when the IO edge is a delay. Do that by finding the first actor along that signal chain and use that rate.
  2. This also does not generate a new IRId for delay edges and just uses the input one. Note that we still need the alias for the input signal since we always do the lookup.
  3. ForSyDeIRToProceduralIR needs to use the translated buffers for foldActorSignals so it can see the actual buffer id.
  4. Add examples for these scenarios.
  5. Also remove the old hardcoded first attempt at translating Core functions to ProceduralIR.

@klarasm klarasm linked an issue Dec 14, 2025 that may be closed by this pull request
@klarasm klarasm force-pushed the delayGlobalInputOutput branch 5 times, most recently from e4b5884 to 806154e Compare December 14, 2025 19:23
@klarasm klarasm changed the title WIP: Allow delays connected to global inputs and outputs Allow delays connected to global inputs and outputs Dec 14, 2025
@klarasm klarasm marked this pull request as ready for review December 14, 2025 19:32
(IRString $ show inSignalId ++ "_" ++ show outSignalId) -- delay edge names are combined
-- use the input signal id as the delay edge id
inSignalId
(findActorByName srcIn)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This improvement looks great! I think you have written nonDelayInputSignal and nonDelayInputSignal to recursively find the first non delay signal, so it should support a delay actor chain and I wrote a simple example to test it. However, there was an error Actor not found: a_delay2 when I tried running an SDF graph like this:

-- system: s_in -> a_add -> d1 -> d2 -> s_out

I think the error was caused here SDFSchedule:95, when it was converting ForSyDe IR delay edges. It assumed the src and dst of an delay edge should be a non-delay actor so it just used findActorByName to find them, but it failed if there existed an delay chain. So there should also be a recursive way to build delay edges. Should I make a new issue and work on that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right, we would need to merge successive delays between regular actors too (and ignore delay-delay connections if the chain is on an input or outout). I think it's more a nice to have since the same model with a single (merged) delay element on the edge should have the same semantic meaning (so we could get the same result by changing the model). Creating the issue would be good though, since we want to document what parts of the compiler is incomplete, and we can work on it if we have time left over.

Copy link
Collaborator

@SamuelMiksits SamuelMiksits left a comment

Choose a reason for hiding this comment

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

I think this is fine for the most part. I ran all of the models we have now and did not get any errors, and I tried the test suite and it passes. From the code perspective, I looked at it and it looks fine. But someone else might have other opinions or scenarios where it doesn't work

@klarasm
Copy link
Collaborator Author

klarasm commented Dec 16, 2025

I added a few more examples to this PR.

@sthaeron
Copy link
Owner

I'm a little confused about how the implementation works. I don't quite understand why adding inputDelays and outputDelays statement lists to the translation context was necessary. The signal names of system inputs and outputs, which are delayed, seem to be propagated to the process constructor for SDF examples 20 and 21. Thus, shouldn't the foldActorSignal pass for a_add add the necessary statements as they are global inputs/outputs?

Perhaps we can talk about it tomorrow in person after the meeting @klarasm. But I can't seem to wrap my head around why the original solution didn't just work once the SDF Scheduler was updated?

Nevertheless, the implementation in its current state does work!

@klarasm klarasm force-pushed the delayGlobalInputOutput branch 2 times, most recently from 6ef0d2c to 6cf4029 Compare December 16, 2025 19:34
@klarasm
Copy link
Collaborator Author

klarasm commented Dec 16, 2025

I'm a little confused about how the implementation works. I don't quite understand why adding inputDelays and outputDelays statement lists to the translation context was necessary. The signal names of system inputs and outputs, which are delayed, seem to be propagated to the process constructor for SDF examples 20 and 21. Thus, shouldn't the foldActorSignal pass for a_add add the necessary statements as they are global inputs/outputs?

The reason the current solution doesn't work is that foldActorSignals only looks at the immediate input/output, meaning if there's a delay there, it won't produce the statements for reading or writing the buffer. But this actually gave me an idea, maybe it's better to make this function continue until it finds either an actor or global input/output (similar how I did it in the scheduler)?

Perhaps we can talk about it tomorrow in person after the meeting @klarasm. But I can't seem to wrap my head around why the original solution didn't just work once the SDF Scheduler was updated?

We can do that.

Nevertheless, the implementation in its current state does work!

@klarasm klarasm force-pushed the delayGlobalInputOutput branch from 6cf4029 to 03ef1e2 Compare December 16, 2025 20:21
@klarasm
Copy link
Collaborator Author

klarasm commented Dec 16, 2025

@sthaeron

It was a good thing you commented on that, turns out the only thing we actually needed for ForSyDeIRToProceduralIR was:

diff --git a/src/ForSyDeIRToProceduralIR.hs b/src/ForSyDeIRToProceduralIR.hs
index f88c4091b8cc..43974dc59d88 100644
--- a/src/ForSyDeIRToProceduralIR.hs
+++ b/src/ForSyDeIRToProceduralIR.hs
@@ -193,8 +193,8 @@ translateIRConstructor initialContext constructor = case constructor of
                     ++ [EVar $ show functionId]
                 )
             )
-        inputStmts = foldl' foldActorSignals [] inputSignals
-        outputStmts = foldl' foldActorSignals [] outputSignals
+        inputStmts = foldl' foldActorSignals [] translatedInputSignals
+        outputStmts = foldl' foldActorSignals [] translatedOutputSignals
         stmts = reverse inputStmts ++ [actorCallStmt] ++ reverse outputStmts
         context1 = initialContext {actors = (actorId, stmts) : actors initialContext}
      in context1

Then the IO will be handled by the actors as you said.

@klarasm klarasm requested a review from sthaeron December 16, 2025 20:29
@klarasm
Copy link
Collaborator Author

klarasm commented Dec 17, 2025

Hmm. One issue with the approach in this PR is that even if we could produce output immediately we will still try to get input first. But this we can probably put as future work.

@klarasm klarasm force-pushed the delayGlobalInputOutput branch from 03ef1e2 to 1f4d4bb Compare December 17, 2025 14:59
For now just put parentheses around all expressions, regardless of if
needed or not. This might be less readable, but the current solution is
too simplified. We would additionally need to consider operator
precedence and associativeness (left or right).
We have a general solution in CoreIRToProceduralIR now.
Just use the input one. This should make the generated code a bit easier
to read. It should also be more efficient in the compilation steps since
we're comparing less strings.

Note that this also makes an alias for the input edge since we always
lookup the alias list in ForSyDeIRToProceduralIR.
@klarasm klarasm force-pushed the delayGlobalInputOutput branch from 1f4d4bb to 2f5290a Compare December 17, 2025 17:13
@klarasm
Copy link
Collaborator Author

klarasm commented Dec 17, 2025

I reverted back to just placing parentheses around everything in this PR, though I have another idea in #309.

@klarasm
Copy link
Collaborator Author

klarasm commented Dec 17, 2025

Hmm. One issue with the approach in this PR is that even if we could produce output immediately we will still try to get input first. But this we can probably put as future work.

I think I address this one in the latest commit. I'm not sure about the formatting on the initial delay tokens on global output, though.

@klarasm klarasm force-pushed the delayGlobalInputOutput branch 2 times, most recently from 16015d0 to 262153c Compare December 18, 2025 12:44
Erroring out prevents having delay edges on global IO. This shouldn't
affect the generated schedule.
Currently, `foldActorSignals` is fed with the unmodified signals, which
means it doesn't account for the aliases of delay edges. Change it to
use `translatedInputSignals` and `translatedOutputSignals` which do take
those into account. This means that IO on delay edges will be taken care
of by the regular actor IO.
We want the token rate the first non-delay actor needs so we can get the
appropriate number of tokens. Since we will have different names for the
buffers, make an alias list in similar way as we do for internal delays.
@klarasm klarasm force-pushed the delayGlobalInputOutput branch from 262153c to 1d4ff63 Compare December 18, 2025 13:16
@klarasm klarasm merged commit b77c184 into sthaeron:dev Dec 18, 2025
1 check passed
@klarasm klarasm deleted the delayGlobalInputOutput branch December 18, 2025 15:34
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.

Allow for delays connected to global input/output in scheduler

4 participants