unaligned.h 4.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
#ifndef __ASM_ARM_UNALIGNED_H
#define __ASM_ARM_UNALIGNED_H

#include <asm/types.h>

6
extern int __bug_unaligned_x(const void *ptr);
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

/*
 * What is the most efficient way of loading/storing an unaligned value?
 *
 * That is the subject of this file.  Efficiency here is defined as
 * minimum code size with minimum register usage for the common cases.
 * It is currently not believed that long longs are common, so we
 * trade efficiency for the chars, shorts and longs against the long
 * longs.
 *
 * Current stats with gcc 2.7.2.2 for these functions:
 *
 *	ptrsize	get:	code	regs	put:	code	regs
 *	1		1	1		1	2
 *	2		3	2		3	2
 *	4		7	3		7	3
 *	8		20	6		16	6
 *
 * gcc 2.95.1 seems to code differently:
 *
 *	ptrsize	get:	code	regs	put:	code	regs
 *	1		1	1		1	2
 *	2		3	2		3	2
 *	4		7	4		7	4
 *	8		19	8		15	6
 *
 * which may or may not be more efficient (depending upon whether
 * you can afford the extra registers).  Hopefully the gcc 2.95
 * is inteligent enough to decide if it is better to use the
 * extra register, but evidence so far seems to suggest otherwise.
 *
 * Unfortunately, gcc is not able to optimise the high word
 * out of long long >> 32, or the low word from long long << 32
 */

#define __get_unaligned_2_le(__p)					\
	(__p[0] | __p[1] << 8)

#define __get_unaligned_2_be(__p)					\
	(__p[0] << 8 | __p[1])

#define __get_unaligned_4_le(__p)					\
	(__p[0] | __p[1] << 8 | __p[2] << 16 | __p[3] << 24)

#define __get_unaligned_4_be(__p)					\
	(__p[0] << 24 | __p[1] << 16 | __p[2] << 8 | __p[3])

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#define __get_unaligned_8_le(__p)					\
	((unsigned long long)__get_unaligned_4_le((__p+4)) << 32 |	\
		__get_unaligned_4_le(__p))

#define __get_unaligned_8_be(__p)					\
	((unsigned long long)__get_unaligned_4_be(__p) << 32 |		\
		__get_unaligned_4_be((__p+4)))

#define __get_unaligned_le(ptr)						\
	({								\
		const __u8 *__p = (const __u8 *)(ptr);			\
		__builtin_choose_expr(sizeof(*(ptr)) == 1, *__p,	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 2, __get_unaligned_2_le(__p),	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 4, __get_unaligned_4_le(__p),	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 8, __get_unaligned_8_le(__p),	\
		    (void)__bug_unaligned_x(__p)))));			\
L
Linus Torvalds 已提交
70 71
	})

72 73 74 75 76 77 78 79
#define __get_unaligned_be(ptr)						\
	({								\
		const __u8 *__p = (const __u8 *)(ptr);			\
		__builtin_choose_expr(sizeof(*(ptr)) == 1, *__p,	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 2, __get_unaligned_2_be(__p),	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 4, __get_unaligned_4_be(__p),	\
		  __builtin_choose_expr(sizeof(*(ptr)) == 8, __get_unaligned_8_be(__p),	\
		    (void)__bug_unaligned_x(__p)))));			\
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
	})


static inline void __put_unaligned_2_le(__u32 __v, register __u8 *__p)
{
	*__p++ = __v;
	*__p++ = __v >> 8;
}

static inline void __put_unaligned_2_be(__u32 __v, register __u8 *__p)
{
	*__p++ = __v >> 8;
	*__p++ = __v;
}

static inline void __put_unaligned_4_le(__u32 __v, register __u8 *__p)
{
	__put_unaligned_2_le(__v >> 16, __p + 2);
	__put_unaligned_2_le(__v, __p);
}

static inline void __put_unaligned_4_be(__u32 __v, register __u8 *__p)
{
	__put_unaligned_2_be(__v >> 16, __p);
	__put_unaligned_2_be(__v, __p + 2);
}

static inline void __put_unaligned_8_le(const unsigned long long __v, register __u8 *__p)
{
	/*
	 * tradeoff: 8 bytes of stack for all unaligned puts (2
	 * instructions), or an extra register in the long long
	 * case - go for the extra register.
	 */
	__put_unaligned_4_le(__v >> 32, __p+4);
	__put_unaligned_4_le(__v, __p);
}

static inline void __put_unaligned_8_be(const unsigned long long __v, register __u8 *__p)
{
	/*
	 * tradeoff: 8 bytes of stack for all unaligned puts (2
	 * instructions), or an extra register in the long long
	 * case - go for the extra register.
	 */
	__put_unaligned_4_be(__v >> 32, __p);
	__put_unaligned_4_be(__v, __p+4);
}

/*
 * Try to store an unaligned value as efficiently as possible.
 */
#define __put_unaligned_le(val,ptr)					\
	({							\
		switch (sizeof(*(ptr))) {			\
		case 1:						\
			*(ptr) = (val);				\
			break;					\
		case 2: __put_unaligned_2_le((val),(__u8 *)(ptr));	\
			break;					\
		case 4:	__put_unaligned_4_le((val),(__u8 *)(ptr));	\
			break;					\
		case 8:	__put_unaligned_8_le((val),(__u8 *)(ptr)); \
			break;					\
		default: __bug_unaligned_x(ptr);		\
			break;					\
		}						\
		(void) 0;					\
	})

#define __put_unaligned_be(val,ptr)					\
	({							\
		switch (sizeof(*(ptr))) {			\
		case 1:						\
			*(ptr) = (val);				\
			break;					\
		case 2: __put_unaligned_2_be((val),(__u8 *)(ptr));	\
			break;					\
		case 4:	__put_unaligned_4_be((val),(__u8 *)(ptr));	\
			break;					\
		case 8:	__put_unaligned_8_be((val),(__u8 *)(ptr)); \
			break;					\
		default: __bug_unaligned_x(ptr);		\
			break;					\
		}						\
		(void) 0;					\
	})

/*
 * Select endianness
 */
#ifndef __ARMEB__
#define get_unaligned	__get_unaligned_le
#define put_unaligned	__put_unaligned_le
#else
#define get_unaligned	__get_unaligned_be
#define put_unaligned	__put_unaligned_be
#endif

#endif