5

This is a follow-up question to "How to declare native array of fixed size in Perl 6?".

In that question it was discussed how to incorporate an array of a fixed size into a CStruct. In this answer it was suggested to use HAS to inline a CArray in the CStruct. When I tested this idea, I ran into some strange behavior that could not be resolved in the comments section below the question, so I decided to write it up as a new question. Here is is my C test library code:

slib.c:

#include <stdio.h>

struct myStruct
{
    int A; 
    int B[3];
    int C;
};

void use_struct (struct myStruct *s) {
    printf("sizeof(struct myStruct): %ld\n", sizeof( struct myStruct ));
    printf("sizeof(struct myStruct *): %ld\n", sizeof( struct myStruct *));
    printf("A = %d\n", s->A);
    printf("B[0] = %d\n", s->B[0]);
    printf("B[1] = %d\n", s->B[1]);
    printf("B[2] = %d\n", s->B[2]);
    printf("C = %d\n", s->C);
}

To generate a shared library from this i used:

gcc -c -fpic slib.c
gcc -shared -o libslib.so slib.o

Then, the Perl 6 code:

p.p6:

use v6;
use NativeCall;

class myStruct is repr('CStruct') {
    has int32 $.A is rw;
    HAS int32 @.B[3] is CArray is rw;
    has int32 $.C is rw;
}

sub use_struct(myStruct $s) is native("./libslib.so") { * };

my $s = myStruct.new();
$s.A = 1;
$s.B[0] = 2;
$s.B[1] = 3;
$s.B[2] = 4;
$s.C = 5;
say "Expected size of Perl 6 struct: ", (nativesizeof(int32) * 5);
say "Actual size of Perl 6 struct: ", nativesizeof( $s );
say 'Number of elements of $s.B: ', $s.B.elems;
say "B[0] = ", $s.B[0];
say "B[1] = ", $s.B[1];
say "B[2] = ", $s.B[2];
say "Calling library function..";
say "--------------------------";
use_struct( $s );

The output from the script is:

Expected size of Perl 6 struct: 20
Actual size of Perl 6 struct: 24
Number of elements of $s.B: 3
B[0] = 2
B[1] = 3
B[2] = 4
Calling library function..
--------------------------
sizeof(struct myStruct): 20
sizeof(struct myStruct *): 8
A = 1
B[0] = 0         # <-- Expected 2
B[1] = 653252032 # <-- Expected 3
B[2] = 22030     # <-- Expected 4
C = 5

Questions:

  • Why does nativesizeof( $s ) give 24 (and not the expected value of 20)?

  • Why is the content of the array B in the structure not as expected when printed from the C function?

Note:

I am using Ubuntu 18.04 and Perl 6 Rakudo version 2018.04.01, but have also tested with version 2018.05

Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • 2
    It's obviously aligning the data to 64 bit, as removing `$.A` reduces the struct by 8 bits. – Brad Gilbert Jun 09 '18 at 19:39
  • 2
    @BradGilbert Yes, good observation! If I remove `$.A` from the struct, the size of reduces with 8 bytes from 24 bytes to 16 bytes, while I would expect a reduction of only 4 bytes, since `$.A` is an `uint32`. – Håkon Hægland Jun 09 '18 at 19:47
  • 2
    You can also add a int32 before `$.B` and it doesn't change the size. I think this was overlooked when `HAS … is CArray` was added. – Brad Gilbert Jun 09 '18 at 20:09
  • 1
    @BradGilbert Yes adding `has int32 $.D is rw` before the declaration of `@.B` does not change the size. At least it seems the four the extra alignment bytes is added at the end of the struct, so the fields in the struct are not displaced. And yes, the extra alignment bytes seems related to the `CArray`, since replacing the `CArray` with 3 `int32`'s gives a `nativesizeof( $s )` of 20 and not 24 as when having the `CArray` – Håkon Hægland Jun 10 '18 at 05:20
  • When I run this, `B[1]` changes every time, and the other are always equal to 0. – jjmerelo Jun 10 '18 at 07:08
  • Could it be related to this bug? http://grokbase.com/t/perl/perl6-compiler/15bjs2nnt9/perl-126675-bug-moar-nativecall-evaluating-the-size-of-a-structure-is-wrongly-done Although it's marked as resolved. – jjmerelo Jun 10 '18 at 07:29
  • 1
    @jjmerelo Interesting. Tobias, who fixed that bug, (with a test, as always, so it should not have regressed) is also the one who wrote the new code that landed in 2018.05 to allocate an inline CArray. He comes and goes, sometimes away for many months at a time. I've .tell'd him with links on #perl6-dev, and nudged timotimo / others. http://colabti.org/irclogger/irclogger_log/perl6-dev?date=2018-06-10#l66 – raiph Jun 10 '18 at 12:00

1 Answers1

5

Your code is correct. I just fixed that bug in MoarVM, and added tests to rakudo, similar to your code:

In C:

typedef struct {
    int a;
    int b[3];
    int c;
} InlinedArrayInStruct;

In Perl 6:

class InlinedArrayInStruct is repr('CStruct') {
    has int32 $.a is rw;
    HAS int32 @.b[3] is CArray;
    has int32 $.c is rw;
}

See these patches: https://github.com/MoarVM/MoarVM/commit/ac3d3c76954fa3c1b1db14ea999bf3248c2eda1c https://github.com/rakudo/rakudo/commit/f8b79306cc1900b7991490eef822480f304a56d9

If you are not building rakudo (and also NQP and MoarVM) directly from latest source from github, you probably have to wait for the 2018.08 release that will appear here: https://rakudo.org/files

Tobias Leich
  • 300
  • 1
  • 7