You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/guide/advanced/built-in_instruction_set/jump_clause.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -117,4 +117,4 @@ main = (
117
117
)
118
118
```
119
119
120
-
> **Manual stack management**: For scenarios requiring explicit control over the return address stack — such as early exit from nested scopes or custom stack unwinding — see the `RET_FAR` instruction documented in [Advanced Topic: Manual Stack Space Management](/guide/practice/manual-stack-management).
120
+
> **Manual stack management**: `PUSH_STACK` + `GOTO` + `RET_FAR` give you explicit control over the return address stack, and can be combined with `ARCHIVED_NODES` for subroutine-like patterns. See [Advanced Topic: Manual Stack Space Management](/guide/practice/manual-stack-management) for full details.
Copy file name to clipboardExpand all lines: docs/guide/practice/manual-stack-management.md
+70-26Lines changed: 70 additions & 26 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,56 +2,48 @@
2
2
3
3
The `CALL` instruction and `call_sub` method automatically manage the return address stack (`_ret_addr_stack`) for you: they push the current pointer before entering a subroutine, and the `finally` block pops it upon return. For most workflows, this is all you need.
4
4
5
-
However, AmritaSense also exposes the return address stack for **manual control** via `RET_FAR`. The pattern is:
5
+
However, AmritaSense also exposes the return address stack for **manual control** via `PUSH_STACK` and `RET_FAR`. The pattern is:
6
6
7
-
1.**Manual push** — A node explicitly pushes a `PointerVector` onto `_ret_addr_stack`
7
+
1.**PUSH_STACK** — Push an alias or address onto `_ret_addr_stack`
8
8
2.**GOTO** — Jump somewhere else in the workflow
9
9
3.**RET_FAR** — Pop the saved address and jump back
10
10
11
11
This lets you implement custom call/return schemes that don't follow the rigid `CALL`/`call_sub` discipline.
12
12
13
13
## The Return Address Stack
14
14
15
-
`_ret_addr_stack` is a `Stack[PointerVector]` on the `WorkflowInterpreter`. `CALL` pushes the current pointer onto it; the `finally` block of `call_sub` pops and restores it. But you can also push onto it directly from any node that has access to the interpreter via `POINTER_DEPENDS`.
15
+
`_ret_addr_stack` is a `Stack[PointerVector]` on the `WorkflowInterpreter`. `CALL` pushes the current pointer onto it; the `finally` block of `call_sub` pops and restores it. With `PUSH_STACK`, you can push any alias target onto the stack directly from the composition chain without writing a custom node.
16
16
17
17
```mermaid
18
18
sequenceDiagram
19
-
participant N as Node (manual push)
19
+
participant N as PUSH_STACK
20
20
participant S as _ret_addr_stack
21
21
participant W as Work Section
22
22
23
-
N->>S: push(PointerVector(return_addr))
23
+
N->>S: push(target_addr)
24
24
N->>W: GOTO("work")
25
25
W-->>W: execute...
26
26
W->>S: RET_FAR pops
27
27
W->>N: jump_far_ptr(base_addr)
28
28
```
29
29
30
-
## RET_FAR
30
+
## PUSH_STACK and RET_FAR
31
31
32
-
`RET_FAR` is a factory function that creates a `RetFarNode`. At runtime, it does exactly one thing:
32
+
-**`PUSH_STACK(alias_or_idata)`** — pushes the resolved address of a target alias (or a raw address list) onto `_ret_addr_stack`. It is a `PushStackNode`, placed directly in the `>>` chain.
33
+
-**`RET_FAR()`** — pops the top entry from `_ret_addr_stack` and calls `jump_far_ptr` to jump to the saved address. Also a regular `BaseNode` placed in the composition chain.
33
34
34
-
1. Pops the top entry from `_ret_addr_stack`
35
-
2. Calls `pc.jump_far_ptr(ptr.base_addr)` to jump to the saved address
35
+
Neither instruction should be `return`-ed from inside a `@Node()` function — place them directly in the `>>` chain.
36
36
37
-
`RetFarNode` is a regular `BaseNode`, placed directly in the workflow composition — **not returned from inside another node**.
38
-
39
-
## Example: Manual Push + GOTO + RET_FAR
37
+
## Example: PUSH_STACK + GOTO + RET_FAR
40
38
41
39
```python
42
-
from amrita_sense importALIAS, NOP, Node, PointerVector, WorkflowInterpreter
43
-
from amrita_sense.instructions importRET_FAR, GOTO
40
+
from amrita_sense importALIAS, NOP, Node, WorkflowInterpreter
41
+
from amrita_sense.instructions importGOTO, PUSH_STACK, RET_FAR
| Multi-level stack unwinding | Push multiple addresses, `RET_FAR` once per level |
91
83
| Non-linear control flow | Combine with `GOTO` for arbitrary jump patterns |
92
84
85
+
## Subroutine-like Pattern with ARCHIVED_NODES
86
+
87
+
`PUSH_STACK` + `GOTO` + `RET_FAR` can be combined with `ARCHIVED_NODES` to create self-contained "subroutines" that are skipped during normal execution but can be entered via `GOTO`:
88
+
89
+
```python
90
+
from amrita_sense importALIAS, ARCHIVED_NODES, NOP, Node, WorkflowInterpreter
91
+
from amrita_sense.instructions importGOTO, PUSH_STACK, RET_FAR
92
+
93
+
@Node()
94
+
asyncdefstart() -> None:
95
+
print("Start")
96
+
97
+
@Node()
98
+
asyncdefstep1() -> None:
99
+
print(" Step 1")
100
+
101
+
@Node()
102
+
asyncdefstep2() -> None:
103
+
print(" Step 2")
104
+
105
+
@Node()
106
+
asyncdefafter_return() -> None:
107
+
print("Back here (via RET_FAR)")
108
+
109
+
# Self-contained subroutine: normal flow skips it, GOTO enters it.
1.`PUSH_STACK("after")` saves the return destination
131
+
2.`GOTO("sub_entry")` enters the subroutine at `NOP` (the entry marker)
132
+
3.`step1 >> step2` execute sequentially
133
+
4.`RET_FAR()` pops the saved address and jumps back to `after_return`
134
+
135
+
The `NOP` aliased as `"sub_entry"` acts as the named entry point — `GOTO` targets the alias, and the node itself is a no-op.
136
+
93
137
## Caution
94
138
95
-
-**Stack integrity**: `RET_FAR` pops from `_ret_addr_stack` unconditionally. If the stack is empty, this raises an `IndexError`. Always push a corresponding address before reaching `RET_FAR`.
139
+
-**Stack integrity**: `RET_FAR` pops from `_ret_addr_stack` unconditionally. If the stack is empty, this raises an `IndexError`. Always push a corresponding address (via `CALL` or `PUSH_STACK`) before reaching `RET_FAR`.
96
140
-**Jump flag**: `RET_FAR` calls `jump_far_ptr` which is decorated with `@markup`, setting `_jump_marked = True`. The interpreter will NOT advance the pointer after `RET_FAR` — execution resumes at the jumped-to address.
97
-
-**Not a subprogram instruction**: `RET_FAR` is a standalone node in the composition chain. Do NOT `return RET_FAR()` from inside a `@Node()` function — that return value is ignored. Place `RET_FAR()` directly in the `>>` chain.
141
+
-**Not a subprogram instruction**: `PUSH_STACK` and `RET_FAR` are standalone nodes in the composition chain. Do NOT call them from inside a `@Node()` function — place them directly in the `>>` chain.
0 commit comments