https://github.com/xroche updated https://github.com/llvm/llvm-project/pull/215479
>From 9a8bea8fd6cd6cf125ae410b8a3ab07633461e7e Mon Sep 17 00:00:00 2001 From: Xavier Roche <[email protected]> Date: Mon, 10 Aug 2026 18:03:56 +0200 Subject: [PATCH 1/3] [Clang] Sequence explicit-object operator operands on Microsoft ABI targets Clang evaluated the object after the other operands of explicit-object operator[] and operator() calls on Microsoft ABI targets. C++ requires the object first. Emit it before the remaining operands while keeping their relative ABI order and the hidden size argument attached to the object. Assisted-by: Claude (Anthropic) Co-Authored-By: Claude Opus 5 <[email protected]> Co-Authored-By: Codex GPT-6 <[email protected]> --- clang/docs/ReleaseNotes.md | 5 + clang/lib/CodeGen/CGCall.cpp | 26 ++++- clang/lib/CodeGen/CGExpr.cpp | 16 +-- clang/lib/CodeGen/CodeGenFunction.h | 5 +- clang/test/CodeGenCXX/cxx1z-eval-order.cpp | 72 ++++++++++++ .../CodeGenCXX/microsoft-abi-arg-order.cpp | 109 +++++++++++++++++- 6 files changed, 218 insertions(+), 15 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index c5eed2b035e66..4f70963b7864d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -621,6 +621,11 @@ features cannot lower the translation-unit ABI level; #### Bug Fixes to C++ Support +- Clang now evaluates the object before the arguments of overloaded + `operator[]` and `operator()` with explicit object parameters on Microsoft + ABI targets. Parameter destruction still follows Microsoft ABI order and + can differ from reverse construction order. + - Fixed lambdas with specifiers or attributes after the capture list being misparsed as function declarations in direct-initialization contexts under `-fms-extensions` or in HLSL mode. diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index c294621b28d1d..fb191f4c428e6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5131,8 +5131,12 @@ void CodeGenFunction::EmitCallArgs( ? Order == EvaluationOrder::ForceLeftToRight : Order != EvaluationOrder::ForceRightToLeft; + // Keep the ABI order of the remaining arguments when hoisting the first. + bool HoistFirstArg = !LeftToRight && ArgTypes.size() > 1 && + Order == EvaluationOrder::ForceFirstBeforeRest; + auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg, - RValue EmittedArg) { + RValue EmittedArg, bool Reversed) { if (!AC.hasFunctionDecl() || I >= AC.getNumParams()) return; auto *PS = AC.getParamDecl(I)->getAttr<PassObjectSizeAttr>(); @@ -5148,7 +5152,7 @@ void CodeGenFunction::EmitCallArgs( Args.add(RValue::get(V), SizeTy); // If we're emitting args in reverse, be sure to do so with // pass_object_size, as well. - if (!LeftToRight) + if (Reversed) std::swap(Args.back(), *(&Args.back() - 1)); }; @@ -5160,9 +5164,17 @@ void CodeGenFunction::EmitCallArgs( } // Evaluate each argument in the appropriate order. - size_t CallArgsStart = Args.size(); + // Keep the hoisted argument and its hidden parameters outside the reversal. + size_t ReversedStart = Args.size(); for (unsigned I = 0, E = ArgTypes.size(); I != E; ++I) { - unsigned Idx = LeftToRight ? I : E - I - 1; + unsigned Idx; + if (LeftToRight) + Idx = I; + else if (HoistFirstArg) + Idx = I == 0 ? 0 : E - I; + else + Idx = E - I - 1; + bool Reversed = !LeftToRight && !(HoistFirstArg && I == 0); CallExpr::const_arg_iterator Arg = ArgRange.begin() + Idx; unsigned InitialArgSize = Args.size(); // If *Arg is an ObjCIndirectCopyRestoreExpr, check that either the types of @@ -5188,14 +5200,16 @@ void CodeGenFunction::EmitCallArgs( // @llvm.objectsize should never have side-effects and shouldn't need // destruction/cleanups, so we can safely "emit" it after its arg, // regardless of right-to-leftness - MaybeEmitImplicitObjectSize(Idx, *Arg, RVArg); + MaybeEmitImplicitObjectSize(Idx, *Arg, RVArg, Reversed); } + if (HoistFirstArg && I == 0) + ReversedStart = Args.size(); } if (!LeftToRight) { // Un-reverse the arguments we just evaluated so they match up with the LLVM // IR function. - std::reverse(Args.begin() + CallArgsStart, Args.end()); + std::reverse(Args.begin() + ReversedStart, Args.end()); // Reverse the writebacks to match the MSVC ABI. Args.reverseWritebacks(); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 8acf4b95f3e77..bc3e77d4a008c 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -7270,11 +7270,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, if (Chain) Args.add(RValue::get(Chain), CGM.getContext().VoidPtrTy); - // C++17 requires that we evaluate arguments to a call using assignment syntax - // right-to-left, and that we evaluate arguments to certain other operators - // left-to-right. Note that we allow this to override the order dictated by - // the calling convention on the MS ABI, which means that parameter - // destruction order is not necessarily reverse construction order. + // Operator notation follows the language's operand order. On the MS ABI, + // this can make parameter destruction differ from reverse construction order. // FIXME: Revisit this based on C++ committee response to unimplementability. EvaluationOrder Order = EvaluationOrder::Default; bool StaticOperator = false; @@ -7291,6 +7288,10 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, case OO_ArrowStar: Order = EvaluationOrder::ForceLeftToRight; break; + case OO_Subscript: + case OO_Call: + Order = EvaluationOrder::ForceFirstBeforeRest; + break; default: break; } @@ -7304,8 +7305,9 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, auto Arguments = E->arguments(); if (StaticOperator) { - // If we're calling a static operator, we need to emit the object argument - // and ignore it. + // A static operator evaluates and discards the object before the call. + if (Order == EvaluationOrder::ForceFirstBeforeRest) + Order = EvaluationOrder::Default; EmitIgnoredExpr(E->getArg(0)); Arguments = drop_begin(Arguments, 1); } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9976a411fd673..f2db5d59bc88f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5653,7 +5653,10 @@ class CodeGenFunction : public CodeGenTypeCache { ///! Language semantics require left-to-right evaluation. ForceLeftToRight, ///! Language semantics require right-to-left evaluation. - ForceRightToLeft + ForceRightToLeft, + ///! Language semantics require only the first argument to be evaluated + ///! first; the others are indeterminately sequenced. + ForceFirstBeforeRest }; // Wrapper for function prototype sources. Wraps either a FunctionProtoType or diff --git a/clang/test/CodeGenCXX/cxx1z-eval-order.cpp b/clang/test/CodeGenCXX/cxx1z-eval-order.cpp index 04c1b50f497e8..023d95cd88536 100644 --- a/clang/test/CodeGenCXX/cxx1z-eval-order.cpp +++ b/clang/test/CodeGenCXX/cxx1z-eval-order.cpp @@ -1,6 +1,9 @@ // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-ITANIUM +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS struct B; struct A { @@ -269,3 +272,72 @@ void andor_lhs_before_rhs() { // CHECK: call {{.*}}@{{.*}}make_b{{.*}}( make_c() || make_b(); } + +#if __cplusplus >= 202302L +struct D { + void operator[](this D self, B b); + void operator[](this D self, B b, C c); + void operator[](this D self, B b, C c, A a); + void operator()(this D self, B b, C c); +}; +struct E { + static void operator()(B b, C c); +}; +D make_d(); +E make_e(); + +// CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_index{{.*}}( +void subscript_object_before_index() { + // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_b{{.*}}( + make_d()[make_b()]; +// CXX23: } +} + +// CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_indices{{.*}}( +void subscript_object_before_indices() { + // The indices can run in either order, so keep the ABI order. + // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + make_d()[make_b(), make_c()]; +// CXX23: } +} + +// CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_three_indices{{.*}}( +void subscript_object_before_three_indices() { + // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_a{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_a{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + make_d()[make_b(), make_c(), make_a()]; +// CXX23: } +} + +// CXX23-LABEL: define {{.*}}@{{.*}}call_object_before_args{{.*}}( +void call_object_before_args() { + // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + make_d()(make_b(), make_c()); +// CXX23: } +} + +// CXX23-LABEL: define {{.*}}@{{.*}}static_operator_object_first{{.*}}( +void static_operator_object_first() { + // CXX23: call {{.*}}@{{.*}}make_e{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + make_e()(make_b(), make_c()); +// CXX23: } +} +#endif diff --git a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp index c879c483686e5..0e0a3de19ba7c 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -mconstructor-aliases -std=c++11 -fexceptions -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s -check-prefix=X86 // RUN: %clang_cc1 -mconstructor-aliases -std=c++11 -fexceptions -emit-llvm %s -o - -triple=x86_64-pc-win32 | FileCheck %s -check-prefix=X64 +// RUN: %clang_cc1 -mconstructor-aliases -std=c++23 -fexceptions -emit-llvm %s -o - -triple=i386-pc-win32 | FileCheck %s -check-prefix=X86-CXX23 +// RUN: %clang_cc1 -mconstructor-aliases -std=c++23 -fexceptions -emit-llvm %s -o - -triple=x86_64-pc-win32 | FileCheck %s -check-prefix=X64-CXX23 struct A { A(int a); @@ -11,7 +13,7 @@ struct A { void foo(A a, A b, A c) { } -// Order of destruction should be left to right. +// Parameter destruction is left to right. // // X86-LABEL: define dso_local void @"?foo@@YAXUA@@00@Z" // X86: (ptr inalloca([[argmem_ty:<{ %struct.A, %struct.A, %struct.A }>]]) %0) @@ -74,3 +76,108 @@ void call_foo() { // // ehcleanup: // X64: call void @"??1A@@QEAA@XZ"(ptr {{[^,]*}} %[[arg3]]) + +#if __cplusplus >= 202302L +struct B { + B(int b); + B(const B &o); + ~B(); + int b; + void operator[](this B self, B i, B j); +}; + +void B::operator[](this B self, B i, B j) { +} + +// Parameter destruction is left to right. +// +// X86-CXX23-LABEL: define dso_local void @"??AB@@SAX_VU0@00@Z" +// X86-CXX23: (ptr inalloca([[argmem_b:<{ %struct.B, %struct.B, %struct.B }>]]) %0) +// X86-CXX23: %[[self:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %0, i32 0, i32 0 +// X86-CXX23: %[[i:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %0, i32 0, i32 1 +// X86-CXX23: %[[j:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %0, i32 0, i32 2 +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[self]]) +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[i]]) +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[j]]) +// X86-CXX23: ret void + +// X64-CXX23-LABEL: define dso_local void @"??AB@@SAX_VU0@00@Z" +// X64-CXX23: (ptr {{[^,]*}} %[[self:[^,]*]], ptr {{[^,]*}} %[[i:[^,]*]], ptr {{[^,]*}} %[[j:[^)]*]]) +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[self]]) +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[i]]) +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[j]]) +// X64-CXX23: ret void + + +void call_subscript() { + B(1)[B(2), B(3)]; +} + +// X86-CXX23-LABEL: define dso_local void @"?call_subscript@@YAXXZ"() +// X86-CXX23: %[[argmem:[^ ]*]] = alloca inalloca [[argmem_b]] +// X86-CXX23: %[[obj:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 0 +// X86-CXX23: call x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[obj]], i32 noundef 1) +// X86-CXX23: %[[idx2:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 2 +// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[idx2]], i32 noundef 3) +// X86-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] +// X86-CXX23: %[[idx1:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 1 +// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[idx1]], i32 noundef 2) +// X86-CXX23: to label %{{[^ ]+}} unwind label %[[IDX_CLEANUP:[^ ]+]] +// X86-CXX23: call void @"??AB@@SAX_VU0@00@Z"(ptr inalloca([[argmem_b]]) %[[argmem]]) +// X86-CXX23: ret void +// +// X86-CXX23: [[IDX_CLEANUP]]: +// X86-CXX23: cleanuppad within none [] +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[idx2]]) +// X86-CXX23: cleanupret {{.*}} unwind label %[[OBJ_CLEANUP]] +// +// X86-CXX23: [[OBJ_CLEANUP]]: +// X86-CXX23: cleanuppad within none [] +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[obj]]) + +// X64-CXX23-LABEL: define dso_local void @"?call_subscript@@YAXXZ"() +// X64-CXX23: call noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[obj:[^,]*]], i32 noundef 1) +// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[idx2:[^,]*]], i32 noundef 3) +// X64-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] +// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[idx1:[^,]*]], i32 noundef 2) +// X64-CXX23: to label %{{[^ ]+}} unwind label %[[IDX_CLEANUP:[^ ]+]] +// X64-CXX23: call void @"??AB@@SAX_VU0@00@Z" +// X64-CXX23: (ptr {{[^,]*}} %[[obj]], ptr {{[^,]*}} %[[idx1]], ptr {{[^,]*}} %[[idx2]]) +// X64-CXX23: ret void +// +// X64-CXX23: [[IDX_CLEANUP]]: +// X64-CXX23: cleanuppad within none [] +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[idx2]]) +// X64-CXX23: cleanupret {{.*}} unwind label %[[OBJ_CLEANUP]] +// +// X64-CXX23: [[OBJ_CLEANUP]]: +// X64-CXX23: cleanuppad within none [] +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[obj]]) + +struct SizeObject { + operator SizeObject *(); + int operator[](this SizeObject *const Self + __attribute__((pass_object_size(0))), + int Index); +}; +SizeObject make_size_object(); +int make_size_index(); + +int pass_object_size_for_object() { + return make_size_object()[make_size_index()]; +} + +// X86-CXX23-LABEL: define dso_local noundef i32 @"?pass_object_size_for_object@@YAHXZ"() +// X86-CXX23: call void @"?make_size_object@@YA?AUSizeObject@@XZ"() +// X86-CXX23: %[[SELF:[^ ]+]] = call x86_thiscallcc noundef ptr @"??BSizeObject@@QAEPAU0@XZ" +// X86-CXX23: %[[SIZE:[^ ]+]] = call i32 @llvm.objectsize.i32.p0(ptr %[[SELF]], +// X86-CXX23: %[[INDEX:[^ ]+]] = call noundef i32 @"?make_size_index@@YAHXZ"() +// X86-CXX23: call noundef i32 @"??ASizeObject@@SAH_VQAU0@W4__pass_object_size0@__clang@@H@Z"(ptr noundef %[[SELF]], i32 noundef %[[SIZE]], i32 noundef %[[INDEX]]) + +// X64-CXX23-LABEL: define dso_local noundef i32 @"?pass_object_size_for_object@@YAHXZ"() +// X64-CXX23: call i8 @"?make_size_object@@YA?AUSizeObject@@XZ"() +// X64-CXX23: %[[SELF:[^ ]+]] = call noundef ptr @"??BSizeObject@@QEAAPEAU0@XZ" +// X64-CXX23: %[[SIZE:[^ ]+]] = call i64 @llvm.objectsize.i64.p0(ptr %[[SELF]], +// X64-CXX23: %[[INDEX:[^ ]+]] = call noundef i32 @"?make_size_index@@YAHXZ"() +// X64-CXX23: call noundef i32 @"??ASizeObject@@SAH_VQEAU0@W4__pass_object_size0@__clang@@H@Z"(ptr noundef %[[SELF]], i64 noundef %[[SIZE]], i32 noundef %[[INDEX]]) +#endif >From 1c19f327aa029be6bac776d1faeedc30ed32ebf2 Mon Sep 17 00:00:00 2001 From: Xavier Roche <[email protected]> Date: Thu, 1 Oct 2026 09:49:39 +0200 Subject: [PATCH 2/3] [Clang] Clarify explicit-object evaluation order comment Assisted-by: Claude (Anthropic) Co-Authored-By: Codex GPT-6 <[email protected]> --- clang/lib/CodeGen/CodeGenFunction.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index f2db5d59bc88f..759b03772f56b 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -5654,8 +5654,7 @@ class CodeGenFunction : public CodeGenTypeCache { ForceLeftToRight, ///! Language semantics require right-to-left evaluation. ForceRightToLeft, - ///! Language semantics require only the first argument to be evaluated - ///! first; the others are indeterminately sequenced. + ///! Language semantics require the first argument before the others. ForceFirstBeforeRest }; >From 315d5c4c96c27fea42199c8ac3a5e5081ff249d5 Mon Sep 17 00:00:00 2001 From: Xavier Roche <[email protected]> Date: Fri, 2 Oct 2026 09:07:33 +0200 Subject: [PATCH 3/3] [Clang] Evaluate overloaded subscript indices left to right MSVC evaluates scalar subscript operands from left to right, while class-valued operands can follow ABI argument order. Match the left-to-right subscript behavior in operator notation without changing argument placement or destruction order. Extend the x86 and x64 CodeGen checks for explicit and implicit object parameters. Keep a call-operator check for ABI slot placement. Assisted-by: Claude (Anthropic) Co-Authored-By: Claude Opus 4.6 <[email protected]> Co-Authored-By: Codex GPT-6 <[email protected]> --- clang/docs/ReleaseNotes.md | 5 +- clang/lib/CodeGen/CGExpr.cpp | 2 +- clang/lib/CodeGen/CGExprCXX.cpp | 6 ++- clang/test/CodeGenCXX/cxx1z-eval-order.cpp | 45 +++++++++++------ .../CodeGenCXX/microsoft-abi-arg-order.cpp | 48 ++++++++++++++++--- 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 4f70963b7864d..e04ae39b3287d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -623,8 +623,9 @@ features cannot lower the translation-unit ABI level; - Clang now evaluates the object before the arguments of overloaded `operator[]` and `operator()` with explicit object parameters on Microsoft - ABI targets. Parameter destruction still follows Microsoft ABI order and - can differ from reverse construction order. + ABI targets. It evaluates multidimensional subscript indices left to right. + Parameter destruction still follows Microsoft ABI order and can differ from + reverse construction order. - Fixed lambdas with specifiers or attributes after the capture list being misparsed as function declarations in direct-initialization contexts under diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index bc3e77d4a008c..f81051aa60e1d 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -7286,9 +7286,9 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, case OO_PipePipe: case OO_Comma: case OO_ArrowStar: + case OO_Subscript: Order = EvaluationOrder::ForceLeftToRight; break; - case OO_Subscript: case OO_Call: Order = EvaluationOrder::ForceFirstBeforeRest; break; diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 8279611accba3..b6cfdc3cbcbed 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -67,13 +67,17 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, GlobalDecl GD, } else if (CE) { // Special case: skip first argument of CXXOperatorCall (it is "this"). unsigned ArgsToSkip = 0; + CodeGenFunction::EvaluationOrder Order = + CodeGenFunction::EvaluationOrder::Default; if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(CE)) { if (const auto *M = dyn_cast<CXXMethodDecl>(Op->getCalleeDecl())) ArgsToSkip = static_cast<unsigned>(!M->isExplicitObjectMemberFunction()); + if (Op->getOperator() == OO_Subscript) + Order = CodeGenFunction::EvaluationOrder::ForceLeftToRight; } CGF.EmitCallArgs(Args, FPT, drop_begin(CE->arguments(), ArgsToSkip), - CE->getDirectCallee()); + CE->getDirectCallee(), /*ParamsToSkip=*/0, Order); } else { assert( FPT->getNumParams() == 0 && diff --git a/clang/test/CodeGenCXX/cxx1z-eval-order.cpp b/clang/test/CodeGenCXX/cxx1z-eval-order.cpp index 023d95cd88536..1f8ae551e43fe 100644 --- a/clang/test/CodeGenCXX/cxx1z-eval-order.cpp +++ b/clang/test/CodeGenCXX/cxx1z-eval-order.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS // RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS -// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-ITANIUM -// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS -// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple %itanium_abi_triple | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ITANIUM --check-prefix=CXX23 --check-prefix=CXX23-ITANIUM +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple i686-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS +// RUN: %clang_cc1 -std=c++23 %s -emit-llvm -o - -triple x86_64-windows | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-WINDOWS --check-prefix=CXX23 --check-prefix=CXX23-WINDOWS struct B; struct A { @@ -282,9 +282,14 @@ struct D { }; struct E { static void operator()(B b, C c); + static void operator[](B b, C c); +}; +struct F { + void operator[](B b, C c); }; D make_d(); E make_e(); +F make_f(); // CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_index{{.*}}( void subscript_object_before_index() { @@ -296,12 +301,9 @@ void subscript_object_before_index() { // CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_indices{{.*}}( void subscript_object_before_indices() { - // The indices can run in either order, so keep the ABI order. // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( - // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( - // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( - // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( - // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_c{{.*}}( make_d()[make_b(), make_c()]; // CXX23: } } @@ -309,16 +311,22 @@ void subscript_object_before_indices() { // CXX23-LABEL: define {{.*}}@{{.*}}subscript_object_before_three_indices{{.*}}( void subscript_object_before_three_indices() { // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( - // CXX23-ITANIUM: call {{.*}}@{{.*}}make_b{{.*}}( - // CXX23-ITANIUM: call {{.*}}@{{.*}}make_c{{.*}}( - // CXX23-ITANIUM: call {{.*}}@{{.*}}make_a{{.*}}( - // CXX23-WINDOWS: call {{.*}}@{{.*}}make_a{{.*}}( - // CXX23-WINDOWS: call {{.*}}@{{.*}}make_c{{.*}}( - // CXX23-WINDOWS: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_c{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_a{{.*}}( make_d()[make_b(), make_c(), make_a()]; // CXX23: } } +// CXX23-LABEL: define {{.*}}@{{.*}}implicit_subscript_object_before_indices{{.*}}( +void implicit_subscript_object_before_indices() { + // CXX23: call {{.*}}@{{.*}}make_f{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_c{{.*}}( + make_f()[make_b(), make_c()]; +// CXX23: } +} + // CXX23-LABEL: define {{.*}}@{{.*}}call_object_before_args{{.*}}( void call_object_before_args() { // CXX23: call {{.*}}@{{.*}}make_d{{.*}}( @@ -340,4 +348,13 @@ void static_operator_object_first() { make_e()(make_b(), make_c()); // CXX23: } } + +// CXX23-LABEL: define {{.*}}@{{.*}}static_subscript_object_first{{.*}}( +void static_subscript_object_first() { + // CXX23: call {{.*}}@{{.*}}make_e{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_b{{.*}}( + // CXX23: call {{.*}}@{{.*}}make_c{{.*}}( + make_e()[make_b(), make_c()]; +// CXX23: } +} #endif diff --git a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp index 0e0a3de19ba7c..7d3a325e5072b 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-arg-order.cpp @@ -84,6 +84,7 @@ struct B { ~B(); int b; void operator[](this B self, B i, B j); + void operator()(this B self, B i, B j); }; void B::operator[](this B self, B i, B j) { @@ -117,18 +118,18 @@ void call_subscript() { // X86-CXX23: %[[argmem:[^ ]*]] = alloca inalloca [[argmem_b]] // X86-CXX23: %[[obj:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 0 // X86-CXX23: call x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[obj]], i32 noundef 1) -// X86-CXX23: %[[idx2:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 2 -// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[idx2]], i32 noundef 3) -// X86-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] // X86-CXX23: %[[idx1:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 1 // X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[idx1]], i32 noundef 2) +// X86-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] +// X86-CXX23: %[[idx2:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 2 +// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[idx2]], i32 noundef 3) // X86-CXX23: to label %{{[^ ]+}} unwind label %[[IDX_CLEANUP:[^ ]+]] // X86-CXX23: call void @"??AB@@SAX_VU0@00@Z"(ptr inalloca([[argmem_b]]) %[[argmem]]) // X86-CXX23: ret void // // X86-CXX23: [[IDX_CLEANUP]]: // X86-CXX23: cleanuppad within none [] -// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[idx2]]) +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[idx1]]) // X86-CXX23: cleanupret {{.*}} unwind label %[[OBJ_CLEANUP]] // // X86-CXX23: [[OBJ_CLEANUP]]: @@ -137,9 +138,9 @@ void call_subscript() { // X64-CXX23-LABEL: define dso_local void @"?call_subscript@@YAXXZ"() // X64-CXX23: call noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[obj:[^,]*]], i32 noundef 1) -// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[idx2:[^,]*]], i32 noundef 3) -// X64-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] // X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[idx1:[^,]*]], i32 noundef 2) +// X64-CXX23: to label %{{[^ ]+}} unwind label %[[OBJ_CLEANUP:[^ ]+]] +// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[idx2:[^,]*]], i32 noundef 3) // X64-CXX23: to label %{{[^ ]+}} unwind label %[[IDX_CLEANUP:[^ ]+]] // X64-CXX23: call void @"??AB@@SAX_VU0@00@Z" // X64-CXX23: (ptr {{[^,]*}} %[[obj]], ptr {{[^,]*}} %[[idx1]], ptr {{[^,]*}} %[[idx2]]) @@ -147,13 +148,46 @@ void call_subscript() { // // X64-CXX23: [[IDX_CLEANUP]]: // X64-CXX23: cleanuppad within none [] -// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[idx2]]) +// X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[idx1]]) // X64-CXX23: cleanupret {{.*}} unwind label %[[OBJ_CLEANUP]] // // X64-CXX23: [[OBJ_CLEANUP]]: // X64-CXX23: cleanuppad within none [] // X64-CXX23: call void @"??1B@@QEAA@XZ"(ptr {{[^,]*}} %[[obj]]) +void call_call() { + B(1)(B(2), B(3)); +} + +// The call arguments must occupy their declared parameter slots. +// +// X86-CXX23-LABEL: define dso_local void @"?call_call@@YAXXZ"() +// X86-CXX23: %[[argmem:[^ ]*]] = alloca inalloca [[argmem_b]] +// X86-CXX23: %[[obj:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 0 +// X86-CXX23: call x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[obj]], i32 noundef 1) +// X86-CXX23: %[[arg2:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 2 +// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[arg2]], i32 noundef 3) +// X86-CXX23: %[[arg1:[^ ]*]] = getelementptr inbounds nuw [[argmem_b]], ptr %[[argmem]], i32 0, i32 1 +// X86-CXX23: invoke x86_thiscallcc noundef ptr @"??0B@@QAE@H@Z"(ptr {{[^,]*}} %[[arg1]], i32 noundef 2) +// X86-CXX23: call void @"??RB@@SAX_VU0@00@Z"(ptr inalloca([[argmem_b]]) %[[argmem]]) +// X86-CXX23: ret void +// +// ehcleanup: +// X86-CXX23: cleanuppad within none [] +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[arg2]]) +// X86-CXX23: cleanupret +// +// ehcleanup4: +// X86-CXX23: cleanuppad within none [] +// X86-CXX23: call x86_thiscallcc void @"??1B@@QAE@XZ"(ptr {{[^,]*}} %[[obj]]) + +// X64-CXX23-LABEL: define dso_local void @"?call_call@@YAXXZ"() +// X64-CXX23: call noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[obj:[^,]*]], i32 noundef 1) +// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[arg2:[^,]*]], i32 noundef 3) +// X64-CXX23: invoke noundef ptr @"??0B@@QEAA@H@Z"(ptr {{[^,]*}} %[[arg1:[^,]*]], i32 noundef 2) +// X64-CXX23: call void @"??RB@@SAX_VU0@00@Z" +// X64-CXX23: (ptr {{[^,]*}} %[[obj]], ptr {{[^,]*}} %[[arg1]], ptr {{[^,]*}} %[[arg2]]) +// X64-CXX23: ret void struct SizeObject { operator SizeObject *(); int operator[](this SizeObject *const Self _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
