cc65: Support for volatile data missing

When activating the “Optimize code, inline standard funtions” (-Os) or the “Optimize code, inline more code” (-Oi) option, if a C source code has (in sequence) a direct write statement and a direct read statement on the same RAM memory location, the reading step is omitted.

The problem arises if the memory is accessed using the address directly. It does not occur if the address to read and write from is stored in a (temporary) pointer variable, or if the optimization is disabled.

Currently, the only workaround to avoid allocating an additional variable is to assign a value of 0 to the (destination) memory location: this prevents the optimizer from exploiting the value just written.

However, I believe there should be a way to indicate that (specific?) memory locations can be “volatile” so that the optimizer cannot make any assumptions about the reusability of their value.

Example:

unsigned char port;
[...]
(*(unsigned char*)0xff08) = 0x04;
port = (*(unsigned char*)0xff08);        `

This is the correct assembly output (without -Osir optimizations):

00042Cr 1               ;
00042Cr 1               ; (*(unsigned char*)0xff08) = 0x04;
00042Cr 1               ;
00042Cr 1  A2 00        	ldx     #$00
00042Er 1  A9 04        	lda     #$04
000430r 1  8D 08 FF     	sta     $FF08
000433r 1               ;
000433r 1               ; port = (*(unsigned char*)0xff08);
000433r 1               ;
000433r 1  A2 00        	ldx     #$00
000435r 1  AD 08 FF     	lda     $FF08 ; <--- this is the step missed!
000438r 1  8D rr rr     	sta     L0117
00043Br 1               ;

This is the (wrong) assembly output:

000281r 1               ; (*(unsigned char*)0xff08) = 0x04;
000281r 1               ;
000281r 1  A9 04        	lda     #$04
000283r 1  8D 08 FF     L0158:	sta     $FF08
000286r 1               ;
000286r 1               ; port = (*(unsigned char*)0xff08);
000286r 1               ;
000286r 1  8D rr rr     	sta     L0118
000289r 1               ;

This is the workaround:

000289r 1               ; (*(unsigned char*)0xff08) = 0x04;
000289r 1               ;
000289r 1  8D 08 FF     	sta     $FF08
00028Cr 1               ;
00028Cr 1               ; port = 0;
00028Cr 1               ;
00028Cr 1  8C rr rr     	sty     L0118
00028Fr 1               ;
00028Fr 1               ; port = (*(unsigned char*)0xff08);
00028Fr 1               ;
00028Fr 1  AD 08 FF     	lda     $FF08 ; <--- this is the step needed
000292r 1  8D rr rr     	sta     L0118

Command line used to compile:

cl65 -T -l obj/plus4/midres_plus4.asm -t plus4 -c -W -const-comparison -Osir -Cl -D__CBM__ -o obj/plus4/midres_plus4.o obj/plus4/midres_plus4.c

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 27 (26 by maintainers)

Most upvoted comments

Likely there’s a misunderstanding. I didn’t (want to) comment on the question “pragma and/or volatile”. I only wanted to comment on

the indication of memory volatility is an attribute of the target platform

where I don’t (generally) agree with. However, in case we would have such a pragma I could imagine that there are targets which could benefit from such pragma “predefined” in a target header file.

In ANSI C volatile may also refer to non-pointer variable. The most common use-case for volatile non-pointer values arises with shared variables in multi-threading. This is clearly out of the scope of CC65.

Now I see your point - thanks for clarifying.

@Fabrizio-Caruso

@oliverschmidt I agree with everything you have written except for a detail (which is insignificant for CC65). In ANSI C volatile may also refer to non-pointer variable. The most common use-case for volatile non-pointer values arises with shared variables in multi-threading. This is clearly out of the scope of CC65.

It’s not shared variables in multi-threading but shared variables in concurrent programming. This includes interrupt routines. And you don’t even need to write your own — some target’s operating systems use them and store interesting stuff in known memory locations. So this isn’t very far fetched. Also you can define extern variables that are not pointers but refer to hardware registers.

To cut a long story short: From my perspective every optimizing C compiler targeting a CPU/MCU with memory-mapped I/O is supposed to have mature volatile support - but cc65 doesn’t 😦