<div dir="ltr">huh, that's super surprising to me. I'd have thought the unoptimized code from the frontend would make it pretty difficult for the middle-end to reorder any of the loads, stores, etc.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Sep 26, 2019 at 11:38 PM Martin Storsjö <<a href="mailto:martin@martin.st">martin@martin.st</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Thu, 26 Sep 2019, David Blaikie wrote:<br>
<br>
> I'd be surprised if Clang or GCC's behavior here varied depending on the<br>
> size of anything, but maybe?<br>
> <br>
> In any case, C++17 or so requires the RHS to be evaluated before the LHS for<br>
> assignments - so this is now /always/ wrong, not just unspecified (which, I<br>
> guess, also always wrong... just sometimes accidentally right) as it was<br>
> before.<br>
<br>
It's not a question about when the operands are evaluated - both do <br>
evaluate them in the same order. But evaluating the left and right hand <br>
side leaves you with two references. Then to do the assignment, you <br>
dereference the right hand side reference and assign it to the <br>
dereferenced left hand side reference.<br>
<br>
If the value type is as small as the reference itself, Clang dereferences <br>
the right hand side reference before evaluating the left hand side.<br>
<br>
The testcase in <a href="https://bugs.llvm.org/show_bug.cgi?id=42065" rel="noreferrer" target="_blank">https://bugs.llvm.org/show_bug.cgi?id=42065</a> allows you to <br>
play around with different variants of this; small or large value type, <br>
and different forms of the assignment expression (where one form of the <br>
expression helped for Clang but not for GCC).<br>
<br>
The core part of the testcase is this:<br>
<br>
void copy(void *a, void *b) {<br>
         GlobalMap[a] = GlobalMap[b];<br>
}<br>
<br>
When compiled with Clang, ends up like this:<br>
<br>
define void @_Z4copyPvS_(i8*, i8*) #1 {<br>
   %3 = alloca i8*, align 8<br>
   %4 = alloca i8*, align 8<br>
   store i8* %0, i8** %3, align 8<br>
   store i8* %1, i8** %4, align 8<br>
   %5 = call dereferenceable(8) i8** @_ZN4llvm12DenseMapBaseINS_8DenseMapIPvS2_NS_12DenseMapInfoIS2_EENS_6detail12DenseMapPairIS2_S2_EEEES2_S2_S4_S7_EixERKS2_(%"class.llvm::DenseMapBase"* getelementptr inbounds (%"class.llvm::DenseMap", %"class.llvm::DenseMap"* @GlobalMap, i32 0, i32 0), i8** dereferenceable(8) %4)<br>
   %6 = load i8*, i8** %5, align 8<br>
   %7 = call dereferenceable(8) i8** @_ZN4llvm12DenseMapBaseINS_8DenseMapIPvS2_NS_12DenseMapInfoIS2_EENS_6detail12DenseMapPairIS2_S2_EEEES2_S2_S4_S7_EixERKS2_(%"class.llvm::DenseMapBase"* getelementptr inbounds (%"class.llvm::DenseMap", %"class.llvm::DenseMap"* @GlobalMap, i32 0, i32 0), i8** dereferenceable(8) %3)<br>
   store i8* %6, i8** %7, align 8<br>
   ret void<br>
}<br>
<br>
Here %6 is the value type loaded from the RHS reference %5, loaded before <br>
evaluating the LHS.<br>
<br>
If the value type of the map is something larger than just a pointer, the <br>
emitted IR looks like this instead:<br>
<br>
define void @_Z4copyPvS_(i8*, i8*) #1 {<br>
   %3 = alloca i8*, align 8<br>
   %4 = alloca i8*, align 8<br>
   store i8* %0, i8** %3, align 8<br>
   store i8* %1, i8** %4, align 8<br>
   %5 = call dereferenceable(800) %struct.LargeValue* @_ZN4llvm12DenseMapBaseINS_8DenseMapIPv10LargeValueNS_12DenseMapInfoIS2_EENS_6detail12DenseMapPairIS2_S3_EEEES2_S3_S5_S8_EixERKS2_(%"class.llvm::DenseMapBase"* getelementptr inbounds (%"class.llvm::DenseMap", %"class.llvm::DenseMap"* @GlobalMap, i32 0, i32 0), i8** dereferenceable(8) %4)<br>
   %6 = call dereferenceable(800) %struct.LargeValue* @_ZN4llvm12DenseMapBaseINS_8DenseMapIPv10LargeValueNS_12DenseMapInfoIS2_EENS_6detail12DenseMapPairIS2_S3_EEEES2_S3_S5_S8_EixERKS2_(%"class.llvm::DenseMapBase"* getelementptr inbounds (%"class.llvm::DenseMap", %"class.llvm::DenseMap"* @GlobalMap, i32 0, i32 0), i8** dereferenceable(8) %3)<br>
   %7 = bitcast %struct.LargeValue* %6 to i8*<br>
   %8 = bitcast %struct.LargeValue* %5 to i8*<br>
   call void @llvm.memcpy.p0i8.p0i8.i64(i8* %7, i8* %8, i64 800, i32 8, i1 false)<br>
   ret void<br>
}<br>
<br>
In this case, the RHS reference isn't dereferenced until both references <br>
are available, and the value can be copied with a memcpy.<br>
<br>
// Martin<br>
<br>
</blockquote></div>