
The C# compiler and ‘Lowering’ - matthewwarren
http://mattwarren.org/2017/05/25/Lowering-in-the-C-Compiler/
======
dfox
Coming from Lisp background one would take this as pretty obvious approach.

In fact I somewhat suspect that CL's tagbody is explicitly designed such that
every non-trivial control structure in the language could be expanded into
that.

------
johnbender
"Lowering" (though I've never heard it called that) is also handy in the
course of working with formal semantics especially in the case of proofs and
other serious reasoning. You can "port" your reasoning from the "lower"
construct to the special case of the higher construct.

In general this also translates to simply understanding the semantics of a
language. If you can describe something in terms of another concept the
listener already has an intuition for, often it makes the new thing easier to
understand and learn.

Though it's easy to imagine cases where the "lower" thing is so abstract that
it's hard to apprehend in the first place (e.g. "everything is just a
closure!").

~~~
fanf2
I thought the usual term is "desugaring"

~~~
saurik
I have been caring a lot about languages and compilers for almost two decades
now, and I have heard the term "lowering" often... but it means something
different (and broader) than "desugaring". Essentially: I disagree with the
linguistic argument of this author :/. Here is a comment I left elsewhere on
this post with a bunch of examples from a variety of compiler projects showing
how I have always heard and understood the term "lowering":
[https://news.ycombinator.com/item?id=14429533](https://news.ycombinator.com/item?id=14429533)
.

------
maxxxxx
The C preprocessor allows you to do lowering to some degree. If I remember
correctly Lisp is pretty much built on the idea of extending the language by
itself.

It would be nice if other languages allowed implementing "syntactic sugar" in
an easy way. A lot of stuff that's done with reflection in C# could be
replaced.

~~~
matthewwarren
> It would be nice if other languages allowed implementing "syntactic sugar"
> in an easy way. A lot of stuff that's done with reflection in C# could be
> replaced.

They're talking about allowing that in C#, but it's not there yet, see
[https://github.com/dotnet/csharplang/issues/107](https://github.com/dotnet/csharplang/issues/107)

~~~
maxxxxx
Nice. It's a little disappointing to read that Visual Studio is holding this
up.

------
DonbunEf7
The E and Monte programming languages do this too, lowering from Full-E or
Full-Monte to Kernel-E or Kernel-Monte via a canonical "expansion" phase. In
Monte, this indeed is the bulk of the work done by the compiler, and the
optimizer only operates on Kernel-Monte.

------
WorldMaker
Yesterday I was doing some debugging on the async/await downlevel support in
Typescript and found myself exploring the ways Typescript lowers code in the
source itself (as much to satisfy curiosity as anything else, similar to this
article series' experiments/explorations of the Roslyn codebase). In its case
the similarities between Typescript itself and its target language provide a
particularly interesting question of what counts as lowering to Typescript
versus lowering to JS/ES, particularly in places where one becomes the other
as JS/ES standardizes different pieces into the wild.

For Typescript "lowering" seems a more accurate term than "compiling" in just
about all cases.

------
jstimpfle
The way I understand it, "lowering" is just another word for "compiling" to
intermediate representations. It probably exists because traditionally there
aren't many IR, or none at all.

There have been attempts to express compilation as a series of many more,
maybe 50, intermediate steps, implemented in some LISP. I don't know if there
are success stories. I think there is always a tension between modeling data
structures close enough to your understanding to enable clean implementation
and not modeling so many data structures that one loses track of them.

~~~
infogulch
The difference between compiling and lowering is that the syntax of the output
of lowering is a strict subset of the syntax of its' input, whereas the output
of compiling produces a completely different format, e.g. an AST, an in-memory
graph, assembly, machine code, etc.

This makes implementing the next layer (compiler perhaps) simpler because it
has to understand fewer syntax elements. This also makes a difference is that
you could manually write the output of the lowered code yourself in the
original language. Another effect is that (ideally) applying the lowering
function L() to any code a second time should produce no change i.e. L(code)
== L(L(code)). This is not true of compiling.

~~~
saurik
I am pretty sure you have added a lot of personal context to this word that is
unrelated to how it is actually used: I will argue that for decades, the term
"lowering" has meant a transformation to a "lower-level" representation, and
that this representation often is "a completely different format".

[https://www.cs.utexas.edu/users/cart/Scale/lowering.html](https://www.cs.utexas.edu/users/cart/Scale/lowering.html)

> Lowering is the transformation of higher level representations to lower
> level representations. For example, subscript expressions are lowered to
> address arithmetic operations. A compiler traditionally lowers a high level
> language to machine code in one or more steps.

[https://en.m.wikibooks.org/wiki/GNU_C_Compiler_Internals/GNU...](https://en.m.wikibooks.org/wiki/GNU_C_Compiler_Internals/GNU_C_Compiler_Architecture_4_1)

> As a result of lowering a function its control-flow graph is generated.

[http://tap2k.org/projects/WIL/](http://tap2k.org/projects/WIL/)

> Generally each data value will start with an abstract representation, which
> will in turn go through a sequence of lowerings as representation choices
> are made. These lowerings will eventually transform both the representation
> and the operations upon it to fully concrete forms, down to the level of the
> actual bit layout of the value in memory, allowing for easy low-level code
> generation.

[https://pdfs.semanticscholar.org/92ea/93e93a77b80465e7aeb53d...](https://pdfs.semanticscholar.org/92ea/93e93a77b80465e7aeb53df679fee2203caa.pdf)

> Instead of the compiler backend lowering object operations to machine
> operations using hard-wired runtime- specific logic, XIR allows the runtime
> system to implement this logic, simultaneously simplifying and separating
> the backend from runtime-system details.

[https://www.researchgate.net/profile/Michael_Burke9/publicat...](https://www.researchgate.net/profile/Michael_Burke9/publication/2649955_The_Jalapeno_Dynamic_Optimizing_Compiler_for_Java/links/00b7d518a8b2b3cb67000000/The-
Jalapeno-Dynamic-Optimizing-Compiler-for-Java.pdf)

> After high-level analyses and optimizations are performed, HIR is lowered to
> low-level IR (LIR). In contrast to HIR, the LIR expands instructions into
> operations that are specific to the Jalapeño JVM implementation, such as
> object layouts or parameter-passing mechanisms of the Jalapefio JVM. For
> example, operations in HIR to invoke methods of an object or of a class
> consist of a single instruction, matching the corresponding bytecode
> instructions invokevirtual/invokestatic. These single-instruction operations
> are lowered (i.e., converted) into multiple-instruction LIR operations that
> invoke the methods based on the virtual-function-table layout.

