<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl" -->

---
title: Learning Assembly: function calls | daily.dev
description: This walkthrough explains how function calls work in ARM64 assembly on Apple silicon, built with clang. It starts with a minimal `_main` that exits with code...
canonical: https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Learning Assembly: function calls | daily.dev
og:description: This walkthrough explains how function calls work in ARM64 assembly on Apple silicon, built with clang. It starts with a minimal `_main` that exits with code...
og:url: https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl
og:image: https://api.daily.dev/og/posts/FYIKCxOxl.png
og:image:alt: Learning Assembly: function calls
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Learning Assembly: function calls

**[Developer 2.0](https://daily.dev/sources/developer20)** · 5 min read · 0 upvotes · 0 comments

## Summary

This walkthrough explains how function calls work in ARM64 assembly on Apple silicon, built with clang. It starts with a minimal `_main` that exits with code 0, then shows `_main` calling `_foo` to compute 42 from 41 plus 1. The example demonstrates saving `x29` (frame pointer) and `x30` (link register) with `stp` before a call and restoring them with `ldp` afterward, since `bl` overwrites `x30`. It covers the ARM64 16-byte stack alignment requirement and shows passing two arguments via `w0` and `w1`. It also suggests using `clang -S -O1` to inspect the generated assembly for a tiny C snippet when unsure what assembly to emit.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://developer20.com/learning-assembly-function-calls>

## Questions this post answers

### Why does bl _foo cause an infinite loop if I don't save x30 in ARM64 assembly?

The bl instruction overwrites x30 with the return address right after itself, so if the caller never saved the previous x30 value, the callee's ret jumps back into the caller, whose own ret then jumps to the same x30 again, creating an infinite loop between the two ret instructions.

_Debugging ARM64 stack corruption gets easier with worked examples like this one on daily.dev._

### Why does the stack pointer need to be 16-byte aligned on ARM64 even when saving only one 8-byte register?

ARM64 hardware requires sp to always be 16-byte aligned whenever it accesses memory, so even saving just the 8-byte link register x30 requires moving sp by 16 bytes rather than 8, as in stp x29, x30, [sp, #-16]!.

_Developers writing low-level ARM64 code can keep alignment rules like this handy on daily.dev._

### How do you pass multiple integer arguments to a function in ARM64 assembly?

ARM64 passes the first eight integer arguments in registers w0 through w7, or x0 through x7 for 64-bit values. For a two-argument function like adding two numbers, the caller places arguments in w0 and w1 before calling, and the callee returns its result in w0.

_Anyone building a compiler backend can track ARM64 calling conventions through daily.dev._

## Similar posts on daily.dev

- [Lets explore the stack](https://daily.dev/posts/lets-explore-the-stack-byttrvvkv) · Meeting C\+\+ · 2 upvotes · 0 comments
- [Calling a function in C without naming it](https://daily.dev/posts/calling-a-function-in-c-without-naming-it-kxojpx3bs) · Lobsters · 4 upvotes · 1 comments
- [A Basic Just-In-Time Compiler](https://daily.dev/posts/a-basic-just-in-time-compiler-mrdvhvck5) · Hacker News · 1 upvotes · 0 comments
- [Notes on the WASM Basic C ABI](https://daily.dev/posts/notes-on-the-wasm-basic-c-abi-asdzbd8hm) · Eli Bendersky · 1 upvotes · 0 comments

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#hardware](https://daily.dev/tags/hardware), [#assembly](https://daily.dev/tags/assembly)

[View this post on daily.dev](https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Learning Assembly: function calls","url":"https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl"},"datePublished":"2026-10-26T18:59:06.728Z","dateModified":"2026-10-26T19:06:04.072Z","description":"This walkthrough explains how function calls work in ARM64 assembly on Apple silicon, built with clang. It starts with a minimal `_main` that exits with code...","image":"https://media.daily.dev/image/upload/s--1KxV4ohY--/f_auto/v1722860400/public/Placeholder%2007","thumbnailUrl":"https://media.daily.dev/image/upload/s--1KxV4ohY--/f_auto/v1722860400/public/Placeholder%2007","isAccessibleForFree":true,"articleSection":"Developer 2.0","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Developer 2.0","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/4ce9fa00296c4debace3f47acd3e3d16","url":"https://daily.dev/sources/developer20"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"architecture,hardware,assembly","timeRequired":"PT5M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Developer 2.0","item":"https://daily.dev/sources/developer20"},{"@type":"ListItem","position":3,"name":"Learning Assembly: function calls"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/learning-assembly-function-calls-fyikcxoxl#faq","mainEntity":[{"@type":"Question","name":"Why does bl _foo cause an infinite loop if I don't save x30 in ARM64 assembly?","acceptedAnswer":{"@type":"Answer","text":"The bl instruction overwrites x30 with the return address right after itself, so if the caller never saved the previous x30 value, the callee's ret jumps back into the caller, whose own ret then jumps to the same x30 again, creating an infinite loop between the two ret instructions. Debugging ARM64 stack corruption gets easier with worked examples like this one on daily.dev."}},{"@type":"Question","name":"Why does the stack pointer need to be 16-byte aligned on ARM64 even when saving only one 8-byte register?","acceptedAnswer":{"@type":"Answer","text":"ARM64 hardware requires sp to always be 16-byte aligned whenever it accesses memory, so even saving just the 8-byte link register x30 requires moving sp by 16 bytes rather than 8, as in stp x29, x30, [sp, #-16]!. Developers writing low-level ARM64 code can keep alignment rules like this handy on daily.dev."}},{"@type":"Question","name":"How do you pass multiple integer arguments to a function in ARM64 assembly?","acceptedAnswer":{"@type":"Answer","text":"ARM64 passes the first eight integer arguments in registers w0 through w7, or x0 through x7 for 64-bit values. For a two-argument function like adding two numbers, the caller places arguments in w0 and w1 before calling, and the callee returns its result in w0. Anyone building a compiler backend can track ARM64 calling conventions through daily.dev."}}]}
```

