SDCC version: 3.7.1 #10406
Command line used:
--nooverlay --nogcse --nolabelopt --noinvariant --noinduction --noloopreverse --no-peep --no-reg-params --nolospre --use-non-free --opt-code-size libc18f.lib -c -mpic16 -p18f13k50 teste.c
Code to reproduce the bug:
#include <pic18fregs.h>
const unsigned short seg_a = 0b1101011111101101;
const unsigned short seg_b = 0b0010011110011111;
void set_bits(unsigned char sn)
{
LATCbits.LATC0 = ((seg_a >> sn) & 1) ^ ~0x1; /* 1 */
LATCbits.LATC1 = ((seg_b >> sn) & 1) ^ ~0x1; /* 2 */
//LATCbits.LATC1 = ((seg_a >> sn) & 1); /* 3 */
//LATCbits.LATC1 = ((seg_b >> sn) & 1); /* 4 */
//LATCbits.LATC2 = ((seg_b >> sn) & 1) ^ (~0x1 & 1); /* 5 */
}
void main(void)
{
unsigned char i;
for (i = 0; i < 10; i++) set_bits(i);
}
Bug:
The expressions 1 and 2, clearly variant, are treated by the front-end as invariant and compiled to the following assembler:
; .line 8; teste.c LATCbits.LATC0 = ((seg_a >> sn) & 1) ^ ~0x1; /* 1 */
BSF _LATCbits, 0
; .line 9; teste.c LATCbits.LATC1 = ((seg_b >> sn) & 1) ^ ~0x1; /* 2 */
BSF _LATCbits, 1
If expression 2 is commented out and expression 3 is uncommented (expression 3 is a portion of expression 1) the code is compiled as:
; .line 8; teste.c LATCbits.LATC0 = ((seg_a >> sn) & 1) ^ ~0x1; /* 1 */
MOVLW LOW(_seg_a)
MOVWF TBLPTRL
MOVLW HIGH(_seg_a)
MOVWF TBLPTRH
MOVLW UPPER(_seg_a)
MOVWF TBLPTRU
TBLRD*+
MOVFF TABLAT, r0x01
TBLRD*+
MOVFF TABLAT, r0x02
MOVF r0x00, W
MOVWF FSR0L
MOVFF r0x01, r0x00
MOVFF r0x02, r0x03
MOVF FSR0L, W
BZ _00107_DS_
NEGF WREG
BCF STATUS, 0
_00108_DS_:
RRCF r0x03, F
RRCF r0x00, F
ADDLW 0x01
BNC _00108_DS_
_00107_DS_:
MOVF r0x00, W
ANDLW 0x01
MOVWF r0x00
BSF _LATCbits, 0
; .line 10; teste.c LATCbits.LATC1 = ((seg_a >> sn) & 1); /* 3 */
MOVF r0x00, W
ANDLW 0x01
RLNCF WREG, W
MOVWF PRODH
MOVF _LATCbits, W
ANDLW 0xfd
IORWF PRODH, W
MOVWF _LATCbits
If expressions 1 and 5 are the ones uncommented then the following is generated:
; .line 8; teste.c LATCbits.LATC0 = ((seg_a >> sn) & 1) ^ ~0x1; /* 1 */
BSF _LATCbits, 0
; .line 12; teste.c LATCbits.LATC2 = ((seg_b >> sn) & 1) ^ (~0x1 & 1); /* 5 */
MOVLW LOW(_seg_b)
MOVWF TBLPTRL
MOVLW HIGH(_seg_b)
MOVWF TBLPTRH
MOVLW UPPER(_seg_b)
MOVWF TBLPTRU
TBLRD*+
MOVFF TABLAT, r0x01
TBLRD*+
MOVFF TABLAT, r0x02
MOVF r0x00, W
MOVWF FSR0L
MOVFF r0x01, r0x00
MOVFF r0x02, r0x03
MOVF FSR0L, W
BZ _00107_DS_
NEGF WREG
BCF STATUS, 0
_00108_DS_:
RRCF r0x03, F
RRCF r0x00, F
ADDLW 0x01
BNC _00108_DS_
_00107_DS_:
MOVF r0x00, W
ANDLW 0x01
MOVWF r0x00
MOVF r0x00, W
ANDLW 0x01
RLNCF WREG, W
RLNCF WREG, W
MOVWF PRODH
MOVF _LATCbits, W
ANDLW 0xfb
IORWF PRODH, W
MOVWF _LATCbits
So there is clearly a problem determining the expression types, causing them to be treated as a invariants, depending on the surrounding code. I think the problem is on the front-end because if the --debug-xtra switch is added the following is generated (notice the '..AOP_LIT..' bit):
; ;ic ` 0x160 (ADDRESS_OF)
; ;ic = 0x3d (=)
; ;ic = 0x3d (=)
; ; *** genNearPointerSet 9873
; ; pic16_aopOp 981
; ; *** 1032: symbol name = iTemp2, regType = 2
; ; 1048
; ; gen.c:796 dir size: 1
; ; 1686 pic16_popRegFromString _LATCbits 1/0
; ; 805: rname _LATCbits, val 0, const = 0
; ; *** genNearPointerSet 9891
; ; pic16_aopOp 981
; ; 1368: pic16_aopGet AOP_PCODE type PO_DIR
; ; _LATCbits offset 0
; ; line = 9893 result AOP_PCODE=_LATCbits, left -=-, right AOP_LIT=0x01, size = 1
; ; *** genPackBits 9547
; ; genPackBits 9561 optimize bit assignment
; ; pic16_popGet AOP_PCODE (PO_DIR) 1892 _LATCbits offset 0
; .line 8; teste.c LATCbits.LATC0 = ((seg_a >> sn) & 1) ^ ~0x1; /* 1 */
BSF _LATCbits, 0 ;key=008, flow seq=007
Some more info:
By using the --dump-i-code switch and compiling the expressions 1 and 5 i got the .dumpraw1 file, which seems to pinpoint the problem (lines in bold below):
Apparently the XOR and one's complement operators are being morphed together into an OR operator on expression 1, which turns it into an invariant. The question is why is this happening...
Last edit: bsccara 2018-05-12
More info, AST dumped:
Line 8 has an OR token, while line 12 has a XOR one. Both should have XORs.
I verified that this is a front-end bug, affecting all targets.
Philipp
In [r10453], I added a test for this, which can be enabled by removing the define guard.
Philipp
What happens is that at some time, the result of ((seg_a >> sn) & 1) is considered to be _Bool / __bit. That value is then xored with something that is neither 0 nor 1, resulting in a 1 (and an optimization for some reason uses | to implement the 1, instead of plain assignment).
Philipp
Last edit: Philipp Klaus Krause 2018-06-21
Fixed in [r11013]