# A Mental Model

### Calling Conventions

A calling convention defines how functions communicate at the machine level.

When one function calls another, many low-level details must be agreed upon:

- where arguments are stored
- where return values go
- which registers are preserved
- how the stack is used
- how the function returns

The calling convention defines these rules.

Without calling conventions, separately compiled code could not safely call each other.

Calling conventions are extremely important in:

- operating systems
- C interoperability
- assembly programming
- embedded systems
- dynamic libraries
- foreign function interfaces

Most beginners do not think about them initially because compilers handle the details automatically. But systems programming requires understanding them.

## A Mental Model

Suppose function A calls function B.

Both sides must agree on rules like:

```text
where is parameter 1 stored?
where is parameter 2 stored?
where is the return value placed?
who cleans up the stack?
```

The calling convention answers these questions.

## Default Zig Calling Convention

Normally, Zig chooses the default calling convention automatically.

Example:

```zig
fn add(a: i32, b: i32) i32 {
    return a + b;
}
```

Most of the time, this is what you want.

The compiler selects the best convention for the current target platform.

## Explicit Calling Conventions

You can specify a calling convention explicitly.

Example:

```zig
fn add(
    a: i32,
    b: i32,
) callconv(.C) i32 {
    return a + b;
}
```

This uses the C calling convention.

The syntax:

```zig
callconv(.C)
```

means:

```text
use C ABI rules
```

ABI means:

```text
Application Binary Interface
```

An ABI defines low-level compatibility between compiled programs.

## Why the C Calling Convention Matters

C is the universal systems language.

Many libraries expose C APIs.

If Zig wants to call C safely, both sides must agree on:

- stack layout
- parameter passing
- return conventions

Example:

```zig
extern fn printf(
    format: [*:0]const u8,
    ...
) c_int;
```

This uses the C calling convention automatically because it is an external C function.

Without matching conventions, calls would corrupt memory or crash.

## Example: Calling C Functions

```zig
const std = @import("std");

extern fn puts(
    str: [*:0]const u8,
) c_int;

pub fn main() void {
    _ = puts("Hello from C");
}
```

Here Zig calls the C standard library.

The ABI compatibility is critical.

## Machine-Level Parameter Passing

Modern CPUs have registers.

Calling conventions define which registers store arguments.

Example conceptually:

```text
argument 1 -> register A
argument 2 -> register B
return value -> register C
```

Different platforms use different rules.

Example:

| Platform | Common ABI |
|---|---|
| Linux x86_64 | System V ABI |
| Windows x86_64 | Microsoft x64 ABI |
| ARM64 | AArch64 ABI |

Zig abstracts this complexity for you.

## Stack Cleanup

Some calling conventions specify:

```text
caller cleans stack
```

Others specify:

```text
callee cleans stack
```

Historically this mattered heavily in 32-bit systems.

Modern 64-bit ABIs are more standardized.

## Variadic Functions

Some C functions accept variable numbers of arguments.

Example:

```c
printf("%d %d", 10, 20);
```

Zig supports this with C calling conventions.

Example:

```zig
extern fn printf(
    format: [*:0]const u8,
    ...
) c_int;
```

The `...` means variadic arguments.

Variadic functions require special ABI handling.

## Zig Calling Convention Enum

Zig exposes calling conventions through enum values.

Examples include:

| Convention | Purpose |
|---|---|
| `.C` | C ABI |
| `.Inline` | inline calling behavior |
| `.Naked` | no generated prologue/epilogue |
| `.Async` | async execution support |

Platform-specific conventions may also exist.

## Naked Functions

A naked function disables normal function setup code.

Example conceptually:

```zig
fn handler() callconv(.Naked) void {

}
```

Normally, compilers generate setup instructions automatically:

- stack adjustment
- register saving
- frame creation

Naked functions skip this.

This is extremely low-level and dangerous.

They are mainly used for:

- interrupt handlers
- bootloaders
- kernels
- assembly integration

## Inline Calling Convention

Zig internally supports inline calling behavior.

Conceptually:

```zig
callconv(.Inline)
```

This relates to compile-time inlining behavior.

Normally you use the `inline` keyword instead.

## Async Calling Convention

Async functions may use specialized conventions internally.

Example conceptually:

```zig
callconv(.Async)
```

Async execution requires special stack/state handling.

You will study async systems later.

## Exported Functions

When Zig exposes functions to external programs, ABI compatibility matters.

Example:

```zig
export fn add(
    a: i32,
    b: i32,
) i32 {
    return a + b;
}
```

This creates a symbol visible to external code.

Usually exported APIs use:

```zig
callconv(.C)
```

to ensure compatibility.

## Function Pointer Compatibility

Function pointers must match calling conventions too.

Example:

```zig
const Callback =
    *const fn(i32) callconv(.C) void;
```

A mismatched convention can corrupt execution.

The compiler checks compatibility carefully.

## Why Calling Conventions Exist

Different CPUs and operating systems evolved differently.

Calling conventions standardized communication between separately compiled code.

Without them:

```text
library A could not safely call library B
```

They are foundational to binary compatibility.

## Assembly Interaction

Calling conventions are critical when working with assembly.

Example conceptually:

```text
assembly code must know:
- where arguments are
- where return values go
- which registers must survive
```

Otherwise execution breaks immediately.

## Debuggers and Stack Traces

Calling conventions also affect:

- debuggers
- profilers
- stack traces
- exception systems

The debugger needs to understand stack layout rules.

## Why Beginners Rarely Notice Them

Most code uses default conventions automatically.

Example:

```zig
fn add(a: i32, b: i32) i32
```

No explicit convention needed.

The compiler handles everything.

But systems programmers eventually must understand what happens underneath.

## A Practical Example

```zig
const std = @import("std");

fn zigAdd(
    a: i32,
    b: i32,
) i32 {
    return a + b;
}

fn cAdd(
    a: i32,
    b: i32,
) callconv(.C) i32 {
    return a + b;
}

pub fn main() void {
    const x = zigAdd(10, 20);
    const y = cAdd(30, 40);

    std.debug.print(
        "{} {}\n",
        .{ x, y },
    );
}
```

Output:

```text
30 70
```

Both functions behave similarly at the source-code level.

The difference is the machine-level ABI contract.

## Real-World Importance

Calling conventions matter heavily in:

| Area | Why |
|---|---|
| Operating systems | interrupt and syscall handling |
| C interoperability | ABI compatibility |
| Game engines | plugin APIs |
| Embedded systems | hardware interfaces |
| Dynamic libraries | binary linking |
| Compilers | code generation |
| Virtual machines | execution models |

This is core systems-programming knowledge.

## Mental Model

A calling convention is:

```text
a machine-level agreement for how functions communicate
```

It defines rules such as:

- where parameters go
- where results go
- how the stack behaves
- which registers are preserved

Most of the time, Zig handles these details automatically.

But when interacting with:

- C
- assembly
- operating systems
- low-level runtimes

understanding calling conventions becomes essential.

