@@ -129,16 +129,7 @@ internal func _withUnsafeTemporaryAllocation<T, R>(
129
129
let byteCount = _byteCountForTemporaryAllocation ( of: type, capacity: capacity)
130
130
131
131
guard _isStackAllocationSafe ( byteCount: byteCount, alignment: alignment) else {
132
- // Fall back to the heap. This may still be optimizable if escape analysis
133
- // shows that the allocated pointer does not escape.
134
- let buffer = UnsafeMutableRawPointer . allocate (
135
- byteCount: byteCount,
136
- alignment: alignment
137
- )
138
- defer {
139
- buffer. deallocate ( )
140
- }
141
- return try body ( buffer. _rawValue)
132
+ return try _fallBackToHeapAllocation ( byteCount: byteCount, alignment: alignment, body)
142
133
}
143
134
144
135
// This declaration must come BEFORE Builtin.stackAlloc() or
@@ -170,6 +161,63 @@ internal func _withUnsafeTemporaryAllocation<T, R>(
170
161
#endif
171
162
}
172
163
164
+ #if $BuiltinUnprotectedStackAlloc
165
+ @_alwaysEmitIntoClient @_transparent
166
+ internal func _withUnprotectedUnsafeTemporaryAllocation< T, R> (
167
+ of type: T . Type ,
168
+ capacity: Int ,
169
+ alignment: Int ,
170
+ _ body: ( Builtin . RawPointer ) throws -> R
171
+ ) rethrows -> R {
172
+ // How many bytes do we need to allocate?
173
+ let byteCount = _byteCountForTemporaryAllocation ( of: type, capacity: capacity)
174
+
175
+ guard _isStackAllocationSafe ( byteCount: byteCount, alignment: alignment) else {
176
+ return try _fallBackToHeapAllocation ( byteCount: byteCount, alignment: alignment, body)
177
+ }
178
+
179
+ // This declaration must come BEFORE Builtin.unprotectedStackAlloc() or
180
+ // Builtin.stackDealloc() will end up blowing it away (and the verifier will
181
+ // notice and complain.)
182
+ let result : R
183
+
184
+ let stackAddress = Builtin . unprotectedStackAlloc (
185
+ capacity. _builtinWordValue,
186
+ MemoryLayout < T > . stride. _builtinWordValue,
187
+ alignment. _builtinWordValue
188
+ )
189
+
190
+ // The multiple calls to Builtin.stackDealloc() are because defer { } produces
191
+ // a child function at the SIL layer and that conflicts with the verifier's
192
+ // idea of a stack allocation's lifetime.
193
+ do {
194
+ result = try body ( stackAddress)
195
+ Builtin . stackDealloc ( stackAddress)
196
+ return result
197
+
198
+ } catch {
199
+ Builtin . stackDealloc ( stackAddress)
200
+ throw error
201
+ }
202
+ }
203
+ #endif
204
+
205
+ @_alwaysEmitIntoClient @_transparent
206
+ internal func _fallBackToHeapAllocation< R> (
207
+ byteCount: Int ,
208
+ alignment: Int ,
209
+ _ body: ( Builtin . RawPointer ) throws -> R
210
+ ) rethrows -> R {
211
+ let buffer = UnsafeMutableRawPointer . allocate (
212
+ byteCount: byteCount,
213
+ alignment: alignment
214
+ )
215
+ defer {
216
+ buffer. deallocate ( )
217
+ }
218
+ return try body ( buffer. _rawValue)
219
+ }
220
+
173
221
// MARK: - Public interface
174
222
175
223
/// Provides scoped access to a raw buffer pointer with the specified byte count
@@ -222,6 +270,34 @@ public func withUnsafeTemporaryAllocation<R>(
222
270
}
223
271
}
224
272
273
+ /// Provides scoped access to a raw buffer pointer with the specified byte count
274
+ /// and alignment.
275
+ ///
276
+ /// This function is similar to `withUnsafeTemporaryAllocation`, except that it
277
+ /// doesn't trigger stack protection for the stack allocated memory.
278
+ @_alwaysEmitIntoClient @_transparent
279
+ public func _withUnprotectedUnsafeTemporaryAllocation< R> (
280
+ byteCount: Int ,
281
+ alignment: Int ,
282
+ _ body: ( UnsafeMutableRawBufferPointer ) throws -> R
283
+ ) rethrows -> R {
284
+ return try _withUnsafeTemporaryAllocation (
285
+ of: Int8 . self,
286
+ capacity: byteCount,
287
+ alignment: alignment
288
+ ) { pointer in
289
+ #if $BuiltinUnprotectedStackAlloc
290
+ let buffer = UnsafeMutableRawBufferPointer (
291
+ start: . init( pointer) ,
292
+ count: byteCount
293
+ )
294
+ return try body ( buffer)
295
+ #else
296
+ withUnsafeTemporaryAllocation ( byteCount: byteCount, alignment: alignment, body)
297
+ #endif
298
+ }
299
+ }
300
+
225
301
/// Provides scoped access to a buffer pointer to memory of the specified type
226
302
/// and with the specified capacity.
227
303
///
@@ -272,3 +348,32 @@ public func withUnsafeTemporaryAllocation<T, R>(
272
348
return try body ( buffer)
273
349
}
274
350
}
351
+
352
+ /// Provides scoped access to a buffer pointer to memory of the specified type
353
+ /// and with the specified capacity.
354
+ ///
355
+ /// This function is similar to `withUnsafeTemporaryAllocation`, except that it
356
+ /// doesn't trigger stack protection for the stack allocated memory.
357
+ @_alwaysEmitIntoClient @_transparent
358
+ public func _withUnprotectedUnsafeTemporaryAllocation< T, R> (
359
+ of type: T . Type ,
360
+ capacity: Int ,
361
+ _ body: ( UnsafeMutableBufferPointer < T > ) throws -> R
362
+ ) rethrows -> R {
363
+ return try _withUnprotectedUnsafeTemporaryAllocation (
364
+ of: type,
365
+ capacity: capacity,
366
+ alignment: MemoryLayout< T> . alignment
367
+ ) { pointer in
368
+ #if $BuiltinUnprotectedStackAlloc
369
+ Builtin . bindMemory ( pointer, capacity. _builtinWordValue, type)
370
+ let buffer = UnsafeMutableBufferPointer < T > (
371
+ start: . init( pointer) ,
372
+ count: capacity
373
+ )
374
+ return try body ( buffer)
375
+ #else
376
+ return try withUnsafeTemporaryAllocation ( of: type, capacity: capacity, body)
377
+ #endif
378
+ }
379
+ }
0 commit comments