Skip to content

Commit 3a099c5

Browse files
efriedma-quicAlexisPerry
authored andcommitted
[ARM64EC] Fix thunks for C++ methods returning structs. (llvm#95876)
For C++ methods, the first argument is "this", and the second is the sret argument, which needs to be returned indirectly. Add handling for this case.
1 parent ca040d6 commit 3a099c5

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,27 +209,36 @@ void AArch64Arm64ECCallLowering::getThunkRetType(
209209
#endif
210210
if (T->isVoidTy()) {
211211
if (FT->getNumParams()) {
212-
auto SRetAttr = AttrList.getParamAttr(0, Attribute::StructRet);
213-
auto InRegAttr = AttrList.getParamAttr(0, Attribute::InReg);
214-
if (SRetAttr.isValid() && InRegAttr.isValid()) {
212+
Attribute SRetAttr0 = AttrList.getParamAttr(0, Attribute::StructRet);
213+
Attribute InRegAttr0 = AttrList.getParamAttr(0, Attribute::InReg);
214+
Attribute SRetAttr1, InRegAttr1;
215+
if (FT->getNumParams() > 1) {
216+
// Also check the second parameter (for class methods, the first
217+
// parameter is "this", and the second parameter is the sret pointer.)
218+
// It doesn't matter which one is sret.
219+
SRetAttr1 = AttrList.getParamAttr(1, Attribute::StructRet);
220+
InRegAttr1 = AttrList.getParamAttr(1, Attribute::InReg);
221+
}
222+
if ((SRetAttr0.isValid() && InRegAttr0.isValid()) ||
223+
(SRetAttr1.isValid() && InRegAttr1.isValid())) {
215224
// sret+inreg indicates a call that returns a C++ class value. This is
216225
// actually equivalent to just passing and returning a void* pointer
217-
// as the first argument. Translate it that way, instead of trying
218-
// to model "inreg" in the thunk's calling convention, to simplify
219-
// the rest of the code.
226+
// as the first or second argument. Translate it that way, instead of
227+
// trying to model "inreg" in the thunk's calling convention; this
228+
// simplfies the rest of the code, and matches MSVC mangling.
220229
Out << "i8";
221230
Arm64RetTy = I64Ty;
222231
X64RetTy = I64Ty;
223232
return;
224233
}
225-
if (SRetAttr.isValid()) {
234+
if (SRetAttr0.isValid()) {
226235
// FIXME: Sanity-check the sret type; if it's an integer or pointer,
227236
// we'll get screwy mangling/codegen.
228237
// FIXME: For large struct types, mangle as an integer argument and
229238
// integer return, so we can reuse more thunks, instead of "m" syntax.
230239
// (MSVC mangles this case as an integer return with no argument, but
231240
// that's a miscompile.)
232-
Type *SRetType = SRetAttr.getValueAsType();
241+
Type *SRetType = SRetAttr0.getValueAsType();
233242
Align SRetAlign = AttrList.getParamAlignment(0).valueOrOne();
234243
Type *Arm64Ty, *X64Ty;
235244
canonicalizeThunkType(SRetType, SRetAlign, /*Ret*/ true, ArgSizeBytes,

llvm/test/CodeGen/AArch64/arm64ec-entry-thunks.ll

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,50 @@ define %T2 @simple_struct(%T1 %0, %T2 %1, %T3, %T4) nounwind {
444444
ret %T2 %1
445445
}
446446

447+
define void @cxx_method(ptr noundef nonnull align 8 dereferenceable(8) %0, ptr dead_on_unwind inreg noalias writable sret(i64) align 8 %1) {
448+
; CHECK-LABEL: .def $ientry_thunk$cdecl$i8$i8i8;
449+
; CHECK: .section .wowthk$aa,"xr",discard,$ientry_thunk$cdecl$i8$i8i8
450+
; CHECK: // %bb.0:
451+
; CHECK-NEXT: stp q6, q7, [sp, #-176]! // 32-byte Folded Spill
452+
; CHECK-NEXT: .seh_save_any_reg_px q6, 176
453+
; CHECK-NEXT: stp q8, q9, [sp, #32] // 32-byte Folded Spill
454+
; CHECK-NEXT: .seh_save_any_reg_p q8, 32
455+
; CHECK-NEXT: stp q10, q11, [sp, #64] // 32-byte Folded Spill
456+
; CHECK-NEXT: .seh_save_any_reg_p q10, 64
457+
; CHECK-NEXT: stp q12, q13, [sp, #96] // 32-byte Folded Spill
458+
; CHECK-NEXT: .seh_save_any_reg_p q12, 96
459+
; CHECK-NEXT: stp q14, q15, [sp, #128] // 32-byte Folded Spill
460+
; CHECK-NEXT: .seh_save_any_reg_p q14, 128
461+
; CHECK-NEXT: stp x29, x30, [sp, #160] // 16-byte Folded Spill
462+
; CHECK-NEXT: .seh_save_fplr 160
463+
; CHECK-NEXT: add x29, sp, #160
464+
; CHECK-NEXT: .seh_add_fp 160
465+
; CHECK-NEXT: .seh_endprologue
466+
; CHECK-NEXT: blr x9
467+
; CHECK-NEXT: adrp x8, __os_arm64x_dispatch_ret
468+
; CHECK-NEXT: ldr x1, [x8, :lo12:__os_arm64x_dispatch_ret]
469+
; CHECK-NEXT: mov x8, x0
470+
; CHECK-NEXT: .seh_startepilogue
471+
; CHECK-NEXT: ldp x29, x30, [sp, #160] // 16-byte Folded Reload
472+
; CHECK-NEXT: .seh_save_fplr 160
473+
; CHECK-NEXT: ldp q14, q15, [sp, #128] // 32-byte Folded Reload
474+
; CHECK-NEXT: .seh_save_any_reg_p q14, 128
475+
; CHECK-NEXT: ldp q12, q13, [sp, #96] // 32-byte Folded Reload
476+
; CHECK-NEXT: .seh_save_any_reg_p q12, 96
477+
; CHECK-NEXT: ldp q10, q11, [sp, #64] // 32-byte Folded Reload
478+
; CHECK-NEXT: .seh_save_any_reg_p q10, 64
479+
; CHECK-NEXT: ldp q8, q9, [sp, #32] // 32-byte Folded Reload
480+
; CHECK-NEXT: .seh_save_any_reg_p q8, 32
481+
; CHECK-NEXT: ldp q6, q7, [sp], #176 // 32-byte Folded Reload
482+
; CHECK-NEXT: .seh_save_any_reg_px q6, 176
483+
; CHECK-NEXT: .seh_endepilogue
484+
; CHECK-NEXT: br x1
485+
; CHECK-NEXT: .seh_endfunclet
486+
; CHECK-NEXT: .seh_endproc
487+
ret void
488+
}
489+
490+
447491
; Verify the hybrid bitmap
448492
; CHECK-LABEL: .section .hybmp$x,"yi"
449493
; CHECK-NEXT: .symidx "#no_op"
@@ -476,3 +520,6 @@ define %T2 @simple_struct(%T1 %0, %T2 %1, %T3, %T4) nounwind {
476520
; CHECK-NEXT: .symidx "#simple_struct"
477521
; CHECK-NEXT: .symidx $ientry_thunk$cdecl$m8$i8m8m16m24
478522
; CHECK-NEXT: .word 1
523+
; CHECK-NEXT: .symidx "#cxx_method"
524+
; CHECK-NEXT: .symidx $ientry_thunk$cdecl$i8$i8i8
525+
; CHECK-NEXT: .word 1

0 commit comments

Comments
 (0)