Skip to content

Commit e6f8d1b

Browse files
committed
[cxx-interop][windows] Pass trivial indirect result after 'this' when invoking C++ MSVC ABI method
1 parent cdcdfa9 commit e6f8d1b

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

lib/IRGen/GenCall.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,15 @@ void CallEmission::emitToUnmappedMemory(Address result) {
29062906
assert(LastArgWritten == 1 && "emitting unnaturally to indirect result");
29072907

29082908
Args[0] = result.getAddress();
2909+
if (IGF.IGM.Triple.isWindowsMSVCEnvironment() &&
2910+
getCallee().getRepresentation() ==
2911+
SILFunctionTypeRepresentation::CXXMethod &&
2912+
Args[1] == getCallee().getCXXMethodSelf()) {
2913+
// C++ methods in MSVC ABI pass `this` before the
2914+
// indirectly returned value.
2915+
std::swap(Args[0], Args[1]);
2916+
assert(!isa<llvm::UndefValue>(Args[1]));
2917+
}
29092918
SILFunctionConventions FnConv(CurCallee.getSubstFunctionType(),
29102919
IGF.getSILModule());
29112920

@@ -3276,6 +3285,37 @@ void CallEmission::emitToExplosion(Explosion &out, bool isOutlined) {
32763285
emitToMemory(temp, substResultTI, isOutlined);
32773286
return;
32783287
}
3288+
if (IGF.IGM.Triple.isWindowsMSVCEnvironment() &&
3289+
getCallee().getRepresentation() ==
3290+
SILFunctionTypeRepresentation::CXXMethod &&
3291+
substResultType.isVoid()) {
3292+
// Some C++ methods return a value but are imported as
3293+
// returning `Void` (e.g. `operator +=`). In this case
3294+
// we should allocate the correct temp indirect return
3295+
// value for it.
3296+
// FIXME: MSVC ABI hits this as it makes some SIL direct
3297+
// returns as indirect at IR layer, so fix this for MSVC
3298+
// first to get this into Swfit 5.9. However, then investigate
3299+
// if this could also apply to Itanium ABI too.
3300+
auto fnType = getCallee().getFunctionPointer().getFunctionType();
3301+
assert(fnType->getNumParams() > 1);
3302+
auto func = dyn_cast<llvm::Function>(
3303+
getCallee().getFunctionPointer().getRawPointer());
3304+
if (func) {
3305+
// `this` comes before the returned value under the MSVC ABI
3306+
// so return value is parameter #1.
3307+
assert(func->hasParamAttribute(1, llvm::Attribute::StructRet));
3308+
auto resultTy = func->getParamStructRetType(1);
3309+
auto temp = IGF.createAlloca(resultTy, Alignment(/*safe alignment*/ 16),
3310+
"indirect.result");
3311+
if (IGF.IGM.getLLVMContext().supportsTypedPointers()) {
3312+
temp = IGF.Builder.CreateElementBitCast(
3313+
temp, fnType->getParamType(1)->getNonOpaquePointerElementType());
3314+
}
3315+
emitToMemory(temp, substResultTI, isOutlined);
3316+
return;
3317+
}
3318+
}
32793319

32803320
StackAddress ctemp = substResultTI.allocateStack(IGF, substResultType,
32813321
"call.aggresult");
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-emit-irgen -I %t/Inputs -enable-experimental-cxx-interop -Xcc -std=c++17 %t/test.swift -module-name Test | %FileCheck %s
4+
5+
// REQUIRES: OS=windows-msvc
6+
7+
//--- Inputs/module.modulemap
8+
module MsvcUseVecIt {
9+
header "test.h"
10+
requires cplusplus
11+
}
12+
13+
//--- Inputs/test.h
14+
15+
#pragma once
16+
17+
class str {
18+
int x;
19+
};
20+
21+
class It {
22+
public:
23+
int x;
24+
25+
bool operator ==(const It &other) const ;
26+
bool operator !=(const It &other) const ;
27+
};
28+
29+
template<class T> class vec {
30+
int x;
31+
public:
32+
vec(const vec<T> &);
33+
~vec();
34+
35+
It begin() const;
36+
It end() const;
37+
};
38+
39+
using VecStr = vec<str>;
40+
41+
struct LoadableIntWrapper {
42+
int value;
43+
44+
LoadableIntWrapper operator+=(LoadableIntWrapper rhs) {
45+
value += rhs.value;
46+
return *this;
47+
}
48+
};
49+
50+
//--- test.swift
51+
52+
import MsvcUseVecIt
53+
54+
public func test(_ result: VecStr) -> CInt {
55+
let begin = result.__beginUnsafe()
56+
return begin.x
57+
}
58+
59+
// CHECK: swiftcc i32 @"$s4Test4testys5Int32VSo0014vecstr_yuJCataVF"({{.*}} %[[RESULT:.*]])
60+
// CHECK: call void @"?begin@?$vec@Vstr@@@@QEBA?AVIt@@XZ"(ptr %[[RESULT]], ptr sret{{.*}}
61+
62+
public func passTempForIndirectRetToVoidCall() {
63+
var lhs = LoadableIntWrapper(value: 2)
64+
let rhs = LoadableIntWrapper(value: 2)
65+
lhs += rhs
66+
}
67+
68+
// CHECK: void @"$sSo18LoadableIntWrapperV2peoiyyABz_ABtFZ"(ptr
69+
// CHECK: %[[OPRESULT:.*]] = alloca %struct.LoadableIntWrapper, align 16
70+
// CHECK: call void @"??YLoadableIntWrapper@@QEAA?AU0@U0@@Z"(ptr {{.*}}, ptr sret(%struct.LoadableIntWrapper) %[[OPRESULT]], i32

0 commit comments

Comments
 (0)