x86inc.asm 55.6 KB
Newer Older
Henrik Gramner's avatar
Henrik Gramner committed
1
;*****************************************************************************
2
;* x86inc.asm: x86 abstraction layer
Henrik Gramner's avatar
Henrik Gramner committed
3
;*****************************************************************************
Henrik Gramner's avatar
Henrik Gramner committed
4
;* Copyright (C) 2005-2022 x264 project
Henrik Gramner's avatar
Henrik Gramner committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;*
;* Authors: Loren Merritt <lorenm@u.washington.edu>
;*          Henrik Gramner <henrik@gramner.com>
;*          Anton Mitrofanov <BugMaster@narod.ru>
;*          Fiona Glaser <fiona@x264.com>
;*
;* Permission to use, copy, modify, and/or distribute this software for any
;* purpose with or without fee is hereby granted, provided that the above
;* copyright notice and this permission notice appear in all copies.
;*
;* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
;* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
;* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
;* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
;* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
;* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
;*****************************************************************************

24
; This is a header file for the x86inc.asm assembly language, which uses
Henrik Gramner's avatar
Henrik Gramner committed
25
26
27
; NASM/YASM syntax combined with a large number of macros to provide easy
; abstraction between different calling conventions (x86_32, win64, linux64).
; It also has various other useful features to simplify writing the kind of
28
; DSP functions that are most often used.
Henrik Gramner's avatar
Henrik Gramner committed
29
30

%ifndef private_prefix
31
    %error private_prefix not defined
Henrik Gramner's avatar
Henrik Gramner committed
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
%endif

%ifndef public_prefix
    %define public_prefix private_prefix
%endif

%ifndef STACK_ALIGNMENT
    %if ARCH_X86_64
        %define STACK_ALIGNMENT 16
    %else
        %define STACK_ALIGNMENT 4
    %endif
%endif

%define WIN64  0
%define UNIX64 0
%if ARCH_X86_64
    %ifidn __OUTPUT_FORMAT__,win32
        %define WIN64  1
    %elifidn __OUTPUT_FORMAT__,win64
        %define WIN64  1
    %elifidn __OUTPUT_FORMAT__,x64
        %define WIN64  1
    %else
        %define UNIX64 1
    %endif
%endif

%define FORMAT_ELF 0
61
%define FORMAT_MACHO 0
Henrik Gramner's avatar
Henrik Gramner committed
62
63
64
65
66
67
%ifidn __OUTPUT_FORMAT__,elf
    %define FORMAT_ELF 1
%elifidn __OUTPUT_FORMAT__,elf32
    %define FORMAT_ELF 1
%elifidn __OUTPUT_FORMAT__,elf64
    %define FORMAT_ELF 1
68
69
70
71
72
73
%elifidn __OUTPUT_FORMAT__,macho
    %define FORMAT_MACHO 1
%elifidn __OUTPUT_FORMAT__,macho32
    %define FORMAT_MACHO 1
%elifidn __OUTPUT_FORMAT__,macho64
    %define FORMAT_MACHO 1
Henrik Gramner's avatar
Henrik Gramner committed
74
75
76
77
78
79
80
81
%endif

%ifdef PREFIX
    %define mangle(x) _ %+ x
%else
    %define mangle(x) x
%endif

82
83
84
85
86
; Use VEX-encoding even in non-AVX functions
%ifndef FORCE_VEX_ENCODING
    %define FORCE_VEX_ENCODING 0
%endif

Henrik Gramner's avatar
Henrik Gramner committed
87
88
89
90
91
92
93
94
95
96
%macro SECTION_RODATA 0-1 16
    %ifidn __OUTPUT_FORMAT__,win32
        SECTION .rdata align=%1
    %elif WIN64
        SECTION .rdata align=%1
    %else
        SECTION .rodata align=%1
    %endif
%endmacro

97
98
%if ARCH_X86_64
    %define PIC 1 ; always use PIC on x86-64
Henrik Gramner's avatar
Henrik Gramner committed
99
    default rel
100
101
102
103
%elifidn __OUTPUT_FORMAT__,win32
    %define PIC 0 ; PIC isn't used on 32-bit Windows
%elifndef PIC
    %define PIC 0
Henrik Gramner's avatar
Henrik Gramner committed
104
105
%endif

106
%define HAVE_PRIVATE_EXTERN 1
Henrik Gramner's avatar
Henrik Gramner committed
107
108
%ifdef __NASM_VER__
    %use smartalign
109
110
111
    %if __NASM_VERSION_ID__ < 0x020e0000 ; 2.14
        %define HAVE_PRIVATE_EXTERN 0
    %endif
Henrik Gramner's avatar
Henrik Gramner committed
112
113
114
115
116
%endif

; Macros to eliminate most code duplication between x86_32 and x86_64:
; Currently this works only for leaf functions which load all their arguments
; into registers at the start, and make no other use of the stack. Luckily that
117
; covers most use cases.
Henrik Gramner's avatar
Henrik Gramner committed
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

; PROLOGUE:
; %1 = number of arguments. loads them from stack if needed.
; %2 = number of registers used. pushes callee-saved regs if needed.
; %3 = number of xmm registers used. pushes callee-saved xmm regs if needed.
; %4 = (optional) stack size to be allocated. The stack will be aligned before
;      allocating the specified stack size. If the required stack alignment is
;      larger than the known stack alignment the stack will be manually aligned
;      and an extra register will be allocated to hold the original stack
;      pointer (to not invalidate r0m etc.). To prevent the use of an extra
;      register as stack pointer, request a negative stack size.
; %4+/%5+ = list of names to define to registers
; PROLOGUE can also be invoked by adding the same options to cglobal

; e.g.
; cglobal foo, 2,3,7,0x40, dst, src, tmp
; declares a function (foo) that automatically loads two arguments (dst and
; src) into registers, uses one additional register (tmp) plus 7 vector
; registers (m0-m6) and allocates 0x40 bytes of stack space.

; TODO Some functions can use some args directly from the stack. If they're the
; last args then you can just not declare them, but if they're in the middle
; we need more flexible macro.

; RET:
; Pops anything that was pushed by PROLOGUE, and returns.

; REP_RET:
; Use this instead of RET if it's a branch target.

; registers:
; rN and rNq are the native-size register holding function argument N
; rNd, rNw, rNb are dword, word, and byte size
; rNh is the high 8 bits of the word size
; rNm is the original location of arg N (a register or on the stack), dword
; rNmp is native size

%macro DECLARE_REG 2-3
    %define r%1q %2
    %define r%1d %2d
    %define r%1w %2w
    %define r%1b %2b
    %define r%1h %2h
    %define %2q %2
    %if %0 == 2
        %define r%1m  %2d
        %define r%1mp %2
    %elif ARCH_X86_64 ; memory
        %define r%1m [rstk + stack_offset + %3]
        %define r%1mp qword r %+ %1 %+ m
    %else
        %define r%1m [rstk + stack_offset + %3]
        %define r%1mp dword r %+ %1 %+ m
    %endif
    %define r%1  %2
%endmacro

%macro DECLARE_REG_SIZE 3
    %define r%1q r%1
    %define e%1q r%1
    %define r%1d e%1
    %define e%1d e%1
    %define r%1w %1
    %define e%1w %1
    %define r%1h %3
    %define e%1h %3
    %define r%1b %2
    %define e%1b %2
    %if ARCH_X86_64 == 0
        %define r%1 e%1
    %endif
%endmacro

DECLARE_REG_SIZE ax, al, ah
DECLARE_REG_SIZE bx, bl, bh
DECLARE_REG_SIZE cx, cl, ch
DECLARE_REG_SIZE dx, dl, dh
DECLARE_REG_SIZE si, sil, null
DECLARE_REG_SIZE di, dil, null
DECLARE_REG_SIZE bp, bpl, null

; t# defines for when per-arch register allocation is more complex than just function arguments

%macro DECLARE_REG_TMP 1-*
    %assign %%i 0
    %rep %0
        CAT_XDEFINE t, %%i, r%1
        %assign %%i %%i+1
        %rotate 1
    %endrep
%endmacro

%macro DECLARE_REG_TMP_SIZE 0-*
    %rep %0
        %define t%1q t%1 %+ q
        %define t%1d t%1 %+ d
        %define t%1w t%1 %+ w
        %define t%1h t%1 %+ h
        %define t%1b t%1 %+ b
        %rotate 1
    %endrep
%endmacro

DECLARE_REG_TMP_SIZE 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14

%if ARCH_X86_64
    %define gprsize 8
%else
    %define gprsize 4
%endif

229
230
231
232
233
234
235
236
237
238
239
240
%macro LEA 2
%if ARCH_X86_64
    lea %1, [%2]
%elif PIC
    call $+5 ; special-cased to not affect the RSB on most CPU:s
    pop %1
    add %1, (%2)-$+1
%else
    mov %1, %2
%endif
%endmacro

Henrik Gramner's avatar
Henrik Gramner committed
241
242
243
244
245
246
247
248
249
250
; Repeats an instruction/operation for multiple arguments.
; Example usage: "REPX {psrlw x, 8}, m0, m1, m2, m3"
%macro REPX 2-* ; operation, args
    %xdefine %%f(x) %1
    %rep %0 - 1
        %rotate 1
        %%f(%1)
    %endrep
%endmacro

Henrik Gramner's avatar
Henrik Gramner committed
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
%macro PUSH 1
    push %1
    %ifidn rstk, rsp
        %assign stack_offset stack_offset+gprsize
    %endif
%endmacro

%macro POP 1
    pop %1
    %ifidn rstk, rsp
        %assign stack_offset stack_offset-gprsize
    %endif
%endmacro

%macro PUSH_IF_USED 1-*
    %rep %0
        %if %1 < regs_used
            PUSH r%1
        %endif
        %rotate 1
    %endrep
%endmacro

%macro POP_IF_USED 1-*
    %rep %0
        %if %1 < regs_used
            pop r%1
        %endif
        %rotate 1
    %endrep
%endmacro

%macro LOAD_IF_USED 1-*
    %rep %0
        %if %1 < num_args
            mov r%1, r %+ %1 %+ mp
        %endif
        %rotate 1
    %endrep
%endmacro

%macro SUB 2
    sub %1, %2
    %ifidn %1, rstk
        %assign stack_offset stack_offset+(%2)
    %endif
%endmacro

%macro ADD 2
    add %1, %2
    %ifidn %1, rstk
        %assign stack_offset stack_offset-(%2)
    %endif
%endmacro

%macro movifnidn 2
    %ifnidn %1, %2
        mov %1, %2
    %endif
%endmacro

Ronald S. Bultje's avatar
Ronald S. Bultje committed
312
313
314
315
%if ARCH_X86_64 == 0
    %define movsxd movifnidn
%endif

Henrik Gramner's avatar
Henrik Gramner committed
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
%macro movsxdifnidn 2
    %ifnidn %1, %2
        movsxd %1, %2
    %endif
%endmacro

%macro ASSERT 1
    %if (%1) == 0
        %error assertion ``%1'' failed
    %endif
%endmacro

%macro DEFINE_ARGS 0-*
    %ifdef n_arg_names
        %assign %%i 0
        %rep n_arg_names
            CAT_UNDEF arg_name %+ %%i, q
            CAT_UNDEF arg_name %+ %%i, d
            CAT_UNDEF arg_name %+ %%i, w
            CAT_UNDEF arg_name %+ %%i, h
            CAT_UNDEF arg_name %+ %%i, b
            CAT_UNDEF arg_name %+ %%i, m
            CAT_UNDEF arg_name %+ %%i, mp
            CAT_UNDEF arg_name, %%i
            %assign %%i %%i+1
        %endrep
    %endif

    %xdefine %%stack_offset stack_offset
    %undef stack_offset ; so that the current value of stack_offset doesn't get baked in by xdefine
    %assign %%i 0
    %rep %0
        %xdefine %1q r %+ %%i %+ q
        %xdefine %1d r %+ %%i %+ d
        %xdefine %1w r %+ %%i %+ w
        %xdefine %1h r %+ %%i %+ h
        %xdefine %1b r %+ %%i %+ b
        %xdefine %1m r %+ %%i %+ m
        %xdefine %1mp r %+ %%i %+ mp
        CAT_XDEFINE arg_name, %%i, %1
        %assign %%i %%i+1
        %rotate 1
    %endrep
    %xdefine stack_offset %%stack_offset
    %assign n_arg_names %0
%endmacro

%define required_stack_alignment ((mmsize + 15) & ~15)
%define vzeroupper_required (mmsize > 16 && (ARCH_X86_64 == 0 || xmm_regs_used > 16 || notcpuflag(avx512)))
%define high_mm_regs (16*cpuflag(avx512))

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
; Large stack allocations on Windows need to use stack probing in order
; to guarantee that all stack memory is committed before accessing it.
; This is done by ensuring that the guard page(s) at the end of the
; currently committed pages are touched prior to any pages beyond that.
%if WIN64
    %assign STACK_PROBE_SIZE 8192
%elifidn __OUTPUT_FORMAT__, win32
    %assign STACK_PROBE_SIZE 4096
%else
    %assign STACK_PROBE_SIZE 0
%endif

%macro PROBE_STACK 1 ; stack_size
    %if STACK_PROBE_SIZE
        %assign %%i STACK_PROBE_SIZE
        %rep %1 / STACK_PROBE_SIZE
            mov eax, [rsp-%%i]
            %assign %%i %%i+STACK_PROBE_SIZE
        %endrep
    %endif
%endmacro

389
%macro ALLOC_STACK 0-2 0, 0 ; stack_size, n_xmm_regs (for win64 only)
Henrik Gramner's avatar
Henrik Gramner committed
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    %ifnum %1
        %if %1 != 0
            %assign %%pad 0
            %assign stack_size %1
            %if stack_size < 0
                %assign stack_size -stack_size
            %endif
            %if WIN64
                %assign %%pad %%pad + 32 ; shadow space
                %if mmsize != 8
                    %assign xmm_regs_used %2
                    %if xmm_regs_used > 8
                        %assign %%pad %%pad + (xmm_regs_used-8)*16 ; callee-saved xmm registers
                    %endif
                %endif
            %endif
            %if required_stack_alignment <= STACK_ALIGNMENT
                ; maintain the current stack alignment
                %assign stack_size_padded stack_size + %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1))
409
                PROBE_STACK stack_size_padded
Henrik Gramner's avatar
Henrik Gramner committed
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
                SUB rsp, stack_size_padded
            %else
                %assign %%reg_num (regs_used - 1)
                %xdefine rstk r %+ %%reg_num
                ; align stack, and save original stack location directly above
                ; it, i.e. in [rsp+stack_size_padded], so we can restore the
                ; stack in a single instruction (i.e. mov rsp, rstk or mov
                ; rsp, [rsp+stack_size_padded])
                %if %1 < 0 ; need to store rsp on stack
                    %xdefine rstkm [rsp + stack_size + %%pad]
                    %assign %%pad %%pad + gprsize
                %else ; can keep rsp in rstk during whole function
                    %xdefine rstkm rstk
                %endif
                %assign stack_size_padded stack_size + ((%%pad + required_stack_alignment-1) & ~(required_stack_alignment-1))
425
                PROBE_STACK stack_size_padded
Henrik Gramner's avatar
Henrik Gramner committed
426
427
428
429
430
431
432
433
434
435
                mov rstk, rsp
                and rsp, ~(required_stack_alignment-1)
                sub rsp, stack_size_padded
                movifnidn rstkm, rstk
            %endif
            WIN64_PUSH_XMM
        %endif
    %endif
%endmacro

436
%macro SETUP_STACK_POINTER 0-1 0
Henrik Gramner's avatar
Henrik Gramner committed
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    %ifnum %1
        %if %1 != 0 && required_stack_alignment > STACK_ALIGNMENT
            %if %1 > 0
                ; Reserve an additional register for storing the original stack pointer, but avoid using
                ; eax/rax for this purpose since it can potentially get overwritten as a return value.
                %assign regs_used (regs_used + 1)
                %if ARCH_X86_64 && regs_used == 7
                    %assign regs_used 8
                %elif ARCH_X86_64 == 0 && regs_used == 1
                    %assign regs_used 2
                %endif
            %endif
            %if ARCH_X86_64 && regs_used < 5 + UNIX64 * 3
                ; Ensure that we don't clobber any registers containing arguments. For UNIX64 we also preserve r6 (rax)
                ; since it's used as a hidden argument in vararg functions to specify the number of vector registers used.
                %assign regs_used 5 + UNIX64 * 3
            %endif
        %endif
    %endif
%endmacro

%if WIN64 ; Windows x64 ;=================================================

DECLARE_REG 0,  rcx
DECLARE_REG 1,  rdx
DECLARE_REG 2,  R8
DECLARE_REG 3,  R9
DECLARE_REG 4,  R10, 40
DECLARE_REG 5,  R11, 48
DECLARE_REG 6,  rax, 56
DECLARE_REG 7,  rdi, 64
DECLARE_REG 8,  rsi, 72
DECLARE_REG 9,  rbx, 80
DECLARE_REG 10, rbp, 88
DECLARE_REG 11, R14, 96
DECLARE_REG 12, R15, 104
DECLARE_REG 13, R12, 112
DECLARE_REG 14, R13, 120

476
%macro PROLOGUE 2-5+ 0, 0 ; #args, #regs, #xmm_regs, [stack_size,] arg_names...
Henrik Gramner's avatar
Henrik Gramner committed
477
478
479
480
481
482
483
484
485
486
487
    %assign num_args %1
    %assign regs_used %2
    ASSERT regs_used >= num_args
    SETUP_STACK_POINTER %4
    ASSERT regs_used <= 15
    PUSH_IF_USED 7, 8, 9, 10, 11, 12, 13, 14
    ALLOC_STACK %4, %3
    %if mmsize != 8 && stack_size == 0
        WIN64_SPILL_XMM %3
    %endif
    LOAD_IF_USED 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
488
489
490
491
492
493
494
495
496
    %if %0 > 4
        %ifnum %4
            DEFINE_ARGS %5
        %else
            DEFINE_ARGS %4, %5
        %endif
    %elifnnum %4
        DEFINE_ARGS %4
    %endif
Henrik Gramner's avatar
Henrik Gramner committed
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
%endmacro

%macro WIN64_PUSH_XMM 0
    ; Use the shadow space to store XMM6 and XMM7, the rest needs stack space allocated.
    %if xmm_regs_used > 6 + high_mm_regs
        movaps [rstk + stack_offset +  8], xmm6
    %endif
    %if xmm_regs_used > 7 + high_mm_regs
        movaps [rstk + stack_offset + 24], xmm7
    %endif
    %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8
    %if %%xmm_regs_on_stack > 0
        %assign %%i 8
        %rep %%xmm_regs_on_stack
            movaps [rsp + (%%i-8)*16 + stack_size + 32], xmm %+ %%i
            %assign %%i %%i+1
        %endrep
    %endif
%endmacro

%macro WIN64_SPILL_XMM 1
    %assign xmm_regs_used %1
    ASSERT xmm_regs_used <= 16 + high_mm_regs
    %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8
    %if %%xmm_regs_on_stack > 0
        ; Allocate stack space for callee-saved xmm registers plus shadow space and align the stack.
        %assign %%pad %%xmm_regs_on_stack*16 + 32
        %assign stack_size_padded %%pad + ((-%%pad-stack_offset-gprsize) & (STACK_ALIGNMENT-1))
        SUB rsp, stack_size_padded
    %endif
    WIN64_PUSH_XMM
%endmacro

%macro WIN64_RESTORE_XMM_INTERNAL 0
    %assign %%pad_size 0
    %assign %%xmm_regs_on_stack xmm_regs_used - high_mm_regs - 8
    %if %%xmm_regs_on_stack > 0
        %assign %%i xmm_regs_used - high_mm_regs
        %rep %%xmm_regs_on_stack
            %assign %%i %%i-1
            movaps xmm %+ %%i, [rsp + (%%i-8)*16 + stack_size + 32]
        %endrep
    %endif
    %if stack_size_padded > 0
        %if stack_size > 0 && required_stack_alignment > STACK_ALIGNMENT
            mov rsp, rstkm
        %else
            add rsp, stack_size_padded
            %assign %%pad_size stack_size_padded
        %endif
    %endif
    %if xmm_regs_used > 7 + high_mm_regs
        movaps xmm7, [rsp + stack_offset - %%pad_size + 24]
    %endif
    %if xmm_regs_used > 6 + high_mm_regs
        movaps xmm6, [rsp + stack_offset - %%pad_size +  8]
    %endif
%endmacro

%macro WIN64_RESTORE_XMM 0
    WIN64_RESTORE_XMM_INTERNAL
    %assign stack_offset (stack_offset-stack_size_padded)
    %assign stack_size_padded 0
    %assign xmm_regs_used 0
%endmacro

%define has_epilogue regs_used > 7 || stack_size > 0 || vzeroupper_required || xmm_regs_used > 6+high_mm_regs

%macro RET 0
    WIN64_RESTORE_XMM_INTERNAL
    POP_IF_USED 14, 13, 12, 11, 10, 9, 8, 7
    %if vzeroupper_required
        vzeroupper
    %endif
    AUTO_REP_RET
%endmacro

%elif ARCH_X86_64 ; *nix x64 ;=============================================

DECLARE_REG 0,  rdi
DECLARE_REG 1,  rsi
DECLARE_REG 2,  rdx
DECLARE_REG 3,  rcx
DECLARE_REG 4,  R8
DECLARE_REG 5,  R9
DECLARE_REG 6,  rax, 8
DECLARE_REG 7,  R10, 16
DECLARE_REG 8,  R11, 24
DECLARE_REG 9,  rbx, 32
DECLARE_REG 10, rbp, 40
DECLARE_REG 11, R14, 48
DECLARE_REG 12, R15, 56
DECLARE_REG 13, R12, 64
DECLARE_REG 14, R13, 72

592
%macro PROLOGUE 2-5+ 0, 0 ; #args, #regs, #xmm_regs, [stack_size,] arg_names...
Henrik Gramner's avatar
Henrik Gramner committed
593
594
595
596
597
598
599
600
601
    %assign num_args %1
    %assign regs_used %2
    %assign xmm_regs_used %3
    ASSERT regs_used >= num_args
    SETUP_STACK_POINTER %4
    ASSERT regs_used <= 15
    PUSH_IF_USED 9, 10, 11, 12, 13, 14
    ALLOC_STACK %4
    LOAD_IF_USED 6, 7, 8, 9, 10, 11, 12, 13, 14
602
603
604
605
606
607
608
609
610
    %if %0 > 4
        %ifnum %4
            DEFINE_ARGS %5
        %else
            DEFINE_ARGS %4, %5
        %endif
    %elifnnum %4
        DEFINE_ARGS %4
    %endif
Henrik Gramner's avatar
Henrik Gramner committed
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
%endmacro

%define has_epilogue regs_used > 9 || stack_size > 0 || vzeroupper_required

%macro RET 0
    %if stack_size_padded > 0
        %if required_stack_alignment > STACK_ALIGNMENT
            mov rsp, rstkm
        %else
            add rsp, stack_size_padded
        %endif
    %endif
    POP_IF_USED 14, 13, 12, 11, 10, 9
    %if vzeroupper_required
        vzeroupper
    %endif
    AUTO_REP_RET
%endmacro

%else ; X86_32 ;==============================================================

DECLARE_REG 0, eax, 4
DECLARE_REG 1, ecx, 8
DECLARE_REG 2, edx, 12
DECLARE_REG 3, ebx, 16
DECLARE_REG 4, esi, 20
DECLARE_REG 5, edi, 24
DECLARE_REG 6, ebp, 28
%define rsp esp

%macro DECLARE_ARG 1-*
    %rep %0
        %define r%1m [rstk + stack_offset + 4*%1 + 4]
        %define r%1mp dword r%1m
        %rotate 1
    %endrep
%endmacro

DECLARE_ARG 7, 8, 9, 10, 11, 12, 13, 14

651
%macro PROLOGUE 2-5+ 0, 0 ; #args, #regs, #xmm_regs, [stack_size,] arg_names...
Henrik Gramner's avatar
Henrik Gramner committed
652
653
654
655
656
657
658
659
660
661
662
663
664
665
    %assign num_args %1
    %assign regs_used %2
    ASSERT regs_used >= num_args
    %if num_args > 7
        %assign num_args 7
    %endif
    %if regs_used > 7
        %assign regs_used 7
    %endif
    SETUP_STACK_POINTER %4
    ASSERT regs_used <= 7
    PUSH_IF_USED 3, 4, 5, 6
    ALLOC_STACK %4
    LOAD_IF_USED 0, 1, 2, 3, 4, 5, 6
666
667
668
669
670
671
672
673
674
    %if %0 > 4
        %ifnum %4
            DEFINE_ARGS %5
        %else
            DEFINE_ARGS %4, %5
        %endif
    %elifnnum %4
        DEFINE_ARGS %4
    %endif
Henrik Gramner's avatar
Henrik Gramner committed
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
%endmacro

%define has_epilogue regs_used > 3 || stack_size > 0 || vzeroupper_required

%macro RET 0
    %if stack_size_padded > 0
        %if required_stack_alignment > STACK_ALIGNMENT
            mov rsp, rstkm
        %else
            add rsp, stack_size_padded
        %endif
    %endif
    POP_IF_USED 6, 5, 4, 3
    %if vzeroupper_required
        vzeroupper
    %endif
    AUTO_REP_RET
%endmacro

%endif ;======================================================================

%if WIN64 == 0
    %macro WIN64_SPILL_XMM 1
698
        %assign xmm_regs_used %1
Henrik Gramner's avatar
Henrik Gramner committed
699
700
    %endmacro
    %macro WIN64_RESTORE_XMM 0
701
        %assign xmm_regs_used 0
Henrik Gramner's avatar
Henrik Gramner committed
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
    %endmacro
    %macro WIN64_PUSH_XMM 0
    %endmacro
%endif

; On AMD cpus <=K10, an ordinary ret is slow if it immediately follows either
; a branch or a branch target. So switch to a 2-byte form of ret in that case.
; We can automatically detect "follows a branch", but not a branch target.
; (SSSE3 is a sufficient condition to know that your cpu doesn't have this problem.)
%macro REP_RET 0
    %if has_epilogue || cpuflag(ssse3)
        RET
    %else
        rep ret
    %endif
    annotate_function_size
%endmacro

%define last_branch_adr $$
%macro AUTO_REP_RET 0
    %if notcpuflag(ssse3)
        times ((last_branch_adr-$)>>31)+1 rep ; times 1 iff $ == last_branch_adr.
    %endif
    ret
    annotate_function_size
%endmacro

%macro BRANCH_INSTR 0-*
    %rep %0
        %macro %1 1-2 %1
            %2 %1
            %if notcpuflag(ssse3)
                %%branch_instr equ $
                %xdefine last_branch_adr %%branch_instr
            %endif
        %endmacro
        %rotate 1
    %endrep
%endmacro

BRANCH_INSTR jz, je, jnz, jne, jl, jle, jnl, jnle, jg, jge, jng, jnge, ja, jae, jna, jnae, jb, jbe, jnb, jnbe, jc, jnc, js, jns, jo, jno, jp, jnp

744
%macro TAIL_CALL 1-2 1 ; callee, is_nonadjacent
Henrik Gramner's avatar
Henrik Gramner committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
    %if has_epilogue
        call %1
        RET
    %elif %2
        jmp %1
    %endif
    annotate_function_size
%endmacro

;=============================================================================
; arch-independent part
;=============================================================================

%assign function_align 16

; Begin a function.
; Applies any symbol mangling needed for C linkage, and sets up a define such that
; subsequent uses of the function name automatically refer to the mangled version.
; Appends cpuflags to the function name if cpuflags has been specified.
; The "" empty default parameter is a workaround for nasm, which fails if SUFFIX
; is empty and we call cglobal_internal with just %1 %+ SUFFIX (without %2).
%macro cglobal 1-2+ "" ; name, [PROLOGUE args]
    cglobal_internal 1, %1 %+ SUFFIX, %2
%endmacro
%macro cvisible 1-2+ "" ; name, [PROLOGUE args]
    cglobal_internal 0, %1 %+ SUFFIX, %2
%endmacro
%macro cglobal_internal 2-3+
    annotate_function_size
    %ifndef cglobaled_%2
775
776
777
778
779
        %if %1
            %xdefine %2 mangle(private_prefix %+ _ %+ %2)
        %else
            %xdefine %2 mangle(public_prefix %+ _ %+ %2)
        %endif
Henrik Gramner's avatar
Henrik Gramner committed
780
781
782
783
784
785
        %xdefine %2.skip_prologue %2 %+ .skip_prologue
        CAT_XDEFINE cglobaled_, %2, 1
    %endif
    %xdefine current_function %2
    %xdefine current_function_section __SECT__
    %if FORMAT_ELF
786
787
788
789
790
791
792
        %if %1
            global %2:function hidden
        %else
            global %2:function
        %endif
    %elif FORMAT_MACHO && HAVE_PRIVATE_EXTERN && %1
        global %2:private_extern
Henrik Gramner's avatar
Henrik Gramner committed
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
    %else
        global %2
    %endif
    align function_align
    %2:
    RESET_MM_PERMUTATION        ; needed for x86-64, also makes disassembly somewhat nicer
    %xdefine rstk rsp           ; copy of the original stack pointer, used when greater alignment than the known stack alignment is required
    %assign stack_offset 0      ; stack pointer offset relative to the return address
    %assign stack_size 0        ; amount of stack space that can be freely used inside a function
    %assign stack_size_padded 0 ; total amount of allocated stack space, including space for callee-saved xmm registers on WIN64 and alignment padding
    %assign xmm_regs_used 0     ; number of XMM registers requested, used for dealing with callee-saved registers on WIN64 and vzeroupper
    %ifnidn %3, ""
        PROLOGUE %3
    %endif
%endmacro

; Create a global symbol from a local label with the correct name mangling and type
%macro cglobal_label 1
    %if FORMAT_ELF
        global current_function %+ %1:function hidden
813
814
    %elif FORMAT_MACHO && HAVE_PRIVATE_EXTERN
        global current_function %+ %1:private_extern
Henrik Gramner's avatar
Henrik Gramner committed
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
    %else
        global current_function %+ %1
    %endif
    %1:
%endmacro

%macro cextern 1
    %xdefine %1 mangle(private_prefix %+ _ %+ %1)
    CAT_XDEFINE cglobaled_, %1, 1
    extern %1
%endmacro

; like cextern, but without the prefix
%macro cextern_naked 1
    %ifdef PREFIX
        %xdefine %1 mangle(%1)
    %endif
    CAT_XDEFINE cglobaled_, %1, 1
    extern %1
%endmacro

%macro const 1-2+
    %xdefine %1 mangle(private_prefix %+ _ %+ %1)
    %if FORMAT_ELF
        global %1:data hidden
840
841
    %elif FORMAT_MACHO && HAVE_PRIVATE_EXTERN
        global %1:private_extern
Henrik Gramner's avatar
Henrik Gramner committed
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
    %else
        global %1
    %endif
    %1: %2
%endmacro

; This is needed for ELF, otherwise the GNU linker assumes the stack is executable by default.
%if FORMAT_ELF
    [SECTION .note.GNU-stack noalloc noexec nowrite progbits]
%endif

; Tell debuggers how large the function was.
; This may be invoked multiple times per function; we rely on later instances overriding earlier ones.
; This is invoked by RET and similar macros, and also cglobal does it for the previous function,
; but if the last function in a source file doesn't use any of the standard macros for its epilogue,
; then its size might be unspecified.
%macro annotate_function_size 0
    %ifdef __YASM_VER__
        %ifdef current_function
            %if FORMAT_ELF
                current_function_section
                %%ecf equ $
                size current_function %%ecf - current_function
                __SECT__
            %endif
        %endif
    %endif
%endmacro

; cpuflags

873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
%assign cpuflags_mmx       (1<<0)
%assign cpuflags_mmx2      (1<<1)  | cpuflags_mmx
%assign cpuflags_3dnow     (1<<2)  | cpuflags_mmx
%assign cpuflags_3dnowext  (1<<3)  | cpuflags_3dnow
%assign cpuflags_sse       (1<<4)  | cpuflags_mmx2
%assign cpuflags_sse2      (1<<5)  | cpuflags_sse
%assign cpuflags_sse2slow  (1<<6)  | cpuflags_sse2
%assign cpuflags_lzcnt     (1<<7)  | cpuflags_sse2
%assign cpuflags_sse3      (1<<8)  | cpuflags_sse2
%assign cpuflags_ssse3     (1<<9)  | cpuflags_sse3
%assign cpuflags_sse4      (1<<10) | cpuflags_ssse3
%assign cpuflags_sse42     (1<<11) | cpuflags_sse4
%assign cpuflags_aesni     (1<<12) | cpuflags_sse42
%assign cpuflags_gfni      (1<<13) | cpuflags_sse42
%assign cpuflags_avx       (1<<14) | cpuflags_sse42
%assign cpuflags_xop       (1<<15) | cpuflags_avx
%assign cpuflags_fma4      (1<<16) | cpuflags_avx
%assign cpuflags_fma3      (1<<17) | cpuflags_avx
%assign cpuflags_bmi1      (1<<18) | cpuflags_avx|cpuflags_lzcnt
%assign cpuflags_bmi2      (1<<19) | cpuflags_bmi1
%assign cpuflags_avx2      (1<<20) | cpuflags_fma3|cpuflags_bmi2
%assign cpuflags_avx512    (1<<21) | cpuflags_avx2 ; F, CD, BW, DQ, VL
%assign cpuflags_avx512icl (1<<22) | cpuflags_avx512|cpuflags_gfni ; VNNI, IFMA, VBMI, VBMI2, VPOPCNTDQ, BITALG, VAES, VPCLMULQDQ

%assign cpuflags_cache32   (1<<23)
%assign cpuflags_cache64   (1<<24)
%assign cpuflags_aligned   (1<<25) ; not a cpu feature, but a function variant
%assign cpuflags_atom      (1<<26)
Henrik Gramner's avatar
Henrik Gramner committed
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

; Returns a boolean value expressing whether or not the specified cpuflag is enabled.
%define    cpuflag(x) (((((cpuflags & (cpuflags_ %+ x)) ^ (cpuflags_ %+ x)) - 1) >> 31) & 1)
%define notcpuflag(x) (cpuflag(x) ^ 1)

; Takes an arbitrary number of cpuflags from the above list.
; All subsequent functions (up to the next INIT_CPUFLAGS) is built for the specified cpu.
; You shouldn't need to invoke this macro directly, it's a subroutine for INIT_MMX &co.
%macro INIT_CPUFLAGS 0-*
    %xdefine SUFFIX
    %undef cpuname
    %assign cpuflags 0

    %if %0 >= 1
        %rep %0
            %ifdef cpuname
                %xdefine cpuname cpuname %+ _%1
            %else
                %xdefine cpuname %1
            %endif
            %assign cpuflags cpuflags | cpuflags_%1
            %rotate 1
        %endrep
        %xdefine SUFFIX _ %+ cpuname

        %if cpuflag(avx)
            %assign avx_enabled 1
        %endif
        %if (mmsize == 16 && notcpuflag(sse2)) || (mmsize == 32 && notcpuflag(avx2))
            %define mova movaps
            %define movu movups
            %define movnta movntps
        %endif
        %if cpuflag(aligned)
            %define movu mova
        %elif cpuflag(sse3) && notcpuflag(ssse3)
            %define movu lddqu
        %endif
    %endif

    %if ARCH_X86_64 || cpuflag(sse2)
        %ifdef __NASM_VER__
            ALIGNMODE p6
        %else
            CPU amdnop
        %endif
    %else
        %ifdef __NASM_VER__
            ALIGNMODE nop
        %else
            CPU basicnop
        %endif
    %endif
%endmacro

; Merge mmx, sse*, and avx*
; m# is a simd register of the currently selected size
; xm# is the corresponding xmm register if mmsize >= 16, otherwise the same as m#
; ym# is the corresponding ymm register if mmsize >= 32, otherwise the same as m#
; zm# is the corresponding zmm register if mmsize >= 64, otherwise the same as m#
; (All 4 remain in sync through SWAP.)

%macro CAT_XDEFINE 3
    %xdefine %1%2 %3
%endmacro

%macro CAT_UNDEF 2
    %undef %1%2
%endmacro

%macro DEFINE_MMREGS 1 ; mmtype
    %assign %%prev_mmregs 0
    %ifdef num_mmregs
        %assign %%prev_mmregs num_mmregs
    %endif

    %assign num_mmregs 8
    %if ARCH_X86_64 && mmsize >= 16
        %assign num_mmregs 16
        %if cpuflag(avx512) || mmsize == 64
            %assign num_mmregs 32
        %endif
    %endif

    %assign %%i 0
    %rep num_mmregs
        CAT_XDEFINE m, %%i, %1 %+ %%i
        CAT_XDEFINE nn%1, %%i, %%i
        %assign %%i %%i+1
    %endrep
    %if %%prev_mmregs > num_mmregs
        %rep %%prev_mmregs - num_mmregs
            CAT_UNDEF m, %%i
            CAT_UNDEF nn %+ mmtype, %%i
            %assign %%i %%i+1
        %endrep
    %endif
    %xdefine mmtype %1
%endmacro