struct-init: Fix zero initialization with multi-level designators
See the added testcase. When one used designators like .a.x to initialize sub-members of members, and didn't then initialize all of them the required zero-initialization of the other sub-members wasn't done. The fix also enables tiny code cleanups.
This commit is contained in:
		
							parent
							
								
									0757234560
								
							
						
					
					
						commit
						ff998900b1
					
				
					 3 changed files with 96 additions and 67 deletions
				
			
		
							
								
								
									
										126
									
								
								tccgen.c
									
										
									
									
									
								
							
							
						
						
									
										126
									
								
								tccgen.c
									
										
									
									
									
								
							| 
						 | 
					@ -5915,17 +5915,37 @@ static void parse_init_elem(int expr_type)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* put zeros for variable based init */
 | 
				
			||||||
 | 
					static void init_putz(Section *sec, unsigned long c, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (sec) {
 | 
				
			||||||
 | 
					        /* nothing to do because globals are already set to zero */
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        vpush_global_sym(&func_old_type, TOK_memset);
 | 
				
			||||||
 | 
					        vseti(VT_LOCAL, c);
 | 
				
			||||||
 | 
					#ifdef TCC_TARGET_ARM
 | 
				
			||||||
 | 
					        vpushs(size);
 | 
				
			||||||
 | 
					        vpushi(0);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					        vpushi(0);
 | 
				
			||||||
 | 
					        vpushs(size);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        gfunc_call(3);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* t is the array or struct type. c is the array or struct
 | 
					/* t is the array or struct type. c is the array or struct
 | 
				
			||||||
   address. cur_field is the pointer to the current
 | 
					   address. cur_field is the pointer to the current
 | 
				
			||||||
   value, for arrays the 'c' member contains the current start
 | 
					   field, for arrays the 'c' member contains the current start
 | 
				
			||||||
   index and the 'r' contains the end index (in case of range init).
 | 
					   index.  'size_only' is true if only size info is needed (only used
 | 
				
			||||||
   'size_only' is true if only size info is needed (only used
 | 
					   in arrays).  al contains the already initialized length of the
 | 
				
			||||||
   in arrays) */
 | 
					   current container (starting at c).  This returns the new length of that.  */
 | 
				
			||||||
static void decl_designator(CType *type, Section *sec, unsigned long c, 
 | 
					static int decl_designator(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
                            Sym **cur_field, int size_only)
 | 
					                           Sym **cur_field, int size_only, int al)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Sym *s, *f;
 | 
					    Sym *s, *f;
 | 
				
			||||||
    int index, index_last, align, l, nb_elems, elem_size;
 | 
					    int index, index_last, align, l, nb_elems, elem_size;
 | 
				
			||||||
 | 
					    unsigned long corig = c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elem_size = 0;
 | 
					    elem_size = 0;
 | 
				
			||||||
    nb_elems = 1;
 | 
					    nb_elems = 1;
 | 
				
			||||||
| 
						 | 
					@ -5947,10 +5967,8 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
	    if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
 | 
						    if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
 | 
				
			||||||
		index_last < index)
 | 
							index_last < index)
 | 
				
			||||||
	        tcc_error("invalid index");
 | 
						        tcc_error("invalid index");
 | 
				
			||||||
            if (cur_field) {
 | 
					            if (cur_field)
 | 
				
			||||||
		(*cur_field)->c = index;
 | 
							(*cur_field)->c = index_last;
 | 
				
			||||||
		(*cur_field)->r = index_last;
 | 
					 | 
				
			||||||
	    }
 | 
					 | 
				
			||||||
            type = pointed_type(type);
 | 
					            type = pointed_type(type);
 | 
				
			||||||
            elem_size = type_size(type, &align);
 | 
					            elem_size = type_size(type, &align);
 | 
				
			||||||
            c += index * elem_size;
 | 
					            c += index * elem_size;
 | 
				
			||||||
| 
						 | 
					@ -5995,6 +6013,10 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
            c += f->c;
 | 
					            c += f->c;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    /* must put zero in holes (note that doing it that way
 | 
				
			||||||
 | 
					       ensures that it even works with designators) */
 | 
				
			||||||
 | 
					    if (!size_only && c - corig > al)
 | 
				
			||||||
 | 
						init_putz(sec, corig + al, c - corig - al);
 | 
				
			||||||
    decl_initializer(type, sec, c, 0, size_only);
 | 
					    decl_initializer(type, sec, c, 0, size_only);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* XXX: make it more general */
 | 
					    /* XXX: make it more general */
 | 
				
			||||||
| 
						 | 
					@ -6023,6 +6045,10 @@ static void decl_designator(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    c += nb_elems * type_size(type, &align);
 | 
				
			||||||
 | 
					    if (c - corig > al)
 | 
				
			||||||
 | 
					      al = c - corig;
 | 
				
			||||||
 | 
					    return al;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* store a value or an expression directly in global data or in local array */
 | 
					/* store a value or an expression directly in global data or in local array */
 | 
				
			||||||
| 
						 | 
					@ -6195,25 +6221,6 @@ static void init_putv(CType *type, Section *sec, unsigned long c)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* put zeros for variable based init */
 | 
					 | 
				
			||||||
static void init_putz(Section *sec, unsigned long c, int size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (sec) {
 | 
					 | 
				
			||||||
        /* nothing to do because globals are already set to zero */
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        vpush_global_sym(&func_old_type, TOK_memset);
 | 
					 | 
				
			||||||
        vseti(VT_LOCAL, c);
 | 
					 | 
				
			||||||
#ifdef TCC_TARGET_ARM
 | 
					 | 
				
			||||||
        vpushs(size);
 | 
					 | 
				
			||||||
        vpushi(0);
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
        vpushi(0);
 | 
					 | 
				
			||||||
        vpushs(size);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        gfunc_call(3);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* 't' contains the type and storage info. 'c' is the offset of the
 | 
					/* 't' contains the type and storage info. 'c' is the offset of the
 | 
				
			||||||
   object in section 'sec'. If 'sec' is NULL, it means stack based
 | 
					   object in section 'sec'. If 'sec' is NULL, it means stack based
 | 
				
			||||||
   allocation. 'first' is true if array '{' must be read (multi
 | 
					   allocation. 'first' is true if array '{' must be read (multi
 | 
				
			||||||
| 
						 | 
					@ -6222,7 +6229,7 @@ static void init_putz(Section *sec, unsigned long c, int size)
 | 
				
			||||||
static void decl_initializer(CType *type, Section *sec, unsigned long c, 
 | 
					static void decl_initializer(CType *type, Section *sec, unsigned long c, 
 | 
				
			||||||
                             int first, int size_only)
 | 
					                             int first, int size_only)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int index, array_length, n, no_oblock, nb, i;
 | 
					    int len, n, no_oblock, nb, i;
 | 
				
			||||||
    int size1, align1;
 | 
					    int size1, align1;
 | 
				
			||||||
    int have_elem;
 | 
					    int have_elem;
 | 
				
			||||||
    Sym *s, *f;
 | 
					    Sym *s, *f;
 | 
				
			||||||
| 
						 | 
					@ -6252,7 +6259,6 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
    } else if (type->t & VT_ARRAY) {
 | 
					    } else if (type->t & VT_ARRAY) {
 | 
				
			||||||
        s = type->ref;
 | 
					        s = type->ref;
 | 
				
			||||||
        n = s->c;
 | 
					        n = s->c;
 | 
				
			||||||
        array_length = 0;
 | 
					 | 
				
			||||||
        t1 = pointed_type(type);
 | 
					        t1 = pointed_type(type);
 | 
				
			||||||
        size1 = type_size(t1, &align1);
 | 
					        size1 = type_size(t1, &align1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6275,6 +6281,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
             (t1->t & VT_BTYPE) == VT_INT
 | 
					             (t1->t & VT_BTYPE) == VT_INT
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
            ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
 | 
					            ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
 | 
				
			||||||
 | 
						    len = 0;
 | 
				
			||||||
            while (tok == TOK_STR || tok == TOK_LSTR) {
 | 
					            while (tok == TOK_STR || tok == TOK_LSTR) {
 | 
				
			||||||
                int cstr_len, ch;
 | 
					                int cstr_len, ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6285,8 +6292,8 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
                    cstr_len = tokc.str.size / sizeof(nwchar_t);
 | 
					                    cstr_len = tokc.str.size / sizeof(nwchar_t);
 | 
				
			||||||
                cstr_len--;
 | 
					                cstr_len--;
 | 
				
			||||||
                nb = cstr_len;
 | 
					                nb = cstr_len;
 | 
				
			||||||
                if (n >= 0 && nb > (n - array_length))
 | 
					                if (n >= 0 && nb > (n - len))
 | 
				
			||||||
                    nb = n - array_length;
 | 
					                    nb = n - len;
 | 
				
			||||||
                if (!size_only) {
 | 
					                if (!size_only) {
 | 
				
			||||||
                    if (cstr_len > nb)
 | 
					                    if (cstr_len > nb)
 | 
				
			||||||
                        tcc_warning("initializer-string for array is too long");
 | 
					                        tcc_warning("initializer-string for array is too long");
 | 
				
			||||||
| 
						 | 
					@ -6294,7 +6301,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
                       string in global variable, we handle it
 | 
					                       string in global variable, we handle it
 | 
				
			||||||
                       specifically */
 | 
					                       specifically */
 | 
				
			||||||
                    if (sec && tok == TOK_STR && size1 == 1) {
 | 
					                    if (sec && tok == TOK_STR && size1 == 1) {
 | 
				
			||||||
                        memcpy(sec->data + c + array_length, tokc.str.data, nb);
 | 
					                        memcpy(sec->data + c + len, tokc.str.data, nb);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        for(i=0;i<nb;i++) {
 | 
					                        for(i=0;i<nb;i++) {
 | 
				
			||||||
                            if (tok == TOK_STR)
 | 
					                            if (tok == TOK_STR)
 | 
				
			||||||
| 
						 | 
					@ -6302,75 +6309,61 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
                            else
 | 
					                            else
 | 
				
			||||||
                                ch = ((nwchar_t *)tokc.str.data)[i];
 | 
					                                ch = ((nwchar_t *)tokc.str.data)[i];
 | 
				
			||||||
			    vpushi(ch);
 | 
								    vpushi(ch);
 | 
				
			||||||
                            init_putv(t1, sec, c + (array_length + i) * size1);
 | 
					                            init_putv(t1, sec, c + (len + i) * size1);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                array_length += nb;
 | 
					                len += nb;
 | 
				
			||||||
                next();
 | 
					                next();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            /* only add trailing zero if enough storage (no
 | 
					            /* only add trailing zero if enough storage (no
 | 
				
			||||||
               warning in this case since it is standard) */
 | 
					               warning in this case since it is standard) */
 | 
				
			||||||
            if (n < 0 || array_length < n) {
 | 
					            if (n < 0 || len < n) {
 | 
				
			||||||
                if (!size_only) {
 | 
					                if (!size_only) {
 | 
				
			||||||
		    vpushi(0);
 | 
							    vpushi(0);
 | 
				
			||||||
                    init_putv(t1, sec, c + (array_length * size1));
 | 
					                    init_putv(t1, sec, c + (len * size1));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                array_length++;
 | 
					                len++;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
						    len *= size1;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
	    indexsym.c = 0;
 | 
						    indexsym.c = 0;
 | 
				
			||||||
	    indexsym.r = 0;
 | 
					 | 
				
			||||||
	    f = &indexsym;
 | 
						    f = &indexsym;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          do_init_list:
 | 
					          do_init_list:
 | 
				
			||||||
 | 
						    len = 0;
 | 
				
			||||||
	    while (tok != '}' || have_elem) {
 | 
						    while (tok != '}' || have_elem) {
 | 
				
			||||||
		decl_designator(type, sec, c, &f, size_only);
 | 
							len = decl_designator(type, sec, c, &f, size_only, len);
 | 
				
			||||||
		have_elem = 0;
 | 
							have_elem = 0;
 | 
				
			||||||
		index = f->c;
 | 
					 | 
				
			||||||
		/* must put zero in holes (note that doing it that way
 | 
					 | 
				
			||||||
		   ensures that it even works with designators) */
 | 
					 | 
				
			||||||
		if (!size_only && array_length < index) {
 | 
					 | 
				
			||||||
		    init_putz(sec, c + array_length * size1,
 | 
					 | 
				
			||||||
			      (index - array_length) * size1);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (type->t & VT_ARRAY) {
 | 
							if (type->t & VT_ARRAY) {
 | 
				
			||||||
		    index = indexsym.c = ++indexsym.r;
 | 
							    ++indexsym.c;
 | 
				
			||||||
 | 
							    /* special test for multi dimensional arrays (may not
 | 
				
			||||||
 | 
							       be strictly correct if designators are used at the
 | 
				
			||||||
 | 
							       same time) */
 | 
				
			||||||
 | 
							    if (no_oblock && len >= n*size1)
 | 
				
			||||||
 | 
							        break;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
		    index = index + type_size(&f->type, &align1);
 | 
					 | 
				
			||||||
		    if (s->type.t == TOK_UNION)
 | 
							    if (s->type.t == TOK_UNION)
 | 
				
			||||||
		        f = NULL;
 | 
							        f = NULL;
 | 
				
			||||||
		    else
 | 
							    else
 | 
				
			||||||
		        f = f->next;
 | 
							        f = f->next;
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (index > array_length)
 | 
					 | 
				
			||||||
		    array_length = index;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (type->t & VT_ARRAY) {
 | 
					 | 
				
			||||||
		    /* special test for multi dimensional arrays (may not
 | 
					 | 
				
			||||||
		       be strictly correct if designators are used at the
 | 
					 | 
				
			||||||
		       same time) */
 | 
					 | 
				
			||||||
		    if (no_oblock && index >= n)
 | 
					 | 
				
			||||||
		        break;
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
		    if (no_oblock && f == NULL)
 | 
							    if (no_oblock && f == NULL)
 | 
				
			||||||
		        break;
 | 
							        break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (tok == '}')
 | 
							if (tok == '}')
 | 
				
			||||||
		    break;
 | 
							    break;
 | 
				
			||||||
		skip(',');
 | 
							skip(',');
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        /* put zeros at the end */
 | 
					        /* put zeros at the end */
 | 
				
			||||||
        if (!size_only && array_length < n) {
 | 
						if (!size_only && len < n*size1)
 | 
				
			||||||
            init_putz(sec, c + array_length * size1,
 | 
						    init_putz(sec, c + len, n*size1 - len);
 | 
				
			||||||
                      (n - array_length) * size1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!no_oblock)
 | 
					        if (!no_oblock)
 | 
				
			||||||
            skip('}');
 | 
					            skip('}');
 | 
				
			||||||
        /* patch type size if needed, which happens only for array types */
 | 
					        /* patch type size if needed, which happens only for array types */
 | 
				
			||||||
        if (n < 0)
 | 
					        if (n < 0)
 | 
				
			||||||
            s->c = array_length;
 | 
					            s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
 | 
				
			||||||
    } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
 | 
					    } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
 | 
				
			||||||
	size1 = 1;
 | 
						size1 = 1;
 | 
				
			||||||
        no_oblock = 1;
 | 
					        no_oblock = 1;
 | 
				
			||||||
| 
						 | 
					@ -6380,7 +6373,6 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        s = type->ref;
 | 
					        s = type->ref;
 | 
				
			||||||
        f = s->next;
 | 
					        f = s->next;
 | 
				
			||||||
        array_length = 0;
 | 
					 | 
				
			||||||
        n = s->c;
 | 
					        n = s->c;
 | 
				
			||||||
	goto do_init_list;
 | 
						goto do_init_list;
 | 
				
			||||||
    } else if (tok == '{') {
 | 
					    } else if (tok == '{') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,7 +217,40 @@ void test_multi_relocs(void)
 | 
				
			||||||
  for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
 | 
					  for (i = 0; i < sizeof(table)/sizeof(table[0]); i++)
 | 
				
			||||||
    table[i]();
 | 
					    table[i]();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Following is from GCC gcc.c-torture/execute/20050613-1.c.  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SEA { int i; int j; int k; int l; };
 | 
				
			||||||
 | 
					struct SEB { struct SEA a; int r[1]; };
 | 
				
			||||||
 | 
					struct SEC { struct SEA a; int r[0]; };
 | 
				
			||||||
 | 
					struct SED { struct SEA a; int r[]; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					test_correct_filling (struct SEA *x)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static int i;
 | 
				
			||||||
 | 
					  if (x->i != 0 || x->j != 5 || x->k != 0 || x->l != 0)
 | 
				
			||||||
 | 
					    printf("sea_fill%d: wrong\n", i);
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    printf("sea_fill%d: okay\n", i);
 | 
				
			||||||
 | 
					  i++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					test_zero_init (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  /* The peculiarity here is that only a.j is initialized.  That
 | 
				
			||||||
 | 
					     means that all other members must be zero initialized.  TCC
 | 
				
			||||||
 | 
					     once didn't do that for sub-level designators.  */
 | 
				
			||||||
 | 
					  struct SEB b = { .a.j = 5 };
 | 
				
			||||||
 | 
					  struct SEC c = { .a.j = 5 };
 | 
				
			||||||
 | 
					  struct SED d = { .a.j = 5 };
 | 
				
			||||||
 | 
					  test_correct_filling (&b.a);
 | 
				
			||||||
 | 
					  test_correct_filling (&c.a);
 | 
				
			||||||
 | 
					  test_correct_filling (&d.a);
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main()
 | 
					int main()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  print(ce);
 | 
					  print(ce);
 | 
				
			||||||
| 
						 | 
					@ -244,5 +277,6 @@ int main()
 | 
				
			||||||
  //printf("q: %s\n", q);
 | 
					  //printf("q: %s\n", q);
 | 
				
			||||||
  test_compound_with_relocs();
 | 
					  test_compound_with_relocs();
 | 
				
			||||||
  test_multi_relocs();
 | 
					  test_multi_relocs();
 | 
				
			||||||
 | 
					  test_zero_init();
 | 
				
			||||||
  return 0;
 | 
					  return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,3 +38,6 @@ flow: 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0
 | 
				
			||||||
one
 | 
					one
 | 
				
			||||||
two
 | 
					two
 | 
				
			||||||
three
 | 
					three
 | 
				
			||||||
 | 
					sea_fill0: okay
 | 
				
			||||||
 | 
					sea_fill1: okay
 | 
				
			||||||
 | 
					sea_fill2: okay
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue