unaligned.h 5.1 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
#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)						\
A
Al Viro 已提交
63
	((__force typeof(*(ptr)))({					\
64 65 66 67 68 69
		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)))));			\
A
Al Viro 已提交
70
	}))
L
Linus Torvalds 已提交
71

72
#define __get_unaligned_be(ptr)						\
A
Al Viro 已提交
73
	((__force typeof(*(ptr)))({					\
74 75 76 77 78 79
		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)))));			\
A
Al Viro 已提交
80
	}))
L
Linus Torvalds 已提交
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


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)					\
	({							\
A
Al Viro 已提交
134
		(void)sizeof(*(ptr) = (val));			\
L
Linus Torvalds 已提交
135 136 137 138
		switch (sizeof(*(ptr))) {			\
		case 1:						\
			*(ptr) = (val);				\
			break;					\
A
Al Viro 已提交
139
		case 2: __put_unaligned_2_le((__force u16)(val),(__u8 *)(ptr));	\
L
Linus Torvalds 已提交
140
			break;					\
A
Al Viro 已提交
141
		case 4:	__put_unaligned_4_le((__force u32)(val),(__u8 *)(ptr));	\
L
Linus Torvalds 已提交
142
			break;					\
A
Al Viro 已提交
143
		case 8:	__put_unaligned_8_le((__force u64)(val),(__u8 *)(ptr)); \
L
Linus Torvalds 已提交
144 145 146 147 148 149 150 151 152
			break;					\
		default: __bug_unaligned_x(ptr);		\
			break;					\
		}						\
		(void) 0;					\
	})

#define __put_unaligned_be(val,ptr)					\
	({							\
A
Al Viro 已提交
153
		(void)sizeof(*(ptr) = (val));			\
L
Linus Torvalds 已提交
154 155 156 157
		switch (sizeof(*(ptr))) {			\
		case 1:						\
			*(ptr) = (val);				\
			break;					\
A
Al Viro 已提交
158
		case 2: __put_unaligned_2_be((__force u16)(val),(__u8 *)(ptr));	\
L
Linus Torvalds 已提交
159
			break;					\
A
Al Viro 已提交
160
		case 4:	__put_unaligned_4_be((__force u32)(val),(__u8 *)(ptr));	\
L
Linus Torvalds 已提交
161
			break;					\
A
Al Viro 已提交
162
		case 8:	__put_unaligned_8_be((__force u64)(val),(__u8 *)(ptr)); \
L
Linus Torvalds 已提交
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
			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