Data abort at start lvgl v7.0, compiled with full speed optimisation only

@embig71
I also though that 1-2 month ago :smiley: But turned out that itā€™s more complicated. E.g. we canā€™t be sure that a pointer to data has the same size as a pointer to a function. See here

If such tricky case exists, juggling with pointer this might be not fully portable.

Anyway, determining the pointer size in compile time would be easy but the resulting code would look ugly.

switch(sizeof(void*)) {
  case 2: {
     uint16_t * p = ...load prev. pointer's address...
    *p = new_prev;
    break;
  }
  case 4: {
     uint32t * p = ...load prev. pointer's address...
    *p = new_prev;
    break;
  }
  case 8: {
     uint64_t * p = ...load prev. pointer's address...
    *p = new_prev;
    break;
  }
}

A linked list node contains pointers to the same nodes. We turn to the members of this structure according to the index. That is, by pointer to pointer. Donā€™t they have the same size within the same architecture? The only case of a strange pointer size I saw for only one case: the harvard AVR architecture, with paged memory access, and even then, for const data. Are we looking at non-linear memory architectures (like x286)? Can we give at least one example of such a controller for which pointers to different data types would be presented differently? Data, not functions.

I think they can be left at the end as long as we ensure proper alignment. Iā€™m looking at the code and I think all it takes is an additional rounding clause in _lv_ll_init to make sure that n_size is aligned to sizeof(lv_ll_node_t *). It shouldnā€™t require any special code for dealing with pointer sizes.

Alignment of structure members to the size of the platform data bus, the compiler always does automatically (described in the standard). Only if you do not apply the #pragma pack () structure when declaring. By the way, what is the difference between a doubly linked list in lvgl and any other portable implementation in C / CPP? In my opinion nothing. And if you look at any of these implementations, then access to pointers to nodes is made as usual pointers, without any magic.

The only magic in LGLā€™s linked list is it supports variable node size (no only e.g. int data). Probably there are similar implementations though.

Iā€™ve updated the linked lists to remove the memcpys. It was easy with the get functions but needed to make a for loop for set functions.

Does it work now with the optimization?

Strange, but the behavior has not changed. Almost all functions in lv_ll.c have to be prefixed with #pragma optimize = speed no_cse. I donā€™t understand why this happens.

Rounding to the nearest larger divisible by 4 or 8 can be made easier.

#ifdef LV_MEM_ENV64
    /*Round the size up to 8*/
    node_size = (node_size + 8) & ~7UL;
#else
    /*Round the size up to 4*/
    node_size = (node_size + 4) & ~3UL;
#endif

I always keep a minimum of branching in the code. Especially in cycles.

I suspect itā€™s because of the non-trivial set functions. The optimizer might be having problems figuring out what memory is being touched.

@kisvegabor Why do we need a for loop for the setter functions? As long as the lv_ll_node_t structure is aligned to lv_ll_node_t *, it should be possible to just do something like act->prev = prev, right?

True, but it should be +7 and +3, isnā€™t it?

I was overcomplicating it. :sweat_smile:
Now no cycle or memcpy is used:

Hi.

Yes, of course, without thinking wrote.

With the latest changes, the project is built and works at any optimization level of the ARM IAR compiler. The problem can be considered resolved. Regards.

Awesome! Thank you very much!