forked from cirosantilli/cpp-cheat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
asm.c
282 lines (214 loc) · 6.4 KB
/
asm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
# Assembly
# Inline assembly
# asm
Great intro: http://www.ibm.com/developerworks/library/l-ia/index.html
Can be used if you really, really want to optimize at the cost of:
- architecture dependance
- tying you to gcc
If you use this, do it like the linux kernel and separate different architecture
code in different dirs.
General syntax:
asm (
"mov $1, %%eax;" // commands string
: "=X" (y), // outputs
"=X" (z)
: "X" (x) // inputs
: "%eax" // clobbered registers
);
where:
- commands: actual gas code into a single c string. Remember: each command has to end in newline or `;`.
- outputs: start with `=`. gcc has to enforce is that at the end of the `asm` block that those values are set.
- inputs:
- clobbered registers:
Registers that may be modified explicitly in the assembly code.
Normally, users have no direct access to registers,
so gcc is free to optimize by leaving values in those registers for later use.
This tells gcc not to leave values in the listed since those may be modified.
E.g.:
mov $0, %eax
Clearly clobbers eax, so you would need to list eax in the clobber list.
Note that certain instructions clobber registers
even if they are not explicitly written in the code.
Both inputs and outputs are constraints. `X` will indicate the constraint type
TODO: # __asm__ vs asm
TODO: # asmlinkage
TODO: # asm volatile
*/
#include "common.h"
int main(void) {
#if defined(__i386__) || defined(__x86_64__)
puts("__i386__ || __x86_64__");
/*
# Basic asm
# No colon
There are two types of asm: basic and extended.
The basic one does not have a colon after the string.
Basic is strictly less powerful: it can only deal with literal commands.
*/
{
#ifdef __i386__
asm volatile ("push %eax; mov $1, %eax; pop %eax;");
#else
asm volatile ("push %rax; mov $1, %rax; pop %rax;");
#endif
}
/*
# m constraint
Instructs GCC to keep value of given expressions into RAM.
This is the most basic way to get/set values of C variables in assembly code.
*/
{
int in = 1;
int out = 0;
/*out = in*/
asm volatile (
"movl %1, %%eax;"
"movl %%eax, %0"
: "=m" (out)
: "m" (in)
: "%eax"
);
assert(out == 1);
}
/* No input. */
{
int out = 0;
/*out = 1*/
asm volatile (
"movl $1, %0"
: "=m" (out)
);
assert(out == 1);
}
/* Simple example using floats */
{
float in = 1.0;
float out = 0.0;
/*out = -in*/
asm volatile (
"flds %1;"
"fchs;"
"fstps %0;"
: "=m" (out)
: "m" (in)
);
assert(out == -1.0);
}
/* Input and output can be the same memory location. */
{
float x = 1.0;
/*x = -x*/
asm (
"flds %1;"
"fchs;"
"fstps %0;"
: "=m" (x)
: "m" (x)
);
assert(x == -1.0);
}
/*
# Register constraints
https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html
Tell GCC to automatically read memory into registers or write registers into memory
This is more precise and complicated than using `m`:
- r: gcc chooses any free register
- a: %eax
- b: %ebx
- c: %ecx
- d: %edx
- S: %esi
- D: %edi
- 0: matching register
*/
/*
# r register constraint
gcc will automatically put the value of `in` from RAM into a register for us
and `out` from a register into ram at the end
We can do an `inc` operation directly on both `%1` and `%0`,
so they must both already be inside a registers as expected
GCC just makes sure they are written from/to memory before/after the operations.
*/
{
int in = 0;
int out = 0;
/*out = in + 2*/
asm (
"incl %1;"
"movl %1, %0;"
"incl %0;"
: "=r" (out)
: "r" (in)
);
/* in was not modified by inc. It had already been moved to a register. */
assert(in == 0);
/* Out was modified. But only after our custom assembly executed. */
assert(out == 2);
}
/*
# Matching constraint
# 0 constraint
Represented by digits.
Specifies that an input/output has the same constraint as another one.
Often used when we want a single variable to be both input and output
and minimize the use of new registers.
*/
{
int x = 0;
asm (
"incl %0"
: "=r" (x)
: "0" (x) /* x has the same constraint
as constraint 0 (`r`)*/
);
assert(x == 1);
}
/*
# Specific register constraints
# a
Forces it to be put into eax.
*/
{
int x = 0;
asm (
"incl %%eax"
: "=a" (x)
: "0" (x)
);
assert(x == 1);
}
/*
# Register variables
http://stackoverflow.com/questions/2114163/reading-a-register-value-into-a-c-variable
https://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Explicit-Reg-Vars.html
*/
{
register int eax asm ("eax");
asm ("mov $1, %%eax;" : : : "%eax");
assert(eax == 1);
asm ("mov $2, %%eax;" : : : "%eax");
assert(eax == 2);
}
/*
# modifiers
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Modifiers.html#Modifiers
# =
Says that a value may have been modified by asm.
TODO: `=` vs indicating that it is an output? (before the second `:`?
# +
Both read from and written to.
*/
/*
# asm volatile
http://stackoverflow.com/questions/14449141/the-difference-between-asm-asm-volatile-and-clobbering-memory
TODO minimal example.
*/
/*
# volatile variable modified in asm
TODO do you need to mark variables modified in `asm` as volatile?
I think this is exactly what the `=` modifier does already, so maybe not.
*/
#endif
return EXIT_SUCCESS;
}