Function to unsafe pointer and back


(Lou Zell) #1

Hi all,
In both examples below I'm attempting to call a function from the
function's address. The first example works but the second doesn't. I
suspect I'm missing something fundamental about function invocations. Can
someone explain why the second example fails? First:

func doNothing() {}
func call(_ ptr: UnsafePointer<() -> ()>) {
    ptr.pointee()
}
var x = doNothing
call(&x)

There's no crash there. Everything works as expected - in that it does
nothing. So far so good, but this crashes:

doNothing
$R16: () -> () = 0x0000000100561060 $__lldb_expr2`__lldb_expr_1.doNothing
() -> () at repl.swift:1
let ptr = UnsafePointer<() -> ()>(bitPattern: 0x0000000100561060)
ptr!.pointee()

Crashes! Also, I notice that the address of the pointee changes each time
I ask for it:

ptr!.pointee
$R17: () -> () = 0x0000000100567440 $__lldb_expr50`partial apply forwarder
for reabstraction thunk helper from @callee_owned (@in ()) -> (@out ()) to
@callee_owned () -> () at repl49.swift
ptr!.pointee
$R18: () -> () = 0x0000000100567740 $__lldb_expr52`partial apply forwarder
for reabstraction thunk helper from @callee_owned (@in ()) -> (@out ()) to
@callee_owned () -> () at repl51.swift
Questions:
1. Why does the first example succeed but the second one fail?
2. Why does the pointee address change on sequential calls?
3. What is a reabstraction thunk helper (do I want to know :))

I know it's a lot to ask, if there's something you would rather point me to
to read that's a-ok!

Thanks,
Lou


(Quinn “The Eskimo!”) #2

This makes for a great read.

<https://medium.com/@slavapestov/how-to-talk-to-your-kids-about-sil-type-use-6b45f7595f43>

As to your real question, what’s your high-level goal? Swift doesn’t really do pointers to functions [1] but it does provide lots of other excellent ‘treat code as data’ features. If you can explain more about your goal, perhaps we can direct you to a better path.

Share and Enjoy

···

On 1 Sep 2016, at 01:41, Lou Zell via swift-users <swift-users@swift.org> wrote:

3. What is a reabstraction thunk helper (do I want to know :))

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

[1] At least not natively. You can use @convention(c) to interact with C APIs that require a function pointer.


(Lou Zell) #3

As to your real question, what’s your high-level goal? Swift doesn’t
really do pointers to functions [1] but it does provide lots of other
excellent ‘treat code as data’ features. If you can explain more about
your goal, perhaps we can direct you to a better path.


Curiosity got the better of me on this one - there's no higher level goal
other than improved understanding. I was playing around with function
currying in the repl and that's what lead me to those experiments.

The article, and the preceding one in the series, has plenty for me to work
with. Thank you!


(Andrew Trick) #4

That’s an awesome article, but I don’t think you need to understand any of it! I’m not an expert in this either, but here’s what I can see from your code...

It looks like you’re trying to capture the address of a function that was JIT’d for the purpose of evaluating a statement in the repl. That address might not be valid in the next statement.

Also, in the first case the UnsafePointer points to local memory that holds the function value. In the second case, you’re trying to load a function value from the address of the function body. So a level of indirection is missing.

You can’t take an address to a function in Swift. You can assign a variable to the function, as you did in the first case:

var x = doNothing

And view the address of that variable as an argument, only for the duration of the call, as you did in the first case:

call(&x)

-Andy

···

On Sep 1, 2016, at 12:37 PM, Lou Zell via swift-users <swift-users@swift.org> wrote:

As to your real question, what’s your high-level goal? Swift doesn’t really do pointers to functions [1] but it does provide lots of other excellent ‘treat code as data’ features. If you can explain more about your goal, perhaps we can direct you to a better path.

Curiosity got the better of me on this one - there's no higher level goal other than improved understanding. I was playing around with function currying in the repl and that's what lead me to those experiments.

The article, and the preceding one in the series, has plenty for me to work with. Thank you!


(Jordan Rose) #5

I feel like there’s still one last piece missing here, which is that in Swft a "function-typed value” is not the same as a “function pointer” in C. Because a function value might have come from a closure, it might include captured state…so the representation of a closure is not the same as a representation of a plain function.

Because of that, the address you’re seeing isn’t the address of the function in memory; it’s the address of the temporary allocation used to represent “this global function and no associated state”. That’s why you can’t just form a pointer with that bit-pattern and expect it to work.

If you really need something compatible with a C function, you can use the type '@convention(c) () -> Void’ (or whatever). Note the lack of UnsafePointer here—the reference-ness is baked in in Swift. Even then, though, you can’t rely on the function having the same address every time you run the program (at least on macOS), because of address space randomization—the offset at which your code is loaded will be different on each run. There shouldn’t be any need to do this anyway.

(One place we are still weak is in converting between C function references and UnsafeRawPointer—there’s no dedicated API to do this. I’m not sure we have a recommendation in the rare cases when this is necessary. Andy?)

Hope you’ve gotten some useful information between the three of us. :slight_smile:
Jordan

···

On Sep 1, 2016, at 13:48, Andrew Trick via swift-users <swift-users@swift.org> wrote:

On Sep 1, 2016, at 12:37 PM, Lou Zell via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

As to your real question, what’s your high-level goal? Swift doesn’t really do pointers to functions [1] but it does provide lots of other excellent ‘treat code as data’ features. If you can explain more about your goal, perhaps we can direct you to a better path.

Curiosity got the better of me on this one - there's no higher level goal other than improved understanding. I was playing around with function currying in the repl and that's what lead me to those experiments.

The article, and the preceding one in the series, has plenty for me to work with. Thank you!

That’s an awesome article, but I don’t think you need to understand any of it! I’m not an expert in this either, but here’s what I can see from your code...

It looks like you’re trying to capture the address of a function that was JIT’d for the purpose of evaluating a statement in the repl. That address might not be valid in the next statement.

Also, in the first case the UnsafePointer points to local memory that holds the function value. In the second case, you’re trying to load a function value from the address of the function body. So a level of indirection is missing.

You can’t take an address to a function in Swift. You can assign a variable to the function, as you did in the first case:

var x = doNothing

And view the address of that variable as an argument, only for the duration of the call, as you did in the first case:

call(&x)


(Andrew Trick) #6

Good question. I don’t know how to do off the top of my head that without passing a function typed value off to C as a function pointer and returning a void*.

-Andy

···

On Sep 2, 2016, at 9:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:

(One place we are still weak is in converting between C function references and UnsafeRawPointer—there’s no dedicated API to do this. I’m not sure we have a recommendation in the rare cases when this is necessary. Andy?)


(Lou Zell) #7

Also, in the first case the UnsafePointer points to local memory that
holds the function value. In the second case, you’re trying to load a
function value from the address of the function body.

Beginning to see...

I feel like there’s still one last piece missing here, which is that in
Swft a "function-typed value” is not the same as a “function pointer” in C.
Because a function value might have come from a closure, it might include
captured state…so the representation of a closure is not the same as a
representation of a plain function.

Because of *that*, the address you’re seeing isn’t the address of the
function in memory; it’s the address of the temporary allocation used to
represent “this global function and no associated state”. That’s why you
can’t just form a pointer with that bit-pattern and expect it to work.

Seeing!

Hope you’ve gotten some useful information between the three of us. :slight_smile:

That's the trifecta of support right there! Thank you all!

I ran a new little experiment for the curious:

var a: () -> () = {
    print("foo!")
}
withUnsafePointer(&a) {$0}
$R0: UnsafePointer<() -> ()> = 0x00007fff5fbffce8

UnsafePointer<() -> ()>(bitPattern: 0x00007fff5fbffce8)!.pointee()
// foo!
UnsafePointer<() -> ()>(bitPattern: 0x0000000100561080)!.pointee()
// Crashes as expected!

And:

typealias MyType = @convention(c) () -> ()
var b: MyType = {
    print("bar!")
}
b: __lldb_expr_10.MyType = 0x000000010056b150 $<snip> at repl12.swift
withUnsafePointer(&b) {$0}
$R1: UnsafePointer<__lldb_expr_10.MyType> = 0x0000000100564c80

UnsafePointer<MyType>(bitPattern: 0x0000000100564c80)!.pointee()
// bar!
UnsafePointer<MyType>(bitPattern: 0x000000010056b150)!.pointee()
// Crash!

I am surprised to find here that the addresses are different, albeit much
closer in space than the first example. I suppose there's still some swift
machinery at work to make a function type declared as @convention(c) to
_behave_ as a function pointer. Cool.

Thanks again Quinn, Andy, Jordan!

···

On Thu, Sep 1, 2016 at 1:48 PM, Andrew Trick <atrick@apple.com> wrote:
On Fri, Sep 2, 2016 at 9:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:
a: () -> () = 0x0000000100561080 $<snip> at repl2.swift