Home Ask Login Register

Developers Planet

Your answer is one click away!

tozak February 2016

What is the life-time of asm volatile("" ::: "memory")?

I have read a number of explanations about compile barriers and memory barriers, though I am not sure yet about how the compiler knows where the prevention of compile memory ordering starts and where it ends. (On the other hand I understand how a cpu memory barrier works...)

Below is an arbitrary example, with no compile barrier.

int func(int *x, int *y) {
        int var = 0;
        x[0] += 1;
        var += y[0];
        y[0] += 1;
        x[1] += 1;
        var += y[1];
        y[0] += 1;

        return var;
}

For example if I want to prevent the compile memory ordering only in this function, and not in other functions, should I insert the asm volatile("" ::: "memory") to end of the function, before returning var?

Like:

int func(int *x, int *y) {
        int var = 0;
        x[0] += 1;
        var += y[0];
        y[0] += 1;
        x[1] += 1;
        var += y[1];
        y[0] += 1;

        asm volatile("" ::: "memory");
        return var;
}

Answers


e0k February 2016

The barrier prevents reordering (or optimization) wherever you put it. There is no magical "scope". Just look at the inline assembly instruction:

asm volatile (""::: "memory");

The volatile keyword means to put the asm statement exactly where I put it, and don't optimize it away (i.e. remove it). After the third : is the list of clobbers, so this means "I have clobbered the memory." You are basically telling the compiler "I have done something to affect the memory."

In your example, you have something like

y[0] += 1;
y[0] += 1;

The compiler is very clever and knows this is not as efficient as it could be. It will probably compile this into something like

load y[0] from memory to register
add 2 to this register
store result to y[0]

Because of pipelining reasons, it may also be more efficient to combine this with other load/modify/store operations. So the compiler may reorder this even further by merging it with nearby operations.

To prevent this, you can place a memory barrier between them:

y[0] += 1;
asm volatile (""::: "memory");
y[0] += 1;

This tells the compiler that after the first instruction, "I have done something to the memory, you may not know about it, but it happened." So it can not use its standard logic and assume that adding one twice to the same memory location is the same as adding two to it, since something happened in between. So this would be compiled into something more like

load y[0] from memory to register
add 1 to this register
store result to y[0]
load y[0] from memory to register
add 1 to this register
store result to y[0]

Again, it could possibly reorder t

Post Status

Asked in February 2016
Viewed 3,592 times
Voted 6
Answered 1 times

Search




Leave an answer


Quote of the day: live life