Proposal: [343] Special memory access words

Informal

This page is dedicated to discussing this specific proposal

ContributeContributions

AntonErtlavatar of AntonErtl [343] Special memory access wordsProposal2024-06-14 15:18:23

Author:

M. Anton Ertl

Change Log:

2024-06-14 initial version

Problem:

Data coming from or going to a file or the net often contain 16-bit, 32-bit, and 64-bit integer values that may be signed or unsigned, may be naturally aligned or not, and may be in big-endian or little-endian instead of the native byte order. Architectures tend to provide convenient instructions for accessing these data, but the Forth standard does not provide words for that, and synthesizing the operations from C@ and C! is not just cumbersome, but also leads to inefficient code.

Solution:

This proposal only targets byte-addressed systems. See the discussion below about word-addressed machines.

We use the following prefixes:

prefix Meaning informal name
c 8 bits Byte
w 16 bits Wyde
l 32 bits Long
x 64 bits eXtended

For the w prefex this proposal specifies the following set of words:

w@ w! for unaligned 16-bit memory accesses; w@ zero-extends.

Right after w@ or right before w! you can adjust the byte order: wbe converts from big-endian to native byte-order and from native to big-endian byte order. wle is the corresponding word for little-endian byte order.

On fetching signed values can then be sign-extended with w>s. Unsigned values are already in the proper zero-extended form. On storing all the target bits are present in the cell, so no extension is necessary.

These five words allow us to fetch and store big-endian, little-endian or natively ordered signed and unsigned 16-bit values, with sequences like:

w@ wbe w>s   \ 16-bit unaligned signed big-endian fetch
>r wle r> w! \ 16-bit unaligned     little-endian store

For the l prefix the corresponding five words l@ l! lbe lle l>s are proposed and for the x prefix the corresponding five words x@ x! xbe xle x>s.

For the c prefix Standard Forth already has c@ and c!, and byte order and alignment are not issues there, so the only thing needed is sign extension, so c>s is proposed.

These words do not work properly if the data does not fit into a cell, so a 16-bit system would only implement the c and w words, a 32-bit system only the c, w and l words, and only systems with cell size >= 64 bits would implement all the words.

Typical use: (Optional)

( c-addr ) l@ lle l>s \ 32-bit unaligned signed little-endian fetch
( c-addr ) w@         \ 16-bit unaligned unsigned native-order fetch
( n|u c-addr ) >r xbe r> x! \ 64-bit unaligned big-endian   store
( n|u c-addr ) l!           \ 32-bit unaligned native-order store

Discussion

Previous work

The present proposal can be seen as another take on the problems attacked with the following proposals.

Memory Access

Federico de Ceballos (with Stephen Pelc) has proposed a wordset for solving the same problem by having words like

be-w@ \ 16-bit unaligned unsigned big-endian fetch
le-w! \ 16-bit unaligned       little-endian store

That would require 6 words be-w@ le-w@ w@ be-w! le-w! w!, but would still not work for fetching signed values, so you either need w>s to be possibly used after any of the ...w@ words (for a total of 7 w words, but it's still a composing approach), or it would need a doubling of the ...w@ words (for a total of 9 w words, but now everything is precomposed).

This proposal also includes words like w, walign waligned wfield: discussed below.

This proposal has been met with significant resistance due to the large number of words proposed.

This proposal also includes b words for dealing with bytes, but given the 1 chars = 1 proposal that has been accepted in 2016, c seems good enough on byte-addressed systems.

16-bit memory access

This proposes w@ w! as working with w-addr addresses that are not defined in the proposal (but I would expect them to require 16-bit alignment, but OTOH neither waligned nor walign are proposed). No solution for byte order or sign extension is presented. The proposal includes w, which requires 16-bit alignment of the data-space pointer.

32-bit memory operators

This is the 32-bit variant (using l as prefix) of the "16-bit memory access" proposal discussed above.

Efficiency

Does the proposed approach not lead to less efficient code than the approach of the Memory Access proposal mentioned above? The more advanced Forth systems combine code sequences and produce efficient code for them. E.e., in the present case, for l@ lle l>s gforth-fast on AMD64 produces:

movsx   r8,dword PTR [r8]

Simpler systems will indeed be less efficient when such special memory accesses are performed, but the present proposal proposes fewer words, which is often more in line with the philosophy behind many simple systems.

Also, is a lot of time spent accessing input and output data?

Larger address units

On some systems (in particular on word-addressed hardware) the address unit is larger than one byte. How can these words work there? The only way I can see is to work with a special memory layout where each address unit contains only one byte, and the upper bits are ignored on fetching, and are set to 0 on storing. The reference implementation of the proposed words can be used in such a setting.

This memory layout would be used between I/O words that produce or consume this layout, and the words using the special memory access words to fetch from or store to this layout. For the file words, this layout could come into play through a file access mode modifier (similar to bin).

Require alignment or not

One might wonder whether we should not have versions of the fetch and store words that require alignment as well as versions that do not, but we have decided to only supply the words that do not require alignment, for the following reasons. All the surviving general-purpose architectures (IA-32/AMD64, ARMv7-A/R ff. (since 2005), RISC-V, Power, S/390x) have converged on supporting unaligned accesses, so on these architectures both variants would use the same instructions.

On other architectures w@ will be slower than a hypothetical w@a, but given that these words are not used that often, that these machines are no longer widespread, and that alignment is sometimes lost by embedding one structure inside another (as has occured in network protocols), we decided that w@a and friends are more trouble than they are worth.

Upper bit handling for the byte-order words

How do we specify the upper bits in the results for wle, wbe etc.? E.g., on 64-bit Gforth I see:

$1234567890abcdef wbe hex. \ output: $EFCD  ok
$1234567890abcdef wle hex. \ output: $1234567890ABCDEF  ok

So in one case it sets the other bits to zero, in the other case it leaves them alone. However, we do not want to specify that the upper bits can be anything, otherwise w@ wle would not work as unsigned little-endian 16-bit fetch, and we would need to add a word w>u or somesuch.

So we specify that the upper bits of the result are either untouched or 0 (when applying wbe wce to the result of w@, that produces the same result in either case).

Accesses to values larger than one cell

Gforth has xd words where the on-stack representation is a double-cell. This allows implementing 64-bit accesses on systems with 32-bit cells. When I presented these words at the 2023 Forth200x meeting, I was asked not to include them in this proposal. So access to values larger than a cell is not supported by the proposed words.

Additional words

Gforth has the following words related to this proposal:

  • /w ( -- u ) specifies the size of a 16-bit value, i.e. 2.

  • w, ( x -- ) allocates and stores a 16-bit value. wbe or wle can be used before. SwiftForth and VFX Forth also have w,.

  • walign ( -- ) naturally aligns the dictionary pointer to a 16-bit boundary..

  • waligned ( u1 -- u2 ) does the same for an address or offset on the stack.

  • *aligned ( u1 u2 -- u ): u2 divides u, and u is the next value u >= u1 with that property. The result of the operation is *not specified if u2 is not a power of two.

  • wfield: ( u1 "name" -- u2 ) defines a naturally (i.e., 16-bit) aligned 16-bit field. wfield: is equivalent to waligned /w +field.

  • wvalue: ( u1 "name" -- u2 ) defines a naturally-aligned value-flavoured 16-bit field. No easy way exists to define a value-flavoured field without imposing alignment.

These words (and their l and x siblings) were not in my presentation at the 2023 meeting, so I have not been asked to include them in this proposal, and therefore I have not included them, but if consensus emerges that we want to include some of them, I am prepared to do that. But do we need them and do we need them in this form?

  • /w just means 2, but documents the intent (number of bytes accessed by w@) better.

  • w, is convenient in interactive usage, but for maintained code its usage often is problematic: In many cases it redundantly respecifies the layout of a data structure (already defined with the field words), which means that a change to the layout results several changes in the code.

  • walign may be useful in connection with w,, but has the same problem of redundancy.

  • waligned may be useful for influencing field layout, but one could also write /w *aligned (replacing three aligned words with one). Also, if the structure layout is coming from outside the Forth system, we probably just want to transfer it using the C interface rather than defining it the way we would a Forth-internal data structure.

  • The automatic alignment of wfield: and wvalue: is in line with the automatic alignment of field: etc., but is at odds with with the idea that these words are for data structures defined outside of Forth where fields may be unaligned. Variable-flavoured fields for such data structures can be defined with +field, e.g., 15 0 +field \<name\> drop. For value-flavoured fields an unaligned version of wvalue: would be useful, with the possible usage 15 wvalue:u \<name\> drop.

  • Value-flavoured fields also inspire the idea that the byte order and signedness should also be part of the field definition.

Do we want to add any such words to the proposal?

FP memory accesses

The words sf@ sf! df@ df! are also intended for data exchange with the outside world, but they require alignment and there is no provision for dealing with different byte orders.

For dealing with alignment we could add support for unaligned accesses to these words. This would require a change in the standard. What is your opinion about that?

For dealing with different byte orders one can do the potential byte swapping on the integer side, as follows:

create dfbuf 1 dfloats allot

: be-df@ ( c-addr -- r ) x@ xbe dfbuf x! dfbuf df@ ;
: be-df! ( r c-addr -- ) dfbuf df! dfbuf x@ xbe swap x! ;

Proposal:

Add the following words:

w@ ( c-addr -- u ) "w-fetch"

u is the zero-extended 16-bit value stored at c_addr.

w! ( x c-addr -- ) "w-store"

Store the bottom 16 bits of x at c_addr.

wbe ( u1 -- u2 )

Convert 16-bit value in u1 from native byte order to big-endian or from big-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

wle ( u1 -- u2 )

Convert 16-bit value in u1 from native byte order to little-endian or from little-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

w>s ( x -- n ) "w-to-s"

Sign-extend the 16-bit value in x to cell n.

l@ ( c-addr -- u ) "l-fetch"

u is the zero-extended 32-bit value stored at c_addr.

l! ( x c-addr -- ) "l-store"

Store the bottom 32 bits of x at c_addr.

lbe ( u1 -- u2 )

Convert 32-bit value in u1 from native byte order to big-endian or from big-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

lle ( u1 -- u2 )

Convert 32-bit value in u1 from native byte order to little-endian or from little-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

l>s ( x -- n ) "l-to-s"

Sign-extend the 32-bit value in x to cell n.

x@ ( c-addr -- u ) "x-fetch"

u is the zero-extended 64-bit value stored at c_addr.

x! ( x c-addr -- ) "x-store"

Store the bottom 64 bits of x at c_addr.

xbe ( u1 -- u2 )

Convert 64-bit value in u1 from native byte order to big-endian or from big-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

xle ( u1 -- u2 )

Convert 64-bit value in u1 from native byte order to little-endian or from little-endian to native byte order (the same operation). The other bits are either untouched or set to 0.

x>s ( x -- n ) "l-to-s"

Sign-extend the 64-bit value in x to cell n.

c>s ( x -- n ) "c-to-s"

Sign-extend the 8-bit value in x to cell n.

Reference implementation:

Will be provided at a later time.

Testing: (Optional)

Will be provided at a later time.

ruvavatar of ruv

Data types

The standard word @ has the stack diagram ( a-addr -- x ), ditto for !, lshift, rshift — they operate on the most general single-cell data type x.

The proposed words w>s ( x -- n ), l>s ( x -- n ) also operate on a parameter of the data type x.

Thus, the following words probably should also have the data type x instead of the data type u in their stack diagrams:

w@ ( c-addr -- u )
l@ ( c-addr -- u )
x@ ( c-addr -- u )
wbe ( u1 -- u2 )
wle ( u1 -- u2 )
lbe ( u1 -- u2 )
lle ( u1 -- u2 )
xbe ( u1 -- u2 )
xle ( u1 -- u2 )

Alternatively, new data types x8, x16, x32, x64 can be introduced and used for the fetch operations.

Also, addr should be used instead of c-addr. Because either these data types are equal (in a Forth-2019 system), or these words should work on addr (in a Forth-2012 system).

Wording

w>s ( x -- n ) Sign-extend the 16-bit value in x to cell n.

Does it follow from this that $1234567890abcdef w>s shall produce $ffffffffffffcdef on a 64bit system?

Since the part "16-bit value in x" makes impression that the value in x, if interpreted as unsigned number, must belong to the range { 0 ... 65535 }.

16-bit and 32-bit systems

For l-family and x-family words, it should be mentioned in a note that the word can be only provided by the system if the cell size is not less than 32 and 64 bits correspondingly.


If the Forth-system has the address unit 32 bit (for example, a JavaScript-based Forth system), may this system provide w@ and w! words? Of course, these words can only read and write 16 least significant bits of an address unit in such a system.

Characters and bytes

c>s ( x -- n ) Sign-extend the 8-bit value in x to cell n.

The characters are not bound to 8 bit in the standard. For example, the character size can be the same as the cell size (on a cell-addressed Forth system). So, the text description for this word shall say: "Sign-extend the character value in x to cell n"


I suggest to add the words b! ( x addr -- ), b@ ( addr -- x ), b>s ( x -- n ) to operate on octets.

If the character size is 8 bit, they aliased to c-words, otherwise they either have own implementation, or are not provided by the system.

The word с@ should not be used to read octets without testing the environment.

ruvavatar of ruv

w! ( x c-addr -- ) Store the bottom 16 bits of x at c_addr.

The standard uses the term "least significant" instead of "bottom". Taking into account my previous comment, I would suggest the following specifications:

w! ( x addr -- ) "w-store"
Store the least significant 16 bits of x at addr. When the address unit size is greater than 16 bits, only 16 least significant bits can be modified at addr.

w@ ( addr -- x ) "w-fetch"
x is the zero-extended 16-bit value stored at 16 addr. When the address unit size is greater than 16 bits, only 16 least significant bits from addr are transferred.

If this wording is acceptable, it should be also used for l-family and x-family words.

ruvavatar of ruv

For l-family and x-family words, it should be mentioned in a note that the word can be only provided by the system if the cell size is not less than 32 and 64 bits correspondingly.

I mean, it should be mentioned in some normative part: either in the glossary entry for the word, or somewhere in the section "Optional Special Memory Access word set".

GeraldWodniavatar of GeraldWodni

Yes, thanks! I cannot believe we have not yet standardized this, let's check for system compliance and get that standardized.

There is a small typo: It ways w is Wyde. I guess that should be Word or Wide?

One little bike-shedding I want to point out, is that words like w>s look like conversion words. lbe does not. I would profer >lbe, but this should be discussed face 2 face.

AntonErtlavatar of AntonErtl

The sign-extending words w>s etc. are specified as ( x -- n ) because these can be considered as bitwise operations like lshift. Alternatively, they could be specified as ( u -- n ) because the result of the fetching words and the byte-order words are unsigned. But it makes no difference how they are specified.

The fetching words and byte-order words have unsigned results because they zero-extend (if anything) the data that they fetch or reorder. Specifying u here indicates that.

The addresses are specified as c-addr to point out that the addresses are not required to be aligned. This is less clear with addr.

About the wording of the sign-extension words: good point. I will change it to

Sign-extend the low-order 8 bits in x to the full cell width.

Concerning the l and x words: I expect that these words will be optional. It should be obvious to the implementor of a 32-bit system that the x words don't make sense for their system, but just in case we could mention that in the Rationale. I don't see a point in putting this in the normative part, and it would make the text more verbose and less usable.

There is a section "Larger address units" that discusses the case of address units larger than 8 bits. I don't see how the proposal (or any other practically usable wordset) could work with w@ w! that read the low-order 16 bits of an address unit and still result in portable code. There is a reason why byte-addressed architectures have won.

The section "Larger address units" outlines an approach where w@ w! etc. use only 8 bits per address unit. I don't want to prescribe this approach (maybe there are other ways, although I don't see them), so w@ w! etc. are not specified in this way; the current specification is also much more readable for the vast majority of users (those on byte-addressed systems) than one that prescribes accesses to 8 bits per au. Addressing systems with larger aus in the rationale looks good enough to me; and maybe the implementors of those systems don't want to implement any of these words anyway; there's a reason why they went for a system that is not byte-addressed. But maybe I should be putting in some normative text in the proposal that prescribes 8 bits per address unit (maybe associated with a type b-addr).

Octet is a term from telecommunications that has no place in computing since 8-bit bytes won >50 years ago.

c>s sign-extends 8-bit bytes.

There is no need for b words, even on systems with larger address units than bytes. See the section "Larger address units".

However, with possible systems where c@ zero-extends bigger units than 8 bits, we need a c>u that zero-extends 8-bit units. I will add this to the proposal.

I will replace "bottom" with "least significant".

As for addressing larger address units, see the discussion above.

"Wyde" is not a typo, but a word I have from Bernd Paysan; just as "byte" is not a typo for "bite", and "nybble" is not a typo for "nibble" (but "nibble" is more common). "w" may originally have come from DEC/Motorola/Intel usage, which is based on the idea of a 16-bit word (as in the PDP-11, 6800, and 8080), but other architectures started with 32-bit words, so "word" would not only conflict with other usage of "word" in Forth, but also with other usage in various computer architectures, and with the general use in computer architecture (where "word" means the same as "cell" means in Forth).

'lbe' is a conversion word, but it converts to and from big-endian order, so how should we place the >? l>be or lbe>. Either one is wrong for one of the two usages, so it was just called lbe.

ruvavatar of ruv

'lbe' is a conversion word, but it converts to and from big-endian order, so how should we place the >?

I agree with the form like lbe. It can be read as a modifier like bin ( fam1 -- fam2 ).


But maybe I should be putting in some normative text in the proposal that prescribes 8 bits per address unit (maybe associated with a type b-addr).

Introducing b-addr seems like a good idea! It will then be obvious that these words are for byte-addressed systems only (by design).

The addresses are specified as c-addr to point out that the addresses are not required to be aligned. This is less clear with addr.

Can't agree.
According to 3.1.1 Data-type relationships:

a-addr ⇒ c-addr ⇒ addr ⇒ u

Where "c-addr" is a symbol for the "character-aligned address" data type. See also 3.3.3.1 Address alignment.

In Forth-2012, the c-addr data type may be not equal to the addr data type; it means, the address returned by align here 1+ does not belong to c-addr in the general case (because, in some plausible system a character consists of four address units). So it's absolutely clear that a parameter of the addr data type is not required to be aligned at all.

In Forth-2019, c-addr is equal to addr, but "c-addr" is still defined as "character-aligned address".

Thus, a better way is to either introduce b-addr, or use addr.


The fetching words and byte-order words have unsigned results because they zero-extend (if anything) the data that they fetch or reorder. Specifying u here indicates that.

To indicate zero-extending, another data type should be introduced. By design, the data type u does not indicate zero-extending. This data type only indicates that the operation interprets a parameter of this type as unsigned number, and the result will be incorrect if the parameter is interpreted by the user as, for example, a negative number.

So, for a word that is specified as wbe ( u1 -- u2 ), the stack parameter in the position u2 should be always interpreted by the user as a particular unsigned number (ditto for u1). But it's wrong in the general case for the word wbe. Because after the byte order is changed (or before it is changed), the parameter is just a tuple of bits.

Also, it is possible that even in the native byte order a parameter is not interpreted as a number at all (that is, neither a signed number, nor an unsigned number). For example, xt is formally not a number (and xt ⇒ x). And when you specify u, you exclude such use case.

ruvavatar of ruv

after the byte order is changed (or before it is changed), the parameter is just a tuple of bits.

An improved version of the parameter data type specifications (via stack diagrams):

w@ ( addr -- x )
wbe ( u1 -- x2 | x1 -- u2 )
w>s ( x1 -- n2 )

But I'm still not happy enough with it.

Because the following sequence:

w@ ( addr -- x ) wbe ( x -- u )  w>s ( x -- n )

is formally incorrect, because on the last step we actually convert the parameter of the u data type to the parameter of the n data type.

If we have:

w@ ( addr -- x )
wbe ( x1 -- x2 )
w>s ( x1 -- n2 )

The following sequence is correct (in terms of data types matching):

w@ ( addr -- x ) wbe ( x -- x )  w>s ( x -- n )

PeterFalthavatar of PeterFalth

The following sequence is correct (in terms of data types matching):

w@ ( addr -- x ) wbe ( x -- x ) w>s ( x -- n )

Does this sequence make any sense? how can w>s know that the stack item is in big-endian in this case and work properly?

I think it is a mistake to have a separate words for sign extending.

In lxf/ntf I have <w@ for sign extending when retrieving a number. I think this is much more simpler

BR Peter

ruvavatar of ruv

w>s accepts argument in the only native endianness of the Forth system.

w@ ( addr -- x ) wbe ( x -- x ) w>s ( x -- n )

In this sequence we know that the wyde in memory is in big-endian. w@ reads this wyde as is, and wbe converts it from the big-endian to the native endianness. And w>s interprets 16 least significant bits of its parameter as a signed 16-bit 2s complement value, and extends the sign to the full cell.

Probably we should also specify two's complement in some normative parts.

AntonErtlavatar of AntonErtl

Address units

I will extend the discussion of larger address units to suggest adding b-addr and related stuff, or alternatively specifying that systems that implement these words are required to have 8-bit address units.

But in contrast to what you write, if we take the latter option, there is no need for b-addr.

addr vs. c-addr

Yes, as far as the standard is concerned, addr has the least alignment requirements. But it is also used in practice as a stand-in for any kind of address, including addresses with stricter alignment requirements (I am sure I am not alone in this usage of addr); so using c-addr makes the intent of not requiring alignment clearer to the reader.

u vs. x

Zero-extending is what you do with unsigned numbers, so if you use the result of w@ as a number, the result of w@ is an unsigned number. OTOH, if you apply w>s to the result of w@, the result is just treated as a funny representation of a signed number (not an n, but not a u either); if we don't know what it is, we use x, so using x would be ok here. Likewise, if we use, say, lbe on the result of w@, one can see the input of lbe as a funny representation of an unsigned or signed number, and the output as an unsigned number or a funny representation of a signed number. So yes, one can argue for using x everywhere except for the output of the sign-extending words.

However, I don't see that it makes a difference in what kinds of programs are considered conforming to this specification or how systems are implemented, so we should use the type that makes it easiest to understand what is going on. And in this respect using u as output type for w@ and w@ lbe makes it very obvious that the result is zero-extended, no need to consult the prose of these words for determining that.

Another idea that I had some time ago was to have specialized types for the various intermediate results of the decomposition, e.g., bewn for a big-endian 16-bit signed number. This would allow specifying the proper sequences in the conversion through the type system, but would make the specification more complex. E.g., for the w words we would have:

w@ ( c-addr -- u|wn|bewn|lewn|bewu|lewu )
w! ( u|n|bewn|lewn|bewu|lewu c-addr -- )
wbe ( bewn -- wn | bewu -- u |  n -- bewn | u -- bewu )
wle ( lewn -- wn | lewu -- u |  n -- lewn | u -- lewu )
w>s ( wn -- n )

I think that this amount of detail (including specifying all these types) makes the specification harder rather than easier to understand.

Concerning the whether the things that are processed with these words are numbers: they certainly cannot be xts in a portable program, because an xt may consume a full cell, which may be larger than 16 bits, 32 bits, and in theory even larger than 64 bits. Also, the xt is specific to the process where it originates from, so it makes no sense to communicate it to elsewhere. The same goes for addresses.

So that leaves us with integer numbers and bitmaps (including Forth flags). For bitmaps, x is more appropriate than u. Does this outweigh the benefit of making the zero extension obvious? If we add x to the variant with the specialized types above, things become even more complex.

2s-complement

2s-Complement Wrap-Around Integers have been standardized at the 2015 meeting.

ruvavatar of ruv

2s-Complement Wrap-Around Integers have been standardized at the 2015 meeting.

Yes. But it's for single-cell and double-cell singed integer numbers in the Forth system.

If a binary interface uses another format for negative integers (for example, the least significant bit for sign), then the sequence:

w@ ( addr -- x ) wbe ( x -- x ) w>s ( x -- n )

does not return an implied signed integer number.

Why is this so from a formal point of view? Where are data types not matched?

I think, the text description for w>s ( x -- n ) should specify a format for the stack parameter in the position x. From this description it should be clear that the least significant 16 bits of x must represent a signed integer in two's complement format, and the remaining bits of x have no meaning.

ruvavatar of ruv

u vs. x

I don't see that it makes a difference in what kinds of programs are considered conforming to this specification or how systems are implemented,

"u" is a symbol for the "unsigned number" data type; the set of values of this data type is the integers in the range { 0 ... max-u } (max-u is defined in 3.2.6 Environmental queries). This data type defines not only a format (zero-extended), but also the interpretation for data objects as particular integer numbers. Namely, u defines a particular mapping from the set of data objects to the set of values.

If a mapping from the set of single-cell data objects to a set of values is unknown (or can vary) for a stack parameter, the standard can use the only x data type for this parameter.

For example, in the phrase w@ ( x1 ) dup 1 rshift swap %1 and if negate then ( n ) the stack parameter in the position x1 is not a member of u, since the u mapping from the data objects to the values does not hold for this parameter! In this example, if the parameter in the position x1 is %11 (as a tuple of bits), it is not the number 3, but the number -1, and it's defined by the application, not by the standard.

using u as output type for w@ and w@ lbe makes it very obvious that the result is zero-extended, no need to consult the prose of these words for determining that.

The use of u also implies the specific mapping from the set of data objects (bit tuples) to the set of values for the stack parameter. And this mapping does not hold. Therefore, this use is invalid.

Reply New Version