This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to ensure order of operations is not altered by optimizer?

Reading spru514 it mentions that some optimization levels may reorder operations.

Is there a way to ensure order of operations are maintained for specific code blocks?

Given the example:

int16_t run_handler(const handler_t handler, const OBJ_Handle handle) {
	int16_t e = EXIT_SUCCESS;
	if (handle->busy != true) {
		handle->busy = true;
		e = handler(handle);
		handle->busy = false;
	} else {
		e = EXIT_FAILURE;
	}
	return e;
}

How can we ensure that busy will only be set to false after the function has been called? 

In this case qualifying the busy variable as volatile is not enough I think, as J.3.10 of the aforementioned document says

"The TI compiler will not reorder two volatile accesses, but it may reorder a volatile and a non-volatile access"

While the handler is not critical code (interrupts should still run while it executes and may be free to change the value of busy if they see fit), it obviously would not make sense for the compiler to access the handler call before it has set busy to true or after it has set busy to false.

Thanks, T

  • You're safe as long as the optimizer doesn't know what handler() does (e.g. because it's only resolved at link time). In that case it has to assume that handle->busy will have influence on the observable behavior of your program. You cannot however be sure that a) there will never be any optimizations at link time, and b) handler->busy will not be set _more often_ than is apparent from the code (unless you make it volatile).

    For ultimate control you will have to write assembly.

  • Since handler is a function pointer passed in as an argument, there aren't many ways you might have any trouble.  If you build with --opt_level=4, the compiler might be sure it sees all calls to run_handler, and the function pointer argument might always the same.  Then it might change that indirect call to a direct call.  Then it might inline that direct call.  And operations from inlining that call might get mixed with the assignments to handler->busy.  That's several uses of "might" all in succession.

    If you are concerned that might occur, then consider making busy volatile.  If you do that, please check on whether this change has a negative impact on performance or code size.  I suspect it won't, but the only way to know is to try it and see.

    Thanks and regards,

    -George

  • Hi George,

    Thanks for the explanation. However, the quoted line from the document specifically says that qualifying busy as volatile will NOT help in this regard (At the moment busy is volatile *anyway* in my particular situation because an ISR uses it in other code).

    Hypothetically, swap the call to the function pointed at as just some few mathematical operations on a variable with scope greater than local (i.e. module or global level) and as far as I can tell, the quoted line means that any volatile qualifier would make zero difference in this regard! Do you mean my understanding of the TI compiler is incorrect in this regard?

    Usually to be sure of ordering one would use a compiler barrier, but AFAIK the TI compiler does not provide them and IIRC specifically warns against using asm for this.

    Thanks, T

  • You are correct.  With regard to the instruction order, volatile accesses will not cross each other, but they may cross other non-volatile accesses.  They do not constitute a barrier which nothing may cross.

    Thanks and regards,

    -George

  • Based on your example, you are trying to create a critical section.  The TI compiler does not itself support any critical section primitives.  You'd probably be best served by adding a library which does support multithreading, or perhaps using SYS-BIOS.

  • What if handle and/or e were made volatile?  Would that then trigger the first clause (and not the second) of J.3.10 and prohibit the undesired reordering?

  • Yes, making handle volatile should achieve the goal of preventing reordering. Making e volatile will not make a difference (in this case). You still don't get a critical section, per se, but it may be sufficient.