https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67529
Bug ID: 67529 Summary: rx-elf C++ inherited class malformed call to overridden methods Product: gcc Version: 5.2.0 Status: UNCONFIRMED Severity: critical Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: ericns1 at spirilis dot net Target Milestone: --- Created attachment 36313 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=36313&action=edit Files needed to reproduce this bug using rx-elf-g++ cmd listed in bug report. I've been able to reproduce what appears to be a glitch in the rx-elf-g++ C++ compiler. Originally found on GCC 4.8.4 (KPIT Cummins' GNURXv15.01-SP1 release that pairs with Renesas' e2studio IDE), I have compiled GCC 5.2.0 from scratch for the rx-elf target and confirmed similar ASM output. In this example, I am writing an all-pure virtual method-laden class to define an interface of sorts for a common hardware API, and writing implementation classes which override the pure virtual methods with their correct implementations. When a method inside the class attempts to execute another method inside the class that was overriding a pure virtual method in the parent class, the actual function call produced includes a JSR instruction against a register value which appears to be derived from the in-SRAM address of the object's data storage, not the actual in-Flash location of the function itself. Example: my_template_parent.h: #ifndef MY_TEMPLATE_PARENT_H_ #define MY_TEMPLATE_PARENT_H_ #include <stdint.h> #include <ctype.h> #include <sys/cdefs.h> class MyTemplateParent { public: // prove it doesn't need to be a pure virtual function for the bug to occur virtual void begin(void) { return; }; virtual void check(void) = 0; }; my_template.h: #ifndef MY_TEMPLATE_H_ #define MY_TEMPLATE_H_ #include <stdint.h> #include <ctype.h> #include <sys/cdefs.h> #include "my_template_parent.h" class MyTemplate : MyTemplateParent { private: uint8_t buffer[256]; unsigned int head; uint8_t value = 0; public: __noinline void begin(void) { head = 0; buffer[0] = value; } __noinline void begin(uint8_t val1) { buffer[1] = val1; //begin(); // set buffer[0] to value (template argument) check(); }; __noinline void check(void) { if (buffer[2] == 0xF3) buffer[3] = 0xF3; } }; #endif /* MY_TEMPLATE_H_ */ Main provided by "main.cpp": #include "my_template.h" MyTemplate obj; int main(void) { volatile unsigned long i = 0; obj.begin(33); while(1) i++; } Compiled using: rx-elf-g++ -T rx2108.ld -ffunction-sections -fdata-sections -Wl,--gc-sections -D__RX_LITTLE_ENDIAN__=1 -DCPPAPP -mlittle-endian-data -mcpu=rx200 -nofpu -o a.out main.cpp (rx2108.ld provided as an attachment) The assembler output, rx-elf-objdump -C -dS, produces the following for the MyTemplate-related functions: fff80160 <MyTemplateParent::begin()>: fff80160: 7e a6 push.l r6 fff80162: 71 06 fc add #-4, r0, r6 fff80165: ef 60 mov.l r6, r0 fff80167: e3 61 mov.l r1, [r6] fff80169: 03 nop fff8016a: 3f 66 02 rtsd #8, r6-r6 fff8016d <MyTemplate::begin()>: fff8016d: 6e 6b pushm r6-r11 fff8016f: 71 06 fc add #-4, r0, r6 fff80172: ef 60 mov.l r6, r0 fff80174: e3 61 mov.l r1, [r6] fff80176: ec 6a mov.l [r6], r10 fff80178: f9 a6 41 00 mov.l #0, 260[r10] fff8017c: ec 6a mov.l [r6], r10 fff8017e: ce ab 08 01 mov.b 264[r10], r11 fff80182: ec 6a mov.l [r6], r10 fff80184: c7 ab 04 mov.b r11, 4[r10] fff80187: 03 nop fff80188: 3f 6b 07 rtsd #28, r6-r11 fff8018b <MyTemplate::begin(unsigned char)>: fff8018b: 6e 6b pushm r6-r11 fff8018d: 71 06 f8 add #-8, r0, r6 fff80190: ef 60 mov.l r6, r0 fff80192: e3 61 mov.l r1, [r6] fff80194: 81 62 mov.b r2, 4[r6] fff80196: ec 6a mov.l [r6], r10 fff80198: cd 6b 04 mov.b 4[r6], r11 fff8019b: c7 ab 05 mov.b r11, 5[r10] fff8019e: ec 6a mov.l [r6], r10 fff801a0: ec aa mov.l [r10], r10 fff801a2: 62 4a add #4, r10 fff801a4: ec aa mov.l [r10], r10 fff801a6: ec 61 mov.l [r6], r1 fff801a8: 7f 1a jsr r10 fff801aa: 03 nop fff801ab: 3f 6b 08 rtsd #32, r6-r11 fff801ae <MyTemplate::check()>: fff801ae: 7e aa push.l r10 fff801b0: 7e a6 push.l r6 fff801b2: 71 06 fc add #-4, r0, r6 fff801b5: ef 60 mov.l r6, r0 fff801b7: e3 61 mov.l r1, [r6] fff801b9: ec 6a mov.l [r6], r10 fff801bb: cd aa 06 mov.b 6[r10], r10 fff801be: 5b aa movu.b r10, r10 fff801c0: 75 5a f3 cmp #243, r10 fff801c3: 1f bne.s fff801ca <_idkey_0+0xfff801cb> fff801c4: ec 6a mov.l [r6], r10 fff801c6: f9 a4 07 f3 mov.b #243, 7[r10] fff801ca: 03 nop fff801cb: 62 40 add #4, r0 fff801cd: 7e b6 pop r6 fff801cf: 7e ba pop r10 fff801d1: 02 rts The critical piece is: fff8019e: ec 6a mov.l [r6], r10 fff801a0: ec aa mov.l [r10], r10 fff801a2: 62 4a add #4, r10 fff801a4: ec aa mov.l [r10], r10 fff801a6: ec 61 mov.l [r6], r1 fff801a8: 7f 1a jsr r10 JSR R10 .... appears from the Renesas IDE's debug view to branch the CPU to address 0x00000000, at the beginning of SRAM. On the other hand, if we break the inheritence by reworking my_template.h so it no longer inherits from MyTemplateParent: class MyTemplate { ... the disassembled output looks a bit different: fff80160 <MyTemplate::begin(unsigned char)>: fff80160: 6e 6b pushm r6-r11 fff80162: 71 06 f8 add #-8, r0, r6 fff80165: ef 60 mov.l r6, r0 fff80167: e3 61 mov.l r1, [r6] fff80169: 81 62 mov.b r2, 4[r6] fff8016b: ec 6a mov.l [r6], r10 fff8016d: cd 6b 04 mov.b 4[r6], r11 fff80170: c7 ab 01 mov.b r11, 1[r10] fff80173: ec 61 mov.l [r6], r1 fff80175: 05 08 00 00 bsr.a fff8017d <_idkey_0+0xfff8017e> fff80179: 03 nop fff8017a: 3f 6b 08 rtsd #32, r6-r11 fff8017d <MyTemplate::check()>: fff8017d: 7e aa push.l r10 fff8017f: 7e a6 push.l r6 fff80181: 71 06 fc add #-4, r0, r6 fff80184: ef 60 mov.l r6, r0 fff80186: e3 61 mov.l r1, [r6] fff80188: ec 6a mov.l [r6], r10 fff8018a: cd aa 02 mov.b 2[r10], r10 fff8018d: 5b aa movu.b r10, r10 fff8018f: 75 5a f3 cmp #243, r10 fff80192: 1f bne.s fff80199 <_idkey_0+0xfff8019a> fff80193: ec 6a mov.l [r6], r10 fff80195: f9 a4 03 f3 mov.b #243, 3[r10] fff80199: 03 nop fff8019a: 62 40 add #4, r0 fff8019c: 7e b6 pop r6 fff8019e: 7e ba pop r10 fff801a0: 02 rts Here a "BSR.A" instruction is used to branch to the new function, and it runs successfully. You can try having MyTemplate::begin(uint8_t) call begin() instead of check() .... the same JSR type of instruction results and it goes off into lala land.