POD assignment with no effect?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct RGB{
    byte_t r, g, b, a;
};

//#define DEFINE_ME

auto pixels = (RGB *)void_pixels;
for (int y = 0; y < h; y++){
    auto row = pixels + y * w;
    RGB px;
    //initialize px
    for (int x = 0; x < w; x++){
#ifndef DEFINE_ME
        row[x] = px;
#else
        row[x].r = px.r;
        row[x].g = px.g;
        row[x].b = px.b;
        row[x].a = px.a;
#endif
    }
}
If DEFINE_ME is defined and optimizations are enabled, the loop has no effect. What's going on here?
px is uninitialized. I'm not intimately familiar with all of what optimizers do, but assigning the values of an uninitialized struct to values in row[x] may be optimized out by the compiler.

Try assigning values to px and run it again.
px is not uninitialized. I've simply omitted the initialization because it's uninteresting.
Post the declaration of void_pixels, and how it has been initialised / assigned a value.
1
2
3
4
5
6
7
8
void *void_pixels;
int pitch;

if (SDL_LockTexture(this->background.get(), nullptr, &void_pixels, &pitch) < 0)
    throw std::runtime_error("Console::initialize_background(): Failed to initialize texture.");

auto pixels = (RGB *)void_pixels;
//... 
background is just an std::unique_ptr<SDL_Texture>. Trust me that it's properly initialized, or at this rate I'll end up posting the entire program.
Is pitch equal to w*sizeof(RGB)?
> Trust me that it's properly initialized

The question was to see if strict-aliasing rules were being violated. However, since I know nothing about SDL or SDL pixels, I just don't know what the situation here is re. strict aliasing.
Peter87: Yes, the texture is a 32-bit texture, so no padding is inserted. If the pitch was not divisible by 4, undefining DEFINE_ME would break the code in a different way.

JLBorges: Well, casts between generic pointers are not supposed to break strict aliasing, IIRC. Either way, void_pixels is never used again after the cast, and SDL_LockTexture() is an external function, so the compiler should not be able to make any assumptions about its behavior.
(First of all, my OP had an error. Where it says "#ifndef DEFINE_ME" it should say "#ifdef DEFINE_ME".)

Okay, so this is super weird.

I've managed to get a minimal snippet that reproduces the issue:
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
#include <fstream>
#include <cstdint>
#include <memory>

typedef std::uint8_t byte_t;

struct RGB{
	byte_t r, g, b, a;
};

#define DEFINE_ME

int main(){
	const int w = 20 * 8 * 4;
	const int h = 18 * 8 * 4;
	const size_t size = w * h * sizeof(RGB);
	std::unique_ptr</*RGB*/RGB[]> pixels(new RGB[w * h]);
	for (int y = 0; y < h; y++){
		auto row = pixels.get() + y * w;
		RGB px;
		px.r = px.g = px.b = 0x55 + 0x55 * y / (h - 1);
		px.a = 0x80;
		for (int x = 0; x < w; x++){
#ifdef DEFINE_ME
			row[x] = px;
#else
			row[x].r = px.r;
			row[x].g = px.g;
			row[x].b = px.b;
			row[x].a = px.a;
#endif
		}
	}
	std::ofstream file("output.bin", std::ios::binary);
	file.write((const char *)pixels.get(), size);

	return 0;
}
Like I said, DEFINE_ME causes garbage to be written to the file. However, every fourth byte in the file (file_position % 4 == 3) is set to 0x80!
Some other things I've tried:
* The issue goes away completely if all members of px are initialized to constant values.
* Adding a constructor to RGB doesn't make a difference, neither does initializing its members in the loop to constant values before setting them to variable values. This would be expected.

Looking at the disassembly, it seems all the compiler has generated for the entire nested loop was the RGB::a assignment to 0x80 and a rep stos of size / 4 DWORDs. So it seems to believe for some reason that the assignments from variable values should have no effect on the output.

As far as I can tell, this has to be a compiler bug.
Last edited on
The type of that unique_ptr is wrong. The template argument should be an array of unknown bound, i.e.,
std::unique_ptr<RGB[]> pixels(new RGB[w * h]);
Last edited on
Oh, yeah, I missed that when rewriting the example to get rid of void_pixels. Still, it has no effect on the issue.
Huh.

Well in any case I can't reproduce the problem.
My output file is full of repeating 0x55555580, as expected.

What's your compile command look like? What's the output of sdl[2]-config --cflags --libs? Which compiler(s)? Which version of SDL?
Last edited on
I'm using MSVC 14.0 (2015). Just a default console program on release mode with the default options. No non-standard libraries required for reproduction.
Although I'm less interested in seeing it reproduced than in knowing if anyone can spot any possible source of undefined behavior.

PS: Just tried it on 14.1 (2017) and I can't reproduce it.
Last edited on
It can be reproduced with MSVC 19.0
Original code - no optimisation: http://rextester.com/SNLSN10040
Original code -Ox http://rextester.com/TOTDCM96405

However, not with this modified version: http://rextester.com/WXRO73360
Topic archived. No new replies allowed.