GOTO & CALL Jump Instructions
GOTO and CALL are two core control flow jump instructions in AmritaSense. They share the same alias-based addressing infrastructure, but serve very different scenarios: GOTO is an unconditional one-way jump, while CALL is a subroutine invocation with a return.
Common foundation Both rely on the same alias registry (
ALIAS), perform address resolution during_pre_check, and use the@markupdecorator to manage jump markers. Understanding these common mechanisms helps clarify the difference between them.
Non-self-compiled direct nodes
GOTO and CALL are not SelfCompileInstruction. They do not expand into NodeCompose at compile time; instead, they exist as single jump nodes in the workflow array.
That means:
- they are compiled as a single
JumpNodeorCallNodeelement - runtime behavior is expressed entirely through address resolution and pointer rewriting
- they do not create new Bubbles or nesting scopes
GOTO
GOTO is a factory wrapper for JumpNode. At runtime, it calls the interpreter’s jump_to method and performs a one-way, non-returning pointer rewrite.
Execution flow
- Address resolution (
_pre_check): resolve alias names through the alias table, or validate raw addresses. - Jump marker: call
pc.jump_to(addr), which is guarded by@markupand sets_jump_marked=True. - Pointer replacement: replace
_pointercompletely with the target address vector. - Interpreter response: the main loop sees
_jump_markedand skips normal_advance_pointer(), continuing from the jump target.
Key characteristics
- Does not manage the call stack:
GOTOdoes not push anything onto_ret_addr_stack. Once jumped, there is no return. - Can jump across any nesting level: because
far_to(addr)replaces the entire pointer vector,GOTOcan cross Bubble boundaries. - Supports both alias and raw address:
GOTO("target")uses a symbol, whileGOTO([1, 2, 3])uses an absolute address.
Typical use cases
- Error handling jumps: jump directly to a centralized error handler when a failure is detected.
- Branch merging: multiple conditional branches converge on the same
NOPpoint. - State machine transitions: jump to different next states based on runtime conditions.
CALL
CALL is a factory wrapper for CallNode. At runtime, it calls the interpreter’s call_sub method and performs a push → jump → execute → pop subroutine call.
Execution flow
- Address resolution (
_pre_check): resolve alias names and cache the absolute address. - Push return address:
call_subsaves the current pointer vector on_ret_addr_stack. - Pointer replacement: set the execution pointer to the subroutine’s entry address.
- Execute subroutine: the interpreter advances through the subroutine nodes.
- Pop return address: when the subroutine completes, the
finallyblock pops the saved pointer and restores it. - Continue execution: the interpreter continues from the node after the
CALLinstruction.
Key characteristics
- Manages call stack: pushes the return address and pops it later, supporting nested calls.
- Subroutine source: the target can be any addressable node sequence, not necessarily stored in
ARCHIVED_NODES. - No direct parameter passing:
CALLitself does not pass arguments. If the subroutine needs parameters, useDependsinside the subroutine node or callcall_subdirectly within a node.
Typical use cases
- Code reuse: encapsulate reusable node sequences as subroutines and call them multiple times.
- Modular decomposition: split complex workflows into independent subprocedures and keep the main flow simple.
- Interrupt handling: external systems can invoke predefined interrupt handlers via
call_sub(interrupt=True).
GOTO vs CALL: comparison
| Feature | GOTO | CALL |
|---|---|---|
| Saves return addr | No | Yes (_ret_addr_stack) |
| After execution | continues from target onward | returns to caller afterward |
| Call stack effect | none | push once, pop once |
| Use cases | one-way jump, branch merge, error handling | subroutine reuse, modular flow, interrupts |
| Underlying API | pc.jump_to(addr) | pc.call_sub(addr) |
Usage notes
- GOTO is not a substitute for loops:
GOTOdoes not provide return semantics. Jumping out of a loop withGOTOwill not correctly manage the loop state. UseBreakLoopfor loop exit andCALLfor reusable subroutines. - CALL targets must be addressable: the target node or entry node must have
address_able=True, which is required byALIAS. - GOTO and CALL share alias space: both look up aliases in
alias2vector_map. Avoid alias name conflicts. - CALL return depends on stack integrity: a
GOTOinside a subroutine sets_jump_marked, which can causecall_subto skip stack restoration. Understand thatGOTOinside a subroutine can override normal return behavior.
Example
from amrita_sense.instructions import GOTO, CALL, ALIAS, ARCHIVED_NODES
from amrita_sense.node import Node
@Node()
def error_handler():
print("Handling error")
@Node()
def reusable_step():
print("Executing reusable logic")
# GOTO: jump to error cleanup
workflow = (
start
>> do_something
>> GOTO("error_cleanup")
>> ALIAS(error_handler, "error_cleanup")
)
# CALL: reuse a subroutine
subprogram = ARCHIVED_NODES(
ALIAS(reusable_step, "reusable"),
)
main = (
init
>> CALL("reusable")
>> process
>> CALL("reusable")
>> end
>> subprogram
)