1 Getting Started with VIXL 2 ========================= 3 4 5 This guide will show you how to use the VIXL framework. We will see how to set 6 up the VIXL assembler and generate some code. We will also go into details on a 7 few useful features provided by VIXL and see how to run the generated code in 8 the VIXL simulator. 9 10 The source code of the example developed in this guide can be found in the 11 `examples` directory (`examples/getting-started.cc`). 12 13 14 Creating the macro assembler and the simulator. 15 ----------------------------------------------- 16 17 First of all you need to make sure that the header files for the assembler and 18 the simulator are included. You should have the following lines at the beginning 19 of your source file: 20 21 #include "a64/simulator-a64.h" 22 #include "a64/macro-assembler-a64.h" 23 24 VIXL's assembler will generate some code at run-time, and this code needs to 25 be stored in a buffer. It must be large enough to contain all of the 26 instructions and data that will be generated. In this guide we will use a 27 default value of 4096 but you are free to change it to something that suits your 28 needs. 29 30 #define BUF_SIZE (4096) 31 32 All VIXL components are declared in the `vixl` namespace, so let's add this to 33 the beginning of the file for convenience: 34 35 using namespace vixl; 36 37 Now we are ready to create and initialize the different components. 38 39 First of all we need to allocate the code buffer and to create a macro 40 assembler object which uses this buffer. 41 42 byte assm_buf[BUF_SIZE]; 43 MacroAssembler masm(assm_buf, BUF_SIZE); 44 45 We also need to set-up the simulator. The simulator uses a Decoder object to 46 read and decode the instructions from the code buffer. We need to create a 47 decoder and bind our simulator to this decoder. 48 49 Decoder decoder; 50 Simulator simulator(&decoder); 51 52 53 Generating some code. 54 --------------------- 55 56 We are now ready to generate some code. The macro assembler provides methods 57 for all the instructions that you can use. As it's a macro assembler, 58 the instructions that you tell it to generate may not directly map to a single 59 hardware instruction. Instead, it can produce a short sequence of instructions 60 that has the same effect. 61 62 For instance, the hardware `add` instruction can only take a 12-bit immediate 63 optionally shifted by 12, but the macro assembler can generate one or more 64 instructions to handle any 64-bit immediate. For example, `Add(x0, x0, -1)` 65 will be turned into `Sub(x0, x0, 1)`. 66 67 Before looking at how to generate some code, let's introduce a simple but handy 68 macro: 69 70 #define __ masm-> 71 72 It allows us to write `__ Mov(x0, 42);` instead of `masm->Mov(x0, 42);` to 73 generate code. 74 75 Now we are going to write a C++ function to generate our first assembly 76 code fragment. 77 78 void GenerateDemoFunction(MacroAssembler *masm) { 79 __ Ldr(x1, 0x1122334455667788); 80 __ And(x0, x0, x1); 81 __ Ret(); 82 } 83 84 The generated code corresponds to a function with the following C prototype: 85 86 uint64_t demo_function(uint64_t x); 87 88 This function doesn't perform any useful operation. It loads the value 89 0x1122334455667788 into x1 and performs a bitwise `and` operation with 90 the function's argument (stored in x0). The result of this `and` operation 91 is returned by the function in x0. 92 93 Now in our program main function, we only need to create a label to represent 94 the entry point of the assembly function and to call `GenerateDemoFunction` to 95 generate the code. 96 97 Label demo_function; 98 masm.Bind(&demo_function); 99 GenerateDemoFunction(&masm); 100 masm.Finalize(); 101 102 Now we are going to learn a bit more on a couple of interesting VIXL features 103 which are used in this example. 104 105 ### Label 106 107 VIXL's assembler provides a mechanism to represent labels with `Label` objects. 108 They are easy to use: simply create the C++ object and bind it to a location in 109 the generated instruction stream. 110 111 Creating a label is easy, since you only need to define the variable and bind it 112 to 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 117 The target of a branch using a label will be the address to which it has been 118 bound. 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 127 If we run this code fragment the `Mov(x0, 42)` will never be executed since 128 the first thing this code does is to jump to `foo`, which correspond to the 129 `Mov(x1, 0xc001)` instruction. 130 131 When working with labels you need to know that they are only to be used for 132 local branches, and should be passed around with care. There are two reasons 133 for 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 149 On ARMv8 instructions are 32 bits long, thus immediate values encoded in the 150 instructions have limited size. If you want to load a constant bigger than this 151 limit you have two possibilities: 152 153 1. 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 163 2. 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 183 Running the code in the simulator. 184 ---------------------------------- 185 186 Now we are going to see how to use the simulator to run the code that we 187 generated previously. 188 189 Use the simulator to assign a value to the registers. Our previous code example 190 uses the register x0 as an input, so let's set the value of this register. 191 192 simulator.set_xreg(0, 0x8899aabbccddeeff); 193 194 Now we can jump to the "entry" label to execute the code: 195 196 simulator.RunFrom(entry.target()); 197 198 When the execution is finished and the simulator returned, you can inspect 199 the value of the registers after the execution. For instance: 200 201 printf("x0 = %" PRIx64 "\n", simulator.xreg(0)); 202 203 The example shown in this tutorial is very simple, because the goal was to 204 demonstrate the basics of the VIXL framework. There are more complex code 205 examples in the VIXL `examples` directory showing more features of both the 206 macro assembler and the ARMv8 architecture. 207