---
name: masm-padding
description: Enforce stack padding conventions for Miden Assembly (.masm) procedures based on invocation type (call vs exec). Use when editing, reviewing, or creating .masm procedures, especially those with Invocation annotations.
---

# MASM Padding Conventions

## Overview

Padding requirements differ based on procedure invocation type:

| Invocation | Padding Required | Input/Output Elements |
|------------|------------------|----------------------|
| `call`     | Explicit padding in comments | Exactly 16 |
| `exec`     | No explicit padding | No requirement |

## Stack Depth Floor: 16

Miden VM enforces a minimum operand-stack depth of 16 elements (`MIN_STACK_DEPTH = 16` in the VM core). When an operation would naively shrink the stack below 16, the VM auto-fills the missing positions with zeros via the overflow-table mechanism. The actual depth stays exactly 16; only the visible content shrinks.

This invariant applies at the entry boundary of:

- `call` procedures,
- note scripts and transaction scripts (entered via `dyncall` at depth 16).

It does NOT apply to mid-chain `exec` procedures, which share the caller's stack and can drop the visible count below 16 by consuming caller elements (see Danger Zone).

### Tracking the floor in inline comments

When the naive math would put the stack below 16, the `# =>` tracker must reflect the actual auto-padded depth, not the naive count.

```masm
# entry at depth 16: [VALUE, pad(12)]
dropw
# => [pad(16)]   # correct: VALUE replaced with zeros, depth still 16
```

Not:

```masm
# => [pad(12)]   # wrong: depth is still 16, only the visible content shrank
```

This shows up most often at the start of note scripts that don't use their input arguments:

```masm
@note_script
pub proc main(args: NoteArgs)
    dropw
    # => [pad(16)]
    ...
end
```

## Call Procedures

Procedures invoked with `call` must have explicit padding in:
1. **Doc comments** (`#!`) for Inputs/Outputs
2. **Inline comments** (`#`) showing stack state

### Doc Comment Format

Use `pad(N)` notation where N + other elements = 16:

```masm
#! Inputs:  [ASSET, pad(12)]
#! Outputs: [pad(16)]
#!
#! Invocation: call
pub proc receive_asset
```

### Inline Comment Format

Track padding through the procedure:

```masm
exec.native_account::set_item
# => [OLD_VALUE, pad(12)]

dropw
# => [pad(16)] auto-padded to 16 elements    
```

## Exec Procedures

Procedures invoked with `exec` should NOT have explicit padding:

```masm
#! Inputs:  [PUB_KEY]
#! Outputs: []
#!
#! Invocation: exec
pub proc authenticate_transaction
```

### Why No Padding for Exec

`exec` procedures share the caller's stack directly. Explicit padding would be misleading because:
- The actual stack may have additional elements from the caller
- The procedure may consume caller's stack elements

### Danger Zone

If an `exec` procedure's stack falls below the specified stack elements, it will consume stack items from its caller, potentially leading to unexpected behavior. This is a bug and should be fixed by ensuring the procedure maintains sufficient stack depth and avoiding dropping more stack elements than available.

### Example of Dangerous Behavior

```masm
# => [num_approvers, threshold]
dropw # dropw drops 4 elements, which will result in "negative" stack consumption (consuming 2 elements from the caller's stack)
```


## Intermediate States

Inside a procedure, the stack may temporarily exceed 16 elements:

```masm
# => [num_approvers, threshold, MULTISIG_CONFIG, pad(12)]
#     ^--- 18 elements total, must be reduced before return
```

These extra elements must be explicitly dropped before the procedure returns (directly or via called procedures).

## Debugging Stack Depth

Use the event-based procedures in `miden::core::debug` to inspect VM state. These are ordinary procedure calls: they emit print events whenever invoked, affect the program being executed, and consume cycles (`print_stack` costs 3 cycles). Remove them from production programs.

```masm
use miden::core::debug

begin
    exec.debug::print_stack
    sdepth push.16 eq assert.err="depth must be 16 here"
end
```

## Validation Checklist

For all invocation types:
- [ ] Inline `# =>` trackers reflect the post-auto-pad depth (never below 16) at boundaries that enforce the floor (`call`, note scripts, tx scripts)
- [ ] No `miden::core::debug` procedure call is left in production MASM

For `call` procedures:
- [ ] Inputs doc comment shows exactly 16 elements with `pad(N)`
- [ ] Outputs doc comment shows exactly 16 elements with `pad(N)`
- [ ] Inline comments use `# =>` format with `pad(N)` notation
- [ ] All intermediate states track the full stack including padding

For `exec` procedures:
- [ ] No `pad(N)` in Inputs/Outputs doc comments
- [ ] No explicit padding in inline stack state comments
- [ ] Verify stack never drops below safe depth
