How to create a static/constant `UnsafePointer`?

It fixes "0, 0, 0" to be desired "1, 2, 3" but it is still returning heap memory address to me on macOS + debug. (It returns the desired static data address on macOS + release, FTM).

Yeah because the array storage is allocated on the heap. The global just stores the pointer to the heap allocation.

In this case it seems like the object should be promotable to global storage, and subsequently that the one-time initializer and indirection in the global variable can also be optimized away. I'm not sure why that happens when the literal appears in a local context but not when assigned to a global variable.

1 Like

Can you elaborate?
Are we talking simply about making the array-as-static optimization a guarantee, or some new constructs that can be usable for this?
And does it include something for more complex cases, like static pointers inside the data (Could currently be represented with a tuple, but not with an array)?

This optimization does occur for globals in -O, but in -Onone this does not happen and the array is instead lazily initialized (meaning we do a heap alloc and the global just stores this pointer).

More specifically, the array object is still lazily initialized in -O, but we don't do a heap alloc, we have a dedicated static initialized object that we initialize the array with on program entry.

let array = [1, 2, 3]

gives me:

main:
        push    rax
        lea     rdi, [rip + (demangling cache variable for type metadata for Swift._ContiguousArrayStorage<Swift.Int>)]
        call    __swift_instantiateConcreteTypeFromMangledName
        lea     rsi, [rip + mainTv_+8]
        mov     rdi, rax
        call    swift_initStaticObject@PLT
        mov     qword ptr [rip + (output.array : [Swift.Int])], rax
        xor     eax, eax
        pop     rcx
        ret

output.array : [Swift.Int]:
        .zero   8

mainTv_:
        .zero   8
        .zero   16
        .quad   3
        .quad   6
        .quad   1
        .quad   2
        .quad   3
2 Likes

Wow, good to know... So it is still O(n) instead of O(1), even though it's not using heap... Do you know a way to distinguish static data memory mapped from executable and this static data initialised upon the app launch? Will check if could get that info from vmmap.

If you're referring to the initialization, that has been optimized into a global constant. That's the

in the assembly.

1 Like

Do you know why?

Looking at swift_initStaticObject, it seems to set a metadata pointer and initialise an immortal refcount. I'm assuming the refcount layout is ABI already so that could be done at compile-time. Can the metadata pointer not also be derived?

Yeah, I was looking for an alternative way of figuring that out, e.g. when you only have the pointer itself but not the code. It looks like region type (in vmmap) is a good indicator:

// C:
const char data[] = { ... } // __TEXT
char data[] = {...}         // __DATA
// Swift:
var data = (42, 24, ... )   // __DATA

Changing the above Swift's "var data" to "let" doesn't place the bytes in a readonly memory unlike what happens in C.

let bytes: (B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B) = (
    0x86, 0x73, 0xF1, 0xDE, 0x63, 0x19, 0x50, 0x27, 0x66, 0x39, 0x72, 0x04, 0x58, 0x29, 0x32, 0x46
)
let f = findBytes() // this searches app memory for the above byte pattern
let ptr = UnsafeMutablePointer<UInt8>(bitPattern: f)!
print(String(bytes.2, radix: 0x10)) // f1
ptr[2] = 0x42 // no crash here compared to C
print(String(bytes.2, radix: 0x10)) // 42
1 Like

Yeah, in this snippet the metadata pointer is being allocated at runtime. I thought we could specialize metadata, but it just seems to not be kicking in or maybe it only kicks in for the defining module to create.

1 Like