• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Getting Started with VIXL
2=========================
3
4
5This guide will show you how to use the VIXL framework. We will see how to set
6up the VIXL assembler and generate some code. We will also go into details on a
7few useful features provided by VIXL and see how to run the generated code in
8the VIXL simulator.
9
10The source code of the example developed in this guide can be found in the
11`examples` directory (`examples/getting-started.cc`).
12
13
14Creating the macro assembler and the simulator.
15-----------------------------------------------
16
17First of all you need to make sure that the header files for the assembler and
18the simulator are included. You should have the following lines at the beginning
19of your source file:
20
21    #include "a64/simulator-a64.h"
22    #include "a64/macro-assembler-a64.h"
23
24VIXL's assembler will generate some code at run-time, and this code needs to
25be stored in a buffer. It must be large enough to contain all of the
26instructions and data that will be generated. In this guide we will use a
27default value of 4096 but you are free to change it to something that suits your
28needs.
29
30    #define BUF_SIZE (4096)
31
32All VIXL components are declared in the `vixl` namespace, so let's add this to
33the beginning of the file for convenience:
34
35    using namespace vixl;
36
37Now we are ready to create and initialize the different components.
38
39First of all we need to allocate the code buffer and to create a macro
40assembler object which uses this buffer.
41
42    byte assm_buf[BUF_SIZE];
43    MacroAssembler masm(assm_buf, BUF_SIZE);
44
45We also need to set-up the simulator. The simulator uses a Decoder object to
46read and decode the instructions from the code buffer. We need to create a
47decoder and bind our simulator to this decoder.
48
49    Decoder decoder;
50    Simulator simulator(&decoder);
51
52
53Generating some code.
54---------------------
55
56We are now ready to generate some code. The macro assembler provides methods
57for all the instructions that you can use. As it's a macro assembler,
58the instructions that you tell it to generate may not directly map to a single
59hardware instruction. Instead, it can produce a short sequence of instructions
60that has the same effect.
61
62For instance, the hardware `add` instruction can only take a 12-bit immediate
63optionally shifted by 12, but the macro assembler can generate one or more
64instructions to handle any 64-bit immediate. For example, `Add(x0, x0, -1)`
65will be turned into `Sub(x0, x0, 1)`.
66
67Before looking at how to generate some code, let's introduce a simple but handy
68macro:
69
70    #define __ masm->
71
72It allows us to write `__ Mov(x0, 42);` instead of `masm->Mov(x0, 42);` to
73generate code.
74
75Now we are going to write a C++ function to generate our first assembly
76code fragment.
77
78    void GenerateDemoFunction(MacroAssembler *masm) {
79      __ Ldr(x1, 0x1122334455667788);
80      __ And(x0, x0, x1);
81      __ Ret();
82    }
83
84The generated code corresponds to a function with the following C prototype:
85
86    uint64_t demo_function(uint64_t x);
87
88This function doesn't perform any useful operation. It loads the value
890x1122334455667788 into x1 and performs a bitwise `and` operation with
90the function's argument (stored in x0). The result of this `and` operation
91is returned by the function in x0.
92
93Now in our program main function, we only need to create a label to represent
94the entry point of the assembly function and to call `GenerateDemoFunction` to
95generate the code.
96
97    Label demo_function;
98    masm.Bind(&demo_function);
99    GenerateDemoFunction(&masm);
100    masm.Finalize();
101
102Now we are going to learn a bit more on a couple of interesting VIXL features
103which are used in this example.
104
105### Label
106
107VIXL's assembler provides a mechanism to represent labels with `Label` objects.
108They are easy to use: simply create the C++ object and bind it to a location in
109the generated instruction stream.
110
111Creating a label is easy, since you only need to define the variable and bind it
112to a location using the macro assembler.
113
114    Label my_label;      // Create the label object.
115    __ Bind(&my_label);  // Bind it to the current location.
116
117The target of a branch using a label will be the address to which it has been
118bound. For example, let's consider the following code fragment:
119
120    Label foo;
121
122    __ B(&foo);     // Branch to foo.
123    __ Mov(x0, 42);
124    __ Bind(&foo);  // Actual address of foo is here.
125    __ Mov(x1, 0xc001);
126
127If we run this code fragment the `Mov(x0, 42)` will never be executed since
128the first thing this code does is to jump to `foo`, which correspond to the
129`Mov(x1, 0xc001)` instruction.
130
131When working with labels you need to know that they are only to be used for
132local branches, and should be passed around with care. There are two reasons
133for this:
134
135  - They can't safely be passed or returned by value because this can trigger
136    multiple constructor and destructor calls. The destructor has assertions
137    to check that we don't try to branch to a label that hasn't been bound.
138
139  - The `B` instruction does not branch to labels which are out of range of the
140    branch. The `B` instruction has a range of 2^28 bytes, but other variants
141    (such as conditional or `CBZ`-like branches) have smaller ranges. Confining
142    them to local ranges doesn't mean that we won't hit these limits, but it
143    makes the lifetime of the labels much shorter and eases the debugging of
144    these kinds of issues.
145
146
147### Literal Pool
148
149On ARMv8 instructions are 32 bits long, thus immediate values encoded in the
150instructions have limited size. If you want to load a constant bigger than this
151limit you have two possibilities:
152
1531. Use multiple instructions to load the constant in multiple steps. This
154  solution is already handled in VIXL. For instance you can write:
155
156  `__ Mov(x0, 0x1122334455667788);`
157
158  The previous instruction would not be legal since the immediate value is too
159  big. However, VIXL's macro assembler will automatically rewrite this line into
160  multiple instructions to efficiently generate the value.
161
162
1632. Store the constant in memory and load this value from the memory. The value
164  needs to be written near the code that will load it since we use a PC-relative
165  offset to indicate the address of this value. This solution has the advantage
166  of making the value easily modifiable at run-time; since it does not reside
167  in the instruction stream, it doesn't require cache maintenance when updated.
168
169  VIXL also provides a way to do this:
170
171  `__ Ldr(x0, 0x1122334455667788);`
172
173  The assembler will store the immediate value in a "literal pool", a set of
174  constants embedded in the code. VIXL will emit literal pools after natural
175  breaks in the control flow, such as unconditional branches or return
176  instructions.
177
178  Literal pools are emitted regularly, such that they are within range of the
179  instructions that refer to them. However, you can force a literal pool to be
180  emitted using `masm.EmitLiteralPool()`.
181
182
183Running the code in the simulator.
184----------------------------------
185
186Now we are going to see how to use the simulator to run the code that we
187generated previously.
188
189Use the simulator to assign a value to the registers. Our previous code example
190uses the register x0 as an input, so let's set the value of this register.
191
192    simulator.set_xreg(0, 0x8899aabbccddeeff);
193
194Now we can jump to the "entry" label to execute the code:
195
196    simulator.RunFrom(entry.target());
197
198When the execution is finished and the simulator returned, you can inspect
199the value of the registers after the execution. For instance:
200
201    printf("x0 = %" PRIx64 "\n", simulator.xreg(0));
202
203The example shown in this tutorial is very simple, because the goal was to
204demonstrate the basics of the VIXL framework. There are more complex code
205examples in the VIXL `examples` directory showing more features of both the
206macro assembler and the ARMv8 architecture.
207