[ltt-dev] [RFC] Portable bitfields for trace format
Mathieu Desnoyers
mathieu.desnoyers at efficios.com
Sat Sep 18 01:42:29 EDT 2010
Hi,
After looking at gcc bitfields, I found out that they were really non-portable.
One particularity of these bitfields is that the fields are put from high to low
bits on big endian, and from low to high bits on little endian. So it is not
enough to do the byte swap between architectures, one must also take care to
reorder the fields.
Also, the fact that gcc requires bitfields to fit in the current unit adds much
padding that is unwanted in a trace e.g.
struct {
int a:3;
int b:31;
int c:3;
};
will be 12 bytes in size because "b" does not fit in the first "int" unit.
So I created a bitfield library with write and read primitives that can be made
compatible with gcc bitfields by specifying the bit padding manually. The
write-side writes bitfields in the native endianness, either with 1, 2, 4 or 8
bytes memory writes. The read side is architecture-agnostic, so it can read
bitfields generated by either little or big endian architectures.
The userspace code is below. It includes a rather useful test-suite. The code
generated is quite compact when the parameters are fixed. A x86-32 disassembly
of fct() below which contains the macro instance:
unsigned int glob[1];
void fct(void)
{
bitfield_write(glob, 0x12345678, 12, 15);
}
Compared to this, we have the generated assembly from gcc 4.3.4 for:
struct d3 {
unsigned int a:12;
unsigned int b:15;
};
struct d3 glob;
void fct(void)
{
glob.b = 0x12345678;
}
glob.b = 0x12345678;
On x86-32, the bitfield library looks like:
08048470 <fct>:
8048470: a1 70 ba 04 08 mov 0x804ba70,%eax
8048475: 55 push %ebp
8048476: 89 e5 mov %esp,%ebp
8048478: 5d pop %ebp
8048479: 25 ff 0f 00 f8 and $0xf8000fff,%eax
804847e: 0d 00 80 67 05 or $0x5678000,%eax
8048483: a3 70 ba 04 08 mov %eax,0x804ba70
8048488: c3 ret
8048489: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
The gcc bitfields generate:
080483d0 <fct>:
80483d0: a1 64 96 04 08 mov 0x8049664,%eax
80483d5: 55 push %ebp
80483d6: 89 e5 mov %esp,%ebp
80483d8: 5d pop %ebp
80483d9: 25 ff 0f 00 f8 and $0xf8000fff,%eax
80483de: 0d 00 80 67 05 or $0x5678000,%eax
80483e3: a3 64 96 04 08 mov %eax,0x8049664
80483e8: c3 ret
80483e9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
and on powerpc 32, the bitfield library:
10000520 <fct>:
10000520: 3d 20 10 01 lis r9,4097
10000524: 80 09 33 80 lwz r0,13184(r9)
10000528: 54 00 06 d6 rlwinm r0,r0,0,27,11
1000052c: 64 00 00 0a oris r0,r0,10
10000530: 60 00 cf 00 ori r0,r0,52992
10000534: 90 09 33 80 stw r0,13184(r9)
10000538: 4e 80 00 20 blr
1000053c: 60 00 00 00 nop
And gcc bitfields:
10000490 <fct>:
10000490: 3d 20 10 01 lis r9,4097
10000494: 39 60 56 78 li r11,22136
10000498: 80 09 0a a4 lwz r0,2724(r9)
1000049c: 51 60 2b 34 rlwimi r0,r11,5,12,26
100004a0: 90 09 0a a4 stw r0,2724(r9)
100004a4: 4e 80 00 20 blr
100004a8: 60 00 00 00 nop
100004ac: 60 00 00 00 nop
Comments are welcome (code below).
Thanks,
Mathieu
/*
* Common Trace Format
*
* Bitfields implementation
*
* Copyright 2010 - Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
*
* Dual LGPL v2.1/GPL v2 license.
*/
#include <endian.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
/* We can't shift a int from 32 bit, >> 32 on int is a no-op on x86 */
#define piecewise_rshift(v, shift) \
do { \
int sb = (shift) / (sizeof(v) * 8 - 1); \
int final = (shift) % (sizeof(v) * 8 - 1); \
\
for (; sb; sb--) \
(v) >>= sizeof(v) * 8 - 1; \
(v) >>= final; \
} while (0)
/*
* Save integer to the bitfield, which starts at the "start" bit, has "len"
* bits.
* The inside of a bitfield is from high bits to low bits.
* Uses native endianness.
* For unsigned "v", pad MSB with 0 if bitfield is larger than v.
* For signed "v", sign-extend v if bitfield is larger than v.
*
* On little endian, bytes are placed from the less significant to the most
* significant. Also, consecutive bitfields are placed from lower bits to higher
* bits.
*
* On big endian, bytes are places from most significant to less significant.
* Also, consecutive bitfields are placed from higher to lower bits.
*/
#if (BYTE_ORDER == LITTLE_ENDIAN)
#define bitfield_write(ptr, _v, _start, _length) \
do { \
typeof(*(ptr)) mask, cmask; \
unsigned int start = (_start), length = (_length); \
typeof(_v) v = (_v); \
int start_unit, end_unit, this_unit; \
unsigned int end, cshift; /* cshift is "complement shift" */ \
unsigned int ts = sizeof(__typeof__(*(ptr))) * 8; /* type size */ \
\
if (!length) \
break; \
\
end = start + length; \
start_unit = start / ts; \
end_unit = (end + (ts - 1)) / ts; \
\
/* Trim v high bits */ \
if (length < sizeof(v) * 8) \
v &= ~(~0ULL << length); \
\
/* We can now append v with a simple "or", shift it piece-wise */ \
this_unit = start_unit; \
if (start_unit == end_unit - 1) { \
mask = ~((typeof(*(ptr))) ~0ULL << (start % ts)); \
mask |= (typeof(*(ptr))) ~0ULL << (end % ts ? : ts); \
cmask = (typeof(*(ptr))) v << (start % ts); \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
break; \
} \
if (start % ts) { \
cshift = start % ts; \
mask = ~((typeof(*(ptr))) ~0ULL << cshift); \
cmask = (typeof(*(ptr))) v << cshift; \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
if (ts - cshift >= sizeof(v) * 8) \
piecewise_rshift(v, ts - cshift); \
else \
v >>= ts - cshift; \
start += ts - cshift; \
this_unit++; \
} \
for (; this_unit < end_unit - 1; this_unit++) { \
ptr[this_unit] = (typeof(*(ptr))) v; \
if (ts >= sizeof(v) * 8) \
piecewise_rshift(v, ts); \
else \
v >>= ts; \
start += ts; \
} \
if (end % ts) { \
mask = (typeof(*(ptr))) ~0ULL << (end % ts); \
cmask = (typeof(*(ptr))) v; \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
} else \
ptr[this_unit] = (typeof(*(ptr))) v; \
} while (0)
#else /* (BYTE_ORDER != LITTLE_ENDIAN) */
#define bitfield_write(ptr, _v, _start, _length) \
do { \
typeof(_v) v = (_v); \
unsigned int start = _start, length = _length; \
int start_unit, end_unit, this_unit; \
unsigned int end, cshift; /* cshift is "complement shift" */ \
typeof(*(ptr)) mask, cmask; \
unsigned int ts = sizeof(__typeof__(*(ptr))) * 8; /* type size */ \
\
if (!length) \
break; \
\
end = start + length; \
start_unit = start / ts; \
end_unit = (end + (ts - 1)) / ts; \
\
/* Trim v high bits */ \
if (length < sizeof(v) * 8) \
v &= ~(~0ULL << length); \
\
/* We can now append v with a simple "or", shift it piece-wise */ \
this_unit = end_unit - 1; \
if (start_unit == end_unit - 1) { \
mask = ~((typeof(*(ptr))) ~0ULL << ((ts - (end % ts)) % ts)); \
mask |= (typeof(*(ptr))) ~0ULL << (ts - (start % ts)); \
cmask = (typeof(*(ptr))) v << ((ts - (end % ts)) % ts); \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
break; \
} \
if (end % ts) { \
cshift = end % ts; \
mask = ~((typeof(*(ptr))) ~0ULL << (ts - cshift)); \
cmask = (typeof(*(ptr))) v << (ts - cshift); \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
if (cshift >= sizeof(v) * 8) \
piecewise_rshift(v, cshift); \
else \
v >>= cshift; \
end -= cshift; \
this_unit--; \
} \
for (; this_unit >= start_unit + 1; this_unit--) { \
ptr[this_unit] = (typeof(*(ptr))) v; \
if (ts >= sizeof(v) * 8) \
piecewise_rshift(v, ts); \
else \
v >>= ts; \
end -= ts; \
} \
if (start % ts) { \
mask = (typeof(*(ptr))) ~0ULL << (ts - (start % ts)); \
cmask = (typeof(*(ptr))) v; \
ptr[this_unit] &= mask; \
ptr[this_unit] |= cmask; \
} else \
ptr[this_unit] = (typeof(*(ptr))) v; \
} while (0)
#endif
/*
* Read a bitfield byte-wise. This function is arch-agnostic.
*/
uint64_t bitfield_read_64(unsigned char *ptr,
unsigned int start, unsigned int len,
int byte_order, int signedness)
{
int start_unit, end_unit, this_unit;
unsigned int end, cshift; /* cshift is "complement shift" */
unsigned int ts = sizeof(unsigned char) * 8;
unsigned char mask, cmask;
uint64_t v = 0;
if (!len)
return 0;
end = start + len;
start_unit = start / ts;
end_unit = (end + (ts - 1)) / ts;
/*
* We can now fill v piece-wise, from lower bits to upper bits.
* We read the bitfield in the opposite direction it was written.
*/
switch (byte_order) {
case LITTLE_ENDIAN:
this_unit = end_unit - 1;
if (signedness) {
if (ptr[this_unit] & (1U << ((end % ts ? : ts) - 1)))
v = ~0ULL;
}
if (start_unit == end_unit - 1) {
mask = (unsigned char) ~0ULL << (end % ts ? : ts);
mask |= ~((unsigned char) ~0ULL << (start % ts));
cmask = ptr[this_unit];
cmask &= ~mask;
cmask >>= (start % ts);
v <<= end - start;
v |= cmask;
break;
}
if (end % ts) {
cshift = (end % ts ? : ts);
mask = (unsigned char) ~0ULL << cshift;
cmask = ptr[this_unit];
cmask &= ~mask;
v <<= cshift;
v |= (uint64_t) cmask;
end -= cshift;
this_unit--;
}
for (; this_unit >= start_unit + 1; this_unit--) {
v <<= ts;
v |= (uint64_t) ptr[this_unit];
end -= ts;
}
if (start % ts) {
cmask = ptr[this_unit] >> (start % ts);
v <<= ts - (start % ts);
v |= (uint64_t) cmask;
} else {
v <<= ts;
v |= (uint64_t) ptr[this_unit];
}
break;
case BIG_ENDIAN:
this_unit = start_unit;
if (signedness) {
if (ptr[this_unit] & (1U << (ts - (start % ts) - 1)))
v = ~0ULL;
}
if (start_unit == end_unit - 1) {
mask = (unsigned char) ~0ULL << (ts - (start % ts));
mask |= ~((unsigned char) ~0ULL << ((ts - (end % ts)) % ts));
cmask = ptr[this_unit];
cmask &= ~mask;
cmask >>= (ts - (end % ts)) % ts;
v <<= end - start;
v |= (uint64_t) cmask;
break;
}
if (start % ts) {
mask = (unsigned char) ~0ULL << (ts - (start % ts));
cmask = ptr[this_unit];
cmask &= ~mask;
v <<= ts - (start % ts);
v |= (uint64_t) cmask;
start += ts - (start % ts);
this_unit++;
}
for (; this_unit < end_unit - 1; this_unit++) {
v <<= ts;
v |= (uint64_t) ptr[this_unit];
start += ts;
}
if (end % ts) {
cmask = ptr[this_unit];
cmask >>= (ts - (end % ts)) % ts;
v <<= (end % ts);
v |= (uint64_t) cmask;
} else {
v <<= ts;
v |= (uint64_t) ptr[this_unit];
}
break;
default:
assert(0);
}
return v;
}
unsigned int glob[1];
void fct(void)
{
bitfield_write(glob, 0x12345678, 12, 15);
}
int run_test_unsigned(void)
{
unsigned long src;
union {
unsigned char c[8];
unsigned short s[4];
unsigned int i[2];
unsigned long l[2];
unsigned long long ll[1];
} target;
uint64_t readval;
unsigned int s, l;
int err = 0;
printf("Running unsigned test\n");
src = 1;
for (s = 0; s < 64; s++) {
for (l = 1; l < 64 - s; l++) {
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.c, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 0);
if (readval != src) {
printf("Error (bytewise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.s, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 0);
if (readval != src) {
printf("Error (shortwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.i, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 0);
if (readval != src) {
printf("Error (intwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.l, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 0);
if (readval != src) {
printf("Error (longwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.ll, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 0);
if (readval != src) {
printf("Error (longlongwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
}
}
if (!err)
printf("Success!\n");
else
printf("Failed!\n");
return err;
}
int run_test_signed(void)
{
long src;
union {
char c[8];
short s[4];
int i[2];
long l[2];
long long ll[1];
} target;
int64_t readval;
unsigned int s, l;
int err = 0;
printf("Running signed test\n");
src = 1;
for (s = 0; s < 64; s++) {
for (l = 1; l < 64 - s; l++) {
target.i[0] = 0x00000000;
target.i[1] = 0x00000000;
bitfield_write(target.c, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 1);
if (readval != src) {
printf("Error (bytewise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0x00000000;
target.i[1] = 0x00000000;
bitfield_write(target.s, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 1);
if (readval != src) {
printf("Error (shortwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0x00000000;
target.i[1] = 0x00000000;
bitfield_write(target.i, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 1);
if (readval != src) {
printf("Error (intwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0x00000000;
target.i[1] = 0x00000000;
bitfield_write(target.l, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 1);
if (readval != src) {
printf("Error (longwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
target.i[0] = 0x00000000;
target.i[1] = 0x00000000;
bitfield_write(target.ll, src, s, l);
readval = bitfield_read_64(target.c, s, l, BYTE_ORDER, 1);
if (readval != src) {
printf("Error (longlongwise) src %lX read %llX shift %d len %d\n",
src, readval, s, l);
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
err = 1;
}
}
}
if (!err)
printf("Success!\n");
else
printf("Failed!\n");
return err;
}
int run_test(void)
{
int err = 0;
err |= run_test_unsigned();
err |= run_test_signed();
return err;
}
int main(int argc, char **argv)
{
unsigned long src;
unsigned int shift, len;
int ret;
union {
unsigned char c[8];
unsigned short s[4];
unsigned int i[2];
unsigned long l[2];
unsigned long long ll[1];
} target;
uint64_t readval;
if (argc > 1)
src = atoi(argv[1]);
else
src = 0x12345678;
if (argc > 2)
shift = atoi(argv[2]);
else
shift = 12;
if (argc > 3)
len = atoi(argv[3]);
else
len = 40;
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.c, src, shift, len);
printf("bytewise\n");
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.s, src, shift, len);
printf("shortwise\n");
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.i, src, shift, len);
printf("intwise\n");
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.l, src, shift, len);
printf("longwise\n");
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
target.i[0] = 0xFFFFFFFF;
target.i[1] = 0xFFFFFFFF;
bitfield_write(target.ll, src, shift, len);
printf("lluwise\n");
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
target.c[0], target.c[1], target.c[2], target.c[3],
target.c[4], target.c[5], target.c[6], target.c[7]);
readval = bitfield_read_64(target.c, shift, len, BYTE_ORDER, 0);
printf("read: %llX\n", readval);
ret = run_test();
return ret;
}
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list