Subroutines in Low Level Code - Computerphile

ComputerphileComputerphile
Education3 min read33 min video
May 6, 2025|70,306 views|2,182|120
Save to Pod

Key Moments

TL;DR

Subroutines allow code reuse in low-level programming via CALL/RETURN, using stacks to manage nested calls and preserve state.

Key Insights

1

Subroutines, or functions, enable code reuse by encapsulating reusable logic into callable blocks.

2

The CALL instruction transfers control to a subroutine, and RETURN brings execution back to the caller.

3

Stacks, managed by a stack pointer, are crucial for handling nested subroutine calls by storing return addresses.

4

Calling conventions define how arguments are passed and how registers are managed between caller and callee.

5

Recursive functions, which call themselves, rely heavily on stacks to manage nested calls and local state.

6

Stack overflows occur when the stack runs out of space, potentially leading to security vulnerabilities.

7

Compiler Explorer demonstrates how high-level code translates into low-level assembly, showing subroutine usage.

THE NEED FOR REUSABLE CODE

Modern programming relies on breaking down complex problems into smaller, manageable pieces that can be reused. Manually re-typing the same sequences of instructions for common tasks, like calculating a vector's magnitude, is inefficient and error-prone in low-level programming. Subroutines provide a mechanism to encapsulate these frequently used code snippets, allowing them to be invoked from different parts of a program without redundant code duplication.

CALL AND RETURN MECHANISMS

The fundamental instructions for employing subroutines are CALL and RETURN. A CALL instruction transfers the program's execution flow to the starting address of a subroutine. Crucially, before jumping, the CPU must record where to return after the subroutine completes. The RETURN instruction then directs the program back to the instruction immediately following the CALL, enabling sequential execution of the main program.

MANAGING NESTED CALLS WITH STACKS

When subroutines call other subroutines (nesting), the system needs a way to track multiple return points. This is where the stack, managed by a stack pointer (SP), becomes essential. A stack operates on a Last-In, First-Out (LIFO) principle. When a CALL occurs, the return address is 'pushed' onto the stack. Upon RETURN, the most recent return address is 'popped' from the stack to resume execution, ensuring correct control flow even with deep nesting.

CALLING CONVENTIONS: A CONTRACT FOR INTERACTION

To ensure different parts of a program, or code written by different developers, can work together seamlessly, calling conventions are established. These are agreements on how to pass arguments to subroutines (e.g., in specific registers like R0, R1) and how the subroutine should return values (e.g., in R0). They also define which registers a subroutine must preserve (e.g., R4-R15) to avoid corrupting the caller's data.

RECURSION AND STACK OVERFLOWS

Subroutines that call themselves, known as recursive functions (like the Fibonacci example), heavily utilize the stack. Each recursive call pushes a new return address and potentially preserves state. However, if not carefully managed, the stack can grow too large, leading to a 'stack overflow' – an error where the stack runs out of allocated memory. This can cause program crashes and, when data from external sources influences stack contents, can lead to security vulnerabilities like buffer overflows.

HARDWARE IMPLEMENTATION AND TOOLS

The stack pointer is a hardware register within the CPU, and the stack itself resides in memory. Efficient access often places the stack in the CPU's cache. Tools like Compiler Explorer visualize how high-level code (like C) translates into assembly language, demonstrating that the principles of subroutines, stacks, and register usage discussed are fundamental and evident even in modern compiled programs, albeit often abstracted away from the programmer.

Subroutine Programming Best Practices

Practical takeaways from this episode

Do This

Break down large programs into smaller, reusable code snippets (subroutines/functions).
Use the 'CALL' instruction to transfer control to a subroutine and 'RETURN' to come back.
Use a stack to store return addresses and temporary data when subroutines call other subroutines (nesting).
Follow a calling convention to ensure consistent argument passing and register usage between functions.
Preserve 'callee-saved' registers (e.g., R4-R15 in a typical convention) when modifying them within a subroutine.
Ensure every 'push' to the stack has a corresponding 'pop' in the correct order.
Store crucial information like return addresses on the stack before making nested calls.

Avoid This

Do not write monolithic programs with no modularity.
Avoid having subroutines that call other subroutines without a mechanism (like a stack) to track return points.
Do not corrupt preserved registers (e.g., R4-R15) without saving and restoring them.
Do not mismatch pushes and pops on the stack, as this can lead to incorrect execution flow or security issues.
Do not assume calling conventions are hardwired into the CPU; they are often operating system or architecture specific.
Be wary of writing data past the end of the stack buffer, which can lead to stack overflows and security vulnerabilities.

Common Questions

A subroutine, also known as a function or procedure, is a block of code designed to perform a specific task that can be called and reused from different parts of a larger program. This helps in organizing code and avoiding repetition.

Topics

Mentioned in this video

conceptFibonacci sequence

A sequence where each number is the sum of the two preceding ones, often used as an example for recursive programming.

conceptCall

An instruction that allows one part of a program to execute another piece of code (a subroutine) and then return.

conceptReturn

An instruction that signals the end of a subroutine and directs the program to resume execution after the 'call' instruction.

conceptSP (stack pointer)

A special register used to keep track of the current position in a data structure called a stack.

conceptStack

A data structure where elements are added (pushed) and removed (popped) in a last-in, first-out (LIFO) manner, used to manage function calls and return addresses.

conceptR0

A general-purpose register, specified in the calling convention to hold the first argument and the return value.

conceptR4 through R15

Registers designated as 'callee-saved' or 'preserved' registers in a calling convention, meaning a function must not alter their values or must restore them before returning.

conceptMagnitude of a vector

A hypothetical code snippet the speaker wants to write and reuse, calculated as the square root of x^2 + y^2.

conceptPush

An operation to add an item to the top of the stack.

conceptARM architecture

A common processor architecture used in various devices, for which the Fibonacci program was compiled.

conceptPop

An operation to remove an item from the top of the stack.

conceptA register

A specific register mentioned as part of the temporary working space.

softwareCompiler Explorer

A website that allows users to write code in various languages and see the resulting assembly code, used here to demonstrate C code compilation for the ARM architecture.

conceptB register

A specific register mentioned as part of the temporary working space.

conceptCalling Convention

A set of rules or guidelines that define how functions or subprograms should interact, including how arguments are passed and how registers are used and preserved.

conceptx^2 + y^2

The mathematical expression that forms part of the magnitude calculation for a vector.

conceptSquare root

A mathematical operation required to calculate the magnitude of a vector.

conceptRegisters

Temporary working storage space within the CPU, like small local post-it notes.

softwareStack Overflow
conceptC (programming language)
softwareFibonacci sequence program

More from Computerphile

View all 82 summaries

Found this useful? Build your knowledge library

Get AI-powered summaries of any YouTube video, podcast, or article in seconds. Save them to your personal pods and access them anytime.

Try Summify free