@@ -24,9 +24,9 @@ components.
24
24
25
25
=== Working Memory: A stack machine, it is not
26
26
27
- As opposed to its predecessor Jam (Joe's Abstract Machine) which was a
27
+ As opposed to its predecessor JAM (Joe's Abstract Machine) which was a
28
28
stack machine, the BEAM is a register machine loosely based on WAM
29
- (TODO: cite) . In a stack machine each operand to an instruction is
29
+ <<warren>> . In a stack machine each operand to an instruction is
30
30
first pushed to the working stack, then the instruction pops its
31
31
arguments and then it pushes the result on the stack.
32
32
49
49
50
50
This code can be generated directly from the parse tree of
51
51
the expression. By using Erlang expression and the modules
52
- +erl_scan+ and +erl_parse+ we can build the world's most simplistic
53
- compiler.
52
+ https://erlang.org/doc/man/erl_scan.html[+erl_scan+] and
53
+ https://erlang.org/doc/man/erl_parse.html[+erl_parse+] we
54
+ can build the world's most simplistic compiler.
54
55
55
56
[source,erlang]
56
57
-------------------------------------------
@@ -177,16 +178,16 @@ Then we get the following code for the add function:
177
178
------------------------------------------
178
179
179
180
Here we can see that the code (starting at label 2) first allocates a
180
- stack slot, to get space to save the argument _B_ over the function
181
- call _id (A)_ . The value is then saved by the instruction
182
- _ {move,{x,1},{y,0}}_ (read as move x1 to y0 or in imperative style: y0
183
- := x1).
181
+ stack slot, to get space to save the argument `B` over the function
182
+ call `id (A)` . The value is then saved by the instruction
183
+ ` {move,{x,1},{y,0}}` (read as move `x1` to `y0` or in imperative style: ` y0
184
+ := x1` ).
184
185
185
186
The id function (at label f4) is then called by
186
- _ {call,1,{f,4}}_ . (We will come back to what the argument "1" stands for later.)
187
- Then the result of the call (now in X0 )
188
- needs to be saved on the stack (Y0 ), but the argument _B_
189
- is saved in Y0 so the BEAM does a bit of shuffling:
187
+ ` {call,1,{f,4}}` . (We will come back to what the argument "1" stands for later.)
188
+ Then the result of the call (now in `X0` )
189
+ needs to be saved on the stack (`Y0` ), but the argument `B`
190
+ is saved in `Y0` so the BEAM does a bit of shuffling:
190
191
191
192
Except for the x and y registers, there are a number of special
192
193
purpose registers:
@@ -208,12 +209,12 @@ fields in the PCB.
208
209
{move,{x,1},{y,0}}. % y0 := x1 (id(A))
209
210
------------------------------------------
210
211
211
- Now we have the second argument _B_ in x0 (the first
212
- argument register) and we can call the _id_ function
213
- again _ {call,1,{f,4}}_.
212
+ Now we have the second argument `B` in `x0` (the first
213
+ argument register) and we can call the `id` function
214
+ again ` {call,1,{f,4}}`.
214
215
215
- After the call x0 contains _id (B)_ and y0 contains _id (A)_ ,
216
- now we can do the addition: _ {gc_bif,'+',{f,0},1,[{y,0},{x,0}],{x,0}}_ .
216
+ After the call x0 contains `id (B)` and `y0` contains `id (A)` ,
217
+ now we can do the addition: ` {gc_bif,'+',{f,0},1,[{y,0},{x,0}],{x,0}}` .
217
218
(We will go into the details of BIF calls and GC later.)
218
219
219
220
=== Dispatch: Directly Threaded Code
@@ -297,7 +298,7 @@ int run(char *code) {
297
298
You see, a virtual machine written in C does not need to
298
299
be very complicated. This machine is just a loop checking
299
300
the byte code at each instruction by looking at the value
300
- pointed to by the _instruction pointer _ (ip ).
301
+ pointed to by the _instruction pointer_ (`ip` ).
301
302
302
303
For each byte code instruction it will switch on the instruction byte
303
304
code and jump to the case which executes the instruction. This
@@ -424,7 +425,7 @@ values".
424
425
We will look closer at the BEAM emulator later but we will take a
425
426
quick look at how the add instruction is implemented. The code is
426
427
somewhat hard to follow due to the heavy usage of macros. The
427
- STORE_ARITH_RESULT macro actually hides the dispatch function which
428
+ ` STORE_ARITH_RESULT` macro actually hides the dispatch function which
428
429
looks something like: `I += 4; Goto(*I);`.
429
430
430
431
[source, C]
@@ -496,10 +497,10 @@ in this code:
496
497
{move,{x,1},{y,0}}.
497
498
-------------------------------------------
498
499
499
- This code first saves the return value of the function call (x0 ) in a
500
- new register (x1 ). Then it moves the caller saves register (y0 ) to
501
- the first argument register (x0 ). Finally it moves the saved value in
502
- x1 to the caller save register (y0 ) so that it will survive the next
500
+ This code first saves the return value of the function call (`x0` ) in a
501
+ new register (`x1` ). Then it moves the caller saves register (`y0` ) to
502
+ the first argument register (`x0` ). Finally it moves the saved value in
503
+ x1 to the caller save register (`y0` ) so that it will survive the next
503
504
function call.
504
505
505
506
Imagine that we would implement three instruction in BEAM called
@@ -633,7 +634,8 @@ need to do explicit memory management. On the BEAM level, though, the
633
634
code is responsible for checking for stack and heap overrun, and for
634
635
allocating enough space on the stack and the heap.
635
636
636
- The BEAM instruction +test_heap+ will ensure that there is as much
637
+ The BEAM instruction https://github.com/erlang/otp/blob/OTP-23.0/lib/compiler/src/genop.tab#L118[+test_heap+]
638
+ will ensure that there is as much
637
639
space on the heap as requested. If needed the instruction will call
638
640
the garbage collector to reclaim space on the heap. The garbage
639
641
collector in turn will call the lower levels of the memory subsystem
0 commit comments