Skip to content

Commit 776ed12

Browse files
IRGen & Runtime: Use absolute function pointer for Wasm unconditionally
On some Harvard architectures like WebAssembly that allow sliding code and data address space offsets independently, it's impossible to make direct relative reference to code from data because the relative offset between them is not representable. Use absolute function references instead of relative ones on such targets.
1 parent 1242d93 commit 776ed12

27 files changed

+397
-101
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===--- CompactFunctionPointer.h - Compact Function Pointers ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Swift's runtime structures often use relative function pointers to reduce the
14+
// size of metadata and also to minimize load-time overhead in PIC.
15+
// This file defines pointer types whose size and interface are compatible with
16+
// the relative pointer types for targets that do not support relative references
17+
// to code from data.
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef SWIFT_ABI_COMPACTFUNCTIONPOINTER_H
21+
#define SWIFT_ABI_COMPACTFUNCTIONPOINTER_H
22+
23+
namespace swift {
24+
25+
/// A compact unconditional absolute function pointer that can fit in a 32-bit
26+
/// integer.
27+
/// As a trade-off compared to relative pointers, this has load-time overhead in PIC
28+
/// and is only available on 32-bit targets.
29+
template <typename T>
30+
class AbsoluteFunctionPointer {
31+
T *Pointer;
32+
static_assert(sizeof(T *) == sizeof(int32_t),
33+
"Function pointer must be 32-bit when using compact absolute pointer");
34+
35+
public:
36+
using PointerTy = T *;
37+
38+
PointerTy get() const & { return Pointer; }
39+
40+
operator PointerTy() const & { return this->get(); }
41+
42+
bool isNull() const & { return Pointer == nullptr; }
43+
44+
/// Resolve a pointer from a `base` pointer and a value loaded from `base`.
45+
template <typename BasePtrTy, typename Value>
46+
static PointerTy resolve(BasePtrTy *base, Value value) {
47+
return reinterpret_cast<PointerTy>(value);
48+
}
49+
50+
template <typename... ArgTy>
51+
typename std::result_of<T *(ArgTy...)>::type operator()(ArgTy... arg) const {
52+
static_assert(std::is_function<T>::value,
53+
"T must be an explicit function type");
54+
return this->get()(std::forward<ArgTy>(arg)...);
55+
}
56+
};
57+
58+
// TODO(katei): Add another pointer structure for 64-bit targets and for efficiency on PIC
59+
60+
} // namespace swift
61+
62+
#endif // SWIFT_ABI_COMPACTFUNCTIONPOINTER_H

include/swift/ABI/Executor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ template <class AsyncSignature>
241241
class AsyncFunctionPointer {
242242
public:
243243
/// The function to run.
244-
RelativeDirectPointer<AsyncFunctionType<AsyncSignature>,
244+
TargetCompactFunctionPointer<InProcess, AsyncFunctionType<AsyncSignature>,
245245
/*nullable*/ false,
246246
int32_t> Function;
247247

include/swift/ABI/KeyPath.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ class KeyPathComponentHeader {
192192

193193
enum ComputedPropertyIDResolution {
194194
Resolved,
195+
ResolvedAbsolute,
195196
IndirectPointer,
196197
FunctionCall,
197198
};
@@ -214,6 +215,7 @@ class KeyPathComponentHeader {
214215
? _SwiftKeyPathComponentHeader_ComputedIDByVTableOffsetFlag : 0)
215216
| (hasArguments ? _SwiftKeyPathComponentHeader_ComputedHasArgumentsFlag : 0)
216217
| (resolution == Resolved ? _SwiftKeyPathComponentHeader_ComputedIDResolved
218+
: resolution == ResolvedAbsolute ? _SwiftKeyPathComponentHeader_ComputedIDResolvedAbsolute
217219
: resolution == IndirectPointer ? _SwiftKeyPathComponentHeader_ComputedIDUnresolvedIndirectPointer
218220
: resolution == FunctionCall ? _SwiftKeyPathComponentHeader_ComputedIDUnresolvedFunctionCall
219221
: (assert(false && "invalid resolution"), 0)));

include/swift/ABI/Metadata.h

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,20 @@ struct TargetMethodDescriptor {
502502
MethodDescriptorFlags Flags;
503503

504504
/// The method implementation.
505-
TargetRelativeDirectPointer<Runtime, void> Impl;
505+
union {
506+
TargetCompactFunctionPointer<Runtime, void> Impl;
507+
TargetRelativeDirectPointer<Runtime, void> AsyncImpl;
508+
};
506509

507510
// TODO: add method types or anything else needed for reflection.
511+
512+
void *getImpl() const {
513+
if (Flags.isAsync()) {
514+
return AsyncImpl.get();
515+
} else {
516+
return Impl.get();
517+
}
518+
}
508519
};
509520

510521
using MethodDescriptor = TargetMethodDescriptor<InProcess>;
@@ -574,7 +585,20 @@ struct TargetMethodOverrideDescriptor {
574585
TargetRelativeMethodDescriptorPointer<Runtime> Method;
575586

576587
/// The implementation of the override.
577-
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> Impl;
588+
union {
589+
TargetCompactFunctionPointer<Runtime, void, /*nullable*/ true> Impl;
590+
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> AsyncImpl;
591+
};
592+
593+
void *getImpl() const {
594+
auto *baseMethod = Method.get();
595+
assert(baseMethod && "no base method");
596+
if (baseMethod->Flags.isAsync()) {
597+
return AsyncImpl.get();
598+
} else {
599+
return Impl.get();
600+
}
601+
}
578602
};
579603

580604
/// Header for a class vtable override descriptor. This is a variable-sized
@@ -1519,7 +1543,18 @@ struct TargetProtocolRequirement {
15191543
// TODO: name, type
15201544

15211545
/// The optional default implementation of the protocol.
1522-
RelativeDirectPointer<void, /*nullable*/ true> DefaultImplementation;
1546+
union {
1547+
TargetCompactFunctionPointer<Runtime, void, /*nullable*/ true> DefaultFuncImplementation;
1548+
TargetRelativeDirectPointer<Runtime, void, /*nullable*/ true> DefaultImplementation;
1549+
};
1550+
1551+
void *getDefaultImplementation() const {
1552+
if (Flags.isFunctionImpl()) {
1553+
return DefaultFuncImplementation.get();
1554+
} else {
1555+
return DefaultImplementation.get();
1556+
}
1557+
}
15231558
};
15241559

15251560
using ProtocolRequirement = TargetProtocolRequirement<InProcess>;
@@ -1808,7 +1843,18 @@ using GenericBoxHeapMetadata = TargetGenericBoxHeapMetadata<InProcess>;
18081843
template <typename Runtime>
18091844
struct TargetResilientWitness {
18101845
TargetRelativeProtocolRequirementPointer<Runtime> Requirement;
1811-
RelativeDirectPointer<void> Witness;
1846+
union {
1847+
TargetRelativeDirectPointer<Runtime, void> Impl;
1848+
TargetCompactFunctionPointer<Runtime, void> FuncImpl;
1849+
};
1850+
1851+
void *getWitness(ProtocolRequirementFlags flags) const {
1852+
if (flags.isFunctionImpl()) {
1853+
return FuncImpl.get();
1854+
} else {
1855+
return Impl.get();
1856+
}
1857+
}
18121858
};
18131859
using ResilientWitness = TargetResilientWitness<InProcess>;
18141860

@@ -1871,10 +1917,13 @@ struct TargetGenericWitnessTable {
18711917
uint16_t WitnessTablePrivateSizeInWordsAndRequiresInstantiation;
18721918

18731919
/// The instantiation function, which is called after the template is copied.
1874-
RelativeDirectPointer<void(TargetWitnessTable<Runtime> *instantiatedTable,
1875-
const TargetMetadata<Runtime> *type,
1876-
const void * const *instantiationArgs),
1877-
/*nullable*/ true> Instantiator;
1920+
TargetCompactFunctionPointer<
1921+
Runtime,
1922+
void(TargetWitnessTable<Runtime> *instantiatedTable,
1923+
const TargetMetadata<Runtime> *type,
1924+
const void *const *instantiationArgs),
1925+
/*nullable*/ true>
1926+
Instantiator;
18781927

18791928
using PrivateDataType = void *[swift::NumGenericMetadataPrivateDataWords];
18801929

@@ -2606,12 +2655,12 @@ using MetadataCompleter =
26062655
template <typename Runtime>
26072656
struct TargetGenericMetadataPattern {
26082657
/// The function to call to instantiate the template.
2609-
TargetRelativeDirectPointer<Runtime, MetadataInstantiator>
2658+
TargetCompactFunctionPointer<Runtime, MetadataInstantiator>
26102659
InstantiationFunction;
26112660

26122661
/// The function to call to complete the instantiation. If this is null,
26132662
/// the instantiation function must always generate complete metadata.
2614-
TargetRelativeDirectPointer<Runtime, MetadataCompleter, /*nullable*/ true>
2663+
TargetCompactFunctionPointer<Runtime, MetadataCompleter, /*nullable*/ true>
26152664
CompletionFunction;
26162665

26172666
/// Flags describing the layout of this instantiation pattern.
@@ -2718,10 +2767,10 @@ struct TargetGenericClassMetadataPattern final :
27182767
using TargetGenericMetadataPattern<Runtime>::PatternFlags;
27192768

27202769
/// The heap-destructor function.
2721-
TargetRelativeDirectPointer<Runtime, HeapObjectDestroyer> Destroy;
2770+
TargetCompactFunctionPointer<Runtime, HeapObjectDestroyer> Destroy;
27222771

27232772
/// The ivar-destructor function.
2724-
TargetRelativeDirectPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
2773+
TargetCompactFunctionPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
27252774
IVarDestroyer;
27262775

27272776
/// The class flags.
@@ -2922,7 +2971,7 @@ class MetadataAccessFunction {
29222971
template <typename Runtime>
29232972
struct TargetForeignMetadataInitialization {
29242973
/// The completion function. The pattern will always be null.
2925-
TargetRelativeDirectPointer<Runtime, MetadataCompleter, /*nullable*/ true>
2974+
TargetCompactFunctionPointer<Runtime, MetadataCompleter, /*nullable*/ true>
29262975
CompletionFunction;
29272976
};
29282977

@@ -2967,14 +3016,14 @@ struct TargetResilientClassMetadataPattern {
29673016
///
29683017
/// If this is null, the runtime instead calls swift_relocateClassMetadata(),
29693018
/// passing in the class descriptor and this pattern.
2970-
TargetRelativeDirectPointer<Runtime, MetadataRelocator, /*nullable*/ true>
3019+
TargetCompactFunctionPointer<Runtime, MetadataRelocator, /*nullable*/ true>
29713020
RelocationFunction;
29723021

29733022
/// The heap-destructor function.
2974-
TargetRelativeDirectPointer<Runtime, HeapObjectDestroyer> Destroy;
3023+
TargetCompactFunctionPointer<Runtime, HeapObjectDestroyer> Destroy;
29753024

29763025
/// The ivar-destructor function.
2977-
TargetRelativeDirectPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
3026+
TargetCompactFunctionPointer<Runtime, ClassIVarDestroyer, /*nullable*/ true>
29783027
IVarDestroyer;
29793028

29803029
/// The class flags.
@@ -3018,7 +3067,7 @@ struct TargetSingletonMetadataInitialization {
30183067

30193068
/// The completion function. The pattern will always be null, even
30203069
/// for a resilient class.
3021-
TargetRelativeDirectPointer<Runtime, MetadataCompleter>
3070+
TargetCompactFunctionPointer<Runtime, MetadataCompleter>
30223071
CompletionFunction;
30233072

30243073
bool hasResilientClassPattern(
@@ -3047,7 +3096,7 @@ struct TargetCanonicalSpecializedMetadatasListEntry {
30473096

30483097
template <typename Runtime>
30493098
struct TargetCanonicalSpecializedMetadataAccessorsListEntry {
3050-
TargetRelativeDirectPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false> accessor;
3099+
TargetCompactFunctionPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false> accessor;
30513100
};
30523101

30533102
template <typename Runtime>
@@ -3067,7 +3116,7 @@ class TargetTypeContextDescriptor
30673116
/// The function type here is a stand-in. You should use getAccessFunction()
30683117
/// to wrap the function pointer in an accessor that uses the proper calling
30693118
/// convention for a given number of arguments.
3070-
TargetRelativeDirectPointer<Runtime, MetadataResponse(...),
3119+
TargetCompactFunctionPointer<Runtime, MetadataResponse(...),
30713120
/*Nullable*/ true> AccessFunctionPtr;
30723121

30733122
/// A pointer to the field descriptor for the type, if any.
@@ -3332,7 +3381,7 @@ class TargetClassDescriptor final
33323381
using MetadataListEntry =
33333382
TargetCanonicalSpecializedMetadatasListEntry<Runtime>;
33343383
using MetadataAccessor =
3335-
TargetRelativeDirectPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false>;
3384+
TargetCompactFunctionPointer<Runtime, MetadataResponse(MetadataRequest), /*Nullable*/ false>;
33363385
using MetadataAccessorListEntry =
33373386
TargetCanonicalSpecializedMetadataAccessorsListEntry<Runtime>;
33383387
using MetadataCachingOnceToken =
@@ -4133,12 +4182,23 @@ class DynamicReplacementDescriptor {
41334182
DynamicReplacementKey *
41344183
__ptrauth_swift_dynamic_replacement_key>>
41354184
replacedFunctionKey;
4136-
RelativeDirectPointer<void, false> replacementFunction;
4185+
union {
4186+
TargetCompactFunctionPointer<InProcess, void, false> replacementFunction;
4187+
TargetRelativeDirectPointer<InProcess, void, false> replacementAsyncFunction;
4188+
};
41374189
RelativeDirectPointer<DynamicReplacementChainEntry, false> chainEntry;
41384190
uint32_t flags;
41394191

41404192
enum : uint32_t { EnableChainingMask = 0x1 };
41414193

4194+
void *getReplacementFunction() const {
4195+
if (replacedFunctionKey->isAsync()) {
4196+
return replacementAsyncFunction.get();
4197+
} else {
4198+
return replacementFunction.get();
4199+
}
4200+
}
4201+
41424202
public:
41434203
/// Enable this replacement by changing the function's replacement chain's
41444204
/// root entry.

include/swift/ABI/MetadataValues.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,21 @@ class ProtocolRequirementFlags {
603603

604604
int_type getIntValue() const { return Value; }
605605

606+
/// Is the method implementation is represented as a native function pointer?
607+
bool isFunctionImpl() const {
608+
switch (getKind()) {
609+
case ProtocolRequirementFlags::Kind::Method:
610+
case ProtocolRequirementFlags::Kind::Init:
611+
case ProtocolRequirementFlags::Kind::Getter:
612+
case ProtocolRequirementFlags::Kind::Setter:
613+
case ProtocolRequirementFlags::Kind::ReadCoroutine:
614+
case ProtocolRequirementFlags::Kind::ModifyCoroutine:
615+
return !isAsync();
616+
default:
617+
return false;
618+
}
619+
}
620+
606621
enum : uintptr_t {
607622
/// Bit used to indicate that an associated type witness is a pointer to
608623
/// a mangled name (vs. a pointer to metadata).

include/swift/ABI/TargetLayout.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include "swift/Runtime/Config.h"
3333
#include "swift/Basic/RelativePointer.h"
34+
#include "swift/ABI/CompactFunctionPointer.h"
3435

3536
namespace swift {
3637

@@ -100,6 +101,14 @@ struct InProcess {
100101

101102
template <typename T, bool Nullable = true>
102103
using RelativeDirectPointer = RelativeDirectPointer<T, Nullable>;
104+
105+
template <typename T, bool Nullable = true, typename Offset = int32_t>
106+
#if SWIFT_COMPACT_ABSOLUTE_FUNCTION_POINTER
107+
using CompactFunctionPointer = AbsoluteFunctionPointer<T>;
108+
#else
109+
using CompactFunctionPointer =
110+
swift::RelativeDirectPointer<T, Nullable, Offset>;
111+
#endif
103112
};
104113

105114
/// Represents a pointer in another address space.
@@ -157,6 +166,9 @@ struct External {
157166

158167
template <typename T, bool Nullable = true>
159168
using RelativeDirectPointer = int32_t;
169+
170+
template <typename T, bool Nullable = true, typename Offset = int32_t>
171+
using CompactFunctionPointer = int32_t;
160172
};
161173

162174
template <typename Runtime, typename T>
@@ -182,6 +194,12 @@ template <typename Runtime, typename Pointee, bool Nullable = true>
182194
using TargetRelativeIndirectablePointer
183195
= typename Runtime::template RelativeIndirectablePointer<Pointee,Nullable>;
184196

197+
template <typename Runtime, typename Pointee, bool Nullable = true,
198+
typename Offset = int32_t>
199+
using TargetCompactFunctionPointer =
200+
typename Runtime::template CompactFunctionPointer<Pointee, Nullable,
201+
Offset>;
202+
185203
} // end namespace swift
186204

187205
#endif

include/swift/AST/IRGenOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ class IRGenOptions {
320320
unsigned LazyInitializeProtocolConformances : 1;
321321
unsigned IndirectAsyncFunctionPointer : 1;
322322

323+
/// Use absolute function references instead of relative ones.
324+
/// Mainly used on WebAssembly, that doesn't support relative references
325+
/// to code from data.
326+
unsigned CompactAbsoluteFunctionPointer : 1;
327+
323328
/// Normally if the -read-legacy-type-info flag is not specified, we look for
324329
/// a file named "legacy-<arch>.yaml" in SearchPathOpts.RuntimeLibraryPath.
325330
/// Passing this flag completely disables this behavior.

0 commit comments

Comments
 (0)