1064 | | annoyingly having to incorrectly cast away the constness of kwlist. |
1065 | | Now the code is arguably correct and it wasn't before. |
| 1068 | Annoying, yet more correct than before. |
| 1069 | |
| 1070 | '''dereferencing type-punned pointer will break strict-aliasing rules''' |
| 1071 | this warning is only active when strict-aliasing optimization is active |
| 1072 | |
| 1073 | '''-Wstrict-aliasing''' - equivalent to -Wstring-aliasing=3 (included in -Wall)[[BR]] |
| 1074 | '''-Wstrict-aliasing=N''' where N is in {1,2,3} - Higher N implies less false positives, at the expense of more work. Currently there are 3 levels.[[BR]] |
| 1075 | Level 1: Most agressive, quick, very few false negatives, but many false positives. Might use if higher levels don't warn, but turning on -fstrict-aliasing breaks the code. Warns about pointer conversions even if pointer not dereferenced.[[BR]] |
| 1076 | Level 2: Agressive, quick, not too precise. Less false positives than Level 1, but more false negatives. Only warns if pointer is dereferenced.[[BR]] |
| 1077 | Level 3: Very few false positives and very few false negatives--the default.[[BR]] |
| 1078 | '''-fstrict-aliasing''' - also turned on by -O2, -O3 and -Os. Tells the compiler that it's ok to do a certain class of optimization based on the type of expressions. In particular you're promising by using this flag that an object of one type won't reside at the same address as an object of an incompatible type. |
| 1079 | '''-fno-strict-aliasing''' - turns off this optimization. If this changes the behavior of your code, you have a problem in your code. |
| 1080 | |
| 1081 | This is trying to let you know that you are asking the compiler to do undefined behavior and it may not do what you think it will do. As the optimization level increases, the liklihood that you won't like what it does will increase. I show a simple example later that surprisingly generates the wrong result when optimization at any level is turned on. Ignore this warning at your own peril. You are unlikely to care for the undefined behavior that results. |
| 1082 | {{{ |
| 1083 | From the C++ Standard, section 3.10 Lvalues and rvalues |
| 1084 | 15 If a program attempts to access the stored value of an object through an lvalue of other than |
| 1085 | one of the following types the behavior is undefined |
| 1086 | — the dynamic type of the object, |
| 1087 | — a cv-qualified version of the dynamic type of the object, |
| 1088 | — a type similar (as defined in 4.4) to the dynamic type of the object, |
| 1089 | — a type that is the signed or unsigned type corresponding to the dynamic type of the object, |
| 1090 | — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object, |
| 1091 | — an aggregate or union type that includes one of the aforementioned types among its member |
| 1092 | (including, recursively, a member of a subaggregate or contained union), |
| 1093 | — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object, |
| 1094 | — a char or unsigned char type. |
| 1095 | }}} |
| 1096 | The following program generates 6 warnings about breaking strict-aliasing rules, and many would dismiss them. The correct output of the program is: |
| 1097 | |
| 1098 | 00000020 |
| 1099 | 00200000 |
| 1100 | |
| 1101 | but when optimization is turned on it's: |
| 1102 | |
| 1103 | 00000020 |
| 1104 | 00000020 |
| 1105 | |
| 1106 | THAT's what the warning is trying to tell you, that the optimizer is going to do things that you don't like. In this case seeing that acopy is set to a and never touched again, strict aliasing lets it optimize by just returning the original value of a at the end. |
| 1107 | {{{ |
| 1108 | uint32_t |
| 1109 | swaphalves(uint32_t a) |
| 1110 | { |
| 1111 | uint32_t acopy=a; |
| 1112 | uint16_t *ptr=(uint16_t*)&acopy;// can't use static_cast<>, not legal. |
| 1113 | // you should be warned by that. |
| 1114 | uint16_t tmp=ptr[0]; |
| 1115 | ptr[0]=ptr[1]; |
| 1116 | ptr[1]=tmp; |
| 1117 | return acopy; |
| 1118 | } |
| 1119 | |
| 1120 | int main() |
| 1121 | { |
| 1122 | uint32_t a; |
| 1123 | a=32; |
| 1124 | cout << hex << setfill('0') << setw(8) << a << endl; |
| 1125 | a=swaphalves(a); |
| 1126 | cout << setw(8) << a << endl; |
| 1127 | } |
| 1128 | }}} |
| 1129 | Here's the (annotated) x86 assembler generated by gcc 4.4.1 for swaphalves, let's see what went wrong: |
| 1130 | {{{ |
| 1131 | _Z10swaphalvesj: |
| 1132 | pushl %ebp |
| 1133 | movl %esp, %ebp |
| 1134 | subl $16, %esp |
| 1135 | movl 8(%ebp), %eax # get a in %eax |
| 1136 | movl %eax, -8(%ebp) # and store in in acopy |
| 1137 | leal -8(%ebp), %eax # now get eax pointing at acopy (ptr=&acopy) |
| 1138 | movl %eax, -12(%ebp) # save that ptr at -12(%ebp) |
| 1139 | movl -12(%ebp), %eax # get the ptr back in %eax |
| 1140 | movzwl (%eax), %eax # get 16 bits from ptr[0] in eax |
| 1141 | movw %ax, -2(%ebp) # store the 16 bits into tmp |
| 1142 | movl -12(%ebp), %eax # get the ptr back in eax |
| 1143 | addl $2, %eax # bump up by two to get to ptr[1] |
| 1144 | movzwl (%eax), %edx # get that 16 bits into %edx |
| 1145 | movl -12(%ebp), %eax # get ptr into eax |
| 1146 | movw %dx, (%eax) # store the 16 bits into ptr[1] |
| 1147 | movl -12(%ebp), %eax # get the ptr again |
| 1148 | leal 2(%eax), %edx # get the address of ptr[1] into edx |
| 1149 | movzwl -2(%ebp), %eax # get tmp into eax |
| 1150 | movw %ax, (%edx) # store into ptr[1] |
| 1151 | movl -8(%ebp), %eax # forget all that, return original a. |
| 1152 | leave |
| 1153 | ret |
| 1154 | }}} |
| 1155 | Scary, isn't it? Of course you could use -fno-strict-aliasing to get the right output, but the generated code won't be as good. A better way to accomplish the same thing without the warning's or the incorrect output is to define swaphalves like this: |
| 1156 | |
| 1157 | {{{ |
| 1158 | uint32_t |
| 1159 | swaphalves(uint32_t a) |
| 1160 | { |
| 1161 | union swapem{ |
| 1162 | uint32_t a; |
| 1163 | uint16_t b[2]; |
| 1164 | }; |
| 1165 | swapem s={a}; |
| 1166 | uint16_t tmp; |
| 1167 | tmp=s.b[0]; |
| 1168 | s.b[0]=s.b[1]; |
| 1169 | s.b[1]=tmp; |
| 1170 | return s.a; |
| 1171 | } |
| 1172 | }}} |
| 1173 | The C++ compiler knows that members of a union alias, and this helps the compiler generate MUCH better code: |
| 1174 | {{{ |
| 1175 | _Z10swaphalvesj: |
| 1176 | pushl %ebp # save the original value of ebp |
| 1177 | movl %esp, %ebp # point ebp at the stack frame |
| 1178 | movl 8(%ebp), %eax # get a in eax |
| 1179 | popl %ebp # get the original ebp value back |
| 1180 | roll $16, %eax # swap the two halves of a and return it |
| 1181 | ret |
| 1182 | }}} |
| 1183 | |
| 1184 | |
| 1185 | |
| 1186 | |
| 1187 | |
| 1188 | |