@@ -129,16 +129,7 @@ internal func _withUnsafeTemporaryAllocation<T, R>(
129129 let byteCount = _byteCountForTemporaryAllocation ( of: type, capacity: capacity)
130130
131131 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)
142133 }
143134
144135 // This declaration must come BEFORE Builtin.stackAlloc() or
@@ -170,6 +161,63 @@ internal func _withUnsafeTemporaryAllocation<T, R>(
170161#endif
171162}
172163
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+
173221// MARK: - Public interface
174222
175223/// Provides scoped access to a raw buffer pointer with the specified byte count
@@ -222,6 +270,34 @@ public func withUnsafeTemporaryAllocation<R>(
222270 }
223271}
224272
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+ return try withUnsafeTemporaryAllocation ( byteCount: byteCount, alignment: alignment, body)
297+ #endif
298+ }
299+ }
300+
225301/// Provides scoped access to a buffer pointer to memory of the specified type
226302/// and with the specified capacity.
227303///
@@ -272,3 +348,32 @@ public func withUnsafeTemporaryAllocation<T, R>(
272348 return try body ( buffer)
273349 }
274350}
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