<?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 #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>
    <item>
      <title>LLVM #1 — Lexer, Parser, Codegen</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Fri, 13 Feb 2026 06:14:09 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-1-lexer-parser-codegen-51of</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-1-lexer-parser-codegen-51of</guid>
      <description>&lt;p&gt;The Lexer/Scanner and Parser aren’t very different from what we’ve done earlier. Both are initial/front-end phases of compilation, after which the code is converted into LLVM IR.&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%2Fbuia113ocfszp9jyb2fy.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%2Fbuia113ocfszp9jyb2fy.png" alt=" " width="772" height="1024"&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/commits/main/src?since=2025-12-05&amp;amp;until=2026-02-09" rel="noopener noreferrer"&gt;Commits 15710fe, 2562e61&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;The Lexer:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads raw text input one character at a time and groups them into meaningful chunks of code called Tokens.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enum Token&lt;/code&gt; defines the kinds of words the language can process.
&lt;code&gt;gettok()&lt;/code&gt; loops through characters, skips whitespaces, recognises keywords/numbers, and handles comments too (by skipping text after &lt;code&gt;#&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;Once the tokens are ready, we need to understand how they relate to each other. A tree is the best way to do the same.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ExprAST&lt;/code&gt; is the base/parent class for all the nodes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;public: virtual ~ExprAST() = default;&lt;/code&gt;: Here, &lt;code&gt;Expr&lt;/code&gt; objects will later be manipulated through pointers to the base (&lt;code&gt;ExprAST&lt;/code&gt;) class. &lt;/li&gt;
&lt;li&gt;Without &lt;code&gt;virtual&lt;/code&gt;, if we wish to delete a subclass node, only the base class' destructor will be called, not the subclass' destructor. Any data stored in the subclass won't be deleted and can affect other areas through leaks.&lt;/li&gt;
&lt;li&gt;Thus, we use &lt;code&gt;virtual&lt;/code&gt; to delete/release all memory/resources used by the derived (sub)class.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;NumberExprAST' represents a number,&lt;/code&gt;VariableExprAST&lt;code&gt;represents a variable,&lt;/code&gt;BinaryExprAST&lt;code&gt;represents an operator with two operands,&lt;/code&gt;CallExprAST&lt;code&gt;represents a function call,&lt;/code&gt;PrototypeAST&lt;code&gt;represents a function signature (not the body), and&lt;/code&gt;FunctionAST` represents a full function (prototype + body)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Uses operator-precedence parsing for building AST objects for binary expressions and recursive descent parsing for everything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; On seeing a number token, it creates a &lt;code&gt;NumberExprAST&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;u&gt;Code Generation:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is LLVM specific. We traverse through the AST we've built and ask each node to &lt;code&gt;codegen()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ex:&lt;/strong&gt; In &lt;code&gt;NumberExprAST&lt;/code&gt;, we use &lt;code&gt;ConstantFP::get&lt;/code&gt; to create a floating-point constant in LLVM's internal format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NamedValues&lt;/code&gt; is a symbol table/dictionary for remembering where (in which specific memory location/register) a variable is stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; LLVM uses Static Single Assignment (SSA), meaning its "virtual registers" can only be assigned once. It's why we see names like &lt;code&gt;%multmp&lt;/code&gt; and &lt;code&gt;%multmp1&lt;/code&gt; in our results.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BinaryExprAST::codegen&lt;/code&gt; is for generating code for the left and right sides recursively. &lt;/li&gt;
&lt;li&gt;Then, we use &lt;code&gt;Builder&lt;/code&gt; to create the math instruction (like &lt;code&gt;Builder-&amp;gt;CreateFAdd&lt;/code&gt; for float addition)&lt;/li&gt;
&lt;li&gt;For &lt;code&gt;&amp;lt;&lt;/code&gt;, it converts the result to a float (0.0 or 1.0) as Kaleidoscope only uses doubles.&lt;/li&gt;
&lt;li&gt;Similarly, &lt;code&gt;FunctionAST::codegen&lt;/code&gt; creates a new function in the LLVM &lt;code&gt;Module&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Module&lt;/code&gt; here is like a project folder; a container that holds all our functions together so they can see and call each other.&lt;/li&gt;
&lt;li&gt;It creates a block called &lt;code&gt;entry&lt;/code&gt;. LLVM code lives inside such basic blocks (chunks of instructions)&lt;/li&gt;
&lt;li&gt;It tells the &lt;code&gt;Builder&lt;/code&gt; to start writing instructions into this new block.&lt;/li&gt;
&lt;li&gt;It adds the function arguments to &lt;code&gt;NamedValues&lt;/code&gt; so the body can use them.&lt;/li&gt;
&lt;li&gt;Finally, it creates a &lt;code&gt;ret&lt;/code&gt; instruction to finish the function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) &lt;u&gt;Driver Code:&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;MainLoop&lt;/code&gt; is an infinite loop printing &lt;code&gt;ready&amp;gt;&lt;/code&gt; waiting for the user to type.&lt;/li&gt;
&lt;li&gt;It switches based on what is typed (&lt;code&gt;def&lt;/code&gt;, `extern, or just math) and calls the appropriate handler.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HandleDefinition&lt;/code&gt; parses the code, generates the LLVM IR, and prints it to the screen.&lt;/li&gt;
&lt;li&gt;It also sets up the operator precedence so the math logic works correctly, initialises the module, and starts the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s understand this through an example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;def foo(x) x + 1&lt;/code&gt; is converted by the Lexer into &lt;code&gt;[tok_def] [tok_identifier “foo”] [ ( ] [tok_identifier “x”] [ ) ] [tok_identifier “x”] [ + ] [tok_number1]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The Parser turns it into a &lt;code&gt;FunctionAST&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;The codegen turns that object into LLVM IR &lt;code&gt;define double @foo(double %x) { … }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;Results:&lt;/u&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%2Fy18g4vc7r3vxx3zwszp0.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%2Fy18g4vc7r3vxx3zwszp0.png" alt=" " width="800" height="132"&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%2Fuhdyb203ljte7xczdr2a.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%2Fuhdyb203ljte7xczdr2a.png" alt=" " width="800" height="137"&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%2Fl2df3yhosv5bh7t37jgv.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%2Fl2df3yhosv5bh7t37jgv.png" alt=" " width="800" height="279"&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%2Flqmze34cy46qarhtrimb.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%2Flqmze34cy46qarhtrimb.png" alt=" " width="798" height="844"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt; Optimising and Just-in-time (JIT) compilation!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
The one thing that calms me the most is playing the piano. You see, mindfulness and being focused is a rather difficult thing, especially in the attention economy. So it feels particularly wonderful and refreshing when an activity demands our time, patience, and full focus, lest we compromise on the quality of its outcome. “Right hand first, left hand next, and then do it together.” I feel like every single inch of my brain is dedicated to getting that one bar right. And nothing feels more rewarding than seeing (or maybe hearing?) the fruits of our labour. Whenever I’ve felt bored, lost, confused, overwhelmed, or even happy—the piano has helped me move on feeling better. My mom wanted us to learn some or the other instrument. I was merely “okay” with the idea of learning the piano, but never could I have fathomed how important it would become to me. Life’s little surprises are often like that, with some of the most beautiful experiences coming to us when we least expect it. So it doesn’t hurt to try and be a little open.&lt;/p&gt;

</description>
      <category>llvm</category>
      <category>compilers</category>
      <category>computerscience</category>
      <category>learning</category>
    </item>
    <item>
      <title>LLVM — Introduction and Setup</title>
      <dc:creator>Lahari Tenneti</dc:creator>
      <pubDate>Wed, 11 Feb 2026 12:37:50 +0000</pubDate>
      <link>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-introduction-and-setup-4c3c</link>
      <guid>https://dev.to/lahari_tenneti_4a8a082e9c/llvm-introduction-and-setup-4c3c</guid>
      <description>&lt;p&gt;Helloo! Welcome to my next project—The LLVM framework (the Kaleidoscope tutorial, to be precise). I decided to try this out as soon as I finished the jlox Interpreter in Robert Nystrom’s Crafting Interpreters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s LLVM?&lt;/strong&gt;&lt;br&gt;
The ‘Low Level Virtual Machine’ (LLVM) is a full compiler infrastructure that can take various programming languages, generate LLVM Intermediate Representation (IR), optimise it, and finally convert it into hardware-specific machine level code. This low level code is then run on the CPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why am I doing this?&lt;/strong&gt;&lt;br&gt;
The Abstract Syntax Trees (ASTs) borne out of interpreting any code are specific to its language. When we use LLVM’s IR, which is aware of hardware concepts like registers, memory, arithmetic operations, etc., our language becomes more universal.&lt;/p&gt;

&lt;p&gt;As against assembly language, which is very platform/hardware dependent, LLVM (whose IR almost resembles assembly language) supports various machines like Intel x86, ARM, RISC-V, etc. I find heterogenous hardware very fascinating, especially the prospect of hardware-agnostic compilation. That’s pretty much what drew me LLVM.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;How I set it up:&lt;/strong&gt;&lt;br&gt;
While there are many tutorials to familiarise oneself with the platform, I (surprisingly) found the documentation wonderful. Some of it I skipped, but the set-up and introduction were quite helpful. I still haven’t figured out a lot, so I’m taking things slowly. For anyone wanting to start out with LLVM, I’d recommend these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm" rel="noopener noreferrer"&gt;Getting the source code and building LLVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://llvm.org/docs/GettingStarted.html#an-example-using-the-llvm-tool-chain" rel="noopener noreferrer"&gt;An example for using the LLVM tool chain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disclaimer:&lt;/strong&gt; Most of what I did below is adapted from these tutorials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1) &lt;u&gt;Getting LLVM running&lt;/u&gt; on macOS (especially Apple Silicon) is very straightforward with &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew.&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;llvm
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/opt/llvm/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CMAKE_PREFIX_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/homebrew/opt/llvm"&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;u&gt;Managing Dependencies:&lt;/u&gt; I used CMake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llvm &lt;span class="nt"&gt;-config&lt;/span&gt; —version
&lt;span class="nb"&gt;cd&lt;/span&gt; ~
&lt;span class="nb"&gt;mkdir &lt;/span&gt;toy-compiler
&lt;span class="nb"&gt;cd &lt;/span&gt;toy-compiler
&lt;span class="nb"&gt;mkdir &lt;/span&gt;src build
nano CMakeLists.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Here's the &lt;code&gt;CMakeLists.txt&lt;/code&gt; (configuration) for the toy-compiler/Kaleidoscope project I'll be doing. It serves as a project manager, telling the compiler where the LLVM "brains" are and which specific libraries (like JIT, native codegen) we want to use. &lt;/li&gt;
&lt;li&gt;It doesn't compile the code itself, and instead gathers all the ingredients (libraries, headers, compiler settings) so the build tool (Ninja, in my case) knows what to do.
&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.13&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;ToyCompiler LANGUAGES C CXX&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD 17&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;CMAKE_CXX_STANDARD_REQUIRED ON&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;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&lt;/span&gt;&lt;span class="si"&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;add_executable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;toy src/main.cpp&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# For JIT and native codegen&lt;/span&gt;
&lt;span class="nf"&gt;llvm_map_components_to_libnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;llvm_libs core orcjit native&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;target_link_libraries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;toy &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;llvm_libs&lt;/span&gt;&lt;span class="si"&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 build system:&lt;/u&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To ensure the libraries are linking correctly, I wrote a simple sanity check in &lt;code&gt;src/main.cpp&lt;/code&gt; using LLVM's output stream, &lt;code&gt;llvm::outs()&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;"llvm/Support/raw_ostream.h"&lt;/span&gt;&lt;span class="cp"&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="n"&gt;llvm&lt;/span&gt;&lt;span class="o"&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;"LLVM setup works!&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="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;To actually compile the project, I used Ninja. It's a small build system focused on speed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;ninja
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/toy-compiler/build
cmake &lt;span class="nt"&gt;-G&lt;/span&gt; Ninja ..
ninja
./toy -&amp;gt; LLVM setup works!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) &lt;u&gt;An example:&lt;/u&gt; To truly understand how compilation happens at lower levels, it helps to follow a program through the entire pipeline, from high level code to bit code, and finally to a native binary. &lt;br&gt;
&lt;code&gt;test1.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&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="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"First go at LLVM!"&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;
&lt;code&gt;clang -O3 -emit-llvm test1.c -c -o test1.bc&lt;/code&gt; (Clang is the C compiler macOS uses as a front-end; &lt;code&gt;emit-llvm&lt;/code&gt; creates the bitcode.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lli test1.bc&lt;/code&gt; (&lt;code&gt;lli&lt;/code&gt; directly executes the bytecode.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llvm-dis &amp;lt; test1.bc | less&lt;/code&gt; (This is for looking at the human-readable LLVM assembly code.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llc test1.bc -o test1.s&lt;/code&gt; (&lt;code&gt;llc&lt;/code&gt; converts bitcode to native assembly.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gcc test1.s -o test1.native&lt;/code&gt; (This assembles the native file into a program.)&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;./test1.native&lt;/code&gt; → First go at LLVM!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What's next:&lt;/strong&gt;&lt;br&gt;
The lexer and parser for Kaleidoscope!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Musings:&lt;/strong&gt;&lt;br&gt;
Sometimes, I get these sudden, intense urges to just... be somewhere. I often find myself wishing for Doraemon’s "Anywhere Door" so I could instantly step into a completely different world. It’s not that I don’t enjoy where I am, but there’s an incomparable high that comes from being somewhere totally foreign.&lt;/p&gt;

&lt;p&gt;Travel has always been my ultimate meditation. I once &lt;a href="https://www.stevenmtaylor.com/essays/from-the-unreal-to-the-real/#:~:text=The%20same%20thing,place%20to%20them." rel="noopener noreferrer"&gt;read&lt;/a&gt; that our memories of new places are so vivid because we become hyper-sensitive to our surroundings. In our daily lives—the same commute, the same routine—we stop truly "seeing" the world. We stop noticing the colour of the sky, the old man reading the newspaper by the shopfront, or the little puppy prancing around with an aluminium foil. But in a new place, with your senses wide open, you notice everything.&lt;/p&gt;

&lt;p&gt;A few years ago, I visited Sikkim (an absolutely stunning state in our North-East), and I’ve never felt more alive. Even now, I can recall every sound, smell, and sight from that trip with perfect clarity. That level of observation taught me how to be more attentive to the daily moments in my life. Ever since, I've tried to carry that traveler’s spirit with me, finding joy in the small details—in the "normal" and the everyday.&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%2Fjucljmm85ixb9yvhnvcz.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%2Fjucljmm85ixb9yvhnvcz.png" alt=" " width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

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