Skip to content

Inconsistent tagof return for _:. #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Y-Less opened this issue Mar 19, 2017 · 12 comments
Closed

Inconsistent tagof return for _:. #160

Y-Less opened this issue Mar 19, 2017 · 12 comments

Comments

@Y-Less
Copy link
Member

Y-Less commented Mar 19, 2017

Tagof(var, tag1 = tagof (var))
{
	new tag2 = tagof (var);
	printf("%d %x", var, tag1);
	printf("%d %x", var, tag2);
}

main()
{
	Tagof(4);
}

Expected output should be something consistent, either:

4 0
4 0

Or:

4 80000000
4 80000000

Most tags seem to have the top bit set, but in some of my experiments, the default _: tag was just 0 - which is quite nice in some ways to distinguish it from other tags. However, the actual output is both:

4 0
4 80000000
Y-Less added a commit to pawn-lang/amx_assembly that referenced this issue Mar 19, 2017
@Y-Less
Copy link
Member Author

Y-Less commented Mar 19, 2017

tagof (_:) returns 0x80000000.

@YashasSamaga
Copy link
Member

YashasSamaga commented Jan 3, 2018

The compiler seems to do some mysterious stuff:

tagsym=find_constval_byval(&tagname_tab,tag | PUBLICTAG);

tagsym=find_constval_byval(&tagname_tab,tag & PUBLICTAG);

tag=(int)(ptr->value & TAGMASK);

PUBLICTAG is 0x80000000Lu

FIXEDTAG is 0x40000000Lu

TAGMASK is ~PUBLICTAG

What's a public tag and fixed tag? The PAWN Language Guide doesn't seem to mention about them anywhere.

@Y-Less
Copy link
Member Author

Y-Less commented Jan 3, 2018

No. I did some research a while ago. A public tag is just like any other public thing - the name is in the AMX header. A fixed tag is a strong tag - I've never seen the fixed name before.

https://github.com/Zeex/amx_assembly/blob/c390cbebfa6e88425205ec21577ed09e34e7229b/amx_header.inc#L33-L34

@YashasSamaga
Copy link
Member

What kind of tags do not end up in the AMX header?

@Y-Less
Copy link
Member Author

Y-Less commented Jan 3, 2018

Only _: AFAIK.

@YashasSamaga
Copy link
Member

How about forcing _: (as 0x80000000) to be present in the tag table irrespective of whether that tag has been used? (not sure if there's a meaningful script which doesn't use it).

@Y-Less
Copy link
Member Author

Y-Less commented Jan 4, 2018

Why? That doesn't add anything and doesn't solve anything. The bug was that _: is sometimes cellmin and sometimes 0. I suppose forcing it in to the table might fix it in a round-about way by making it always cellmin, but I'm not convinced unless what I suspect is an underlying bug is fixed to stop the inconsistency.

@YashasSamaga
Copy link
Member

YashasSamaga commented Jan 5, 2018

I did a comparison between the free standing tagof operator code and the tagof operator used as a default argument.

The tagof operator handles only LABELS and SYMBOLS; the labels are not relevant to our discussion as the tagof operator which is used as default argument cannot accept labels because we can't pass labels as arguments. The free standing tagof operator has the following code for handling SYMBOL as an argument:

tag=sym->tag;
.
.
.
 exporttag(tag);
    clear_value(lval);
    lval->ident=iCONSTEXPR;
    lval->constval=tag | PUBLICTAG;
    ldconst(lval->constval,sPRI);

sym is a pointer to the structure which holds information about the symbol passed to the tagof operator. Here sym->tag can be zero if the symbol is untagged. Even if it is zero, the code which follows it in the above snippet is executed. This would mean that 0 would be tried to be exported (exporttag) to the tags table but would fail as exporttag ignores 0 as tag value. The actual value which is returned by tagof is stored in the lvalue as tag | PUBLICTAG which is why you get 0x80000000 when standalone tagof operator is used.

The code which handles tagof as default argument is a bit more involved. The compiler creates a local (as in a local variable in the compiler code) tag list. For each argument passed to the function, it pushes the argument name and it's tag value to the local tag list IF AND ONLY IF the tag value is NOT 0.

if (lval.tag!=0)
            append_constval(&taglst,arg[argidx].name,lval.tag,0);

Hence, for untagged arguments, the untagged tag entry does not exist in the local tag list.

When the compiler finds that an argument that has tagof as default value, it executes the following code:

asz=find_constval(&taglst,arg[argidx].defvalue.size.symname,
                        arg[argidx].defvalue.size.level);
      if (asz != NULL) {
        exporttag(asz->value);
        array_sz=asz->value | PUBLICTAG;  /* must be set, because it just was exported */
      } else {
        array_sz=0;
      } /* if */
    ldconst(array_sz,sPRI);
    pushreg(sPRI);              /* store the function argument on the stack */

In the untagged case, asz will be NULL as an entry in the local tag list does not exist for untagged tag. Because of this, arrary_sz is set to 0 and this value is what is pushed as argument (ldconst followed by pushreg) to the function.

The differences as stated earlier between the two is that the standalone tagof operator exports the untagged tag after which it sets the PUBLICTAG bit but the tagof operator used as default argument does not export (and hence doesn't bother setting the PUBLICTAG bit).

@YashasSamaga
Copy link
Member

YashasSamaga commented Jan 6, 2018

I'm not sure what's the bug.

If untagged tag is not exported (the function which adds tags to the tag table is pc_addtag and it ignores 0), then it should actually be zero? So if we go by the meaning of the PUBLICTAG bit, the bug should then be tagof operator returning 0x80000000 for a tag which isn't exported?

This would mean the standalone tag operator is not doing it correctly by not taking care of the special case of tag being zero while setting the PUBLICTAG bit.


Printed what goes into the AMX header. Every line with [TAG] is an entry in the compiler's tag table. Writing indicates that it is being written to the tag table.
The last number indicates if the PUBLICTAG bit is set or not.

printf("[TAG] %s %d %d\n", constptr->name, constptr->value, (constptr->value & PUBLICTAG));
printf("Writing to tag table: \"%s\" %d (after masking) %d(before masking)\n", constptr->name, constptr->value & TAGMASK, constptr->value);

[TAG] _ 0 0
[TAG] bool 1 0
[TAG] floatround_method 2 0
[TAG] anglemode 3 0
[TAG] Float -1073741820 -2147483648
Writing to tag table: "Float" 1073741828 (after masking) -1073741820(before masking)
[TAG] filemode 5 0
[TAG] seek_whence 6 0
[TAG] File 1073741831 0
[TAG] PlayerText 1073741832 0
[TAG] DB 1073741833 0
[TAG] DBResult 1073741834 0
[TAG] Menu 1073741835 0
[TAG] Text 1073741836 0
[TAG] Text3D 1073741837 0
[TAG] PlayerText3D 1073741838 0
[TAG] PInfo_t 1073741839 0
[TAG] TAG_H 1073741840 0
[TAG] TAG_G 1073741841 0
[TAG] TAG_I_1 1073741842 0
[TAG] TAG_I_2 1073741843 0
[TAG] TAG_I_3 1073741844 0
[TAG] TAG_J_1 1073741845 0
[TAG] TAG_J_2 1073741846 0
[TAG] TAG_J_3 1073741847 0
[TAG] TAG_J_4 1073741848 0
[TAG] TAG_J_5 1073741849 0
[TAG] TAG_A 1073741850 0
[TAG] TAG_B 1073741851 0
[TAG] TAG_B_1 1073741852 0
[TAG] TAG_B_2 1073741853 0
[TAG] TAG_C 1073741854 0
[TAG] TAG_D 1073741855 0
[TAG] TAG_E 1073741856 0
[TAG] TAG_F 1073741857 0
[TAG] TAG_F_1 1073741858 0
[TAG] TAG_G_1 1073741859 0

This list is for tags which would end up in the tag table even when a debug mode is disabled. It appears that only Float ends up in the tag table.

The implementer guide also tells:

The “tags” table uses the same format as well. This table only holds tags whose name or number might be useful to the host application or extension modules: tags that are used with the exit or sleep instructions or used with the tagof operator.

@YashasSamaga
Copy link
Member

YashasSamaga commented Jan 6, 2018

Adding tags & exporting:

The general procedure for adding the tag and exporting is:

  • if a tag is found, it is added to the compiler's tag list (list of all tags: both public and non-public)
  • if necessary, the tag is exported

The exporttag function is used to export the tag. This function expects the tag to be present in the tag list. All this function does is sets the PUBLICTAG bit of the tag in the tag list.

tagof operator (not as default argument) exports tags

The compiler tries to export tags of symbols which are passed to tagof. This quite useful as this allows plugins to find out the tag of an argument passed to natives from the tag identifier.

Verification:

new MyTag:a = 1;
	printf("%x",tagof(a));

MyTag ends up in the tag table in the AMX header.

If a label is passed as an argument to tagof, it does not add it to the tag table because the tag entry doesn't even exist in the tag list for exporttag to set the PUBLICTAG.

Whether this is a bug or not is up for speculation. I think the tags which are passed as labels to tagof and weren't used before are not added to the tag table because they aren't needed because no variable in the script may have that tag. If there was a variable with that tag and it's tag was being equated with a tagof operator result for a label, there must be a tagof operator which was used on a symbol with that tag if the tags have to match.

Contents of the tag table

Every tag which has the PUBLICTAG bit set in the tag list is added to the tag table always. If debug mode is enabled, a new table consisting of tags is appended to the header but this does not have anything to do with the main tag table.

Exception:
The compiler checks if the tag identifier is 0 after masking off the PUBLICTAG bit before adding it to the tag table. If it is zero, it ignores it.

In other words, if a tag has a tag identifier as 0 and has PUBLICTAG bit set, it won't be added to the tag table.

@YashasSamaga
Copy link
Member

YashasSamaga commented Jan 6, 2018

The issue now is what should tagof(_:) return. In my opinion, the standalone tagof operator is getting it wrong. As the untagged tag is not exported to the tag table even if the PUBLICTAG bit is set, the tag identifier for untagged cells must never have the PUBLICTAG bit set and hence must be zero to maintain consistency.

If we can get a consenus on what the tag identifier for untagged cells must be, the issue will be resolved. It takes editing of just 3 lines to patch this either way (0 or 0x80000...)

The reason why the tagof operator behaves differently in two different contexts has already been explained in one of my previous comments. We just have to decide in which of the two contexts, the tagof operator is doing it correctly.

@Y-Less
Copy link
Member Author

Y-Less commented Jan 7, 2018

I'd vote 0. As you say it isn't public, and 0 is close to none which _ almost is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants