<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr" -->

---
title: How PHP executes bytecode | daily.dev
description: A deep dive into the Zend Engine's bytecode execution explains how PHP compiles source code into opcodes and how the executor dispatches them. The Zend Engine...
canonical: https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How PHP executes bytecode | daily.dev
og:description: A deep dive into the Zend Engine's bytecode execution explains how PHP compiles source code into opcodes and how the executor dispatches them. The Zend Engine...
og:url: https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr
og:image: https://api.daily.dev/og/posts/nMrPDR8hr.png
og:image:alt: How PHP executes bytecode
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.

# How PHP executes bytecode

**[PHPUnit Expert](https://daily.dev/sources/phpunit-expert)** · 24 min read · 19 upvotes · 1 comments

## Summary

A deep dive into the Zend Engine's bytecode execution explains how PHP compiles source code into opcodes and how the executor dispatches them. The Zend Engine ships five dispatch models (CALL, SWITCH, GOTO, HYBRID, TAILCALL), of which only CALL, HYBRID, and TAILCALL are actually used in production builds. The piece explains how each model interacts with CPU branch prediction, calling conventions, and register allocation, and traces how PHP 8.5 introduced TAILCALL to give Clang-built binaries the same performance as GCC-built HYBRID binaries. It also shows how the executor itself is generated from templates by a PHP script rather than hand-written.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://phpunit.expert/articles/how-php-executes-bytecode.html>

## Questions this post answers

### What is the TAILCALL dispatch model added in PHP 8.5 and why does it exist?

TAILCALL is a PHP executor dispatch mode introduced in PHP 8.5 that uses guaranteed tail calls (via the musttail attribute) combined with the preserve_none calling convention so handler functions jump directly to the next handler without growing the stack. It exists because Clang supports computed gotos but not global register variables, so Clang-built PHP binaries previously fell back to the slower CALL model; before TAILCALL, Clang builds were 2.8% to 44% slower than GCC builds depending on the benchmark, and afterwards they are on par.

_Track PHP 8.5 runtime internals like this on daily.dev before deciding which compiler toolchain to build against._

### Which dispatch model does a standard Linux PHP binary use versus a macOS or FreeBSD one?

Linux distributions typically build PHP with GCC, which selects the HYBRID dispatch model, combining separate handler functions with computed goto and global register variables pinning execute_data and opline into CPU registers %r14 and %r15 on x86-64. macOS and FreeBSD use Clang as the default toolchain, which since PHP 8.5 selects TAILCALL instead, reaching comparable performance through guaranteed tail calls and the preserve_none calling convention rather than GCC-specific extensions.

_Compare PHP performance benchmarks across toolchains on daily.dev when your build's compiler choice actually matters._

### How can I check which Zend VM dispatch model my PHP build is using?

Since PHP 8.5, running `php -r 'echo ZEND_VM_KIND, PHP_EOL;'` prints the active dispatch model, such as ZEND_VM_KIND_HYBRID. The choice is fixed at compile time by preprocessor checks for GCC global register variable support, or for musttail and preserve_none support with a matching x86-64 or ARM64 target; if none apply, the build falls back to the portable CALL model.

_Keep tabs on PHP internals like ZEND_VM_KIND on daily.dev before comparing benchmarks across installations._

## Community discussion

Top comments from developers on daily.dev.

**@agustinbarrientos** · 0 upvotes

> TAILCALL sounds like an optimization and a threat

## Similar posts on daily.dev

- [How PHP optimises bytecode](https://daily.dev/posts/how-php-optimises-bytecode-yx3ukx0i5) · PHPUnit Expert · 2 upvotes · 0 comments
- [How PHP generates machine code](https://daily.dev/posts/how-php-generates-machine-code-zwfynbekr) · PHPUnit Expert · 14 upvotes · 1 comments
- [Compile PHP to Native Binaries with TypePHP](https://daily.dev/posts/compile-php-to-native-binaries-with-typephp-erj9ukxpc) · Laravel News · 16 upvotes · 4 comments
- [How JavaScript learned to cheat — A Deep Dive into the JS Engine](https://daily.dev/posts/how-javascript-learned-to-cheat-a-deep-dive-into-the-js-engine-aqrk9b7iq) · Medium · 1 upvotes · 0 comments
- [V8 Engine: The Journey of JavaScript from Code to Execution](https://daily.dev/posts/v8-engine-the-journey-of-javascript-from-code-to-execution-junca4frw) · JavaScript in Plain English · 0 upvotes · 0 comments

---

Tags: [#php](https://daily.dev/tags/php)

[View this post on daily.dev](https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr)

```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":"How PHP executes bytecode","url":"https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr"},"datePublished":"2026-08-19T02:13:51.119Z","dateModified":"2026-09-13T18:56:19.282Z","description":"A deep dive into the Zend Engine's bytecode execution explains how PHP compiles source code into opcodes and how the executor dispatches them. The Zend Engine...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/68b750bf950884dd8d36b7c21c99e082?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/68b750bf950884dd8d36b7c21c99e082?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"PHPUnit Expert","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":"PHPUnit Expert","logo":"https://media.daily.dev/image/upload/s--FMZoysoG--/f_auto,q_auto/v1774964057/logos/phpunit-expert?_a=BAMAMiWQ0","url":"https://daily.dev/sources/phpunit-expert"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":19},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"php","timeRequired":"PT24M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"PHPUnit Expert","item":"https://daily.dev/sources/phpunit-expert"},{"@type":"ListItem","position":3,"name":"How PHP executes bytecode"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr","comment":[{"@type":"Comment","text":"TAILCALL sounds like an optimization and a threat","datePublished":"2026-08-20T23:51:49.535Z","url":"https://daily.dev/posts/nMrPDR8hr#c-3sK0EttDs","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-php-executes-bytecode-nmrpdr8hr#faq","mainEntity":[{"@type":"Question","name":"What is the TAILCALL dispatch model added in PHP 8.5 and why does it exist?","acceptedAnswer":{"@type":"Answer","text":"TAILCALL is a PHP executor dispatch mode introduced in PHP 8.5 that uses guaranteed tail calls (via the musttail attribute) combined with the preserve_none calling convention so handler functions jump directly to the next handler without growing the stack. It exists because Clang supports computed gotos but not global register variables, so Clang-built PHP binaries previously fell back to the slower CALL model; before TAILCALL, Clang builds were 2.8% to 44% slower than GCC builds depending on the benchmark, and afterwards they are on par. Track PHP 8.5 runtime internals like this on daily.dev before deciding which compiler toolchain to build against."}},{"@type":"Question","name":"Which dispatch model does a standard Linux PHP binary use versus a macOS or FreeBSD one?","acceptedAnswer":{"@type":"Answer","text":"Linux distributions typically build PHP with GCC, which selects the HYBRID dispatch model, combining separate handler functions with computed goto and global register variables pinning execute_data and opline into CPU registers %r14 and %r15 on x86-64. macOS and FreeBSD use Clang as the default toolchain, which since PHP 8.5 selects TAILCALL instead, reaching comparable performance through guaranteed tail calls and the preserve_none calling convention rather than GCC-specific extensions. Compare PHP performance benchmarks across toolchains on daily.dev when your build's compiler choice actually matters."}},{"@type":"Question","name":"How can I check which Zend VM dispatch model my PHP build is using?","acceptedAnswer":{"@type":"Answer","text":"Since PHP 8.5, running `php -r 'echo ZEND_VM_KIND, PHP_EOL;'` prints the active dispatch model, such as ZEND_VM_KIND_HYBRID. The choice is fixed at compile time by preprocessor checks for GCC global register variable support, or for musttail and preserve_none support with a matching x86-64 or ARM64 target; if none apply, the build falls back to the portable CALL model. Keep tabs on PHP internals like ZEND_VM_KIND on daily.dev before comparing benchmarks across installations."}}]}
```

