Skip to content

Commit 4077870

Browse files
authored
Merge pull request #67808 from hyp/eng/5.9/windows-67685
[cxx-interop][5.9] fix windows ABI convention for methods returning trivial records indirectly
2 parents eebec9d + e6f8d1b commit 4077870

File tree

5 files changed

+171
-16
lines changed

5 files changed

+171
-16
lines changed

lib/IRGen/GenCall.cpp

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,22 +1424,7 @@ void SignatureExpansion::expandExternalSignatureTypes() {
14241424
attrKindForExtending(signExt));
14251425
}
14261426

1427-
// If we return indirectly, that is the first parameter type.
1428-
if (returnInfo.isIndirect()) {
1429-
addIndirectResult();
1430-
}
1431-
1432-
size_t firstParamToLowerNormally = 0;
1433-
1434-
// Use a special IR type for passing block pointers.
1435-
if (FnType->getRepresentation() == SILFunctionTypeRepresentation::Block) {
1436-
assert(FI.arg_begin()[0].info.isDirect() &&
1437-
"block pointer not passed directly?");
1438-
ParamIRTypes.push_back(IGM.ObjCBlockPtrTy);
1439-
firstParamToLowerNormally = 1;
1440-
}
1441-
1442-
for (auto i : indices(paramTys).slice(firstParamToLowerNormally)) {
1427+
auto emitArg = [&](size_t i) {
14431428
auto &AI = FI.arg_begin()[i].info;
14441429

14451430
// Add a padding argument if required.
@@ -1528,8 +1513,35 @@ void SignatureExpansion::expandExternalSignatureTypes() {
15281513
case clang::CodeGen::ABIArgInfo::InAlloca:
15291514
llvm_unreachable("Need to handle InAlloca during signature expansion");
15301515
}
1516+
};
1517+
1518+
size_t firstParamToLowerNormally = 0;
1519+
1520+
// If we return indirectly, that is the first parameter type.
1521+
if (returnInfo.isIndirect()) {
1522+
if (IGM.Triple.isWindowsMSVCEnvironment() &&
1523+
FnType->getRepresentation() ==
1524+
SILFunctionTypeRepresentation::CXXMethod) {
1525+
// Windows ABI places `this` before the
1526+
// returned indirect values.
1527+
emitArg(0);
1528+
firstParamToLowerNormally = 1;
1529+
addIndirectResult();
1530+
} else
1531+
addIndirectResult();
1532+
}
1533+
1534+
// Use a special IR type for passing block pointers.
1535+
if (FnType->getRepresentation() == SILFunctionTypeRepresentation::Block) {
1536+
assert(FI.arg_begin()[0].info.isDirect() &&
1537+
"block pointer not passed directly?");
1538+
ParamIRTypes.push_back(IGM.ObjCBlockPtrTy);
1539+
firstParamToLowerNormally = 1;
15311540
}
15321541

1542+
for (auto i : indices(paramTys).slice(firstParamToLowerNormally))
1543+
emitArg(i);
1544+
15331545
if (returnInfo.isIndirect() || returnInfo.isIgnore()) {
15341546
ResultIRType = IGM.VoidTy;
15351547
} else {
@@ -2894,6 +2906,15 @@ void CallEmission::emitToUnmappedMemory(Address result) {
28942906
assert(LastArgWritten == 1 && "emitting unnaturally to indirect result");
28952907

28962908
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+
}
28972918
SILFunctionConventions FnConv(CurCallee.getSubstFunctionType(),
28982919
IGF.getSILModule());
28992920

@@ -3264,6 +3285,37 @@ void CallEmission::emitToExplosion(Explosion &out, bool isOutlined) {
32643285
emitToMemory(temp, substResultTI, isOutlined);
32653286
return;
32663287
}
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+
}
32673319

32683320
StackAddress ctemp = substResultTI.allocateStack(IGF, substResultType,
32693321
"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

test/Interop/Cxx/stdlib/Inputs/module.modulemap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ module StdPair {
2727
header "std-pair.h"
2828
requires cplusplus
2929
}
30+
31+
module MsvcUseVecIt {
32+
header "msvc-std-vector-it.h"
33+
requires cplusplus
34+
export *
35+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include <vector>
4+
#include <string>
5+
6+
struct FetchProvidersResult {
7+
std::vector<std::string> providers;
8+
};
9+
10+
inline FetchProvidersResult *f() noexcept {
11+
return new FetchProvidersResult();
12+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop -Xcc -std=c++17)
2+
3+
// REQUIRES: OS=windows-msvc
4+
// REQUIRES: executable_test
5+
6+
import MsvcUseVecIt
7+
8+
func test() -> Bool {
9+
let result = f()
10+
let begin = result.pointee.providers.__beginUnsafe()
11+
let end = result.pointee.providers.__endUnsafe()
12+
return begin != end
13+
}
14+
15+
let _ = test()

0 commit comments

Comments
 (0)