<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lahari Tenneti</title>
    <description>The latest articles on DEV Community by Lahari Tenneti (@lahari_tenneti_4a8a082e9c).</description>
    <link>https://dev.to/lahari_tenneti_4a8a082e9c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3479289%2F25c84943-6853-40b8-83d9-4333147f4b0d.png</url>
      <title>DEV Community: Lahari Tenneti</title>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lahari_tenneti_4a8a082e9c"/>
    <language>en</language>
    <item>
      <title>LLVM #12 — Testing my Custom Pass on a Billion Additions. It was INSANE!</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Thu, 16 Jul 2026 12:13:58 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-12-testing-my-custom-pass-on-a-billion-additions-it-was-insane-4646</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-12-testing-my-custom-pass-on-a-billion-additions-it-was-insane-4646</guid>
      <description>&lt;p&gt;I've spent the last several posts building a pass that finds loops and attaches a "vectorise this" hint to them. Today I actually tested it on a 1000×1000 matrix addition, run 1000 times. That's a billion element-wise floating point additions in the hot loop. And I kid you not... I was NOT prepared for the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1134ms without the pass. 271ms with it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's nearly 4x faster. Same source code, same machine, same input. The only difference was one piece of LLVM metadata that changed how the optimiser was allowed to transform the loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/LLVM-VectorisationHintPass/commits/main/?since=2026-07-16&amp;amp;until=2026-07-16" rel="noopener noreferrer"&gt;Benchmark Experiment History&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I did:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Writing a 2D matrix-addition:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I'd been testing this on a flat 1D array. And the results were pretty nice. From 394 ms without the pass to 324 ms with it. But this time, I wanted something that actually deserves to be called matrix addition — two 1000×1000 grids of floats, added element-by-element:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;chrono&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;matrix_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;high_resolution_clock&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// run 1000 times to make timing meaningful&lt;/span&gt;
        &lt;span class="n"&gt;matrix_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;high_resolution_clock&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;duration_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;chrono&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;" ms&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// this is just to test it with -o3, so the compiler doesn't abandon the computation.&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;There were no function calls or external dependencies, like we had last time with &lt;code&gt;printf&lt;/code&gt;. A billion additions were enough to make the timing difference real and impossible to dismiss.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Compiling to raw IR:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clang++ &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-emit-llvm&lt;/span&gt; &lt;span class="nt"&gt;-O0&lt;/span&gt; &lt;span class="nt"&gt;-Xclang&lt;/span&gt; &lt;span class="nt"&gt;-disable-O0-optnone&lt;/span&gt; &lt;span class="nt"&gt;-fno-discard-value-names&lt;/span&gt; matxAdd2D.cpp &lt;span class="nt"&gt;-o&lt;/span&gt; matxAdd2D_raw.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We compile to LLVM IR with no optimisations. The &lt;code&gt;-disable-O0-optnone&lt;/code&gt; flag is essential because without it, &lt;code&gt;-O0&lt;/code&gt; stamps every function with &lt;code&gt;optnone&lt;/code&gt;, which then blocks all subsequent &lt;code&gt;opt&lt;/code&gt; passes from running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Canonicalising the IR to give the loops structure:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'mem2reg,loop-simplify,loop-rotate'&lt;/span&gt; matxAdd2D_raw.ll &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; matxAdd2D_canonical.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We clean up the loop shape, promoting stack variables to SSA registers, giving each loop a single clean preheader and latch, and rotating it into the form the vectoriser expects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Generating a binary out of our pass and the vectoriser:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-load-pass-plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./build/libMyPass.so &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-pass,loop-vectorize"&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; matxAdd2D_canonical.ll &lt;span class="nt"&gt;-o&lt;/span&gt; matxAdd2D_vectorized.ll
clang++ &lt;span class="nt"&gt;-O0&lt;/span&gt; matxAdd2D_vectorized.ll &lt;span class="nt"&gt;-o&lt;/span&gt; runWithPass2D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Our pass runs first, finding the inner loop and attaching &lt;code&gt;!llvm.loop.vectorize.enable = true&lt;/code&gt; to its latch branch.&lt;/li&gt;
&lt;li&gt;Then LLVM's built-in vectoriser runs immediately after, reads that hint, and rewrites the loop to use SIMD. &lt;/li&gt;
&lt;li&gt;The vectorised IR gets compiled to a binary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9d616o77g9z12eelbtsv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9d616o77g9z12eelbtsv.png" alt=" " width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) &lt;u&gt;Generating the binary without our pass:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clang++ &lt;span class="nt"&gt;-O0&lt;/span&gt; matxAdd2D_canonical.ll &lt;span class="nt"&gt;-o&lt;/span&gt; runWithoutPass2D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here, we compile the canonical IR directly without invoking the vectoriser, so the loop remains scalar.&lt;/li&gt;
&lt;li&gt;Each floating-point instruction operates on one scalar value instead of a vector containing multiple values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;The result:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./runWithPass2D    → 271 ms
./runWithoutPass2D → 1134 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4hjmtoaolwpukrzyejc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq4hjmtoaolwpukrzyejc.png" alt=" " width="800" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fabkc3wmw1andz3m90w1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fabkc3wmw1andz3m90w1l.png" alt=" " width="800" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Four times faster! On a billion floating point additions, the difference between scalar and vectorised is 890 milliseconds of wall-clock time.&lt;/li&gt;
&lt;li&gt;As a sanity check, I ran it with &lt;code&gt;-O3&lt;/code&gt;, which clocks in at 140ms.&lt;/li&gt;
&lt;li&gt;It is obviously much faster because it runs 50+ passes including everything we did and more. But the point was never to beat &lt;code&gt;-O3&lt;/code&gt;, and was instead to understand one piece of what's inside it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clang++ &lt;span class="nt"&gt;-O3&lt;/span&gt; matxAdd2D.cpp &lt;span class="nt"&gt;-o&lt;/span&gt; runO3
./runO3 → 140 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbml3nr5v9pikdss2mz0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbml3nr5v9pikdss2mz0s.png" alt=" " width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Understanding the Assembly&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To understand what's actually happening, I dumped both to assembly and grepped for floating point and vector instructions:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) &lt;u&gt;Without the pass:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"fadd|fmul|v[0-9]|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;4s|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;2s"&lt;/span&gt; matxAdd2D_plain.s | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"cfi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight armasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;fadd&lt;/span&gt; &lt;span class="nb"&gt;s0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;s0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;s1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsr51fi28cbqhfkhs09s2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsr51fi28cbqhfkhs09s2.png" alt=" " width="800" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here, one instruction repeats a billion times. &lt;/li&gt;
&lt;li&gt;Both &lt;code&gt;s0&lt;/code&gt; and &lt;code&gt;s1&lt;/code&gt; hold exactly one float. &lt;/li&gt;
&lt;li&gt;We just add them, store the result, and move to the next element, one at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;With pass:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"fadd|fmul|v[0-9]|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;4s|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;2s"&lt;/span&gt; matxAdd2D_vectorized.s | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"cfi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight armasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;fadd&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v3&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v7&lt;/span&gt;
&lt;span class="nl"&gt;fadd&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v2&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v6&lt;/span&gt;
&lt;span class="nl"&gt;fadd&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;
&lt;span class="nl"&gt;fadd&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v4&lt;/span&gt;
&lt;span class="nl"&gt;fadd&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v1&lt;/span&gt;
&lt;span class="nl"&gt;fadd&lt;/span&gt; &lt;span class="nb"&gt;s0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;s0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;s1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feul0fybnfjl66n6ztbof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feul0fybnfjl66n6ztbof.png" alt=" " width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here, &lt;code&gt;v0&lt;/code&gt;–&lt;code&gt;v7&lt;/code&gt; are 128-bit NEON registers, each holding 4 floats (32-bit) packed side by side. 

&lt;ul&gt;
&lt;li&gt;NEON registers are special vector registers in ARM CPUs that let the processor operate on multiple pieces of data at the same time, hence enabling SIMD.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.4s&lt;/code&gt; means "perform this on 4 singles at once." &lt;/li&gt;
&lt;li&gt;The vectoriser also unrolled the loop (four chunks of 4 per iteration, so 16 elements per pass through the loop):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v0 = [ C[j+0]  | C[j+1]  | C[j+2]  | C[j+3]  ]
v1 = [ C[j+4]  | C[j+5]  | C[j+6]  | C[j+7]  ]
v2 = [ C[j+8]  | C[j+9]  | C[j+10] | C[j+11] ]
v3 = [ C[j+12] | C[j+13] | C[j+14] | C[j+15] ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;All four additions happen in parallel. With the pass, we have about ~62.5 million iterations of 16 elements each.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;fadd s0, s0, s1&lt;/code&gt; at the end is the scalar tail, handling leftover elements when the count isn't a clean multiple of 16. &lt;/li&gt;
&lt;li&gt;Each row has 1000 elements: 62 full NEON chunks of 16, plus 8 leftovers. The scalar tail handles those 8.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;The other instructions in the vectorised assembly:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight armasm"&gt;&lt;code&gt;&lt;span class="nl"&gt;movi&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v0&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nl"&gt;dup&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt;  &lt;span class="nb"&gt;v1&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;w8&lt;/span&gt;
&lt;span class="nl"&gt;add&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt;  &lt;span class="nb"&gt;v4&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v1&lt;/span&gt;
&lt;span class="nl"&gt;scvtf&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt; &lt;span class="nb"&gt;v17&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v17&lt;/span&gt;
&lt;span class="nl"&gt;mul&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nl"&gt;4s&lt;/span&gt;  &lt;span class="nb"&gt;v5&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;These aren't from &lt;code&gt;matrix_add&lt;/code&gt;. They're from the initialisation loops in &lt;code&gt;main&lt;/code&gt; that fill A and B with values. &lt;/li&gt;
&lt;li&gt;Our pass practically ran on every function it visited, so the vectoriser rewrote the setup code too. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;scvtf&lt;/code&gt; converts integers to floats 4 at a time. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mul.4s&lt;/code&gt; computes &lt;code&gt;i * j&lt;/code&gt; for four index pairs simultaneously. &lt;/li&gt;
&lt;li&gt;The hint helped perform vectorisation everywhere, not just in &lt;code&gt;matrix_add&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Another remark in the pass output:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remark: &amp;lt;unknown&amp;gt;:0:0: loop not vectorized: call instruction cannot be vectorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;That's the outer &lt;code&gt;for (int t = 0; t &amp;lt; 1000; t++)&lt;/code&gt; timing loop in &lt;code&gt;main&lt;/code&gt;, which calls &lt;code&gt;matrix_add&lt;/code&gt;. This is, from the vectoriser's perspective, an external function call. &lt;/li&gt;
&lt;li&gt;Like we saw last time, a function call cannot be split across SIMD lanes, so the vectoriser declined that one, because our hint on the inner loop actually mattered more.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1134 to 271. I keep staring at those numbers. It was bewildering when I saw it, even though part of me was anticipating something significant.&lt;/p&gt;

&lt;p&gt;The thing is... the hardware was always capable of it. The CPU had NEON registers sitting there the entire time, perfectly capable of adding 4 floats at once. It just wasn't being told to. One tiny tag on the loop and suddenly the machine restructures an entire computation.&lt;/p&gt;

&lt;p&gt;So much of performance is just permission. The potential is always there. Al it took was someone to say, "go ahead."&lt;/p&gt;

&lt;p&gt;Anyhow, wow. I wrote my custom LLVM pass. Feels pretty great, honestly! I hope to begin another compiler adventure soon because I've absolutely been loving learning about all that goes on behind-the-scenes when we hit run.&lt;/p&gt;

&lt;p&gt;Until then, off I go to watch more of Agent Kim Reactivated! 😍&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ay4dup1ql04w94b6qor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ay4dup1ql04w94b6qor.png" alt=" " width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>computerscience</category>
      <category>performance</category>
      <category>cpp</category>
    </item>
    <item>
      <title>LLVM #11 — Attaching Metadata to Loops</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 14 Jul 2026 13:44:03 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-11-attaching-metadata-to-loops-5eca</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-11-attaching-metadata-to-loops-5eca</guid>
      <description>&lt;p&gt;Earlier, we taught the pass to find loops. Then we practiced constructing a metadata node in isolation. Today, we combine both by taking the loop we found, taking the metadata node we built, and actually staple one onto the other. Here, the pass changes the IR instead of merely observing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/LLVM-VectorisationHintPass/commit/9dc0a659c359af561b3bb055062283c7971feab4" rel="noopener noreferrer"&gt;Commit 9dc0a65&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59xfdkt58d866eamo3d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F59xfdkt58d866eamo3d3.png" alt=" " width="799" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we can see in the IR, in &lt;code&gt;br i1 %cmp, label %for.body, label %for.end, !llvm.loop !6&lt;/code&gt;, the &lt;code&gt;!llvm.loop !6&lt;/code&gt; at the end is metadata attached to a specific branch instruction at the latch block.&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Latch Blocks and Branch Instructions:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A latch block closes the loop. In our case, that's &lt;code&gt;for.inc&lt;/code&gt;, the last stop before the loop either repeats or ends. It contains the back-edge: the "go back to the top" jump.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;for.inc&lt;/code&gt; increments &lt;code&gt;i&lt;/code&gt;, checks whether &lt;code&gt;i &amp;lt; 10&lt;/code&gt;, and then either jumps back to &lt;code&gt;for.body&lt;/code&gt; or falls out to &lt;code&gt;for.end&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Simply put, a latch is any block that has the loop header as one of its successors. Simple &lt;code&gt;for&lt;/code&gt; loops have exactly one, but complex loops with multiple &lt;code&gt;break&lt;/code&gt; or &lt;code&gt;continue&lt;/code&gt; paths can have many, in which case &lt;code&gt;getLoopLatch()&lt;/code&gt; returns a &lt;code&gt;nullptr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In memory, the branch instruction is a C++ object with fields for the condition being checked, where to jump if true/false, and a metadata slot.&lt;/li&gt;
&lt;li&gt;Right now, that metadata slot holds a pointer to the &lt;code&gt;mustprogress&lt;/code&gt; node clang already put there. When we call &lt;code&gt;BI-&amp;gt;setMetadata("llvm.loop", LoopID)&lt;/code&gt;, we're filling that slot with a pointer to our new combined node. Not replacing, but more like appending, because we cannot just overwrite the existing &lt;code&gt;must progress&lt;/code&gt; node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Getting the latch and its branch instruction:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For every loop found, we first identify the latch block:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BasicBlock&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Latch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getLoopLatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Latch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;outs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"  No single latch found, skipping.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getLoopLatch()&lt;/code&gt; can return null if the loop has multiple latches, so we guard against that before proceeding.&lt;/li&gt;
&lt;li&gt;Then we fetch the branch instruction at the end of the latch block:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BranchInst&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;BI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dyn_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BranchInst&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Latch&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getTerminator&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;BI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;outs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"  Latch terminator is not a branch, skipping.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getTerminator()&lt;/code&gt; returns a generic instruction type, so &lt;code&gt;dyn_cast&amp;lt;BranchInst&amp;gt;&lt;/code&gt; safely converts it. If it isn't a branch, we get null instead of a crash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Building the hint tuple:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same as &lt;a href="https://dev.to/lahari_tenneti_4a8a082e9c/llvm-10-constructing-mdnodes-4lb2"&gt;last time&lt;/a&gt;, we build the vectorize hint:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MDString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;HintName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDString&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"llvm.loop.vectorize.enable"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ConstantInt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TrueVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConstantInt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getInt1Ty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ValueAsMetadata&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TrueMD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ValueAsMetadata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrueVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;VectorizeHint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;HintName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TrueMD&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) &lt;u&gt;Reading what's already on the branch:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;SmallVector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MDs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ExistingMD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"llvm.loop"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MDs&lt;/code&gt; is just an empty list for now.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BI-&amp;gt;getMetadata("llvm.loop")&lt;/code&gt; asks the branch: "do you already have loop metadata?" In our case, clang put &lt;code&gt;mustprogress&lt;/code&gt; there, so &lt;code&gt;ExistingMD&lt;/code&gt; will point to that.&lt;/li&gt;
&lt;li&gt;We then loop through the existing node's operands and collect them:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExistingMD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ExistingMD&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getNumOperands&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MDs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ExistingMD&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getOperand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We start at &lt;code&gt;i = 1&lt;/code&gt; because operand 0 is always the self-reference. We don't copy it. Our new node will have its own.&lt;/li&gt;
&lt;li&gt;Then we push our new hint in:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MDs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VectorizeHint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MDs&lt;/code&gt; now has both: &lt;code&gt;mustprogress&lt;/code&gt; and the vectorize hint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Building the combined self-referential node:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same as last time, just with more content now:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getTemporary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;SmallVector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;AllOps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;AllOps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;AllOps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MDs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;MDs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;LoopID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllOps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;replaceAllUsesWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;deleteTemporary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AllOps&lt;/code&gt; is the complete list with itself, &lt;code&gt;mustprogress&lt;/code&gt;, and the vectorize hint. The result:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;itself&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mustprogress_hint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vectorize_hint&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6) &lt;u&gt;Attaching it:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"llvm.loop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This line updates the branch instruction's metadata slot to point at the new combined node. Everything before this was preparation. This is the moment the IR actually changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;7) &lt;u&gt;Informing the Pass Manager:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PreservedAnalyses&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Changed from &lt;code&gt;::all()&lt;/code&gt; to &lt;code&gt;::none()&lt;/code&gt;, telling LLVM to recompute whatever it needs rather than trusting stale cached results. This is because we modified the IR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahvimlt7jjh0npaz51mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fahvimlt7jjh0npaz51mn.png" alt=" " width="800" height="40"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Verifying it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After rebuilding and running the pass, we check the output IR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;for.inc:&lt;/span&gt;
  &lt;span class="nv"&gt;%inc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;add&lt;/span&gt; &lt;span class="k"&gt;nsw&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%i.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="nv"&gt;%cmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;icmp&lt;/span&gt; &lt;span class="k"&gt;slt&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%cmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%for.body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%for.end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;!llvm.loop&lt;/span&gt; &lt;span class="nv"&gt;!6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphx15hw9t9s5giql1nok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphx15hw9t9s5giql1nok.png" alt=" " width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And at the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;!6&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt; &lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nv"&gt;!6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;!7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;!8&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;!7&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nv"&gt;!"llvm.loop.mustprogress"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;!8&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nv"&gt;!"llvm.loop.vectorize.enable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiief8f6jtmyji7wlzde8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiief8f6jtmyji7wlzde8.png" alt=" " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before our pass ran, &lt;code&gt;!6&lt;/code&gt; had two operands. Now it has three. &lt;code&gt;!8&lt;/code&gt; is new. Our pass merged the existing hint with the new one rather than overwriting it.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Testing with the vectoriser:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Running both passes in sequence (ours first, then LLVM's built-in loop vectoriser) gives us this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remark: &amp;lt;unknown&amp;gt;:0:0: loop not vectorized: call instruction cannot be vectorized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foylm3vqjyhv0c9pnkwjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foylm3vqjyhv0c9pnkwjq.png" alt=" " width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is exactly the expected result. The vectoriser now explicitly considers the loop because of our hint. It simply declined because our loop calls &lt;code&gt;printf&lt;/code&gt;, and you can't split a function call across SIMD lanes. The hint worked but the loop just isn't actually vectorisable with this test case.&lt;/p&gt;

&lt;p&gt;To see vectorisation happen properly, we need a loop doing pure arithmetic on an array, with no function calls.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Testing the hint pass with a matrix multiplication loop.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I don't think of myself as someone who procrastinates often, though perhaps everyone says that. I'm human too, and I postpone things more than I'd like. So it was of some solace to discover that even Marcus Aurelius lamented about this. Perhaps some struggles are timeless.&lt;/p&gt;

&lt;p&gt;Time, though, is peculiar. We experience it as a relentless march forward, with every second disappearing forever. A clock seems to mock us with its ticking. And yet its very shape suggests something different. Its hands always return, holding the promise of "again." Time feels both linear and cyclical at once: always moving forward, yet endlessly repeating itself.&lt;/p&gt;

&lt;p&gt;Whenever I find myself somewhere, whether familiar or new, I wonder what stood there a century ago. "What was it like here? Who lived here? What sights did they see?"&lt;/p&gt;

&lt;p&gt;A hospital near my home became a shopping mall. A place where countless stories ended became a place where new ones began: weddings, birthdays, celebrations, and the like. The walls changed. But maybe what the place ultimately &lt;em&gt;held&lt;/em&gt; never did. It remained a vessel for human lives, only in a different form.&lt;/p&gt;

&lt;p&gt;Go back another hundred years and perhaps where I'm sitting was nothing but a forest. Maybe a bird once perched on the very spot where I now sit, looking at the trees, assuming the greenery was all forever. It's amusing to think about whether permanence is a uniquely human fantasy, or whether every living thing mistakes the world it inherits, for a world that will always exist.&lt;/p&gt;

&lt;p&gt;The more I think about history, the less convinced I am that we are fundamentally different from those who came before. Did they look to the future with the same mixture of hope and confusion? Did they believe they were living through unprecedented change? Did they contemplate on their place in this world... on their role in this grand scheme? Every generation seems to ask the same questions, albeit through different lenses.&lt;/p&gt;

&lt;p&gt;And that leaves me wondering about something stranger still. If everything changes, from cities, forests, and buildings, to people and memories... who or what remembers it all? Who keeps the ledger of existence? Is there anything that witnesses every beginning and every ending without itself being altered? To whom is time nothing? Who doesn't have to wonder what it's really meant for? What ultimately remains?&lt;/p&gt;

&lt;p&gt;I hope to find answers one day. Tomorrow feels like a good day to start ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F016h6prlqzbpfxg63i1j.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F016h6prlqzbpfxg63i1j.jpeg" alt=" " width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #10 — Constructing Metadata Nodes</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Thu, 02 Jul 2026 18:03:43 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-10-constructing-mdnodes-4lb2</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-10-constructing-mdnodes-4lb2</guid>
      <description>&lt;p&gt;As of now, we're purely reading loops, but aren't changing anything about/in the IR.&lt;br&gt;
We have to attach 'vectorise this' tags and not merely read. In this post, we'll just be practicing the metadata node construction in isolation and not attach them to any loops detected.&lt;/p&gt;

&lt;p&gt;What I built: &lt;a href="https://github.com/laharitenneti/LLVM-VectorisationHintPass/commit/ad7ad71d014d09ac917a2eab15d90fda383cd407" rel="noopener noreferrer"&gt;Commit ad7ad71&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Metadata:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The metadata nodes in IR live in memory. So when we attach metadata to a branch, we're just setting a pointer.&lt;/li&gt;
&lt;li&gt;As we know, a module is a container for everything LLVM knows about a file.&lt;/li&gt;
&lt;li&gt;All these objects (like functions, basic blocks, instructions, types, constants, metadata, etc.) need to be stored somewhere in the computer's memory as LLVM works on them. That somewhere is the &lt;code&gt;LLVMContext&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;code&gt;LLVMContext&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a shared memory area that owns/manages all the fundamental building blocks. It is especially helpful with "interning."

&lt;ul&gt;
&lt;li&gt; When different functions use the same type object, they don't have their own copies as they point to the same object in the context.&lt;/li&gt;
&lt;li&gt;So if we create the same metadata node twice, LLVM checks the context's intern table and returns the existing one.&lt;/li&gt;
&lt;li&gt;It is important for any MDNode  call to take the context as an argument because we could have MDNodes created in a different context but being put in a different one. This can cause LLVM to crash or give corrupt IR.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;LLVMContext&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;This fetches the context the function belongs to, so the metadata node we attach belongs to the same memory area as the function it's being attached to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Creating hint operands:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MDString&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;HintName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDString&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"llvm.loop.vectorize.enable"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ConstantInt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TrueVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConstantInt&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getInt1Ty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ValueAsMetadata&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TrueMD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ValueAsMetadata&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrueVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This creates an LLVM-native string that can live inside a metadata node. LLVM looks for &lt;code&gt;"llvm.loop.vectorize.enable"&lt;/code&gt; before deciding whether or not to vectorise a loop.&lt;/li&gt;
&lt;li&gt;Then, we create the LLVM IR equivalent of the &lt;code&gt;bool&lt;/code&gt; type, so we can set it to true, hinting that the loop must be vectorised. We fetch the 1-bit integer type from the context and create an actual constant value of this type, holding the number 1.&lt;/li&gt;
&lt;li&gt;Because &lt;code&gt;MDNode::get()&lt;/code&gt; expects its operands to be &lt;code&gt;Metadata*&lt;/code&gt; and &lt;code&gt;ConstantInt&lt;/code&gt; is a &lt;code&gt;Value*&lt;/code&gt;, it literally cannot be passed where &lt;code&gt;Metadata*&lt;/code&gt; is expected. &lt;/li&gt;
&lt;li&gt;So we use &lt;code&gt;ValueAsMetadata&lt;/code&gt; as the bridge class to promote &lt;code&gt;Value*&lt;/code&gt; into a &lt;code&gt;Metadata*&lt;/code&gt; so the type system is satisfied.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Building the node:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We now assemble the metadata node, which is a list containing two operands: the name string and the true value.&lt;/li&gt;
&lt;li&gt;It results in the in-memory equivalent of the IR text:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nv"&gt;!"llvm.loop.vectorize.enable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) &lt;u&gt;Self-referencing:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLVM needs a way to uniquely identify an exact loop using a metadata node. And for this, the loop ID's node must list itself as its own first operand, like so:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;!x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;!{&lt;/span&gt;&lt;span class="nv"&gt;!x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;!y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This self-referencing helps LLVM distinguish a "loop ID node" from any other ordinary metadata node.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getTemporary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;LoopID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VectorizeHint&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;replaceAllUsesWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;MDNode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;deleteTemporary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TempLoopID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We first create an empty (temporary) placeholder node with no real operands.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;release()&lt;/code&gt; is for manually deleting a smart pointer, that would otherwise have been deleted automatically through &lt;code&gt;getTemporary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then, we build the actual loop ID node, with its first operand being the placeholder node.&lt;/li&gt;
&lt;li&gt;We then replace all instances of the temporary node with the newly created "actual" &lt;code&gt;LoopID&lt;/code&gt; node. This is how we create a self-referencing node.&lt;/li&gt;
&lt;li&gt;Finally, since we took manual ownership of the placeholder, we destroy it to prevent memory leakage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6sk68063ivyfwa7sd3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm6sk68063ivyfwa7sd3q.png" alt=" " width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Attach the created metadata to the actual for block.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
I have the cutest little peace lily. Her name is Lulu, and she brings me immense joy. I read and sing to her, play the piano for her sometimes, and tell her just about everything. As crazy as it might sound, I think she understands and responds. I once read that plants respond incredibly well to chatting and affection in general. And I totally believe it. After a long day's work, being welcomed by the greenest and freshest of leaves, and the prettiest of flowers, sure does bring one a lot of peace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fceon372adglskelmwukl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fceon372adglskelmwukl.png" alt=" " width="576" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #9 — Finding Loops</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Sat, 27 Jun 2026 10:03:07 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-9-finding-loops-c7l</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-9-finding-loops-c7l</guid>
      <description>&lt;p&gt;Now that we have the basic skeleton for the pass ready, we need to add loop-detecting logic to it.&lt;/p&gt;

&lt;p&gt;What I built: &lt;a href="https://github.com/laharitenneti/LLVM-VectorisationHintPass/commits/main/?since=2026-06-26&amp;amp;until=2026-06-26" rel="noopener noreferrer"&gt;Commits 432455d and ddc46b3&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;br&gt;
1) &lt;u&gt;Using the FunctionAnalysisManager for detecting loops:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In our &lt;code&gt;run()&lt;/code&gt; function, we've declared the &lt;code&gt;FAM&lt;/code&gt;, but haven't really used it. We merely printed the function name that we detected.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;FAM&lt;/code&gt; either performs analyses fresh or fetches a cached result.&lt;/li&gt;
&lt;li&gt;LLVM already knows about every loop (where a loop is, nested loops, loop headers, latches, etc.) in a function through &lt;code&gt;LoopInfo&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;LoopInfo&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;LI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FAM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoopAnalysis&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LI&lt;/code&gt; now hols every loop in this function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Fetching top-level loops:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We iterate over the function to fetch top-level loops.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Loop *L : LI) {&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;        outs() &amp;lt;&amp;lt; "&lt;/span&gt;&lt;span class="n"&gt;Found&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;" &amp;lt;&amp;lt; L-&amp;gt;getHeader()-&amp;gt;getName() &amp;lt;&amp;lt; "&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="s"&gt;";&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Each &lt;code&gt;Loop*&lt;/code&gt; represents every top-level loop in the function.&lt;/li&gt;
&lt;li&gt;We can later even fetch nested loops through &lt;code&gt;getSubloops()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Testing it:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can write a loop in C++.&lt;code&gt;test_loop.cpp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqc6j0x9nbi1bfrkqdla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqc6j0x9nbi1bfrkqdla.png" alt=" " width="799" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then, we convert it to LLVM IR (raw, unmodified).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clang &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-emit-llvm&lt;/span&gt; &lt;span class="nt"&gt;-O0&lt;/span&gt; &lt;span class="nt"&gt;-Xclang&lt;/span&gt; &lt;span class="nt"&gt;-disable-O0-optnone&lt;/span&gt; &lt;span class="nt"&gt;-fno-discard-value-names&lt;/span&gt; test_loop.cpp &lt;span class="nt"&gt;-o&lt;/span&gt; test_loop_raw.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We then run three canonicalisation passes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mem2reg&lt;/code&gt;: To promote stack variables into SSA registers with phi nodes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loop-simplify&lt;/code&gt;: Guarantees that every loop has one preheader, one latch, and dedicated exit blocks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loop-rotate&lt;/code&gt;: Converts &lt;code&gt;for.cond&lt;/code&gt; into &lt;code&gt;for.body&lt;/code&gt;, which is the form the vectoriser needs.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'mem2reg,loop-simplify,loop-rotate'&lt;/span&gt; test_loop_raw.ll &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; test_loop_canonical.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;test_loop_canonical.ll&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz87ji78zrt360koht9a0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz87ji78zrt360koht9a0.png" alt=" " width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We then link this &lt;code&gt;.ll&lt;/code&gt; file to our pass with &lt;code&gt;opt&lt;/code&gt; to check if the loops have been detected or not.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-load-pass-plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./libMyPass.so &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-pass &lt;span class="nt"&gt;-disable-output&lt;/span&gt; test_loop_canonical.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmm07evm5mzezadpb3kge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmm07evm5mzezadpb3kge.png" alt=" " width="798" height="65"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Figuring out LLVM's metadata API.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
The weather is beauuuuutiful today. I absolutely love the monsoon. It's 700% worth waiting for after a long and testing summer. And nothing beats having some piping hot chai while enjoying the breeze. I'm in the company of two of my favourite gals as I finish today's work and life feels fiiiiine! 👌&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbv5p4ovb1pe2sqauf6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqbv5p4ovb1pe2sqauf6n.png" alt=" " width="800" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
      <category>llvm</category>
    </item>
    <item>
      <title>LLVM #8 — Setting Up Infrastructure for a Custom Pass</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Thu, 25 Jun 2026 19:14:07 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-8-setting-up-infrastructure-for-a-custom-pass-13dj</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-8-setting-up-infrastructure-for-a-custom-pass-13dj</guid>
      <description>&lt;p&gt;Welcome, welcome, welcome to the next leg of my LLVM journey!&lt;/p&gt;

&lt;p&gt;This is where I get into the "proper" backend of compilation. I know I've said that before, but for real. Proper backend now because at its core, Kaleidoscope was an extended front-end. Although I did do things like emitting object code and setting up JIT execution engines, Kaleidoscope took files, parsed them into ASTs, generated LLVM IR, &lt;strong&gt;and then handed it over to LLVM's pre-built infrastructure.&lt;/strong&gt; That's pretty nice, but now I'm going to interact with LLVM's core optimisation engine itself (which is sort of like the middle-end, but humour me).&lt;/p&gt;

&lt;p&gt;My goal for this leg is to write a 'Loop Vectorisation Hint' pass, which should be able to look at control flow and inject metadata hints that tell LLVM how to optimise loops by vectorising them through SIMD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/LLVM-VectorisationHintPass/commit/9da4d33fe350b95ed141853bdb8b41ceb96fff73" rel="noopener noreferrer"&gt;Commit 9da4d33&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The What and Why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right now, our CPU adds numbers one at a time. &lt;/li&gt;
&lt;li&gt;Imagine we had to write a simple loop in C++ to add thousands of numbers together.&lt;/li&gt;
&lt;li&gt;Modern CPUs can do the same operation on multiple numbers at once (like 4 or 8 pairs) using special wide instructions like SIMD, which greatly help with speed.&lt;/li&gt;
&lt;li&gt;LLVM has an inbuilt "auto-vectoriser" that looks at our loops and automatically tries to rewrite them to use these SIMD instead of doing one element at a time.&lt;/li&gt;
&lt;li&gt;But the auto-vectoriser is also cautious. It only vectorises a loop if it can prove it's safe to do so.

&lt;ul&gt;
&lt;li&gt;Ex: If a loop calculates a value that depends directly on the result of the previous iteration, the operations cannot be run in parallel&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Real code is often messy enough that LLVM can't exactly prove safety, even when the loop is okay enough to vectorise. So LLVM sort of backs off, leaving the loop slow.&lt;/li&gt;
&lt;li&gt;Now the good news is that LLVM lets us manually tell it to do something using metadata. Normally, a human programmer adds this by writing little notes directly into their C/C++ source code.&lt;/li&gt;
&lt;li&gt;So instead of us manually annotating the source code loop by loop, this pass automatically scans compiled code and attaches these "go-ahead and vectorise it" tags to loops.&lt;/li&gt;
&lt;li&gt;A little word of caution. There's a reason the safety-check exists. I'm doing this merely for learning how passes work. Production grade optimisation must absolutely not be like this.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because LLVM is a massive library in C++, it has all the core data structures a compiler needs.&lt;/li&gt;
&lt;li&gt;To write a tool that optimises code, we need to write a small C++ program (a pass) that can work with LLVM. &lt;/li&gt;
&lt;li&gt;As I'd mentioned &lt;a href="https://dev.to/lahari_tenneti_4a8a082e9c/llvm-2-optimiser-support-jit-compilation-15hm#:~:text=A%20pass%20is%20simply%20one%20%22go%22%20over%20the%20IR%20that%20looks%20for%20a%20specific%20pattern%20and%20rewrites%20it.%20The%20FunctionPassManager%20contains%20a%20sequence%20of%20passes%20and%20runs%20them%20over%20each%20function%20in%20that%20order%2C%20passing%20the%20output%20of%20one%20as%20input%20to%20the%20next."&gt;here&lt;/a&gt;, the Pass Manager determines how the passes must be run on the generated LLVM IR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) &lt;u&gt;Setting it up:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Like before (&lt;a href="https://dev.to/lahari_tenneti_4a8a082e9c/llvm-introduction-and-setup-4c3c"&gt;when we set up LLVM&lt;/a&gt;), we create a CMake to declare our requirements and automatically write a blueprint telling our computer how exactly to compile our code.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;cmake_minimum_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;VERSION 3.15&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyVectorPass&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;find_package&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;LLVM REQUIRED CONFIG&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;STATUS &lt;span class="s2"&gt;"Found LLVM &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_PACKAGE_VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;STATUS &lt;span class="s2"&gt;"Using LLVMConfig.cmake in: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;include_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_INCLUDE_DIRS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;link_directories&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_LIBRARY_DIRS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;separate_arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;LLVM_DEFINITIONS_LIST NATIVE_COMMAND &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_DEFINITIONS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;add_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LLVM_DEFINITIONS_LIST&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;add_library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyPass MODULE MyPass.cpp&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;APPLE&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyPass PRIVATE &lt;span class="s2"&gt;"-undefined"&lt;/span&gt; &lt;span class="s2"&gt;"dynamic_lookup"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nb"&gt;target_compile_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyPass PRIVATE cxx_std_17&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This time, instead of telling CMake to make a standalone application executable (like we did for Kaleidoscope), we tell it to build a dynamic &lt;code&gt;MODULE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Executables have their own &lt;code&gt;main()&lt;/code&gt;. When run, the OS starts executing from the very first line of &lt;code&gt;main()&lt;/code&gt;, and our program controls the entire CPU process.&lt;/li&gt;
&lt;li&gt;Back then, CMake had to fetch the massive LLVM libraries and physically inject them into our binary so out compiler had its "brains."&lt;/li&gt;
&lt;li&gt;Now, to connect our pass to the LLVM infrastructure, we compile our code into a &lt;strong&gt;Plugin&lt;/strong&gt; (a shared module like a &lt;code&gt;.so&lt;/code&gt; or a &lt;code&gt;.dylib&lt;/code&gt;), which is basically compiled code without a &lt;code&gt;main()&lt;/code&gt;, so it can't really run by itself.&lt;/li&gt;
&lt;li&gt;So we use LLVM's command line optimisation tool called &lt;code&gt;opt&lt;/code&gt;, which when run in the terminal, reads our instruction flag (&lt;code&gt;-load-pass-plugin=./libMyPass.so&lt;/code&gt;), reached out into our folder, opens our module, and injects our code (the pass) directly into its own running process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;The skeleton&lt;/u&gt; (&lt;code&gt;MyPass.cpp&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To ensure our plugin can talk to the modern LLVM Pass Manager infrastructure, I wrote a basic C++ boilerplate. &lt;/li&gt;
&lt;li&gt;It doesn't optimise anything yet and just registers a callback under the name "my-pass". - Whenever it sees a function, it receives it, fetches its name using F.getName(), and prints it out.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"llvm/IR/PassManager.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"llvm/Passes/PassPlugin.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"llvm/Passes/PassBuilder.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"llvm/Support/raw_ostream.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;llvm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;MyPass&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;PassInfoMixin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyPass&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PreservedAnalyses&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FunctionAnalysisManager&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FAM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//printing the fxn name&lt;/span&gt;
      &lt;span class="n"&gt;outs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Visiting function: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PreservedAnalyses&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//registering the pass so 'opt' can find it by name&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="n"&gt;LLVM_ATTRIBUTE_WEAK&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;llvm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PassPluginLibraryInfo&lt;/span&gt; &lt;span class="nf"&gt;llvmGetPassPluginInfo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LLVM_PLUGIN_API_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MyPass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LLVM_VERSION_STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="n"&gt;PassBuilder&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;PB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registerPipelineParsingCallback&lt;/span&gt;&lt;span class="p"&gt;([](&lt;/span&gt;&lt;span class="n"&gt;StringRef&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FunctionPassManager&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FPM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ArrayRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PassBuilder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PipelineElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"my-pass"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;FPM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) &lt;u&gt;Verifying the plugin:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I created an LLVM IR file (&lt;code&gt;test.ll&lt;/code&gt;) with two empty functions, &lt;code&gt;@foo&lt;/code&gt; and &lt;code&gt;@bar&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="vg"&gt;@foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;entry:&lt;/span&gt;
    &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="vg"&gt;@bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;entry:&lt;/span&gt;
    &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Then I built it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;build &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;build
cmake &lt;span class="nt"&gt;-G&lt;/span&gt; Ninja &lt;span class="nt"&gt;-DLLVM_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;llvm-config &lt;span class="nt"&gt;--cmakedir&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; ..
ninja
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To test it, I fed my dummy IR file into LLVM's &lt;code&gt;opt&lt;/code&gt; tool and loaded the new shared module:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-load-pass-plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./libMyPass.so &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-pass &lt;span class="nt"&gt;-disable-output&lt;/span&gt; ../test.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3owxmksh9yy9gn236g4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3owxmksh9yy9gn236g4i.png" alt=" " width="793" height="54"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Linker problem:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project compiled to 50% when I ran the ninja build command, after which the linker gave me this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Undefined symbols for architecture arm64:
"llvm::outs()", referenced from: ...
"llvm::Value::getName() const", referenced from: ...
ld: symbol(s) not found for architecture arm64
clang++: error: linker command failed with exit code 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;When we compile a standard executable program on any OS, the linker's job is to make sure every single function call in our code maps to a concrete definition. &lt;/li&gt;
&lt;li&gt;If we call &lt;code&gt;llvm::outs()&lt;/code&gt;, the linker will go searching through the static libraries or &lt;code&gt;.dylib&lt;/code&gt; files on our computer, find the compiled binary code for &lt;code&gt;outs()&lt;/code&gt;, and either inject it into our executable or explicitly link against a library that contains it. &lt;/li&gt;
&lt;li&gt;If it can't find it, it halts compilation with an &lt;code&gt;Undefined symbols&lt;/code&gt; error.&lt;/li&gt;
&lt;li&gt;Our pass is a plugin designed to be loaded into the host program (&lt;code&gt;opt&lt;/code&gt;), which already contains code for things like &lt;code&gt;llvm::outs()&lt;/code&gt;, &lt;code&gt;llvm::Value::getName()&lt;/code&gt;, and the rest of the LLVM architecture.&lt;/li&gt;
&lt;li&gt;If our linked forced our little plugin to statically include those giant LLVM functions, the plugin file would be excessively large and with duplicate code!&lt;/li&gt;
&lt;li&gt;macOS' linker &lt;code&gt;ld&lt;/code&gt; demands that all symbols must be resolved at compile time, even for dynamic modules. When it saw &lt;code&gt;outs()&lt;/code&gt; in our &lt;code&gt;MyPass.cpp&lt;/code&gt; and realised we weren't actively injecting LLVM's massive engine into our plugin, it went into error mode.&lt;/li&gt;
&lt;li&gt;On linux, the linker is content with leaving symbols unresolved while building the shared library (at compile time). It assumes that at runtime, whatever loads the library will also provide the missing functions.&lt;/li&gt;
&lt;li&gt;So we add a flag in our CMake for the macOS linker to switch to Linux-style behaviour, and treat unresolved symbols at compile time as normal, and wait for &lt;code&gt;opt&lt;/code&gt; to provide the required binaries at runtime:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cmake"&gt;&lt;code&gt;&lt;span class="nb"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;APPLE&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;target_link_options&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;MyPass PRIVATE &lt;span class="s2"&gt;"-undefined"&lt;/span&gt; &lt;span class="s2"&gt;"dynamic_lookup"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;endif&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;code&gt;.dylib&lt;/code&gt; &lt;u&gt;vs&lt;/u&gt; &lt;code&gt;.so&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This didn't work:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-load-pass-plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./MyPass.dylib &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-pass &lt;span class="nt"&gt;-disable-output&lt;/span&gt; ../test.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;So I had to switch to this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opt &lt;span class="nt"&gt;-load-pass-plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./libMyPass.so &lt;span class="nt"&gt;-passes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-pass &lt;span class="nt"&gt;-disable-output&lt;/span&gt; ../test.ll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is because a shared library (&lt;code&gt;.dylib&lt;/code&gt; on Mac, &lt;code&gt;.so&lt;/code&gt; on Linux) is meant for a program to link against at compile time.&lt;/li&gt;
&lt;li&gt;But a shared module (&lt;code&gt;.so&lt;/code&gt; on both Linux and Mac) is a library that is specifically meant to be loaded at runtime.&lt;/li&gt;
&lt;li&gt;Because LLVM is designed to be cross-platform and work exactly the same way across Linux, Windows, and macOS, its build conventions favour more universal defaults.&lt;/li&gt;
&lt;li&gt;So when CMake processes &lt;code&gt;add_library(MyPass MODULE ...)&lt;/code&gt;, it follows the platform-independent rule for a plugin module rather than the native macOS rule for a generic system library. &lt;/li&gt;
&lt;li&gt;Hence, even if we are on an ARM64 Mac, CMake intentionally outputs &lt;code&gt;libMyPass.so&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Looking for loops!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
It's past midnight as I write this. And I'm supremely satisfied I finished the day's task. But I can't for the life of me decide whether I'm an early bird or late owl. I do well during both times. My productivity is independent of the time of the day, because if I sit down to finish something, I finish it. Not like a flex (though it can be considered one, hehe). The only thing I probably need is some way to structure this... persistence? I've tried timetables, but I don't seem to stick to them for too long. I sometimes envy people in institutions like the armed forces because their consistency is absolutely insane. To put it very simply, I just need to finish my work before sunset and sleep on time. But for now, &lt;em&gt;buonanotte&lt;/em&gt; (and &lt;em&gt;buongiorno&lt;/em&gt;)!&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>llvm</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #7 — Debugging!</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 23 Jun 2026 05:51:03 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-7-debugging-51a3</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-7-debugging-51a3</guid>
      <description>&lt;p&gt;We've built a language that lexes, parses, generates IR, optimises, JITs, and now even emits object code. But how do we know when something goes wrong in Kaleidoscope?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/41ba81d3a03e4e93e2d4f79452fb117d6418c545" rel="noopener noreferrer"&gt;Commit 41ba81d&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The problem:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we open our compiled binary inside a debugger like &lt;code&gt;lldb&lt;/code&gt; or &lt;code&gt;gdb&lt;/code&gt;, it sees raw machine code bytes sitting at memory addresses.&lt;/li&gt;
&lt;li&gt;It has no idea what line of Kaleidoscope source, or what variable name produced any of it.&lt;/li&gt;
&lt;li&gt;Basically, the debugger is fluent in assembly, but doesn't speak Kaleidoscope.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;The solution:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source-level debugging! &lt;/li&gt;
&lt;li&gt;It works by creating a mapping (basically metadata) between the machine code and the original source. &lt;/li&gt;
&lt;li&gt;In LLVM, this metadata is formatted using a global standard called DWARF, which includes:

&lt;ul&gt;
&lt;li&gt;A line table: Maps a CPU instruction address back to a specific file and line number.&lt;/li&gt;
&lt;li&gt;A variable map: Maps a memory address or CPU register to a variable name.&lt;/li&gt;
&lt;li&gt;A type registry: Tells the debugger what a chunk of memory actually represents (in our case, a double), so it can format the value sensibly instead of just printing raw bytes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Also, we turn off optimisation.

&lt;ul&gt;
&lt;li&gt;Tracing a bug back to a specific source line while looking at optimised code is difficult, for instructions get merged, reordered, and shared across what used to be separate statements.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;We can't use the JIT/REPL based approach here.

&lt;ul&gt;
&lt;li&gt;Debugging ephemeral code that exists only in memory is difficult, for debuggers like &lt;code&gt;lldb&lt;/code&gt; need a stable amd permanent file on disk they can open, inspect, and step through.&lt;/li&gt;
&lt;li&gt;So we go back to the script-file approach from the object code chapter (our &lt;code&gt;test.k&lt;/code&gt; file) rather than the REPL.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The overall plan is to use &lt;code&gt;DIBuilder&lt;/code&gt; to insert explicit hooks into the lexer, parser, and AST, so that every piece of generated IR carries a tag back to its source location.&lt;/li&gt;
&lt;li&gt;Then we can open the compiled binary in a debugger, set breakpoints, and step through Kaleidoscope code line-by-line, as if it were any other compiled language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;Some key concepts:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DIBuilder&lt;/code&gt;: The engine that creates DWARF debug nodes. It is the debug equivalent of &lt;code&gt;IRBuilder&lt;/code&gt;. Where &lt;code&gt;IRBuilder&lt;/code&gt; emits instructions, &lt;code&gt;DIBuilder&lt;/code&gt; emits metadata describing those instructions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CompileUnit&lt;/code&gt;: A structural node representing the entire source file (&lt;code&gt;test.k&lt;/code&gt;) being compiled. It holds global data like source language, directory path, compiler name, etc. This is DWARF's root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lexical Blocks:&lt;/strong&gt; This is a scope stack. Functions (and in principle, nested blocks) get pushed here so that variables and instructions know exactly which scope they belong to.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I did&lt;/strong&gt;&lt;br&gt;
1) &lt;u&gt;Tracking the lexer's location:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Previously, the lexer just called &lt;code&gt;getchar()&lt;/code&gt; and moved on, with no memory of where it was.&lt;/li&gt;
&lt;li&gt;Now &lt;code&gt;advance()&lt;/code&gt; replaces every &lt;code&gt;getchar()&lt;/code&gt; call inside &lt;code&gt;gettok()&lt;/code&gt;, incrementing line and column counters as it goes.
&lt;code&gt;gettok()&lt;/code&gt; also stamps &lt;code&gt;CurLoc&lt;/code&gt; at the start of each token.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;advance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;LastChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getchar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LastChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="err"&gt;`\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;LastChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="err"&gt;`\&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;LexLoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Line&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;LexLoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;LexLoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Col&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;LastChar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;//inside gettok():&lt;/span&gt;
&lt;span class="n"&gt;CurLoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LexLoc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;2) Adding source locations to AST nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The&lt;code&gt;ExprAST&lt;/code&gt; base class now stores a &lt;code&gt;SourceLocation&lt;/code&gt;, defaulting to whatever &lt;code&gt;CurLoc&lt;/code&gt; was at construction time, so most subclasses inherit it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;VariableExprAST&lt;/code&gt; and &lt;code&gt;CallExprAST&lt;/code&gt; needed explicit locations passed in &lt;code&gt;LitLoc&lt;/code&gt; and &lt;code&gt;BinLoc&lt;/code&gt; respectively, since they're constructed at specific points in parsing where the "current" location matters.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExprAST&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;SourceLocation&lt;/span&gt; &lt;span class="n"&gt;Loc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
   &lt;span class="n"&gt;ExprAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SourceLocation&lt;/span&gt; &lt;span class="n"&gt;Loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CurLoc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Loc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Loc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
   &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Line&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;3) The &lt;code&gt;DebugInfo&lt;/code&gt; struct.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bundles &lt;code&gt;TheCU&lt;/code&gt; (the compile unit), &lt;code&gt;DblTy&lt;/code&gt; (a cached type descriptor for &lt;code&gt;double&lt;/code&gt;), and &lt;code&gt;LexicalBlocks&lt;/code&gt; (the scope stack) together, alongside &lt;code&gt;DBuilder&lt;/code&gt; as a global.&lt;/li&gt;
&lt;li&gt;This is the single source of truth for "what debug state are we in right now?"
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;DebugInfo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;DICompileUnit&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheCU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;DIType&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DblTy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DIScope&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;LexicalBlocks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;KSDbgInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;4) &lt;u&gt;Declaring the DWARF versio&lt;/u&gt;n in &lt;code&gt;main()&lt;/code&gt;, and creating the compile unit (&lt;code&gt;test.k&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addModuleFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Debug Info Version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DEBUG_METADATA_VERSION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;DBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DIBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;KSDbgInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TheCU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;createCompileUnit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dwarf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DW_LANG_C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;createFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test.k"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Kaleidoscope Compiler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) &lt;u&gt;Emitting debug info per function:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside &lt;code&gt;FunctionAST::codegen()&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;We create a &lt;code&gt;DISubprogram&lt;/code&gt;, which is DWARF's description of a function, so the debugger knows it's looking at &lt;code&gt;celsius&lt;/code&gt; or &lt;code&gt;fib&lt;/code&gt;, not just an anonymous block of instructions.&lt;/li&gt;
&lt;li&gt;We push that &lt;code&gt;DISubprogram&lt;/code&gt; onto &lt;code&gt;LexicalBlocks&lt;/code&gt;, so any nested expressions know which function scope they're in.&lt;/li&gt;
&lt;li&gt;We register each argument's location in memory (its &lt;code&gt;alloca&lt;/code&gt;) with the debugger, so it can show argument values when you break inside the function.&lt;/li&gt;
&lt;li&gt;We also perform prologue suppression. A prologue is when we have instructions at the very start of a function and which have no location at all. So the debugger skips past the &lt;code&gt;alloca&lt;/code&gt;/&lt;code&gt;store&lt;/code&gt; boilerplate when we set breakpoints, landing us on the actual first line of logic instead.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;DISubprogram&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;SP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;createFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringRef&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LineNo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreateFunctionType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;arg_size&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;LineNo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DINode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FlagPrototyped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DISubprogram&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SPFlagDefinition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setSubprogram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;KSDbgInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LexicalBlocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;KSDbgInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emitLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//prologue suppression&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6) &lt;code&gt;emitLocation(this)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every &lt;code&gt;codegen()&lt;/code&gt; method on a subclass of &lt;code&gt;ExprAST&lt;/code&gt; calls this before emitting its instructions, tagging the next instruction with that AST node's line and column.&lt;/li&gt;
&lt;li&gt;Only &lt;code&gt;ExprAST&lt;/code&gt; subclasses do this. &lt;code&gt;PrototypeAST&lt;/code&gt; and &lt;code&gt;FunctionAST&lt;/code&gt; are not expressions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;NumberExprAST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;KSDbgInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emitLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ConstantFP&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;APFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;7) &lt;code&gt;DBuilder-&amp;gt;finalize()&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This serialises all the DWARF metadata we've built into the module. It has to happen after codegen and before the JIT takes ownership of the module.&lt;/li&gt;
&lt;li&gt;Because once &lt;code&gt;addModule()&lt;/code&gt; moves it, we can't touch it anymore. Each module gets its own finalised debug info, and the next module starts fresh.&lt;/li&gt;
&lt;li&gt;The AOT object emission, by the way, can happen before &lt;code&gt;finalize()&lt;/code&gt; because writing &lt;code&gt;output.o&lt;/code&gt; runs through a completely separate pass pipeline that doesn't care whether the module's DWARF metadata is finalised or not.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;DBuilder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;finalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;TSM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ThreadSafeModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheContext&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheJIT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TSM&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;RT&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Why don't &lt;code&gt;PrototypeAST&lt;/code&gt; and &lt;code&gt;FunctionAST&lt;/code&gt; call &lt;code&gt;emitLocation()&lt;/code&gt; the same way everything else does?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because they aren't expressions. Every other AST node (numbers, variables, calls, if/else, loops) represents something that evaluates to a value at a specific point in the source. &lt;/li&gt;
&lt;li&gt;A prototype doesn't evaluate to anything and is merely a declaration. &lt;/li&gt;
&lt;li&gt;A function definition isn't a single point either, and is a container for a whole sequence of expressions, each with its own location. &lt;/li&gt;
&lt;li&gt;So &lt;code&gt;FunctionAST::codegen()&lt;/code&gt; has to handle location-tagging more deliberately. First suppressing it for the setup code, then explicitly stamping the body's location once real logic starts. &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;The Debugger in Action&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftdoej6esua9zqj2max6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftdoej6esua9zqj2max6k.png" alt=" " width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running &lt;code&gt;dwarfdump output.o&lt;/code&gt; after compiling with &lt;code&gt;-g -O0&lt;/code&gt; shows the DWARF metadata baked into the object file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx781m19zpgq4f2luxnbq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx781m19zpgq4f2luxnbq.png" alt=" " width="800" height="504"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DW_TAG_compile_unit
  DW_AT_producer  ("Kaleidoscope Compiler")
  DW_AT_language  (DW_LANG_C)
  DW_AT_name      ("test.k")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The debugger now knows that this binary came from a file called &lt;code&gt;test.k&lt;/code&gt;, compiled by something calling itself the "Kaleidoscope Compiler," using C-style calling conventions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DW_TAG_subprogram
  DW_AT_name      ("celsius")
  DW_AT_decl_line (1)
  DW_AT_type      (0x0000005e "double")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The debugger knows there's a function named &lt;code&gt;celsius&lt;/code&gt; on line 1, returning a &lt;code&gt;double&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DW_AT_low_pc/DW_AT_high_pc&lt;/code&gt; give the actual machine address range this function occupies, which is how &lt;code&gt;break celsius&lt;/code&gt; knows where to stop.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DW_TAG_formal_parameter
  DW_AT_name      ("fahrenheit")
  DW_AT_decl_line (1)
  DW_AT_type      (0x0000005e "double")
  DW_AT_location  (...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This tells the debugger exactly where to find &lt;code&gt;fahrenheit&lt;/code&gt;'s value at any given point in the function.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0x0...0,  0x0...10): DW_OP_regx B0
[0x0...10, 0x0...1c): DW_OP_entry_value(DW_OP_regx B0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For the first 0x10 bytes of machine code, &lt;code&gt;fahrenheit&lt;/code&gt; lives in register B0. After that, the codegen may have reused B0 for something else, so the debugger recovers the entry value (what B0 held when the function started) to still show &lt;code&gt;fahrenheit&lt;/code&gt; correctly. &lt;/li&gt;
&lt;li&gt;That's LLVM automatically being smart about register reuse, which we get for free just by setting up &lt;code&gt;DILocalVariable&lt;/code&gt; correctly.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DW_TAG_base_type
  DW_AT_name      ("double")
  DW_AT_encoding  (DW_ATE_float)
  DW_AT_byte_size (0x08)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is &lt;code&gt;KSDbgInfo.getDoubleTy()&lt;/code&gt;, the cached &lt;code&gt;DIType*&lt;/code&gt; built once and reused everywhere. &lt;/li&gt;
&lt;li&gt;Only a single &lt;code&gt;DW_TAG_base_type&lt;/code&gt; entry appears in the dump, referenced by both the function's return type and the parameter's type via the &lt;code&gt;0x0000005e&lt;/code&gt; offset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73tccpdoqnqxzc38xitq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F73tccpdoqnqxzc38xitq.png" alt=" " width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When I ran the binary under &lt;code&gt;lldb&lt;/code&gt;, set &lt;code&gt;break celsius&lt;/code&gt;, and &lt;code&gt;run&lt;/code&gt;, it stopped at the right place, showing &lt;code&gt;fahrenheit&lt;/code&gt;'s value, and knew it was a &lt;code&gt;double&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Vectorisation hints.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Fun fact: the word "debugging" traces back to Grace Hopper's team at Harvard in 1947, when they found a literal moth lodged in a relay of the Mark II computer. Hopper taped it into the logbook with the note "first actual case of bug being found." She didn't coin the term (as bug for a technical glitch predates her) but she gave us the artefact, and the word stuck to software forever after. &lt;/p&gt;

&lt;p&gt;Anyhow, I finished the Kaleidoscope tutorial!!!!! I mean !!!!! &lt;br&gt;
Two years back, I could barely understand what compilers did. Now I (partly) worked on and understood the backend of compilation! &lt;br&gt;
Like I did for the &lt;a href="https://dev.to/lahari_tenneti_4a8a082e9c/series/33287"&gt;jlox interpreter&lt;/a&gt;, I'll be adding some custom extensions to this project so I can understand it even better.&lt;/p&gt;

&lt;p&gt;Time absolutely &lt;em&gt;flies&lt;/em&gt;. Happy to have finished this &lt;em&gt;mam&lt;u&gt;moth&lt;/u&gt;&lt;/em&gt; of a task. It sure won't &lt;em&gt;bug&lt;/em&gt; me anymore. 😌&lt;/p&gt;

&lt;p&gt;(I'll see myself out, bye for now)&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #6 — Compiling to Object Code.</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Mon, 08 Jun 2026 05:41:40 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-6-compiling-to-object-code-1a47</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-6-compiling-to-object-code-1a47</guid>
      <description>&lt;p&gt;This is a short chapter but with a very tangible and "objective" payoff. &lt;/p&gt;

&lt;p&gt;Basically, every function we typed into the REPL got compiled and ran inside the same process, in RAM. The JIT took the IR, converted it into machine code, and executed it instantly. When the process exits, it's all gone.&lt;/p&gt;

&lt;p&gt;Now, instead of "running" the code, we write it to the disk as a &lt;code&gt;.o&lt;/code&gt; file, which is compiled into machine code in a format the linker understands. This can be linked with any other C++ program, enabling us to call the Kaleidoscope functions as if they were normal C functions. The code actually outlives the compiler process that produced it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/f229e861d6f1e8d1ca47490f2a9792b730279a81" rel="noopener noreferrer"&gt;Commit f229e86&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The process of emitting object code includes picking a target, describing the machine, configuring the module, and emitting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) &lt;u&gt;The Target Triple:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;So LLVM is designed for cross-compilation, meaning it can target an Intel Mac, an ARM Phone, a Windows PC... anything.&lt;/li&gt;
&lt;li&gt;For this, it needs a complete machine profile encoded as a string called the target triple. &lt;/li&gt;
&lt;li&gt;Format: &lt;code&gt;&amp;lt;architecture&amp;gt;-&amp;lt;vendor&amp;gt;-&amp;lt;operating-system&amp;gt;-&amp;lt;ABI&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;x86_64-unknown-linux-gnu&lt;/code&gt; means a 64-bit Intel, unspecified vendor, Linux, and GNU calling conventions. &lt;/li&gt;
&lt;li&gt;It's just that instead of hardcoding a triple, we ask LLVM for the current machine's:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;TargetTriple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getDefaultTargetTriple&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;u&gt;Initialising subsystems:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLVM doesn't activate all its backends by default. The JIT only needed the native target. But for writing an object file to the disk, we need everything (hardware platform information, core codegen, machine-code abstractions, and assembly reader and writer).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InitializeAllTargetInfos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//registers available hardware platforms so LLVM knows what targets exist&lt;/span&gt;
&lt;span class="n"&gt;InitializeAllTargets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//loads the code generators; without this, lookupTarget() returns nullptr even with a valid triple&lt;/span&gt;
&lt;span class="n"&gt;InitializeAllTargetMCs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//handles the MC layer to turn abstract instructions into actual bytes&lt;/span&gt;
&lt;span class="n"&gt;InitializeAllAsmParsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//enables reading assembly text as input&lt;/span&gt;
&lt;span class="n"&gt;InitializeAllAsmPrinters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;//enables writing machine instructions to a file; needed for addPassesToEmitFile()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) &lt;u&gt;Target Machine:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once the matching target is obtained from the registry, we create the Target Machine, which is a generic CPU with no special features.&lt;/li&gt;
&lt;li&gt;We also mark the data layout and triple onto the module, which tells the optimiser about things like pointer sizes, alignment rules, and the target's memory layout.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;TM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Target&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;createTargetMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TargetTriple&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"generic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Reloc&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PIC_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDataLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;createDataLayout&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setTargetTriple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Triple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TargetTriple&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) &lt;u&gt;Emitting: &lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;legacy::PassManager&lt;/code&gt; with one pass runs over the module and writes the object file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;raw_fd_ostream&lt;/span&gt; &lt;span class="nf"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"output.o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OF_None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;legacy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PassManager&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;TM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPassesToEmitFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CodeGenFileType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ObjectFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;What I didn't uderstand:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More like where I faced issues:
1) &lt;u&gt;Empty object file:&lt;/u&gt;
&lt;/li&gt;
&lt;li&gt;The tutorial places all the emit code at the bottom of &lt;code&gt;main()&lt;/code&gt;, after &lt;code&gt;MainLoop()&lt;/code&gt; returns. The idea is to parse everything, and then bake it all to disk. I expected this to work:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./toy &amp;lt; test.k        &lt;span class="c"&gt;#should parse celsius, emit output.o&lt;/span&gt;
nm output.o           &lt;span class="c"&gt;#should show: T celsius&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;But instead got: &lt;code&gt;0000000000000000 t ltmp0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;t&lt;/code&gt; instead of a &lt;code&gt;T&lt;/code&gt; meant it was a local symbol, implying the &lt;code&gt;celsius&lt;/code&gt; function was absent. &lt;/li&gt;
&lt;li&gt;This was because inside &lt;code&gt;HandleDefinition()&lt;/code&gt;, right after codegen, the module moves into the JIT and is reset.&lt;/li&gt;
&lt;li&gt;By the time &lt;code&gt;main()&lt;/code&gt; reached the emit code, &lt;code&gt;TheModule&lt;/code&gt; has nothing inside it. We'd be compiling an empty module to disk, and thus the &lt;code&gt;ltmp0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The answer is to emit inside &lt;code&gt;HandleDefinition()&lt;/code&gt; before the JIT move. The function is still alive in &lt;code&gt;TheModule&lt;/code&gt; at that point, so &lt;code&gt;pass.run(*TheModule)&lt;/code&gt; actually has something to work with.
2) Symbol name mismatch:&lt;/li&gt;
&lt;li&gt;Even after fixing the empty module, the link still failed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Undefined symbols for architecture arm64:
  "_celsius", referenced from: _main in testing_temp.o
ld: symbol(s) not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The linker is looking for &lt;code&gt;_celsius&lt;/code&gt; (with an underscore). On macOS, C symbols in object files get a &lt;code&gt;_&lt;/code&gt; prefix automatically, so &lt;code&gt;celsius&lt;/code&gt; in the Kaleidoscope source becomes &lt;code&gt;_celsius&lt;/code&gt; in &lt;code&gt;output.o&lt;/code&gt;. But &lt;code&gt;testing_temp.cpp&lt;/code&gt; was declaring it as plain &lt;code&gt;celsius&lt;/code&gt;, so the linker couldn't match them.&lt;/li&gt;
&lt;li&gt;The fix is an asm label that tells the linker the exact symbol name to look for:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;celsius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;__asm__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_celsius"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Running it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;test.k&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;celsius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fahrenheit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;32.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.555556&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;testing_temp.cpp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;celsius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;__asm__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_celsius"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;68.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;celsius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;" degrees Fahrenheit is "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;" degrees Celsius!"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;We feed the &lt;code&gt;celsius&lt;/code&gt; function through the compiler, link the object file, and run it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./toy &amp;lt; test.k
clang++ testing_temp.cpp output.o &lt;span class="nt"&gt;-o&lt;/span&gt; test_celsius
./test_celsius   &lt;span class="c"&gt;#68 degrees Fahrenheit is 20 degrees Celsius!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtpbpb7z6otajr51lbvq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgtpbpb7z6otajr51lbvq.png" alt=" " width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Debugging!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Sometimes, I feel like I have no idea where things are heading. Like I have absolutely zero control over the outcome of things I put effort into. But Indian philosophy and even Greek (I’ve been reading Marcus Aurelius’ Meditations lately) seem to emphasise that that’s exactly the point. That we do our duty and just let go of the rest. Slightly harder than I thought. &lt;/p&gt;

&lt;p&gt;In times like these, among the few things that grounds me is looking at the night sky (of all directions). I feel like it's been witness to countless tales like these. In a way, knowing that I’m but only a tiny, tiny, one-millionth of this pale blue dot that is our earth, amidst the vast endlessness that is our universe, helps me feel like the things I think of, may after all, not really be as final as they feel in the moment. And that all will be okay. Those stars remind me of Tennyson’s ‘For men may come and men may go, but I go on forever’ and help me ground myself into, and enjoy the now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70528x8qceausfa97pgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70528x8qceausfa97pgi.png" alt=" " width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #5 — Mutable Variables</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Mon, 18 May 2026 09:40:38 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-5-mutable-variables-ppg</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-5-mutable-variables-ppg</guid>
      <description>&lt;p&gt;So far, Kaleidoscope has been a functional language with immutable variables and no reassignment. But to write anything resembling real code (loops that accumulate or programs with state), we need mutation. We add it now.&lt;/p&gt;

&lt;p&gt;We introduce two features: the ability to mutate variables with &lt;code&gt;=&lt;/code&gt;, and the ability to define new local variables with &lt;code&gt;var/in&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0dh8dz6w09tuod59bif.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0dh8dz6w09tuod59bif.png" alt=" " width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/7911f2b0a21b63c3bd240f376290dc49e017d985" rel="noopener noreferrer"&gt;Commit 7911f2b&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Context&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kaleidoscope, in its functional paradigm, only ever had immutable variables. &lt;/li&gt;
&lt;li&gt;But what if we want to write things like this iterative Fibonacci:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def fibi(x)
  var a = 1, b = 1, c in
  (for i = 3, i &amp;lt; x in
     c = a + b :
     a = b :
     b = c) :
  b;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The issue is that LLVM IR requires SSA form. In SSA, a variable is assigned exactly once. But mutation means assigning the same variable multiple times. &lt;/li&gt;
&lt;li&gt;If we want to maintain SSA while making our language more imperative, there's immediate confusion over where to put the PHI nodes.&lt;/li&gt;
&lt;li&gt;Because unlike &lt;code&gt;if/else&lt;/code&gt; where the merge point is obvious from the AST, here the assignments could be scattered anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Solution:&lt;/p&gt;

&lt;p&gt;a) &lt;u&gt;The "Easy Version:"&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The trick LLVM recommends is that we write a deliberately simple (and temporarily inefficient) IR, and let LLVM clean it up. Here's how it works:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) At the start of the function, we c&lt;u&gt;reate a "box" on the stack&lt;/u&gt; for each mutable variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%x_addr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;alloca&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This gives &lt;code&gt;x&lt;/code&gt; a memory address. The compiler doesn't need to track how many times &lt;code&gt;x&lt;/code&gt; has been changed and just always knows the address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Every time the user changes the value, we &lt;u&gt;update the box&lt;/u&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="k"&gt;store&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;%x_addr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Every time we need to use the variable, we &lt;u&gt;peek inside the box&lt;/u&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%x_val&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;load&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;%x_addr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is easy for the front-end to generate and requires no PHI node reasoning. &lt;/li&gt;
&lt;li&gt;The cost is that we're constantly reading and writing to memory, which is slow. &lt;/li&gt;
&lt;li&gt;But that's where &lt;code&gt;mem2reg&lt;/code&gt; comes in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) &lt;u&gt;mem2reg: The lifting pass:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After we generate the "easy version", we run &lt;code&gt;PromotePass()&lt;/code&gt; (which is &lt;code&gt;mem2reg&lt;/code&gt;). It performs a "lifting" operation:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) It looks at each &lt;code&gt;alloca&lt;/code&gt; and checks if it's only used for simple loads and stores.&lt;/p&gt;

&lt;p&gt;2) If yes, it deletes the &lt;code&gt;alloca&lt;/code&gt;, &lt;code&gt;load&lt;/code&gt;, and &lt;code&gt;store&lt;/code&gt; instructions entirely.&lt;/p&gt;

&lt;p&gt;3) It replaces them with high-speed CPU registers (and for this, it tracks the lifetime of the values like a timeline), assigning a new SSA register every time a new value is written.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When paths merge (like after an &lt;code&gt;if/else&lt;/code&gt;), &lt;code&gt;mem2reg&lt;/code&gt; mathematically figures out where the PHI nodes need to go, using a graph theory concept called &lt;strong&gt;dominance frontier&lt;/strong&gt;, so the logic remains correct but the speed is improved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The dominance frontier works like this: if we have a block that forks into two paths and merges later (Block A → Block C, Block B → Block C), then C is in A's dominance frontier if A can influence what happens right before C, but A doesn't have "total" control over C (because B is an alternate path that skips A). At every such frontier, &lt;code&gt;mem2reg&lt;/code&gt; knows a PHI node is needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The final result is that we don't have to look at a box in memory anymore and only need pure, high-speed CPU registers. And we never had to figure out PHI placement ourselves.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rules for &lt;code&gt;mem2reg&lt;/code&gt; to work:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;alloca&lt;/code&gt; instructions must be in the entry block of the function (so the memory address is only created once per function call).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;alloca&lt;/code&gt; variable can only be used for direct load/store operations and can't pass its address into another function.&lt;/li&gt;
&lt;li&gt;Works on single numbers, booleans, and pointers. Won't promote complex data structures like arrays or custom structs (that needs a different pass, &lt;code&gt;sroa&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;&lt;u&gt;Making it happen:&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;code&gt;NamedValues&lt;/code&gt; changes type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AllocaInst&lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Previously it held &lt;code&gt;Value*&lt;/code&gt;. Now it holds &lt;code&gt;AllocaInst*&lt;/code&gt;. This one line physically transitions the compiler from tracking values to tracking memory locations. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;Adding an &lt;code&gt;alloca&lt;/code&gt;&lt;/u&gt; at the beginning of a block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;AllocaInst&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;CreateEntryBlockAlloca&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringRef&lt;/span&gt; &lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;IRBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TmpB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getEntryBlock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                   &lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getEntryBlock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TmpB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateAlloca&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;getDoubleTy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheContext&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This creates a temporary &lt;code&gt;IRBuilder&lt;/code&gt; pointing at the very first instruction of the entry block, then creates an &lt;code&gt;alloca&lt;/code&gt; there.&lt;/li&gt;
&lt;li&gt;Why the entry block specifically? Because &lt;code&gt;mem2reg&lt;/code&gt; only promotes allocas it can find in the entry block guaranteeing that the box is created exactly once per function call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Loading/Reading Variables:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;AllocaInst&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;CreateLoad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getAllocatedType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Every variable reference is now a load from a memory address rather than a direct SSA value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Registering variables in &lt;code&gt;NamedValues&lt;/code&gt;:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each argument, we now make an alloca, store the initial value into it, and register the alloca in &lt;code&gt;NamedValues&lt;/code&gt;. This is what allows function arguments to be mutable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Getting rid of the Phi node in the codegen for &lt;code&gt;For&lt;/code&gt;:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The for loop no longer needs a PHI node for its induction variable. &lt;/li&gt;
&lt;li&gt;Instead, we create an alloca for the loop variable in the entry block.&lt;/li&gt;
&lt;li&gt;Store the start value into it.&lt;/li&gt;
&lt;li&gt;At the end of each iteration, load the current value, add the step, and store the result back.&lt;/li&gt;
&lt;li&gt;With this, the PHI node is gone and &lt;code&gt;mem2reg&lt;/code&gt; handles the SSA construction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;Adding &lt;code&gt;mem2reg&lt;/code&gt;:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PromotePass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is the &lt;code&gt;mem2reg&lt;/code&gt; pass. It runs first, before &lt;code&gt;InstCombine&lt;/code&gt;, &lt;code&gt;GVN&lt;/code&gt;, etc. and converts all our alloca/load/store patterns back into clean SSA registers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;7) &lt;u&gt;The Assignment Operator:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;=&lt;/code&gt; is parsed as a binary operator with precedence 2 (lower than everything else), but its codegen is a special case as it doesn't follow the normal "emit LHS, emit RHS, do computation" model. Instead:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Op&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VariableExprAST&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;LHSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;VariableExprAST&lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LHS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RHS&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;CreateStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Variable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It's important to note that the LHS must be a variable and not an expression. &lt;code&gt;(x + 1) = 5&lt;/code&gt; is illegal; only &lt;code&gt;x = 5&lt;/code&gt; is valid. &lt;/li&gt;
&lt;li&gt;And assignment returns the assigned value, which allows chaining like &lt;code&gt;x = (y = z)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;8)&lt;u&gt;&lt;code&gt;var/in&lt;/code&gt; (user-defined local variables):&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;var/in&lt;/code&gt; declares one or more variables, optionally initialises them (defaulting to &lt;code&gt;0.0&lt;/code&gt;), and makes them available for the duration of the body expression. It has the aame procedure as always — lexer, AST, parser, codegen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;VarExprAST::codegen()&lt;/code&gt; loops over all the declared variables, emits each initialiser before adding the variable to scope (so &lt;code&gt;var a = 1 in var a = a in ...&lt;/code&gt; correctly refers to the outer &lt;code&gt;a&lt;/code&gt;), creates an alloca, stores the initial value, and saves the old binding in &lt;code&gt;OldBindings&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the body runs, it restores all the old bindings.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbb3hi56y7atm36xnetup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbb3hi56y7atm36xnetup.png" alt=" " width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Compiling to object code.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's something nice about the &lt;code&gt;alloca&lt;/code&gt; + &lt;code&gt;mem2reg&lt;/code&gt; pattern. We deliberately write something worse (slower, more verbose, and naively use memory where registers would do) and then trust a pass to fix it. The front-end stays simple while the complexity lives in the optimiser, where it's been tested and tuned.&lt;/p&gt;

&lt;p&gt;I suppose not every problem needs to be solved at the level it's encountered. Sometimes the right move is to do the honest and simpler version of something and let a more capable system handle the hard part. The trick is knowing which problems are ours to solve and which ones we can hand off.&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #4 — User Defined Operators</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 13 May 2026 11:56:31 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-4-user-defined-operators-3f58</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-4-user-defined-operators-3f58</guid>
      <description>&lt;p&gt;Kaleidoscope's grammar can now be extended by the user. They can define their own binary and unary operators with custom symbols and precedence, without rewriting the parser or adding new cases to the codegen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/89fa3f80c85039674b1e9e73b0c38cf5dc439cb5" rel="noopener noreferrer"&gt;Commit 89fa3f8&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The idea is simple. We should allow for users to write something like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxplc91k6k8g5oiqukzth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxplc91k6k8g5oiqukzth.png" alt=" " width="786" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr765qe3v9tg1lamp9c56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr765qe3v9tg1lamp9c56.png" alt=" " width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Through which we can do:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0r4vfcwqd4xy8o13nnzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0r4vfcwqd4xy8o13nnzh.png" alt=" " width="800" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Lexer:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We add two new tokens: &lt;code&gt;tok_binary&lt;/code&gt; and &lt;code&gt;tok_unary&lt;/code&gt;, along with their checks in &lt;code&gt;gettok()&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;AST:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We create a &lt;code&gt;UnaryExprAST&lt;/code&gt;, which is pretty similar to &lt;code&gt;BinaryExprAST&lt;/code&gt;, but with one child instead of two. &lt;/li&gt;
&lt;li&gt;We also extend &lt;code&gt;PrototypeAST&lt;/code&gt; to have two new fields: &lt;code&gt;IsOperator&lt;/code&gt; (&lt;code&gt;bool&lt;/code&gt;) and &lt;code&gt;Precedence&lt;/code&gt; (&lt;code&gt;unsigned&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;A prototype now knows whether it's defining an operator, and if yes, at what precedence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Parser:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ParsePrototype()&lt;/code&gt; uses a switch-case on &lt;code&gt;CurTok&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If it sees &lt;code&gt;tok_identifier&lt;/code&gt;, it's a regular function, like before.&lt;/li&gt;
&lt;li&gt;But if it sees &lt;code&gt;tok_binary&lt;/code&gt;, it reads the operator character, optionally reads a precedence number (in case of binary), and builds the name &lt;code&gt;"binary" + char&lt;/code&gt; (so &lt;code&gt;binary|&lt;/code&gt;, &lt;code&gt;binary&amp;gt;&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it sees &lt;code&gt;tok_unary&lt;/code&gt;, it does the same but without precedence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ParseUnary()&lt;/code&gt; is also new. It sits between &lt;code&gt;ParseExpression()&lt;/code&gt; and &lt;code&gt;ParsePrimary()&lt;/code&gt; in the call chain. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the current token looks like a unary operator (an ASCII character that isn't &lt;code&gt;(&lt;/code&gt; or &lt;code&gt;,&lt;/code&gt;), it consumes it and recursively calls &lt;code&gt;ParseUnary()&lt;/code&gt; on the rest. This is for handling chaining (like &lt;code&gt;!!x&lt;/code&gt;). Otherwise, it falls through to &lt;code&gt;ParsePrimary()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ParseExpression()&lt;/code&gt; and &lt;code&gt;ParseBinOpRHS()&lt;/code&gt; are also updated to call &lt;code&gt;ParseUnary()&lt;/code&gt; instead of &lt;code&gt;ParsePrimary()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Codegen:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;This is where things change a bit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For binary operators, &lt;code&gt;BinaryExprAST::codegen()&lt;/code&gt; already had a switch-case on &lt;code&gt;Op&lt;/code&gt;. We just add a default case that does a symbol table lookup for &lt;code&gt;"binary" + Op&lt;/code&gt; and emits a call to it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s"&gt;"binary operator not found!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Ops&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;CreateCall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Ops&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binop"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;User-defined operators are mostly similar to functions (only with new names). The codegen doesn't bother distinguishing whether it's a function or UDF; It merely finds the function and calls it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Likewise, for unary operators, &lt;code&gt;UnaryExprAST::codegen()&lt;/code&gt; looks up &lt;code&gt;"unary" + Opcode&lt;/code&gt; and calls it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Like I mentioned earlier, before building the function body, if the prototype is a binary operator, we register its precedence in &lt;code&gt;BinopPrecedence&lt;/code&gt;. This change is made in &lt;code&gt;FunctionAST::codegen()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isBinaryOp&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;BinopPrecedence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getOperatorName&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getBinaryPrecedence&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The grammar is dynamically extensible at JIT runtime: define a new operator and it is immediately available with the right precedence.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What I didn't understand:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) How does &lt;u&gt;naming operators&lt;/u&gt; &lt;code&gt;binary|&lt;/code&gt; or &lt;code&gt;unary!&lt;/code&gt; work?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I had this question because we construct a string like &lt;code&gt;binary|&lt;/code&gt; and use it as a function name. &lt;/li&gt;
&lt;li&gt;The thing is, LLVM's symbol table allows names with symbols, so &lt;code&gt;binary|&lt;/code&gt; is a perfectly valid function name in LLVM IR. &lt;/li&gt;
&lt;li&gt;When the user writes &lt;code&gt;x | y&lt;/code&gt;, codegen looks up &lt;code&gt;binary|&lt;/code&gt; in the module, finds the user-defined function, and emits a call. &lt;/li&gt;
&lt;li&gt;It is an ordinary function dispatch dressed up to look like operator syntax.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) Why do user-defined operators not need &lt;u&gt;new AST nodes?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is because the existing &lt;code&gt;BinaryExprAST&lt;/code&gt; and &lt;code&gt;UnaryExprAST&lt;/code&gt; already represent “an operator applied to operands”, and thus don't care whether the operator is built-in or user-defined. &lt;/li&gt;
&lt;li&gt;The only thing that changes is what &lt;code&gt;codegen()&lt;/code&gt; does with an unrecognised &lt;code&gt;Op&lt;/code&gt;. Instead of erroring, it looks the operator up as a function. &lt;/li&gt;
&lt;li&gt;The AST stays blissfully unaware of the distinction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lsgc4ssvy2ovzgm576o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lsgc4ssvy2ovzgm576o.png" alt=" " width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsk0uvmlvi3mpmr0gf0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsk0uvmlvi3mpmr0gf0k.png" alt=" " width="800" height="834"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Mutable variables and SSA construction (the last big piece).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have the insanest Tiny Chef obsession. He's the most adorable, sassy, and tiny little bundle of joy I've seen in a long, long time now. He's so refreshingly and unabashedly authentic. Bad singing (but still does it anyway), pop-astrology, yoga, wardrobe dilemmas, and above all — unapologetic optimism. I never imagined I'd find myself rooting for, or seeking life-lessons from a barely legible green little ball of felt. But hey, here we are. When the going gets tough, all we've got to do is put our hand on our heart and say, "You know what? I'm blenough, and it's all going to be blokay." &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpn4h3tetl9flrlllbuf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpn4h3tetl9flrlllbuf3.png" alt=" " width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>llvm</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #3 — Control Flow</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 06 May 2026 10:16:56 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-3-control-flow-1c42</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-3-control-flow-1c42</guid>
      <description>&lt;p&gt;After all we've done (building a lexer, parser, code-generator, optimiser, and the JIT), we give Kaleidoscope decision-making abilities by adding support for if/then else conditionals and for-loops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/5ba58038714185be6bd1471d9719d2539f03fa07" rel="noopener noreferrer"&gt;Commit 5ba5803&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;If/Then/Else:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Lexer:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We add three new tokens, namely &lt;code&gt;tok_if&lt;/code&gt;, &lt;code&gt;tok_then&lt;/code&gt;, and &lt;code&gt;tok_else&lt;/code&gt;, and their corresponding checks in &lt;code&gt;gettok()&lt;/code&gt; (through &lt;code&gt;if (IdentifierStr == ...)&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;u&gt;AST:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;IfExprAST&lt;/code&gt; holds three child expressions — &lt;code&gt;Cond&lt;/code&gt;, &lt;code&gt;Then&lt;/code&gt;, and &lt;code&gt;Else&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It's worth noting that in Kaleidoscope, everything is an expression. There are no statements. This means that &lt;code&gt;if/then/else&lt;/code&gt; doesn't result in an action, and instead returns a value.&lt;/li&gt;
&lt;li&gt;This is to keep the language consistent, as the codegen never has to resolve whether something is an expression or a statement, as only the former is allowed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Parser:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ParseIfExpr()&lt;/code&gt; consumes &lt;code&gt;if&lt;/code&gt;, parses the condition, expects &lt;code&gt;then&lt;/code&gt;, parses the then-expression, expects &lt;code&gt;else&lt;/code&gt;, parses the else-expression, and returns an &lt;code&gt;IfExprAST&lt;/code&gt;. This is simple recursive descent at play.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Codegen:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When we generate code for an if-then-else condition, we can't emit instructions linearly anymore. The two branches (then, else) are mutually exclusive, and only one runs. &lt;/li&gt;
&lt;li&gt;This is where we need proper control-flow: a conditional branch, two separate blocks of code, and a merge point.&lt;/li&gt;
&lt;li&gt;That looks like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;entry:&lt;/span&gt;
   &lt;span class="nv"&gt;%ifcond&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fcmp&lt;/span&gt; &lt;span class="k"&gt;one&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
   &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%ifcond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%then&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%else&lt;/span&gt;

&lt;span class="nl"&gt;then:&lt;/span&gt;
   &lt;span class="nv"&gt;%calltmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%ifcont&lt;/span&gt;

&lt;span class="nl"&gt;else:&lt;/span&gt;
  &lt;span class="nv"&gt;%calltmp1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%ifcont&lt;/span&gt;

&lt;span class="nl"&gt;ifcont:&lt;/span&gt;
  &lt;span class="nv"&gt;%iftmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;phi&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%calltmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%then&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%calltmp1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%else&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%iftmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I didn't understand (in if/else):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) &lt;u&gt;Why basic blocks?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is merely a list of instructions. Why not just emit them line-by-line and tell the CPU to 'jump' when it hits an &lt;code&gt;if&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;This is because it would be nightmarish for the optimiser to understand the flow in such a scenario. LLVM hence forces us to use 'basic blocks,' which are chunks of code guaranteed to execute from beginning to end, without any jumping in or out.&lt;/li&gt;
&lt;li&gt;Through blocks, the compiler has a somewhat high-level map (&lt;strong&gt;Ex:&lt;/strong&gt; It knows exactly what happens in a &lt;code&gt;ThenBB&lt;/code&gt;, an &lt;code&gt;ElseBB&lt;/code&gt;, etc.), which matters for optimisation. If the optimiser knows a block runs as a unit, it can reason about the whole block at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) &lt;u&gt;Why the Phi node?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why couldn't I just assign a value to a variable in both blocks, depending on the condition, and then have the &lt;code&gt;ifcont&lt;/code&gt; read the value? For example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;even&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
   &lt;span class="n"&gt;ans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;odd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This is because LLVM uses SSA. The same variable cannot be changed once defined, thereby making the &lt;code&gt;else&lt;/code&gt; branch impossible unless... we use a Phi node.&lt;/li&gt;
&lt;li&gt;This Phi node sits at the junction where both &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;else&lt;/code&gt; branches meet. It does no "calculation" and only looks back at the path the CPU took. At runtime, when the CPU jumps from &lt;code&gt;then&lt;/code&gt; or &lt;code&gt;else&lt;/code&gt; to &lt;code&gt;ifcont&lt;/code&gt;, it already carries information about which block it just came from. &lt;/li&gt;
&lt;li&gt;Phi reads that "came from" information and resolves to the corresponding value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; my value is &lt;code&gt;calltmp&lt;/code&gt; if we came from &lt;code&gt;then&lt;/code&gt;, or &lt;code&gt;calltmp1&lt;/code&gt; if we came from &lt;code&gt;else&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;c) &lt;u&gt;Why do we insert the Phi node manually&lt;/u&gt; for &lt;code&gt;if/else&lt;/code&gt;, instead of using &lt;code&gt;alloca&lt;/code&gt; + &lt;code&gt;mem2reg&lt;/code&gt; to handle user variables?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is because we know exactly where the merge happens. &lt;/li&gt;
&lt;li&gt;When codegen is processing an &lt;code&gt;IfExprAST&lt;/code&gt;, it knows the shape of the problem before it even starts. There are two branches. They will meet at exactly one point. That meeting point needs exactly one Phi node with exactly two inputs. It's the same every single time, no matter what. So we just write it directly. &lt;/li&gt;
&lt;li&gt;Contrarily, &lt;code&gt;alloca&lt;/code&gt; + &lt;code&gt;mem2reg&lt;/code&gt; is more helpful when code looks like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here, &lt;code&gt;x&lt;/code&gt; gets assigned in multiple places. And in a more complex program, those assignments could be scattered across loops, nested ifs, all over the place. The compiler can't just look at the AST node for x and know where to put the Phi. It would have to trace every possible path through the entire program to figure out where values of &lt;code&gt;x&lt;/code&gt; merge. &lt;/li&gt;
&lt;li&gt;So instead of doing that hard work ourselves, we use &lt;code&gt;alloca&lt;/code&gt; (we give &lt;code&gt;x&lt;/code&gt; a slot in memory, let every branch just write to that slot, and then hand it off to &lt;code&gt;mem2reg&lt;/code&gt;). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mem2reg&lt;/code&gt; is a pass that already knows how to trace control flow, find all the merge points, and insert the right Phi nodes automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;d) &lt;u&gt;Why do we re-fetch the &lt;code&gt;ThenBB&lt;/code&gt; block?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is there a need for another &lt;code&gt;ThenBB = Builder -&amp;gt; GetInsertBlock()&lt;/code&gt; if we already had it?&lt;/li&gt;
&lt;li&gt;This is to account for recursion. If the code inside our &lt;code&gt;then&lt;/code&gt; block is a simple &lt;code&gt;x + y&lt;/code&gt;, the pointer is the same. But if it contains another &lt;code&gt;if/else&lt;/code&gt;, the nested &lt;code&gt;if&lt;/code&gt; will create its own blocks and move the builder's insertion point. &lt;/li&gt;
&lt;li&gt;In this case, the builder sits at the end of the nested merge block. Hence, we re-fetch it because the Phi node needs to know the final block that ran, and not necessarily the one we started out with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lfeb6zezj7jtqv3upjf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lfeb6zezj7jtqv3upjf.png" alt=" " width="800" height="875"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The for loop:&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="n"&gt;putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;There are four parts to this: start value, end condition, step value (defaults to 1.0), and the body. We follow the same 'lexer, parser, AST, codegen' template as &lt;code&gt;if/else&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The IR generated by the codegen looks like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;entry:&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt;

&lt;span class="nl"&gt;loop:&lt;/span&gt;
  &lt;span class="nv"&gt;%i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;phi&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%entry&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;%nextvar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nv"&gt;%calltmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="vg"&gt;@putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;42.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;%nextvar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1.0&lt;/span&gt;
  &lt;span class="nv"&gt;%loopcond&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fcmp&lt;/span&gt; &lt;span class="k"&gt;one&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%booltmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%loopcond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%afterloop&lt;/span&gt;

&lt;span class="nl"&gt;afterloop:&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I didn't understand (in for loops):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;a) &lt;u&gt;Why is there a preheader&lt;/u&gt; in the for-loop, before it even starts?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Again, for the Phi node. It needs to know which block a value came from. When we enter a loop for the first time, we aren't wntering from within the loop itself. As trivial as it sounds, we enter from outside the loop.&lt;/li&gt;
&lt;li&gt;The Preheader thus gives the Phi node a clear starting point for the first iteration, and without which the Phi node wouldn't know what our counter's initial value should be.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;b) &lt;u&gt;Why return a &lt;code&gt;0.0&lt;/code&gt;?&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As of now, our for loops return a value of &lt;code&gt;0.0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is because we don't have mutable memory yet. The loop variable disappears once the loop ends, causing the body's value to not be accumulated anywhere. &lt;/li&gt;
&lt;li&gt;This is just a temporary placeholder of sorts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;c) &lt;u&gt;What if the variable we use for the loop already exists?&lt;/u&gt; What happens to its value after the loop ends?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is better understood with an example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;
  &lt;span class="nf"&gt;putchard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The outer &lt;code&gt;i&lt;/code&gt;'s value is 99. And the loop has an &lt;code&gt;i&lt;/code&gt; value of its own. &lt;/li&gt;
&lt;li&gt;The problem is that both live in the same symbol table, &lt;code&gt;NamedValues&lt;/code&gt;. Whenever the loop writes &lt;code&gt;NamedValues["i"] = Variable&lt;/code&gt;, the previous value gets overwritten. Thus, when the loop ends, the pre-loop value is either gone, or replaced by the loop's last value. &lt;/li&gt;
&lt;li&gt;The answer to this is Variable shadowing. Pretty similar to what we did with Lox's environments. We peek at &lt;code&gt;NamedValues&lt;/code&gt; to see that pre-loop value, and save it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OldVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Variable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// loop's i takes over&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we restore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldVal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldVal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// 99 comes back&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="n"&gt;NamedValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VarName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// nothing was there before, so clean up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The inner variable doesn't destroy the outer one and only temporarily steps in front of it. Once the loop exits, the outer scope is exactly as it was. Same principle as Lox's chained environments, just done manually here since we're managing the symbol table ourselves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48b7f6mtfe90cesad9le.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48b7f6mtfe90cesad9le.png" alt=" " width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Extending Kaleidoscope with user-defined operators. More control.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Hehe, hello again. Twenty thousand different things came up. I hope to be more consistent with this though. I mean, it is sort of good that I keep coming back. Not even as an obligation because I actually enjoy it very much. But I could do better.&lt;/p&gt;

&lt;p&gt;Anyhoo, I've been walking quite a bit lately (which I absolutely love). It is among the few things I do to relax. I think excellently when I'm walking. Any problem I feel I'm unable to find a solution to seems to solve itself merely 15-20 minutes into a walk. I feel more optimistic and in charge of life. I also learn to, briefly though it may be, set aside all that goes on in my little world, and just feel like I'm part of something bigger. Almost akin to that mix of awe and ease one feels knowing they're in the audience witnessing something grand. Our world too, can be beautiful and inspire hope if we figure out how to look at it. A walk out there helps. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0lqo0wjktc7gsvg3igb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj0lqo0wjktc7gsvg3igb.png" alt=" " width="800" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM #2 — Optimiser Support &amp; JIT Compilation</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 17 Mar 2026 06:48:33 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-2-optimiser-support-jit-compilation-15hm</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-2-optimiser-support-jit-compilation-15hm</guid>
      <description>&lt;p&gt;Having understood what compiler optimisations are and how they work in theory, we now actually wire them into Kaleidoscope, and then take things one step further by adding a JIT compiler so our REPL can evaluate expressions on the spot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built:&lt;/strong&gt; &lt;a href="https://github.com/laharitenneti/Kaleidoscope/commit/91267d2382ab9ff11d8a907290aca1ae0168b81f" rel="noopener noreferrer"&gt;Commit 91267d2&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What I understood:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Adding Optimisation Passes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;So far, our codegen was correct but not efficient. The IR we produced was merely a pretty-print of the AST. LLVM provides a &lt;code&gt;FunctionPassManager&lt;/code&gt; to change that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A pass is simply one "go" over the IR that looks for a specific pattern and rewrites it. The &lt;code&gt;FunctionPassManager&lt;/code&gt; contains a sequence of passes and runs them over each function in that order, passing the output of one as input to the next.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The key change is in &lt;code&gt;InitialiseModuleAndManagers()&lt;/code&gt;. After creating the module and the IR builder, we now also have a whole suite of analysis and pass managers:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FunctionPassManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheLAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;LoopAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheFAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FunctionAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheCGAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CGSCCAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheMAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_unique&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ModuleAnalysisManager&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The four &lt;code&gt;AnalysisManagers&lt;/code&gt; each correspond to a level of LLVM's IR hierarchy — loops, functions, call-graph SCCs, and whole modules.&lt;/li&gt;
&lt;li&gt;They're required so transform passes can look up analysis results when they need them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side note:&lt;/strong&gt; A call graph is one with functions for nodes, with every edge representing a call. So an edge from A to B means A calls B.&lt;/li&gt;
&lt;li&gt;SCCs (Strongly Connected Components) in a call graph represent a group of functions where each function can reach/call the other. (Mutual recursion of sorts)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we create four transform passes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InstCombinePass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// peephole optimisations&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ReassociatePass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// reorder expressions&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GVNPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;            &lt;span class="c1"&gt;// eliminate redundant computations&lt;/span&gt;
&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addPass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SimplifyCFGPass&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;    &lt;span class="c1"&gt;// clean up unreachable blocks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A) InstCombinePass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles "peephole" optimisations (small/local rewrites).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1+2&lt;/code&gt; becoming &lt;code&gt;3.0&lt;/code&gt; before the program ever runs is constant folding at play, and this pass is what catches it in the generated IR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;B) ReassociatePass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reorders expressions to enable more opportunities for other passes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(x+1)+2&lt;/code&gt; becomes &lt;code&gt;x+3&lt;/code&gt;. Small change, but it means GVN can now recognise that &lt;code&gt;(x+1)+2&lt;/code&gt; and &lt;code&gt;1+(x+2)&lt;/code&gt; are the same thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;C) GVNPass (Global Value Numbering):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assigns a symbolic identity to each new/unique computation and replaces duplicates.&lt;/li&gt;
&lt;li&gt;Ex: if we write &lt;code&gt;(1+2+x)*(x+(1+2))&lt;/code&gt;, both sides of the multiplication are &lt;code&gt;x+3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Without GVN, the IR computes &lt;code&gt;x+3&lt;/code&gt; twice. With it, the result is computed once and reused.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So before GVN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%addtmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;
&lt;span class="nv"&gt;%addtmp1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;
&lt;span class="nv"&gt;%multmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fmul&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%addtmp1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After GVN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nv"&gt;%addtmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fadd&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3.000000e+00&lt;/span&gt;
&lt;span class="nv"&gt;%multmp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fmul&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;%addtmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;D) SimplifyCFGPass:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleans up the control flow graph. If a branch can never be entered into, or a block has no predecessors, this pass removes it.&lt;/li&gt;
&lt;li&gt;It's more like keeping the IR tidy after the other passes have finished their work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we run the pass manager after every function is constructed, just before returning it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheFPM&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TheFAM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;FunctionPassManager&lt;/code&gt; updates the function in-place. The IR going in is the naive transcription; the IR coming out is the cleaned-up, optimised version.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;2) JIT Compilation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Now that we have nice IR coming out of the optimiser, we want to execute it — not just pretty-print it. That's where the JIT comes in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JIT (Just-In-Time) compilation means converting LLVM IR to native machine code at runtime, in memory, right as the user types. The result is a pointer to executable code we can call directly, as if it were a C function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting it up requires initialising the native target first:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InitializeNativeTarget&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;InitializeNativeTargetAsmPrinter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;InitializeNativeTargetAsmParser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;TheJIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KaleidoscopeJIT&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Side note: LLVM is cross-platform by design. These initialisation calls are what tell&lt;/span&gt;
&lt;span class="c1"&gt;// the JIT to look at the hardware the user is on and prepare to speak its specific language.&lt;/span&gt;
&lt;span class="c1"&gt;// Ex: Even if we're on Apple Silicon (arm64), LLVM could generate x86 code if we told it to.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And setting the module's data layout to match the JIT's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDataLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheJIT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getDataLayout&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This is important as it ensures that the memory layout of structs, function arguments, and return values matches what the host machine expects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the user types a top-level expression like &lt;code&gt;4+5;&lt;/code&gt;, we wrap it in an anonymous function (&lt;code&gt;__anon_expr&lt;/code&gt;), add the module to the JIT, look up the symbol, and call it:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;ExprSymbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TheJIT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"__anon_expr"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ExprSymbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toPtr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Evaluated to %f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FP&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The JIT compiles the LLVM IR to machine code, returns its address, we cast it to a function pointer, and call it like any other native function.&lt;/li&gt;
&lt;li&gt;There's no difference at the hardware level between JIT-compiled code and statically linked machine code (i.e., they live in the same address space).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After calling it, we clean it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;ExitOnErr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RT&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ResourceTracker&lt;/code&gt; (&lt;code&gt;RT&lt;/code&gt;) is responsible for the JIT'd memory allocated to that anonymous expression. Removing it frees that memory.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;3) The module lifetime problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The issue in the REPL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ready&amp;gt; def testfunc(x y) x + y*2;
ready&amp;gt; testfunc(4, 10);
Evaluated to 24.000000

ready&amp;gt; testfunc(5, 10);
LLVM ERROR: Program used external function 'testfunc' which could not be resolved!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;testfunc&lt;/code&gt; was defined in the same module as the anonymous expression for &lt;code&gt;testfunc(4, 10)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When we removed that module from the JIT to free the memory for the anonymous expression, we inadvertently deleted &lt;code&gt;testfunc&lt;/code&gt; along with it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The solution: Every function definition gets its own module. The JIT can resolve calls across module boundaries, so &lt;code&gt;testfunc&lt;/code&gt; lives in its own module indefinitely. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each new anonymous expression gets a fresh module, which we remove after execution. Function definitions stay.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But this creates a &lt;u&gt;new problem&lt;/u&gt;: when the codegen for a new anonymous expression tries to emit a call to &lt;code&gt;testfunc&lt;/code&gt;, that function doesn't exist in the current module. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The IR emitter needs at least a declaration of &lt;code&gt;testfunc&lt;/code&gt; to generate a valid &lt;code&gt;call&lt;/code&gt; instruction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;u&gt;solution&lt;/u&gt; is &lt;code&gt;getFunction()&lt;/code&gt;, a helper that first checks the current module for a declaration, and if it doesn't find one, regenerates it from &lt;code&gt;FunctionProtos&lt;/code&gt; (a map of the most recent prototype for every function we've seen):&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Function&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;getFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TheModule&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;FI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FunctionProtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FI&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;FunctionProtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;FI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;codegen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;So the function "body" lives in its own module in the JIT.&lt;/li&gt;
&lt;li&gt;The function "declaration" gets re-emitted into each new module that needs to call it.&lt;/li&gt;
&lt;li&gt;The JIT links them at call time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllyp4upwj4mphefnw20r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fllyp4upwj4mphefnw20r.png" alt=" " width="800" height="1062"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Control flow (if/then/else and loops).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Learning the piano taught me something about passes. When you're learning a new piece, you don't play the whole thing perfectly on the first try. You play it slowly, fix the wrong notes, correct the timing, then fix the phrasing, then the dynamics. Each run is a pass, and each one builds on the last until what comes out sounds nothing like the stumbling first attempt, but means exactly the same thing. The optimiser does the same thing. The IR that enters is technically correct and the IR that exits is still correct; only better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kgwhyquel08cbbecl0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kgwhyquel08cbbecl0l.png" alt=" " width="800" height="1007"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>llvm</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>Compiler Optimisations</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Tue, 24 Feb 2026 11:03:40 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/compiler-optimisations-6al</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/compiler-optimisations-6al</guid>
      <description>&lt;p&gt;Before we perform compiler optimisations, we must know what they are, why they're needed, and how we do them.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;What:&lt;/u&gt;&lt;br&gt;
Compiler Optimisations are systematic transformations that rewrite our source code into faster, smaller machine code without changing its meaning.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Why:&lt;/u&gt;&lt;br&gt;
They're needed because programs are full of inefficiencies—redundant calculations, impossible branches, or operations the CPU could do cheaper. Raw code from the parser is readable but slow, so optimisations squeeze out every drop of performance. &lt;/p&gt;

&lt;p&gt;Importantly, many impactful optimisations aren't fully hardware-agnostic. Universal ones like "this expression is always 0, delete it" work anywhere, but the big wins (like using SIMD registers, scheduling for a chip's pipeline, or picking specific CPU instructions) are often hardware-specific. &lt;/p&gt;

&lt;p&gt;Without a shared layer like LLVM's IR, every backend duplicates this effort. LLVM lets you write optimisations once against the IR, with backends handling hardware translation later.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;How:&lt;/u&gt;&lt;br&gt;
Compilers apply optimisations in structured passes over the code. First local (within basic blocks, like folding constants), then global (across the function, like dead code elimination), often in multiple rounds. Each pass analyses data flow, rewrites IR, and repeats until no more gains are possible.&lt;/p&gt;

&lt;p&gt;As I'd mentioned in the previous post, LLVM uses SSA for this. In action, it's the &lt;code&gt;%addtmp&lt;/code&gt; or &lt;code&gt;%multmp1&lt;/code&gt; variables we keep seeing in Kaleidoscope's output. While it looks redundant (mostly because we're used to seeing the output directly), it's what optimisation ultimately depends on.&lt;/p&gt;

&lt;p&gt;In most programming languages, we can change/reassign a variable's value whenever we want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 5
x = x + 2
x = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In SSA form, every variable is assigned exactly once. If we wish to change the value of x, the compiler creates a new version of it. (Remember persistent data structures and the blockchain example from the Lox resolver?) Hence, the code above looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%x1 = 5
%x2 = %x1 + 2
%x3 = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, through dead-code elimination (see below), it would become more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%x3 = 10 #because %x2 isn't used anywhere and %x3 comes immediately after
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is to address the issue of Data Flow Analysis — where did a value come from?&lt;/p&gt;

&lt;p&gt;In non-SSA code, if you see a variable on a certain line, you'll have to look at every line before it to figure out which assignment currently "owns" that variable.&lt;/p&gt;

&lt;p&gt;But in SSA, the name of the variable is its definition. We know exactly where &lt;code&gt;%x2&lt;/code&gt; was born and what value it holds.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;How SSA is used in Compiler Optimisations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) &lt;u&gt;Constant Propagation and Folding:&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;It took me a while to understand that these were two different things. But it's only that they both feed into each other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Propagation&lt;/strong&gt; is simply fetching values. If I know the value of a constant used in certain expressions, I'll just replace that variable with its value directly.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r = 5
area = 3.14 * r * r
#becomes
area = 3.14 * 5 * 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Folding&lt;/strong&gt; is merely precomputing known values — the actual math is performed at compile-time instead of runtime. It's like saying, "I know the answer to this. Why do I make the CPU calculate it later?"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;area = 3.14 * 5 * 5
#becomes
area = 78.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;u&gt;Value Range Propagation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracking the possible range of values a variable can hold, so the compiler can make smarter decisions later in the program.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (x &amp;gt; 0 &amp;amp;&amp;amp; x &amp;lt; 10):
    y = x * 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The compiler now knows &lt;code&gt;y&lt;/code&gt; must be in the range (0, 20). If a later condition asks something like &lt;code&gt;if (y &amp;gt; 100)&lt;/code&gt;, the compiler can eliminate that branch entirely because it's impossible.&lt;/li&gt;
&lt;li&gt;SSA makes it easy to track a variable's range through the program since each definition has a single, known origin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;u&gt;Sparse Conditional Constant Propagation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constant propagation + branch awareness; only propagating values along branches that are actually reachable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 5
if (x &amp;gt; 10):
    y = x * 2   #dead; compiler knows x = 5 can never satisfy x &amp;gt; 10
else:
    y = x + 1   #compiler propagates: y = 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;SSA names are unique per definition, so once the compiler knows a branch is dead, every variable inside it is unreachable too. This avoids wastage of effort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Dead Code Elimination:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing code that will never execute or whose result is never used.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 10
y = x + 5    #y is never used again
return x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = 10
return x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Every SSA variable has a list of uses. If that list is empty, the variable (and the code that produced it) is eliminated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Global Value Numbering:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assigning a symbolic "number" to each unique computation, then replacing duplicates that produce the same result with a single reference.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a = x + y
b = x + y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Both expressions get the same value number. The compiler replaces &lt;code&gt;b&lt;/code&gt; with &lt;code&gt;a&lt;/code&gt;, computing &lt;code&gt;x + y&lt;/code&gt; only once, even if &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are in different parts of the function. Again, SSA at play.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6) &lt;u&gt;Partial Redundancy Elimination:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing calculations that are redundant on some paths through the program, by hoisting them to a point where they cover all paths.&lt;/li&gt;
&lt;li&gt;In the following example, in case of &lt;code&gt;heavy_traffic = true&lt;/code&gt;, The CPU calculates &lt;code&gt;distance/speed&lt;/code&gt; inside the &lt;code&gt;if&lt;/code&gt; block, then calculates it again at the end. That’s unnecessary.&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;else&lt;/code&gt;, The CPU skips the first calculation and only does it once at the end.&lt;/li&gt;
&lt;li&gt;Compiler optimisation's goal is to make the work uniform so the final result is always "pre-calculated."
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (heavy_traffic):
    time = distance/speed
else:
    pass

total_time = distance/speed #why calculate the same thing a second time?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler "hoists" the missing calculation into the else block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (heavy_traffic):
    tmp = distance/speed
    time = tmp
else:
    tmp = distance/speed

#Now the final result just uses the 'tmp' already in the register
total_time = tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;It might look like we're adding code, but we’re actually ensuring that no matter which path the CPU takes, it only performs the heavy division exactly once.&lt;/li&gt;
&lt;li&gt;SSA tracks exactly where every value was computed. So the compiler can see at a glance, "this path did the work, that one didn't."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;7) &lt;u&gt;Strength Reduction:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swapping out a costly operation for a cheaper one that produces the same result.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = x * 8
# becomes
y = x &amp;lt;&amp;lt; 3 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Multiplying by 8 is multiplying by 2³. In binary, multiplying by 2 is a left shift by one bit, so multiplying by 8 is simply a left shift by three bits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The CPU performs this using a barrel shifter, which shifts bits in a single clock cycle. It’s basically a network of wires and switches that reroutes bits to new positions simultaneously; more like rearranging than computing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;General multiplication is more complex. The hardware must perform multiple additions and shifts internally, requiring more circuitry and cycles.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;8) &lt;u&gt;Register Allocation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Mapping the potentially unlimited SSA virtual variables down to the finite number of physical CPU registers available at runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ex:&lt;/strong&gt; If the function produces &lt;code&gt;%tmp1&lt;/code&gt; through &lt;code&gt;%tmp20&lt;/code&gt; but the CPU only has 6 registers, the compiler figures out which temporaries work at the same time and assigns them registers accordingly, spilling the rest to the stack. Good allocation means fewer memory round-trips and faster code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Having established some context to compiler-optimisations, we can proceed with adding support for them and JIT, for Kaleidoscope.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Optimisation, at its core, is about simplicity. There's an incredible story from the Mahabharata about this. Dronacharya, the renowned guru, gathered his students—already among the world's finest—to find the greatest archer. The goal was the eye of a wooden bird perched high in a tree. Pointing to it, he asked each student what they saw. They described the tree, the bird’s feathers, and the sky. He dismissed them. When he asked Arjuna, the latter replied, “I only see the eye of the bird.” He shot, and the arrow struck the centre instantly. By treating everything except the target as “noise,” we focus all resources on the singular point of impact. Compiler optimisations do the same, ensuring the CPU never looks at anything but the essential logic. After all, &lt;em&gt;Neti Neti&lt;/em&gt;-ing eventually led those ancient minds to the Ultimate Answer.&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
      <category>llvm</category>
    </item>
  </channel>
</rss>
