File: /usr/src/linux/include/asm-mips/unaligned.h

1     /*
2      * This file is subject to the terms and conditions of the GNU General Public
3      * License.  See the file "COPYING" in the main directory of this archive
4      * for more details.
5      *
6      * Copyright (C) 1996, 1999, 2000 by Ralf Baechle
7      * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8      */
9     #ifndef _ASM_UNALIGNED_H
10     #define _ASM_UNALIGNED_H
11     
12     extern void __get_unaligned_bad_length(void);
13     extern void __put_unaligned_bad_length(void);
14     
15     /*
16      * Load double unaligned.
17      *
18      * This could have been implemented in plain C like IA64 but egcs 1.0.3a
19      * inflates this to 23 instructions ...
20      */
21     extern inline unsigned long long __ldq_u(const unsigned long long * __addr)
22     {
23     	unsigned long long __res;
24     
25     	__asm__("ulw\t%0, %1\n\t"
26     		"ulw\t%D0, 4+%1"
27     		: "=&r" (__res)
28     		: "m" (*__addr));
29     
30     	return __res;
31     }
32     
33     /*
34      * Load word unaligned.
35      */
36     extern inline unsigned long __ldl_u(const unsigned int * __addr)
37     {
38     	unsigned long __res;
39     
40     	__asm__("ulw\t%0,%1"
41     		: "=&r" (__res)
42     		: "m" (*__addr));
43     
44     	return __res;
45     }
46     
47     /*
48      * Load halfword unaligned.
49      */
50     extern inline unsigned long __ldw_u(const unsigned short * __addr)
51     {
52     	unsigned long __res;
53     
54     	__asm__("ulh\t%0,%1"
55     		: "=&r" (__res)
56     		: "m" (*__addr));
57     
58     	return __res;
59     }
60     
61     /*
62      * Store doubleword ununaligned.
63      */
64     extern inline void __stq_u(unsigned long __val, unsigned long long * __addr)
65     {
66     	__asm__("usw\t%1, %0\n\t"
67     		"usw\t%D1, 4+%0"
68     		: "=m" (*__addr)
69     		: "r" (__val));
70     }
71     
72     /*
73      * Store long ununaligned.
74      */
75     extern inline void __stl_u(unsigned long __val, unsigned int * __addr)
76     {
77     	__asm__("usw\t%1, %0"
78     		: "=m" (*__addr)
79     		: "r" (__val));
80     }
81     
82     /*
83      * Store word ununaligned.
84      */
85     extern inline void __stw_u(unsigned long __val, unsigned short * __addr)
86     {
87     	__asm__("ush\t%1, %0"
88     		: "=m" (*__addr)
89     		: "r" (__val));
90     }
91     
92     /*
93      * get_unaligned - get value from possibly mis-aligned location
94      * @ptr: pointer to value
95      *
96      * This macro should be used for accessing values larger in size than 
97      * single bytes at locations that are expected to be improperly aligned, 
98      * e.g. retrieving a u16 value from a location not u16-aligned.
99      *
100      * Note that unaligned accesses can be very expensive on some architectures.
101      */
102     #define get_unaligned(ptr)						\
103     ({									\
104     	__typeof__(*(ptr)) __val;					\
105     									\
106     	switch (sizeof(*(ptr))) {					\
107     	case 1:								\
108     		__val = *(const unsigned char *)ptr;			\
109     		break;							\
110     	case 2:								\
111     		__val = __ldw_u((const unsigned short *)ptr);		\
112     		break;							\
113     	case 4:								\
114     		__val = __ldl_u((const unsigned int *)ptr);		\
115     		break;							\
116     	case 8:								\
117     		__val = __ldq_u((const unsigned long long *)ptr);	\
118     		break;							\
119     	default:							\
120     		__get_unaligned_bad_length();				\
121     		break;							\
122     	}								\
123     									\
124     	__val;								\
125     })
126     
127     /*
128      * put_unaligned - put value to a possibly mis-aligned location
129      * @val: value to place
130      * @ptr: pointer to location
131      *
132      * This macro should be used for placing values larger in size than 
133      * single bytes at locations that are expected to be improperly aligned, 
134      * e.g. writing a u16 value to a location not u16-aligned.
135      *
136      * Note that unaligned accesses can be very expensive on some architectures.
137      */
138     #define put_unaligned(val,ptr)						\
139     do {									\
140     	switch (sizeof(*(ptr))) {					\
141     	case 1:								\
142     		*(unsigned char *)(ptr) = (val);			\
143     		break;							\
144     	case 2:								\
145     		__stw_u(val, (unsigned short *)(ptr));			\
146     		break;							\
147     	case 4:								\
148     		__stl_u(val, (unsigned int *)(ptr));			\
149     		break;							\
150     	case 8:								\
151     		__stq_u(val, (unsigned long long *)(ptr));		\
152     		break;							\
153     	default:							\
154     		__put_unaligned_bad_length();				\
155     		break;							\
156     	}								\
157     } while(0)
158     
159     #endif /* _ASM_UNALIGNED_H */
160