<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://willmcpherson2.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://willmcpherson2.com/" rel="alternate" type="text/html" /><updated>2026-04-27T01:01:41+00:00</updated><id>https://willmcpherson2.com/feed.xml</id><title type="html">willmcpherson2</title><subtitle>Will McPherson&apos;s personal blog</subtitle><author><name>Will McPherson</name></author><entry><title type="html">The Populist Spectrum</title><link href="https://willmcpherson2.com/2026/02/04/the-populist-spectrum.html" rel="alternate" type="text/html" title="The Populist Spectrum" /><published>2026-02-04T00:00:00+00:00</published><updated>2026-02-04T00:00:00+00:00</updated><id>https://willmcpherson2.com/2026/02/04/the-populist-spectrum</id><content type="html" xml:base="https://willmcpherson2.com/2026/02/04/the-populist-spectrum.html"><![CDATA[<p><em>Populism</em> describes the relationship between a political movement and the political establishment.
A movement is populist if it derives power from the masses and opposes the status quo entirely, rather than just opposing a specific force.
This will either involve electing a political outsider, empowering a rogue elitist, or direct action.
It will usually allege some sort of conspiracy within the establishment.
Popularity within the movement is tied directly to the unpopularity of the establishment.</p>

<p>The <em>populist spectrum</em> describes political movements on a scale from populist to elitist.</p>

<h1 id="revolutionary">Revolutionary</h1>

<p>Political revolution is the extreme of populism.
It rejects the political process entirely and uses direct action instead.</p>

<p>The Velvet Revolution was a very popular revolution in Czechoslovakia, mainly consisting of protests and strikes.
The fact that it was largely nonviolent actually makes it <em>less</em> populist, because this constitutes some degree of compliance with the laws of the regime.</p>

<p>The Russian Revolution was similar, although perhaps not as popular, seeing as it devolved into civil war.
It was also much more violent.
You can’t really get more populist than murdering the royal family.</p>

<h1 id="populist">Populist</h1>

<p>Populism, after which the populist spectrum is named, is somewhere in the middle of the spectrum.
Populism is not revolutionary, and works within the system to elect an outsider.
These movements will criticise the status quo, accusing it of dysfunction or outright corruption.
They will often scapegoat a specific group of people, such as minorities, extremists or capitalists.
They will also make some attempt to dismantle institutions, depending on how committed they are.</p>

<p>Adolf Hitler was a very successful populist.
He initially pursued power through <a href="https://en.wikipedia.org/wiki/Beer_Hall_Putsch">revolutionary means</a> but eventually found success within the electoral system.
Even during his dictatorship, he held up a veneer of legality and democracy.
He scapegoated several groups, especially Jews, but really anyone who was not an ideal Nazi loyalist.</p>

<h1 id="popularist">Popularist</h1>

<p>Popularism is a strategy where establishment politicians focus on bipartisan legislation and popular policies.
This is essentially just a representative democracy working as intended.</p>

<p>Bill Clinton was a successful popularist.
He mostly pursued popular, centrist and sometimes even conservative policies using a <a href="https://en.wikipedia.org/wiki/Nixon_goes_to_China"><em>Nixon goes to China</em></a> style strategy called <a href="https://en.wikipedia.org/wiki/Triangulation_(politics)">triangulation</a>.
This contributed to his reelection and <a href="https://en.wikipedia.org/wiki/United_States_presidential_approval_rating#Historical_comparison">average approval of 55%</a>, making him the 5th most popular president.</p>

<h1 id="elitist">Elitist</h1>

<p>Elitism is rule by experts, professionals, aristocrats, monarchs, dictators, one-party states or philosopher kings.
While these governments are often authoritarian for historical reasons, that is not what defines this end of the spectrum.
What makes a government elitist is if decisions are made by leaders and not the public.</p>

<p>A good example of modern elitism is the chair of the Federal Reserve.
The chair is nominated by the president and confirmed by the senate, so they’re elected by other elitists and only have an indirect democratic mandate.
Furthermore, the chair is historically selected from elite institutions, and the position requires technical expertise, so there is little room for demagoguery.</p>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[Populism describes the relationship between a political movement and the political establishment. A movement is populist if it derives power from the masses and opposes the status quo entirely, rather than just opposing a specific force. This will either involve electing a political outsider, empowering a rogue elitist, or direct action. It will usually allege some sort of conspiracy within the establishment. Popularity within the movement is tied directly to the unpopularity of the establishment.]]></summary></entry><entry><title type="html">What should the international community learn from American isolationism?</title><link href="https://willmcpherson2.com/2025/12/23/american-isolationism.html" rel="alternate" type="text/html" title="What should the international community learn from American isolationism?" /><published>2025-12-23T00:00:00+00:00</published><updated>2025-12-23T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/12/23/american-isolationism</id><content type="html" xml:base="https://willmcpherson2.com/2025/12/23/american-isolationism.html"><![CDATA[<p>It has been said that America is experiencing an isolationist revival.
The Trump administration certainly has turned its back on the international community:</p>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/Tariffs_in_the_second_Trump_administration">Initiating a global trade war</a></li>
  <li><a href="https://en.wikipedia.org/wiki/United_States_and_the_Paris_Agreement">Withdrawing from the Paris Agreement</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Executive_Order_14155">Withdrawing from the WHO</a></li>
  <li><a href="https://www.bbc.com/news/44537372">Withdrawing from UNHRC</a></li>
  <li><a href="https://www.nytimes.com/2025/07/23/world/europe/unesco-trump-us-withdrawal.html">Withdrawing from UNESCO</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Intermediate-Range_Nuclear_Forces_Treaty#US_withdrawal_and_termination">Withdrawing from the INF Treaty</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Treaty_on_Open_Skies#American_withdrawal">Withdrawing from the Treaty on Open Skies</a></li>
  <li><a href="https://en.wikipedia.org/wiki/United_States_withdrawal_from_the_Joint_Comprehensive_Plan_of_Action">Withdrawing from the JCPOA</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Global_minimum_corporate_tax_rate#Possible_implementation_in_the_United_States">Withdrawing from the GMCT</a></li>
  <li><a href="https://en.wikipedia.org/wiki/United_States_Agency_for_International_Development#Budget_cuts_and_absorption_into_the_State_Department_(2025)">Cutting USAID funding</a></li>
  <li><a href="https://en.wikipedia.org/wiki/President%27s_Emergency_Plan_for_AIDS_Relief#PEPFAR_in_2nd_Trump_presidency">Cutting PEPFAR funding</a></li>
  <li><a href="https://apnews.com/article/trump-un-human-rights-palestinian-refugees-israel-05e1d57bbb41df38771d1ab69adb21a3">Cutting UNRWA funding</a></li>
  <li><a href="https://www.npr.org/sections/goats-and-soda/2025/07/31/nx-s1-5475219/what-will-rescission-do-to-foreign-aid-details-are-murky-heres-what-we-found-out">Cutting UNICEF funding</a></li>
  <li><a href="https://www.bbc.com/news/articles/c179p4wvz29o">Undermining the UN</a></li>
  <li><a href="https://en.wikipedia.org/wiki/National_Security_Strategy_(United_States)#2025_NSS">Undermining the EU</a></li>
</ul>

<p>However, the overall foreign policy of the administration is not simply “isolationist”.
If anything, they have followed a policy of interventionism, especially in the Western Hemisphere:</p>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/Gulf_of_Mexico#Name">Attempting to rename “Gulf of Mexico” to “Gulf of America”</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Movements_for_the_annexation_of_Canada_to_the_United_States#Proposals_to_annex_Canada_by_President_Donald_Trump">Proposing the annexation of Canada</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Proposed_United_States_acquisition_of_Greenland#Proposals_by_Donald_Trump">Proposing the annexation of Greenland</a></li>
  <li><a href="https://www.bbc.com/news/articles/c4gzn48jwz2o">Threatening military intervention in Panama</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Proposed_United_States_invasion_of_Venezuela">Threatening military intervention in Venezuela</a></li>
  <li><a href="https://www.npr.org/2025/10/24/g-s1-94970/us-sanctions-colombia-president-petro-drug-allegations">Sanctioning the president of Colombia</a></li>
  <li><a href="https://www.bbc.com/news/articles/c9qewln7912o">Pardoning the former president of Honduras</a></li>
  <li><a href="https://www.theguardian.com/world/2025/nov/30/honduras-vote-election-trump-threat-cut-aid-if-nasry-tito-asfura-loses">Threatening to cut foreign aid if a Honduran presidential candidate is not elected</a></li>
  <li><a href="https://en.wikipedia.org/wiki/2022%E2%80%932023_Brazilian_coup_plot">Using sanctions, tariffs and visa revocations to prevent the prosecution of the president of Brazil</a></li>
  <li><a href="https://www.cnbc.com/2025/10/09/us-buys-argentine-pesos-agrees-to-20-billion-swap-line-bessent-says.html">Opening a $20b swap line with Argentina</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Immigration_policy_of_the_second_Trump_administration#Use_of_El_Salvador">Imprisoning deportees in El Salvador</a></li>
  <li><a href="https://en.wikipedia.org/wiki/2025_United_States_military_strikes_on_alleged_drug_traffickers">Killing 105 alleged drug traffickers in the Caribbean and Pacific</a></li>
  <li><a href="https://www.nbcnews.com/politics/trump-administration/trump-orders-blockade-sanctioned-oil-tankers-venezuela-rcna249616">Seizing and blockading Venezuelan oil tankers</a></li>
</ul>

<p>I would describe this as a shift towards a <a href="https://www.dwarkesh.com/p/sarah-paine?open=false#%C2%A7continental-vs-maritime-powers">continental strategy</a>, rather than simply “isolationism” or “interventionism”.
This basically means America is becoming more like Russia: isolated from the international community while bullying its neighbours.</p>

<p>This has been difficult for people to process, especially because America found so much success as the foremost <a href="https://www.dwarkesh.com/p/sarah-paine?open=false#%C2%A7continental-vs-maritime-powers">maritime power</a>.
It often seems that Trump’s foreign policy is unpredictable, contradictory or heavy-handed.
However, this behaviour makes sense once you understand that Trump has a zero-sum worldview, admires continental powers, and prefers an aggressive negotiation style.</p>

<p>This zero-sum paranoia is what leads to the idea that trade deficits are betrayals, that alliances benefit small countries only at the expense of large countries, or that minor powers need to be controlled rather than uplifted.
So while this new foreign policy may seem like sabotage by a foreign asset, the more simple explanation is that it’s a strategic misstep caused by ideology.</p>

<p>Many in the international community, especially Europeans, feel betrayed by America.
At a time when we are most in need of international cooperation, the supposedly pro-Western president decides to take a critical view of the UN, the EU, NATO and Ukraine.
The coalition of the willing followed America into the Middle East, but when Russia invades Europe: <a href="https://www.ft.com/content/3e16c530-d54a-48b9-867a-a6ec6fa054d5">“It’s a war in another continent. We’ve got issues in our own hemisphere we need to be dealing with.”</a></p>

<p>Some internationalists are reacting pessimistically: that we can no longer trust the United States, and that we will need to become independent from this former ally.
I find this to be equally isolationist.
In the international order, we don’t cut ties.
Russia is still in the UN.
Hungary is still in the EU.
The point of internationalism is that we use democracy, diplomacy, free trade and soft power to make it easy to be a good guy and difficult to be a bad guy.
Of course it hurts when good guys turn bad, but you should be working with them, not ignoring the problem.
What if Russia’s wartime economy starts to collapse?
What if the pro-EU party wins the <a href="https://en.wikipedia.org/wiki/2026_Hungarian_parliamentary_election">2026 Hungarian election</a>?</p>

<p>Conversely, some are reacting naively: that this populism thing is just a fad, and that we just need to wait until 2028 to get an internationalist president.
This is similarly counterproductive.
Autocratisation is a global trend — not just an American one.</p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/autocratisation.jpg" alt="Number of countries experiencing autocratisation and democratisation since 1900" title="Number of countries experiencing autocratisation and democratisation since 1900" width="1000" />
    
        <div style="margin-top: 1em">
            
<p><a href="https://www.tandfonline.com/doi/full/10.1080/13510347.2021.1923006">Number of countries experiencing autocratisation and democratisation since 1900</a></p>

        </div>
    
</div>

<p>This trend will put international institutions to the test.
We will have to sanction.
We will have to spend on defence.
We will have to form new alliances.
Every parliament will have to struggle against populist movements.</p>

<p>Don’t give up.</p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/american-isolationism/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-12-23-american-isolationism.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[It has been said that America is experiencing an isolationist revival. The Trump administration certainly has turned its back on the international community:]]></summary></entry><entry><title type="html">Open Social Media</title><link href="https://willmcpherson2.com/2025/11/04/open-social-media.html" rel="alternate" type="text/html" title="Open Social Media" /><published>2025-11-04T00:00:00+00:00</published><updated>2025-11-04T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/11/04/open-social-media</id><content type="html" xml:base="https://willmcpherson2.com/2025/11/04/open-social-media.html"><![CDATA[<p>The information landscape has changed.
More people are online, for more hours per day, at increasingly younger ages.
Journalists, authors, professors, and politicians are no longer the gatekeepers of media.
All information is injected into the algorithm and the tech companies pull the levers.</p>

<p>The <a href="https://en.wikipedia.org/wiki/Facebook%E2%80%93Cambridge_Analytica_data_scandal">Cambridge Analytica scandal</a> in 2018 was the public’s introduction to the power accumulated in Silicon Valley.
By this time it was an open secret that <em>we are the product</em>.
But it wasn’t clear exactly how our information environment would change.</p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/robert-california.jpg" alt="Robert California" title="Robert California" width="400" />
    
        <div style="margin-top: 1em">
            
<p><em>Would you prefer an environmental metaphor or a feudalism metaphor?</em></p>

        </div>
    
</div>

<p>Silicon Valley is like the coal industry.
In order to turn a profit, they must manufacture engagement, which creates waste products.
An atmosphere of outrage increases the political temperature and causes extreme social events.</p>

<p>If you were addicted to Twitter on the 10th of September 2025, you were probably irradiated by the nuclear fallout of Charlie Kirk’s assassination.
<em>For You</em> pages were almost entirely filled with content related to the event.
The most inflammatory discourse floated to the top of the pile.
Many users were exposed to footage of the brutal killing through Twitter’s autoplay feature.
The site was flooded with speculation, misinformation, AI “enhanced” images, political extremism, doxxing and cancellations.
Anyone naive to the attention economy might have thought America was about to implode.
The victim of the killing was a culture warrior and the perpetrator was terminally online, and so the cycle continues.</p>

<p>Silicon Valley are like feudal lords.
They make money by simply owning all the servers.
They rent them out to the peasants who create all the information but own none of it.
The peasants could revolt, but they don’t, and it will take some catastrophic event like the Black Death for the lords to lose power.</p>

<p>I like the analogy of a feudal manor, because the internet truly is <a href="https://www.youtube.com/watch?v=oOyQpwPrf2Q">a place where we live now</a>.
On the internet we work, play and learn.
But this is rented space.
There is no freedom of speech on a server owned by someone else.
We don’t get to vote on the terms of service.
They don’t even tell us what algorithms they’re using.
We celebrated the death of the legacy media but we are now standing idly by while the new media is consolidated in the hands of a small group of unelected billionaires.</p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/big-tech.jpg" alt="Mark Zuckerberg, Jeff Bezos, Sundar Pichai and Elon Musk attending the second inauguration of Donald Trump" title="Mark Zuckerberg, Jeff Bezos, Sundar Pichai and Elon Musk attending the second inauguration of Donald Trump" width="800" />
    
</div>

<p>Will we unionise, by banding together under a browser extension?
Or will we leave these sites and form cooperatives on the <a href="https://en.wikipedia.org/wiki/AT_Protocol">AT Protocol</a>?
If we do nothing, the government will surely subsume this democratic institution before it begins.</p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/robert-california-2.jpg" alt="Robert California" title="Robert California" width="400" />
    
        <div style="margin-top: 1em">
            
<p><em>The fallacy is that it is up to the steamroller. It is up to the object whether it will be flattened or not.</em></p>

        </div>
    
</div>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/open-social-media/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-11-04-open-social-media.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[The information landscape has changed. More people are online, for more hours per day, at increasingly younger ages. Journalists, authors, professors, and politicians are no longer the gatekeepers of media. All information is injected into the algorithm and the tech companies pull the levers.]]></summary></entry><entry><title type="html">Wokeness in Retrospect</title><link href="https://willmcpherson2.com/2025/09/21/wokeness-in-retrospect.html" rel="alternate" type="text/html" title="Wokeness in Retrospect" /><published>2025-09-21T00:00:00+00:00</published><updated>2025-09-21T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/09/21/wokeness-in-retrospect</id><content type="html" xml:base="https://willmcpherson2.com/2025/09/21/wokeness-in-retrospect.html"><![CDATA[<ul id="markdown-toc">
  <li><a href="#is-it-actually-dying" id="markdown-toc-is-it-actually-dying">Is it actually dying?</a>    <ul>
      <li><a href="#offensive-language" id="markdown-toc-offensive-language">Offensive language</a></li>
      <li><a href="#cancel-culture" id="markdown-toc-cancel-culture">Cancel culture</a></li>
      <li><a href="#ideology" id="markdown-toc-ideology">Ideology</a></li>
      <li><a href="#establishment" id="markdown-toc-establishment">Establishment</a></li>
    </ul>
  </li>
  <li><a href="#why-is-it-dying" id="markdown-toc-why-is-it-dying">Why is it dying?</a></li>
  <li><a href="#what-is-next" id="markdown-toc-what-is-next">What is next?</a></li>
</ul>

<hr />

<p>In <a href="https://paulgraham.com/woke.html"><em>The Origins of Wokeness</em></a>, Paul Graham outlines the rise and fall of the second wave of political correctness.
He reports that the first wave took place throughout the 90s, and the second wave throughout the 2010s.
I wasn’t around for the first wave, but I was for the second.</p>

<p>I should first make it clear that <a href="https://en.wikipedia.org/wiki/Woke"><em>woke</em></a> is an AAVE word that is almost 100 years old, but has since been repurposed.
Regardless, “woke” is currently the designated term for this 2010s wave of political correctness.</p>

<p>Additionally, it seems “woke” is currently undergoing another semantic shift.
With terms like <a href="https://en.wikipedia.org/wiki/Dark_Woke"><em>dark woke</em></a>, we are seeing signs of a post-ironic reclamation of wokeness.</p>

<p>So in short: <em>woke</em> in AAVE originally meant “social consciousness”, then was adopted by white progressives, then became a pejorative for progressivism, and is now being reclaimed by the left (with a meaning that is yet to be fully determined).
So while the era of woke political correctness may be in decline, the <a href="https://books.google.com/ngrams/graph?content=woke">usage of the term</a> will likely continue.</p>

<h1 id="is-it-actually-dying">Is it actually dying?</h1>

<p>I have observed several examples of woke decline:</p>

<h2 id="offensive-language">Offensive language</h2>

<p>The central force of wokeness is <a href="https://en.wikipedia.org/wiki/Word_taboo">word taboo</a>.
This is the ever-growing list of prohibited language.
While <a href="https://www.youtube.com/watch?v=Knv1OSMW2rU">taboo</a> is an inherent feature of all languages, linguistic orthodoxy is one of the primary symptoms of political correctness.</p>

<p>But recently, we have seen a resurgence in derogatory terms like “gay” and “retard”, which have now become somewhat accepted on the left.
For example the highly influential <a href="https://en.wikipedia.org/wiki/Dirtbag_left">dirtbag left</a> rejected political correctness on an anti-liberal basis, permitting offensive humour and usage of these more benign slurs.</p>

<h2 id="cancel-culture">Cancel culture</h2>

<p>Another core mechanism of wokeness is mob policing.
Because political discourse has moved increasingly online, public shaming has become effortless and instant.</p>

<p>This too resulted in massive overextension and a subsequent backlash.
Most netizens are now sceptical of cancellations and have assumed a policy of “innocent until proven guilty”.</p>

<h2 id="ideology">Ideology</h2>

<p>There are also some ideological propositions that failed to gain traction:
<a href="https://en.wikipedia.org/wiki/Prejudice_plus_power">prejudice plus power</a>,
<a href="https://en.wikipedia.org/wiki/Cultural_appropriation">cultural appropriation</a>,
<a href="https://en.wikipedia.org/wiki/Neopronoun">neopronouns</a>,
<a href="https://en.wikipedia.org/wiki/Microaggression">microaggressions</a> and
<a href="https://en.wikipedia.org/wiki/Safe_space">safe spaces</a>, to name a few.
These new rules were unpopular because they were, ironically, <a href="https://youtu.be/4oXi-WeMxbY?si=mZBLj6JoCA5Odqa7&amp;t=31">oppressive</a>.</p>

<h2 id="establishment">Establishment</h2>

<p>Woke became the establishment.
Socially, and to some extent politically.
This kind of defeats the purpose.
Wokeness was supposed to fight power, but it became power.</p>

<h1 id="why-is-it-dying">Why is it dying?</h1>

<p>A simple explanation is that wokeness purity spiralled to death.
Extremists found new ways to virtue signal, and moderates were conditioned to accept new standards of virtue.
This process predictably ended in a market correction.</p>

<h1 id="what-is-next">What is next?</h1>

<p>The left appears to be reevaluating political correctness and free speech, especially in light of recent events such as
the <a href="https://www.techpolicy.press/online-safety-regulations-around-the-world-the-state-of-play-and-the-way-forward/">proliferation of online safety legislation</a>,
the <a href="https://en.wikipedia.org/wiki/Palestine_Action">crackdown on Palestine Action</a>,
the <a href="https://en.wikipedia.org/wiki/Disciplinary_actions_related_to_comments_on_the_assassination_of_Charlie_Kirk">cancellations related to the assassination of Charlie Kirk</a> and
the <a href="https://www.washingtonpost.com/world/2025/09/19/hungary-antifa-terrorist-organization-trump-orban/d575029e-952d-11f0-8336-4757e168ec2a_story.html">designation of antifa as a terrorist organisation</a>.</p>

<p>My guess is that the left will become more pro-speech, more irreverent and generally more pragmatic.
This will be in reaction to peak woke, but also as a defence against the rise of the far-right.
It’s an open secret that the left <a href="https://www.tiktok.com/@wtf_is.left/video/7551412336905456918">eats itself</a>
and would rather <a href="https://youtu.be/aPhrTOg1RUk?si=ogr03n5tcCdmNt2s&amp;t=5245">critique power</a> than actually have any.
Purity testing will be thrown out once external threats become sufficiently obvious.</p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/bernie-blm.jpg" alt="Black Lives Matter activists disrupting Bernie Sanders speech" title="Black Lives Matter activists disrupting Bernie Sanders speech" width="800" />
    
</div>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/wokeness-in-retrospect/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-09-21-wokeness-in-retrospect.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">The height of the American empire was Whitney Houston’s performance at the 1991 Super Bowl</title><link href="https://willmcpherson2.com/2025/08/18/whitney-houston.html" rel="alternate" type="text/html" title="The height of the American empire was Whitney Houston’s performance at the 1991 Super Bowl" /><published>2025-08-18T00:00:00+00:00</published><updated>2025-08-18T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/08/18/whitney-houston</id><content type="html" xml:base="https://willmcpherson2.com/2025/08/18/whitney-houston.html"><![CDATA[<p>It’s the 27th of January, 1991.</p>

<p>The Cold War is coming to an end.
The Berlin Wall has fallen.
With fascism and communism defeated, Fukuyama declares the end of history.
The announcer begins the introduction.</p>

<blockquote>
  <p>And now to honour America, especially the brave men and women serving our nation in the Persian Gulf, and throughout the world, please join in the singing of our national anthem.</p>
</blockquote>

<p>America is 10 days into the Gulf War.
They lead a 42-country coalition in a campaign to liberate Kuwait.</p>

<p>The United States enjoys the strongest military in the world, the largest economy, veto power in the UN, powerful positions in NATO and a dominant cultural export.</p>

<blockquote>
  <p>The anthem will be followed by a flyover of F-16 jets from the 56th Tactical Training Wing at MacDill air force base…</p>
</blockquote>

<p>Those same F-16s have just established air superiority over Iraq.</p>

<blockquote>
  <p>…and will be performed by The Florida Orchestra under the direction of Maestro Jahja Ling and sung by Grammy Award winner Whitney Houston.</p>
</blockquote>

<p>The performance is being broadcast to 750 million people around the world.
And for the first time, in Russia.
14% of the world will hear John Clayton’s jazz arrangement of The Star-Spangled Banner.</p>

<p>Whitney Houston delivers what is now considered the best rendition of the US national anthem of all time.</p>

<p>The F-16s roar over the stadium.
The coalition wins the war a month later.</p>

<p>But after the war, the presence of troops in Saudi Arabia would eventually become one of the motivations for the September 11 attacks.
And it was the son of the Gulf War president who would lead the same coalition into the war on terror.</p>

<p>With a growing wealth divide, military overextension and protracted culture wars, America turned to cronyism, isolationism and fascism.
It graduated from world police to global pariah.</p>

<p>Rest in peace.</p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/whitney-houston/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-08-18-whitney-houston.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[It’s the 27th of January, 1991.]]></summary></entry><entry><title type="html">Do not vote third-party</title><link href="https://willmcpherson2.com/2025/07/05/do-not-vote-third-party.html" rel="alternate" type="text/html" title="Do not vote third-party" /><published>2025-07-05T00:00:00+00:00</published><updated>2025-07-05T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/07/05/do-not-vote-third-party</id><content type="html" xml:base="https://willmcpherson2.com/2025/07/05/do-not-vote-third-party.html"><![CDATA[<table>
  <tbody>
    <tr>
      <td>ℹ️️️ This is a comment on US politics. I live in Australia.</td>
    </tr>
  </tbody>
</table>

<p>Many US voters are unsatisfied with the two-party system.
This post has nothing to do with specific voting preferences, but rather on rational voting within this system.</p>

<p>Most US elections are <em>winner takes all</em>.
Third-party supporters are therefore faced with a dilemma: support their party with an inconsequential vote, or vote for the least bad two-party candidate.</p>

<p>This is a basic <a href="https://en.wikipedia.org/wiki/Collective_action_problem">collective action problem</a>.
If all the supporters acted rationally, no votes would be wasted and the third-party would win (or at least the two-party system would start to crack).</p>

<p>In practice, voters don’t act monolithically, and so some supporters stick it out while others chicken out.
Therefore the strategy is to grow support until the party becomes viable.
Votes are wasted in the short-term, but have some potential to win a future election by persuading other supporters.</p>

<p>Is this strategy feasible?
Technically yes.
It would take a long time and the opportunity cost would be massive, but you could waste millions of votes on hundreds of elections and eventually break the two-party system.</p>

<p>Is it worth it?
This depends on the expected utility of each party.
If the third-party supporters expect the third-party to be massively favourable over the major parties, then there is no other option.
If they expect the third-party to only be marginally better than the least bad party, then wasting votes becomes more wasteful.</p>

<p>Is it sustainable?
Probably not.
Even when you finally elect a third-party, the rules of the game don’t change.
You’ve merely influenced the behaviour of the players.</p>

<p>But America isn’t a game theory puzzle.
There are some real-world variables that are missing from this equation.
Rules can be changed.</p>

<p>Remember when I said most US elections are winner takes all?
This is because they use a <em>first-past-the-post</em> (FPP) voting system.
In this system, whoever gets the most votes wins.
Your third-party vote is wasted because you’re forced to choose a single candidate when you really wanted to express your <em>preferences</em>.</p>

<p>But there are several US elections which use <em>ranked-choice voting</em> (RCV)!
In fact one of the most important US elections of this year, the <a href="https://en.wikipedia.org/wiki/2025_New_York_City_Democratic_mayoral_primary">2025 NYC Democratic mayoral primary</a>, used RCV.
In Australia, we call this <em>preferential voting</em>, and we use it for almost all of our elections.</p>

<p>RCV would allow American voters to support minor parties without wasting their vote.
In the 2016 US presidential election, <a href="https://en.wikipedia.org/wiki/2016_United_States_presidential_election#Electoral_results">over 7 million votes</a> were wasted in an election with a margin of 3 million votes.</p>

<p>RCV <em>changes the game</em> rather than the outcome of a few matches.
Voting for third parties in a two-party system is like bringing a baseball bat to a basketball game.</p>

<p>How does this change our strategy for US voters who are unsatisfied with the two-party system?
Well, now the goal is to introduce RCV instead of winning elections.
But even if you just wanted to win elections, a ranked-choice vote is the only realistic way of achieving this.
In fact, passing RCV legislation is probably more likely than electing a third-party.
Therefore your strategy should actually be to <strong>vote for the two-party candidate more likely to introduce RCV!</strong></p>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/us-house.svg" alt="US House of Representatives diagram" title="US House of Representatives diagram" width="400" />
    
        <div style="margin-top: 1em">
            
<p>US House of Representatives, with 0 third-party representation</p>

        </div>
    
</div>

<div align="center" style="margin-top: 4em; margin-bottom: 4em">
    <img src="/assets/images/australian-house.svg" alt="Australian House of Representatives diagram" title="Australian House of Representatives diagram" width="400" />
    
        <div style="margin-top: 1em">
            
<p>Australian House of Representatives, with 13 crossbenchers</p>

        </div>
    
</div>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/do-not-vote-third-party/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-07-05-do-not-vote-third-party.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[ℹ️️️ This is a comment on US politics. I live in Australia.]]></summary></entry><entry><title type="html">Do not make AI</title><link href="https://willmcpherson2.com/2025/06/30/do-not-make-ai.html" rel="alternate" type="text/html" title="Do not make AI" /><published>2025-06-30T00:00:00+00:00</published><updated>2025-06-30T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/06/30/do-not-make-ai</id><content type="html" xml:base="https://willmcpherson2.com/2025/06/30/do-not-make-ai.html"><![CDATA[<p>We are currently in an arms race to create an artificial superintelligence.</p>

<p>If we make an AI that is more intelligent than us, or has more agency than us, it will almost certainly eliminate its competition.
There is absolutely no reason to assume that its goals will align with ours.</p>

<p>AI alignment is not even close to being solved.
We don’t even know if it’s solvable.</p>

<p><strong>Pause AI development immediately.</strong></p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/do-not-make-ai/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-06-30-do-not-make-ai.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[We are currently in an arms race to create an artificial superintelligence.]]></summary></entry><entry><title type="html">Metasyntax</title><link href="https://willmcpherson2.com/2025/06/29/metasyntax.html" rel="alternate" type="text/html" title="Metasyntax" /><published>2025-06-29T00:00:00+00:00</published><updated>2025-06-29T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/06/29/metasyntax</id><content type="html" xml:base="https://willmcpherson2.com/2025/06/29/metasyntax.html"><![CDATA[<ul id="markdown-toc">
  <li><a href="#syntax" id="markdown-toc-syntax">Syntax</a></li>
  <li><a href="#metasyntax" id="markdown-toc-metasyntax">Metasyntax</a>    <ul>
      <li><a href="#s-expressions" id="markdown-toc-s-expressions">S-expressions</a></li>
      <li><a href="#indentation-trees" id="markdown-toc-indentation-trees">Indentation trees</a></li>
      <li><a href="#b-expressions" id="markdown-toc-b-expressions">B-expressions</a></li>
      <li><a href="#et-al" id="markdown-toc-et-al">et al.?</a></li>
    </ul>
  </li>
</ul>

<hr />

<p>In this post I will look at some <em>metasyntaxes</em>.
These are general-purpose syntaxes that don’t have inherent semantics.
Because a metasyntax describes a generic data structure instead of an AST, they are very easy to extend within the language through macros.</p>

<table>
  <tbody>
    <tr>
      <td>ℹ️ <a href="https://en.wikipedia.org/wiki/Metasyntax">“Metasyntax”</a> actually means <em>a syntax that can be used to describe other syntaxes</em>. I’m reusing that name for a different concept.</td>
    </tr>
  </tbody>
</table>

<h1 id="syntax">Syntax</h1>

<p>Most programming languages have a 1:1 relationship between syntax tokens and the AST.
You use keywords, delimiters and terminators to change the state of the parser.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def f(x, y) { print(x); print(y); }

def main() { f(1, 2); }
</code></pre></div></div>

<p>This is very easy to implement with a <a href="https://en.wikipedia.org/wiki/Comparison_of_parser_generators">parser generator</a>.</p>

<p>This approach is fine, and it’s what most programming languages do.
However, these languages tend to become more complex over time, and we end up with a very large set of syntax rules.
It would be much better if we had a single, predictable way to write expressions.</p>

<h1 id="metasyntax">Metasyntax</h1>

<h2 id="s-expressions">S-expressions</h2>

<p>With an s-expression syntax, you basically just parse a tree using parentheses.
For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(def f (x y) (do (print x) (print y)))

(def main () (do (f 1 2)))
</code></pre></div></div>

<p>Everything is either a tree <code class="language-plaintext highlighter-rouge">(f x ...)</code> or an atom <code class="language-plaintext highlighter-rouge">x</code>.</p>

<p>You can see that even though it has built-in keywords like <code class="language-plaintext highlighter-rouge">def</code> and <code class="language-plaintext highlighter-rouge">do</code>, the parser will also recognise something like <code class="language-plaintext highlighter-rouge">(table (1 2 3) (4 5 6))</code>, and the language can provide some system to parse that tree within a program.
That’s what Lisp’s metaprogramming system is.</p>

<h2 id="indentation-trees">Indentation trees</h2>

<p>Another way to encode trees is with whitespace:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def f (x y)
  do
    print x
    print y

def main ()
  do (f 1 2)
</code></pre></div></div>

<p>There are a few ways of doing this, but here I’m using these rules:</p>

<ol>
  <li>Add an opening parenthesis to the start of each line</li>
  <li>Add <code class="language-plaintext highlighter-rouge">max(0, 1 - N)</code> closing parentheses to the end of each line, where <code class="language-plaintext highlighter-rouge">N</code> is the change in indentation of the next line</li>
</ol>

<p>You end up with this tree, which is identical to the previous s-expression:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(def f (x y)
  (do
    (print x)
    (print y)))

(def main ()
  (do (f 1 2)))
</code></pre></div></div>

<p>I like this metasyntax, because we already structure programs with indentation, this just makes it explicit.
However based on my research, whitespace-sensitivity is very controversial!
I also get the impression that most new languages are completely insensitive to whitespace.</p>

<h2 id="b-expressions">B-expressions</h2>

<p>Instead of parsing a <a href="https://en.wikipedia.org/wiki/Rose_tree">rose tree</a> with parentheses, you can also parse a <a href="https://en.wikipedia.org/wiki/Binary_tree">binary tree</a> with binary operators.
I call these “b-expressions”.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f = x, y -&gt; print x; print y.

main = () -&gt; f 1 2
</code></pre></div></div>

<p>Everything is a binary expression <code class="language-plaintext highlighter-rouge">f &lt;op&gt; x</code> or an atom <code class="language-plaintext highlighter-rouge">x</code>.</p>

<ul>
  <li>We use an “empty operator” for function application <code class="language-plaintext highlighter-rouge">f x</code>. In Haskell this is sometimes called the “whitespace operator”.</li>
  <li>We’re even using a “full-stop operator” to separate definitions, making the entire program one big binary expression.</li>
  <li>We’re using operator precedence and associativity, which removes the need for parentheses:</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((f = ((x , y) -&gt; ((print x) ; (print y)))) . (main = (() -&gt; ((f 1) 2))))
</code></pre></div></div>

<p>This syntax is well-suited for expression-oriented functional programming languages.</p>

<p>I really like this metasyntax, but it does have some quirks.</p>

<p>First of all, it really needs operator precedence, otherwise it’s unwieldy.
And if we get a user-defined operator <code class="language-plaintext highlighter-rouge">x &gt;&gt;= y</code>, we need a precedence declaration to figure out how to parse it.
But we need to parse the program to get the precedence declaration…
This is a bit of a chicken-and-egg problem.</p>

<p>Also, top-level expressions are a bit awkward.
In my example I used a full-stop operator, similar to <a href="https://learnxinyminutes.com/coq">Coq</a>.
But unlike Coq, it’s a separator and not a terminator, so the last expression can’t actually end in anything.</p>

<h2 id="et-al">et al.?</h2>

<p>What other metasyntaxes are out there?</p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/metasyntax/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-06-29-metasyntax.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Inline Your Runtime</title><link href="https://willmcpherson2.com/2025/05/18/inline-your-runtime.html" rel="alternate" type="text/html" title="Inline Your Runtime" /><published>2025-05-18T00:00:00+00:00</published><updated>2025-05-18T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/05/18/inline-your-runtime</id><content type="html" xml:base="https://willmcpherson2.com/2025/05/18/inline-your-runtime.html"><![CDATA[<ul id="markdown-toc">
  <li><a href="#compiler-vs-runtime" id="markdown-toc-compiler-vs-runtime">Compiler vs Runtime</a></li>
  <li><a href="#link-your-runtime" id="markdown-toc-link-your-runtime">Link Your Runtime</a></li>
  <li><a href="#inline-your-runtime" id="markdown-toc-inline-your-runtime">Inline Your Runtime</a></li>
  <li><a href="#implementation" id="markdown-toc-implementation">Implementation</a>    <ul>
      <li><a href="#toolchain" id="markdown-toc-toolchain">Toolchain</a></li>
      <li><a href="#runtime" id="markdown-toc-runtime">Runtime</a></li>
      <li><a href="#compiler" id="markdown-toc-compiler">Compiler</a></li>
    </ul>
  </li>
  <li><a href="#conclusion" id="markdown-toc-conclusion">Conclusion</a></li>
  <li><a href="#homework" id="markdown-toc-homework">Homework</a>    <ul>
      <li><a href="#build-the-runtime-on-std" id="markdown-toc-build-the-runtime-on-std">Build the runtime on <code class="language-plaintext highlighter-rouge">std</code></a></li>
      <li><a href="#use-a-better-allocator" id="markdown-toc-use-a-better-allocator">Use a better allocator</a></li>
      <li><a href="#target-machine-optimisations" id="markdown-toc-target-machine-optimisations">Target machine optimisations</a></li>
      <li><a href="#statically-linked-compiler" id="markdown-toc-statically-linked-compiler">Statically linked compiler</a></li>
      <li><a href="#portable-static-linking" id="markdown-toc-portable-static-linking">Portable static linking</a></li>
    </ul>
  </li>
</ul>

<hr />

<table>
  <tbody>
    <tr>
      <td>💾 <a href="https://github.com/willmcpherson2/inline-your-runtime">github.com/willmcpherson2/inline-your-runtime</a></td>
    </tr>
  </tbody>
</table>

<blockquote>
  <p>Inlining is the mother of all optimisations</p>
</blockquote>

<p>- Rémi Forax (<a href="https://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers#788347f5d21641a7115ba069f58715848dba9850">earliest source I could find</a>)</p>

<h1 id="compiler-vs-runtime">Compiler vs Runtime</h1>

<p>In programming language implementation, we often separate the <strong>compiler</strong> from the <strong>runtime</strong>.
This is useful when generating code that remains constant.</p>

<p>For example, arithmetic and conditionals require generating very specific instructions, whereas a built-in data structure should be defined in the runtime system.
The compiler can then generate instructions to call into the runtime API.
This is especially pertinent if you’re using something like LLVM where generating instructions can be cumbersome.</p>

<p>So basically: if you find yourself defining a function <em>via code generation</em>, you should just define that function in a programming language.</p>

<p>This separation of concerns becomes practically unavoidable for high-level languages where you need to define closures, data structures, I/O, etc.</p>

<h1 id="link-your-runtime">Link Your Runtime</h1>

<p>Ok, so how do we actually use this technique?
The most straightforward solution is to implement the programming language as an executable compiler and a runtime library.
The compiler generates a binary which is then linked against the runtime library.</p>

<p>This is a fine solution, and I actually recommend it over some of the hacks that I’ll be describing in this post.
However, it’s worth understanding some of the downsides of linking your runtime.</p>

<p>By separately compiling the executable and the runtime, we miss out on a lot of the optimisations available in LLVM.
While the executable and the runtime can be optimised separately, information is lost in the boundary.</p>

<p>This is particularly problematic because this is the <em>runtime of a programming language</em>.
Micro-optimisations actually matter here - a 1% improvement is a 1% improvement for every program.</p>

<h1 id="inline-your-runtime">Inline Your Runtime</h1>

<p>Basically we want something like <a href="https://llvm.org/docs/LinkTimeOptimization.html">Link Time Optimisation</a> (LTO), which can optimise across module boundaries.</p>

<p>LTO is a bit confusing and I don’t understand it.
But basically, instead of linking object files, you tell the compiler (e.g. Clang) to emit something it can actually optimise (e.g. LLVM bitcode).
This enables whole-program optimisation.</p>

<p>But how do we do that with the generated code and the runtime?
Well, we could compile our runtime to LLVM bitcode ahead of time, generate our code as LLVM bitcode and then link them.
For example if <code class="language-plaintext highlighter-rouge">rts.bc</code> is our runtime library and <code class="language-plaintext highlighter-rouge">output.bc</code> is our generated code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ llvm-link rts.bc output.bc -o main.bc
$ opt main.bc -o main.bc
$ llc --filetype=obj main.bc -o main.o
$ cc main.o -o main
</code></pre></div></div>

<p>Or in the case of JIT compiling:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ llvm-link rts.bc output.bc -o main.bc
$ opt main.bc -o main.bc
$ lli main.bc
</code></pre></div></div>

<p>As you can see, this is pretty straightforward.
Let’s see how we can actually generate the bitcode, and how we can use this LLVM functionality through the API.</p>

<h1 id="implementation">Implementation</h1>

<p>To properly implement this in a compiler, we can do the following:</p>

<ul>
  <li>Build a runtime library
    <ul>
      <li>Use a Rust toolchain to compile the library to LLVM bitcode</li>
    </ul>
  </li>
  <li>Build a compiler
    <ul>
      <li>Include the runtime library in the compiler</li>
      <li>Link the compiler to the same LLVM version as the runtime library</li>
      <li>Write some code to call our runtime API</li>
      <li>Use the Rust toolchain to compile the compiler to a native executable</li>
    </ul>
  </li>
</ul>

<h2 id="toolchain">Toolchain</h2>

<p>First we need a development environment which provides LLVM libraries for our compiler, and also a Rust toolchain that uses the same LLVM version.</p>

<p><a href="https://github.com/willmcpherson2/inline-your-runtime/blob/3a27713dddb4cebad4f6cf9fd9b0dbffda49419d/flake.nix"><code class="language-plaintext highlighter-rouge">flake.nix</code></a></p>

<div class="language-nix highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nv">description</span> <span class="o">=</span> <span class="s2">"Rust + LLVM"</span><span class="p">;</span>

  <span class="nv">inputs</span> <span class="o">=</span> <span class="p">{</span>
    <span class="nv">nixpkgs</span><span class="o">.</span><span class="nv">url</span> <span class="o">=</span> <span class="s2">"github:NixOS/nixpkgs/nixos-24.11"</span><span class="p">;</span>
    <span class="nv">rust-overlay</span><span class="o">.</span><span class="nv">url</span> <span class="o">=</span> <span class="s2">"github:oxalica/rust-overlay"</span><span class="p">;</span>
    <span class="nv">flake-utils</span><span class="o">.</span><span class="nv">url</span> <span class="o">=</span> <span class="s2">"github:numtide/flake-utils"</span><span class="p">;</span>
  <span class="p">};</span>

  <span class="nv">outputs</span> <span class="o">=</span>
    <span class="p">{</span>
      <span class="nv">self</span><span class="p">,</span>
      <span class="nv">nixpkgs</span><span class="p">,</span>
      <span class="nv">rust-overlay</span><span class="p">,</span>
      <span class="nv">flake-utils</span><span class="p">,</span>
      <span class="o">...</span>
    <span class="p">}:</span>
    <span class="nv">flake-utils</span><span class="o">.</span><span class="nv">lib</span><span class="o">.</span><span class="nv">eachDefaultSystem</span> <span class="p">(</span>
      <span class="nv">system</span><span class="p">:</span>
      <span class="kd">let</span>
        <span class="nv">overlays</span> <span class="o">=</span> <span class="p">[</span> <span class="p">(</span><span class="kr">import</span> <span class="nv">rust-overlay</span><span class="p">)</span> <span class="p">];</span>
        <span class="nv">pkgs</span> <span class="o">=</span> <span class="kr">import</span> <span class="nv">nixpkgs</span> <span class="p">{</span>
          <span class="kn">inherit</span> <span class="nv">system</span> <span class="nv">overlays</span><span class="p">;</span>
        <span class="p">};</span>
        <span class="nv">llvm</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">llvmPackages_18</span><span class="o">.</span><span class="nv">llvm</span><span class="p">;</span>
      <span class="kn">in</span>
      <span class="p">{</span>
        <span class="nv">devShells</span><span class="o">.</span><span class="nv">default</span> <span class="o">=</span> <span class="nv">pkgs</span><span class="o">.</span><span class="nv">mkShell</span> <span class="p">{</span>
          <span class="nv">buildInputs</span> <span class="o">=</span> <span class="p">[</span>
            <span class="nv">llvm</span>
            <span class="nv">pkgs</span><span class="o">.</span><span class="nv">valgrind</span>
            <span class="nv">pkgs</span><span class="o">.</span><span class="nv">libffi</span>
            <span class="nv">pkgs</span><span class="o">.</span><span class="nv">libxml2</span>
            <span class="p">(</span><span class="nv">pkgs</span><span class="o">.</span><span class="nv">rust-bin</span><span class="o">.</span><span class="nv">nightly</span><span class="o">.</span><span class="s2">"2024-07-31"</span><span class="o">.</span><span class="nv">default</span><span class="o">.</span><span class="nv">override</span> <span class="p">{</span>
              <span class="nv">extensions</span> <span class="o">=</span> <span class="p">[</span>
                <span class="s2">"rust-src"</span>
                <span class="s2">"rust-analyzer-preview"</span>
                <span class="s2">"miri"</span>
              <span class="p">];</span>
            <span class="p">})</span>
          <span class="p">];</span>
        <span class="p">};</span>
      <span class="p">}</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I chose LLVM 18 because it’s supported by the <a href="https://github.com/TheDan64/inkwell">Inkwell crate</a> which provides high-level Rust bindings to LLVM.</p>

<p>We should verify that our LLVM versions match:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ llvm-config --version
18.1.8

$ rustc -vV
rustc 1.82.0-nightly (f8060d282 2024-07-30)
binary: rustc
commit-hash: f8060d282d42770fadd73905e3eefb85660d3278
commit-date: 2024-07-30
host: x86_64-unknown-linux-gnu
release: 1.82.0-nightly
LLVM version: 18.1.7
</code></pre></div></div>

<p>Close enough.</p>

<p>To find a Rust version that uses a certain LLVM version, you can trawl through the <a href="https://raw.githubusercontent.com/rust-lang/rust/master/RELEASES.md">Rust changelog</a>.</p>

<h2 id="runtime">Runtime</h2>

<p>The next step is to build the runtime library.</p>

<p><a href="https://github.com/willmcpherson2/inline-your-runtime/blob/3a27713dddb4cebad4f6cf9fd9b0dbffda49419d/rts/src/lib.rs"><code class="language-plaintext highlighter-rouge">rts/src/lib.rs</code></a></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![no_std]</span>
<span class="nd">#![allow(internal_features)]</span>
<span class="nd">#![feature(rustc_attrs,</span> <span class="nd">linkage)]</span>

<span class="k">extern</span> <span class="k">crate</span> <span class="n">alloc</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">alloc</span><span class="p">::</span><span class="n">vec</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">alloc</span><span class="p">::{</span>
    <span class="nn">alloc</span><span class="p">::{</span><span class="n">GlobalAlloc</span><span class="p">,</span> <span class="n">Layout</span><span class="p">},</span>
    <span class="nn">boxed</span><span class="p">::</span><span class="nb">Box</span><span class="p">,</span>
    <span class="nn">vec</span><span class="p">::</span><span class="nb">Vec</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">use</span> <span class="nn">libc</span><span class="p">::{</span><span class="n">abort</span><span class="p">,</span> <span class="n">aligned_alloc</span><span class="p">,</span> <span class="nb">c_void</span><span class="p">,</span> <span class="n">free</span><span class="p">};</span>

<span class="nd">#[allow(unused_imports)]</span>
<span class="k">use</span> <span class="nn">core</span><span class="p">::</span><span class="nn">panic</span><span class="p">::</span><span class="n">PanicInfo</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">Allocator</span><span class="p">;</span>

<span class="k">unsafe</span> <span class="k">impl</span> <span class="n">GlobalAlloc</span> <span class="k">for</span> <span class="n">Allocator</span> <span class="p">{</span>
    <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">alloc</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">layout</span><span class="p">:</span> <span class="n">Layout</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">aligned_alloc</span><span class="p">(</span><span class="n">layout</span><span class="nf">.align</span><span class="p">(),</span> <span class="n">layout</span><span class="nf">.size</span><span class="p">())</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">dealloc</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">ptr</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">u8</span><span class="p">,</span> <span class="n">_layout</span><span class="p">:</span> <span class="n">Layout</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">free</span><span class="p">(</span><span class="n">ptr</span> <span class="k">as</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">c_void</span><span class="p">)</span> <span class="p">};</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="nd">#[global_allocator]</span>
<span class="k">static</span> <span class="n">GLOBAL</span><span class="p">:</span> <span class="n">Allocator</span> <span class="o">=</span> <span class="n">Allocator</span><span class="p">;</span>

<span class="nd">#[cfg(not(test))]</span>
<span class="nd">#[panic_handler]</span>
<span class="k">fn</span> <span class="nf">panic</span><span class="p">(</span><span class="n">_info</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">PanicInfo</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">!</span> <span class="p">{</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">abort</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>

<span class="nd">#[rustc_std_internal_symbol]</span>
<span class="nd">#[linkage</span> <span class="nd">=</span> <span class="s">"weak"</span><span class="nd">]</span>
<span class="k">fn</span> <span class="nf">__rust_alloc_error_handler</span><span class="p">(</span><span class="n">_size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">_align</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">!</span> <span class="p">{</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">abort</span><span class="p">()</span> <span class="p">}</span>
<span class="p">}</span>

<span class="nd">#[rustc_std_internal_symbol]</span>
<span class="nd">#[linkage</span> <span class="nd">=</span> <span class="s">"weak"</span><span class="nd">]</span>
<span class="nd">#[allow(non_upper_case_globals)]</span>
<span class="k">static</span> <span class="n">__rust_no_alloc_shim_is_unstable</span><span class="p">:</span> <span class="nb">u8</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="k">pub</span> <span class="k">struct</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="n">numbers</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">i32</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">new_foo</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">Foo</span> <span class="p">{</span>
            <span class="n">numbers</span><span class="p">:</span> <span class="nd">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span>
        <span class="p">})</span>
    <span class="p">}</span>

    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">sum_foo</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Foo</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.numbers</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.sum</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">free_foo</span><span class="p">(</span><span class="n">_foo</span><span class="p">:</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{}</span>
<span class="p">}</span>

<span class="nd">#[cfg(test)]</span>
<span class="k">mod</span> <span class="n">test</span> <span class="p">{</span>
    <span class="k">use</span> <span class="k">super</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>

    <span class="nd">#[test]</span>
    <span class="k">fn</span> <span class="nf">test_rts</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">foo</span> <span class="o">=</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">new_foo</span><span class="p">();</span>
        <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">foo</span><span class="nf">.sum_foo</span><span class="p">();</span>
        <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">result</span><span class="p">,</span> <span class="mi">6</span><span class="p">);</span>
        <span class="nn">Foo</span><span class="p">::</span><span class="nf">free_foo</span><span class="p">(</span><span class="n">foo</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is a pretty typical <code class="language-plaintext highlighter-rouge">no_std</code> library that just depends on <a href="https://langdev.stackexchange.com/questions/3233/why-do-common-rust-packages-depend-on-c-code/3237#3237">libc</a>.
The only quirk is that we need to define <a href="https://stdrs.dev/nightly/x86_64-unknown-linux-gnu/alloc/alloc/fn.__rust_alloc_error_handler.html"><code class="language-plaintext highlighter-rouge">__rust_alloc_error_handler</code></a> and <a href="https://stdrs.dev/nightly/x86_64-unknown-linux-gnu/alloc/alloc/static.__rust_no_alloc_shim_is_unstable.html"><code class="language-plaintext highlighter-rouge">__rust_no_alloc_shim_is_unstable</code></a>, which are special symbols inserted by the Rust compiler which are mysteriously <em>not</em> inserted when compiling to LLVM bitcode.</p>

<p>Our runtime system provides a simple API for allocating, summing and freeing a <code class="language-plaintext highlighter-rouge">Foo</code>.
Imagine this is a very important built-in data structure in our language.</p>

<p>Verifying the correctness of the runtime system is extremely important.
Any bug or vulnerability in this system compromises the security of every program in the language.
Here we’re using both of the age-old techniques for program verification:</p>

<p><strong>Types</strong>: by providing methods on an opaque pointer, we can write safe Rust code.
You don’t have to write your runtime in C.</p>

<p><strong>Tests</strong>: because our runtime is just a standalone library, we can develop a comprehensive test suite.
We can use the <a href="https://github.com/rust-lang/miri">Miri interpreter</a> to verify the safety of any low-level code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cargo miri test -p rts
   Compiling rts v0.1.0 (/home/will/Desktop/inline-your-runtime/rts)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.06s
     Running unittests src/lib.rs (target/miri/x86_64-unknown-linux-gnu/debug/deps/rts-b728a07dc5e60d37)

running 1 test
test test::test_rts ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s

   Doc-tests rts

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
</code></pre></div></div>

<p>Now we can compile the runtime library for consumption in the compiler.</p>

<p><a href="https://github.com/willmcpherson2/inline-your-runtime/blob/3a27713dddb4cebad4f6cf9fd9b0dbffda49419d/Makefile"><code class="language-plaintext highlighter-rouge">Makefile</code></a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ARCH=x86_64-unknown-linux-gnu

.PHONY: debug
debug:
	$(MAKE) build DIR=debug PROFILE=dev

.PHONY: release
release:
	$(MAKE) build DIR=release PROFILE=release

.PHONY: build
build:
	RUSTFLAGS="--emit=llvm-bc" cargo build \
		--package rts \
		--profile $(PROFILE) \
		--target $(ARCH) \
		-Z build-std=core,compiler_builtins,alloc

	llvm-link \
		target/$(ARCH)/$(DIR)/deps/core-*.bc \
		target/$(ARCH)/$(DIR)/deps/compiler_builtins-*.bc \
		target/$(ARCH)/$(DIR)/deps/alloc-*.bc \
		target/$(ARCH)/$(DIR)/deps/libc-*.bc \
		target/$(ARCH)/$(DIR)/deps/rts-*.bc \
		-o target/rts.bc

	opt \
		--internalize-public-api-list="new_foo,sum_foo,free_foo" \
		--passes="internalize,globaldce" \
		target/rts.bc \
		-o target/rts.bc

	opt \
		--passes="internalize" \
		target/rts.bc \
		-o target/rts.bc
</code></pre></div></div>

<p>Basically we do the following:</p>

<ul>
  <li>Compile the library and its dependencies to LLVM bitcode</li>
  <li>Link the LLVM modules</li>
  <li>Perform dead-code elimination using <a href="https://llvm.org/docs/Passes.html#internalize-internalize-global-symbols">internalize</a> and <a href="https://llvm.org/docs/Passes.html#globaldce-dead-global-elimination">globaldce</a></li>
  <li>Perform a final internalize pass</li>
</ul>

<p>The dead-code elimination is important because without it, the final module includes the code of all its dependencies.
Also, the size of the runtime module will affect the performance of our compiler.</p>

<p>Let’s check that we have all our symbols:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ llvm-nm target/rts.bc
---------------- t
---------------- t .Ltmp0
---------------- t .Ltmp1
---------------- t _ZN5alloc5alloc18handle_alloc_error17he8ceb1c27494fe14E
---------------- t __rust_alloc_error_handler
---------------- d __rust_no_alloc_shim_is_unstable
---------------- T __rust_probestack
                 U abort
                 U aligned_alloc
                 U free
---------------- T free_foo
---------------- T new_foo
---------------- T sum_foo
</code></pre></div></div>

<p>Perfect - the only undefined symbols are the C functions we’re calling.
Note that this is for the <code class="language-plaintext highlighter-rouge">release</code> profile and the <code class="language-plaintext highlighter-rouge">dev</code> profile will create a much larger module.</p>

<p>If you want to simplify this setup, it seems that if you write C-like code (i.e. only using the <code class="language-plaintext highlighter-rouge">libc</code> crate and manually managing memory), then the <code class="language-plaintext highlighter-rouge">--emit=llvm-bc</code> flag will pretty much give you a standalone LLVM module.
As soon as you import <code class="language-plaintext highlighter-rouge">Box</code> however, things start to get complicated.</p>

<h2 id="compiler">Compiler</h2>

<p>Finally we can write our compiler.</p>

<p><a href="https://github.com/willmcpherson2/inline-your-runtime/blob/3a27713dddb4cebad4f6cf9fd9b0dbffda49419d/compiler/src/main.rs"><code class="language-plaintext highlighter-rouge">compiler/src/main.rs</code></a></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">inkwell</span><span class="p">::{</span>
    <span class="nn">context</span><span class="p">::</span><span class="n">Context</span><span class="p">,</span>
    <span class="nn">memory_buffer</span><span class="p">::</span><span class="n">MemoryBuffer</span><span class="p">,</span>
    <span class="nn">module</span><span class="p">::</span><span class="n">Module</span><span class="p">,</span>
    <span class="nn">passes</span><span class="p">::</span><span class="n">PassBuilderOptions</span><span class="p">,</span>
    <span class="nn">targets</span><span class="p">::{</span><span class="n">FileType</span><span class="p">,</span> <span class="n">InitializationConfig</span><span class="p">,</span> <span class="n">Target</span><span class="p">,</span> <span class="n">TargetMachine</span><span class="p">,</span> <span class="n">TargetMachineOptions</span><span class="p">},</span>
    <span class="n">OptimizationLevel</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::{</span><span class="nn">env</span><span class="p">::</span><span class="n">args</span><span class="p">,</span> <span class="nn">path</span><span class="p">::</span><span class="n">Path</span><span class="p">,</span> <span class="nn">process</span><span class="p">::</span><span class="n">exit</span><span class="p">};</span>

<span class="k">const</span> <span class="n">RTS_BC</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">u8</span><span class="p">]</span> <span class="o">=</span> <span class="nd">include_bytes!</span><span class="p">(</span><span class="s">"../../target/rts.bc"</span><span class="p">);</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">Target</span><span class="p">::</span><span class="nf">initialize_all</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">InitializationConfig</span><span class="p">::</span><span class="nf">default</span><span class="p">());</span>
    <span class="k">let</span> <span class="n">triple</span> <span class="o">=</span> <span class="nn">TargetMachine</span><span class="p">::</span><span class="nf">get_default_triple</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">target</span> <span class="o">=</span> <span class="nn">Target</span><span class="p">::</span><span class="nf">from_triple</span><span class="p">(</span><span class="o">&amp;</span><span class="n">triple</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">options</span> <span class="o">=</span> <span class="nn">TargetMachineOptions</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.set_level</span><span class="p">(</span><span class="nn">OptimizationLevel</span><span class="p">::</span><span class="nb">None</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">machine</span> <span class="o">=</span> <span class="n">target</span>
        <span class="nf">.create_target_machine_from_options</span><span class="p">(</span><span class="o">&amp;</span><span class="n">triple</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">context</span> <span class="o">=</span> <span class="nn">Context</span><span class="p">::</span><span class="nf">create</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">buffer</span> <span class="o">=</span> <span class="nn">MemoryBuffer</span><span class="p">::</span><span class="nf">create_from_memory_range</span><span class="p">(</span><span class="n">RTS_BC</span><span class="p">,</span> <span class="s">"main"</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">module</span> <span class="o">=</span> <span class="nn">Module</span><span class="p">::</span><span class="nf">parse_bitcode_from_buffer</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buffer</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">context</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">context</span><span class="nf">.create_builder</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">main_fun_type</span> <span class="o">=</span> <span class="n">context</span><span class="nf">.i32_type</span><span class="p">()</span><span class="nf">.fn_type</span><span class="p">(</span><span class="o">&amp;</span><span class="p">[],</span> <span class="k">false</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">main_fun</span> <span class="o">=</span> <span class="n">module</span><span class="nf">.add_function</span><span class="p">(</span><span class="s">"main"</span><span class="p">,</span> <span class="n">main_fun_type</span><span class="p">,</span> <span class="nb">None</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">block</span> <span class="o">=</span> <span class="n">context</span><span class="nf">.append_basic_block</span><span class="p">(</span><span class="n">main_fun</span><span class="p">,</span> <span class="s">"start"</span><span class="p">);</span>
    <span class="n">builder</span><span class="nf">.position_at_end</span><span class="p">(</span><span class="n">block</span><span class="p">);</span>

    <span class="k">let</span> <span class="n">new_foo</span> <span class="o">=</span> <span class="n">module</span><span class="nf">.get_function</span><span class="p">(</span><span class="s">"new_foo"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">builder</span>
        <span class="nf">.build_call</span><span class="p">(</span><span class="n">new_foo</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">[],</span> <span class="s">"foo"</span><span class="p">)</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.try_as_basic_value</span><span class="p">()</span>
        <span class="nf">.unwrap_left</span><span class="p">()</span>
        <span class="nf">.into_pointer_value</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">sum_foo</span> <span class="o">=</span> <span class="n">module</span><span class="nf">.get_function</span><span class="p">(</span><span class="s">"sum_foo"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="n">builder</span>
        <span class="nf">.build_call</span><span class="p">(</span><span class="n">sum_foo</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">foo</span><span class="nf">.into</span><span class="p">()],</span> <span class="s">"result"</span><span class="p">)</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.try_as_basic_value</span><span class="p">()</span>
        <span class="nf">.unwrap_left</span><span class="p">()</span>
        <span class="nf">.into_int_value</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">free_foo</span> <span class="o">=</span> <span class="n">module</span><span class="nf">.get_function</span><span class="p">(</span><span class="s">"free_foo"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="n">builder</span><span class="nf">.build_call</span><span class="p">(</span><span class="n">free_foo</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">foo</span><span class="nf">.into</span><span class="p">()],</span> <span class="s">""</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>

    <span class="n">builder</span><span class="nf">.build_return</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">result</span><span class="p">))</span><span class="nf">.unwrap</span><span class="p">();</span>

    <span class="n">module</span>
        <span class="nf">.run_passes</span><span class="p">(</span><span class="s">"default&lt;O3&gt;"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">machine</span><span class="p">,</span> <span class="nn">PassBuilderOptions</span><span class="p">::</span><span class="nf">create</span><span class="p">())</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="n">module</span><span class="nf">.verify</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

    <span class="k">let</span> <span class="n">eval</span> <span class="o">=</span> <span class="nf">args</span><span class="p">()</span><span class="nf">.any</span><span class="p">(|</span><span class="n">arg</span><span class="p">|</span> <span class="n">arg</span> <span class="o">==</span> <span class="s">"-e"</span><span class="p">);</span>
    <span class="k">if</span> <span class="n">eval</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">engine</span> <span class="o">=</span> <span class="n">module</span>
            <span class="nf">.create_jit_execution_engine</span><span class="p">(</span><span class="nn">OptimizationLevel</span><span class="p">::</span><span class="n">Aggressive</span><span class="p">)</span>
            <span class="nf">.unwrap</span><span class="p">();</span>
        <span class="k">let</span> <span class="n">code</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="n">engine</span><span class="nf">.run_function_as_main</span><span class="p">(</span><span class="n">main_fun</span><span class="p">,</span> <span class="o">&amp;</span><span class="p">[])</span> <span class="p">};</span>
        <span class="nf">exit</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">machine</span>
            <span class="nf">.write_to_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">module</span><span class="p">,</span> <span class="nn">FileType</span><span class="p">::</span><span class="n">Object</span><span class="p">,</span> <span class="nn">Path</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">"main.o"</span><span class="p">))</span>
            <span class="nf">.unwrap</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The compiler is the easy part.</p>

<ul>
  <li>Include the runtime bitcode directly in the executable</li>
  <li>Load the runtime bitcode to create a <code class="language-plaintext highlighter-rouge">main</code> module</li>
  <li>Define a <code class="language-plaintext highlighter-rouge">main</code> function which calls our runtime API</li>
  <li>Run some optimisation passes</li>
  <li>Verify the module</li>
  <li>Either run the JIT or compile to an object file</li>
</ul>

<p>Let’s JIT:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ./target/debug/compiler -e
$ echo $?
6
</code></pre></div></div>

<p>Success! We got our <code class="language-plaintext highlighter-rouge">1 + 2 + 3</code>.</p>

<p>What’s great about the JIT compiler is that it basically doesn’t require anything from the environment.
No opening files, no invoking a C compiler, just direct execution.</p>

<p>Let’s compile:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ./target/debug/compiler
$ cc main.o -o main
$ ./main
$ echo $?
6
</code></pre></div></div>

<p>We can even make a static binary if we want:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gcc -static main.o -o main -L $(nix path-info nixpkgs#glibc.static)/lib
$ ldd main
        not a dynamic executable
</code></pre></div></div>

<h1 id="conclusion">Conclusion</h1>

<ul>
  <li>We’ve achieved a clear separation of concerns between our compiler and runtime.</li>
  <li>The runtime is safe, ergonomic, correct, efficient, testable and small.</li>
  <li>The runtime can be directly embedded into the compiler for maximum portability.</li>
  <li>The compiler can generate code that can easily call into the runtime API.</li>
  <li>The runtime code occupies the same module as the generated code, and we run optimisations on the whole program - allowing LLVM to inline our runtime functions.</li>
</ul>

<p>However:</p>

<ul>
  <li>This relies on <code class="language-plaintext highlighter-rouge">RUSTFLAGS="--emit=llvm-bc"</code>, which is a pretty creative <a href="https://xkcd.com/1172/">workflow</a>.</li>
  <li>We have to be very selective with our Rust version because we need to generate code that uses the same LLVM version. This really limits our flexibility.</li>
  <li>We have to be careful about what code we generate and what our runtime API looks like. Of course, code generation is always dangerous, but Rust brings with it a lot of extra rules (e.g. <a href="https://doc.rust-lang.org/nomicon/aliasing.html">aliasing</a>)</li>
</ul>

<h1 id="homework">Homework</h1>

<h2 id="build-the-runtime-on-std">Build the runtime on <code class="language-plaintext highlighter-rouge">std</code></h2>

<p>I used <code class="language-plaintext highlighter-rouge">no_std</code> to simplify the build.
But there shouldn’t be anything stopping us from linking to <code class="language-plaintext highlighter-rouge">std</code>.</p>

<h2 id="use-a-better-allocator">Use a better allocator</h2>

<p>The allocator I’m using in <a href="https://github.com/willmcpherson2/inline-your-runtime/blob/3a27713dddb4cebad4f6cf9fd9b0dbffda49419d/rts/src/lib.rs"><code class="language-plaintext highlighter-rouge">rts/src/lib.rs</code></a> isn’t very efficient.
We need to also implement <a href="https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html#method.alloc_zeroed"><code class="language-plaintext highlighter-rouge">alloc_zeroed</code></a> and <a href="https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html#method.realloc"><code class="language-plaintext highlighter-rouge">realloc</code></a>. Better yet, integrate a modern allocator like <a href="https://github.com/purpleprotocol/mimalloc_rust">mimalloc</a> or <a href="https://github.com/EmbarkStudios/rpmalloc-rs">rpmalloc</a>.</p>

<h2 id="target-machine-optimisations">Target machine optimisations</h2>

<p>For some reason, using anything other than <code class="language-plaintext highlighter-rouge">OptimizationLevel::None</code> for the <code class="language-plaintext highlighter-rouge">TargetMachine</code> causes memory errors when compiling to an object file.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">options</span> <span class="o">=</span> <span class="nn">TargetMachineOptions</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.set_level</span><span class="p">(</span><span class="nn">OptimizationLevel</span><span class="p">::</span><span class="n">Aggressive</span><span class="p">);</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ valgrind ./target/debug/compiler 
Conditional jump or move depends on uninitialised value(s)
   at 0x34A0BA2: llvm::APInt::setBits(unsigned int, unsigned int) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x6BB0684: computeKnownBits(llvm::Value const*, llvm::APInt const&amp;, llvm::KnownBits&amp;, unsigned int, llvm::SimplifyQuery const&amp;) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x6BBAADF: llvm::computeKnownBits(llvm::Value const*, llvm::KnownBits&amp;, llvm::DataLayout const&amp;, unsigned int, llvm::AssumptionCache*, llvm::Instruction const*, llvm::DominatorTree const*, bool) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x5AADDA2: llvm::SelectionDAG::InferPtrAlign(llvm::SDValue) const (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x599C8DF: (anonymous namespace)::DAGCombiner::visitLOAD(llvm::SDNode*) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x59E8E14: (anonymous namespace)::DAGCombiner::combine(llvm::SDNode*) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x59EA68C: llvm::SelectionDAG::Combine(llvm::CombineLevel, llvm::AAResults*, llvm::CodeGenOptLevel) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x5AFE320: llvm::SelectionDAGISel::CodeGenAndEmitDAG() (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x5B00EE6: llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&amp;) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x5B03469: llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&amp;) [clone .part.0] (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x36D3D4F: (anonymous namespace)::X86DAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&amp;) (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
   by 0x5E18707: llvm::MachineFunctionPass::runOnFunction(llvm::Function&amp;) [clone .part.0] (in /home/will/Desktop/inline-your-runtime/target/debug/compiler)
</code></pre></div></div>

<p>This code path doesn’t have any <code class="language-plaintext highlighter-rouge">unsafe</code> blocks, and the module has been verified, so there’s likely a bug in Inkwell or LLVM.</p>

<h2 id="statically-linked-compiler">Statically linked compiler</h2>

<p>Rust supports static linking, but I haven’t looked too deep into it.
It would be great to have a compiler that is completely self-contained.</p>

<h2 id="portable-static-linking">Portable static linking</h2>

<p>It would be awesome if the compiler could be distributed with a static libc and a linker, so that it can simply emit binaries that are statically linked without depending on the system libc or C compiler.</p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/inline-your-runtime/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-05-18-inline-your-runtime.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Calling Rust from Haskell</title><link href="https://willmcpherson2.com/2025/04/03/calling-rust-from-haskell.html" rel="alternate" type="text/html" title="Calling Rust from Haskell" /><published>2025-04-03T00:00:00+00:00</published><updated>2025-04-03T00:00:00+00:00</updated><id>https://willmcpherson2.com/2025/04/03/calling-rust-from-haskell</id><content type="html" xml:base="https://willmcpherson2.com/2025/04/03/calling-rust-from-haskell.html"><![CDATA[<ul id="markdown-toc">
  <li><a href="#c-abi" id="markdown-toc-c-abi">C ABI</a></li>
  <li><a href="#into-the-burrito" id="markdown-toc-into-the-burrito">Into the burrito</a></li>
</ul>

<hr />

<table>
  <tbody>
    <tr>
      <td>💾 This setup is available as a project template: <a href="https://github.com/willmcpherson2/hsrs">hsrs</a></td>
    </tr>
  </tbody>
</table>

<p>Haskell is great, but not low-level.
Fortunately, Haskell has a low-overhead FFI which allows you to call C.
Of course, it doesn’t have to be C.
Any C ABI will do.</p>

<h1 id="c-abi">C ABI</h1>

<p>If you want to learn what “C ABI” actually means, I highly recommend Alexis King’s answer to the Stack Exchange question <a href="https://langdev.stackexchange.com/a/3237">“Why do common Rust packages depend on C code?”</a></p>

<p>But basically, it’s the lingua franca.
Rust speaks the lingua franca like this:</p>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/537e437c606fe5c57a4045450bd5bdf4af9e3115/rs/lib.rs"><code class="language-plaintext highlighter-rouge">rs/lib.rs</code></a></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[repr(C)]</span>
<span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Point</span> <span class="p">{</span>
    <span class="n">x</span><span class="p">:</span> <span class="nb">f64</span><span class="p">,</span>
    <span class="n">y</span><span class="p">:</span> <span class="nb">f64</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Point</span> <span class="p">{</span>
    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">new_point</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">f64</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">f64</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Point</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">Point</span> <span class="p">{</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="p">})</span>
    <span class="p">}</span>

    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">point_length</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">f64</span> <span class="p">{</span>
        <span class="p">(</span><span class="k">self</span><span class="py">.x</span><span class="nf">.powi</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="k">self</span><span class="py">.y</span><span class="nf">.powi</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span><span class="nf">.sqrt</span><span class="p">()</span>
    <span class="p">}</span>

    <span class="nd">#[no_mangle]</span>
    <span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">print_point</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span> <span class="k">self</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="nd">#[no_mangle]</span>
<span class="k">pub</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">free_point</span><span class="p">(</span><span class="n">_point</span><span class="p">:</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Point</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">free_point</code> definition is interesting - since we take the <code class="language-plaintext highlighter-rouge">Box&lt;Point&gt;</code> by value, it is moved into the function and dropped.
So while the function looks redundant, there is a call to <a href="https://doc.rust-lang.org/std/mem/fn.drop.html"><code class="language-plaintext highlighter-rouge">std::mem::drop</code></a>.
You can verify that this works if you call it from C and compile with <code class="language-plaintext highlighter-rouge">-fsanitize=leak</code>.</p>

<p>We need to set up our crate to compile to a static library (<code class="language-plaintext highlighter-rouge">.a</code>) and a header file (<code class="language-plaintext highlighter-rouge">.h</code>):</p>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/537e437c606fe5c57a4045450bd5bdf4af9e3115/Cargo.toml"><code class="language-plaintext highlighter-rouge">Cargo.toml</code></a></p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[package]</span>
<span class="py">name</span> <span class="p">=</span> <span class="s">"hsrs"</span>
<span class="py">version</span> <span class="p">=</span> <span class="s">"0.1.0"</span>
<span class="py">edition</span> <span class="p">=</span> <span class="s">"2021"</span>

<span class="nn">[lib]</span>
<span class="py">path</span> <span class="p">=</span> <span class="s">"rs/lib.rs"</span>
<span class="py">crate-type</span> <span class="p">=</span> <span class="nn">["staticlib"]</span>

<span class="nn">[build-dependencies]</span>
<span class="py">cbindgen</span> <span class="p">=</span> <span class="s">"0.28.0"</span>
</code></pre></div></div>

<p>We’re using <a href="https://github.com/mozilla/cbindgen">cbindgen</a> to generate header files, which you can run via Rust build script:</p>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/537e437c606fe5c57a4045450bd5bdf4af9e3115/build.rs"><code class="language-plaintext highlighter-rouge">build.rs</code></a></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">crate_name</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"CARGO_PKG_NAME"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">crate_dir</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">env</span><span class="p">::</span><span class="nf">var</span><span class="p">(</span><span class="s">"CARGO_MANIFEST_DIR"</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">header_path</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">path</span><span class="p">::</span><span class="nn">PathBuf</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="nd">format!</span><span class="p">(</span><span class="s">"lib/{}.h"</span><span class="p">,</span> <span class="n">crate_name</span><span class="p">));</span>

    <span class="nn">cbindgen</span><span class="p">::</span><span class="nn">Builder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
        <span class="nf">.with_crate</span><span class="p">(</span><span class="n">crate_dir</span><span class="p">)</span>
        <span class="nf">.with_language</span><span class="p">(</span><span class="nn">cbindgen</span><span class="p">::</span><span class="nn">Language</span><span class="p">::</span><span class="n">C</span><span class="p">)</span>
        <span class="nf">.generate</span><span class="p">()</span>
        <span class="nf">.unwrap</span><span class="p">()</span>
        <span class="nf">.write_to_file</span><span class="p">(</span><span class="n">header_path</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now we can build our library:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cargo build -Z unstable-options --artifact-dir=lib
   Compiling hsrs v0.1.0 (/home/will/Desktop/hsrs)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s

$ ls lib
hsrs.h  libhsrs.a
</code></pre></div></div>

<p>Perfect.
This can be consumed by anything that speaks the C ABI.
Let’s set up our Haskell project to do so.</p>

<h1 id="into-the-burrito">Into the burrito</h1>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/537e437c606fe5c57a4045450bd5bdf4af9e3115/hsrs.cabal"><code class="language-plaintext highlighter-rouge">hsrs.cabal</code></a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cabal-version: 3.4
name:          hsrs
version:       0.1.0.0
build-type:    Simple

executable hsrs
  hs-source-dirs:     hs
  main-is:            Main.hs
  other-modules:      Lib
  include-dirs:       lib
  extra-lib-dirs:     lib
  extra-libraries:    hsrs
  default-language:   GHC2024
  build-depends:      base &gt;=4.7 &amp;&amp; &lt;5
  build-tool-depends: c2hs:c2hs &gt;=0.28.8
</code></pre></div></div>

<p>The important parts are <code class="language-plaintext highlighter-rouge">include-dirs</code>, <code class="language-plaintext highlighter-rouge">extra-lib-dirs</code>, <code class="language-plaintext highlighter-rouge">extra-libraries</code> and the dependency on <a href="https://github.com/haskell/c2hs">c2hs</a>, which basically does the opposite of cbindgen - consuming C header files.</p>

<p>You will need a bit of interfacing code however:</p>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/de28e89db03e817c6a02978cce8d029123edf5e7/hs/Lib.chs"><code class="language-plaintext highlighter-rouge">hs/Lib.chs</code></a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module Lib where

import Foreign.Ptr
import Foreign.C.Types

#include "hsrs.h"

{#pointer *Point as PointPtr foreign newtype #}

{#fun new_point as ^ { `Double', `Double' } -&gt; `PointPtr' #}

{#fun point_length as ^ { `PointPtr' } -&gt; `Double' #}

{#fun print_point as ^ { `PointPtr' } -&gt; `()' #}

{#fun free_point as ^ { `PointPtr' } -&gt; `()' #}
</code></pre></div></div>

<p>This isn’t a Haskell file - it’s a <code class="language-plaintext highlighter-rouge">.chs</code> file, which c2hs will use to generate marshalling code and <a href="https://en.wikibooks.org/wiki/Haskell/FFI">FFI calls</a>.</p>

<p>We’re including the C header file that we generated with cbindgen and defining some bindings to it.
In this case, I’m not defining the fields of the <code class="language-plaintext highlighter-rouge">Point</code> struct - I’m just defining an opaque pointer.</p>

<p>I find this pattern very useful: you marshal an opaque pointer to an object which has some state in it, and you call functions with it.
This is basically OOP encapsulation - define an object and only access it through methods, because the insides are too hairy to work with directly.
This design makes your FFI layer thin, which means less can go wrong.</p>

<p>Let’s call our new FFI functions.</p>

<p><a href="https://github.com/willmcpherson2/hsrs/blob/de28e89db03e817c6a02978cce8d029123edf5e7/hs/Main.hs"><code class="language-plaintext highlighter-rouge">hs/Main.hs</code></a></p>

<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">module</span> <span class="nn">Main</span> <span class="kr">where</span>

<span class="kr">import</span> <span class="nn">Lib</span>

<span class="n">main</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
  <span class="n">point</span> <span class="o">&lt;-</span> <span class="n">newPoint</span> <span class="mf">1.5</span> <span class="mf">2.0</span>
  <span class="n">printPoint</span> <span class="n">point</span>
  <span class="n">length</span> <span class="o">&lt;-</span> <span class="n">pointLength</span> <span class="n">point</span>
  <span class="n">putStrLn</span> <span class="o">$</span> <span class="s">"point length: "</span> <span class="o">&lt;&gt;</span> <span class="n">show</span> <span class="n">length</span>
  <span class="n">freePoint</span> <span class="n">point</span>
</code></pre></div></div>

<p>You can see that <code class="language-plaintext highlighter-rouge">snake_case</code> became <code class="language-plaintext highlighter-rouge">camelCase</code>. Other than that, no surprises.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cabal run
Point { x: 1.5, y: 2.0 }
point length: 2.5
</code></pre></div></div>

<p>Awesome.</p>

<p>This setup is available as a <a href="https://github.com/willmcpherson2/hsrs">template on GitHub</a>. It includes a Nix flake to install everything you need to develop a Haskell + Rust project, including language servers and formatters.</p>

<hr />

<div>
    <a href="https://willmcpherson2.substack.com/p/calling-rust-from-haskell/comments"><img src="/assets/images/comment.png" alt="Comment icon" title="Comment" width="32" /></a><a href="https://github.com/willmcpherson2/willmcpherson2.github.io/blob/main/_posts/2025-04-03-calling-rust-from-haskell.md" style="margin-left: 1em"><img src="/assets/images/source.png" alt="Source code icon" title="Source code" width="32" /></a>
</div>]]></content><author><name>Will McPherson</name></author><summary type="html"><![CDATA[]]></summary></entry></feed>