{"id":19099503,"url":"https://github.com/dentrax/go-tilde-operator","last_synced_at":"2025-04-30T16:21:27.190Z","repository":{"id":103621420,"uuid":"372420738","full_name":"Dentrax/go-tilde-operator","owner":"Dentrax","description":"PoC: How to make ~ work on Go Compiler","archived":false,"fork":false,"pushed_at":"2021-06-22T12:32:44.000Z","size":61,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-19T05:15:49.208Z","etag":null,"topics":["compiler","go","golang"],"latest_commit_sha":null,"homepage":"https://medium.com/trendyol-tech/contributing-the-go-compiler-adding-new-tilde-operator-f66d0c6cff7","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Dentrax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-31T07:31:56.000Z","updated_at":"2024-01-12T14:47:31.000Z","dependencies_parsed_at":"2023-05-24T00:15:27.662Z","dependency_job_id":null,"html_url":"https://github.com/Dentrax/go-tilde-operator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dentrax%2Fgo-tilde-operator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dentrax%2Fgo-tilde-operator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dentrax%2Fgo-tilde-operator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dentrax%2Fgo-tilde-operator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dentrax","download_url":"https://codeload.github.com/Dentrax/go-tilde-operator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251739935,"owners_count":21635949,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","go","golang"],"created_at":"2024-11-09T03:51:01.419Z","updated_at":"2025-04-30T16:21:27.181Z","avatar_url":"https://github.com/Dentrax.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"Auto-generated by [mediumexporter](https://www.npmjs.com/package/mediumexporter)\n\n# Contributing the Go Compiler: Adding New Tilde (~) Operator\n\n\n\nCompilers have always had challenges to be solved, even today. Go is not so different too. There are so many things to do, so many problems to solve, so many features to implement… That’s why there is no such thing as “OK, this compiler is fully completed.”. It is almost impossible to say. In these years, we are actually so lucky that we have the opportunity to witness how legacy compiler structures are being restructured, improved, and increased efficiently together with the applying of modern approaches and solutions.\n\nMy obsession with compilers began while trying to write a simple memory reader with AutoIt for Silkroad Online. Something was somehow working perfectly integrated with the grace of God. So, how do they do it? I knew it would take me years to answer this question. But, it shouldn’t have been left unanswered. There are so many things to learn before digging into compilers. Missing or not knowing any of this information may be a major obstacle to our learning process. From CPU scheduling to Memory management, from logic design to instruction set architecture… Remember that we are trying to climb an enormous tree with an infinite number of branches. The soil of this tree is physics, obviously. I have learned that I still have a lot to learn.\n\nEspecially in the last two years, I had the opportunity to think a lot about compilers due to the pandemic. I have read many articles, papers, took a lot of notes, lot of trying. Monkey, which is one of the sources that inspired me. Do you like Monkey? Thorsten Ball did. Somehow, I came across an excellent book called “Writing A Compiler In Go”. After implementing the whole book in Rust, I immediately found the opportunity to clone the Go source code to examine further and strengthen the knowledge. In this learning process, an idea came to my mind that could answer questions such as “What have I learned?, What will I learn?, What I would like to learn?” and that I would enjoy sharing what I learned with you. Then I tried to write this article as much as I could. Do not forget to bring your coffee and background music with you. It is going to be a bit long journey.\n\n## Abstract\n\nThis study conceptualizes, examines, and aims the implementation process for a bitwise complement operator ~ *(pronounced as tilde)* in Go. Even though this article does not follow any official Go proposal, it will provide a guideline for writing a technical proposal for this operator initially. This article will describe a set of specific instructions for the step-by-step learning of the full implementation process.* *The first chapters will provide a brief introduction about Compiler Front-Ends and Compiler Back-Ends and describe the Tokenizer, Abstract Syntax Tree (AST), Parser, Scanner and deep-dives to more  Compiler Back-End parts of Op-Code, Intermediate Representation (IR), Static Single Assignment (SSA), architecture-based code generation, architecture based optimizer rules, and finally introduces a brand-new tilde operator. This background information lays the groundwork for the implementation of the operator in subsequent chapters.\n\n## Motivation\n\nCurrently, Go [does not have support](https://golang.org/ref/spec#Operators) for the~ unary operator. Neither implemented in the [original frontend](https://github.com/golang/gofrontend/blob/master/go/operator.h). I could find neither an actively discussing [issue](https://github.com/golang/go/issues?q=tilde) nor a [PR](https://github.com/golang/go/pulls?q=tilde). If you would ask why humankind on Earth who writes Go code needs an ~ operator: Why not? It is a great opportunity to learn compiler theory and start contributing to Go.\n\nI have never contributed to Go before. Together, we will learn how to contribute. In doing so, we will follow Go’s official documentation step by step. We *will* get feedback from the proposal that we are going to open up. Or maybe it will just be closed. But what we are aiming for is not whether the proposal we are doing got accepted or not. The point is to be able to do something educational and learn the process.\n\nActually, I do not know if we are going to do it correctly. But we can improve our implementation by getting feedback from the maintainers and the contributors.\n\n**P.S.: ***While I started writing this work, there is no such operator called Tilde. I just wanted to pull the latest commits from the master branch. What did I see? This operator [has been added](https://github.com/golang/go/commit/4bbe046aad2ca27f25d3811b061fb8f7926b8695) by [griesemer](https://github.com/griesemer). :( Yep, I couldn’t first :) So, I updated this post consequently.*\n\n## Table of Contents\n\n1.⠀Introduction\n⠀1.1.⠀Becoming a Contributor\n⠀1.2.⠀Preparing Environment\n⠀1.3.⠀Writing Proposal\n2.⠀Compiler Front-End\n⠀2.1.⠀go/* (Legacy)\n⠀⠀2.1.1.⠀Token\n⠀⠀⠀2.1.1.1. Operator Precedence\n⠀⠀2.1.2.⠀AST\n⠀⠀2.1.3.⠀Scanner\n⠀⠀2.1.4.⠀Parser\n⠀⠀2.1.5.⠀Constant\n⠀⠀2.1.6.⠀Math\n⠀⠀2.1.6.1.⠀big.Int\n⠀2.2.⠀cmd/compile\n⠀⠀2.2.1.⠀Differences with go/*\n⠀⠀2.2.2.⠀Creating Test Script\n⠀⠀2.2.3.⠀Token\n⠀⠀⠀2.2.3.1.⠀Operator String\n⠀⠀⠀2.2.3.2.⠀[Stringer](https://pkg.go.dev/golang.org/x/tools/cmd/stringer#pkg-overview)\n⠀⠀2.2.4.⠀Scanner\n⠀⠀2.2.5.⠀Parser\n3. Compiler Middle-End\n⠀3.1.⠀Intermediate Representation\n⠀⠀3.1.1.⠀Node\n⠀⠀3.1.2.⠀Expr\n⠀3.2.⠀Escape Analysis\n⠀3.3.⠀Walk\n⠀3.4.⠀Constant\n⠀3.5.⠀Math\n⠀⠀3.5.1.⠀big.Int\n4.⠀Compiler Back-End\n⠀4.1.⠀Introduction\n⠀4.2.⠀Static Single Assignment (SSA)\n⠀4.3.⠀Go’s SSA Backend\n⠀4.3.1.⠀Generic OPs\n⠀4.3.2.⠀Rewrite Rules\n5.⠀Result\n⠀5.1.⠀Trying New Operator\n⠀5.2.⠀Test \u0026 Build Go Compiler\n6.⠀Conclusion\n7.⠀Furthermore\n8.⠀References\n⠀8.1.⠀Normative\n⠀8.2.⠀Informative\n\n## 1. Introduction\n\nProgramming languages are not magical. They were built by blood, sweat, and tears. We *may* do not know what kind of difficulties they had been going through. What we know about them is that every single language “just works”. Have you ever asked yourself “how”? Well, never mind, neither did I. But instead, I rather keep telling myself, “It is more important the know how something can be created *from scratch*” rather than “How it works?”. If we knew *“How to create something”*, it would be easier for us to understand *“How it works?”. *Moreover, we can have the skills and knowledge to answer this question ourselves. Let’s move on to what we’re going to do without too much noisy talk. We are not going to release a book at the end of the day, are we?\n\nWhat we will be achieving in this article is such a brand-new *tilde* ~ operator in Go. So, first things first, we are going to write an educational [proposal](https://github.com/golang/proposal) by opening an issue at GitHub. Then, we will prepare our build environment, and we follow the Go contribution guideline, open-source culture, and standards.\n\nWe can consider this article in two parts in general: Compiler Front-End and Compiler Back-End. In the Compiler Frond-End section, we will see what an *Abstract Syntax Tree (AST)* is, extract AST using Scanner, what *Tokens* are, how tokens are read by *Parser* and create meaningful statements… In the Compiler Back-End section, we will start implementing core logic, a.k.a. One’s Complement *(bitwise not)*, Op-Code with auto code generation. Then, we will touch SSA a bit for creating architecture-based optimizer rules (i.e., RISCV64, Generic). Finally, we will deep dive into the Go Compiler universe for the final touches. Let the party begin!\n\n### 1.1. Becoming a Contributor\n\nThe Go project welcomes all contributors. The first step is registering as a Go contributor and configuring your environment. Here is a checklist of the required steps to follow: [[0]](https://golang.org/doc/contribute#contributor)\n\n* **Step 0**: Decide on a single Google Account you will be using to contribute to Go. Use that account for all the following steps and make sure that git is configured to create commits with that account's e-mail address.\n\n    $ git config --global user.email name@example.com\n\n* **Step 1**: [Sign and submit](https://cla.developers.google.com/clas) a CLA (Contributor License Agreement).\n\n* **Step 2**: Configure authentication credentials for the Go Git repository. Visit [go.googlesource.com](https://go.googlesource.com), click “Generate Password” in the page’s top-right menu bar, and follow the instructions.\n\n* **Step 3**: Register for Gerrit, the code review tool used by the Go team, by [visiting this page](https://go-review.googlesource.com/login/). The CLA and the registration need to be done only once for your account.\n\n* **Step 4**: Install git-codereview by running go get -u golang.org/x/review/git-codereview\n\n### 1.2. Preparing Environment\n\n* **Step 1:** Clone the source code\n\n    $ git clone https://go.googlesource.com/go\n    $ cd go\n\n*Clone anywhere you want as long as it’s outside your $GOPATH . Clone from go.googlesource.com (not GitHub).*\n\n* **Step 2:** Prepare changes in a new branch\n\n    $ git checkout -b feature/new-operator-tilde\n\n*We will work on the feature/new-operator-tilde branch that checked out from the default working branch.*\n\n* **Step 3:** Verify the compilation succeed\n\n    $ cd ./src\n    $ ./make.bash # use ./all.bash to run the all tests\n\n    Building Go cmd/dist using /usr/local/Cellar/go/1.16.3/libexec. (go1.16.3 darwin/amd64)\n    Building Go toolchain1 using /usr/local/Cellar/go/1.16.3/libexec.\n    Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.\n    Building Go toolchain2 using go_bootstrap and Go toolchain1.\n    Building Go toolchain3 using go_bootstrap and Go toolchain2.\n    Building packages and commands for darwin/amd64.\n    ---\n    Installed Go for darwin/amd64 in /Users/furkan/src/public/go\n    Installed commands in /Users/furkan/src/public/go/bin\n\n    # gtime: 253.04user 40.64system 0:58.38elapsed 503%CPU\n\n*Make sure go is installed in your shell path so that the go toolchain uses the Go compiler itself.*\n\nThe project welcomes code patches, but you should discuss any significant change before starting the work to ensure things are well coordinated. [[1]](https://golang.org/doc/contribute#before_contributing)\n\nSo, this is exactly what we will do as a next step.\n\n### 1.3. Proposing\n\nThe crucial step is that before we start doing anything, we need to write down what we will achieve at the end of the day. It may not be right to work and make decisions alone. We can not do random walks on such a large company or community-driven big open-source projects, right?. We cannot just randomly write a sloppy proposal. Since the Go project’s development process is *design-driven*, we must follow the [proposal design document](https://github.com/golang/proposal) standards.\n\nWe can create an issue to discuss the feature idea just before we start writing the *actual* proposal. Since we will not do compiler change but small implementation, it's OK to create the proposal as an issue by following the [TEMPLATE](https://github.com/golang/proposal/blob/master/design/TEMPLATE.md).\n\u003e **Title:** proposal: spec: add built-in tilde ~ (bitwise not) unary operator\n**Description:\n***TL;DR: I propose adding built-in support for ~*(bitwise not) unary *operator.*\n\u003e ***Abstract*\n***The ~ operator *(pronounced as tilde)* is *complement (unary) operator*, performs a [*bitwise NOT operation](http://en.wikipedia.org/wiki/Bitwise_operators#NOT)*. It takes one bit operand and returns its complement. If the operand is 1, it returns 0, and if it is 0, it returns 1. When applied on numbers, represented by [*bitwise not](https://en.wikipedia.org/wiki/Bitwise_operation#NOT)*, it changes the number’s sign and then subtracts one. [[2]](https://stackoverflow.com/a/3952128/5685796)*\n\u003e *For example:\n10101000 11101001 // Original  (-22,295 in 16-bit one's complement) \n01010111 00010110 // ~Original (22,294 in 16-bit one's complement)*\n\u003e I propose the addition of a new built-in ~ operator.\n\u003e ***Motivation***\n*Currently, Go [does not have support](https://golang.org/ref/spec#Operators) for ~ operator. I could find neither an actively discussing [issue](https://github.com/golang/go/issues?q=tilde) nor a [PR](https://github.com/golang/go/pulls?q=tilde). It is a great opportunity to learn compiler theory and get used to Go compiler internals.*\n\u003e ***Backgroud**\nTilde ~ operator widely implemented and used by some programming languages. (i.e. [Python](https://blog.finxter.com/tilde-python/), [C/C++](https://www.cplusplus.com/reference/ciso646/), [Java](https://www.tutorialspoint.com/java/java_bitwise_operators_examples.htm), [Rust (!)](https://doc.rust-lang.org/stable/std/ops/trait.Not.html), [etc.](https://en.wikipedia.org/wiki/Tilde#Computing)). Can be used for [different purposes](https://en.wikipedia.org/wiki/Tilde#Computing) in every programming language. We can start implementing this as a unary operator. In the some programming languages, the tilde character is used as [bitwise NOT](https://en.wikipedia.org/wiki/Bitwise_NOT) [operator](https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B), following the notation in logic: ~p *not* means not p, where p is a [proposition](https://en.wikipedia.org/wiki/Proposition).*\n\u003e ***Proposal\n**I propose that ~ should work like any other bitwise operator as described in the spec. We will only support [*prefix notation](https://en.wikipedia.org/wiki/Prefix_notation)* usage on integers. i.e. ~EXPR* .* Following codes should compile successfully:\n** *~1997 // returns -1998 \n* a := 7 and ~a // returns -8\n* *~-15 *// returns *14*\n* *-~15 *// returns 16\n* foo := func(x int) int { return ~x } and ~foo(707) // returns *-708*\n* if ~7 \u003c 5 { // true }\n* ~3.1415 // INVALID\n* x ~= y // INVALID\n* ~bool // INVALID\n* 5~7 // INVALID \n* 7~ // INVALID\nSince the bitwise logical and shift operators apply to integers only, [[3]](https://golang.org/ref/spec#Arithmetic_operators) I propose that we will support only integers.*\n\u003e ***Compatibility\n**This change is fully backwards compatible.*\n\u003e ***Complexity\n**Since we will add just another single unary operator to the compiler, I think it will not cause even the tiny complexity. Tilde ~ just one of the unary operation standards in mathematics. The compiler already supports other bitwise operations. (i.e. *\u0026, | ,^, \u0026^*)*\n\u003e ***Implementation**\nAt a minimum this will impact:\n* spec: add operator to relevant part\n* compiler frontend: add new Token, update Scanner and Parser\n* compiler backend: add OpCode, update SSA and GC\n* optimization: i.e. ~(~7) = 7\n* docs: the tour, all educational resources\n\n*We will be implementing this operator on a [W.I.P] PR simultaneously by writing a “Contribution the Compiler Guide” on Medium.\n\n***Furthermore\n**After implemented for integers, we *may* also add different features for this operator, in the future:\n* *indicating type equality or lazy pattern match\n* object comparison\n* [array](https://en.wikipedia.org/wiki/Array_data_structure) [concatenation](https://en.wikipedia.org/wiki/Concatenation)\n* … and so on.\n\n## 2. Compiler Front-End\n\nWe all know that *almost* every website has a back-end service architecture. Compilers are not so different either, just like we humans read the texts with our eyes and send them back to the visual cortex in our brains. We are not going to go deep into technical details here.  Let’s take a brief overview of some important parts. We will explain them all individually.\n\n![High-Level Overview of Compiler Front-End](https://cdn-images-1.medium.com/max/3944/1*mfz_IhrDTW9QCFP5E4YPlg.png)*High-Level Overview of Compiler Front-End*\n\nAll front-end analyzers *(i.e., lexical, syntax)* analyze the source code to build an *Intermediate Representation (IR)* program. An Intermediate Representation is the data structure used internally by a compiler to represent source code, which is conducive to further processing, such as optimization and translation.\n\u003e [The frontend](https://github.com/golang/gofrontend) was originally developed at Google, and was released in November 2009. It was originally written for GCC.  It was originally written by [Ian Lance Taylor](https://www.airs.com/ian/)\n\nNow we can choose our favorite Go editor *(i.e., Vim, GoLand, etc.)* to move forward. I will go with [Emacs](https://github.com/hlissner/doom-emacs/) in this demo. \n\nFind the Go spec packages under ./src/go. We will start implementing the token package.\n\n![Editor Overview in Package of token](https://cdn-images-1.medium.com/max/8640/1*PXeEZR29WQRwaK9UypWOXQ.png)*Editor Overview in Package of token*\n\n### 2.1.⠀go/* (Legacy)\n\n### 2.1.1. [Token](https://github.com/golang/go/tree/master/src/go/token)\n\n[ELI5:](https://www.reddit.com/r/explainlikeimfive/) Tokens are the smallest cornerstone in the compiler design. Those units add *meaning* to the source code that we are writing. This is the most important difference that distinguishes programming languages from our daily speaking languages.\n\nSpeaking Language:\n\n![](https://cdn-images-1.medium.com/max/3304/1*kVCbahfVSBjBbalFF7b48Q.png)\n\nProgramming Language:\n\n![](https://cdn-images-1.medium.com/max/2752/1*JbayHoUIgxCM1izhq2piwg.png)\n\nTokens are highlighted in blue. Tokens themselves are small, easily categorizable data structures that are then fed to the *parser.*\n\nLet’s suppose we replace these *words* with the Go correspondences:\n\n    The: func\n    quick: foo\n    brown: var1\n    fox: string\n    jumps: var2\n    over: int\n    the: error\n    lazy: bool\n    dog: true\n\nYes, just like you thought! We created *meaning* by using tokens in the sentence we wrote following the above. The tokens we use here are: “‘(‘, ‘,’, ‘)’, ‘{‘, ‘}’” , respectively.\n\nOK, we are going to write our first code in this section. Let’s change the current directory to the [token](https://github.com/golang/go/tree/master/src/go/token). In the [token.go](https://github.com/golang/go/blob/master/src/go/token/token.go), we have to define our new token as a *const *variable:\n\nWe will define it as TILDE, as [CPython ](https://github.com/python/cpython/blob/master/Parser/token.c#L104)did.\n\n    const (\n     // Special tokens\n     ILLEGAL Token = iota\n     ...\n     SHR     // \u003e\u003e\n     AND_NOT // \u0026^\n     TILDE   // ~\n     ...\n    )\n\n    var tokens = [...]string{\n     ILLEGAL: \"ILLEGAL\",\n     ...\n     SHR:     \"\u003e\u003e\",\n     AND_NOT: \"\u0026^\",\n     TILDE:   \"~\",\n     ...\n    }\n\n### 2.1.1.1. Operator Precedence\n\nOperator Precedence represents the *order of operations,* which should make clearer what operator precedence describes: which priority do different operators have.\n\nA canonical example:\n\n1 + 3 – 5 * 7 / ~9 \n\nWhich will become:\n\n1 + (3 — ((5 * 7) / (~9))) \n\nThat’s because the * and / operators have higher precedence *(rank)*. It means that these are *more significant *than the + and —  operators. These must get evaluated before the other operators do.\n\nOperators that have the same precedence are bound to their arguments in the direction of their associativity. For example, the expression a = b = c is parsed as a = (b = c)And not as (a = b) = c because of *right-to-left* [associativity of assignment](https://en.wikipedia.org/wiki/Operator_associativity), but a + b — c is parsed (a + b) — c and not a + (b — c) because of the *left-to-right* associativity of addition and subtraction. [[5]](https://en.cppreference.com/w/cpp/language/operator_precedence)\n\n![[Go Operator Precedence](https://github.com/golang/go/blob/fe26dfadc3630617d133b8c94bcb2ccb2e85dc1b/src/go/token/token.go#L259-L277)](https://cdn-images-1.medium.com/max/2632/1*3lxz-5dZOyFIoDsHDFQu6Q.png)*[Go Operator Precedence](https://github.com/golang/go/blob/fe26dfadc3630617d133b8c94bcb2ccb2e85dc1b/src/go/token/token.go#L259-L277)*\n\nPython has [defined at the FACTOR](https://github.com/python/cpython/blob/503cdc7c124cebbd777008bdf7bd9aa666b25f07/Lib/ast.py#L1318) level in the [*unary operator precedence table](https://github.com/python/cpython/blob/503cdc7c124cebbd777008bdf7bd9aa666b25f07/Lib/ast.py#L1315-L1321)*. Unary [FACTOR level](https://github.com/python/cpython/blob/503cdc7c124cebbd777008bdf7bd9aa666b25f07/Lib/ast.py#L657) simply means:\n\n-7, +7, ~7\n\nWe can define TILDE at level 5:\n\n    func (op Token) Precedence() int {\n     switch op {\n     ...\n     case MUL, QUO, REM, SHL, SHR, AND, AND_NOT, TILDE:\n      return 5\n     ...\n    }\n\n### 2.1.2. Abstract Syntax Tree (AST)\n\nAbstract Syntax Tree (AST) is a tree that represents the syntactic structure of a language construct according to our grammar definition. It basically shows how your parser recognized the language construct, that said, it shows how the start symbol of your grammar derives a certain string in the programming language.\n\nLet’s write a simple Go snippet:\n\n![Go Code Snippet](https://cdn-images-1.medium.com/max/2000/1*Z4yOjNihMT3UbsXdfT9FgQ.png)*Go Code Snippet*\n\nIf we wanted to get the *AST *at high-level, we would get something like the following:\n\n![High-Level Overview of AST](https://cdn-images-1.medium.com/max/2884/1*c4UV17U7RJZKaC0vD-lpGg.png)*High-Level Overview of AST*\n\nIt is not the real world equivalent; we ignored and simplified many things in the tree. You can think of it as a *pseudo-AST*. If we wanted to get a real representation of AST for this code snippet:\n\n    *package *main\n    *func *main() {\n       x := 5\n       y := 3\n       *for *x \u003e y {\n          *if *x == 5 {\n             z := 3 + (5 * 7)\n          }\n       }\n    }\n\nThen you can use either an AST viewer (i.e., [*goast-viewer](https://github.com/yuroyoro/goast-viewer)*) or Go’s official parser package *(in the next section, we will see what the parser is)* to dump the whole AST into stdout manually:\n\n    package main\n    \n    import (\n        \"go/ast\"\n        \"go/parser\"\n        \"go/token\"\n        \"os\"\n    )\n    \n    func main() {\n        fset := new(token.FileSet)\n        f, _ := parser.ParseFile(fset, \"your_source_code.go\", nil, 0)\n        ast.Print(fset, f)\n    }\n\nThis code will print out approximately ~170 lines of AST. I wrapped some object references and positions in the following picture:\n\n![Output of GoAst Viewer](https://cdn-images-1.medium.com/max/2904/1*Y5meRQOhu52onXTjdUpoHA.png)*Output of GoAst Viewer*\n\n### 2.1.3. [Scanner](https://github.com/golang/go/tree/master/src/go/scanner)\n\nThis is where your *syntax error* messages throwing from. Scanner, scans the next token and returns the token position, the token, and the literal string if applicable. The scanner initializes from the *Parser*. Scanner does not create any *AST* structure; all it does is read tokens and catch any semantic *(syntax)* errors.\n\nLet’s jump to ./scanner/scanner.go file:\n\n    func (s *Scanner) Scan() (pos token.Pos, tok token.Token, lit string) {\n     ...\n     switch ch := s.ch; {\n     ...\n     default:\n      ...\n      case '|':\n       tok = s.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR)\n      case '~':\n       tok = token.TILDE\n      default:\n       ...\n      }\n     }\n    }\n\nThe swtich2, swtich3, and swtich4 [functions](https://github.com/golang/go/blob/690a8c3fb136431d4f22894c545ea99278758570/src/go/scanner/scanner.go#L762-L796) are for scanning multi-byte tokens such as \u003e\u003e , += , \u003e\u003e , = . Different routines recognize different length tokens based on matches of the *n-th* character.\n\n* % is a swtich2 , because only allowed: %, %= \n\n* + is a swtich3 , because only allowed: +, +=, ++\n\n* \u003e is a swtich4 , because only allowed: \u003e, ≥, \u003e\u003e, \u003e≥ \n\nAdding only two lines completes our work here. Let’s write some test in ./scanner/scanner_test.go to test our new token.\n\nUpdate the tokens:\n\n    var tokens = […]elt {\n     ...\n     // Operators and delimiters\n     {token.SHR, \"\u003e\u003e\", operator},\n     {token.AND_NOT, \"\u0026^\", operator},\n     {token.TILDE, \"~\", operator},\n     ...\n    }\n\n\nUpdate the lines:\n\n    var lines = []string {\n     \"\",\n     \"\\ufeff#;\", // first BOM is ignored\n     ...\n     \"\u003e\u003e\\n\",\n     \"\u0026^\\n\",\n     \"~\\n\",\n     ...\n    }\n\nRun the tests:\n\n![Scanner Tests](https://cdn-images-1.medium.com/max/8640/1*pNCkM8TG_dC_pkrmmI_t8Q.png)*Scanner Tests*\n\nYay! Our tests are passing!\n\n### 2.1.4. [Parser](https://github.com/golang/go/tree/master/src/go/parser)\n\nGo parser converts this list of tokens into a Tree-like object to represent how the tokens fit together to form a cohesive whole. Input may be provided in various forms; the output is an *Abstract Syntax Tree (AST)* representing the Go source.\n\u003e A **parser** is a software component that takes input data (frequently text) and builds a [data structure](https://en.wikipedia.org/wiki/Data_structure) — often some kind of P[arse Tree](https://en.wikipedia.org/wiki/Parse_tree), A[bstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) or other hierarchical structure, giving a structural representation of the input while checking for correct syntax. The parsing may be preceded or followed by other steps, or these may be combined into a single step. The parser is often preceded by a separate [lexical analyser](https://en.wikipedia.org/wiki/Lexical_analysis), which creates tokens from the sequence of input characters; alternatively, these can be combined in [scannerless parsing](https://en.wikipedia.org/wiki/Scannerless_parsing). [[4]](https://en.wikipedia.org/wiki/Parsing#Parser)\n\nWait… The last sentence *may* a *bit* important for us.\n\u003e **Scannerless parsing** performs tokenization (breaking a stream of characters into words) and parsing (arranging the words into phrases) in a single step, rather than breaking it up into a [pipeline](https://en.wikipedia.org/wiki/Pipeline_(software)) of a [lexer](https://en.wikipedia.org/wiki/Lexical_analysis) followed by a [parser](https://en.wikipedia.org/wiki/Parser), executing [concurrently](https://en.wikipedia.org/wiki/Concurrent_computation).\n\nGo does use a built-in scanner, which means that Go does a separate lexical analysis already.\n\nLet’s assume that we have the necessary compiler front-end components that *somehow* communicate with each other. Usually, the *basic* overall structure will look something like the following:\n\n![Medium Level Overview of The Parser Interface](https://cdn-images-1.medium.com/max/5464/1*Wmr1EE83IToLP0QaNn9WsQ.png)*Medium Level Overview of The Parser Interface*\n\nAt the beginning of this section, we have drawn a high-level overview of Compiler Front-End flow, as you remember. But this… is a medium-level overview of the [Parser Interface](https://github.com/golang/go/blob/master/src/go/parser/interface.go) that describes how Scanner and Parser communicate through exported functions. Parser Interface contains the exported entry points for invoking the parser. We shall not dwell in detail since this is a good topic for deep-diving in the source code.\n\nLet’s jump to parseUnaryExpr function in the ./parser/parser.go:\n\n    func (p *parser) parseUnaryExpr() ast.Expr {\n     ...\n     switch p.tok {\n     case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.TILDE:\n      pos, op := p.pos, p.tok\n      p.next()\n      x := p.parseUnaryExpr()\n      return \u0026ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)}\n     ...\n     }\n    }\n\nJump to parseStmt function to define the new operator:\n\n    func (p *parser) parseStmt() (s ast.Stmt) {\n     ...\n     switch p.tok {\n     ...\n     case\n      // tokens that may start an expression\n      token.IDENT, ..., token.NOT, token.TILDE // unary operators\n\n    }\n\nOut of context, I noticed that a function called TestParse included in parser_test.go file. I like the idea of how the parser tester using itself as a test case scenario. I could not go forward without mentioning this detail. It’s cool, isn’t it:\n\n    *var *validFiles = []string{\n       \"parser.go\",\n       \"parser_test.go\"\n    }\n    \n    *func TestParse*(t *testing.T) {\n       *for *_, filename := *range *validFiles {\n          _, err := ParseFile(token.NewFileSet(), filename, nil, *DeclarationErrors*)\n          ...\n       }\n    }\n\n### 2.1.5. [Constant](https://github.com/golang/go/tree/master/src/go/constant)\n\nPackage constant implements Values representing untyped Go constants and their corresponding operations.\n\nJump to constant/value.go file\n\n    // UnaryOp returns the result of the unary expression op y.\n    // The operation must be defined for the operand.\n    // If prec \u003e 0 it specifies the ^ (xor) result size in bits.\n    // If y is Unknown, the result is Unknown.\n    //\n    func UnaryOp(op token.Token, y Value, prec uint) Value {\n     switch op {\n     case token.ADD:\n     ...\n     ...\n     case token.NOT:\n     ...\n     case token.TILDE:\n      switch y := y.(type) {\n      case unknownVal:\n       return y\n      case intVal:\n       return makeInt(newInt().Com(y.val))\n      case int64Val:\n       return makeInt(newInt().Com(big.NewInt(int64(y))))\n      default:\n       goto Error\n      }\n    }\n\nNEG reserved for *negative* operation and stands for sets z to -x and returns z. We can go with COM for now. \n\nFind the constant/value_test.go file and jump to opTests table:\n\n    var opTests = []string{\n     // unary operations\n     `+ 0 = 0`,\n     ...\n     `^ ? = ?`,\n     `~ 2 = -3`,\n     `~ 60 = -61`,\n     ...\n    }\n\nOne more step to define in optab struct.\n\n    var optab = map[string]token.Token{\n     \"!\": token.NOT,\n     ...\n     \"\u0026^\": token.AND_NOT,\n     \"~\":  token.TILDE,\n     ...\n    }\n\nBy doing this, we will ensure the operation that we stored in opTest is also defined in theoptab table. Find this function:\n\n    func TestOps(t *testing.T) {\n     for _, test := range opTests {\n      a := strings.Split(test, “ “)\n      i := 0 // operator index \n      ...\n      op, ok := optab[a[i]]\n      if !ok {\n       panic(“missing optab entry for “ + a[i])\n      }\n      ...\n      got := doOp(x, op, y)\n      ...\n    }\n\nWe know that unary operators only work with one value. So cannot be used between two values, but others can be. *(Such as: ||, \u003e\u003e, !=, ≥, etc.).*\n\n    func doOp(x Value, op token.Token, y Value) (z Value) {\n     ...\n     if x == nil {\n      return UnaryOp(op, y, 0)\n     }\n     ...\n    }\n\nAs you noticed, we called theUnaryOp function here, which we want to write the test for.\n\nIt is NOT OK to run the test since we do not implement brand-new Com function in themath/big.Int package, yet.\n\n### 2.1.6 [Math](https://github.com/golang/go/tree/master/src/math)\n\nMatch package implements a collection of common math functions. It provides a suite of arithmetic, geometric, complex, and so on functionalities to perform basic math operations for the compiler itself. In addition, you can see some platform-based assembly instructions. Some of the algorithms just implemented a simplified version from the C sources. Especially most are adopted from C implementations from [Netlib Repository](http://netlib.org/), which is a collection of mathematical software, papers, and databases. If you want to implement a mathematical function, then this package is where your address.\n\n### 2.1.6.1. [big.Int](https://github.com/golang/go/blob/master/src/math/big/int.go)\n\nbig.Int package implements signed multi-precision integers. Find the math/big/int.go file. We will write our COM function here.COM will replace the value of a register or memory operand with its one*’s complement*. We can define it bysets z to ~x and returns z*. *Simply, we want to implement the mathematical equivalent of the *bitwise not *algorithm.\n\nSo, easy peasy:\n\n    *// Com sets z to ~x and returns z.\n    func *(z *Int) *Com*(x *Int) *Int {\n       z.Set(x)\n       z.neg = !z.neg\n       *if *z.neg {\n          z.abs = z.abs.add(x.abs, natOne)\n       } *else *{\n          z.abs = z.abs.sub(x.abs, natOne)\n       }\n       *return *z\n    }\n\nWe could have traversed binary bits from least significant to most significant and reverse every bit. Still, adding an unnecessary loop here may complicate things since we already got the correct results.\n\nLet’s write some tests for that in the int_test.go file:\n\n    *func TestCom*(t *testing.T) {\n       *var *z Int\n       *for *_, test := *range *[]*struct *{\n           n    int64\n           want string\n       }{\n          {-7, \"6\"},\n          {-2, \"1\"},\n          {-1, \"0\"},\n          {0, \"-1\"},\n          {4, \"-5\"},\n          {5, \"-6\"},\n          {6, \"-7\"},\n          {7, \"-8\"},\n          {8, \"-9\"},\n          {15, \"-16\"},\n          {-15, \"14\"},\n       } {\n           v := NewInt(test.n)\n           *if *got := z.Com(v).String(); got != test.want {\n               t.Errorf(\"Com(%+v) = %s; want %s\", v, got, test.want)\n           }\n       }\n    }\n\nWow! Our tests are passing! We just implemented thebitwise not operator. *(You can try to reverse bits by walking each bit in a for loop!)* Now let’s run the all of math tests:\n\n    $ go test ./big/.. -v\n\n    ...\n    === RUN   TestCom\n    --- PASS: TestCom (0.00s)\n    ...\n    PASS\n    ok      math/big        2.647s\n\nCool! Now we can able to compile the Go! Jump the root directory and just run:\n\n    $ ./all.bash\n\n    Building Go cmd/dist using /usr/local/go. (go1.16.3 darwin/amd64)\n    Building Go toolchain1 using /usr/local/go.\n    # bootstrap/go/constant\n    /Users/furkan.turkal/src/public/go/src/go/constant/value.go:1003: undefined: token.TILDE\n    go tool dist: FAILED: /usr/local/go/bin/go install -gcflags=-l -tags=math_big_pure_go compiler_bootstrap bootstrap/cmd/...: exit status 2\n\n**WAIT, WHAT!?\n**We just got undefined: token.TILDE error? 😱 But… We have defined it? How is this even possible? OK, let me explain the reason why the *toolchain* threw this error:\n\nThe problem is that go/constant is a bootstrap package. From /src/cmd/dist/buildtool.go . \n\nbootstrapDirs is a list of directories holding code that must be compiled with a **Go 1.4** toolchain to produce the bootstrapTargets. Names beginning with cmd/ no other slashes, which are *commands*, and other paths, which are *packages* supporting the commands.\n\n    *var *bootstrapDirs = []string{\n       ...\n       \"cmd/compile\",\n       \"cmd/compile/internal/...\",\n       ...\n       \"go/constant\",\n       ...\n       \"math/big\",\n       \"math/bits\",\n       \"sort\",\n       \"strconv\",\n    }\n\nThis means that go/constant needs to be compilable by the bootstrapping toolchain (but we need to support down to go1.4). But since we added a case token.TILDE: to value.go, go/constant is no longer compilable with an older toolchain, and the bootstrapping process fails. [[15]](https://github.com/golang/go/issues/45673#issuecomment-824219940)\n\nThis is the side effect of commit [742c05e](https://github.com/golang/go/commit/742c05e3bce2cf2f4631762cb5fb733d2a92bc91). And [mdempsky](https://github.com/mdempsky) said:\n\u003e Some packages support build tags for building simplified variants during bootstrap. E.g., math/big has math_big_pure_go and strconv and math/bits have compiler_bootstrap. It might be possible to do something similar for go/constant, if you're particularly pressed for this to work today.\n\u003e But I think like [@ianlancetaylor](https://github.com/ianlancetaylor) points out, waiting for [#44505](https://github.com/golang/go/issues/44505) seems like the simplest/surest solution. [[16]](https://github.com/golang/go/issues/45673#issuecomment-824374682)\n\nSo, 🤷‍♂️. There is nothing to do except waiting. We can save the stash and wait for the fix for that issue:\n\n    $ git stash save go_tilde_issue_44505\n    $ git stash show \"stash@{0}\" -p \u003e go_tilde_issue_44505.patch\n\nIt’s time to move on to the **NEW**cmd/compiler package, which is including the *real* compiler internals.\n\n### 2.2. cmd/compile\n\u003e cmd/compilecontains the main packages that form the Go compiler. The compiler may be logically split in four phases, which we will briefly describe alongside the list of packages that contain their code.\n\u003e You may sometimes hear the terms *front-end* and *back-end* when referring to the compiler. Roughly speaking, these translate to the first two and last two phases we are going to list here. A third term, *middle-end*, often refers to much of the work that happens in the second phase.\n\u003e It should be clarified that the name gc stands for Go compiler, and has little to do with uppercase GC, which stands for garbage collection.\n\n### 2.2.1.⠀Differences with go/*\n\u003e Note that the *go/** family of packages, such as *go/parser* and *go/types*,\n*have no relation to the compiler.* Since the compiler was initially written in C,\nthe *go/** packages were developed to enable writing tools working with Go code, such as *gofmt* and *vet*. [[6]](https://github.com/golang/go/tree/master/src/cmd/compile#introduction-to-the-go-compiler)\n\nWe learned what should we do during implementation for go/* package, same as here…\n\n### 2.2.2. Creating Test Script\n\n…Before going forward, what we need to do is create a minimal neg.go file to make some test for our new~ token. What we want to achieve is that simply make this minimal code run:\n\n    package main\n    func main() {\n     println(~15)\n    }\n\nNow compile and run:\n\n    $ cd ./src\n    $ ./make.bash\n    $ cd ..\n    $ ./bin/go run neg.go\n\n    ./neg.go:4:13: syntax error: unexpected ~, expecting expression\n\n**NIT:** To avoid run in order these 4 lines of code every time, we can create simple test.sh script at parent directory to handle this process easily:\n\n    #!/bin/bash\n    pushd ./src\n    ./make.bash\n    popd\n    ./bin/go run neg.go\n\n*Do not forget to give executable access: *$ chmod +x ./test.sh\n\nNow let’s jump to ./cmd/compile/internal/snytax folder.\n\n### 2.2.3. Token\n\nWe are going to make this Tilde token part to the const of tokens.go:\n\n    *const *(\n     _ Operator = iota\n     *...\n     Recv  // \u003c-\n     Tilde // ~\n     ...\n    )*\n\n### 2.2.3.1. Operator String\n\nAs you noticed, there is a statement top of the operator_string.go file that says:\n\n// Code generated by “stringer -type Operator -linecomment tokens.go”; DO NOT EDIT. \n\n    func _() {\n     var x [1]struct{}\n     _ = x[Def-1]\n     _ = x[Not-2]\n     _ = x[Recv-3]\n     _ = x[OrOr-4]\n     _ = x[AndAnd-5]\n     _ = x[Eql-6]\n     _ = x[Neq-7]\n     _ = x[Lss-8]\n     _ = x[Leq-9]\n     _ = x[Gtr-10]\n     _ = x[Geq-11]\n     _ = x[Add-12]\n     _ = x[Sub-13]\n     _ = x[Or-14]\n     _ = x[Xor-15]\n     _ = x[Mul-16]\n     _ = x[Div-17]\n     _ = x[Rem-18]\n     _ = x[And-19]\n     _ = x[AndNot-20]\n     _ = x[Shl-21]\n     _ = x[Shr-22]\n    }\n\nThere is a nameless function _() here that enforces every single constant is defined by order.\n\n    const _Operator_name = “:!\u003c-||\u0026\u0026==!=\u003c\u003c=\u003e\u003e=+-|^*/%\u0026\u0026^\u003c\u003c\u003e\u003e”\n\n    var _Operator_index = […]uint8{0, 1, 2, 4, 6, 8, 10, 12, 13, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 30, 32}\n\n    func (i Operator) String() string {\n     i -= 1\n     if i \u003e= Operator(len(_Operator_index)-1) {\n     return “Operator(“ + strconv.FormatInt(int64(i+1), 10) + “)”\n     }\n     return _Operator_name[_Operator_index[i]:_Operator_index[i+1]]\n    }\n\nThis is how Go handles operator to string converts efficiently. We need to add our Tilde operator here. To do that, we need to fill index struct as compiler enforces us, by running $ go generate command, which it will call stringercommand in the background.\n\n### 2.2.3.2. [**Stringer](https://pkg.go.dev/golang.org/x/tools/cmd/stringer#pkg-overview)**\n\nStringer is a tool to automate the creation of methods that satisfy the fmt.Stringer interface. Given the name of a (signed or unsigned) integer type T that has constants defined. Stringer will create a new self-contained Go source file implementing\n\u003e Stringer works best with constants that are consecutive values such as created using iota, but creates good code regardless. In the future it might also provide custom support for constant sets that are bit patterns.\n\n    $ ./bin/go generate ./src/cmd/compile/internal/syntax\n\n![Code Generated by Stringer](https://cdn-images-1.medium.com/max/8640/1*uCcwzm8tfaEkx3TIc2qDOw.png)*Code Generated by Stringer*\n\nOur operator successfully placed at the 4th line.\n\n### 2.2.4. Scanner\n\nSame operations here like we already did in section 2.1.1.\n\nOpen ./scanner.go and jump to next() function:\n\n    *func *(s *scanner) next() {\n     ...\n     *switch *s.ch {\n     ...\n     *case *'!':\n     ...\n     *case *'~':\n       s.nextch()\n       s.op, s.prec = *Tilde*, 0\n       s.tok = _Operator\n     ...\n     }\n    }\n\nWriting for the scanner is straightforward. We just added one line to sampleTokens struct.\n\n    *var *sampleTokens = [...]*struct *{\n     ...\n    }{\n     ...\n     {_Literal, \"`\\r`\", 0, 0},\n     ...\n     {_Operator, \"~\", *Tilde*, 0},\n     ...\n    }\n\nRun the tests:\n\n![Running Scanner Tests](https://cdn-images-1.medium.com/max/8640/1*T5yXuRU3t7pBnfyxixM7jw.png)*Running Scanner Tests*\n\n### 2.2.5. Parser\n\nFind the ./parser.go file and jump to unaryExpr() function, and add Tilde token inside the_Operator, _Star case:\n\n    *func *(p *parser) unaryExpr() Expr {\n     ...\n     *switch *p.tok {\n     *case *_Operator, _Star:\n      *switch *p.op {\n      *case Mul*, *Add*, *Sub*, *Not*, *Xor*, Tilde:\n       x := new(Operation)\n       x.pos = p.pos()\n       x.Op = p.op\n       p.next()\n       x.X = p.unaryExpr()\n       *return *x\n      ...\n     ...\n     }\n    }\n\nOK, so far, so good. Go *actually* knows what the Tildeoperator is and how to parse by scanners, et does not know how to use it and operate it during the compile phase. So it will fail to compile in thegcpackage. So let’s build Go and see what error it throws.\n\nRun the test.sh to see what would happen:\n\n    panic: invalid Operator\n\n    goroutine 1 [running]:\n    cmd/compile/internal/noder.(*noder).unOp(...)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/noder/noder.go:1383\n    cmd/compile/internal/noder.(*noder).expr(0x100cc5b, 0x1a71318, 0xc00039e0c0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/noder/noder.go:744 +0x1baa\n    cmd/compile/internal/noder.(*noder).exprs(0x2319400, 0xc000064030, 0x1, 0x203000)\n\nPanic has thrown because compile did not know how to convert Tilde to Abstract syntax representation yet. Therefore, we need to declare an [Intermediate Representation ](https://en.wikipedia.org/wiki/Intermediate_representation)for the operator in the Middle-End section.\n\n## 3. Compiler Middle-End\n\nThe middle-end, also known as an *optimizer,* performs optimizations on the Intermediate Representation to improve the performance and the quality of the produced machine code. The middle end contains those optimizations that are independent of the CPU architecture being targeted. [[11]](https://en.wikipedia.org/wiki/Compiler#Middle_end)\n\nThe main phases of the middle end include the following:\n\n* [Analysis](https://en.wikipedia.org/wiki/Compiler_analysis): This gathers program information from the intermediate representation derived from the input; [data-flow analysis](https://en.wikipedia.org/wiki/Data-flow_analysis) is used to build [use-define chains](https://en.wikipedia.org/wiki/Use-define_chain), together with [dependence analysis](https://en.wikipedia.org/wiki/Dependence_analysis), [alias analysis](https://en.wikipedia.org/wiki/Alias_analysis), [pointer analysis](https://en.wikipedia.org/wiki/Pointer_analysis), [escape analysis](https://en.wikipedia.org/wiki/Escape_analysis), etc.\n\n* [Optimization](https://en.wikipedia.org/wiki/Compiler_optimization): the intermediate language representation is transformed into functionally equivalent but faster or smaller forms. Popular optimizations are [inline expansion](https://en.wikipedia.org/wiki/Inline_expansion), [dead code elimination](https://en.wikipedia.org/wiki/Dead_code_elimination), [constant propagation](https://en.wikipedia.org/wiki/Constant_propagation), [loop transformation](https://en.wikipedia.org/wiki/Loop_transformation), and even [automatic parallelization](https://en.wikipedia.org/wiki/Automatic_parallelization).\n\n**3.1. Intermediate Representation (IR)**\n\n![High-Level Overview of IR [[8]](https://youtu.be/D2-gaMvWfQY?t=216)](https://cdn-images-1.medium.com/max/4284/1*EgFEXbBGbouH6Og94CBSWw.png)*High-Level Overview of IR [[8]](https://youtu.be/D2-gaMvWfQY?t=216)*\n\n*^ Here’s the [30,000-foot view](https://nanoglobals.com/glossary/30000-foot-view/) of how Machine Code is generating.*\n\u003e An **intermediate representation** is the [data structure](https://en.wikipedia.org/wiki/Data_structure) or code used internally by a [compiler](https://en.wikipedia.org/wiki/Compiler) or [virtual machine](https://en.wikipedia.org/wiki/Virtual_machine) to represent [source code](https://en.wikipedia.org/wiki/Source_code). An IR is designed to be conducive for further processing, such as [optimization](https://en.wikipedia.org/wiki/Compiler_optimization) and [translation](https://en.wikipedia.org/wiki/Program_transformation).\n\u003e An **intermediate language** is the language of an [abstract machine](https://en.wikipedia.org/wiki/Abstract_machine) designed to aid in the analysis of [computer programs](https://en.wikipedia.org/wiki/Computer_program). The term comes from their use in [compilers](https://en.wikipedia.org/wiki/Compiler), where the source code of a program is translated into a form more suitable for code-improving transformations before being used to generate [object](https://en.wikipedia.org/wiki/Object_file) or [machine](https://en.wikipedia.org/wiki/Machine_language) code for a target machine.\n\u003e Use of an intermediate representation allows compiler systems like the [GNU Compiler Collection](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) and [LLVM](https://en.wikipedia.org/wiki/LLVM) to be used by many different source languages to [generate code](https://en.wikipedia.org/wiki/Code_generation_(compiler)) for many different target [architectures](https://en.wikipedia.org/wiki/Instruction_set). [[7]](https://en.wikipedia.org/wiki/Intermediate_representation)\n\nAll *Abstract Syntax Representation *related stuff stored under ./src/cmd/compile/internal/ir/ as ir package.\n\n**3.1.1. Node**\n\nNode is the abstract interface to an IR node.\n\n![](https://cdn-images-1.medium.com/max/2084/1*Iow9izb6dY3zw80jMy0Mfg.png)\n\nWe need to define the new opcode as OCOM under consts:\n\n    *type *Op uint8\n    \n    *// Node ops.\n    const *(\n     *OXXX *Op = iota\n     ...\n     OPLUS  // +X\n     ONEG   // -X\n     OCOM   // ~X\n     ...\n    )\n\nAfter we declared new Node here, we need to run $ stringer -type=Op -trimprefix=O node.go again to automatically generate OCOM OpCode and do update to ./ir/op_string.go file:\n\n![Stringer to ir/op_string.go](https://cdn-images-1.medium.com/max/8640/1*S9-mQX6bCV9eQWlQGVKElw.png)*Stringer to ir/op_string.go*\n\nBy doing so, now we can easily convert the OpCode to String using [1]*struct*{} map:\n\n    *func *(i Op) *String*() string {\n       *if *i \u003e= Op(*len*(_Op_index)-1) {\n          *return *\"Op(\" + strconv.FormatInt(int64(i), 10) + \")\"\n       }\n       *return *_Op_name[_Op_index[i]:_Op_index[i+1]]\n    }\n\nNow what we need to do here is that we have to update the unary map table that declared under ./noder/noder.go.\n\nFind the unOps table:\n\n    var unOps = [...]ir.Op {\n     syntax.Recv: ir.ORECV,\n     ...\n     syntax.Tilde: ir.OCOM\n    }\n\nWe defined new a Syntax token here, and thus compiler will not throw invalid Operator panic anymore:\n\n    *func *(p *noder) unOp(op syntax.Operator) ir.Op {\n       *if *uint64(op) \u003e= uint64(*len*(unOps)) || unOps[op] == 0 {\n          *panic*(\"invalid Operator\")\n       }\n       *return *unOps[op]\n    }\n\nI think it’s a good checkpoint to see how the error is changed after we implemented those. Run the ./test.sh file:\n\n    panic: cannot SetOp COM on XXX\n\n    goroutine 1 [running]:\n    cmd/compile/internal/ir.(*UnaryExpr).SetOp(0xc000076230, 0x1a682e0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/ir/expr.go:672 +0x10c\n    cmd/compile/internal/ir.NewUnaryExpr(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/ir/expr.go:665\n    cmd/compile/internal/noder.(*noder).expr(0x100cc5b, 0x1a71358, 0xc0004000c0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/noder/noder.go:751 +0x1887\n    cmd/compile/internal/noder.(*noder).exprs(0x231d000, 0xc000064030, 0x1, 0x203000)\n\nWow, the panic message is changed. Let’s investigate that where the error is coming from.\n\n**3.1.2. Expr**\n\nExpr is a Node that can appear as an expression.\n\n    *type Expr interface *{\n       Node\n       isExpr()\n    }\n\nWe initialize the unary operator at ./ir/expr.go in a function called NewUnaryExpr():\n\n    *// A UnaryExpr is a unary expression Op X,\n    // or Op(X) for a builtin function that does not end up being a call.\n    type *UnaryExpr *struct *{\n       miniExpr\n       X Node\n    }\n    \n    *func NewUnaryExpr*(pos src.XPos, op Op, x Node) *UnaryExpr {\n       n := \u0026UnaryExpr{X: x}\n       n.pos = pos\n       n.SetOp(op)\n       *return *n\n    }\n\nBut n.SetOp(op) function throw error here because it falls into the  default case, which is throwing panic:\n\n    *func *(n *UnaryExpr) *SetOp*(op Op) {\n     *switch *op {\n     *default*:\n      *panic*(n.no(\"SetOp \" + op.String()))\n    * case OBITNOT*, *ONEG*, *OCOM*, *...*, ..., *OVARLIVE*:\n       n.op = op\n    }\n\nAdding OCOM to the just right of ONEG will fix the problem. Now jump to SameSafeExpr() function and add OCOMto UnaryExpr casting case.\n\n    *// SameSafeExpr checks whether it is safe to reuse one of l and r\n    // instead of computing both. SameSafeExpr assumes that l and r are\n    // used in the same statement or expression.\n    func SameSafeExpr*(l Node, r Node) bool {\n     *if *l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) {\n      *return *false\n     }\n     ...\n     *case ONOT*, *OBITNOT*, *OPLUS*, *ONEG*, *OCOM*:\n      l := l.(*UnaryExpr)\n      r := r.(*UnaryExpr)\n      *return *SameSafeExpr(l.X, r.X)\n     ...\n    }\n\n\nRerun the tests:\n\n    typecheck [0xc0001398b0]\n    .   COM tc(2) # neg.go:4\n    .   .   LITERAL-15 untyped int # neg.go:4\n    ./neg.go:4:13: internal compiler error: typecheck COM\n\n    goroutine 1 [running]:\n    runtime/debug.Stack()\n     /Users/furkan.turkal/src/public/go/src/runtime/debug/stack.go:24 +0x65\n    cmd/compile/internal/base.FatalfAt(0xc0001398b0, 0x191d86a, 0x1a7fb48, 0xc000146be0, 0x1824634, 0x188e860)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:227 +0x157\n    cmd/compile/internal/base.Fatalf(...)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:196\n    cmd/compile/internal/typecheck.typecheck1(0x1a7fb48, 0xc0001398b0, 0x12)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/typecheck/typecheck.go:484 +0x298a\n    cmd/compile/internal/typecheck.typecheck(0x1a7fb48, 0xc0001398b0, 0x12)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/typecheck/typecheck.go:371 +0x4b0\n    cmd/compile/internal/typecheck.typecheckslice(0xc0001028c0, 0x1, 0x11ab40a, 0x1a7eba8)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/typecheck/typecheck.go:177 +0x68\n\nWhat we need to do here is simply define our new syntax token in the typecheck.go file. Then, find the typecheck1() function, and jump to theunaryExpr case.\n\n    *// typecheck1 should ONLY be called from typecheck.\n    func *typecheck1(n ir.Node, top int) ir.Node {\n     ...\n     *case *ir.*OBITNOT*, ir.*ONEG*, ir.*ONOT*, ir.*OPLUS*, ir.*OCOM*:\n       n := n.(*ir.UnaryExpr)\n       *return *tcUnaryArith(n)\n     ...\n    }\n\nSo we may want to know what happens in the function called tcUnaryArith(n). So let’s investigate it:\n\n    *// tcUnaryArith typechecks a unary arithmetic expression.\n    func *tcUnaryArith(n *ir.UnaryExpr) ir.Node {\n     ...\n     *if *!okfor[n.Op()][defaultType(t).Kind()] {\n      base.Errorf(\"invalid operation: %v (operator %v not defined on %s)\", n, n.Op(), typekind(t))\n      n.SetType(nil)\n      *return *n\n     }\n     ...\n    }\n\nIf we’d rerun the test, we’d have panicked here due to this check:!okfor[n.Op()][defaultType(t).Kind()] \n\nJump to ./typecheck/universe.go file. You will see two vars at the top of the file:\n\n    *var *(\n       okfor [ir.*OEND*][]bool\n       iscmp [ir.*OEND*]bool\n    )\n\nI was really impressed when I first saw this file. We are defining the OpCodes what operations they will do. \n\n    *var *(\n       okforeq    [types.*NTYPE*]bool\n       okforadd   [types.*NTYPE*]bool\n       okforand   [types.*NTYPE*]bool\n       okfornone  [types.*NTYPE*]bool\n       okforbool  [types.*NTYPE*]bool\n       okforcap   [types.*NTYPE*]bool\n       okforlen   [types.*NTYPE*]bool\n       okforarith [types.*NTYPE*]bool\n    )\n\nThe OpCode we created OCOM should able to do some arithmetic operations as we mentioned in the proposal:\n\n* We should not allow ~ for bools. i.e. ~true *(since Go already support this with *!true*)* So, not okforbool \n\n* We should allow ~ for arithmetics. i.e. ~7. So, okforarith \n\n* There are no such things called !~, ~~, ~\u003e or \u003c~, So, eliminate all the others but okforarith \n\nLet’s do a little operation on the InitUniverse() function as we mentioned above:\n\n    *// InitUniverse initializes the universe block.\n    func InitUniverse*() {\n     ...\n     ...\n     ...\n     *// unary\n     *okfor[ir.*OBITNOT*] = okforand[:]\n     okfor[ir.*ONEG*] = okforarith[:]\n     okfor[ir.*OCOM*] = okforarith[:]\n     ...\n    }\n\nSo, the okfor[n.Op()][defaultType(t).Kind()] function will never return false once we put the  OCOM in okfor map.\n\nRun the ./test.sh again:\n\n    ./neg.go:4:12: internal compiler error: unexpected untyped expression: \u003cnode COM\u003e\n\n    goroutine 1 [running]:\n    runtime/debug.Stack()\n     /Users/furkan.turkal/src/public/go/src/runtime/debug/stack.go:24 +0x65\n    cmd/compile/internal/base.FatalfAt(0xc0004101e0, 0x1930ce3, 0x9844128, 0xc00011eeb8, 0x12200e8, 0x1a7fb68)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:227 +0x157\n    cmd/compile/internal/base.Fatalf(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:196\n    cmd/compile/internal/typecheck.convlit1(0x1a7fb68, 0xc0004101e0, 0x8, 0x203000, 0x0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/typecheck/const.go:123 +0xb8c\n\nWhops, we forgot the modify ./typecheck/const.go file.\n\nWe did not define the Token inside the tokenForOp map:\n\n    *var *tokenForOp = [...]token.Token {\n     ...\n    }\n\nFind the convlit1 function:\n\n    *// convlit1 converts an untyped expression n to type t. If n already\n    // has a type, convlit1 has no effect.\n    //\n    func *convlit1(n ir.Node, t *types.Type, explicit bool, context *func*() string) ir.Node {\n     ...\n     ...\n     ...\n     *case *ir.*OPLUS*, ir.*ONEG*, ir.OCOM, ...:\n     ...\n     ...\n     ...\n    }\n\nRun the ./test.sh again:\n\n    ./neg.go:4:13: internal compiler error: unexpected expr: COM \u003cnode COM\u003e\n\n    goroutine 1 [running]:\n    runtime/debug.Stack()\n     /Users/furkan.turkal/src/public/go/src/runtime/debug/stack.go:24 +0x65\n    cmd/compile/internal/base.FatalfAt(0x1942672, 0x19273d4, 0x8, 0xc00011efa8, 0x100cc5b, 0x116fff9)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:227 +0x157\n    cmd/compile/internal/base.Fatalf(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:196\n    cmd/compile/internal/escape.(*escape).exprSkipInit(0xc0004101e0, 0xc0004181c0, 0x0, 0x0, 0xc000410000, 0x1a7fb68, 0xc0004101e0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/escape/escape.go:590 +0x2025\n\nWow, it looks like it’s time to move on to Escape Analysis.\n\n### 3.2. Escape Analysis\n\n[Escape analysis](https://en.wikipedia.org/wiki/Escape_analysis), one of the phases of the Go compiler. It analyses the source code and determines what variables should be allocated on the stack and which ones should escape to the heap. Go statically defines what should be heap or stack-allocated during the compilation phase. This analysis is available via the flag -gcflags=\"-m\" when compiling and/or running your code. [[12]](https://medium.com/a-journey-with-go/go-introduction-to-the-escape-analysis-f7610174e890)\n\u003e Here we analyze functions to determine which Go variables can be allocated on the stack. The two key invariants we have to ensure are: (1) pointers to stack objects cannot be stored in the heap, and (2) pointers to a stack object cannot outlive that object (e.g., because the declaring function returned and destroyed the object’s stack frame or its space is reused across loop iterations for logically distinct variables).\n\u003e We implement this with static data-flow analysis of the AST. First, we construct a directed weighted graph where vertices (termed “locations”) represent variables allocated by statements and expressions, and edges represent assignments between variables (with weights representing addressing/dereference counts).\n\u003e Next, we walk the graph looking for assignment paths that might violate the invariants stated above. If a variable v’s address is stored in the heap or elsewhere that may outlive it, then v is marked as requiring heap allocation.\n\u003e To support interprocedural analysis, we also record data-flow from each function’s parameters to the heap and to its result parameters. This information is summarized as “parameter tags”, which are used at static call sites to improve escape analysis of function arguments. [[13]](https://github.com/golang/go/blob/15a374d5c1336e9cc2f8b615477d5917e9477440/src/cmd/compile/internal/escape/escape.go#L20-L46)\n\nFind the exprSkipInit function:\n\n    *func *(e *escape) exprSkipInit(k hole, n ir.Node) {\n     ...\n     *switch *n.Op() {\n     *default*:\n      base.Fatalf(\"unexpected expr: %s %v\", n.Op().String(), n)\n     ...\n     *case *ir.*OPLUS*, ir.*ONEG*, ir.*OCOM*, ir.*OBITNOT*:\n      n := n.(*ir.UnaryExpr)\n      e.unsafeValue(k, n.X)\n     ...\n    }\n\nFind the unsafeValue function:\n\n    *// unsafeValue evaluates a uintptr-typed arithmetic expression looking\n    // for conversions from an unsafe.Pointer.\n    func *(e *escape) unsafeValue(k hole, n ir.Node) {\n     ...\n     *switch *n.Op() {\n     ...\n     *case *ir.*OPLUS*, ir.*ONEG*, ir.*OCOM*, ir.*OBITNOT*:\n      n := n.(*ir.UnaryExpr)\n      e.unsafeValue(k, n.X)\n     ...\n    }\n\nFind the mayAffectMemory function:\n\n    *// mayAffectMemory reports whether evaluation of n may affect the program's\n    // memory state. If the expression can't affect memory state, then it can be\n    // safely ignored by the escape analysis.\n    func *mayAffectMemory(n ir.Node) bool {\n     ...\n     *switch *n.Op() {\n     ...\n     *case *ir.*OLEN*, *...*, ir.*ONEG*, ir.*OCOM*, ir.*OALIGNOF*, *...*:\n       n := n.(*ir.UnaryExpr)\n       *return *mayAffectMemory(n.X)\n\nRun the ./test.sh again:\n\n    walk [0xc0004101e0]\n    . COM tc(1) int # neg.go:4 int\n    . . LITERAL-15 tc(1) int # neg.go:4\n    ./neg.go:4:13: internal compiler error: walkExpr: switch 1 unknown op COM\n\n    goroutine 1 [running]:\n    runtime/debug.Stack()\n     /Users/furkan.turkal/src/public/go/src/runtime/debug/stack.go:24 +0x65\n    cmd/compile/internal/base.FatalfAt(0xc0004101e0, 0x1930e0c, 0x1a7fb68, 0xc0001272a0, 0xc00009d2b0, 0xc00009d2b0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:227 +0x157\n    cmd/compile/internal/base.Fatalf(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:196\n    cmd/compile/internal/walk.walkExpr1(0x1a7fb68, 0xc0004101e0, 0xc0004101e0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/walk/expr.go:82 +0xe9f\n\n### 3.3. Walk\n\nWe walk the given ir.Func and their all statements here.\n\n    *func Walk*(fn *ir.Func) {\n       ir.CurFunc = fn\n       ...\n       order(fn)\n       ...\n       walkStmtList(ir.CurFunc.Body)\n       ...\n    }\n\u003e A Func corresponds to a single function in a Go program (and vice versa: each function is denoted by exactly one *Func).\n\u003e There are multiple nodes that represent a Func in the IR:\n* The ONAME node (Func.Nname) is used for plain references to it.\n* The ODCLFUNC node (the Func itself) is used for its declaration code.\n*The OCLOSURE node (Func.OClosure) is used for a reference to a function literal.\n\n    *type *Func *struct *{\n       miniNode\n       Body Nodes\n    \n       Nname    *Name        *// ONAME node\n       *OClosure *ClosureExpr *// OCLOSURE node\n    \n    *\n       *// ONAME nodes for all params/locals for this func/closure, does NOT include closurevars until transforming closures during walk.\n       *Dcl []*Name\n    \n       *// ClosureVars lists the free variables that are used within a\n       // function literal\n       *ClosureVars []*Name\n    \n       *// Enclosed functions that need to be compiled, populated during walk.\n       *Closures []*Func\n       ...\n       *// Parents records the parent scope of each scope within a\n       // function.\n       *Parents []ScopeID\n       ...*\n       *Pragma PragmaFlag *// go:xxx function annotation.\n       *...\n       NumDefers  int32 *// number of defer calls in the function\n       *NumReturns int32 *// number of explicit returns in the function.\n       ...\n    *}\n    \n    *func NewFunc*(pos src.XPos) *Func {\n       f := new(Func)\n       f.pos = pos\n       f.op = *ODCLFUNC\n       ...\n       return *f\n    }\n\nJump to ./walk/expr.go file and find walkExpr1 function:\n\n    *func *walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {\n     *switch *n.Op() {\n     *default*:\n      ir.Dump(\"walk\", n)\n      base.Fatalf(\"walkExpr: switch 1 unknown op %+v\", n.Op())\n      *panic*(\"unreachable\")\n     ...\n     *case *ir.*ONOT*, ir.*ONEG*, ir.*OCOM*, ir.*OPLUS*, ..., ir.*OIDATA*:\n      n := n.(*ir.UnaryExpr)\n      n.X = walkExpr(n.X, init)\n      *return *n\n     ...\n    }\n\nJust to ./walk/walk.go file and find mayCall function:\n\n    *// mayCall reports whether evaluating expression n may require\n    // function calls, which could clobber function call arguments/results\n    // currently on the stack.\n    func *mayCall(n ir.Node) bool {\n     ...\n     *return *ir.Any(n, *func*(n ir.Node) bool {\n      ...\n      *switch *n.Op() {\n      ...\n      *// When using soft-float, these ops might be rewritten to function calls\n      // so we ensure they are evaluated first.\n      case *ir.*OADD*, ir.*OSUB*, ir.*OMUL*, ir.*ONEG*, ir.*OCOM*:\n       *return *ssagen.Arch.SoftFloat \u0026\u0026 isSoftFloat(n.Type())\n      ...\n     ...\n    }\n\nRun the ./test.sh:\n\n    ./neg.go:4:13: internal compiler error: ‘main’: unhandled expr COM\n\n    goroutine 9 [running]:\n    runtime/debug.Stack()\n     /Users/furkan.turkal/src/public/go/src/runtime/debug/stack.go:24 +0x65\n    cmd/compile/internal/base.FatalfAt(0xc00002414c, 0xc00009ef70, 0x10, 0xc0003cb780, 0x10, 0x2094108)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:227 +0x157\n    cmd/compile/internal/base.Fatalf(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/base/print.go:196\n    cmd/compile/internal/ssagen.(*ssafn).Fatalf(0xc00039c1e0, 0x0, 0x1922c8b, 0x0, 0xc000064570, 0x1, 0x0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/ssagen/ssa.go:7481 +0x187\n    cmd/compile/internal/ssagen.(*state).Fatalf(…)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/ssagen/ssa.go:976\n    cmd/compile/internal/ssagen.(*state).expr(0xc0003f8100, 0x1a7fb68, 0xc00039c1e0)\n     /Users/furkan.turkal/src/public/go/src/cmd/compile/internal/ssagen/ssa.go:3198 +0x8c44\n\nHere we are. The SSA. Let’s see what SSA means exactly in the next section. It’s time to move on to Compiler Back-End.\n\n## 4.⠀Compiler Back-End\n\n![Medium-Level Overview of Compiler Back-End of Go](https://cdn-images-1.medium.com/max/4458/1*HpzzpEpMW0OA_TkHyP5BXg.png)*Medium-Level Overview of Compiler Back-End of Go*\n\nCompiler back-end is responsible for the CPU architecture-specific optimizations and code generation.\n\n### 4.1. Introduction\n\nThe main phases of the back-end *may* include the following:\n\n* [**Machine-dependent optimizations](https://www.themagicalidea.com/machine-dependent-code-optimization-in-compiler-design/):** optimizations that depend on the details of the CPU architecture that the compiler targets.\n\n* [**Code generation](https://en.wikipedia.org/wiki/Code_generation_(compiler)): **the transformed intermediate language is translated into the output language, usually the native [machine language](https://en.wikipedia.org/wiki/Machine_language) of the system.\n\nThere are several production compilers for Go: [[14]](https://golang.org/doc/faq#What_compiler_technology_is_used_to_build_the_compilers)\n\n* Gc is written in Go with a recursive descent parser and uses a custom loader, also written in Go but based on the Plan 9 loader, to generate ELF/Mach-O/PE binaries.\n\n* The Gccgo compiler is a front end written in C++ with a recursive descent parser coupled to the standard GCC back end.\n\n*You can find the compiler entry point [here](https://github.com/golang/go/blob/master/src/cmd/compile/internal/gc/main.go#L52-L330).*\n\n### **4.2. Static Single Assignment (SSA)**\n\n**SSA** is a property of an [intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation) (IR), which requires that each variable be [assigned](https://en.wikipedia.org/wiki/Assignment_(computer_science)) exactly once, and every variable is defined before it is used. Existing variables in the original IR are split into *versions*, new variables typically indicated by the original name with a subscript in textbooks. Every definition gets its own version. In SSA form, [use-def chains](https://en.wikipedia.org/wiki/Use-define_chain) are explicit, and each contains a single element.\n\n![Simple SSA Conversation For Straight Line Code](https://cdn-images-1.medium.com/max/2602/1*W7jwk-Dr2c3V6ybh_MCmXg.png)*Simple SSA Conversation For Straight Line Code*\n\nEssentially means that each register is assigned exactly once. This property simplifies data flow analysis. To handle variables that are assigned more than once in the source code, a notion of [phi](https://llvm.org/docs/LangRef.html#phi-instruction) instructions are used:\n\n![φ instruction](https://cdn-images-1.medium.com/max/2000/1*MipKnkCVQmW09sgzvLNhaw.png)*φ instruction*\n\nThe phi instruction, which is an *implicit merge point*, is used to implement the φ node in the SSA graph representing the function and takes *a list of pairs* as arguments, with one pair for each predecessor basic block of the current block, and models the set of possible incoming values as distinct assignment statements. [[9]](https://llvm.org/docs/LangRef.html#phi-instruction)\n\nSSA enables fast, accurate optimization algorithms for: [[10]](https://www.youtube.com/watch?v=uTMvKVma5ms)\n\n* [Common Subexpression Elimination](https://en.wikipedia.org/wiki/Common_subexpression_elimination)\n\n* [Dead Code Elimination](https://en.wikipedia.org/wiki/Dead_code_elimination)\n\n* [Dead Store Elimination](https://en.wikipedia.org/wiki/Dead_store)\n\n* [Nil Check Elimination](https://jpbempel.github.io/2013/09/03/null-check-elimination.html)\n\n* [Bounds Check Elimination](https://en.wikipedia.org/wiki/Bounds-checking_elimination)\n\n* [Register Allocation](https://en.wikipedia.org/wiki/Register_allocation)\n\n* [Instruction Scheduling](https://en.wikipedia.org/wiki/Instruction_scheduling)\n\n*You can find the list of passes for the SSA compiler [here](https://github.com/golang/go/blob/6c1c055d1ea417d050503efe92c1eead0da68cef/src/cmd/compile/internal/ssa/compile.go#L431-L486).*\n\nYou should able to dump your code to SSA using [ssadump](https://pkg.go.dev/golang.org/x/tools/cmd/ssadump) tool:\n\n    $ go get golang.org/x/tools/cmd/ssadump\n\nTo display and interpret the SSA form of your Go program:\n\n    package main\n    func main() {\n     x := 1\n     y := 2\n     y += x\n     x = x + y\n     println(x)\n    }\n\nSet your [BuilderMode](https://pkg.go.dev/golang.org/x/tools/go/ssa#BuilderMode), give a path, and run the following command:\n\n    $ ssadump -build=F main.go\n\nIt will generate something like:\n\n    # Name: command-line-arguments.main\n    func main():\n    0:                              entry P:0 S:0\n       t0 = 2:int + 1:int                     int\n       t1 = 1:int + t0                        int\n       t2 = println(t1)                        ()\n       return\n\n    # Name: command-line-arguments.init\n    func init():\n    0:                              entry P:0 S:2\n       t0 = *init$guard                      bool\n       if t0 goto 2 else 1\n    1:                         init.start P:1 S:1\n       *init$guard = true:bool\n       jump 2\n    2:                          init.done P:2 S:0\n       return\n\n**P**, stands for *predecessors. *How many blocks come into this block.\n**S,** stands for successors. How many blocks it flows out to.\n\n*When Go meets Assembly.[*](https://www.youtube.com/watch?v=D2-gaMvWfQY\u0026t=564s)*\n\nLet’s create another example:\n\n    package main\n    func main() {\n     x := 1\n     y := 2\n     z := x\n     x = y + z\n     y = x + z\n     z = y + x\n     x = z\n     y = x\n     x = x + z\n     println(x)\n    }\n\n\nRerun the *ssadump* tool, and we will have the following kind of assembly:\n\n![Output of ssadump](https://cdn-images-1.medium.com/max/5416/1*qOYPUBSGtVAoOobNDy2R8A.png)*Output of ssadump*\n\nWe can see how they start to flow, how certain variables turn into other variables and compose into other pieces. Which versions of which variables are used where. We can construct the flow through this graph of where our data is going.\n\n**4.3. Go’s SSA Backend**\n\n\n\n![Picture of SSA Flow](https://cdn-images-1.medium.com/max/5954/1*jvCMGB_t0VEUJrbRiFeBBw.png)*Picture of SSA Flow*\n\u003e In this phase, the AST is converted into Static Single Assignment (SSA) form, a\nlower-level intermediate representation with specific properties that make it\neasier to implement optimizations and to eventually generate machine code from\nit.\n\nDuring this conversion, function intrinsics are applied. These are special\nfunctions that the compiler has been taught to replace with heavily optimized\ncode on a case-by-case basis.\n\nCertain nodes are also lowered into simpler components during the AST to SSA\nconversion, so that the rest of the compiler can work with them. For instance,\nthe copy builtin is replaced by memory moves, and range loops are rewritten into\nfor loops. Some of these currently happen before the conversion to SSA due to\nhistorical reasons, but the long-term plan is to move all of them here.\n\nThen, a series of machine-independent passes and rules are applied. These do not\nconcern any single computer architecture, and thus run on all *GOARCH *variants.\n\nSome examples of these generic passes include dead code elimination, removal of\nunneeded nil checks, and removal of unused branches. The generic rewrite rules\nmainly concern expressions, such as replacing some expressions with constant\nvalues, and optimizing multiplications and float operations.\n\nSSA IR actually implemented at [Go 1.17](https://golang.org/doc/go1.7#compiler) version, proposed by [Keith Randall](https://github.com/randall77)\nat *2/10/2015*, titled [New SSA Backend for the Go Compiler](https://docs.google.com/document/d/1szwabPJJc4J-igUZU4ZKprOrNRNJug2JPD8OYi3i1K0/).\n\nI strongly recommend that you should watch [*GopherCon 2017: Keith Randall — Generating Better Machine Code with SSA](https://www.youtube.com/watch?v=uTMvKVma5ms)* before we move on next.\n\nFind the ./internal/ssagen/ssa.go file and jump to the expr function:\n\n    */ expr converts the expression n to ssa, adds it to s and returns the ssa result.\n    func *(s *state) expr(n ir.Node) *ssa.Value {\n     ...\n     *switch *n.Op() {\n     ...\n     ...\n     ...\n     *case *ir.*ONOT*, ir.*OBITNOT*, ir.*OCOM*:\n      n := n.(*ir.UnaryExpr)\n      a := s.expr(n.X)\n      *return *s.newValue1(s.ssaOp(n.Op(), n.Type()), a.Type, a)\n     ...\n     ...\n     ...\n     *default*:\n      s.Fatalf(\"unhandled expr %v\", n.Op())\n      *return *nil\n     }\n    }\n\nAppend the OCOM to ir, types: ssa mapping table:\n\n    *type *opAndType *struct *{\n       op    ir.Op\n       etype types.Kind\n    }\n\n    *var *opToSSA = *map*[opAndType]ssa.Op {\n     ...\n     opAndType{ir.*OBITNOT*, types.*TINT64*}:  ssa.*OpCom64*,\n     opAndType{ir.*OBITNOT*, types.*TUINT64*}: ssa.*OpCom64*,\n     ...\n     opAndType{ir.*OCOM*, types.*TINT8*}:   ssa.*OpCom8*,\n     opAndType{ir.*OCOM*, types.*TUINT8*}:  ssa.*OpCom8*,\n     opAndType{ir.*OCOM*, types.*TINT16*}:  ssa.*OpCom16*,\n     opAndType{ir.*OCOM*, types.*TUINT16*}: ssa.*OpCom16*,\n     opAndType{ir.*OCOM*, types.*TINT32*}:  ssa.*OpCom32*,\n     opAndType{ir.*OCOM*, types.*TUINT32*}: ssa.*OpCom32*,\n     opAndType{ir.*OCOM*, types.*TINT64*}:  ssa.*OpCom64*,\n     opAndType{ir.*OCOM*, types.*TUINT64*}: ssa.*OpCom64*,\n     ...\n    }\n\nWhat! 😶 Did you see what I saw? 🥲 There was an operator that was defined as OpCom! 😱 This means that OBITNOT operator ^ exactly does what we want to implement. I genuinely did not know that even exist! 😞 Oh, man… I just realized it while writing this article along with the code. I decided to go with COM because NEG [was reserved](https://github.com/golang/go/blob/15a374d5c1336e9cc2f8b615477d5917e9477440/src/cmd/compile/internal/ir/node.go#L226) for the*- operator*. 🤔 It *may* be more accurate to use NEG instead of COM for ~. I thought that way because that’s how I saw on [AVR Instruction Set Manual](http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf) at the college *(Microprocessors lecture). *Thus*,* I wanted to go with OCOM (One’s [COM]plement) for now.🤷‍♂️\n\n![Atmel AVR Instruction Set Manual — NEG](https://cdn-images-1.medium.com/max/3188/1*JMv25k-g-y-1XRmYQ3axmw.png)*Atmel AVR Instruction Set Manual — NEG*\n\nUsing ^x in languages such as *Python, C*, etc. causes an invalid compile and throws a *syntax error*. I should have implemented this myself first before I wrote this article. I have scolded myself already.\n\nIn this situation, we *may* have two things to do:\n\n* Changing the proposal slightly and propose the remove the use of ^x. We should no longer allow that usage. *Not because of to complete our own job* — to be more aligned with the usage of the unary syntax just like in other languages. (which will cause [l*anguage change](https://github.com/golang/go/issues?q=is%3Aissue+label%3ALanguageChange))*\n\n* How could we add a new operator to ssa package ourselves if there is no such thing ssa.OpCom? We will dive into Op generators and architecture rules.\n\n* It seemed more consistent to use NEG instead of COM for the ~ operator. But NEG was already reserved for - . We should mention that in the proposal to swap OpCodes.\n\nNo demoralizing! The important thing is to learn something here! We can easily change the title of our proposal to something like: \n\u003e proposal: spec: add a built-in tilde ~ (bitwise not) unary operator (op: OCOM), **deprecate ^x (XOR) usage**\n\nYes, I know. We **have to** replace all the ^x usages with the ~x in the compiler itself. Sounds like a Ba Dum Tss!\n\n**4.3.1. Generic OPs**\n\nGeneric opcodes typically specify a width. The inputs and outputs of that op are the given number of bits wide. There is no notion of *sign*, so Add32 can be used both for signed and unsigned 32-bit addition.\n\nSigned/unsigned is explicit with the extension ops (SignExt*/ZeroExt*) and implicit as the arg to some opcodes (e.g., the second argument to shifts is unsigned). If not mentioned, all args take signed inputs or don’t care whether their inputs are signed or unsigned.\n\nFind the ./ssa/gen/genericOps.go and jump to genericOps struct:\n\n    *var *genericOps = []opData {\n     *// 2-input arithmetic\n     // Types must be consistent with Go typing. Add, for example, must take two values of the same type and produces that same type.\n     *{name: \"Add8\", argLength: 2, commutative: true}, *// arg0 + arg1\n     *{name: \"Add16\", argLength: 2, commutative: true},\n     ...\n     ...\n     ...\n     *// 1-input ops\n     *{name: \"Neg8\", argLength: 1}, *// -arg0\n     *{name: \"Neg16\", argLength: 1},\n     ...\n     {name: \"Com8\", argLength: 1}, *// ^arg0\n     *{name: \"Com16\", argLength: 1},\n     ...\n     {name: \"Test8\", argLength: 1}, *// ~arg0\n     *{name: \"Test16\", argLength: 1},\n     ...\n    }\n\nIn this supposing, we created a new OpCode called Test . We will not use Test OpCode since Neg and Com already defined there. It’s just for educational purposes to wrap up this article.\n\nJump to ./ssa/opGen.go , we need to implement new Test OpCode by calling $ go generate as already described in the ./ssa/gen/README file. So, run the following command inside the ./ssa/gen directory:\n\n    $ ../../../../../../bin/go run *.go\n\n![](https://cdn-images-1.medium.com/max/8640/1*evYf09lPDcqQ3bXpsxcuMw.png)\n\nHere is the diff:\n\n![](https://cdn-images-1.medium.com/max/8640/1*yuZXnpnxECwXU11o03FVLw.png)\n\n### **4.3.2. Rewrite Rules**\n\nMany optimizations can be specified using rewrite rules on the SSA form.\n\n![Simple Review Rule for x-x=0](https://cdn-images-1.medium.com/max/2096/1*itWL6oxv-K5Ze3xZEBmj1g.png)*Simple Review Rule for x-x=0*\n\nIf we have a subtract *64-bit* subtract of a value itself, we can just replace the with constant 0. Go has a [bunch of optimizations in a big review rule file](https://github.com/golang/go/tree/master/src/cmd/compile/internal/ssa/gen) in the compiler.\n\n\n\n![Rewrite Rule Example](https://cdn-images-1.medium.com/max/5464/1*PO2Uz1WM5CwTa89KVWKicA.png)*Rewrite Rule Example*\n\nYou can find more examples in the [*generic.rules](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/generic.rules)* file, or in the [src/cmd/compile/internal/ssa/gen](https://github.com/golang/go/tree/master/src/cmd/compile/internal/ssa/gen) package:\n\n![CLOC of *.rules](https://cdn-images-1.medium.com/max/3800/1*GLNV4H1_YQHDGPkm2ohmIw.png)*CLOC of *.rules*\n\nRewrite rules are also used to lower machine-independent operations to machine-independent operations.\n\n* The OpCodes on the **left** are the **Go **OpCodes. *(written in lowercase)*\n\n* The OpCodes on the **right** are the **ARCH** OpCodes. *(machine instructions)*\n\n[386:](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/386.rules)\n\n    (Neq64F x y) =\u003e (SETNEF (UCOMISD x y))\n\n[RISCV64:](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/RISCV64.rules)\n\n    (Neq64  x y) =\u003e (SNEZ (SUB x y))\n\n[ARM64:](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/ARM64.rules)\n\n    (Neq64 x y) =\u003e (NotEqual (CMP x y))\n\n[MIPS64:](https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/gen/MIPS64.rules)\n\n    (Neq64 x y) =\u003e (SGTU (XOR x y) (MOVVconst [0]))\n\n[Review rules can get pretty complicated:](https://github.com/golang/go/blob/d4bfe006155ea0d9c2970e22fca5a027125c8a98/src/cmd/compile/internal/ssa/gen/AMD64.rules#L1638-L1653)\n\n![](https://cdn-images-1.medium.com/max/3908/1*3nJT8m2JSrYVkYCP39xtqQ.png)\n\nIt takes two 8-bit loads and replaces them with one 16-bit load if it can. So, this rule sort of checks all possible conditions under which this rewrite rule can apply. If so, it does the rewrite; and uses one 16-bit load instead. [[10]](https://youtu.be/uTMvKVma5ms?t=1735)\n\nConverting the compiler to use an SSA IR led to substantial improvements in the generated code:\n\n![](https://cdn-images-1.medium.com/max/2632/1*MC29Gey3rMag4Sgw17TVAQ.png)\n\nWe can write some optimizations for the~ operator like the following:\n\n![Python Interpreter](https://cdn-images-1.medium.com/max/2000/1*oxTdC6zwqGNVrBNwE9qUMw.png)*Python Interpreter*\n\n* ~~x == x \n\n    (Com(64) (Com(64) x)) =\u003e x\n\n* -~x == x+1 \n\n    (Neg(64) (Com(64) x)) =\u003e (Add(64) (Const(64) [1]) x)\n\n* ~(x-1) == -x \n\n    (Com(64) (Add(64) (Const(64) [-1]) x)) =\u003e (Neg(64)\n\n* ~x+1 == -x \n\n    (Add(64) (Const(64) [1]) (Com(64) x)) =\u003e (Neg(64) x)\n\nFind the generic.rules file and add these instructions to gain some optimizations. We *may* have to use all power of two values inside the OpCode. Use Com(64|32|16|8) instead Com(64) .\n\n## 5. Result\n\nThe big moment has come. I think there is nothing left we have to do except testing. Have you tried this yet?\n\n### 5.1. Trying New Operator\n\nIt is time to lift the curtain, run the ./test.sh script:\n\n    -16\n\nAnd, boom! It *just* worked. That’s pretty amazing! Isn’t it?\n\nLet’s do small modifications to the neg.go snippet:\n\n    package main\n    func main() {\n     x := -15\n     fn := func() int {\n      return x\n     }\n     println(~fn())\n    }\n\nIt prints 14, which means this works beautifully too. Our ~ operator is now fully functional.\n\n### 5.2. Test \u0026 Build Go Compiler\n\nOne more thing before running the tests. Remember Griesemer added Tilde operator with [this commit](https://go-review.googlesource.com/c/go/+/307370). So, we need to update the ./test/fixedbugs/issue23587.go file, which is no more a *failing* test case. We have to update the [Issue 23587](https://github.com/golang/go/issues/23587) test file like the following:\n\n    *// errorcheck\n    package *p\n    *func *_(x int) {\n       _ = ~x\n       _ = x~ *// ERROR \"unexpected ~ at end of statement\"\n    *}\n    *func *_(x int) {\n       _ = x ~ x *// ERROR \"unexpected ~ at end of statement\"\n    *}\n\nWe want to assert the error the compiler will throw. Thus, we used *errorcheck *action* *here*. *We can run the given test file:\n\n    $ ./bin/go run ./test/run.go — ./test/fixedbugs/issue23587.go\n\nThis test passes. Yay! Let’s see if we broke any functionality in the compiler. Jump to the ./src directory and simply run:\n\n    $ ./all.bash\n\n    Building Go cmd/dist using /usr/local/go. (go1.16.3 darwin/amd64)\n    Building Go toolchain1 using /usr/local/go.\n    Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.\n    Building Go toolchain2 using go_bootstrap and Go toolchain1.\n    Building Go toolchain3 using go_bootstrap and Go toolchain2.\n    Building packages and commands for darwin/amd64.\n\n    ##### Testing packages.\n    ok   archive/tar 2.021s\n    ok   archive/zip 1.937s\n    ...\n    ...\n    ...\n    ok   cmd/compile/internal/dwarfgen 6.948s\n    ok   cmd/compile/internal/importer 1.919s\n    ok   cmd/compile/internal/ir 0.424s\n    ok   cmd/compile/internal/logopt 2.288s\n    ok   cmd/compile/internal/noder 0.862s\n    ok   cmd/compile/internal/ssa 1.226s\n    ok   cmd/compile/internal/ssagen 0.801s\n    ok   cmd/compile/internal/syntax 0.368s\n    ok   cmd/compile/internal/test 25.796s\n    ok   cmd/compile/internal/typecheck 13.242s\n    ok   cmd/compile/internal/types 0.336s\n    ok   cmd/compile/internal/types2 6.691s\n    ...\n    ...\n    ...\n    ALL TESTS PASSED\n    ---\n    Installed Go for darwin/amd64 in /Users/furkan.turkal/src/public/go\n    Installed commands in /Users/furkan.turkal/src/public/go/bin\n    ...\n\nThey pass! So, that means… We did a great job! We are done.\n\nYou can find the full patch here:\n\n## 6. Conclusion\n\nI know *less is more*. Obviously, we do not like reading lots of text in a tutorial in general. I could have kept these brief and make our diagrams talk instead. I was furious at myself after I completed the article. If I was going to ask myself, “What could I do better?” I would definitely start writing this article after I implemented and tried it myself first. I really did not know that ^ is handling the ~ thing. \n\nIn this article, we tried to address many compiler subjects at the 201 level. That’s why some of the subjects remained open-ended. Although almost every subject is a completely different world, our goal here was to give a middle-level perspective to the compilers. I do not doubt that feedback will come from the maintainers and contributors for the issue and PR we have opened. Comments such as “we do the exact same thing with the ^ operator. Why we need this?” can be written, and both PR \u0026 issue *may* be closed; or, it can also be considered in terms of *aligning* with other languages.\n\nI generally think I am a result-oriented person. But not for what we did here; not today at least… We have learned some good things today. I learned different new things about the technical details of the Go compiler while writing this article. Although I am not on the road to being a compiler expert, trying to learn the technical details of compilers gives me confidence. And I am inquisitive about these compiler habitats! Deep diving is beautiful. Much more logical, objective, and scientific. My love will never end! Maybe I will not be a compiler contributor, but I do not doubt that I will keep reading all of their changelogs one by one.\n\n## 7. Furthermore\n\nIf we are on the way to becoming engineers* *of the future, and we just do not want to be *consumers* or end-users but creators, I think we also need to know how the products we use are created.\n\nI am not sure that knowing a programming language down to its deepest details will teach anything to us. Technology changes all the time; our knowledge is becoming obsolete. We *have to* keep up with the latest technology changes and updates. So let’s *understand* instead of *memorizing*, let’s *see* instead of *hearing*, let’s *learn* instead of *ignoring*, let’s *demonstrate* instead of *assuming*.\n\nIf you are fascinated by compilers as I do, if you want to know how it works, you should definitely do not postpone it off. We are not talking about creating a compiler from scratch while sitting and writing code for long hours like zombies. We try to learn new perspectives and knowledge by understanding compiler concepts. [There](https://github.com/aalhour/awesome-compilers) [are](https://www.reddit.com/r/learnprogramming/comments/5hwo3x/whats_the_best_resource_to_walk_me_through_making/) [some](https://news.ycombinator.com/item?id=136875) [great](https://gcc.gnu.org/wiki/ListOfCompilerBooks) [resources](https://stackoverflow.com/questions/1669/learning-to-write-a-compiler) out there that should not be missed.\n\nFeature implementation to a compiler is not limited only to Go. Do give it a try on any compiler you want to! So, why not create a brand-new operator for Go, just as we did in this article? What about § operator?\n\n## 8. References\n\n### 8.1. Normative\n\n[0] [https://golang.org/doc/contribute#contributor](https://golang.org/doc/contribute#contributor)\n[1] [https://golang.org/doc/contribute#before_contributing](https://golang.org/doc/contribute#before_contributing)\n[2] [https://stackoverflow.com/a/3952128/5685796](https://stackoverflow.com/a/3952128/5685796)\n[3] [https://golang.org/ref/spec#Arithmetic_operators](https://golang.org/ref/spec#Arithmetic_operators)\n[4] [https://en.wikipedia.org/wiki/Parsing#Parser](https://en.wikipedia.org/wiki/Parsing#Parser)\n[5] [https://en.cppreference.com/w/cpp/language/operator_precedence](https://en.cppreference.com/w/cpp/language/operator_precedence)\n[6] [https://github.com/golang/go/tree/master/src/cmd/compile#introduction-to-the-go-compiler](https://github.com/golang/go/tree/master/src/cmd/compile#introduction-to-the-go-compiler)\n[7] [https://en.wikipedia.org/wiki/Intermediate_representation](https://en.wikipedia.org/wiki/Intermediate_representation)\n[8] [https://youtu.be/D2-gaMvWfQY?t=216](https://youtu.be/D2-gaMvWfQY?t=216)\n[9] [https://llvm.org/docs/LangRef.html#phi-instruction](https://llvm.org/docs/LangRef.html#phi-instruction)\n[10] [https://www.youtube.com/watch?v=uTMvKVma5ms](https://www.youtube.com/watch?v=uTMvKVma5ms)\n[10] [https://youtu.be/uTMvKVma5ms?t=1735](https://youtu.be/uTMvKVma5ms?t=1735)\n[11] [https://en.wikipedia.org/wiki/Compiler#Middle_end](https://en.wikipedia.org/wiki/Compiler#Middle_end)\n[12] [https://medium.com/a-journey-with-go/go-introduction-to-the-escape-analysis-f7610174e890](https://medium.com/a-journey-with-go/go-introduction-to-the-escape-analysis-f7610174e890)\n[13] [https://github.com/golang/go/blob/15a374d5c1336e9cc2f8b615477d5917e9477440/src/cmd/compile/internal/escape/escape.go#L20-L46](https://github.com/golang/go/blob/15a374d5c1336e9cc2f8b615477d5917e9477440/src/cmd/compile/internal/escape/escape.go#L20-L46)\n[14] [https://golang.org/doc/faq#What_compiler_technology_is_used_to_build_the_compilers](https://golang.org/doc/faq#What_compiler_technology_is_used_to_build_the_compilers)\n[15] [https://github.com/golang/go/issues/45673#issuecomment-824219940](https://github.com/golang/go/issues/45673#issuecomment-824219940)\n[16] [https://github.com/golang/go/issues/45673#issuecomment-824374682](https://github.com/golang/go/issues/45673#issuecomment-824374682)\n\n### 8.2. Informative\n\n* [https://en.wikipedia.org/wiki/Compiler](https://en.wikipedia.org/wiki/Compiler)\n\n* [Thorsten Ball ](https://twitter.com/thorstenball)— [Writing an interpreter in Go](https://interpreterbook.com/), [Writing A Compiler In Go](https://compilerbook.com/)\n\nIf you spot a typo, find something wrong with the article, have a suggestion to make, or just a question, feel free to contact me: furkan.turkal@hotmail.com \n\nAlso, feel free to ping me on [Twitter](https://twitter.com/furkanturkaI) or [GitHub](https://github.com/Dentrax/) anytime.\n\n![](https://cdn-images-1.medium.com/max/2000/1*puO9QPsENQ5ww1QKNuf6tw.gif)\n\u003e # “Thank you, and have a very safe and productive day!”\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdentrax%2Fgo-tilde-operator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdentrax%2Fgo-tilde-operator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdentrax%2Fgo-tilde-operator/lists"}