newbie - 3 months ago 12

C Question

I am interpreting a generated intermediate code of a linux kernel module. But i am unable to understand the following line of code of

`pci_set_dma_mask`

`if ((err = pci_set_dma_mask(pdev, (((32) == 64) ? ~0ULL : ((1ULL<<(32))-1))))) {`

(void)((NETIF_MSG_PROBE & nic->msg_enable) && printk("<3>" "e100" ": " "%s: %s: "

"No usable DMA configuration, aborting.\n", nic->netdev->name,func));

}

My understanding is that, since 32 != 64,

`((1ULL<<(32))-1)`

`1ULL`

`1ULL`

Answer

The redundant parentheses and the trivial condition `(32) == 64`

show that this is generated code, possibly the result of running the preprocessor. That condition tests whether something is 32-bit or 64-bit and the code has been generated for a system where that thing is 32-bit.

On a 64-bit system, the value `~0ULL`

is used. With the `ULL`

suffix, the constant is of type `unsigned long long`

, which is a 64-bit type on all platforms that the Linux kernel supports (standard C specifies that `unsigned long long`

is 64-bit or larger but almost all systems have it as exactly 64 bits). The `~`

operator takes the bitwise complement, so the result is the number 2^{64}-1, i.e. a 64-bit constant whose binary representation is all-bits-one. In hexadecimal, that's `0xffffffffffffffff`

.

The reason the code uses `~0ULL`

and not `~0`

is that in `~0`

, the constant `0`

is an `int`

, which is a 32-bit type on all platforms that the Linux kernel support. The `~`

operator applies to this would yield a 32-bit value with all-bits-one, i.e. `0xffffffff`

in hexadecimal, i.e. the number number 2^{32}-1. When converted to a 64-bit value, the numerical value remains the same — *not* an all-64-bits-one constant.

On a 32-bit system, the code uses `1ULL<<(32))-1`

. First `1ULL`

is a 64-bit value; `1ULL << 32`

shifts it left by 32, which means multiplying the numerical value by 2^{32}, which yields the number 2^{32}, or in hexadecimal `0x100000000`

. Subtracting 1 yields 2^{32}, i.e. `0xffffffff`

(an all-32-bits-one constant).

Doing the operations on an `unsigned long long`

is necessary here too, because a left shift by 32 is not defined for 32-bit values. Standard C says that `x << 32`

is undefined behavior if the type of `x`

is a 32-bit type or less. In practice, on many systems, the processor instruction for shifting on a 32-bit type takes a shift amount in the range 0…31, and `1 << 32`

would be compiled to the same code as `1 << 0`

(wrapping modulo 32), yielding 1 instead of 2^{32}.

Source (Stackoverflow)

Comments