ho tre funzioni a()
, b()
e c()
che dovrebbero fare la stessa cosa:Qual è la differenza tra i tipi di vettorizzazione e gli array C incorporati in GCC?
typedef float Builtin __attribute__ ((vector_size (16)));
typedef struct {
float values[4];
} Struct;
typedef union {
Builtin b;
Struct s;
} Union;
extern void printv(Builtin);
extern void printv(Union);
extern void printv(Struct);
int a() {
Builtin m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int b() {
Union m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int c() {
Struct m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
Quando compilo questo codice osservo il seguente comportamento:
- Quando si chiama
printv()
ina()
tutto 4 galleggianti vengono passati da%xmm0
. Non si verificano scritture sulla memoria. - Quando si chiama
printv()
inb()
2 float vengono passati da%xmm0
e gli altri due float da%xmm1
. Per fare ciò, 4 float vengono caricati (.LC0) a%xmm2
e da lì alla memoria. Dopodiché, 2 float vengono letti dallo stesso posto in memoria a%xmm0
e gli altri 2 float vengono caricati (.LC1) a%xmm1
. - Sono un po 'perso su ciò che lo fa
c()
.
Perché a()
, b()
e c()
sono diversi?
Ecco l'output assemblaggio di un():
vmovaps .LC0(%rip), %xmm0
call _Z6printvU8__vectorf
Il gruppo di uscita per b():
vmovaps .LC0(%rip), %xmm2
vmovaps %xmm2, (%rsp)
vmovq .LC1(%rip), %xmm1
vmovq (%rsp), %xmm0
call _Z6printv5Union
E l'uscita assemblaggio per c():
andq $-32, %rsp
subq $32, %rsp
vmovaps .LC0(%rip), %xmm0
vmovaps %xmm0, (%rsp)
vmovq .LC2(%rip), %xmm0
vmovq 8(%rsp), %xmm1
call _Z6printv6Struct
I dati:
.section .rodata.cst16,"aM",@progbits,16
.align 16
.LC0:
.long 1065353216
.long 1073741824
.long 1077936128
.long 1082130432
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC1:
.quad 4647714816524288000
.align 8
.LC2:
.quad 4611686019492741120
Il quad 4647714816524288000
sembra non essere altro che i galleggianti 3.0
e 4.0
in parole adiacenti.
Sembra che questo sia un problema con la convenzione di chiamata. '__m128' può essere passato direttamente dal registro. Ma 'Packed' deve essere passato con la divisione dei parametri tra' xmm0' e 'xmm1'. In breve, la convenzione di chiamata probabilmente impedisce al compilatore di fare una tale ottimizzazione. – Mysticial
Ho modificato la domanda un bel po 'da quando hai postato il tuo commento. Ho sostituito tutte le cose relative ad AVX con i tipi di GCC integrati che ho trovato nei file di intestazione per rendere la domanda molto più comprensibile. Ma penso che potresti avere ragione riguardo alle convenzioni di chiamata. Ma perché? –