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