Hay un montón de preguntas sobre el acceso a la memoria no asignada, que es claramente un comportamiento indefinido. Pero ¿qué pasa con el siguiente caso de esquina?

Considere la siguiente estructura, que está alineada a 16 bytes, pero ocupa solo 8 bytes de eso:

struct alignas(16) A
{
    float data[2]; // the remaining 8 bytes are unallocated
};

Ahora accedemos a 16 bytes de datos mediante intrínsecos de carga / almacenamiento alineados con SSE:

__m128 test_load(const A &a)
{
    return _mm_load_ps(a.data);
}

void test_store(A &a, __m128 v)
{
    _mm_store_ps(a.data, v);
}

¿Este también es un comportamiento indefinido y debería usar relleno en su lugar?

De todos modos, dado que los elementos intrínsecos de Intel no son C ++ estándar, ¿acceder a un bloque de memoria parcialmente asignado pero alineado (no mayor que el tamaño de la alineación) es un comportamiento indefinido en C ++ estándar?

Abordo tanto el caso intrínseco como el caso estándar de C ++. Estoy interesado en los dos.

4
plasmacel 15 dic. 2016 a las 00:59

2 respuestas

La mejor respuesta

Consulte también ¿Es seguro leer más allá del final de un búfer dentro de la misma página en x86 y x64? La parte de lectura de esta pregunta es básicamente un duplicado de eso.

Es UB de acuerdo con el estándar ISO C ++, pero creo que el acceso de solo lectura como este funciona de manera segura (es decir, se compila en el conjunto que cabría esperar) en implementaciones que proporcionan los elementos intrínsecos de Intel (que son libres de definir cualquier comportamiento adicional que deseen ). Definitivamente es seguro en ASM, pero el riesgo es que la optimización de los compiladores de C ++ que convierten UB en un código mal compilado podría causar un problema si pueden demostrar que no hay nada que leer allí. Hay algo de discusión sobre eso en la pregunta vinculada.


Escribir fuera de los objetos siempre es malo . No lo hagas, ni siquiera si vuelves a colocar la misma basura que leíste antes: un par de carga / almacenamiento no atómico puede ser un problema dependiendo de qué datos siguen a tu estructura.

La única vez que esto está bien es en una matriz en la que sabe lo que viene a continuación y que hay relleno sin usar. p.ej. escribir una matriz de estructuras 3 - float usando tiendas 16B superpuestas por 4B. (Sin alignas para sobrealineación, por lo que una matriz los empaqueta sin relleno).


Una estructura de 3 float s sería un ejemplo mucho mejor que 2 floats.

Para este ejemplo específico (de 2 flotantes), puede usar MOVSD para hacer una carga de extensión cero de 64 bits y MOVSD o MOVLPS para hacer un almacenamiento de 64 bits de la mitad inferior de un __m128.

3
Community 23 may. 2017 a las 12:00

La respuesta de un abogado de idiomas a esto es 'la pregunta es discutible'. _mm_load_ps no está definido en el estándar y está utilizando la instrucción ASM que tampoco está definida en el estándar. C ++ no se ocupa de esto.

En cuanto a su segunda pregunta, acceder a una memoria no asignada desde C ++ de esta manera es un comportamiento claramente indefinido. No se colocó ningún objeto en esta memoria, por lo que no puede acceder a él.

0
SergeyA 14 dic. 2016 a las 22:29