1

I'm trying to cast a variable of type layout with a size of 4 bytes to an uint32. It is however not working.

A layout:

typedef layout "little-endian" {
    uint16 NSQA;
    uint16 NCQR;
} DW0_set_features_fid_07;

then

local DW0_set_features_fid_07 DW0 = {
    .NSQA = max_ioqpairs,
    .NCQR = max_ioqpairs
};
local uint32 four_bytes = cast(DW0, uint32);

memcpy does however work:

local DW0_set_features_fid_07 DW0 = {
    .NSQA = 64,
    .NCQR = 64
};
local uint32 four_bytes;
memcpy(&four_bytes, &DW0, sizeoftype(uint32));

Am I making a mistake here when using cast?

toffe
  • 63
  • 5

2 Answers2

1

Using bitfields instead of a layout works here:

local bitfields 32 {
    uint16 NCQA @ [31:16];
    uint16 NSQA @ [15:0];
} DW0;
local uint32 four_bytes = cast(DW0, uint32);

But it's still unclear to me why the approach with a layout does not work

toffe
  • 63
  • 5
0

A layout is in fact a struct; the declaration in your question is translated by DMLC into:

typedef struct {
    uint16_le_t NSQA;
    uint16_le_t NCQR;
} DW0_set_features_fid_07;

DML forbids casts from a struct to an integer, just like C does. Some reasons why this makes sense:

  • The structs may have weaker alignment requirements than the integer, so reading the struct as an int may be a misaligned access which is undefined behaviour (inherited from C by DMLC)
  • The strict-aliasing properties of C, again inherited by DMLC: The memory that backs DW0.NSQA is a uint16, so accessing it as a uint32 is undefined behaviour.
  • uint32 refers to an integer in host representation, so reading the data as-is would give a different result on a big-endian host.
  • There are rare cases where cast from struct to int could be implemented in a safe manner, like converting between struct { int x } and int, but in that case it's clearer anyway to avoid the cast by spelling out the .x.

The following reinterprets the data as an integer in a safe manner:

local uint32 four_bytes = *cast(&DW0, uint32_le_t *);
Erik Carstensen
  • 634
  • 4
  • 14