Does RISC-V prefer to sign-extend 8-bit operands? - riscv

On 64-bit RISC-V, when a 32-bit operand is loaded into a register, it is necessary to decide whether to sign-extend or zero-extend to 64 bits, and the architectural decision was made to prefer the former, presumably on the grounds that the most common int type in C family languages is a signed 32-bit integer. So sign extension is slightly faster than zero extension.
Is the same true of 8-bit operands? In other words, is signed char more efficient than unsigned char?

If you’re going to be widening a lot of 8-bit values to wchar_t, unsigned char is what you want, because that’s a no-op rather than a bitmask. If your char format is UTF-8, you also want to be able to use unsigned math for your shifts. If you’re using library functions, it’s most convenient to use the types your library expects.
The RISC-V architecture has both a LB instruction that loads a sign-extended 8-bit value into a register, and a LBU instruction that zero-extends. Both are equally efficient. In C, any signed char used in an arithmetic operation is widened to int, and the C standard library functions specify widening char to int, so this puts the variable in the correct format to use.
Storing is a matter of truncation, and converting from any integral type to unsigned char is trivial (bitmask by 0xff). Converting from an unsigned char to a signed value can be done in no more than two instructions, without conditionals or register pressure (SLLI to put the sign bit of the char into the sign bit of the machine register, followed by SRLI to sign-extend the upper bits).
There is therefore no additional overhead in this architecture to working with either. The API specifies sign-extension rather than zero-extension of signed quantities.
Incidentally, RV64I does not architecturally prefer sign-extension. That is the ABI convention, but the instruction set adds a LWU instruction to load a 32-bit value from memory with zero-extension and an ADDIW that can sign-extend a zero-extended 32-bit result. (There is no corresponding ADDIB for 8-bit or ADDIH for 16-bit quantities.)

Related

How RISC-V Word Operation is performed?

In case of 64-bit processor,if word operation is to be performed then will the 32nd bit will be considered as signed bit or as a magnitude bit?
The ADDW, SUBW instructions, for example, from riscv-spec-v2.2:
ADDW and SUBW are RV64I-only instructions that are defined analogously
to ADD and SUB but operate on 32-bit values and produce signed 32-bit
results. Overflows are ignored, and the low 32-bits of the result is
sign-extended to 64-bits and written to the destination register.
thus a sign bit. If you don’t want this, then don’t use the ‘w’ variants and make sure the upper register bits are zeros. It gets a bit unusual around the multiply instructions, where you have to manage the upper and lower explicitly.

What are UInt16LE, UInt16BE, etc. in Node JS?

In all of my time programming I have squeaked by without ever learning this stuff. Would love to know more about what these are and how they are used:
UInt8
UInt16LE
UInt16BE
UInt32LE
UInt32BE
Int8
Int16LE
Int16BE
Int32LE
Int32BE
FloatLE
FloatBE
DoubleLE
DoubleBE
See https://nodejs.org/api/buffer.html#buffer_buf_readuint8_offset_noassert for where Node uses these.
This datatypes are related to number representation in appropriate byte-order. It typically essential for:
Network protocols
Binary file formats
It is essential because one system should write integers/floats in such way that will give the same value on reader side. So what format to be used is just convention between two sides (writer and reader).
What acronyms means:
BE suffix stands for BigEndian
LE stands for LittleEndian
Int is Integer
Uint is Unsigned Integer
Appropriate number in integers is number of bits in the word.

Visual C++ x86 ABI: how does VC++ return value?

I know 1/2/4-byte integers are returned in eax, and 8-byte integers are returned in eax:edx.
By the way, how are 4/8/16-byte floating-point values (Maybe I remember long double might be 16 bytes..) returned in cdecl/stdcall?
Thanks to #MatteoItalia, I know that VC++'s long double is 8-byte; Then, how can I use 16-byte floating-point?
(Don't say me "just use 8 byte!". I really need it.)
Um, I think I should be satisfied with 10-byte floating point...
FP return values are returned in the ST0 x87 register (see e.g. here).
By the way, in VC++ long double (which in x87 is 80 bit) is effectively a synonym for double.
You didn't provide the architecture but x86 returns floating-point values in ST(0) and x86_64 return in XMM0. See x86 calling conventions
But long double in VC for x86 and x86_64 is the same as double and won't provide you any more precision. So on Windows to do 80-bit floating-point operations you need to use another compiler such as GCC, Clang or ICC. Also, 80-bit long double is calculated by x87 so it may perform worse than a good SSE library.
If you need more than 10-byte long double then you must implement your own floating-point library or use some external libraries. GCC 4.3 and above have built-in support for __float128 through a soft library. See long double (GCC specific) and __float128
Another approach is using double-double to implement near-quadruple precision values like in PowerPC or SPARC. It's not IEEE compatible but you can utilize hardware double suport to speedup so it'll be faster than soft __float128

Is there a difference between datatypes on different bit-size OSes?

I have a C program that I know works on 32-bit systems. On 64-Bit systems (at least mine) it works to a point and then stops. Reading some forums the program may not be 64-bit safe? I assume it has to do with differences of data types between 32-bit and 64-bit systems.
Is a char the same on both? what about int or long or their unsigned variants? Is there any other way a 32-bit program wouldn't be 64-bit safe? If I wanted to verify the application is 64-bit safe, what steps should I take?
Regular data types in C has minimum ranges of values rather than specific bit widths. For example, a short has to be able to represent, at a minimum, -32767 thru 32767 inclusive.
So,yes, if your code depends on values wrapping around at 32768, it's unlikely to behave well if the short is some big honking 128-bit behemoth.
If you want specific-width data types, look into stdint.h for things like int64_t and so on. There are a wide variety to choose from, specific widths, "at-least" widths, and so on. They also mandate two's complement for these, unlike the "regular" integral types:
integer types having certain exact widths;
integer types having at least certain specified widths;
fastest integer types having at least certain specified widths;
integer types wide enough to hold pointers to objects;
integer types having greatest width.
For example, from C11 7.20.1.1 Exact-width integer types:
The typedef name intN_t designates a signed integer type with width N, no padding
bits, and a two’s complement representation. Thus, int8_t denotes such a signed
integer type with a width of exactly 8 bits.
Provided you have followed the rules (things like not casting pointers to integers), your code should compile and run on any implementation, and any architecture.
If it doesn't, you'll just have to start debugging, then post the detailed information and code that seems to be causing problem on a forum site dedicated to such things. Now where have I seen one of those recently? :-)

What precisely is the "silence" value in the SDL audio API?

In SDL, when you set up your audio output device, you and SDL have to agree on an audio format - e.g. 44.1KHz stereo 16-bit signed little-endian. That's fine. But along with the final agreed format, you also get a computed "silence" value which doesn't seem well documented.
A silent sound sample obviously consist of the same sample value repeated over and over again, and you want that to be at the "zero" level. In a sense any constant value will do, but you have to agree a value (so you don't get pops when switching to a different sound), and in a sane world you want to choose a value bang in the centre of your sample-value range.
So if you happen to use an unsigned format for your sample value range for 0..whatever, your silence value will be (whatever/2).
EDIT - inserted "unsigned" below to avoid confusion.
That's all fine. But the silence value you get given is an unsigned 8-bit integer. That doesn't work very well if you want unsigned 16 bit samples - the logical silence value of 0x8000 requires two different byte values and it requires them to be in the correct endian order.
So the silence value you get from SDL doesn't seem to make much sense. You can't use it to wipe your buffers, for instance, without dealing with extra complications and making inferences which pretty much make the precalculated silence value pointless anyway.
Which means, of course, that I've misunderstood the point.
So - if this isn't how the silence value is meant to be used, how should it be used?
I have no evidence to back this up but I think the assumption here is that "silence" could be interpreted as "silence for common soundcard formats". Those being:
Unsigned 8-bit integers
Signed 16-bit integers
Signed 32-bit integers (for 24-bit audio data)
Normalized 32-bit floating point
Normalized 64-bit floating point.
In all the cases except for unsigned 8-bit, zero (0) is the "zero amplitude" value. So the returned unsigned 8-bit integer contains all the possible values of "zero amplitude" for these formats.

Resources