Date: Sun, 6 Apr 1997 12:24:50 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: hm@kts.org Cc: freebsd-hackers@freebsd.org Subject: Re: How to declare device driver variables and data structures ? Message-ID: <199704061924.MAA28524@phaeton.artisoft.com> In-Reply-To: <m0wDonp-00002GC@ernie.kts.org> from "Hellmuth Michaelis" at Apr 6, 97 12:04:01 pm
next in thread | previous in thread | raw e-mail | index | archive | help
> Inside a device driver or a piece of kernel software, how should one decleare > the size of variables (if one has the choice): > > i.e. if i know a variable can only have values 0 ... 32, shall i make it > an unsigned char ? Is it slower to make such variables an unsigned int > or an unsigned short ? What are the space vs. speed considerations ? As far as types go, "int" is faster. Lattice and several other 68k compilers got "int" wrong, and made it the register size instead of the bus transfer size, and made 68k "int" slow. The default for Aztec and several other Macintosh and Amiga compilers (other than Lattice) was 16 bits, the size of a datum that could be transferred in a single bus fetch. All that said, don't use int unless your ranges are short sized or smaller (int is guaranteed to be as large or larger than short). Of course, there's no guarantee that they won't implement "sort" as less than 16 bits... > i.e. if i know a variable can only have values 0 ... 2048, shall i make it > an unsigned short ? Is it slower to make such variables an unsigned int ? I would recommend that you use the defined sized types (int8_t, int16_t, int32_t) and the unsigned sized types (u_int8_t, u_int16_t, u_int32_t) for devices with defined size requirements. This insures that moving from compiler to compiler and architecture to architecture, your code will still run (even if it isn't as effecient as it might be). One could argue that for the kernel, sized types < sizeof(int) should be implemented with int, for a lot of reasons, including access on alignment boundries being guaranteed for int, but not for anything else (aligned access requirements are a bus attribute, and int is supposed to be sized based on the bus transfer). For systems where there is more strict alignment requirements, the compiler should take care of it for you (either by generating extraction instructions, or, preferrably for speed, promoting the size of int up). > another i.e. for boolean variables, is it better to make them chars, shorts > or ints ? signed or unsigned ? Depends. Again, "int" is easiest, and you need only one bit, so you don't care about size, but are you sure that boolean will not have to change to something else in a later rev of the code? > What is the speed of unsigned variables vs. signed variables ? Generally no difference on a two's complement machine. Most machines are two's complement. The speed penalties for signed vs. unsigned variables come into play when converting from one size of type to another. In order for an 8 bit minus one (0xff) to become a 16 bit minus one (0xffff), it must be "sign extended" on conversion. On some hardware, this is done by emitting code; on other hardware, the processor can do it automatically as part of the access/load cycles. If you want it to be generally fast instead of fast on some hardware and slow on others, use unsigned where possible... or avoid conversions (many comparisons cause implicit sign extension to int, unless you are very careful in your casting). > What is with future ports to other architectures ? Be careful with alignment and assumption of sizes of atomic types that are not per-architecture typedef'ed to sized types. If you use a sized type in a structure, the size will be invariant across architectures (as long as the size is supported at all! Beware the 16 bit size!). Actually, one thing I've wanted for some time now is to be able to turn on the "unaligned access exception bit" on the Intel platform to get the software into shape by flagging the accesses, but not crippling the system while they are being fixed. This would mean trapping exception 17 (decimal) and setting bit 18 of EFlags and bit 18 of Control Register 0 (unfortunately, it only works against code running at priveledge level 3, so it won't help kernel programmers). > How does this all apply to variables being part of structures ? For structures which overlay device data, the structure packing must be such that the device data can overlay correctly. In general, this means setting the packing to 1 (using "#pragma pack(1)" in gcc) and assuming a byte-for-byte alignment of data. Since you still have issues of access alignment, the actual structure may be padded seperately from its packing to force the overall structure to an alignment boundry. When you reference the data in the structure, you want to copy the data if it's a data bus access using an alignment boundry granularity for the copy (this is why the padding is there: so that the extra "garbage" data doesn't stomp on anything). If it's a memory bus access (the device has mapped memory), you can just point the structure at the data and access it normally, as long as the base is at an alignment boundry. This can be a problem for download code for a smart card, since you may not have paid attention to alignment boundries when you generated the mapped memory data on board the card, and the system the card is plugged into now cares about that. Technically, if you are writing Intel code, you can ignore the data alignment; but if you do, your code will run slower and require more bus accesses than if you had paid attention. And that's about the end of my diatribe on alignment. 8-). Regards, Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199704061924.MAA28524>