2.1 __int128 example #10

Closed
opened 4 years ago by wschmidt-ibm · 8 comments
wschmidt-ibm commented 4 years ago (Migrated from github.com)

Paul Clarke has requested an example of how to construct a vector __int128 constant, and this seems warranted.

Paul Clarke has requested an example of how to construct a vector __int128 constant, and this seems warranted.
ThinkOpenly commented 4 years ago (Migrated from github.com)

Is this sort-of the preferred way of constructing a (vector of) __int128?

vector unsigned __int128 b128 = { (((__int128)0x1020304050607080) << 64) | 0x90A0B0C0D0E0F000 };

...maybe an example like above with some words about how "Current C compilers" don't yet support native 128-bit constants. Actually, I now see that you have those words at the end of section 2.1. 👍

(I didn't know if you wanted to handle this issue, or if I should take a stab at it.)

Is this sort-of the preferred way of constructing a (vector of) `__int128`? ``` vector unsigned __int128 b128 = { (((__int128)0x1020304050607080) << 64) | 0x90A0B0C0D0E0F000 }; ``` ...maybe an example like above with some words about how "Current C compilers" don't yet support native 128-bit constants. Actually, I now see that you have those words at the end of section 2.1. :+1: (I didn't know if you wanted to handle this issue, or if I should take a stab at it.)
wschmidt-ibm commented 4 years ago (Migrated from github.com)

Does that even compile? That requires a 128-bit shift operation to be supported, which isn't there in ISA v3.0.

I believe what Steve has done in pveclib is create a union of an __int128 and two long longs. So you get something like

union u1 {
  __int128 x;
  long long y[2];
};
...
u1 z;
#ifdef __BIG_ENDIAN__
  z.y = {0x0001020304050607, 0x08090A0B0C0D0E0F};
#else 
  z.y = {0x08090A0B0C0D0E0F, 0x0001020304050607};
#endif 
__int128 result = z.x;
Does that even compile? That requires a 128-bit shift operation to be supported, which isn't there in ISA v3.0. I believe what Steve has done in pveclib is create a union of an __int128 and two long longs. So you get something like ``` union u1 { __int128 x; long long y[2]; }; ... u1 z; #ifdef __BIG_ENDIAN__ z.y = {0x0001020304050607, 0x08090A0B0C0D0E0F}; #else z.y = {0x08090A0B0C0D0E0F, 0x0001020304050607}; #endif __int128 result = z.x; ```
ThinkOpenly commented 4 years ago (Migrated from github.com)

It compiles and runs. You don't give GCC enough credit! ;-)

$ cat vector_int128.c; gcc --version; gcc -mcpu=power9 -g vector_int128.c -o vector_int128 && ./vector_int128
#include <stdio.h>
#include <altivec.h>
void out_int128 (vector unsigned __int128 v) {
	vector unsigned char r = (vector unsigned char) v;
	unsigned long i = 16;
	do {
		i--;
		printf ("%02x", r[i]);
	} while (i > 0);
	printf("\n");
}
int main() {
#ifdef NOPE
	vector unsigned __int128 b128 = { 0x10000000000000000000000000000000 };
#endif
	vector unsigned __int128 b128 = { (((__int128)0x1020304050607080) << 64) | 0x90A0B0C0D0E0F000 };
	out_int128 (b128);
	return 0;
}
gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4)
[...]

102030405060708090a0b0c0d0e0f000

My way doesn't require any #ifdefs.

Do you have a preference?

It compiles and runs. You don't give GCC enough credit! ;-) ``` $ cat vector_int128.c; gcc --version; gcc -mcpu=power9 -g vector_int128.c -o vector_int128 && ./vector_int128 #include <stdio.h> #include <altivec.h> void out_int128 (vector unsigned __int128 v) { vector unsigned char r = (vector unsigned char) v; unsigned long i = 16; do { i--; printf ("%02x", r[i]); } while (i > 0); printf("\n"); } int main() { #ifdef NOPE vector unsigned __int128 b128 = { 0x10000000000000000000000000000000 }; #endif vector unsigned __int128 b128 = { (((__int128)0x1020304050607080) << 64) | 0x90A0B0C0D0E0F000 }; out_int128 (b128); return 0; } gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4) [...] 102030405060708090a0b0c0d0e0f000 ``` My way doesn't require any `#ifdefs`. Do you have a preference?
ThinkOpenly commented 4 years ago (Migrated from github.com)

My example which uses the 128bit shift in the initializer also works on GCC 4.8.5 (RHEL 7.7).

My example which uses the 128bit shift in the initializer also works on GCC 4.8.5 (RHEL 7.7).
wschmidt-ibm commented 4 years ago (Migrated from github.com)

Please have a look at the code generation in the two different ways, compiling it for both LE and BE. Whichever looks better under those circumstances is what I would prefer to suggest to the community.

Please have a look at the code generation in the two different ways, compiling it for both LE and BE. Whichever looks better under those circumstances is what I would prefer to suggest to the community.
wschmidt-ibm commented 4 years ago (Migrated from github.com)

My guess is that both of them end up just generating a constant and loading it from memory...if that's the case, then I prefer your way.

My guess is that both of them end up just generating a constant and loading it from memory...if that's the case, then I prefer your way.
wschmidt-ibm commented 4 years ago (Migrated from github.com)

That would also require changing some text that talks about requiring different code for endianness...

That would also require changing some text that talks about requiring different code for endianness...
ThinkOpenly commented 4 years ago (Migrated from github.com)

The objdump -d of both waysis identical, and as you guessed: generating a constant and loading it from memory (lxv).

The `objdump -d` of both waysis identical, and as you guessed: generating a constant and loading it from memory (`lxv`).
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: systemsoftware/Programming-Guides#10
Loading…
There is no content yet.