in reply to Understanding Devel::Peek output

I'll be referring to this PDF, Reini Urban's PerlGuts Illustrated. It contains all the information needed to answer your question, but I'll arrange in the information in a more convenient format here.


There isn't just one type of scalar. See the graph at the bottom of the first page of the linked document. The ident that follows "SV =" indicates the type of the scalar. In this case, we have a SVt_IV.

The type of scalar controls what kind of values can be stored in that scalar. In a SVt_IV, one can store an IV (signed int) or a UV (unsigned int), but not a string or floating point number.

So what happens if you want to store a string inside the scalar? Well, Perl must first upgrade the scalar to a type that can handle the string. In this case, it will switch to a SVt_PVIV which can store a string in addition to a IV or UV.

$ perl -MDevel::Peek -e'my $x = 123; Dump($x); $x = "abc"; Dump($x);' SV = IV(0x2764760) at 0x2764770 REFCNT = 1 FLAGS = (IOK,pIOK) IV = 123 SV = PVIV(0x2766c38) at 0x2764770 REFCNT = 1 FLAGS = (POK,IsCOW,pPOK) IV = 123 PV = 0x27c1258 "abc"\0 CUR = 3 LEN = 10 COW_REFCNT = 1

Tangent: Note that while the upgraded scalar can hold both a string and an IV/UV, it only contains a string because POK is set but not IOK. In other words, while the slot for an IV exists (and holds the old IV value), the slot isn't storing any part of the scalar's value at this time because of the flags.


The scalar is potentially referenced by multiple pointers, so how does Perl manage to upgrade the scalar to a larger one without invalidating those pointers? It does so by using two memory blocks to represent a scalar.

The second address in the dump is the address of the head. This is what the various pointers reference. As you can see, the address of the head didn't change when the scalar was upgraded.

Inside the head is a pointer to the second block, called the body. This is the first address in the dump. Upgrading a scalar upgrades a field in the head indicating the type of the scalar, and it replaces this block with a larger one.


Now, let's look at the 64-bit example.

Memory allocation is relatively expensive, so SVt_IV and SVt_RV utilize a hack to avoid memory allocation.

One would expects a SVt_IV scalar to look like this:

Head Body +---------------+ +---------------+ | Ptr to body -------> | IV/UV slot | +---------------+ +---------------+ | Ref count | +---------------+ | Flags & type | +---------------+ | Unused | +---------------+

Instead, it looks like this:

Head Body +---------------+ +---------------+ | Ptr to body -------> | Unused | +---------------+ +---------------+ | Ref count | | Unused | +---------------+ +---------------+ | Flags & type | | Unused | +---------------+ +---------------+ | Unused | | Unused | +---------------+ +---------------+ | IV/UV slot | +---------------+

Buy why? Perl gets (probably significant) performance gains by keeping the IV slot at the same offset into the body of a scalar regardless of the type of the scalar.

But I talked of a hack to avoid allocating a second memory block. We can avoid a memory allocation by overlapping both blocks!

+ - - - - - - - + Unallocated <--+ Head +---------------+ | | Ptr to body -------+ +---------------+ | Ref count | +---------------+ | Flags & type | +---------------+ | IV/UV slot | +---------------+

Now we have a single memory block that points to itself. This is found at the very top of page 12 of the linked document.


Finally, the answer to your question.

The size of some fields in the head and body are independent of architecture (especially since you set the integer size to 64-bits for both builds), and the size of other fields in the head and body are dependent of architecture (e.g. the size of a pointer).

The difference in the offset is based on differences in the total size of the head and body in the two builds.

Replies are listed 'Best First'.
Re^2: Understanding Devel::Peek output
by syphilis (Bishop) on Nov 30, 2020 at 07:23 UTC
    The difference in the offset is based on differences in the total size of the head and body in the two builds.

    Yep - thanks ikegami and dave_the_m.
    In the course of pondering the reason that #18363 was afflicting only x64 -Duselongdouble builds, I was idly doing some Devel::Peek::Dump() operations when I noticed the puzzling behaviour.
    I couldn't think of a reason that the SvIV should be exhibiting the differing behaviour between the two architectures ... but, as you've pointed out, ptrsize will change. (Another one I can think of is sizesize.)

    Cheers,
    Rob