annotate llvm/docs/tutorial/MyFirstLanguageFrontend/LangImpl10.rst @ 266:00f31e85ec16 default tip

Added tag current for changeset 31d058e83c98
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 14 Oct 2023 10:13:55 +0900
parents c4bab56944e8
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 ======================================================
anatofuz
parents:
diff changeset
2 Kaleidoscope: Conclusion and other useful LLVM tidbits
anatofuz
parents:
diff changeset
3 ======================================================
anatofuz
parents:
diff changeset
4
anatofuz
parents:
diff changeset
5 .. contents::
anatofuz
parents:
diff changeset
6 :local:
anatofuz
parents:
diff changeset
7
anatofuz
parents:
diff changeset
8 Tutorial Conclusion
anatofuz
parents:
diff changeset
9 ===================
anatofuz
parents:
diff changeset
10
anatofuz
parents:
diff changeset
11 Welcome to the final chapter of the "`Implementing a language with
anatofuz
parents:
diff changeset
12 LLVM <index.html>`_" tutorial. In the course of this tutorial, we have
anatofuz
parents:
diff changeset
13 grown our little Kaleidoscope language from being a useless toy, to
anatofuz
parents:
diff changeset
14 being a semi-interesting (but probably still useless) toy. :)
anatofuz
parents:
diff changeset
15
anatofuz
parents:
diff changeset
16 It is interesting to see how far we've come, and how little code it has
anatofuz
parents:
diff changeset
17 taken. We built the entire lexer, parser, AST, code generator, an
anatofuz
parents:
diff changeset
18 interactive run-loop (with a JIT!), and emitted debug information in
anatofuz
parents:
diff changeset
19 standalone executables - all in under 1000 lines of (non-comment/non-blank)
anatofuz
parents:
diff changeset
20 code.
anatofuz
parents:
diff changeset
21
anatofuz
parents:
diff changeset
22 Our little language supports a couple of interesting features: it
anatofuz
parents:
diff changeset
23 supports user defined binary and unary operators, it uses JIT
anatofuz
parents:
diff changeset
24 compilation for immediate evaluation, and it supports a few control flow
anatofuz
parents:
diff changeset
25 constructs with SSA construction.
anatofuz
parents:
diff changeset
26
anatofuz
parents:
diff changeset
27 Part of the idea of this tutorial was to show you how easy and fun it
anatofuz
parents:
diff changeset
28 can be to define, build, and play with languages. Building a compiler
anatofuz
parents:
diff changeset
29 need not be a scary or mystical process! Now that you've seen some of
anatofuz
parents:
diff changeset
30 the basics, I strongly encourage you to take the code and hack on it.
anatofuz
parents:
diff changeset
31 For example, try adding:
anatofuz
parents:
diff changeset
32
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
33 - **global variables** - While global variables have questionable value
150
anatofuz
parents:
diff changeset
34 in modern software engineering, they are often useful when putting
anatofuz
parents:
diff changeset
35 together quick little hacks like the Kaleidoscope compiler itself.
anatofuz
parents:
diff changeset
36 Fortunately, our current setup makes it very easy to add global
anatofuz
parents:
diff changeset
37 variables: just have value lookup check to see if an unresolved
anatofuz
parents:
diff changeset
38 variable is in the global variable symbol table before rejecting it.
anatofuz
parents:
diff changeset
39 To create a new global variable, make an instance of the LLVM
anatofuz
parents:
diff changeset
40 ``GlobalVariable`` class.
anatofuz
parents:
diff changeset
41 - **typed variables** - Kaleidoscope currently only supports variables
anatofuz
parents:
diff changeset
42 of type double. This gives the language a very nice elegance, because
anatofuz
parents:
diff changeset
43 only supporting one type means that you never have to specify types.
anatofuz
parents:
diff changeset
44 Different languages have different ways of handling this. The easiest
anatofuz
parents:
diff changeset
45 way is to require the user to specify types for every variable
anatofuz
parents:
diff changeset
46 definition, and record the type of the variable in the symbol table
anatofuz
parents:
diff changeset
47 along with its Value\*.
anatofuz
parents:
diff changeset
48 - **arrays, structs, vectors, etc** - Once you add types, you can start
anatofuz
parents:
diff changeset
49 extending the type system in all sorts of interesting ways. Simple
anatofuz
parents:
diff changeset
50 arrays are very easy and are quite useful for many different
anatofuz
parents:
diff changeset
51 applications. Adding them is mostly an exercise in learning how the
anatofuz
parents:
diff changeset
52 LLVM `getelementptr <../../LangRef.html#getelementptr-instruction>`_ instruction
anatofuz
parents:
diff changeset
53 works: it is so nifty/unconventional, it `has its own
anatofuz
parents:
diff changeset
54 FAQ <../../GetElementPtr.html>`_!
anatofuz
parents:
diff changeset
55 - **standard runtime** - Our current language allows the user to access
anatofuz
parents:
diff changeset
56 arbitrary external functions, and we use it for things like "printd"
anatofuz
parents:
diff changeset
57 and "putchard". As you extend the language to add higher-level
anatofuz
parents:
diff changeset
58 constructs, often these constructs make the most sense if they are
anatofuz
parents:
diff changeset
59 lowered to calls into a language-supplied runtime. For example, if
anatofuz
parents:
diff changeset
60 you add hash tables to the language, it would probably make sense to
anatofuz
parents:
diff changeset
61 add the routines to a runtime, instead of inlining them all the way.
anatofuz
parents:
diff changeset
62 - **memory management** - Currently we can only access the stack in
anatofuz
parents:
diff changeset
63 Kaleidoscope. It would also be useful to be able to allocate heap
anatofuz
parents:
diff changeset
64 memory, either with calls to the standard libc malloc/free interface
anatofuz
parents:
diff changeset
65 or with a garbage collector. If you would like to use garbage
anatofuz
parents:
diff changeset
66 collection, note that LLVM fully supports `Accurate Garbage
anatofuz
parents:
diff changeset
67 Collection <../../GarbageCollection.html>`_ including algorithms that
anatofuz
parents:
diff changeset
68 move objects and need to scan/update the stack.
anatofuz
parents:
diff changeset
69 - **exception handling support** - LLVM supports generation of `zero
anatofuz
parents:
diff changeset
70 cost exceptions <../../ExceptionHandling.html>`_ which interoperate with
anatofuz
parents:
diff changeset
71 code compiled in other languages. You could also generate code by
anatofuz
parents:
diff changeset
72 implicitly making every function return an error value and checking
anatofuz
parents:
diff changeset
73 it. You could also make explicit use of setjmp/longjmp. There are
anatofuz
parents:
diff changeset
74 many different ways to go here.
anatofuz
parents:
diff changeset
75 - **object orientation, generics, database access, complex numbers,
anatofuz
parents:
diff changeset
76 geometric programming, ...** - Really, there is no end of crazy
anatofuz
parents:
diff changeset
77 features that you can add to the language.
anatofuz
parents:
diff changeset
78 - **unusual domains** - We've been talking about applying LLVM to a
anatofuz
parents:
diff changeset
79 domain that many people are interested in: building a compiler for a
anatofuz
parents:
diff changeset
80 specific language. However, there are many other domains that can use
anatofuz
parents:
diff changeset
81 compiler technology that are not typically considered. For example,
anatofuz
parents:
diff changeset
82 LLVM has been used to implement OpenGL graphics acceleration,
anatofuz
parents:
diff changeset
83 translate C++ code to ActionScript, and many other cute and clever
anatofuz
parents:
diff changeset
84 things. Maybe you will be the first to JIT compile a regular
anatofuz
parents:
diff changeset
85 expression interpreter into native code with LLVM?
anatofuz
parents:
diff changeset
86
anatofuz
parents:
diff changeset
87 Have fun - try doing something crazy and unusual. Building a language
anatofuz
parents:
diff changeset
88 like everyone else always has, is much less fun than trying something a
anatofuz
parents:
diff changeset
89 little crazy or off the wall and seeing how it turns out. If you get
236
c4bab56944e8 LLVM 16
kono
parents: 221
diff changeset
90 stuck or want to talk about it, please post on the `LLVM forums
c4bab56944e8 LLVM 16
kono
parents: 221
diff changeset
91 <https://discourse.llvm.org>`_: it has lots of people who are interested
c4bab56944e8 LLVM 16
kono
parents: 221
diff changeset
92 in languages and are often willing to help out.
150
anatofuz
parents:
diff changeset
93
anatofuz
parents:
diff changeset
94 Before we end this tutorial, I want to talk about some "tips and tricks"
anatofuz
parents:
diff changeset
95 for generating LLVM IR. These are some of the more subtle things that
anatofuz
parents:
diff changeset
96 may not be obvious, but are very useful if you want to take advantage of
anatofuz
parents:
diff changeset
97 LLVM's capabilities.
anatofuz
parents:
diff changeset
98
anatofuz
parents:
diff changeset
99 Properties of the LLVM IR
anatofuz
parents:
diff changeset
100 =========================
anatofuz
parents:
diff changeset
101
anatofuz
parents:
diff changeset
102 We have a couple of common questions about code in the LLVM IR form -
anatofuz
parents:
diff changeset
103 let's just get these out of the way right now, shall we?
anatofuz
parents:
diff changeset
104
anatofuz
parents:
diff changeset
105 Target Independence
anatofuz
parents:
diff changeset
106 -------------------
anatofuz
parents:
diff changeset
107
anatofuz
parents:
diff changeset
108 Kaleidoscope is an example of a "portable language": any program written
anatofuz
parents:
diff changeset
109 in Kaleidoscope will work the same way on any target that it runs on.
anatofuz
parents:
diff changeset
110 Many other languages have this property, e.g. lisp, java, haskell,
anatofuz
parents:
diff changeset
111 javascript, python, etc (note that while these languages are portable,
anatofuz
parents:
diff changeset
112 not all their libraries are).
anatofuz
parents:
diff changeset
113
anatofuz
parents:
diff changeset
114 One nice aspect of LLVM is that it is often capable of preserving target
anatofuz
parents:
diff changeset
115 independence in the IR: you can take the LLVM IR for a
anatofuz
parents:
diff changeset
116 Kaleidoscope-compiled program and run it on any target that LLVM
anatofuz
parents:
diff changeset
117 supports, even emitting C code and compiling that on targets that LLVM
anatofuz
parents:
diff changeset
118 doesn't support natively. You can trivially tell that the Kaleidoscope
anatofuz
parents:
diff changeset
119 compiler generates target-independent code because it never queries for
anatofuz
parents:
diff changeset
120 any target-specific information when generating code.
anatofuz
parents:
diff changeset
121
anatofuz
parents:
diff changeset
122 The fact that LLVM provides a compact, target-independent,
anatofuz
parents:
diff changeset
123 representation for code gets a lot of people excited. Unfortunately,
anatofuz
parents:
diff changeset
124 these people are usually thinking about C or a language from the C
anatofuz
parents:
diff changeset
125 family when they are asking questions about language portability. I say
anatofuz
parents:
diff changeset
126 "unfortunately", because there is really no way to make (fully general)
anatofuz
parents:
diff changeset
127 C code portable, other than shipping the source code around (and of
anatofuz
parents:
diff changeset
128 course, C source code is not actually portable in general either - ever
anatofuz
parents:
diff changeset
129 port a really old application from 32- to 64-bits?).
anatofuz
parents:
diff changeset
130
anatofuz
parents:
diff changeset
131 The problem with C (again, in its full generality) is that it is heavily
anatofuz
parents:
diff changeset
132 laden with target specific assumptions. As one simple example, the
anatofuz
parents:
diff changeset
133 preprocessor often destructively removes target-independence from the
anatofuz
parents:
diff changeset
134 code when it processes the input text:
anatofuz
parents:
diff changeset
135
anatofuz
parents:
diff changeset
136 .. code-block:: c
anatofuz
parents:
diff changeset
137
anatofuz
parents:
diff changeset
138 #ifdef __i386__
anatofuz
parents:
diff changeset
139 int X = 1;
anatofuz
parents:
diff changeset
140 #else
anatofuz
parents:
diff changeset
141 int X = 42;
anatofuz
parents:
diff changeset
142 #endif
anatofuz
parents:
diff changeset
143
anatofuz
parents:
diff changeset
144 While it is possible to engineer more and more complex solutions to
anatofuz
parents:
diff changeset
145 problems like this, it cannot be solved in full generality in a way that
anatofuz
parents:
diff changeset
146 is better than shipping the actual source code.
anatofuz
parents:
diff changeset
147
anatofuz
parents:
diff changeset
148 That said, there are interesting subsets of C that can be made portable.
anatofuz
parents:
diff changeset
149 If you are willing to fix primitive types to a fixed size (say int =
anatofuz
parents:
diff changeset
150 32-bits, and long = 64-bits), don't care about ABI compatibility with
anatofuz
parents:
diff changeset
151 existing binaries, and are willing to give up some other minor features,
anatofuz
parents:
diff changeset
152 you can have portable code. This can make sense for specialized domains
anatofuz
parents:
diff changeset
153 such as an in-kernel language.
anatofuz
parents:
diff changeset
154
anatofuz
parents:
diff changeset
155 Safety Guarantees
anatofuz
parents:
diff changeset
156 -----------------
anatofuz
parents:
diff changeset
157
anatofuz
parents:
diff changeset
158 Many of the languages above are also "safe" languages: it is impossible
anatofuz
parents:
diff changeset
159 for a program written in Java to corrupt its address space and crash the
anatofuz
parents:
diff changeset
160 process (assuming the JVM has no bugs). Safety is an interesting
anatofuz
parents:
diff changeset
161 property that requires a combination of language design, runtime
anatofuz
parents:
diff changeset
162 support, and often operating system support.
anatofuz
parents:
diff changeset
163
anatofuz
parents:
diff changeset
164 It is certainly possible to implement a safe language in LLVM, but LLVM
anatofuz
parents:
diff changeset
165 IR does not itself guarantee safety. The LLVM IR allows unsafe pointer
anatofuz
parents:
diff changeset
166 casts, use after free bugs, buffer over-runs, and a variety of other
anatofuz
parents:
diff changeset
167 problems. Safety needs to be implemented as a layer on top of LLVM and,
236
c4bab56944e8 LLVM 16
kono
parents: 221
diff changeset
168 conveniently, several groups have investigated this. Ask on the `LLVM
c4bab56944e8 LLVM 16
kono
parents: 221
diff changeset
169 forums <https://discourse.llvm.org>`_ if you are interested in more details.
150
anatofuz
parents:
diff changeset
170
anatofuz
parents:
diff changeset
171 Language-Specific Optimizations
anatofuz
parents:
diff changeset
172 -------------------------------
anatofuz
parents:
diff changeset
173
anatofuz
parents:
diff changeset
174 One thing about LLVM that turns off many people is that it does not
anatofuz
parents:
diff changeset
175 solve all the world's problems in one system. One specific
anatofuz
parents:
diff changeset
176 complaint is that people perceive LLVM as being incapable of performing
anatofuz
parents:
diff changeset
177 high-level language-specific optimization: LLVM "loses too much
anatofuz
parents:
diff changeset
178 information". Here are a few observations about this:
anatofuz
parents:
diff changeset
179
anatofuz
parents:
diff changeset
180 First, you're right that LLVM does lose information. For example, as of
anatofuz
parents:
diff changeset
181 this writing, there is no way to distinguish in the LLVM IR whether an
anatofuz
parents:
diff changeset
182 SSA-value came from a C "int" or a C "long" on an ILP32 machine (other
anatofuz
parents:
diff changeset
183 than debug info). Both get compiled down to an 'i32' value and the
anatofuz
parents:
diff changeset
184 information about what it came from is lost. The more general issue
anatofuz
parents:
diff changeset
185 here, is that the LLVM type system uses "structural equivalence" instead
anatofuz
parents:
diff changeset
186 of "name equivalence". Another place this surprises people is if you
anatofuz
parents:
diff changeset
187 have two types in a high-level language that have the same structure
anatofuz
parents:
diff changeset
188 (e.g. two different structs that have a single int field): these types
anatofuz
parents:
diff changeset
189 will compile down into a single LLVM type and it will be impossible to
anatofuz
parents:
diff changeset
190 tell what it came from.
anatofuz
parents:
diff changeset
191
anatofuz
parents:
diff changeset
192 Second, while LLVM does lose information, LLVM is not a fixed target: we
anatofuz
parents:
diff changeset
193 continue to enhance and improve it in many different ways. In addition
anatofuz
parents:
diff changeset
194 to adding new features (LLVM did not always support exceptions or debug
anatofuz
parents:
diff changeset
195 info), we also extend the IR to capture important information for
anatofuz
parents:
diff changeset
196 optimization (e.g. whether an argument is sign or zero extended,
anatofuz
parents:
diff changeset
197 information about pointers aliasing, etc). Many of the enhancements are
anatofuz
parents:
diff changeset
198 user-driven: people want LLVM to include some specific feature, so they
anatofuz
parents:
diff changeset
199 go ahead and extend it.
anatofuz
parents:
diff changeset
200
anatofuz
parents:
diff changeset
201 Third, it is *possible and easy* to add language-specific optimizations,
anatofuz
parents:
diff changeset
202 and you have a number of choices in how to do it. As one trivial
anatofuz
parents:
diff changeset
203 example, it is easy to add language-specific optimization passes that
anatofuz
parents:
diff changeset
204 "know" things about code compiled for a language. In the case of the C
anatofuz
parents:
diff changeset
205 family, there is an optimization pass that "knows" about the standard C
anatofuz
parents:
diff changeset
206 library functions. If you call "exit(0)" in main(), it knows that it is
anatofuz
parents:
diff changeset
207 safe to optimize that into "return 0;" because C specifies what the
anatofuz
parents:
diff changeset
208 'exit' function does.
anatofuz
parents:
diff changeset
209
anatofuz
parents:
diff changeset
210 In addition to simple library knowledge, it is possible to embed a
anatofuz
parents:
diff changeset
211 variety of other language-specific information into the LLVM IR. If you
anatofuz
parents:
diff changeset
212 have a specific need and run into a wall, please bring the topic up on
anatofuz
parents:
diff changeset
213 the llvm-dev list. At the very worst, you can always treat LLVM as if it
anatofuz
parents:
diff changeset
214 were a "dumb code generator" and implement the high-level optimizations
anatofuz
parents:
diff changeset
215 you desire in your front-end, on the language-specific AST.
anatofuz
parents:
diff changeset
216
anatofuz
parents:
diff changeset
217 Tips and Tricks
anatofuz
parents:
diff changeset
218 ===============
anatofuz
parents:
diff changeset
219
anatofuz
parents:
diff changeset
220 There is a variety of useful tips and tricks that you come to know after
anatofuz
parents:
diff changeset
221 working on/with LLVM that aren't obvious at first glance. Instead of
anatofuz
parents:
diff changeset
222 letting everyone rediscover them, this section talks about some of these
anatofuz
parents:
diff changeset
223 issues.
anatofuz
parents:
diff changeset
224
anatofuz
parents:
diff changeset
225 Implementing portable offsetof/sizeof
anatofuz
parents:
diff changeset
226 -------------------------------------
anatofuz
parents:
diff changeset
227
anatofuz
parents:
diff changeset
228 One interesting thing that comes up, if you are trying to keep the code
anatofuz
parents:
diff changeset
229 generated by your compiler "target independent", is that you often need
anatofuz
parents:
diff changeset
230 to know the size of some LLVM type or the offset of some field in an
anatofuz
parents:
diff changeset
231 llvm structure. For example, you might need to pass the size of a type
anatofuz
parents:
diff changeset
232 into a function that allocates memory.
anatofuz
parents:
diff changeset
233
anatofuz
parents:
diff changeset
234 Unfortunately, this can vary widely across targets: for example the
anatofuz
parents:
diff changeset
235 width of a pointer is trivially target-specific. However, there is a
anatofuz
parents:
diff changeset
236 `clever way to use the getelementptr
anatofuz
parents:
diff changeset
237 instruction <http://nondot.org/sabre/LLVMNotes/SizeOf-OffsetOf-VariableSizedStructs.txt>`_
anatofuz
parents:
diff changeset
238 that allows you to compute this in a portable way.
anatofuz
parents:
diff changeset
239
anatofuz
parents:
diff changeset
240 Garbage Collected Stack Frames
anatofuz
parents:
diff changeset
241 ------------------------------
anatofuz
parents:
diff changeset
242
anatofuz
parents:
diff changeset
243 Some languages want to explicitly manage their stack frames, often so
anatofuz
parents:
diff changeset
244 that they are garbage collected or to allow easy implementation of
anatofuz
parents:
diff changeset
245 closures. There are often better ways to implement these features than
anatofuz
parents:
diff changeset
246 explicit stack frames, but `LLVM does support
anatofuz
parents:
diff changeset
247 them, <http://nondot.org/sabre/LLVMNotes/ExplicitlyManagedStackFrames.txt>`_
anatofuz
parents:
diff changeset
248 if you want. It requires your front-end to convert the code into
anatofuz
parents:
diff changeset
249 `Continuation Passing
anatofuz
parents:
diff changeset
250 Style <http://en.wikipedia.org/wiki/Continuation-passing_style>`_ and
anatofuz
parents:
diff changeset
251 the use of tail calls (which LLVM also supports).
anatofuz
parents:
diff changeset
252