From 268a53de823a6750d6256ee1fb1e7707b4b45740 Mon Sep 17 00:00:00 2001 From: Xavier ASUS Date: Fri, 18 Oct 2019 00:31:54 +0200 Subject: sdcc-3.9.0 fork implementing GNU assembler syntax This fork aims to provide better support for stm8-binutils --- src/Makefile | 153 + src/Makefile.dep | 231 + src/Makefile.in | 153 + src/README | 8 + src/SDCC.lex | 1187 ++++ src/SDCC.y | 2465 +++++++ src/SDCCBBlock.c | 889 +++ src/SDCCBBlock.h | 113 + src/SDCCargs.h | 67 + src/SDCCasm.c | 623 ++ src/SDCCasm.h | 68 + src/SDCCast.c | 8612 +++++++++++++++++++++++ src/SDCCast.h | 241 + src/SDCCbitv.c | 541 ++ src/SDCCbitv.h | 66 + src/SDCCbtree.cc | 172 + src/SDCCbtree.h | 39 + src/SDCCcflow.c | 483 ++ src/SDCCcflow.h | 35 + src/SDCCcse.c | 2736 ++++++++ src/SDCCcse.h | 67 + src/SDCCdebug.c | 208 + src/SDCCdebug.h | 50 + src/SDCCdflow.c | 405 ++ src/SDCCdflow.h | 39 + src/SDCCdwarf2.c | 3247 +++++++++ src/SDCCdwarf2.h | 432 ++ src/SDCCerr.c | 796 +++ src/SDCCerr.h | 422 ++ src/SDCCgas.c | 132 + src/SDCCgas.h | 26 + src/SDCCgen.c | 355 + src/SDCCgen.h | 105 + src/SDCCglobl.h | 421 ++ src/SDCCglue.c | 2674 +++++++ src/SDCCglue.h | 44 + src/SDCChasht.c | 586 ++ src/SDCChasht.h | 124 + src/SDCCicode.c | 4678 +++++++++++++ src/SDCCicode.h | 364 + src/SDCClabel.c | 535 ++ src/SDCClabel.h | 39 + src/SDCCloop.c | 1425 ++++ src/SDCCloop.h | 57 + src/SDCClospre.cc | 351 + src/SDCClospre.hpp | 808 +++ src/SDCClrange.c | 1248 ++++ src/SDCClrange.h | 48 + src/SDCCmacro.c | 186 + src/SDCCmacro.h | 38 + src/SDCCmain.c | 2726 +++++++ src/SDCCmem.c | 1353 ++++ src/SDCCmem.h | 131 + src/SDCCnaddr.cc | 80 + src/SDCCnaddr.hpp | 511 ++ src/SDCCopt.c | 3328 +++++++++ src/SDCCopt.h | 43 + src/SDCCpeeph.awk | 14 + src/SDCCpeeph.c | 2893 ++++++++ src/SDCCpeeph.h | 62 + src/SDCCptropt.c | 420 ++ src/SDCCptropt.h | 35 + src/SDCCralloc.hpp | 1337 ++++ src/SDCCsalloc.hpp | 445 ++ src/SDCCset.c | 728 ++ src/SDCCset.h | 84 + src/SDCCsymt.c | 4587 ++++++++++++ src/SDCCsymt.h | 734 ++ src/SDCCsystem.c | 408 ++ src/SDCCsystem.h | 37 + src/SDCCtree_dec.hpp | 648 ++ src/SDCCutil.c | 1173 ++++ src/SDCCutil.h | 157 + src/SDCCval.c | 3295 +++++++++ src/SDCCval.h | 178 + src/altlex.c | 1219 ++++ src/avr/Makefile.in | 8 + src/avr/avr.vcxproj | 149 + src/avr/avr.vcxproj.filters | 43 + src/avr/gen.c | 5361 ++++++++++++++ src/avr/gen.h | 84 + src/avr/main.c | 290 + src/avr/main.h | 8 + src/avr/peeph.def | 1701 +++++ src/avr/ralloc.c | 2307 ++++++ src/avr/ralloc.h | 68 + src/cdbFile.c | 523 ++ src/clean.mk | 43 + src/common.h | 76 + src/conf.mk | 10 + src/ds390/Makefile | 7 + src/ds390/Makefile.in | 7 + src/ds390/ds390.vcxproj | 151 + src/ds390/ds390.vcxproj.filters | 43 + src/ds390/gen.c | 14414 ++++++++++++++++++++++++++++++++++++++ src/ds390/gen.h | 83 + src/ds390/main.c | 1710 +++++ src/ds390/main.h | 46 + src/ds390/peeph.def | 2856 ++++++++ src/ds390/ralloc.c | 3501 +++++++++ src/ds390/ralloc.h | 75 + src/hc08/Makefile | 7 + src/hc08/Makefile.in | 7 + src/hc08/gen.c | 10849 ++++++++++++++++++++++++++++ src/hc08/gen.h | 95 + src/hc08/hc08.h | 18 + src/hc08/hc08.vcxproj | 147 + src/hc08/hc08.vcxproj.filters | 49 + src/hc08/main.c | 1058 +++ src/hc08/main.h | 8 + src/hc08/peeph.def | 278 + src/hc08/ralloc.c | 2423 +++++++ src/hc08/ralloc.h | 89 + src/hc08/ralloc2.cc | 674 ++ src/mcs51/Makefile | 7 + src/mcs51/Makefile.in | 7 + src/mcs51/gen.c | 12371 ++++++++++++++++++++++++++++++++ src/mcs51/gen.h | 88 + src/mcs51/main.c | 934 +++ src/mcs51/main.h | 45 + src/mcs51/mcs51.vcxproj | 165 + src/mcs51/mcs51.vcxproj.filters | 55 + src/mcs51/peep.c | 773 ++ src/mcs51/peep.h | 25 + src/mcs51/peeph.def | 5092 ++++++++++++++ src/mcs51/ralloc.c | 3466 +++++++++ src/mcs51/ralloc.h | 81 + src/mcs51/rtrack.c | 1200 ++++ src/mcs51/rtrack.h | 28 + src/pdk/Makefile | 7 + src/pdk/Makefile.dep | 45 + src/pdk/Makefile.in | 7 + src/pdk/gen.c | 4244 +++++++++++ src/pdk/gen.h | 90 + src/pdk/gen.o | Bin 0 -> 538800 bytes src/pdk/main.c | 714 ++ src/pdk/main.o | Bin 0 -> 85464 bytes src/pdk/peep.c | 521 ++ src/pdk/peep.h | 6 + src/pdk/peep.o | Bin 0 -> 117680 bytes src/pdk/peeph.def | 56 + src/pdk/peeph.rul | 58 + src/pdk/port.a | Bin 0 -> 13086816 bytes src/pdk/ralloc.c | 747 ++ src/pdk/ralloc.h | 64 + src/pdk/ralloc.o | Bin 0 -> 128192 bytes src/pdk/ralloc2.cc | 692 ++ src/pdk/ralloc2.o | Bin 0 -> 12156320 bytes src/pic14/Makefile | 7 + src/pic14/Makefile.in | 7 + src/pic14/device.c | 821 +++ src/pic14/device.h | 94 + src/pic14/gen.c | 7842 +++++++++++++++++++++ src/pic14/gen.h | 174 + src/pic14/genarith.c | 1289 ++++ src/pic14/glue.c | 1206 ++++ src/pic14/glue.h | 36 + src/pic14/main.c | 485 ++ src/pic14/main.h | 15 + src/pic14/pcode.c | 5985 ++++++++++++++++ src/pic14/pcode.h | 907 +++ src/pic14/pcodeflow.c | 226 + src/pic14/pcodeflow.h | 68 + src/pic14/pcodepeep.c | 2005 ++++++ src/pic14/pcoderegs.c | 802 +++ src/pic14/pcoderegs.h | 50 + src/pic14/peeph.def | 323 + src/pic14/pic14.vcxproj | 192 + src/pic14/pic14.vcxproj.filters | 79 + src/pic14/ralloc.c | 3874 ++++++++++ src/pic14/ralloc.h | 123 + src/pic16/Makefile | 7 + src/pic16/Makefile.in | 7 + src/pic16/NOTES | 210 + src/pic16/device.c | 1133 +++ src/pic16/device.h | 147 + src/pic16/gen.c | 11583 ++++++++++++++++++++++++++++++ src/pic16/gen.h | 224 + src/pic16/genarith.c | 2036 ++++++ src/pic16/genutils.c | 622 ++ src/pic16/genutils.h | 75 + src/pic16/glue.c | 1980 ++++++ src/pic16/glue.h | 37 + src/pic16/graph.c | 180 + src/pic16/graph.h | 113 + src/pic16/main.c | 1428 ++++ src/pic16/main.h | 63 + src/pic16/pcode.c | 12278 ++++++++++++++++++++++++++++++++ src/pic16/pcode.h | 1141 +++ src/pic16/pcodeflow.c | 351 + src/pic16/pcodeflow.h | 69 + src/pic16/pcodepeep.c | 2515 +++++++ src/pic16/pcoderegs.c | 1007 +++ src/pic16/pcoderegs.h | 42 + src/pic16/peeph.def | 308 + src/pic16/pic16.vcxproj | 204 + src/pic16/pic16.vcxproj.filters | 85 + src/pic16/ralloc.c | 4529 ++++++++++++ src/pic16/ralloc.h | 194 + src/port-clean.mk | 5 + src/port.h | 488 ++ src/port.mk | 52 + src/regression/Makefile | 226 + src/regression/add.c | 191 + src/regression/add2.c | 326 + src/regression/add3.c | 218 + src/regression/add4.c | 82 + src/regression/and1.c | 141 + src/regression/and2.c | 115 + src/regression/arrays.c | 145 + src/regression/b.c | 64 + src/regression/bank1.c | 51 + src/regression/bool1.c | 133 + src/regression/bool2.c | 50 + src/regression/bool3.c | 65 + src/regression/call1.c | 157 + src/regression/compare.c | 186 + src/regression/compare10.c | 322 + src/regression/compare2.c | 362 + src/regression/compare3.c | 247 + src/regression/compare4.c | 317 + src/regression/compare5.c | 429 ++ src/regression/compare6.c | 184 + src/regression/compare7.c | 311 + src/regression/compare8.c | 322 + src/regression/compare9.c | 305 + src/regression/configword.c | 33 + src/regression/create_stc | 15 + src/regression/empty.c | 22 + src/regression/for.c | 108 + src/regression/gpsim_assert.h | 64 + src/regression/init0.c | 45 + src/regression/inline.c | 58 + src/regression/mult1.c | 108 + src/regression/nestfor.c | 139 + src/regression/or1.c | 198 + src/regression/pcodeopt.c | 26 + src/regression/picregs.h | 10 + src/regression/pointer1.c | 134 + src/regression/ptrarg.c | 39 + src/regression/ptrfunc.c | 106 + src/regression/rotate1.c | 207 + src/regression/rotate2.c | 62 + src/regression/rotate3.c | 388 + src/regression/rotate4.c | 223 + src/regression/rotate5.c | 234 + src/regression/rotate6.c | 135 + src/regression/rotate7.c | 388 + src/regression/rt.sh | 20 + src/regression/simulate | 26 + src/regression/string1.c | 53 + src/regression/struct1.c | 104 + src/regression/sub.c | 252 + src/regression/sub2.c | 172 + src/regression/switch1.c | 84 + src/regression/while.c | 42 + src/regression/xor.c | 56 + src/reswords.gperf | 67 + src/sdcc.vcxproj | 524 ++ src/sdcc.vcxproj.filters | 313 + src/stm8/Makefile | 7 + src/stm8/Makefile.in | 7 + src/stm8/gen.c | 8223 ++++++++++++++++++++++ src/stm8/gen.h | 90 + src/stm8/main.c | 556 ++ src/stm8/peep.c | 1236 ++++ src/stm8/peep.h | 5 + src/stm8/peeph.def | 1592 +++++ src/stm8/ralloc.c | 843 +++ src/stm8/ralloc.h | 70 + src/stm8/ralloc2.cc | 608 ++ src/stm8/stm8.vcxproj | 114 + src/stm8/stm8.vcxproj.filters | 52 + src/version.awk | 31 + src/z80/Makefile | 7 + src/z80/Makefile.in | 7 + src/z80/gen.c | 13211 ++++++++++++++++++++++++++++++++++ src/z80/gen.h | 90 + src/z80/main.c | 1758 +++++ src/z80/mappings.i | 475 ++ src/z80/peep.c | 1169 ++++ src/z80/peep.h | 30 + src/z80/peeph-gbz80.def | 823 +++ src/z80/peeph-r2k.def | 2355 +++++++ src/z80/peeph-tlcs90.def | 1518 ++++ src/z80/peeph-z80.def | 2161 ++++++ src/z80/peeph.def | 7 + src/z80/profile.txt | 163 + src/z80/ralloc.c | 3224 +++++++++ src/z80/ralloc.h | 94 + src/z80/ralloc2.cc | 1707 +++++ src/z80/support.c | 37 + src/z80/support.h | 20 + src/z80/z80.h | 55 + src/z80/z80.vcxproj | 214 + src/z80/z80.vcxproj.filters | 79 + src/z80/z80a.vcxproj | 82 + 297 files changed, 268119 insertions(+) create mode 100644 src/Makefile create mode 100644 src/Makefile.dep create mode 100644 src/Makefile.in create mode 100644 src/README create mode 100644 src/SDCC.lex create mode 100644 src/SDCC.y create mode 100644 src/SDCCBBlock.c create mode 100644 src/SDCCBBlock.h create mode 100644 src/SDCCargs.h create mode 100644 src/SDCCasm.c create mode 100644 src/SDCCasm.h create mode 100644 src/SDCCast.c create mode 100644 src/SDCCast.h create mode 100644 src/SDCCbitv.c create mode 100644 src/SDCCbitv.h create mode 100644 src/SDCCbtree.cc create mode 100644 src/SDCCbtree.h create mode 100644 src/SDCCcflow.c create mode 100644 src/SDCCcflow.h create mode 100644 src/SDCCcse.c create mode 100644 src/SDCCcse.h create mode 100644 src/SDCCdebug.c create mode 100644 src/SDCCdebug.h create mode 100644 src/SDCCdflow.c create mode 100644 src/SDCCdflow.h create mode 100644 src/SDCCdwarf2.c create mode 100644 src/SDCCdwarf2.h create mode 100644 src/SDCCerr.c create mode 100644 src/SDCCerr.h create mode 100644 src/SDCCgas.c create mode 100644 src/SDCCgas.h create mode 100644 src/SDCCgen.c create mode 100644 src/SDCCgen.h create mode 100644 src/SDCCglobl.h create mode 100644 src/SDCCglue.c create mode 100644 src/SDCCglue.h create mode 100644 src/SDCChasht.c create mode 100644 src/SDCChasht.h create mode 100644 src/SDCCicode.c create mode 100644 src/SDCCicode.h create mode 100644 src/SDCClabel.c create mode 100644 src/SDCClabel.h create mode 100644 src/SDCCloop.c create mode 100644 src/SDCCloop.h create mode 100644 src/SDCClospre.cc create mode 100644 src/SDCClospre.hpp create mode 100644 src/SDCClrange.c create mode 100644 src/SDCClrange.h create mode 100644 src/SDCCmacro.c create mode 100644 src/SDCCmacro.h create mode 100644 src/SDCCmain.c create mode 100644 src/SDCCmem.c create mode 100644 src/SDCCmem.h create mode 100644 src/SDCCnaddr.cc create mode 100644 src/SDCCnaddr.hpp create mode 100644 src/SDCCopt.c create mode 100644 src/SDCCopt.h create mode 100644 src/SDCCpeeph.awk create mode 100644 src/SDCCpeeph.c create mode 100644 src/SDCCpeeph.h create mode 100644 src/SDCCptropt.c create mode 100644 src/SDCCptropt.h create mode 100644 src/SDCCralloc.hpp create mode 100644 src/SDCCsalloc.hpp create mode 100644 src/SDCCset.c create mode 100644 src/SDCCset.h create mode 100644 src/SDCCsymt.c create mode 100644 src/SDCCsymt.h create mode 100644 src/SDCCsystem.c create mode 100644 src/SDCCsystem.h create mode 100644 src/SDCCtree_dec.hpp create mode 100644 src/SDCCutil.c create mode 100644 src/SDCCutil.h create mode 100644 src/SDCCval.c create mode 100644 src/SDCCval.h create mode 100644 src/altlex.c create mode 100644 src/avr/Makefile.in create mode 100644 src/avr/avr.vcxproj create mode 100644 src/avr/avr.vcxproj.filters create mode 100644 src/avr/gen.c create mode 100644 src/avr/gen.h create mode 100644 src/avr/main.c create mode 100644 src/avr/main.h create mode 100644 src/avr/peeph.def create mode 100644 src/avr/ralloc.c create mode 100644 src/avr/ralloc.h create mode 100644 src/cdbFile.c create mode 100644 src/clean.mk create mode 100644 src/common.h create mode 100644 src/conf.mk create mode 100644 src/ds390/Makefile create mode 100644 src/ds390/Makefile.in create mode 100644 src/ds390/ds390.vcxproj create mode 100644 src/ds390/ds390.vcxproj.filters create mode 100644 src/ds390/gen.c create mode 100644 src/ds390/gen.h create mode 100644 src/ds390/main.c create mode 100644 src/ds390/main.h create mode 100644 src/ds390/peeph.def create mode 100644 src/ds390/ralloc.c create mode 100644 src/ds390/ralloc.h create mode 100644 src/hc08/Makefile create mode 100644 src/hc08/Makefile.in create mode 100644 src/hc08/gen.c create mode 100644 src/hc08/gen.h create mode 100644 src/hc08/hc08.h create mode 100644 src/hc08/hc08.vcxproj create mode 100644 src/hc08/hc08.vcxproj.filters create mode 100644 src/hc08/main.c create mode 100644 src/hc08/main.h create mode 100644 src/hc08/peeph.def create mode 100644 src/hc08/ralloc.c create mode 100644 src/hc08/ralloc.h create mode 100644 src/hc08/ralloc2.cc create mode 100644 src/mcs51/Makefile create mode 100644 src/mcs51/Makefile.in create mode 100644 src/mcs51/gen.c create mode 100644 src/mcs51/gen.h create mode 100644 src/mcs51/main.c create mode 100644 src/mcs51/main.h create mode 100644 src/mcs51/mcs51.vcxproj create mode 100644 src/mcs51/mcs51.vcxproj.filters create mode 100644 src/mcs51/peep.c create mode 100644 src/mcs51/peep.h create mode 100644 src/mcs51/peeph.def create mode 100644 src/mcs51/ralloc.c create mode 100644 src/mcs51/ralloc.h create mode 100644 src/mcs51/rtrack.c create mode 100644 src/mcs51/rtrack.h create mode 100644 src/pdk/Makefile create mode 100644 src/pdk/Makefile.dep create mode 100644 src/pdk/Makefile.in create mode 100644 src/pdk/gen.c create mode 100644 src/pdk/gen.h create mode 100644 src/pdk/gen.o create mode 100644 src/pdk/main.c create mode 100644 src/pdk/main.o create mode 100644 src/pdk/peep.c create mode 100644 src/pdk/peep.h create mode 100644 src/pdk/peep.o create mode 100644 src/pdk/peeph.def create mode 100644 src/pdk/peeph.rul create mode 100644 src/pdk/port.a create mode 100644 src/pdk/ralloc.c create mode 100644 src/pdk/ralloc.h create mode 100644 src/pdk/ralloc.o create mode 100644 src/pdk/ralloc2.cc create mode 100644 src/pdk/ralloc2.o create mode 100644 src/pic14/Makefile create mode 100644 src/pic14/Makefile.in create mode 100644 src/pic14/device.c create mode 100644 src/pic14/device.h create mode 100644 src/pic14/gen.c create mode 100644 src/pic14/gen.h create mode 100644 src/pic14/genarith.c create mode 100644 src/pic14/glue.c create mode 100644 src/pic14/glue.h create mode 100644 src/pic14/main.c create mode 100644 src/pic14/main.h create mode 100644 src/pic14/pcode.c create mode 100644 src/pic14/pcode.h create mode 100644 src/pic14/pcodeflow.c create mode 100644 src/pic14/pcodeflow.h create mode 100644 src/pic14/pcodepeep.c create mode 100644 src/pic14/pcoderegs.c create mode 100644 src/pic14/pcoderegs.h create mode 100644 src/pic14/peeph.def create mode 100644 src/pic14/pic14.vcxproj create mode 100644 src/pic14/pic14.vcxproj.filters create mode 100644 src/pic14/ralloc.c create mode 100644 src/pic14/ralloc.h create mode 100644 src/pic16/Makefile create mode 100644 src/pic16/Makefile.in create mode 100644 src/pic16/NOTES create mode 100644 src/pic16/device.c create mode 100644 src/pic16/device.h create mode 100644 src/pic16/gen.c create mode 100644 src/pic16/gen.h create mode 100644 src/pic16/genarith.c create mode 100644 src/pic16/genutils.c create mode 100644 src/pic16/genutils.h create mode 100644 src/pic16/glue.c create mode 100644 src/pic16/glue.h create mode 100644 src/pic16/graph.c create mode 100644 src/pic16/graph.h create mode 100644 src/pic16/main.c create mode 100644 src/pic16/main.h create mode 100644 src/pic16/pcode.c create mode 100644 src/pic16/pcode.h create mode 100644 src/pic16/pcodeflow.c create mode 100644 src/pic16/pcodeflow.h create mode 100644 src/pic16/pcodepeep.c create mode 100644 src/pic16/pcoderegs.c create mode 100644 src/pic16/pcoderegs.h create mode 100644 src/pic16/peeph.def create mode 100644 src/pic16/pic16.vcxproj create mode 100644 src/pic16/pic16.vcxproj.filters create mode 100644 src/pic16/ralloc.c create mode 100644 src/pic16/ralloc.h create mode 100644 src/port-clean.mk create mode 100644 src/port.h create mode 100644 src/port.mk create mode 100644 src/regression/Makefile create mode 100644 src/regression/add.c create mode 100644 src/regression/add2.c create mode 100644 src/regression/add3.c create mode 100644 src/regression/add4.c create mode 100644 src/regression/and1.c create mode 100644 src/regression/and2.c create mode 100644 src/regression/arrays.c create mode 100644 src/regression/b.c create mode 100644 src/regression/bank1.c create mode 100644 src/regression/bool1.c create mode 100644 src/regression/bool2.c create mode 100644 src/regression/bool3.c create mode 100644 src/regression/call1.c create mode 100644 src/regression/compare.c create mode 100644 src/regression/compare10.c create mode 100644 src/regression/compare2.c create mode 100644 src/regression/compare3.c create mode 100644 src/regression/compare4.c create mode 100644 src/regression/compare5.c create mode 100644 src/regression/compare6.c create mode 100644 src/regression/compare7.c create mode 100644 src/regression/compare8.c create mode 100644 src/regression/compare9.c create mode 100644 src/regression/configword.c create mode 100755 src/regression/create_stc create mode 100644 src/regression/empty.c create mode 100644 src/regression/for.c create mode 100644 src/regression/gpsim_assert.h create mode 100644 src/regression/init0.c create mode 100644 src/regression/inline.c create mode 100644 src/regression/mult1.c create mode 100644 src/regression/nestfor.c create mode 100644 src/regression/or1.c create mode 100644 src/regression/pcodeopt.c create mode 100644 src/regression/picregs.h create mode 100644 src/regression/pointer1.c create mode 100644 src/regression/ptrarg.c create mode 100644 src/regression/ptrfunc.c create mode 100644 src/regression/rotate1.c create mode 100644 src/regression/rotate2.c create mode 100644 src/regression/rotate3.c create mode 100644 src/regression/rotate4.c create mode 100644 src/regression/rotate5.c create mode 100644 src/regression/rotate6.c create mode 100644 src/regression/rotate7.c create mode 100755 src/regression/rt.sh create mode 100755 src/regression/simulate create mode 100644 src/regression/string1.c create mode 100644 src/regression/struct1.c create mode 100644 src/regression/sub.c create mode 100644 src/regression/sub2.c create mode 100644 src/regression/switch1.c create mode 100644 src/regression/while.c create mode 100644 src/regression/xor.c create mode 100644 src/reswords.gperf create mode 100644 src/sdcc.vcxproj create mode 100644 src/sdcc.vcxproj.filters create mode 100644 src/stm8/Makefile create mode 100644 src/stm8/Makefile.in create mode 100644 src/stm8/gen.c create mode 100644 src/stm8/gen.h create mode 100644 src/stm8/main.c create mode 100644 src/stm8/peep.c create mode 100644 src/stm8/peep.h create mode 100644 src/stm8/peeph.def create mode 100644 src/stm8/ralloc.c create mode 100644 src/stm8/ralloc.h create mode 100644 src/stm8/ralloc2.cc create mode 100644 src/stm8/stm8.vcxproj create mode 100644 src/stm8/stm8.vcxproj.filters create mode 100644 src/version.awk create mode 100644 src/z80/Makefile create mode 100644 src/z80/Makefile.in create mode 100644 src/z80/gen.c create mode 100644 src/z80/gen.h create mode 100644 src/z80/main.c create mode 100644 src/z80/mappings.i create mode 100644 src/z80/peep.c create mode 100644 src/z80/peep.h create mode 100644 src/z80/peeph-gbz80.def create mode 100644 src/z80/peeph-r2k.def create mode 100644 src/z80/peeph-tlcs90.def create mode 100644 src/z80/peeph-z80.def create mode 100644 src/z80/peeph.def create mode 100644 src/z80/profile.txt create mode 100644 src/z80/ralloc.c create mode 100644 src/z80/ralloc.h create mode 100644 src/z80/ralloc2.cc create mode 100644 src/z80/support.c create mode 100644 src/z80/support.h create mode 100644 src/z80/z80.h create mode 100644 src/z80/z80.vcxproj create mode 100644 src/z80/z80.vcxproj.filters create mode 100644 src/z80/z80a.vcxproj (limited to 'src') diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..692ac7e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,153 @@ +# +# +# + +srcdir = . +top_srcdir = .. +top_builddir = .. + + +include $(top_builddir)/Makefile.common + +USE_ALT_LEX = 0 + +PORTS = $(shell cat ../ports.build) +ALLPORTS = $(shell cat ../ports.all) +PORT_LIBS = $(PORTS:%=%/port.a) + +LIBS = -lm + +CFLAGS = -pipe -ggdb -g -O2 -Wall -Wno-parentheses +CXXFLAGS = -pipe -ggdb -g -O2 -Wall -Wno-parentheses +CPPFLAGS += -I$(srcdir) +LDFLAGS = + +ifdef SDCC_SUB_VERSION +CFLAGS += -DSDCC_SUB_VERSION_STR=\"$(SDCC_SUB_VERSION)\" +endif + +SLIBOBJS = NewAlloc.o dbuf.o dbuf_string.o findme.o + +OBJECTS = SDCCy.o SDCChasht.o SDCCmain.o \ + SDCCsymt.o SDCCopt.o SDCCast.o SDCCmem.o SDCCval.o \ + SDCCicode.o SDCCbitv.o SDCCset.o SDCClabel.o \ + SDCCBBlock.o SDCCloop.o SDCCcse.o SDCCcflow.o SDCCdflow.o \ + SDCClrange.o SDCCptropt.o SDCCpeeph.o SDCCglue.o \ + SDCCasm.o SDCCmacro.o SDCCutil.o SDCCdebug.o cdbFile.o SDCCdwarf2.o\ + SDCCerr.o SDCCsystem.o SDCCgen.o SDCCgas.o + +SPECIAL = SDCCy.h +ifeq ($(USE_ALT_LEX), 1) +OBJECTS += altlex.o +SPECIAL += reswords.h +else +OBJECTS += SDCClex.o +endif + +SLIBSOURCES = $(patsubst %.o,$(SLIB)/%.c,$(SLIBOBJS)) +SOURCES := $(patsubst %.o,%.c,$(OBJECTS)) + +# C++ sources (.cc extension) +CXXSOURCES += $(sort $(notdir $(wildcard $(srcdir)/*.cc))) +OBJECTS += $(patsubst %.cc,%.o,$(CXXSOURCES)) + +TARGET = $(top_builddir)/bin/sdcc$(EXEEXT) + +# Compiling entire program or any subproject +# ------------------------------------------ +all: checkconf version.h $(TARGET) + +$(PORT_LIBS): FORCE + $(MAKE) -C $(dir $@) + +FORCE: + +$(top_builddir)/support/gc/libgc.a: + $(MAKE) -C $(top_builddir)/support/gc + +# Compiling and installing everything and runing test +# --------------------------------------------------- +install: all installdirs + $(INSTALL) $(TARGET) $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + $(STRIP) $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + + +# Deleting all the installed files +# -------------------------------- +uninstall: + rm -f $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + +# Performing self-test +# -------------------- +check: + + +# Performing installation test +# ---------------------------- +installcheck: + + +# Creating installation directories +# --------------------------------- +installdirs: + $(INSTALL) -d $(DESTDIR)$(bindir) + + +# Creating dependencies +# --------------------- +dep: version.h Makefile.dep + +Makefile.dep: version.h $(SOURCES) $(CXXSOURCES) $(SLIBSOURCES) $(SPECIAL) + $(MAKEDEP) $(CPPFLAGS) $(filter %.c,$^) $(filter %.cc,$^) >Makefile.dep + +# don't include Makefile.dep for the listed targets: +ifeq "$(findstring $(MAKECMDGOALS),uninstall check installcheck installdirs checkconf \ + clean distclean mostlyclean realclean)" "" + -include Makefile.dep +endif +include $(srcdir)/clean.mk + + +# My rules +# -------- +$(TARGET): $(SLIBOBJS) $(OBJECTS) $(PORT_LIBS) + $(CXX) $(LDFLAGS) -o $@ $(SLIBOBJS) $(OBJECTS) $(PORT_LIBS) $(LIBDIRS) $(LIBS) + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +$(SLIBOBJS):%.o:$(SLIB)/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +reswords.h: reswords.gperf Makefile + gperf -o -k1,2,4 -t -C -N is_reserved_word $< > $@ + +altlex.o: altlex.c SDCCy.h reswords.h + +SDCCy.h: SDCCy.c + +SDCCy.c: SDCC.y + $(YACC) -d $< + mv y.tab.c $*.c + mv y.tab.h $*.h + +SDCClex.c: SDCC.lex SDCCy.h + $(LEX) -8 -f -t $< >$@ + +version.h: ../ChangeLog + $(AWK) -f $(srcdir)/version.awk $< > $@ + +.l.c: + rm -f $*.cc + $(LEX) -t $< >$*.cc + + +# Remaking configuration +# ---------------------- +checkconf: + @if [ -f $(top_builddir)/devel ]; then\ + $(MAKE) -f $(srcdir)/conf.mk srcdir="$(srcdir)" top_builddir="$(top_builddir)" \ + freshconf;\ + fi + +# End of main_in.mk/main.mk diff --git a/src/Makefile.dep b/src/Makefile.dep new file mode 100644 index 0000000..f67fbcf --- /dev/null +++ b/src/Makefile.dep @@ -0,0 +1,231 @@ +SDCCy.o: SDCCy.c SDCCglobl.h SDCCset.h ../sdccconf.h SDCCerr.h SDCCsymt.h \ + SDCChasht.h ../support/util/dbuf.h SDCCval.h SDCCmem.h SDCCast.h port.h \ + SDCCicode.h SDCCbitv.h SDCCargs.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h ../support/util/newalloc.h SDCCutil.h \ + SDCCbtree.h SDCCopt.h SDCCBBlock.h +SDCChasht.o: SDCChasht.c SDCCerr.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCChasht.h ../support/util/newalloc.h +SDCCmain.o: SDCCmain.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h SDCCmacro.h +SDCCsymt.o: SDCCsymt.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCopt.o: SDCCopt.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCast.o: SDCCast.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h SDCCbtree.h +SDCCmem.o: SDCCmem.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h SDCCbtree.h +SDCCval.o: SDCCval.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCicode.o: SDCCicode.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCbitv.o: SDCCbitv.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCset.o: SDCCset.c ../support/util/newalloc.h SDCCerr.h SDCCset.h +SDCClabel.o: SDCClabel.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCBBlock.o: SDCCBBlock.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCloop.o: SDCCloop.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCcse.o: SDCCcse.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCcflow.o: SDCCcflow.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCdflow.o: SDCCdflow.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCClrange.o: SDCClrange.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCptropt.o: SDCCptropt.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCpeeph.o: SDCCpeeph.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCglue.o: SDCCglue.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCasm.o: SDCCasm.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCmacro.o: SDCCmacro.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCutil.o: SDCCutil.c ../support/util/dbuf.h SDCCglobl.h SDCCset.h \ + ../sdccconf.h SDCCerr.h SDCCmacro.h SDCChasht.h SDCCutil.h \ + ../support/util/newalloc.h ../support/util/dbuf_string.h \ + ../support/util/dbuf.h ../support/util/findme.h version.h +SDCCdebug.o: SDCCdebug.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +cdbFile.o: cdbFile.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h +SDCCdwarf2.o: SDCCdwarf2.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h SDCCdwarf2.h +SDCCerr.o: SDCCerr.c SDCCglobl.h SDCCset.h ../sdccconf.h SDCCerr.h +SDCCsystem.o: SDCCsystem.c SDCCglobl.h SDCCset.h ../sdccconf.h SDCCerr.h \ + SDCCutil.h SDCChasht.h ../support/util/dbuf.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h SDCCsystem.h \ + ../support/util/newalloc.h +SDCCgen.o: SDCCgen.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +SDCCgas.o: SDCCgas.c SDCCgas.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h ../support/util/newalloc.h +SDCClex.o: SDCClex.c common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +NewAlloc.o: ../support/util/NewAlloc.c ../support/util/newalloc.h +dbuf.o: ../support/util/dbuf.c ../support/util/dbuf.h +dbuf_string.o: ../support/util/dbuf_string.c \ + ../support/util/dbuf_string.h ../support/util/dbuf.h +findme.o: ../support/util/findme.c ../support/util/system.h \ + ../support/util/findme.h +SDCCbtree.o: SDCCbtree.cc common.h SDCCglobl.h SDCCset.h ../sdccconf.h \ + SDCCerr.h SDCCmem.h ../support/util/dbuf.h SDCCsymt.h SDCChasht.h \ + SDCCval.h SDCCast.h SDCCy.h SDCCbitv.h SDCCicode.h SDCClabel.h \ + SDCCBBlock.h SDCCloop.h SDCCcse.h SDCCcflow.h SDCCdflow.h SDCClrange.h \ + SDCCptropt.h SDCCopt.h SDCCglue.h SDCCpeeph.h SDCCgen.h SDCCicode.h \ + SDCCbitv.h SDCCset.h SDCCast.h SDCCdebug.h SDCCutil.h SDCCasm.h \ + SDCCsystem.h port.h SDCCargs.h ../support/util/newalloc.h SDCCbtree.h +SDCClospre.o: SDCClospre.cc SDCClospre.hpp SDCCsymt.h SDCChasht.h \ + SDCCglobl.h SDCCset.h ../sdccconf.h SDCCerr.h ../support/util/dbuf.h \ + SDCCval.h SDCCicode.h SDCCbitv.h SDCCast.h SDCCmem.h SDCCgen.h \ + SDCCicode.h SDCCbitv.h SDCCset.h SDCCast.h SDCCBBlock.h SDCCopt.h \ + SDCCy.h SDCCasm.h port.h SDCCargs.h SDCCpeeph.h SDCCtree_dec.hpp +SDCCnaddr.o: SDCCnaddr.cc SDCCnaddr.hpp SDCCsymt.h SDCChasht.h \ + SDCCglobl.h SDCCset.h ../sdccconf.h SDCCerr.h ../support/util/dbuf.h \ + SDCCval.h SDCCicode.h SDCCbitv.h SDCCast.h SDCCmem.h SDCCBBlock.h \ + SDCCopt.h SDCCy.h SDCCtree_dec.hpp diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..f1d012a --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,153 @@ +# +# +# + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +top_builddir = @top_builddir@ +VPATH = @srcdir@ + +include $(top_builddir)/Makefile.common + +USE_ALT_LEX = 0 + +PORTS = $(shell cat ../ports.build) +ALLPORTS = $(shell cat ../ports.all) +PORT_LIBS = $(PORTS:%=%/port.a) + +LIBS = -lm @LIBS@ + +CFLAGS = @CFLAGS@ @WALL_FLAG@ +CXXFLAGS = @CXXFLAGS@ @WALL_FLAG@ +CPPFLAGS += -I$(srcdir) +LDFLAGS = @LDFLAGS@ + +ifdef SDCC_SUB_VERSION +CFLAGS += -DSDCC_SUB_VERSION_STR=\"$(SDCC_SUB_VERSION)\" +endif + +SLIBOBJS = NewAlloc.o dbuf.o dbuf_string.o findme.o + +OBJECTS = SDCCy.o SDCChasht.o SDCCmain.o \ + SDCCsymt.o SDCCopt.o SDCCast.o SDCCmem.o SDCCval.o \ + SDCCicode.o SDCCbitv.o SDCCset.o SDCClabel.o \ + SDCCBBlock.o SDCCloop.o SDCCcse.o SDCCcflow.o SDCCdflow.o \ + SDCClrange.o SDCCptropt.o SDCCpeeph.o SDCCglue.o \ + SDCCasm.o SDCCmacro.o SDCCutil.o SDCCdebug.o cdbFile.o SDCCdwarf2.o\ + SDCCerr.o SDCCsystem.o SDCCgen.o SDCCgas.o + +SPECIAL = SDCCy.h +ifeq ($(USE_ALT_LEX), 1) +OBJECTS += altlex.o +SPECIAL += reswords.h +else +OBJECTS += SDCClex.o +endif + +SLIBSOURCES = $(patsubst %.o,$(SLIB)/%.c,$(SLIBOBJS)) +SOURCES := $(patsubst %.o,%.c,$(OBJECTS)) + +# C++ sources (.cc extension) +CXXSOURCES += $(sort $(notdir $(wildcard $(srcdir)/*.cc))) +OBJECTS += $(patsubst %.cc,%.o,$(CXXSOURCES)) + +TARGET = $(top_builddir)/bin/sdcc$(EXEEXT) + +# Compiling entire program or any subproject +# ------------------------------------------ +all: checkconf version.h $(TARGET) + +$(PORT_LIBS): FORCE + $(MAKE) -C $(dir $@) + +FORCE: + +$(top_builddir)/support/gc/libgc.a: + $(MAKE) -C $(top_builddir)/support/gc + +# Compiling and installing everything and runing test +# --------------------------------------------------- +install: all installdirs + $(INSTALL) $(TARGET) $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + $(STRIP) $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + + +# Deleting all the installed files +# -------------------------------- +uninstall: + rm -f $(DESTDIR)$(bindir)/`echo sdcc|sed '$(transform)'`$(EXEEXT) + +# Performing self-test +# -------------------- +check: + + +# Performing installation test +# ---------------------------- +installcheck: + + +# Creating installation directories +# --------------------------------- +installdirs: + $(INSTALL) -d $(DESTDIR)$(bindir) + + +# Creating dependencies +# --------------------- +dep: version.h Makefile.dep + +Makefile.dep: version.h $(SOURCES) $(CXXSOURCES) $(SLIBSOURCES) $(SPECIAL) + $(MAKEDEP) $(CPPFLAGS) $(filter %.c,$^) $(filter %.cc,$^) >Makefile.dep + +# don't include Makefile.dep for the listed targets: +ifeq "$(findstring $(MAKECMDGOALS),uninstall check installcheck installdirs checkconf \ + clean distclean mostlyclean realclean)" "" + -include Makefile.dep +endif +include $(srcdir)/clean.mk + + +# My rules +# -------- +$(TARGET): $(SLIBOBJS) $(OBJECTS) $(PORT_LIBS) + $(CXX) $(LDFLAGS) -o $@ $(SLIBOBJS) $(OBJECTS) $(PORT_LIBS) $(LIBDIRS) $(LIBS) + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +$(SLIBOBJS):%.o:$(SLIB)/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +reswords.h: reswords.gperf Makefile + gperf -o -k1,2,4 -t -C -N is_reserved_word $< > $@ + +altlex.o: altlex.c SDCCy.h reswords.h + +SDCCy.h: SDCCy.c + +SDCCy.c: SDCC.y + $(YACC) -d $< + mv y.tab.c $*.c + mv y.tab.h $*.h + +SDCClex.c: SDCC.lex SDCCy.h + $(LEX) -8 -f -t $< >$@ + +version.h: ../ChangeLog + $(AWK) -f $(srcdir)/version.awk $< > $@ + +.l.c: + rm -f $*.cc + $(LEX) -t $< >$*.cc + + +# Remaking configuration +# ---------------------- +checkconf: + @if [ -f $(top_builddir)/devel ]; then\ + $(MAKE) -f $(srcdir)/conf.mk srcdir="$(srcdir)" top_builddir="$(top_builddir)" \ + freshconf;\ + fi + +# End of main_in.mk/main.mk diff --git a/src/README b/src/README new file mode 100644 index 0000000..ac1f53d --- /dev/null +++ b/src/README @@ -0,0 +1,8 @@ +sdcc/src +-------- + +C compiler source. + +Everything shared is in this directory. Port specific files like the +code generator (gen.c), register allocator (ralloc.c) and support +functions are in the appropriate port directory (ie mcs51 for the 8051). diff --git a/src/SDCC.lex b/src/SDCC.lex new file mode 100644 index 0000000..6dd53c2 --- /dev/null +++ b/src/SDCC.lex @@ -0,0 +1,1187 @@ +/*----------------------------------------------------------------------- + SDCC.lex - lexical analyser for use with sdcc (free open source + compiler for 8/16 bit microcontrollers) + Written by : Sandeep Dutta . sandeep.dutta@usa.net (1997) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +B [0-1] +D [0-9] +L [a-zA-Z_$] +H [a-fA-F0-9] +E [Ee][+-]?{D}+ +BE [Pp][+-]?{D}+ +FS (f|F|l|L) +IS (u|U|l|L)* +CP (L|u|U) +HASH (#|%:) + +%{ +#include +#include +#include +#include "common.h" +#include "newalloc.h" +#include "dbuf_string.h" +/* Needed by flex 2.5.4 on NetBSD 5.0.1 sparc64 */ +#ifdef HAVE_UNISTD_H +#include +#endif +/* MSVC has no unistd.h but has read() declaration in io.h */ +#if defined(_MSC_VER) +# include +#endif + +#define TKEYWORD(token) return (isTargetKeyword(yytext) ? token :\ + check_type()) + +#define TKEYWORD99(token) return (options.std_c99 ? token : check_type()) + +int column = 0; /* current column */ + +/* global definitions */ +char *lexFilename; +int lexLineno = 1; + +/* local definitions */ +static struct dbuf_s asmbuff; /* reusable _asm buffer */ + +/* forward declarations */ +int yyerror (char *s); +static const char *stringLiteral (char); +static void count (void); +static void count_char (int); +static int process_pragma (const char *); +static int check_type (void); +static void checkCurrFile (const char *s); +%} + +%x asm +%% +"__asm" { + count (); + if (asmbuff.buf == NULL) + dbuf_init (&asmbuff, INITIAL_INLINEASM); + else + dbuf_set_length (&asmbuff, 0); + + BEGIN (asm); +} +"__endasm" { + count (); + yylval.yystr = dbuf_c_str (&asmbuff); + BEGIN (INITIAL); + return INLINEASM; +} +\n { + count (); + dbuf_append_char(&asmbuff, *yytext); +} +. { + dbuf_append_char(&asmbuff, *yytext); +} +"__asm__" { count (); return ASM; } +"__at" { count (); TKEYWORD (AT); } +"auto" { count (); return AUTO; } +"__bit" { count (); TKEYWORD (BIT); } +"_Bool" { count (); TKEYWORD99 (SD_BOOL); } +"break" { count (); return BREAK; } +"case" { count (); return CASE; } +"char" { count (); return SD_CHAR; } +"__code" { count (); TKEYWORD (CODE); } +"const" { count (); return SD_CONST; } +"continue" { count (); return CONTINUE; } +"__critical" { count (); TKEYWORD (CRITICAL); } +"__data" { count (); TKEYWORD (DATA); } +"default" { count (); return DEFAULT; } +"do" { count (); return DO; } +"double" { count (); werror (W_DOUBLE_UNSUPPORTED); return SD_FLOAT; } +"else" { count (); return ELSE; } +"enum" { count (); return ENUM; } +"extern" { count (); return EXTERN; } +"__far" { count (); TKEYWORD (XDATA); } +"__eeprom" { count (); TKEYWORD (EEPROM); } +"float" { count (); return SD_FLOAT; } +"__fixed16x16" { count (); TKEYWORD (FIXED16X16); } +"__flash" { count (); TKEYWORD (CODE); } +"for" { count (); return FOR; } +"goto" { count (); return GOTO; } +"__idata" { count (); TKEYWORD (IDATA); } +"if" { count (); return IF; } +"int" { count (); return SD_INT; } +"__interrupt" { count (); TKEYWORD (INTERRUPT); } +"__nonbanked" { count (); TKEYWORD (NONBANKED); } +"__banked" { count (); TKEYWORD (BANKED); } +"__trap" { count (); TKEYWORD (TRAP); } +"long" { count (); return SD_LONG; } +"__near" { count (); TKEYWORD (DATA); } +"__pdata" { count (); TKEYWORD (PDATA); } +"__reentrant" { count (); TKEYWORD (REENTRANT); } +"__shadowregs" { count (); TKEYWORD (SHADOWREGS); } +"__wparam" { count (); TKEYWORD (SD_WPARAM); } +"register" { count (); return REGISTER; } +"return" { count (); return RETURN; } +"__sfr" { count (); TKEYWORD (SFR); } +"__sfr16" { count (); TKEYWORD (SFR16); } +"__sfr32" { count (); TKEYWORD (SFR32); } +"__sbit" { count (); TKEYWORD (SBIT); } +"short" { count (); return SD_SHORT; } +"signed" { count (); return SIGNED; } +"sizeof" { count (); return SIZEOF; } +"_Alignof" { count (); return ALIGNOF; } +"__builtin_offsetof" { count (); return OFFSETOF; } +"__sram" { count (); TKEYWORD (XDATA); } +"static" { count (); return STATIC; } +"struct" { count (); return STRUCT; } +"switch" { count (); return SWITCH; } +"typedef" { count (); return TYPEDEF; } +"union" { count (); return UNION; } +"unsigned" { count (); return UNSIGNED; } +"void" { count (); return SD_VOID; } +"volatile" { count (); return VOLATILE; } +"__using" { count (); TKEYWORD (USING); } +"__naked" { count (); TKEYWORD (NAKED); } +"while" { count (); return WHILE; } +"__xdata" { count (); TKEYWORD (XDATA); } +"..." { count (); return VAR_ARGS; } +"__typeof" { count (); return TYPEOF; } +"_JavaNative" { count (); TKEYWORD (JAVANATIVE); } +"__overlay" { count (); TKEYWORD (OVERLAY); } +"inline" { count (); TKEYWORD99 (INLINE); } +"_Noreturn" { count (); return NORETURN;} +"restrict" { count (); TKEYWORD99 (RESTRICT); } +"__smallc" { count (); TKEYWORD (SMALLC); } +"__preserves_regs" { count (); return PRESERVES_REGS; } +"__z88dk_fastcall" { count (); TKEYWORD (Z88DK_FASTCALL); } +"__z88dk_callee" { count (); TKEYWORD (Z88DK_CALLEE); } +"__z88dk_shortcall" { count (); return Z88DK_SHORTCALL; } +"__z88dk_params_offset" { count (); return Z88DK_PARAMS_OFFSET; } +"__addressmod" { count (); return ADDRESSMOD; } +"_Static_assert" { count (); return STATIC_ASSERT; } +"_Alignas" { count (); return ALIGNAS; } +"_Generic" { count (); return GENERIC; } +{L}({L}|{D})* { + if (!options.dollars_in_ident && strchr (yytext, '$')) + { + yyerror ("stray '$' in program"); + } + count (); + return check_type(); +} +0[bB]{B}+{IS}? { + if (!options.std_sdcc) + { + yyerror ("binary (0b) constants are not allowed in ISO C"); + } + count (); + yylval.val = constIntVal (yytext); + return CONSTANT; +} +0[xX]{H}+{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +0[0-7]*{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +[1-9]{D}*{IS}? { count (); yylval.val = constIntVal (yytext); return CONSTANT; } +{CP}?'(\\.|[^\\'])+' { count (); yylval.val = charVal (yytext); return CONSTANT; /* ' make syntax highlighter happy */ } +{D}+{E}{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +{D}*"."{D}+({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +{D}+"."{D}*({E})?{FS}? { count (); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}+{BE}{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}*"."{H}+({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +0[xX]{H}+"."{H}*({BE})?{FS}? { count (); if (!options.std_c99) werror(E_HEXFLOAT_C99); yylval.val = constFloatVal (yytext); return CONSTANT; } +\" { count (); yylval.yystr = stringLiteral (0); return STRING_LITERAL; } +"L\"" { count (); if (!options.std_c95) werror(E_WCHAR_STRING_C95); yylval.yystr = stringLiteral ('L'); return STRING_LITERAL; } +"u8\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral (0); return STRING_LITERAL; } +"u\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('u'); return STRING_LITERAL; } +"U\"" { count (); if (!options.std_c11) werror(E_WCHAR_STRING_C11); yylval.yystr = stringLiteral ('U'); return STRING_LITERAL; } +">>=" { count (); yylval.yyint = RIGHT_ASSIGN; return RIGHT_ASSIGN; } +"<<=" { count (); yylval.yyint = LEFT_ASSIGN; return LEFT_ASSIGN; } +"+=" { count (); yylval.yyint = ADD_ASSIGN; return ADD_ASSIGN; } +"-=" { count (); yylval.yyint = SUB_ASSIGN; return SUB_ASSIGN; } +"*=" { count (); yylval.yyint = MUL_ASSIGN; return MUL_ASSIGN; } +"/=" { count (); yylval.yyint = DIV_ASSIGN; return DIV_ASSIGN; } +"%=" { count (); yylval.yyint = MOD_ASSIGN; return MOD_ASSIGN; } +"&=" { count (); yylval.yyint = AND_ASSIGN; return AND_ASSIGN; } +"^=" { count (); yylval.yyint = XOR_ASSIGN; return XOR_ASSIGN; } +"|=" { count (); yylval.yyint = OR_ASSIGN; return OR_ASSIGN; } +">>" { count (); return RIGHT_OP; } +"<<" { count (); return LEFT_OP; } +"++" { count (); return INC_OP; } +"--" { count (); return DEC_OP; } +"->" { count (); return PTR_OP; } +"&&" { count (); return AND_OP; } +"||" { count (); return OR_OP; } +"<=" { count (); return LE_OP; } +">=" { count (); return GE_OP; } +"==" { count (); return EQ_OP; } +"!=" { count (); return NE_OP; } +";" { count (); return ';'; } +"{"|"<%" { count (); ignoreTypedefType = 0; return '{'; } +"}"|"%>" { count (); return '}'; } +"," { count (); return ','; } +":" { count (); return ':'; } +"=" { count (); return '='; } +"(" { count (); ignoreTypedefType = 0; return '('; } +")" { count (); return ')'; } +"["|"<:" { count (); return '['; } +"]"|":>" { count (); return ']'; } +"." { count (); return '.'; } +"&" { count (); return '&'; } +"!" { count (); return '!'; } +"~" { count (); return '~'; } +"-" { count (); return '-'; } +"+" { count (); return '+'; } +"*" { count (); return '*'; } +"/" { count (); return '/'; } +"%" { count (); return '%'; } +"<" { count (); return '<'; } +">" { count (); return '>'; } +"^" { count (); return '^'; } +"|" { count (); return '|'; } +"?" { count (); return '?'; } +^{HASH}pragma.* { count (); process_pragma (yytext); } +^{HASH}.* { count (); checkCurrFile (yytext); } + +"\r\n" { count (); } +"\n" { count (); } +[ \t\v\f] { count (); } +\\ { + int ch = input (); + + if (ch == '\n') + count_char (ch); + else + { + /* that could have been removed by the preprocessor anyway */ + werror (W_STRAY_BACKSLASH, column); + unput (ch); + } +} +. { count (); } +%% + +/* flex 2.5.31 undefines yytext_ptr, so we have to define it again */ +#ifndef yytext_ptr +#define yytext_ptr yytext +#endif + +static void +checkCurrFile (const char *s) +{ + int lNum; + char *tptr; + + /* skip '#' character */ + if (*s++ != '#') + return; + + /* get the line number */ + lNum = strtol (s, &tptr, 10); + if (tptr == s || !isspace ((unsigned char) *tptr)) + return; + s = tptr; + + /* adjust the line number */ + lineno = lexLineno = lNum - 1; + + /* now see if we have a file name */ + while (*s != '"' && *s) + ++s; + + if (*s) + { + /* there is a file name */ + const char *sb; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + + /* skip the double quote */ + sb = ++s; + + /* preprocessor emits escaped file names + * (e.g. double backslashes on MSDOS-ish file systems), + * so we have to unescape it */ + while (*s && *s != '"') + { + if (*s == '\\') + { + /* append chars before backslash */ + dbuf_append (&dbuf, sb, s - sb); + if (*++s) + { + /* append char after backslash */ + dbuf_append (&dbuf, s, 1); + sb = ++s; + } + } + else + ++s; + } + dbuf_append (&dbuf, sb, s - sb); + + /* DON'T free the file name referenced by lexFilename + * since it will be dereferenced in the future at least + * by function printCLine(), see struct iCode members + * filename in SDCCicode.c */ + + filename = lexFilename = dbuf_detach_c_str (&dbuf); + } +} + +static void +count_char (int ch) +{ + switch (ch) + { + case '\n': + column = 0; + lineno = ++lexLineno; + break; + + case '\t': + column += 8 - (column % 8); + break; + + default: + ++column; + break; + } +} + +static void +count (void) +{ + const char *p; + + for (p = yytext; *p; ++p) + count_char(*p); +} + +static int +check_type (void) +{ + symbol *sym = findSym(SymbolTab, NULL, yytext); + + strncpyz(yylval.yychar, yytext, SDCC_NAME_MAX); + + /* check if it is in the table as a typedef */ + if (!ignoreTypedefType && sym && IS_SPEC (sym->etype) + && SPEC_TYPEDEF (sym->etype) && findSym(TypedefTab, NULL, yytext)) + return (TYPE_NAME); + /* check if it is a named address space */ + else if (findSym (AddrspaceTab, NULL, yytext)) + return (ADDRSPACE_NAME); + else + return(IDENTIFIER); +} + +/* + * Change by JTV 2001-05-19 to not concatenate strings + * to support ANSI hex and octal escape sequences in string literals + */ + +static const char * +stringLiteral (char enc) +{ +#define STR_BUF_CHUNCK_LEN 1024 + int ch; + static struct dbuf_s dbuf; /* reusable string literal buffer */ + + if (dbuf.alloc == 0) + dbuf_init(&dbuf, STR_BUF_CHUNCK_LEN); + else + dbuf_set_length(&dbuf, 0); + + switch (enc) + { + case 'u': // UTF-16 + dbuf_append_str(&dbuf, "u\""); + break; + case 'L': + case 'U': // UTF-32 + enc = 'U'; + dbuf_append_str(&dbuf, "U\""); + break; + default: // UTF-8 or whatever else the source character set is encoded in + dbuf_append_char(&dbuf, '"'); + } + + /* put into the buffer till we hit the first \" */ + + for (; ; ) + { + ch = input(); + count_char(ch); + if (ch == EOF) + break; + + switch (ch) + { + case '\\': + /* if it is a \ then escape char's are allowed */ + ch = input(); + count_char(ch); + if (ch == '\n') + { + /* \ is a continuator */ + } + else + { + char buf[2]; + + if (ch == EOF) + goto out; + + buf[0] = '\\'; + buf[1] = ch; + dbuf_append(&dbuf, buf, 2); /* get the escape char, no further check */ + } + break; /* carry on */ + + case '\n': + /* if new line we have a new line break, which is illegal */ + werror(W_NEWLINE_IN_STRING); + dbuf_append_char(&dbuf, '\n'); + break; + + case '"': + /* if this is a quote then we have work to do */ + /* find the next non whitespace character */ + /* if that is a double quote then carry on */ + dbuf_append_char(&dbuf, '"'); /* Pass end of this string or substring to evaluator */ + while ((ch = input()) && (isspace(ch) || ch == '\\' || ch == '#')) + { + switch (ch) + { + case '\\': + count_char(ch); + if ((ch = input()) != '\n') + { + werror(W_STRAY_BACKSLASH, column); + if (ch != EOF) + unput(ch); + else + count_char(ch); + } + else + count_char(ch); + break; + + case '\n': + count_char(ch); + break; + + case '#': + if (column == 0) + { + /* # at the beginning of the line: collect the entire line */ + struct dbuf_s linebuf; + const char *line; + + count_char(ch); + + dbuf_init(&linebuf, STR_BUF_CHUNCK_LEN); + dbuf_append_char(&linebuf, '#'); + + while ((ch = input()) != EOF && ch != '\n') + dbuf_append_char(&linebuf, (char)ch); + + if (ch == '\n') + count_char(ch); + + line = dbuf_c_str(&linebuf); + + /* process the line */ + if (startsWith(line, "#pragma")) + process_pragma(line); + else + checkCurrFile(line); + + dbuf_destroy(&linebuf); + } + else + { + unput(ch); + goto out; + } + + default: + count_char(ch); + break; + } + } + + if (ch == EOF) + goto out; + + if (ch == 'u' || ch == 'U' || ch == 'L') /* Could be an utf-16 or utf-32 wide string literal prefix */ + { + int ch2; + + if (!(options.std_c11 || options.std_c95 && ch == 'L')) + { + werror (ch == 'L' ? E_WCHAR_STRING_C95 : E_WCHAR_STRING_C11); + unput(ch); + goto out; + } + + ch2 = input(); + if (ch2 != '"') + unput (ch2); + else /* It is an utf-16 or utf-32 wide string literal prefix */ + { + if (!enc) + { + dbuf_prepend_char(&dbuf, ch == 'L' ? 'U' : ch); + enc = ch; + } + count_char(ch); + count_char(ch2); + break; + } + } + + if (ch == 'u') /* Could be an utf-8 wide string literal prefix */ + { + ch = input(); + if (ch != '8') + { + unput(ch); + unput('u'); + goto out; + } + ch = input(); + if (ch != '"') + { + unput(ch); + unput('8'); + unput('u'); + goto out; + } + } + if (ch != '"') + { + unput(ch); + goto out; + } + count_char(ch); + break; + + default: + dbuf_append_char(&dbuf, (char)ch); /* Put next substring introducer into output string */ + } + } + +out: + return dbuf_c_str(&dbuf); +} + + +enum { + P_SAVE = 1, + P_RESTORE, + P_INDUCTION, + P_NOINDUCTION, + P_NOINVARIANT, + P_STACKAUTO, + P_OVERLAY_, /* I had a strange conflict with P_OVERLAY while */ + /* cross-compiling for MINGW32 with gcc 3.2 */ + P_NOOVERLAY, + P_LESSPEDANTIC, + P_NOGCSE, + P_CALLEE_SAVES, + P_EXCLUDE, + P_NOIV, + P_LOOPREV, + P_DISABLEWARN, + P_OPTCODESPEED, + P_OPTCODESIZE, + P_OPTCODEBALANCED, + P_STD_C89, + P_STD_C99, + P_STD_C11, + P_STD_C2X, + P_STD_SDCC89, + P_STD_SDCC99, + P_CODESEG, + P_CONSTSEG +}; + + +/* SAVE/RESTORE stack */ +#define SAVE_RESTORE_SIZE 128 + +STACK_DCL(options_stack, struct options *, SAVE_RESTORE_SIZE) +STACK_DCL(optimize_stack, struct optimize *, SAVE_RESTORE_SIZE) +STACK_DCL(SDCCERRG_stack, struct SDCCERRG *, SAVE_RESTORE_SIZE) + +/* + * cloneXxx functions should be updated every time a new set is + * added to the options or optimize structure! + */ + +static struct options * +cloneOptions (struct options *opt) +{ + struct options *new_opt; + + new_opt = Safe_malloc(sizeof (struct options)); + + /* clone scalar values */ + *new_opt = *opt; + + /* clone sets */ + new_opt->calleeSavesSet = setFromSetNonRev(opt->calleeSavesSet); + new_opt->excludeRegsSet = setFromSetNonRev(opt->excludeRegsSet); + /* not implemented yet: */ + /* new_opt->olaysSet = setFromSetNonRev(opt->olaysSet); */ + + return new_opt; +} + +static struct optimize * +cloneOptimize (struct optimize *opt) +{ + struct optimize *new_opt; + + new_opt = Safe_malloc(sizeof (struct optimize)); + + /* clone scalar values */ + *new_opt = *opt; + + return new_opt; +} + +static struct SDCCERRG * +cloneSDCCERRG (struct SDCCERRG *val) +{ + struct SDCCERRG *new_val; + + new_val = Safe_malloc(sizeof (struct SDCCERRG)); + + /* clone scalar values */ + *new_val = *val; + + return new_val; +} + +static void +copyAndFreeOptions (struct options *dest, struct options *src) +{ + /* delete dest sets */ + deleteSet(&dest->calleeSavesSet); + deleteSet(&dest->excludeRegsSet); + /* not implemented yet: */ + /* deleteSet(&dest->olaysSet); */ + + /* copy src to dest */ + *dest = *src; + + Safe_free(src); +} + +static void +copyAndFreeOptimize (struct optimize *dest, struct optimize *src) +{ + /* copy src to dest */ + *dest = *src; + + Safe_free(src); +} + +static void +copyAndFreeSDCCERRG (struct SDCCERRG *dest, struct SDCCERRG *src) +{ + /* copy src to dest */ + *dest = *src; + + Safe_free(src); +} + +/* + * returns 1 if the pragma was processed, 0 if not + */ +static int +doPragma (int id, const char *name, const char *cp) +{ + struct pragma_token_s token; + int err = 0; + int processed = 1; + + init_pragma_token(&token); + + switch (id) + { + case P_SAVE: + { + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + STACK_PUSH(options_stack, cloneOptions(&options)); + STACK_PUSH(optimize_stack, cloneOptimize(&optimize)); + STACK_PUSH(SDCCERRG_stack, cloneSDCCERRG(&_SDCCERRG)); + } + break; + + case P_RESTORE: + { + struct options *optionsp; + struct optimize *optimizep; + struct SDCCERRG *sdccerrgp; + + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optionsp = STACK_POP(options_stack); + copyAndFreeOptions(&options, optionsp); + + optimizep = STACK_POP(optimize_stack); + copyAndFreeOptimize(&optimize, optimizep); + + sdccerrgp = STACK_POP(SDCCERRG_stack); + copyAndFreeSDCCERRG(&_SDCCERRG, sdccerrgp); + } + break; + + case P_INDUCTION: + case P_NOINDUCTION: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.loopInduction = (id == P_INDUCTION) ? 1 : 0; + break; + + case P_NOINVARIANT: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.loopInvariant = 0; + break; + + case P_STACKAUTO: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.stackAuto = 1; + break; + + case P_NOGCSE: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.global_cse = 0; + break; + + case P_OVERLAY_: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + break; /* notyet */ + + case P_NOOVERLAY: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.noOverlay = 1; + break; + + case P_LESSPEDANTIC: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.lessPedantic = 1; + setErrorLogLevel(ERROR_LEVEL_WARNING); + break; + + case P_CALLEE_SAVES: + /* append to the functions already listed + in callee-saves */ + setParseWithComma(&options.calleeSavesSet, cp); + err = -1; + break; + + case P_EXCLUDE: + { + deleteSet(&options.excludeRegsSet); + setParseWithComma(&options.excludeRegsSet, cp); + err = -1; + } + break; + + case P_NOIV: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.noiv = 1; + break; + + case P_LOOPREV: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.noLoopReverse = 1; + break; + + case P_DISABLEWARN: + { + int warn; + + cp = get_pragma_token(cp, &token); + if (TOKEN_INT != token.type) + { + err = 1; + break; + } + warn = token.val.int_val; + + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + setWarningDisabled(warn); + } + break; + + case P_OPTCODESPEED: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.codeSpeed = 1; + optimize.codeSize = 0; + break; + + case P_OPTCODESIZE: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.codeSpeed = 0; + optimize.codeSize = 1; + break; + + case P_OPTCODEBALANCED: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + optimize.codeSpeed = 0; + optimize.codeSize = 0; + break; + + case P_STD_C89: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + break; + + case P_STD_C99: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + break; + + case P_STD_C11: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 0; + options.std_sdcc = 0; + break; + + case P_STD_C2X: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 1; + options.std_sdcc = 0; + break; + + case P_STD_SDCC89: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 1; + break; + + case P_STD_SDCC99: + cp = get_pragma_token(cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 1; + break; + + case P_CODESEG: + case P_CONSTSEG: + { + struct dbuf_s segname; + + cp = get_pragma_token(cp, &token); + if (token.type == TOKEN_EOL) + { + err = 1; + break; + } + + dbuf_init(&segname, 16); + dbuf_printf(&segname, "%-8s(CODE)", get_pragma_string(&token)); + + cp = get_pragma_token(cp, &token); + if (token.type != TOKEN_EOL) + { + dbuf_destroy(&segname); + err = 1; + break; + } + + if (id == P_CODESEG) + options.code_seg = dbuf_detach(&segname); + else + options.const_seg = dbuf_detach(&segname); + } + break; + + default: + processed = 0; + break; + } + + get_pragma_token(cp, &token); + + if (1 == err || (0 == err && token.type != TOKEN_EOL)) + werror(W_BAD_PRAGMA_ARGUMENTS, name); + + free_pragma_token(&token); + return processed; +} + +static struct pragma_s pragma_tbl[] = { + { "save", P_SAVE, 0, doPragma }, + { "restore", P_RESTORE, 0, doPragma }, + { "induction", P_INDUCTION, 0, doPragma }, + { "noinduction", P_NOINDUCTION, 0, doPragma }, + { "noinvariant", P_NOINVARIANT, 0, doPragma }, + { "noloopreverse", P_LOOPREV, 0, doPragma }, + { "stackauto", P_STACKAUTO, 0, doPragma }, + { "nogcse", P_NOGCSE, 0, doPragma }, + { "overlay", P_OVERLAY_, 0, doPragma }, + { "nooverlay", P_NOOVERLAY, 0, doPragma }, + { "callee_saves", P_CALLEE_SAVES, 0, doPragma }, + { "exclude", P_EXCLUDE, 0, doPragma }, + { "noiv", P_NOIV, 0, doPragma }, + { "less_pedantic", P_LESSPEDANTIC, 0, doPragma }, + { "disable_warning", P_DISABLEWARN, 0, doPragma }, + { "opt_code_speed", P_OPTCODESPEED, 0, doPragma }, + { "opt_code_size", P_OPTCODESIZE, 0, doPragma }, + { "opt_code_balanced", P_OPTCODEBALANCED, 0, doPragma }, + { "std_c89", P_STD_C89, 0, doPragma }, + { "std_c99", P_STD_C99, 0, doPragma }, + { "std_c11", P_STD_C11, 0, doPragma }, + { "std_c2x", P_STD_C2X, 0, doPragma }, + { "std_sdcc89", P_STD_SDCC89, 0, doPragma }, + { "std_sdcc99", P_STD_SDCC99, 0, doPragma }, + { "codeseg", P_CODESEG, 0, doPragma }, + { "constseg", P_CONSTSEG, 0, doPragma }, + { NULL, 0, 0, NULL }, +}; + +/* + * returns 1 if the pragma was processed, 0 if not + */ +int +process_pragma_tbl (const struct pragma_s *pragma_tbl, const char *s) +{ + struct pragma_token_s token; + int i; + int ret = 0; + + init_pragma_token(&token); + + s = get_pragma_token(s, &token); + + /* skip separating whitespace */ + while ('\n' != *s && isspace((unsigned char)*s)) + s++; + + for (i = 0; NULL != pragma_tbl[i].name; ++i) + { + /* now compare and do what needs to be done */ + if (strcmp(get_pragma_string(&token), pragma_tbl[i].name) == 0) + { + if (pragma_tbl[i].deprecated != 0) + werror(W_DEPRECATED_PRAGMA, pragma_tbl[i].name); + + ret = (*pragma_tbl[i].func)(pragma_tbl[i].id, pragma_tbl[i].name, s); + break; + } + } + + free_pragma_token(&token); + return ret; +} + +static int +process_pragma (const char *s) +{ + struct pragma_token_s token; + + init_pragma_token(&token); + + s = get_pragma_token(s, &token); + if (0 != strcmp("#pragma", get_pragma_string(&token))) + { + /* Oops, something went totally wrong - internal error */ + wassertl(0, "pragma parser internal error"); + } + + /* skip spaces */ + while ('\n' != *s && isspace((unsigned char)*s)) + ++s; + + /* First give the port a chance */ + if (port->process_pragma && port->process_pragma(s)) + return 1; + + if (process_pragma_tbl(pragma_tbl, s)) + { + return 1; + } + else + { + werror(W_UNKNOWN_PRAGMA, s); + return 0; + } +} + +int +yywrap (void) +{ + if (!STACK_EMPTY(options_stack) || !STACK_EMPTY(optimize_stack)) + werror(W_SAVE_RESTORE); + + return 1; +} + +int +yyerror (char *s) +{ + fflush(stdout); + + werror (S_SYNTAX_ERROR, yytext, column); + + return 0; +} diff --git a/src/SDCC.y b/src/SDCC.y new file mode 100644 index 0000000..ff79f69 --- /dev/null +++ b/src/SDCC.y @@ -0,0 +1,2465 @@ +/*----------------------------------------------------------------------- + + SDCC.y - parser definition file for sdcc : + Written By : Sandeep Dutta . sandeep.dutta@usa.net (1997) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +%{ +#include +#include +#include +#include "SDCCglobl.h" +#include "SDCCsymt.h" +#include "SDCChasht.h" +#include "SDCCval.h" +#include "SDCCmem.h" +#include "SDCCast.h" +#include "port.h" +#include "newalloc.h" +#include "SDCCerr.h" +#include "SDCCutil.h" +#include "SDCCbtree.h" +#include "SDCCopt.h" + +extern int yyerror (char *); +extern FILE *yyin; +long NestLevel = 0; /* current NestLevel */ +int stackPtr = 1; /* stack pointer */ +int xstackPtr = 0; /* xstack pointer */ +int reentrant = 0; +int blockNo = 0; /* sequential block number */ +int currBlockno=0; +int inCriticalFunction = 0; +int inCriticalBlock = 0; +int seqPointNo= 1; /* sequence point number */ +int ignoreTypedefType=0; +extern int yylex(); +int yyparse(void); +extern int noLineno; +char lbuff[1024]; /* local buffer */ +char function_name[256] = {0}; + +/* break & continue stacks */ +STACK_DCL(continueStack ,symbol *,MAX_NEST_LEVEL) +STACK_DCL(breakStack ,symbol *,MAX_NEST_LEVEL) +STACK_DCL(forStack ,symbol *,MAX_NEST_LEVEL) +STACK_DCL(swStk ,ast *,MAX_NEST_LEVEL) +STACK_DCL(blockNum,int,MAX_NEST_LEVEL*3) + +value *cenum = NULL; /* current enumeration type chain*/ +bool uselessDecl = TRUE; + +#define YYDEBUG 1 + +%} +%expect 6 + +%union { + symbol *sym; /* symbol table pointer */ + structdef *sdef; /* structure definition */ + char yychar[SDCC_NAME_MAX+1]; + sym_link *lnk; /* declarator or specifier */ + int yyint; /* integer value returned */ + value *val; /* for integer constant */ + initList *ilist; /* initial list */ + designation*dsgn; /* designator */ + const char *yystr; /* pointer to dynamicaly allocated string */ + ast *asts; /* expression tree */ +} + +%token IDENTIFIER TYPE_NAME ADDRSPACE_NAME +%token CONSTANT +%token SIZEOF ALIGNOF TYPEOF OFFSETOF +%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP +%token MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN +%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN +%token XOR_ASSIGN OR_ASSIGN +%token TYPEDEF EXTERN STATIC AUTO REGISTER CODE EEPROM INTERRUPT SFR SFR16 SFR32 ADDRESSMOD STATIC_ASSERT +%token AT SBIT REENTRANT USING XDATA DATA IDATA PDATA VAR_ARGS CRITICAL +%token NONBANKED BANKED SHADOWREGS SD_WPARAM +%token SD_BOOL SD_CHAR SD_SHORT SD_INT SD_LONG SIGNED UNSIGNED SD_FLOAT DOUBLE FIXED16X16 SD_CONST VOLATILE SD_VOID BIT +%token STRUCT UNION ENUM RANGE SD_FAR +%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN +%token NAKED JAVANATIVE OVERLAY TRAP +%token STRING_LITERAL INLINEASM +%token IFX ADDRESS_OF GET_VALUE_AT_ADDRESS SET_VALUE_AT_ADDRESS SPIL UNSPIL GETHBIT GETABIT GETBYTE GETWORD +%token BITWISEAND UNARYMINUS IPUSH IPOP PCALL ENDFUNCTION JUMPTABLE +%token RRC RLC +%token CAST CALL PARAM NULLOP BLOCK LABEL RECEIVE SEND ARRAYINIT +%token DUMMY_READ_VOLATILE ENDCRITICAL SWAP INLINE NORETURN RESTRICT SMALLC PRESERVES_REGS Z88DK_FASTCALL Z88DK_CALLEE ALIGNAS Z88DK_SHORTCALL Z88DK_PARAMS_OFFSET +%token GENERIC GENERIC_ASSOC_LIST GENERIC_ASSOCIATION +%token ASM + +%type Interrupt_storage +%type identifier declarator declarator2 declarator3 enumerator_list enumerator +%type struct_declarator function_declarator function_declarator2 +%type struct_declarator_list struct_declaration struct_declaration_list +%type declaration init_declarator_list init_declarator +%type declaration_list identifier_list +%type declaration_after_statement +%type declarator2_function_attributes while do for critical +%type addressmod +%type pointer type_specifier_list type_specifier_list_ type_specifier type_qualifier_list type_qualifier type_name +%type storage_class_specifier struct_or_union_specifier function_specifier alignment_specifier +%type declaration_specifiers declaration_specifiers_ sfr_reg_bit sfr_attributes +%type function_attribute function_attributes enum_specifier +%type abstract_declarator abstract_declarator2 unqualified_pointer +%type parameter_type_list parameter_list parameter_declaration opt_assign_expr +%type stag opt_stag +%type primary_expr +%type postfix_expr unary_expr offsetof_member_designator cast_expr multiplicative_expr +%type additive_expr shift_expr relational_expr equality_expr +%type and_expr exclusive_or_expr inclusive_or_expr logical_or_expr +%type logical_and_expr conditional_expr assignment_expr constant_expr +%type expr argument_expr_list function_definition expr_opt +%type statement_list statement labeled_statement compound_statement +%type expression_statement selection_statement iteration_statement +%type jump_statement function_body else_statement string_literal_val +%type critical_statement asm_statement label +%type generic_selection generic_assoc_list generic_association +%type implicit_block statements_and_implicit block_item_list +%type designator designator_list designation designation_opt +%type initializer initializer_list +%type unary_operator assignment_operator struct_or_union +%type asm_string_literal + +%start file + +%% + +file + : /* empty */ + { + werror(W_EMPTY_SOURCE_FILE); + } + | program + ; + +program + : external_definition + | program external_definition + ; + +external_definition + : function_definition + { + // blockNo = 0; + } + | declaration + { + ignoreTypedefType = 0; + if ($1 && $1->type && IS_FUNC($1->type)) + { + /* The only legal storage classes for + * a function prototype (declaration) + * are extern and static. extern is the + * default. Thus, if this function isn't + * explicitly marked static, mark it + * extern. + */ + if ($1->etype && IS_SPEC($1->etype) && !SPEC_STAT($1->etype)) + { + SPEC_EXTR($1->etype) = 1; + } + } + addSymChain (&$1); + allocVariables ($1); + cleanUpLevel (SymbolTab, 1); + } + | addressmod + { + /* These empty braces here are apparently required by some version of GNU bison on MS Windows. See bug #2858. */ + } + ; + +function_definition + : function_declarator + { /* function type not specified */ + /* assume it to be 'int' */ + addDecl($1,0,newIntLink()); + $1 = createFunctionDecl($1); + if ($1 && FUNC_ISCRITICAL ($1->type)) + inCriticalFunction = 1; + } + function_body + { + $$ = createFunction($1,$3); + if ($1 && FUNC_ISCRITICAL ($1->type)) + inCriticalFunction = 0; + } + | declaration_specifiers function_declarator + { + sym_link *p = copyLinkChain($1); + pointerTypes($2->type,p); + addDecl($2,0,p); + $2 = createFunctionDecl($2); + if ($2 && FUNC_ISCRITICAL ($2->type)) + inCriticalFunction = 1; + /* warn for loss of calling convention for inlined functions. */ + if ($2 && FUNC_ISINLINE ($2->type) && + ( FUNC_ISZ88DK_CALLEE ($2->type) || FUNC_ISZ88DK_FASTCALL ($2->type) || + FUNC_BANKED ($2->type) || FUNC_REGBANK ($2->type) || + FUNC_ISOVERLAY ($2->type) || FUNC_ISISR ($2->type) )) + { + werror (W_INLINE_FUNCATTR, $2->name); + } + } + function_body + { + $$ = createFunction($2,$4); + if ($2 && FUNC_ISCRITICAL ($2->type)) + inCriticalFunction = 0; + } + ; + +function_attribute + : function_attributes + | function_attributes function_attribute { $$ = mergeSpec($1,$2,"function_attribute"); } + ; + +function_attributes + : USING constant_expr { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = (int) ulFromVal(constExprValue($2,TRUE)); + } + | REENTRANT { $$ = newLink (SPECIFIER); + FUNC_ISREENT($$)=1; + } + | CRITICAL { $$ = newLink (SPECIFIER); + FUNC_ISCRITICAL($$) = 1; + } + | NAKED { $$ = newLink (SPECIFIER); + FUNC_ISNAKED($$)=1; + } + | JAVANATIVE { $$ = newLink (SPECIFIER); + FUNC_ISJAVANATIVE($$)=1; + } + | OVERLAY { $$ = newLink (SPECIFIER); + FUNC_ISOVERLAY($$)=1; + } + | NONBANKED {$$ = newLink (SPECIFIER); + FUNC_NONBANKED($$) = 1; + if (FUNC_BANKED($$)) { + werror(W_BANKED_WITH_NONBANKED); + } + } + | SHADOWREGS {$$ = newLink (SPECIFIER); + FUNC_ISSHADOWREGS($$) = 1; + } + | SD_WPARAM {$$ = newLink (SPECIFIER); + FUNC_ISWPARAM($$) = 1; + } + | BANKED {$$ = newLink (SPECIFIER); + FUNC_BANKED($$) = 1; + if (FUNC_NONBANKED($$)) { + werror(W_BANKED_WITH_NONBANKED); + } + } + | Interrupt_storage + { + $$ = newLink (SPECIFIER); + FUNC_INTNO($$) = $1; + FUNC_ISISR($$) = 1; + } + | TRAP + { + $$ = newLink (SPECIFIER); + FUNC_INTNO($$) = INTNO_TRAP; + FUNC_ISISR($$) = 1; + } + | SMALLC { $$ = newLink (SPECIFIER); + FUNC_ISSMALLC($$) = 1; + } + | Z88DK_FASTCALL { $$ = newLink (SPECIFIER); + FUNC_ISZ88DK_FASTCALL($$) = 1; + } + | Z88DK_CALLEE { $$ = newLink (SPECIFIER); + FUNC_ISZ88DK_CALLEE($$) = 1; + } + | Z88DK_PARAMS_OFFSET '(' constant_expr ')' + { + value *offset_v = constExprValue ($3, TRUE); + int offset = 0; + $$ = newLink(SPECIFIER); + if ( offset_v ) + offset = ulFromVal(offset_v); + $$->funcAttrs.z88dk_params_offset = offset; + } + | Z88DK_SHORTCALL '(' constant_expr ',' constant_expr ')' + { + value *rst_v = constExprValue ($3, TRUE); + value *value_v = constExprValue ($5, TRUE); + int rst = -1, value = -1; + $$ = newLink(SPECIFIER); + + if ( rst_v ) + rst = ulFromVal(rst_v); + if ( value_v ) + value = ulFromVal(value_v); + + if ( rst < 0 || rst > 56 || ( rst % 8 ) ) + { + werror(E_SHORTCALL_INVALID_VALUE, "rst", rst); + } + if ( value < 0 || value > 0xfff ) + { + werror(E_SHORTCALL_INVALID_VALUE, "value", value); + } + $$->funcAttrs.z88dk_shortcall_rst = rst; + $$->funcAttrs.z88dk_shortcall_val = value; + FUNC_ISZ88DK_SHORTCALL($$) = 1; + } + | PRESERVES_REGS '(' identifier_list ')' + { + const struct symbol *regsym; + $$ = newLink (SPECIFIER); + + for(regsym = $3; regsym; regsym = regsym->next) + { + int regnum; + + if (!port->getRegByName || ((regnum = port->getRegByName(regsym->name)) < 0)) + werror (W_UNKNOWN_REG, regsym->name); + else + $$->funcAttrs.preserved_regs[regnum] = TRUE; + } + } + ; + +function_body + : compound_statement + | declaration_list compound_statement + { + werror (E_OLD_STYLE, ($1 ? $1->name: "")); + exit (1); + } + ; + +offsetof_member_designator + : identifier { $$ = newAst_VALUE (symbolVal ($1)); } + | offsetof_member_designator '.' { ignoreTypedefType = 1; } identifier + { + ignoreTypedefType = 0; + $4 = newSymbol ($4->name, NestLevel); + $4->implicit = 1; + $$ = newNode ('.', $1, newAst_VALUE (symbolVal ($4))); + } + | offsetof_member_designator '[' expr ']' + { + $$ = newNode ('[', $1, $3); + } + ; + +primary_expr + : identifier { $$ = newAst_VALUE (symbolVal ($1)); } + | CONSTANT { $$ = newAst_VALUE ($1); } + | string_literal_val + | '(' expr ')' { $$ = $2; } + | generic_selection + ; + +generic_selection + : GENERIC '(' assignment_expr ',' generic_assoc_list ')' { $$ = newNode (GENERIC, $3, $5); } + ; + +generic_assoc_list + : generic_association { $$ = newNode (GENERIC_ASSOC_LIST, NULL, $1); } + | generic_assoc_list ',' generic_association { $$ = newNode (GENERIC_ASSOC_LIST, $1, $3); } + ; + +generic_association + : type_name ':' assignment_expr { $$ = newNode (GENERIC_ASSOCIATION, newAst_LINK($1), $3); } + | DEFAULT ':' assignment_expr { $$ = newNode (GENERIC_ASSOCIATION,NULL,$3); } + ; + +string_literal_val + : STRING_LITERAL { + int cnt = 1; + int max = 253, min = 1; + char fb[256]; + /* refer to support/cpp/libcpp/macro.c for details */ + while ((((int) ($1[cnt] & 0xff)) & 0xff) == 0xff) + cnt++; + + if (cnt <= max) + { + $$ = newAst_VALUE (strVal ($1)); + } + else + { + memset (fb, 0x00, sizeof (fb)); + fb[0] = '"'; + strncpy (fb + 1, function_name, max - min + 1); + fb[max + 1] = '"'; + fb[max + 2] = 0; + fb[strlen (fb)] = '"'; + fb[strlen (fb) + 1] = 0; + $$ = newAst_VALUE (strVal (fb)); + } + } + ; + +postfix_expr + : primary_expr + | postfix_expr '[' expr ']' { $$ = newNode ('[', $1, $3); } + | postfix_expr '(' ')' { $$ = newNode (CALL,$1,NULL); + $$->left->funcName = 1;} + | postfix_expr '(' argument_expr_list ')' + { + $$ = newNode (CALL,$1,$3); $$->left->funcName = 1; + } + | postfix_expr '.' { ignoreTypedefType = 1; } identifier + { + ignoreTypedefType = 0; + $4 = newSymbol($4->name,NestLevel); + $4->implicit = 1; + $$ = newNode(PTR_OP,newNode('&',$1,NULL),newAst_VALUE(symbolVal($4))); + } + | postfix_expr PTR_OP { ignoreTypedefType = 1; } identifier + { + ignoreTypedefType = 0; + $4 = newSymbol($4->name,NestLevel); + $4->implicit = 1; + $$ = newNode(PTR_OP,$1,newAst_VALUE(symbolVal($4))); + } + | postfix_expr INC_OP + { $$ = newNode(INC_OP,$1,NULL);} + | postfix_expr DEC_OP + { $$ = newNode(DEC_OP,$1,NULL); } + ; + +argument_expr_list + : assignment_expr + | assignment_expr ',' argument_expr_list { $$ = newNode(PARAM,$1,$3); } + ; + +unary_expr + : postfix_expr + | INC_OP unary_expr { $$ = newNode (INC_OP, NULL, $2); } + | DEC_OP unary_expr { $$ = newNode (DEC_OP, NULL, $2); } + | unary_operator cast_expr + { + if ($1 == '&' && IS_AST_OP ($2) && $2->opval.op == '*' && $2->right == NULL) + $$ = $2->left; + else if ($1 == '*' && IS_AST_OP ($2) && $2->opval.op == '&' && $2->right == NULL) + $$ = $2->left; + else + $$ = newNode ($1, $2, NULL); + } + | SIZEOF unary_expr { $$ = newNode (SIZEOF, NULL, $2); } + | SIZEOF '(' type_name ')' { $$ = newAst_VALUE (sizeofOp ($3)); } + | ALIGNOF '(' type_name ')'{ $$ = newAst_VALUE (alignofOp ($3)); } + | TYPEOF unary_expr { $$ = newNode (TYPEOF, NULL, $2); } + | OFFSETOF '(' type_name ',' offsetof_member_designator ')' { $$ = offsetofOp($3, $5); } + ; + +unary_operator + : '&' { $$ = '&';} + | '*' { $$ = '*';} + | '+' { $$ = '+';} + | '-' { $$ = '-';} + | '~' { $$ = '~';} + | '!' { $$ = '!';} + ; + +cast_expr + : unary_expr + | '(' type_name ')' cast_expr { $$ = newNode(CAST,newAst_LINK($2),$4); } + ; + +multiplicative_expr + : cast_expr + | multiplicative_expr '*' cast_expr { $$ = newNode('*',$1,$3);} + | multiplicative_expr '/' cast_expr { $$ = newNode('/',$1,$3);} + | multiplicative_expr '%' cast_expr { $$ = newNode('%',$1,$3);} + ; + +additive_expr + : multiplicative_expr + | additive_expr '+' multiplicative_expr { $$=newNode('+',$1,$3);} + | additive_expr '-' multiplicative_expr { $$=newNode('-',$1,$3);} + ; + +shift_expr + : additive_expr + | shift_expr LEFT_OP additive_expr { $$ = newNode(LEFT_OP,$1,$3); } + | shift_expr RIGHT_OP additive_expr { $$ = newNode(RIGHT_OP,$1,$3); } + ; + +relational_expr + : shift_expr + | relational_expr '<' shift_expr { $$ = newNode('<', $1,$3);} + | relational_expr '>' shift_expr { $$ = newNode('>', $1,$3);} + | relational_expr LE_OP shift_expr { $$ = newNode(LE_OP,$1,$3);} + | relational_expr GE_OP shift_expr { $$ = newNode(GE_OP,$1,$3);} + ; + +equality_expr + : relational_expr + | equality_expr EQ_OP relational_expr { $$ = newNode(EQ_OP,$1,$3);} + | equality_expr NE_OP relational_expr { $$ = newNode(NE_OP,$1,$3);} + ; + +and_expr + : equality_expr + | and_expr '&' equality_expr { $$ = newNode('&',$1,$3);} + ; + +exclusive_or_expr + : and_expr + | exclusive_or_expr '^' and_expr { $$ = newNode('^',$1,$3);} + ; + +inclusive_or_expr + : exclusive_or_expr + | inclusive_or_expr '|' exclusive_or_expr { $$ = newNode('|',$1,$3);} + ; + +logical_and_expr + : inclusive_or_expr + | logical_and_expr AND_OP { seqPointNo++;} inclusive_or_expr + { $$ = newNode(AND_OP,$1,$4);} + ; + +logical_or_expr + : logical_and_expr + | logical_or_expr OR_OP { seqPointNo++;} logical_and_expr + { $$ = newNode(OR_OP,$1,$4); } + ; + +conditional_expr + : logical_or_expr + | logical_or_expr '?' { seqPointNo++;} expr ':' conditional_expr + { + $$ = newNode(':',$4,$6); + $$ = newNode('?',$1,$$); + } + ; + +assignment_expr + : conditional_expr + | cast_expr assignment_operator assignment_expr + { + + switch ($2) { + case '=': + $$ = newNode($2,$1,$3); + break; + case MUL_ASSIGN: + $$ = createRMW($1, '*', $3); + break; + case DIV_ASSIGN: + $$ = createRMW($1, '/', $3); + break; + case MOD_ASSIGN: + $$ = createRMW($1, '%', $3); + break; + case ADD_ASSIGN: + $$ = createRMW($1, '+', $3); + break; + case SUB_ASSIGN: + $$ = createRMW($1, '-', $3); + break; + case LEFT_ASSIGN: + $$ = createRMW($1, LEFT_OP, $3); + break; + case RIGHT_ASSIGN: + $$ = createRMW($1, RIGHT_OP, $3); + break; + case AND_ASSIGN: + $$ = createRMW($1, '&', $3); + break; + case XOR_ASSIGN: + $$ = createRMW($1, '^', $3); + break; + case OR_ASSIGN: + $$ = createRMW($1, '|', $3); + break; + default : + $$ = NULL; + } + + } +; + +assignment_operator + : '=' { $$ = '=';} + | MUL_ASSIGN + | DIV_ASSIGN + | MOD_ASSIGN + | ADD_ASSIGN + | SUB_ASSIGN + | LEFT_ASSIGN + | RIGHT_ASSIGN + | AND_ASSIGN + | XOR_ASSIGN + | OR_ASSIGN + ; + +expr + : assignment_expr + | expr ',' { seqPointNo++;} assignment_expr { $$ = newNode(',',$1,$4);} + ; + +constant_expr + : conditional_expr + ; + +declaration + : declaration_specifiers ';' + { + /* Special case: if incomplete struct/union declared without name, */ + /* make sure an incomplete type for it exists in the current scope */ + if (IS_STRUCT($1)) + { + structdef *sdef = SPEC_STRUCT($1); + structdef *osdef; + osdef = findSymWithBlock (StructTab, sdef->tagsym, currBlockno, NestLevel); + if (osdef && osdef->block != currBlockno) + { + sdef = newStruct(osdef->tagsym->name); + sdef->level = NestLevel; + sdef->block = currBlockno; + sdef->tagsym = newSymbol (osdef->tagsym->name, NestLevel); + addSym (StructTab, sdef, sdef->tag, sdef->level, currBlockno, 0); + uselessDecl = FALSE; + } + } + if (uselessDecl) + werror(W_USELESS_DECL); + uselessDecl = TRUE; + $$ = NULL; + } + | declaration_specifiers init_declarator_list ';' + { + /* add the specifier list to the id */ + symbol *sym , *sym1; + + for (sym1 = sym = reverseSyms($2);sym != NULL;sym = sym->next) { + sym_link *lnk = copyLinkChain($1); + sym_link *l0 = NULL, *l1 = NULL, *l2 = NULL; + /* check illegal declaration */ + for (l0 = sym->type; l0 != NULL; l0 = l0->next) + if (IS_PTR (l0)) + break; + /* check if creating intances of structs with flexible arrays */ + for (l1 = lnk; l1 != NULL; l1 = l1->next) + if (IS_STRUCT (l1) && SPEC_STRUCT (l1)->b_flexArrayMember) + break; + if (!options.std_c99 && l0 == NULL && l1 != NULL && SPEC_EXTR($1) != 1) + werror (W_FLEXARRAY_INSTRUCT, sym->name); + /* check if creating intances of function type */ + for (l1 = lnk; l1 != NULL; l1 = l1->next) + if (IS_FUNC (l1)) + break; + for (l2 = lnk; l2 != NULL; l2 = l2->next) + if (IS_PTR (l2)) + break; + if (l0 == NULL && l2 == NULL && l1 != NULL) + werrorfl(sym->fileDef, sym->lineDef, E_TYPE_IS_FUNCTION, sym->name); + /* do the pointer stuff */ + pointerTypes(sym->type,lnk); + addDecl (sym,0,lnk); + } + + uselessDecl = TRUE; + $$ = sym1; + } + | static_assert_declaration ';' + { + $$ = NULL; + } + ; + +declaration_specifiers : declaration_specifiers_ { $$ = finalizeSpec($1); }; + +declaration_specifiers_ + : storage_class_specifier { $$ = $1; } + | storage_class_specifier declaration_specifiers_ { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "storage_class_specifier declaration_specifiers - skipped"); + } + | type_specifier { $$ = $1; } + | type_specifier declaration_specifiers_ { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "type_specifier declaration_specifiers - skipped"); + } + | function_specifier { $$ = $1; } + | function_specifier declaration_specifiers_ { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "function_specifier declaration_specifiers - skipped"); + } + | alignment_specifier { $$ = $1; } + | alignment_specifier declaration_specifiers_ { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "alignment_specifier declaration_specifiers - skipped"); + } + ; + +init_declarator_list + : init_declarator + | init_declarator_list ',' init_declarator { $3->next = $1; $$ = $3;} + ; + +init_declarator + : declarator { $1->ival = NULL; } + | declarator '=' initializer { $1->ival = $3; seqPointNo++; } + ; + +designation_opt + : { $$ = NULL; } + | designation + ; + +designation + : designator_list '=' { $$ = revDesignation($1); } + ; + +designator_list + : designator + | designator_list designator { $2->next = $1; $$ = $2; } + ; + +designator + : '[' constant_expr ']' + { + value *tval; + int elemno; + + tval = constExprValue($2, TRUE); + /* if it is not a constant then Error */ + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror (E_CONST_EXPECTED); + elemno = 0; /* arbitrary fixup */ + } + else + { + if ((elemno = (int) ulFromVal(tval)) < 0) + { + werror (E_BAD_DESIGNATOR); + elemno = 0; /* arbitrary fixup */ + } + } + $$ = newDesignation(DESIGNATOR_ARRAY, &elemno); + } + | '.' identifier { $$ = newDesignation(DESIGNATOR_STRUCT,$2); } + ; + +storage_class_specifier + : TYPEDEF { + $$ = newLink (SPECIFIER); + SPEC_TYPEDEF($$) = 1; + } + | EXTERN { + $$ = newLink(SPECIFIER); + SPEC_EXTR($$) = 1; + } + | STATIC { + $$ = newLink (SPECIFIER); + SPEC_STAT($$) = 1; + } + | AUTO { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_AUTO; + } + | REGISTER { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_REGISTER; + } + ; + +function_specifier + : INLINE { + $$ = newLink (SPECIFIER); + SPEC_INLINE($$) = 1; + } + | NORETURN { + $$ = newLink (SPECIFIER); + SPEC_NORETURN($$) = 1; + } + ; + +alignment_specifier + : ALIGNAS '(' type_name ')' + { + checkTypeSanity ($3, "(_Alignas)"); + $$ = newLink (SPECIFIER); + SPEC_ALIGNAS($$) = 1; + } + | ALIGNAS '(' constant_expr ')' + { + value *val = constExprValue ($3, TRUE); + $$ = newLink (SPECIFIER); + SPEC_ALIGNAS($$) = 0; + if (!val) + werror (E_CONST_EXPECTED); + else if (ulFromVal (val) == 0 || isPowerOf2 (ulFromVal (val)) && ulFromVal (val) <= port->mem.maxextalign) + SPEC_ALIGNAS($$) = ulFromVal(val); + else + werror (E_ALIGNAS, ulFromVal(val)); + } + ; + +Interrupt_storage + : INTERRUPT { $$ = INTNO_UNSPEC; } + | INTERRUPT constant_expr + { + value *val = constExprValue($2,TRUE); + int intno = (int) ulFromVal(val); + if (val && (intno >= 0) && (intno <= INTNO_MAX)) + $$ = intno; + else + { + werror(E_INT_BAD_INTNO, intno); + $$ = INTNO_UNSPEC; + } + } + ; + +type_qualifier + : SD_CONST { + $$=newLink(SPECIFIER); + SPEC_CONST($$) = 1; + } + | RESTRICT { + $$=newLink(SPECIFIER); + SPEC_RESTRICT($$) = 1; + } + | VOLATILE { + $$=newLink(SPECIFIER); + SPEC_VOLATILE($$) = 1; + } + | ADDRSPACE_NAME { + $$=newLink(SPECIFIER); + SPEC_ADDRSPACE($$) = findSym (AddrspaceTab, 0, $1); + } + | XDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_XDATA; + } + | CODE { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_CODE; + } + | EEPROM { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_EEPROM; + } + | DATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_DATA; + } + | IDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_IDATA; + } + | PDATA { + $$ = newLink (SPECIFIER); + SPEC_SCLS($$) = S_PDATA; + } + ; + +type_qualifier_list + : type_qualifier + | type_qualifier_list type_qualifier + { + $$ = mergeDeclSpec($1, $2, "type_qualifier_list type_qualifier skipped"); + } + ; + +type_specifier + : type_qualifier { $$ = $1; } + | SD_BOOL { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_BOOL; + ignoreTypedefType = 1; + } + | SD_CHAR { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_CHAR; + ignoreTypedefType = 1; + } + | SD_SHORT { + $$=newLink(SPECIFIER); + SPEC_SHORT($$) = 1; + ignoreTypedefType = 1; + } + | SD_INT { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_INT; + ignoreTypedefType = 1; + } + | SD_LONG { + $$=newLink(SPECIFIER); + SPEC_LONG($$) = 1; + ignoreTypedefType = 1; + } + | SIGNED { + $$=newLink(SPECIFIER); + $$->select.s.b_signed = 1; + ignoreTypedefType = 1; + } + | UNSIGNED { + $$=newLink(SPECIFIER); + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + | SD_VOID { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_VOID; + ignoreTypedefType = 1; + } + | SD_FLOAT { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_FLOAT; + ignoreTypedefType = 1; + } + | FIXED16X16 { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_FIXED16X16; + ignoreTypedefType = 1; + } + | BIT { + $$=newLink(SPECIFIER); + SPEC_NOUN($$) = V_BIT; + SPEC_SCLS($$) = S_BIT; + SPEC_BLEN($$) = 1; + SPEC_BSTR($$) = 0; + ignoreTypedefType = 1; + } + | AT constant_expr { + $$=newLink(SPECIFIER); + /* add this to the storage class specifier */ + SPEC_ABSA($$) = 1; /* set the absolute addr flag */ + /* now get the abs addr from value */ + SPEC_ADDR($$) = (unsigned int) ulFromVal(constExprValue($2,TRUE)); + } + | struct_or_union_specifier { + uselessDecl = FALSE; + $$ = $1; + ignoreTypedefType = 1; + } + | enum_specifier { + cenum = NULL; + uselessDecl = FALSE; + ignoreTypedefType = 1; + $$ = $1; + } + | TYPE_NAME + { + symbol *sym; + sym_link *p; + sym = findSym(TypedefTab,NULL,$1); + $$ = p = copyLinkChain(sym ? sym->type : NULL); + SPEC_TYPEDEF(getSpec(p)) = 0; + ignoreTypedefType = 1; + } + | sfr_reg_bit + ; + +sfr_reg_bit + : SBIT { + $$ = newLink(SPECIFIER); + SPEC_NOUN($$) = V_SBIT; + SPEC_SCLS($$) = S_SBIT; + SPEC_BLEN($$) = 1; + SPEC_BSTR($$) = 0; + ignoreTypedefType = 1; + } + | sfr_attributes + ; + +sfr_attributes + : SFR { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_CHAR; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + | SFR BANKED { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 1; + SPEC_NOUN($$) = V_CHAR; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +sfr_attributes + : SFR16 { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_INT; + SPEC_SCLS($$) = S_SFR; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +sfr_attributes + : SFR32 { + $$ = newLink(SPECIFIER); + FUNC_REGBANK($$) = 0; + SPEC_NOUN($$) = V_INT; + SPEC_SCLS($$) = S_SFR; + SPEC_LONG($$) = 1; + SPEC_USIGN($$) = 1; + ignoreTypedefType = 1; + } + ; + +struct_or_union_specifier + : struct_or_union opt_stag + { + structdef *sdef; + + if (! $2->tagsym) + { + /* no tag given, so new struct def for current scope */ + addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + } + else + { + sdef = findSymWithBlock (StructTab, $2->tagsym, currBlockno, NestLevel); + if (sdef) + { + /* Error if a complete type already defined in this scope */ + if (sdef->block == currBlockno) + { + if (sdef->fields) + { + werror(E_STRUCT_REDEF, $2->tag); + werrorfl(sdef->tagsym->fileDef, sdef->tagsym->lineDef, E_PREVIOUS_DEF); + } + else + { + $2 = sdef; /* We are completing an incomplete type */ + } + } + else + { + /* There is an existing struct def in an outer scope. */ + /* Create new struct def for current scope */ + addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + } + } + else + { + /* There is no existing struct def at all. */ + /* Create new struct def for current scope */ + addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + } + } + + if (!$2->type) + { + $2->type = $1; + } + else + { + if ($2->type != $1) + werror(E_BAD_TAG, $2->tag, $1==STRUCT ? "struct" : "union"); + } + } + '{' struct_declaration_list '}' + { + structdef *sdef; + symbol *sym, *dsym; + + // check for errors in structure members + for (sym=$5; sym; sym=sym->next) + { + if (IS_ABSOLUTE(sym->etype)) + { + werrorfl(sym->fileDef, sym->lineDef, E_NOT_ALLOWED, "'at'"); + SPEC_ABSA(sym->etype) = 0; + } + if (IS_SPEC(sym->etype) && SPEC_SCLS(sym->etype)) + { + werrorfl(sym->fileDef, sym->lineDef, E_NOT_ALLOWED, "storage class"); + printTypeChainRaw (sym->type, NULL); + SPEC_SCLS(sym->etype) = 0; + } + for (dsym=sym->next; dsym; dsym=dsym->next) + { + if (*dsym->name && strcmp(sym->name, dsym->name)==0) + { + werrorfl(sym->fileDef, sym->lineDef, E_DUPLICATE_MEMBER, + $1==STRUCT ? "struct" : "union", sym->name); + werrorfl(dsym->fileDef, dsym->lineDef, E_PREVIOUS_DEF); + } + } + } + + /* Create a structdef */ + sdef = $2; + sdef->fields = reverseSyms($5); /* link the fields */ + sdef->size = compStructSize($1, sdef); /* update size of */ + promoteAnonStructs ($1, sdef); + + /* Create the specifier */ + $$ = newLink (SPECIFIER); + SPEC_NOUN($$) = V_STRUCT; + SPEC_STRUCT($$)= sdef; + } + | struct_or_union stag + { + structdef *sdef; + + sdef = findSymWithBlock (StructTab, $2->tagsym, currBlockno, NestLevel); + + if (sdef) + $2 = sdef; + else + { + /* new struct def for current scope */ + addSym (StructTab, $2, $2->tag, $2->level, currBlockno, 0); + } + $$ = newLink(SPECIFIER); + SPEC_NOUN($$) = V_STRUCT; + SPEC_STRUCT($$) = $2; + + if (!$2->type) + { + $2->type = $1; + } + else + { + if ($2->type != $1) + werror(E_BAD_TAG, $2->tag, $1==STRUCT ? "struct" : "union"); + } + } + ; + +struct_or_union + : STRUCT { $$ = STRUCT; ignoreTypedefType = 1; } + | UNION { $$ = UNION; ignoreTypedefType = 1; } + ; + +opt_stag + : stag + | { /* synthesize a name add to structtable */ + ignoreTypedefType = 0; + $$ = newStruct(genSymName(NestLevel)); + $$->level = NestLevel; + $$->block = currBlockno; + $$->tagsym = NULL; + //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); + } + ; + +stag + : identifier + { /* add name to structure table */ + ignoreTypedefType = 0; + $$ = newStruct($1->name); + $$->level = NestLevel; + $$->block = currBlockno; + $$->tagsym = $1; + //$$ = findSymWithBlock (StructTab, $1, currBlockno); + //if (! $$ ) + // { + // $$ = newStruct($1->name); + // $$->level = NestLevel; + // $$->tagsym = $1; + // //addSym (StructTab, $$, $$->tag, $$->level, currBlockno, 0); + // } + } + ; + +struct_declaration_list + : struct_declaration + | struct_declaration_list struct_declaration + { + symbol *sym = $2; + + /* go to the end of the chain */ + while (sym->next) sym = sym->next; + sym->next = $1; + + $$ = $2; + } + ; + +struct_declaration + : type_specifier_list struct_declarator_list ';' + { + /* add this type to all the symbols */ + symbol *sym; + for ( sym = $2; sym != NULL; sym = sym->next ) + { + sym_link *btype = copyLinkChain($1); + + pointerTypes(sym->type, btype); + if (!sym->type) + { + sym->type = btype; + sym->etype = getSpec(sym->type); + } + else + addDecl (sym, 0, btype); + /* make sure the type is complete and sane */ + checkTypeSanity(sym->etype, sym->name); + } + ignoreTypedefType = 0; + $$ = $2; + } + ; + +struct_declarator_list + : struct_declarator + | struct_declarator_list ',' struct_declarator + { + $3->next = $1; + $$ = $3; + } + ; + +struct_declarator + : declarator + | ':' constant_expr + { + unsigned int bitsize; + $$ = newSymbol (genSymName(NestLevel), NestLevel); + bitsize = (unsigned int) ulFromVal(constExprValue($2, TRUE)); + if (!bitsize) + bitsize = BITVAR_PAD; + $$->bitVar = bitsize; + $$->bitUnnamed = 1; + } + | declarator ':' constant_expr + { + unsigned int bitsize; + bitsize = (unsigned int) ulFromVal(constExprValue($3, TRUE)); + + if (!bitsize) + { + $$ = newSymbol (genSymName(NestLevel), NestLevel); + $$->bitVar = BITVAR_PAD; + werror(W_BITFLD_NAMED); + } + else + $1->bitVar = bitsize; + } + | { $$ = newSymbol ("", NestLevel); } + ; + +enum_specifier + : ENUM '{' enumerator_list '}' + { + $$ = newEnumType ($3); + SPEC_SCLS(getSpec($$)) = 0; + } + | ENUM identifier '{' enumerator_list '}' + { + symbol *csym; + sym_link *enumtype; + + csym = findSymWithLevel(enumTab, $2); + if ((csym && csym->level == $2->level)) + { + werrorfl($2->fileDef, $2->lineDef, E_DUPLICATE_TYPEDEF, csym->name); + werrorfl(csym->fileDef, csym->lineDef, E_PREVIOUS_DEF); + } + + enumtype = newEnumType ($4); + SPEC_SCLS(getSpec(enumtype)) = 0; + $2->type = enumtype; + + /* add this to the enumerator table */ + if (!csym) + addSym (enumTab, $2, $2->name, $2->level, $2->block, 0); + $$ = copyLinkChain(enumtype); + } + | ENUM identifier + { + symbol *csym; + + /* check the enumerator table */ + if ((csym = findSymWithLevel(enumTab, $2))) + $$ = copyLinkChain(csym->type); + else + { + $$ = newLink(SPECIFIER); + SPEC_NOUN($$) = V_INT; + } + } + ; + +enumerator_list + : enumerator + | enumerator_list ',' + | enumerator_list ',' enumerator + { + $3->next = $1; + $$ = $3; + } + ; + +enumerator + : identifier opt_assign_expr + { + symbol *sym; + + // check if the symbol at the same level already exists + if ((sym = findSymWithLevel (SymbolTab, $1)) && sym->level == $1->level) + { + werrorfl ($1->fileDef, $1->lineDef, E_DUPLICATE_MEMBER, "enum", $1->name); + werrorfl (sym->fileDef, sym->lineDef, E_PREVIOUS_DEF); + } + $1->type = copyLinkChain ($2->type); + $1->etype = getSpec ($1->type); + SPEC_ENUM ($1->etype) = 1; + $$ = $1; + // do this now, so we can use it for the next enums in the list + addSymChain (&$1); + } + ; + +opt_assign_expr + : '=' constant_expr + { + value *val; + + val = constExprValue($2, TRUE); + if (!IS_INT(val->type) && !IS_CHAR(val->type) && !IS_BOOL(val->type)) + { + werror(E_ENUM_NON_INTEGER); + SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(val)); + val = constVal(lbuff); + } + $$ = cenum = val; + } + | { + if (cenum) + { + SNPRINTF(lbuff, sizeof(lbuff), "%d", (int) ulFromVal(cenum)+1); + $$ = cenum = constVal(lbuff); + } + else + { + $$ = cenum = constCharVal(0); + } + } + ; + +declarator + : declarator3 { $$ = $1; } + | pointer declarator3 + { + addDecl ($2,0,reverseLink($1)); + $$ = $2; + } + ; + +declarator3 + : declarator2_function_attributes { $$ = $1; } + | declarator2 { $$ = $1; } + ; + +function_declarator + : declarator2_function_attributes + { + $$ = $1; + strncpy (function_name, $$->name, sizeof (function_name) - 4); + memset (function_name + sizeof (function_name) - 4, 0x00, 4); + } + | pointer declarator2_function_attributes + { + addDecl ($2,0,reverseLink($1)); + $$ = $2; + strncpy (function_name, $$->name, sizeof (function_name) - 4); + memset (function_name + sizeof (function_name) - 4, 0x00, 4); + } + ; + +declarator2_function_attributes + : function_declarator2 { $$ = $1; } + | function_declarator2 function_attribute { + // copy the functionAttributes (not the args and hasVargs !!) + struct value *args; + unsigned hasVargs; + sym_link *funcType=$1->type; + + while (funcType && !IS_FUNC(funcType)) + funcType = funcType->next; + + if (!funcType) + werror (E_FUNC_ATTR); + else + { + args=FUNC_ARGS(funcType); + hasVargs=FUNC_HASVARARGS(funcType); + + memcpy (&funcType->funcAttrs, &$2->funcAttrs, + sizeof($2->funcAttrs)); + + FUNC_ARGS(funcType)=args; + FUNC_HASVARARGS(funcType)=hasVargs; + + // just to be sure + memset (&$2->funcAttrs, 0, + sizeof($2->funcAttrs)); + + addDecl ($1,0,$2); + } + } + ; + +declarator2 + : identifier + | '(' declarator ')' { $$ = $2; } + | declarator3 '[' ']' + { + sym_link *p; + + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + DCL_ELEM(p) = 0; + addDecl($1,0,p); + } + | declarator3 '[' type_qualifier_list ']' + { + sym_link *p, *n; + + if (!options.std_c99) + werror (E_QUALIFIED_ARRAY_PARAM_C99); + + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + DCL_ELEM(p) = 0; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1,0,p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | declarator3 '[' constant_expr ']' + { + sym_link *p; + value *tval; + int size; + + tval = constExprValue($3, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + addDecl($1, 0, p); + } + | declarator3 '[' STATIC constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + werror (E_STATIC_ARRAY_PARAM_C99); + + tval = constExprValue($4, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + | declarator3 '[' type_qualifier_list constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + werror (E_QUALIFIED_ARRAY_PARAM_C99); + + tval = constExprValue($4, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } +| declarator3 '[' STATIC type_qualifier_list constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + { + werror (E_STATIC_ARRAY_PARAM_C99); + werror (E_QUALIFIED_ARRAY_PARAM_C99); + } + + tval = constExprValue($5, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($4); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($4); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($4); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($4); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } +| declarator3 '[' type_qualifier_list STATIC constant_expr ']' + { + sym_link *p, *n; + value *tval; + int size; + + if (!options.std_c99) + { + werror (E_QUALIFIED_ARRAY_PARAM_C99); + werror (E_STATIC_ARRAY_PARAM_C99); + } + + tval = constExprValue($5, TRUE); + /* if it is not a constant then Error */ + p = newLink (DECLARATOR); + DCL_TYPE(p) = ARRAY; + + if (!tval || (SPEC_SCLS(tval->etype) != S_LITERAL)) + { + werror(E_CONST_EXPECTED); + /* Assume a single item array to limit the cascade */ + /* of additional errors. */ + size = 1; + } + else + { + if ((size = (int) ulFromVal(tval)) < 0) + { + werror(E_NEGATIVE_ARRAY_SIZE, $1->name); + size = 1; + } + } + DCL_ELEM(p) = size; + DCL_PTR_CONST(p) = SPEC_CONST ($3); + DCL_PTR_RESTRICT(p) = SPEC_RESTRICT ($3); + DCL_PTR_VOLATILE(p) = SPEC_VOLATILE ($3); + DCL_PTR_ADDRSPACE(p) = SPEC_ADDRSPACE ($3); + addDecl($1, 0, p); + n = newLink (SPECIFIER); + SPEC_NEEDSPAR(n) = 1; + addDecl($1,0,n); + } + ; + +function_declarator2 + : declarator2 '(' ')' + { + addDecl ($1, FUNCTION, NULL); + } + | declarator2 '(' + { + NestLevel += LEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + seqPointNo++; /* not a true sequence point, but helps resolve scope */ + } + parameter_type_list ')' + { + sym_link *funcType; + + addDecl ($1, FUNCTION, NULL); + + funcType = $1->type; + while (funcType && !IS_FUNC(funcType)) + funcType = funcType->next; + + assert (funcType); + + FUNC_HASVARARGS(funcType) = IS_VARG($4); + FUNC_ARGS(funcType) = reverseVal($4); + + /* nest level was incremented to take care of the parms */ + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + seqPointNo++; /* not a true sequence point, but helps resolve scope */ + + // if this was a pointer (to a function) + if (!IS_FUNC($1->type)) + cleanUpLevel(SymbolTab, NestLevel + LEVEL_UNIT); + + $$ = $1; + } + | declarator2 '(' identifier_list ')' + { + werror(E_OLD_STYLE,$1->name); + /* assume it returns an int */ + $1->type = $1->etype = newIntLink(); + $$ = $1; + } + ; + +pointer + : unqualified_pointer { $$ = $1;} + | unqualified_pointer type_specifier_list + { + $$ = $1; + if (IS_SPEC($2)) { + DCL_TSPEC($1) = $2; + DCL_PTR_CONST($1) = SPEC_CONST($2); + DCL_PTR_VOLATILE($1) = SPEC_VOLATILE($2); + DCL_PTR_RESTRICT($1) = SPEC_RESTRICT($2); + DCL_PTR_ADDRSPACE($1) = SPEC_ADDRSPACE($2); + } + else + werror (W_PTR_TYPE_INVALID); + } + | unqualified_pointer pointer + { + $$ = $1; + $$->next = $2; + DCL_TYPE($2)=port->unqualified_pointer; + } + | unqualified_pointer type_specifier_list pointer + { + $$ = $1; + if (IS_SPEC($2) && DCL_TYPE($3) == UPOINTER) { + DCL_PTR_CONST($1) = SPEC_CONST($2); + DCL_PTR_VOLATILE($1) = SPEC_VOLATILE($2); + DCL_PTR_RESTRICT($1) = SPEC_RESTRICT($2); + DCL_PTR_ADDRSPACE($1) = SPEC_ADDRSPACE($2); + switch (SPEC_SCLS($2)) { + case S_XDATA: + DCL_TYPE($3) = FPOINTER; + break; + case S_IDATA: + DCL_TYPE($3) = IPOINTER; + break; + case S_PDATA: + DCL_TYPE($3) = PPOINTER; + break; + case S_DATA: + DCL_TYPE($3) = POINTER; + break; + case S_CODE: + DCL_TYPE($3) = CPOINTER; + break; + case S_EEPROM: + DCL_TYPE($3) = EEPPOINTER; + break; + default: + // this could be just "constant" + // werror(W_PTR_TYPE_INVALID); + ; + } + } + else + werror (W_PTR_TYPE_INVALID); + $$->next = $3; + } + ; + +unqualified_pointer + : '*' + { + $$ = newLink(DECLARATOR); + DCL_TYPE($$)=UPOINTER; + } + ; + +type_specifier_list : type_specifier_list_ { $$ = finalizeSpec($1); }; + +type_specifier_list_ + : type_specifier + //| type_specifier_list_ type_specifier { $$ = mergeSpec ($1,$2, "type_specifier_list"); } + | type_specifier_list_ type_specifier { + /* if the decl $2 is not a specifier */ + /* find the spec and replace it */ + $$ = mergeDeclSpec($1, $2, "type_specifier_list type_specifier skipped"); + } + ; + +identifier_list + : identifier + | identifier_list ',' identifier + { + $3->next = $1; + $$ = $3; + } + ; + +parameter_type_list + : parameter_list + | parameter_list ',' VAR_ARGS { $1->vArgs = 1;} + ; + +parameter_list + : parameter_declaration + | parameter_list ',' parameter_declaration + { + $3->next = $1; + $$ = $3; + } + ; + +parameter_declaration + : declaration_specifiers declarator + { + symbol *loop; + + if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) + { + werror (E_STORAGE_CLASS_FOR_PARAMETER, $2->name); + } + pointerTypes ($2->type, $1); + if (IS_SPEC ($2->etype)) + SPEC_NEEDSPAR($2->etype) = 0; + addDecl ($2, 0, $1); + for (loop = $2; loop; loop->_isparm = 1, loop = loop->next) + ; + $$ = symbolVal ($2); + ignoreTypedefType = 0; + } + | type_name + { + $$ = newValue (); + $$->type = $1; + $$->etype = getSpec ($$->type); + ignoreTypedefType = 0; + } + ; + +type_name + : declaration_specifiers + { + if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) + { + werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); + } + $$ = $1; ignoreTypedefType = 0; + } + | declaration_specifiers abstract_declarator + { + /* go to the end of the list */ + sym_link *p; + + if (IS_SPEC ($1) && !IS_VALID_PARAMETER_STORAGE_CLASS_SPEC ($1)) + { + werror (E_STORAGE_CLASS_FOR_PARAMETER, "type name"); + } + pointerTypes ($2,$1); + for (p = $2; p && p->next; p = p->next) + ; + if (!p) + { + werror(E_SYNTAX_ERROR, yytext); + } + else + { + p->next = $1; + } + $$ = $2; + ignoreTypedefType = 0; + } + ; + +abstract_declarator + : pointer { $$ = reverseLink($1); } + | abstract_declarator2 + | pointer abstract_declarator2 { $1 = reverseLink($1); $2->next = $1; $$ = $2; + if (IS_PTR($1) && IS_FUNC($2)) + DCL_TYPE($1) = CPOINTER; + } + ; + +abstract_declarator2 + : '(' abstract_declarator ')' { $$ = $2; } + | '[' ']' { + $$ = newLink (DECLARATOR); + DCL_TYPE($$) = ARRAY; + DCL_ELEM($$) = 0; + } + | '[' constant_expr ']' { + value *val; + $$ = newLink (DECLARATOR); + DCL_TYPE($$) = ARRAY; + DCL_ELEM($$) = (int) ulFromVal(val = constExprValue($2,TRUE)); + } + | abstract_declarator2 '[' ']' { + $$ = newLink (DECLARATOR); + DCL_TYPE($$) = ARRAY; + DCL_ELEM($$) = 0; + $$->next = $1; + } + | abstract_declarator2 '[' constant_expr ']' + { + value *val; + $$ = newLink (DECLARATOR); + DCL_TYPE($$) = ARRAY; + DCL_ELEM($$) = (int) ulFromVal(val = constExprValue($3,TRUE)); + $$->next = $1; + } + | '(' ')' { $$ = NULL;} + | '(' parameter_type_list ')' { $$ = NULL;} + | abstract_declarator2 '(' ')' { + // $1 must be a pointer to a function + sym_link *p=newLink(DECLARATOR); + DCL_TYPE(p) = FUNCTION; + if (!$1) { + // ((void (code *) ()) 0) () + $1=newLink(DECLARATOR); + DCL_TYPE($1)=CPOINTER; + $$ = $1; + } + $1->next=p; + } + | abstract_declarator2 '(' + { + NestLevel += LEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + } + parameter_type_list ')' + { + sym_link *p = newLink(DECLARATOR), *q; + DCL_TYPE(p) = FUNCTION; + + FUNC_HASVARARGS(p) = IS_VARG($4); + FUNC_ARGS(p) = reverseVal($4); + + /* nest level was incremented to take care of the parms */ + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + if (!$1) + { + /* ((void (code *) (void)) 0) () */ + $1 = newLink(DECLARATOR); + DCL_TYPE($1) = CPOINTER; + $$ = $1; + } + for (q = $1; q && q->next; q = q->next); + q->next = p; + } + ; + +initializer + : assignment_expr { $$ = newiList(INIT_NODE,$1); } + | '{' initializer_list '}' { $$ = newiList(INIT_DEEP,revinit($2)); } + | '{' initializer_list ',' '}' { $$ = newiList(INIT_DEEP,revinit($2)); } + ; + +initializer_list + : designation_opt initializer { $2->designation = $1; $$ = $2; } + | initializer_list ',' designation_opt initializer + { + $4->designation = $3; + $4->next = $1; + $$ = $4; + } + ; + +static_assert_declaration + : STATIC_ASSERT '(' constant_expr ',' STRING_LITERAL ')' + { + value *val = constExprValue ($3, TRUE); + if (!val) + werror (E_CONST_EXPECTED); + else if (!ulFromVal(val)) + werror (W_STATIC_ASSERTION, $5); + } + | STATIC_ASSERT '(' constant_expr ')' + { + value *val = constExprValue ($3, TRUE); + if (!options.std_c2x) + werror (E_STATIC_ASSERTION_C2X); + if (!val) + werror (E_CONST_EXPECTED); + else if (!ulFromVal(val)) + werror (W_STATIC_ASSERTION_2); + } + ; + +statement + : labeled_statement + | compound_statement + | expression_statement + | selection_statement + | iteration_statement + | jump_statement + | critical_statement + | asm_statement + ; + +critical + : CRITICAL { + if (inCriticalFunction || inCriticalBlock) + werror(E_INVALID_CRITICAL); + inCriticalBlock = 1; + STACK_PUSH(continueStack,NULL); + STACK_PUSH(breakStack,NULL); + $$ = NULL; + } + ; + +critical_statement + : critical statement { + STACK_POP(breakStack); + STACK_POP(continueStack); + $$ = newNode(CRITICAL,$2,NULL); + inCriticalBlock = 0; + } + ; + +labeled_statement + : label statement { if ($1) {$$ = $1; $1->right = $2;} else $$ = newNode (BLOCK, NULL, NULL); } + | label '}' + { /* Support a label without a statement at the end of a */ + /* compound statement as a SDCC extension. Include the */ + /* closing brace as part of the rule to avoid an */ + /* unacceptably large number of shift/reduce conflicts */ + /* and then reinsert it to be parsed a second time. */ + werror(W_LABEL_WITHOUT_STATEMENT); + $$ = $1; + yychar = '}'; + } + ; + +label + : identifier ':' { $$ = createLabel($1,NULL); + $1->isitmp = 0; } + | CASE constant_expr ':' + { + if (STACK_EMPTY(swStk)) + $$ = createCase(NULL,$2,NULL); + else + $$ = createCase(STACK_PEEK(swStk),$2,NULL); + } + | DEFAULT { $$ = newNode(DEFAULT,NULL,NULL); } ':' + { + if (STACK_EMPTY(swStk)) + $$ = createDefault(NULL,$2,NULL); + else + $$ = createDefault(STACK_PEEK(swStk),$2,NULL); + } + ; + +start_block + : '{' + { + NestLevel += LEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + ignoreTypedefType = 0; + } + ; + +end_block + : '}' + { + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + } + ; + +compound_statement + : start_block end_block { $$ = createBlock(NULL, NULL); } + | start_block block_item_list end_block + { + $$ = $2; + cleanUpLevel(StructTab, NestLevel + LEVEL_UNIT); + } + | error ';' { $$ = NULL; } + ; + +block_item_list + : statements_and_implicit { $$ = createBlock(NULL, $1); } + | declaration_list { $$ = createBlock($1, NULL); } + | declaration_list statements_and_implicit { $$ = createBlock($1, $2); } + ; + +statements_and_implicit + : statement_list + | statement_list implicit_block + { + $$ = newNode(NULLOP, $1, $2); + if (!options.std_c99) + werror(E_DECL_AFTER_STATEMENT_C99); + } + ; + +declaration_after_statement + : { + NestLevel += SUBLEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + ignoreTypedefType = 0; + } + declaration_list { $$ = $2; } + ; + +implicit_block + : declaration_after_statement statements_and_implicit + { + NestLevel -= SUBLEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + $$ = createBlock($1, $2); + cleanUpLevel(StructTab, NestLevel + SUBLEVEL_UNIT); + } + | declaration_after_statement + { + NestLevel -= SUBLEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + $$ = createBlock($1, NULL); + cleanUpLevel(StructTab, NestLevel + SUBLEVEL_UNIT); + } + ; + +declaration_list + : declaration + { + /* if this is typedef declare it immediately */ + if ( $1 && IS_TYPEDEF($1->etype)) { + allocVariables ($1); + $$ = NULL; + } + else + $$ = $1; + ignoreTypedefType = 0; + addSymChain(&$1); + } + + | declaration_list declaration + { + symbol *sym; + + /* if this is a typedef */ + if ($2 && IS_TYPEDEF($2->etype)) { + allocVariables ($2); + $$ = $1; + } + else { + /* get to the end of the previous decl */ + if ( $1 ) { + $$ = sym = $1; + while (sym->next) + sym = sym->next; + sym->next = $2; + } + else + $$ = $2; + } + ignoreTypedefType = 0; + addSymChain(&$2); + } + ; + +statement_list + : statement + | statement_list statement { $$ = newNode(NULLOP,$1,$2);} + ; + +expression_statement + : ';' { $$ = NULL;} + | expr ';' { $$ = $1; seqPointNo++;} + ; + +else_statement + : ELSE statement { $$ = $2; } + | { $$ = NULL; } + ; + + +selection_statement + : IF '(' expr ')' { seqPointNo++;} statement else_statement + { + noLineno++; + $$ = createIf ($3, $6, $7 ); + $$->lineno = $3->lineno; + $$->filename = $3->filename; + noLineno--; + } + | SWITCH '(' expr ')' { + ast *ex; + static int swLabel = 0; + + seqPointNo++; + /* create a node for expression */ + ex = newNode(SWITCH,$3,NULL); + STACK_PUSH(swStk,ex); /* save it in the stack */ + ex->values.switchVals.swNum = swLabel; + + /* now create the label */ + SNPRINTF(lbuff, sizeof(lbuff), + "_swBrk_%d",swLabel++); + $$ = newSymbol(lbuff,NestLevel); + /* put label in the break stack */ + STACK_PUSH(breakStack,$$); + } + statement { + /* get back the switch form the stack */ + $$ = STACK_POP(swStk); + $$->right = newNode (NULLOP,$6,createLabel($5,NULL)); + STACK_POP(breakStack); + } + ; + +while : WHILE { /* create and push the continue , break & body labels */ + static int Lblnum = 0; + /* continue */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilecontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilebreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* body */ + SNPRINTF (lbuff, sizeof(lbuff), "_whilebody_%d",Lblnum++); + $$ = newSymbol(lbuff,NestLevel); + } + ; + +do : DO { /* create and push the continue , break & body Labels */ + static int Lblnum = 0; + + /* continue */ + SNPRINTF(lbuff, sizeof(lbuff), "_docontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF(lbuff, sizeof(lbuff), "_dobreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* do body */ + SNPRINTF(lbuff, sizeof(lbuff), "_dobody_%d",Lblnum++); + $$ = newSymbol (lbuff,NestLevel); + } + ; + +for : FOR { /* create & push continue, break & body labels */ + static int Lblnum = 0; + + NestLevel += LEVEL_UNIT; + STACK_PUSH(blockNum, currBlockno); + btree_add_child(currBlockno, ++blockNo); + currBlockno = blockNo; + ignoreTypedefType = 0; + + /* continue */ + SNPRINTF(lbuff, sizeof(lbuff), "_forcontinue_%d",Lblnum); + STACK_PUSH(continueStack,newSymbol(lbuff,NestLevel)); + /* break */ + SNPRINTF(lbuff, sizeof(lbuff), "_forbreak_%d",Lblnum); + STACK_PUSH(breakStack,newSymbol(lbuff,NestLevel)); + /* body */ + SNPRINTF(lbuff, sizeof(lbuff), "_forbody_%d",Lblnum); + $$ = newSymbol(lbuff,NestLevel); + /* condition */ + SNPRINTF(lbuff, sizeof(lbuff), "_forcond_%d",Lblnum++); + STACK_PUSH(forStack,newSymbol(lbuff,NestLevel)); + } + ; + +iteration_statement + : while '(' expr ')' { seqPointNo++;} statement + { + noLineno++; + $$ = createWhile ( $1, STACK_POP(continueStack), + STACK_POP(breakStack), $3, $6 ); + $$->lineno = $1->lineDef; + $$->filename = $1->fileDef; + noLineno--; + } + | do statement WHILE '(' expr ')' ';' + { + seqPointNo++; + noLineno++; + $$ = createDo ( $1 , STACK_POP(continueStack), + STACK_POP(breakStack), $5, $2); + $$->lineno = $1->lineDef; + $$->filename = $1->fileDef; + noLineno--; + } + | for '(' expr_opt ';' expr_opt ';' expr_opt ')' statement + { + noLineno++; + + $$ = newNode(FOR,$9,NULL); + AST_FOR($$,trueLabel) = $1; + AST_FOR($$,continueLabel) = STACK_POP(continueStack); + AST_FOR($$,falseLabel) = STACK_POP(breakStack); + AST_FOR($$,condLabel) = STACK_POP(forStack); + AST_FOR($$,initExpr) = $3; + AST_FOR($$,condExpr) = $5; + AST_FOR($$,loopExpr) = $7; + + /* This continue label is not at the correct location, */ + /* but we need to create it now for proper binding. The */ + /* code that handles the FOR node will move it to the */ + /* proper location inside the for loop. */ + if (AST_FOR($$,continueLabel)->isref) + $$->right = createLabel(AST_FOR($$,continueLabel),NULL); + $$ = newNode(NULLOP,$$,createLabel(AST_FOR($$,falseLabel),NULL)); + noLineno--; + + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + } + | for '(' declaration expr_opt ';' expr_opt ')' + { + if (!options.std_c99) + werror (E_FOR_INITAL_DECLARATION_C99); + + if ( $3 && IS_TYPEDEF($3->etype)) + allocVariables ($3); + ignoreTypedefType = 0; + addSymChain(&$3); + } + statement + { + + noLineno++; + + $$ = newNode(FOR,$9,NULL); + AST_FOR($$,trueLabel) = $1; + AST_FOR($$,continueLabel) = STACK_POP(continueStack); + AST_FOR($$,falseLabel) = STACK_POP(breakStack); + AST_FOR($$,condLabel) = STACK_POP(forStack); + AST_FOR($$,initExpr) = 0; + AST_FOR($$,condExpr) = $4; + AST_FOR($$,loopExpr) = $6; + + /* This continue label is not at the correct location, */ + /* but we need to create it now for proper binding. The */ + /* code that handles the FOR node will move it to the */ + /* proper location inside the for loop. */ + if (AST_FOR($$,continueLabel)->isref) + $$->right = createLabel(AST_FOR($$,continueLabel),NULL); + $$ = createBlock($3, newNode(NULLOP,$$,createLabel(AST_FOR($$,falseLabel),NULL))); + cleanUpLevel(StructTab, NestLevel + LEVEL_UNIT); + noLineno--; + + NestLevel -= LEVEL_UNIT; + currBlockno = STACK_POP(blockNum); + } +; + +expr_opt + : { $$ = NULL; seqPointNo++; } + | expr { $$ = $1; seqPointNo++; } + ; + +jump_statement + : GOTO identifier ';' { + if (inCriticalBlock) { + werror(E_INVALID_CRITICAL); + $$ = NULL; + } else { + $2->islbl = 1; + $$ = newAst_VALUE(symbolVal($2)); + $$ = newNode(GOTO,$$,NULL); + } + } + | CONTINUE ';' { + /* make sure continue is in context */ + if (STACK_EMPTY(continueStack) || STACK_PEEK(continueStack) == NULL) { + werror(E_BREAK_CONTEXT); + $$ = NULL; + } + else { + $$ = newAst_VALUE(symbolVal(STACK_PEEK(continueStack))); + $$ = newNode(GOTO,$$,NULL); + /* mark the continue label as referenced */ + STACK_PEEK(continueStack)->isref = 1; + } + } + | BREAK ';' { + if (STACK_EMPTY(breakStack) || STACK_PEEK(breakStack) == NULL) { + werror(E_BREAK_CONTEXT); + $$ = NULL; + } else { + $$ = newAst_VALUE(symbolVal(STACK_PEEK(breakStack))); + $$ = newNode(GOTO,$$,NULL); + STACK_PEEK(breakStack)->isref = 1; + } + } + | RETURN ';' { + seqPointNo++; + if (inCriticalBlock) { + werror(E_INVALID_CRITICAL); + $$ = NULL; + } else { + $$ = newNode(RETURN,NULL,NULL); + } + } + | RETURN expr ';' { + seqPointNo++; + if (inCriticalBlock) { + werror(E_INVALID_CRITICAL); + $$ = NULL; + } else { + $$ = newNode(RETURN,NULL,$2); + } + } + ; + +asm_string_literal + : STRING_LITERAL + ; + +asm_statement + : ASM '(' asm_string_literal ')' ';' + { + ast *ex; + + seqPointNo++; + ex = newNode(INLINEASM, NULL, NULL); + ex->values.inlineasm = strdup(copyStr ($3, NULL)); + seqPointNo++; + $$ = ex; + } + | INLINEASM ';' + { + ast *ex; + + seqPointNo++; + ex = newNode(INLINEASM, NULL, NULL); + ex->values.inlineasm = strdup($1); + seqPointNo++; + $$ = ex; + } + ; + +addressmod + : ADDRESSMOD identifier identifier ';' { + symbol *sym; + if ((sym = findSymWithLevel (AddrspaceTab, $3)) && sym->level == $3->level) + werrorfl (sym->fileDef, sym->lineDef, E_PREVIOUS_DEF); + if (!findSymWithLevel (SymbolTab, $2)) + werror (E_ID_UNDEF, $2->name); + addSym (AddrspaceTab, $3, $3->name, $3->level, $3->block, 0); + sym = findSymWithLevel (AddrspaceTab, $3); + sym->addressmod[0] = findSymWithLevel (SymbolTab, $2); + sym->addressmod[1] = 0; + } + | ADDRESSMOD identifier SD_CONST identifier ';' { + symbol *sym; + sym_link *type; + if ((sym = findSymWithLevel (AddrspaceTab, $4)) && sym->level == $4->level) + werrorfl (sym->fileDef, sym->lineDef, E_PREVIOUS_DEF); + if (!findSymWithLevel (SymbolTab, $2)) + werror (E_ID_UNDEF, $2->name); + addSym (AddrspaceTab, $4, $4->name, $4->level, $4->block, 0); + sym = findSymWithLevel (AddrspaceTab, $4); + sym->addressmod[0] = findSymWithLevel (SymbolTab, $2); + sym->addressmod[1] = 0; + type = newLink (SPECIFIER); + SPEC_CONST(type) = 1; + sym->type = sym->etype = type; + } + ; + +identifier + : IDENTIFIER { $$ = newSymbol ($1, NestLevel); } + ; +%% diff --git a/src/SDCCBBlock.c b/src/SDCCBBlock.c new file mode 100644 index 0000000..37c6fdb --- /dev/null +++ b/src/SDCCBBlock.c @@ -0,0 +1,889 @@ +/*------------------------------------------------------------------------- + + SDCCBBlock.c - routines to manipulate basic Blocks + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +int eBBNum = 0; +set *graphEdges = NULL; /* list of edges in this flow graph */ + +struct _dumpFiles dumpFiles[] = { + {DUMP_RAW0, ".dumpraw0", NULL}, + {DUMP_RAW1, ".dumpraw1", NULL}, + {DUMP_CSE, ".dumpcse", NULL}, + {DUMP_DFLOW, ".dumpdflow", NULL}, + {DUMP_GCSE, ".dumpgcse", NULL}, + {DUMP_DEADCODE, ".dumpdeadcode", NULL}, + {DUMP_LOOP, ".dumploop", NULL}, + {DUMP_LOOPG, ".dumploopg", NULL}, + {DUMP_LOOPD, ".dumploopd", NULL}, + {DUMP_RANGE, ".dumprange", NULL}, + {DUMP_PACK, ".dumppack", NULL}, + {DUMP_RASSGN, ".dumprassgn", NULL}, + {DUMP_LRANGE, ".dumplrange", NULL}, + {DUMP_LOSPRE, ".dumplospre", NULL}, + {DUMP_CUSTOM, ".dumpcustom", NULL}, + {0, NULL, NULL} +}; + +/*-----------------------------------------------------------------*/ +/* printEntryLabel - prints entry label of a ebblock */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (printEntryLabel) +{ + eBBlock *bp = item; + + fprintf (stdout, " %-20s ", bp->entryLabel->name); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* neweBBlock - allocate & return a new extended basic block */ +/*-----------------------------------------------------------------*/ +eBBlock * +neweBBlock () +{ + eBBlock *ebb; + + ebb = Safe_alloc (sizeof (eBBlock)); + return ebb; +} + +/*-----------------------------------------------------------------*/ +/* newEdge - allocates & initialises an edge to given values */ +/*-----------------------------------------------------------------*/ +edge * +newEdge (eBBlock * from, eBBlock * to) +{ + edge *ep; + + ep = Safe_alloc (sizeof (edge)); + + ep->from = from; + ep->to = to; + return ep; +} + +/*-----------------------------------------------------------------*/ +/* createDumpFile - create the dump file */ +/*-----------------------------------------------------------------*/ +FILE * +createDumpFile (int id) +{ + struct _dumpFiles *dumpFilesPtr = dumpFiles; + static int dumpIndex = 0; + static char dumpIndexStr[32]; + + while (dumpFilesPtr->id) + { + if (dumpFilesPtr->id == id) + break; + dumpFilesPtr++; + } + + if (!dumpFilesPtr->id) + { + fprintf (stdout, "internal error: createDumpFile: unknown dump file.\n"); + exit (1); + } + + sprintf (dumpIndexStr, ".%d", dumpIndex); + dumpIndex++; + + if (!dumpFilesPtr->filePtr) + { + // not used before, create it + struct dbuf_s dumpFileName; + + dbuf_init (&dumpFileName, PATH_MAX); + dbuf_append_str (&dumpFileName, dstFileName); +#ifdef _DEBUG + dbuf_append_str (&dumpFileName, dumpIndexStr); +#endif + dbuf_append_str (&dumpFileName, dumpFilesPtr->ext); + if (!(dumpFilesPtr->filePtr = fopen (dbuf_c_str (&dumpFileName), "w"))) + { + werror (E_FILE_OPEN_ERR, dbuf_c_str (&dumpFileName)); + dbuf_destroy (&dumpFileName); + exit (1); + } + dbuf_destroy (&dumpFileName); + } + +#if 0 + fprintf (dumpFilesPtr->filePtr, "Dump file index: %d\n", dumpIndex); +#endif + + return dumpFilesPtr->filePtr; +} + +/*-----------------------------------------------------------------*/ +/* closeDumpFiles - close possible opened dumpfiles */ +/*-----------------------------------------------------------------*/ +void +closeDumpFiles () +{ + struct _dumpFiles *dumpFilesPtr; + + for (dumpFilesPtr = dumpFiles; dumpFilesPtr->id; dumpFilesPtr++) + { + if (dumpFilesPtr->filePtr) + { + fclose (dumpFilesPtr->filePtr); + } + } +} + +/*-----------------------------------------------------------------*/ +/* dumpLiveRanges - dump liverange information into a file */ +/*-----------------------------------------------------------------*/ +void +dumpLiveRanges (int id, hTab * liveRanges) +{ + FILE *file; + symbol *sym; + int k; + + if (id) + { + file = createDumpFile (id); + } + else + { + file = stdout; + } + + if (currFunc) + fprintf (file, "------------- Func %s -------------\n", currFunc->name); + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + + fprintf (file, "%s [k%d lr%d:%d so:%d]{ re%d rm%d}", + (sym->rname[0] ? sym->rname : sym->name), + sym->key, sym->liveFrom, sym->liveTo, sym->stack, sym->isreqv, sym->remat); + + fprintf (file, "{"); + printTypeChain (sym->type, file); + if (sym->usl.spillLoc) + { + fprintf (file, "}{ sir@ %s", sym->usl.spillLoc->rname); + } + fprintf (file, "} clashes with "); + bitVectDebugOn (sym->clashes, file); + fprintf (file, "\n"); + } + + fflush (file); +} + + +/*-----------------------------------------------------------------*/ +/* dumpEbbsToFileExt - write all the basic blocks to a file */ +/*-----------------------------------------------------------------*/ +void +dumpEbbsToFileExt (int id, ebbIndex * ebbi) +{ + FILE *of; + int i, d; + eBBlock *bb; + set *cseSet; + eBBlock **ebbs = ebbi->dfOrder ? ebbi->dfOrder : ebbi->bbOrder; + int count = ebbi->count; + + if (id) + { + of = createDumpFile (id); + } + else + { + of = stdout; + } + + for (i = 0; i < count; i++) + { + fprintf (of, "\n----------------------------------------------------------------\n"); + fprintf (of, "Basic Block %s (df:%d bb:%d lvl:%ld:%ld): loopDepth=%d%s%s%s\n", + ebbs[i]->entryLabel->name, + ebbs[i]->dfnum, ebbs[i]->bbnum, + ebbs[i]->entryLabel->level / LEVEL_UNIT, ebbs[i]->entryLabel->level % LEVEL_UNIT, + ebbs[i]->depth, + ebbs[i]->noPath ? " noPath" : "", + ebbs[i]->partOfLoop ? " partOfLoop" : "", ebbs[i]->isLastInLoop ? " isLastInLoop" : ""); + + // a --nolabelopt makes this more readable + fprintf (of, "\nsuccessors: "); + for (bb = setFirstItem (ebbs[i]->succList); bb; bb = setNextItem (ebbs[i]->succList)) + { + fprintf (of, "%s ", bb->entryLabel->name); + } + fprintf (of, "\npredecessors: "); + for (bb = setFirstItem (ebbs[i]->predList); bb; bb = setNextItem (ebbs[i]->predList)) + { + fprintf (of, "%s ", bb->entryLabel->name); + } + fprintf (of, "\ndominators: "); + for (d = 0; d < ebbs[i]->domVect->size; d++) + { + if (bitVectBitValue (ebbs[i]->domVect, d)) + { + fprintf (of, "%s ", ebbi->bbOrder[d]->entryLabel->name); //ebbs[d]->entryLabel->name); + } + } + fprintf (of, "\n"); + + fprintf (of, "\ndefines bitVector :"); + bitVectDebugOn (ebbs[i]->defSet, of); + fprintf (of, "\nlocal defines bitVector :"); + bitVectDebugOn (ebbs[i]->ldefs, of); + fprintf (of, "\npointers Set bitvector :"); + bitVectDebugOn (ebbs[i]->ptrsSet, of); +#if 0 + fprintf (of, "\nin coming definitions :"); + bitVectDebugOn (ebbs[i]->inDefs, of); + fprintf (of, "\nout going definitions :"); + bitVectDebugOn (ebbs[i]->outDefs, of); + fprintf (of, "\ndefines used :"); + bitVectDebugOn (ebbs[i]->usesDefs, of); +#endif + + if (ebbs[i]->isLastInLoop) + { + fprintf (of, "\nInductions Set bitvector :"); + bitVectDebugOn (ebbs[i]->linds, of); + } + + fprintf (of, "\ninExprs:"); + for (cseSet = ebbs[i]->inExprs; cseSet; cseSet = cseSet->next) + { + cseDef *item = cseSet->item; + fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); + if (item->fromGlobal) + fprintf (of, "g"); + } + fprintf (of, "\noutExprs:"); + for (cseSet = ebbs[i]->outExprs; cseSet; cseSet = cseSet->next) + { + cseDef *item = cseSet->item; + fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); + if (item->fromGlobal) + fprintf (of, "g"); + } + fprintf (of, "\nkilledExprs:"); + for (cseSet = ebbs[i]->killedExprs; cseSet; cseSet = cseSet->next) + { + cseDef *item = cseSet->item; + fprintf (of, " %s(%d)", OP_SYMBOL (item->sym)->name, item->diCode->key); + if (item->fromGlobal) + fprintf (of, "g"); + } + + fprintf (of, "\n----------------------------------------------------------------\n"); + printiCChain (ebbs[i]->sch, of); + } + fflush (of); +} + +/*-----------------------------------------------------------------*/ +/* iCode2eBBlock - converts a sequnce till label to a ebb */ +/*-----------------------------------------------------------------*/ +eBBlock * +iCode2eBBlock (iCode * ic) +{ + iCode *loop; + eBBlock *ebb = neweBBlock (); /* allocate an entry */ + + /* put the first one unconditionally */ + ebb->sch = ic; + ic->seq = 0; + + /* if this is a label then */ + if (ic->op == LABEL) + ebb->entryLabel = ic->label; + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_eBBlock%d", eBBNum++); + ebb->entryLabel = newSymbol (dbuf_c_str (&dbuf), 1); + dbuf_destroy (&dbuf); + ebb->entryLabel->key = labelKey++; + } + + if (ic && (ic->op == GOTO || ic->op == JUMPTABLE || ic->op == IFX)) + { + ebb->ech = ebb->sch; + return ebb; + } + + /* if this is a function call */ + if (ic->op == CALL || ic->op == PCALL) + { + sym_link *type = operandType (IC_LEFT (ic)); + ebb->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + if (IS_FUNCPTR (type)) + type = type->next; + if (type && FUNC_ISNORETURN (type)) + { + ebb->ech = ebb->sch; + return ebb; + } + } + + if ((ic->next && ic->next->op == LABEL) || !ic->next) + { + ebb->ech = ebb->sch; + return ebb; + } + + /* loop thru till we find one with a label */ + for (loop = ic->next; loop; loop = loop->next) + { + loop->seq = 0; + + /* if this is the last one */ + if (!loop->next) + break; + /* if this is a function call */ + if (loop->op == CALL || loop->op == PCALL) + { + sym_link *type = operandType (IC_LEFT (loop)); + ebb->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + if (IS_FUNCPTR (type)) + type = type->next; + if (type && FUNC_ISNORETURN (type)) + break; + } + + /* if the next one is a label */ + /* if this is a goto or ifx */ + if (loop->next->op == LABEL || loop->op == GOTO || loop->op == JUMPTABLE || loop->op == IFX) + break; + } + + /* mark the end of the chain */ + ebb->ech = loop; + + return ebb; +} + +/*-----------------------------------------------------------------*/ +/* eBBWithEntryLabel - finds the basic block with the entry label */ +/*-----------------------------------------------------------------*/ +eBBlock * +eBBWithEntryLabel (ebbIndex* ebbi, const symbol *eLabel) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i; + + for (i = 0; i < count; i++) + { + if (isSymbolEqual (ebbs[i]->entryLabel, eLabel)) + return ebbs[i]; + } + + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* ifFromIs - will return 1 if the from block matches this */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifFromIs) +{ + edge *ep = item; + V_ARG (eBBlock *, this); + + if (ep->from == this) + return 1; + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* edgesTo - returns a set of edges with to == supplied value */ +/*-----------------------------------------------------------------*/ +set * +edgesTo (eBBlock * to) +{ + set *result = NULL; + edge *loop; + + for (loop = setFirstItem (graphEdges); loop; loop = setNextItem (graphEdges)) + if (loop->to == to && !loop->from->noPath) + addSet (&result, loop->from); + + return result; +} + + +/*-----------------------------------------------------------------*/ +/* addiCodeToeBBlock - will add an iCode to the end of a block */ +/*-----------------------------------------------------------------*/ +void +addiCodeToeBBlock (eBBlock * ebp, iCode * ic, iCode * ip) +{ + ic->prev = ic->next = NULL; + /* if the insert point is given */ + if (ip) + { + ic->filename = ip->filename; + ic->lineno = ip->lineno; + ic->eBBlockNum = ip->eBBlockNum; + ic->prev = ip->prev; + ip->prev = ic; + ic->next = ip; + if (!ic->prev) + ebp->sch = ic; + else + ic->prev->next = ic; + return; + } + + /* if the block has no instructions */ + if (ebp->ech == NULL) + { + ebp->sch = ebp->ech = ic; + ic->next = NULL; + return; + } + + /* if the last instruction is a goto */ + /* we add it just before the goto */ + if (ebp->ech->op == GOTO || ebp->ech->op == JUMPTABLE || ebp->ech->op == RETURN) + { + ic->filename = ebp->ech->filename; + ic->lineno = ebp->ech->lineno; + ic->prev = ebp->ech->prev; + ebp->ech->prev = ic; + ic->next = ebp->ech; + if (!ic->prev) /* was the last only on in the block */ + ebp->sch = ic; + else + ic->prev->next = ic; + return; + } + + /* if the last one was a ifx statement we check to see */ + /* if the condition was defined in the previous instruction */ + /* if this is true then we put it before the condition else */ + /* we put it before if, this is to reduce register pressure, */ + /* we don't have to hold condition too long in a register */ + + /* loop induction sometimes appends a GOTO instruction, */ + /* it must be at the very end */ + if (ebp->ech->op == IFX && ic->op != GOTO) + { + iCode *ipoint; + +/* if ( !ebp->ech->prev ) */ +/* ipoint = ebp->ech ; */ +/* else */ +/* if (!IC_RESULT(ebp->ech->prev)) */ +/* ipoint = ebp->ech ; */ +/* else */ +/* if (IC_COND(ebp->ech)->key == IC_RESULT(ebp->ech->prev)->key) */ +/* ipoint = ebp->ech->prev; */ +/* else */ +/* ipoint = ebp->ech ; */ + ipoint = ebp->ech; + ic->filename = ipoint->filename; + ic->lineno = ipoint->lineno; + ic->prev = ipoint->prev; + ipoint->prev = ic; + ic->next = ipoint; + if (!ic->prev) + ebp->sch = ic; + else + ic->prev->next = ic; + return; + } + + /* will add it to the very end */ + ip = ebp->ech; + ip->next = ic; + ic->prev = ip; + ic->next = NULL; + ebp->ech = ic; + + return; +} + +/*-----------------------------------------------------------------*/ +/* remiCodeFromeBBlock - remove an iCode from BBlock */ +/*-----------------------------------------------------------------*/ +void +remiCodeFromeBBlock (eBBlock * ebb, iCode * ic) +{ + wassert (ic->seq >= ebb->fSeq && ic->seq <= ebb->lSeq); + if (ic->prev) + ic->prev->next = ic->next; + else + ebb->sch = ic->next; + + if (ic->next) + ic->next->prev = ic->prev; + else + ebb->ech = ic->prev; +} + +/*-----------------------------------------------------------------*/ +/* iCodeBreakDown : breakDown iCode chain to blocks */ +/*-----------------------------------------------------------------*/ +ebbIndex * +iCodeBreakDown (iCode * ic) +{ + eBBlock **ebbs = NULL; + iCode *loop = ic; + ebbIndex *ebbi; + + ebbi = Safe_alloc (sizeof (ebbIndex)); + ebbi->count = 0; + ebbi->dfOrder = NULL; /* no depth first order information yet */ + + /* allocate for the first entry */ + + ebbs = Safe_alloc (sizeof (eBBlock *)); + ebbi->bbOrder = ebbs; + + while (loop) + { + + /* convert 2 block */ + eBBlock *ebb = iCode2eBBlock (loop); + loop = ebb->ech->next; + + ebb->ech->next = NULL; /* mark the end of this chain */ + if (loop) + loop->prev = NULL; + ebb->bbnum = ebbi->count; /* save this block number */ + /* put it in the array */ + ebbs[(ebbi->count)++] = ebb; + + /* allocate for the next one. Remember to clear the new */ + /* pointer at the end, that was created by realloc. */ + + ebbs = Safe_realloc (ebbs, (ebbi->count + 1) * sizeof (eBBlock *)); + ebbi->bbOrder = ebbs; + + ebbs[ebbi->count] = 0; + + /* if this one ends in a goto or a conditional */ + /* branch then check if the block it is going */ + /* to already exists, if yes then this could */ + /* be a loop, add a preheader to the block it */ + /* goes to if it does not already have one */ + if (ebbs[(ebbi->count) - 1]->ech && (ebbs[(ebbi->count) - 1]->ech->op == GOTO || ebbs[(ebbi->count) - 1]->ech->op == IFX)) + { + + symbol *label; + eBBlock *destBlock; + + if (ebbs[(ebbi->count) - 1]->ech->op == GOTO) + label = IC_LABEL (ebbs[(ebbi->count) - 1]->ech); + else if (!(label = IC_TRUE (ebbs[(ebbi->count) - 1]->ech))) + label = IC_FALSE (ebbs[(ebbi->count) - 1]->ech); + + if ((destBlock = eBBWithEntryLabel (ebbi, label)) && + destBlock->preHeader == NULL && otherPathsPresent (ebbs, destBlock)) + { + + symbol *preHeaderLabel = newiTempLoopHeaderLabel (1); + int i, j; + eBBlock *pBlock; + + /* go thru all block replacing the entryLabel with new label */ + /* till we reach the block , then we insert a new ebblock */ + for (i = 0; i < (ebbi->count); i++) + { + if (ebbs[i] == destBlock) + break; + replaceLabel (ebbs[i], label, preHeaderLabel); + } + + (ebbi->count)++; + + /* if we have stopped at the block , allocate for an extra one */ + + ebbs = Safe_realloc (ebbs, (ebbi->count + 1) * sizeof (eBBlock *)); + ebbi->bbOrder = ebbs; + + ebbs[ebbi->count] = 0; + + /* then move the block down one count */ + pBlock = ebbs[j = i]; + for (i += 1; i < (ebbi->count); i++) + { + eBBlock *xBlock; + + xBlock = ebbs[i]; + ebbs[i] = pBlock; + ebbs[i]->bbnum = i; + pBlock = xBlock; + } + + destBlock->preHeader = ebbs[j] = neweBBlock (); + ebbs[j]->bbnum = j; + ebbs[j]->entryLabel = preHeaderLabel; + ebbs[j]->sch = ebbs[j]->ech = newiCodeLabelGoto (LABEL, preHeaderLabel); + ebbs[j]->sch->filename = destBlock->sch->filename; + ebbs[j]->sch->lineno = destBlock->sch->lineno; + } + } + } + + /* mark the end */ + ebbs[ebbi->count] = NULL; + + return ebbi; +} + +/*-----------------------------------------------------------------*/ +/* replaceSymBySym : - replace operand by operand in blocks */ +/* replaces only left & right in blocks */ +/*-----------------------------------------------------------------*/ +void +replaceSymBySym (set * sset, operand * src, operand * dest) +{ + set *loop; + eBBlock *rBlock; + + /* for all blocks in the set do */ + for (loop = sset; loop; loop = loop->next) + { + iCode *ic; + + rBlock = loop->item; + /* for all instructions in this block do */ + for (ic = rBlock->sch; ic; ic = ic->next) + { + + /* if we find usage */ + if (ic->op == IFX && isOperandEqual (src, IC_COND (ic))) + { + bitVectUnSetBit (OP_USES (IC_COND (ic)), ic->key); + IC_COND (ic) = operandFromOperand (dest); + OP_USES (dest) = bitVectSetBit (OP_USES (dest), ic->key); + continue; + } + + if (isOperandEqual (IC_RIGHT (ic), src)) + { + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + IC_RIGHT (ic) = operandFromOperand (dest); + IC_RIGHT (ic)->isaddr = 0; + OP_USES (dest) = bitVectSetBit (OP_USES (dest), ic->key); + } + + if (isOperandEqual (IC_LEFT (ic), src)) + { + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + if (POINTER_GET (ic) && IS_ITEMP (dest)) + { + IC_LEFT (ic) = operandFromOperand (dest); + IC_LEFT (ic)->isaddr = 1; + } + else + { + IC_LEFT (ic) = operandFromOperand (dest); + IC_LEFT (ic)->isaddr = 0; + } + OP_USES (dest) = bitVectSetBit (OP_USES (dest), ic->key); + } + + /* special case for pointer sets */ + if (POINTER_SET (ic) && isOperandEqual (IC_RESULT (ic), src)) + { + bitVectUnSetBit (OP_USES (IC_RESULT (ic)), ic->key); + IC_RESULT (ic) = operandFromOperand (dest); + IC_RESULT (ic)->isaddr = 1; + OP_USES (dest) = bitVectSetBit (OP_USES (dest), ic->key); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* replaceLabel - replace reference to one label by another */ +/*-----------------------------------------------------------------*/ +void +replaceLabel (eBBlock *ebp, symbol *fromLbl, symbol *toLbl) +{ + iCode *ic; + + if (!ebp) + return; + + for (ic = ebp->sch; ic; ic = ic->next) + { + switch (ic->op) + { + + case GOTO: + if (isSymbolEqual (IC_LABEL (ic), fromLbl)) + IC_LABEL (ic) = toLbl; + break; + + case IFX: + if (IC_TRUE (ic) && isSymbolEqual (IC_TRUE (ic), fromLbl)) + IC_TRUE (ic) = toLbl; + else if (isSymbolEqual (IC_FALSE (ic), fromLbl)) + IC_FALSE (ic) = toLbl; + break; + + case JUMPTABLE: + replaceSetItem (IC_JTLABELS (ic), fromLbl, toLbl); + } + } + + return; + +} + + +/*-----------------------------------------------------------------*/ +/* iCodeFromeBBlock - convert basic block to iCode chain */ +/*-----------------------------------------------------------------*/ +iCode * +iCodeFromeBBlock (eBBlock ** ebbs, int count) +{ + int i = 1; + iCode *ric = ebbs[0]->sch; + iCode *lic = ebbs[0]->ech; + + for (; i < count; i++) + { + if (ebbs[i]->sch == NULL) + continue; + + if (ebbs[i]->noPath && optimize.label4 && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + { + iCode *ic = NULL; + bool foundNonlabel = 0; + ic = ebbs[i]->sch; + do + { + if (ic->op != LABEL) + { + foundNonlabel = 1; + break; + } + if (ic == ebbs[i]->ech) + break; + ic = ic->next; + } + while (ic); + if (foundNonlabel && ic) + { + werrorfl (ic->filename, ic->lineno, W_CODE_UNREACH); + continue; + } + } + + lic->next = ebbs[i]->sch; + lic->next->prev = lic; + lic = ebbs[i]->ech; + + } + + return ric; +} + +/*-----------------------------------------------------------------*/ +/* otherPathsPresent - determines if there is a path from _entry */ +/* to this block in a half constructed set of blocks */ +/*-----------------------------------------------------------------*/ +int +otherPathsPresent (eBBlock ** ebbs, eBBlock * this) +{ + int i; + + /* for all blocks preceding this block */ + for (i = 0; i < this->bbnum; i++) + { + iCode *ic; + + /* if there is a reference to the entry label of this block */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + switch (ic->op) + { + case GOTO: + if (IC_LABEL (ic)->key == this->entryLabel->key) + return 1; + break; + + case IFX: + if (IC_TRUE (ic)) + { + if (IC_TRUE (ic)->key == this->entryLabel->key) + return 1; + } + else if (IC_FALSE (ic)->key == this->entryLabel->key) + return 1; + break; + } + } + } + + /* comes here means we have not found it yet */ + /* in this case check if the previous block */ + /* ends in a goto if it does then we have no */ + /* path else we have a path */ + if (this->bbnum && ebbs[this->bbnum - 1]->ech && ebbs[this->bbnum - 1]->ech->op == GOTO) + return 0; + else + return 1; +} + + +/*-----------------------------------------------------------------*/ +/* freeBBlockData - Deallocate data structures associated with */ +/* the current blocks. They will all be recomputed if the */ +/* iCode chain is divided into blocks again later. */ +/*-----------------------------------------------------------------*/ +void +freeeBBlockData(ebbIndex * ebbi) +{ + int i; + eBBlock ** ebbs = ebbi->bbOrder; + + for (i=0; i < ebbi->count; i++) + { + deleteSet (&ebbs[i]->succList); + deleteSet (&ebbs[i]->predList); + freeBitVect (ebbs[i]->succVect); + freeBitVect (ebbs[i]->domVect); + + freeCSEdata(ebbs[i]); + } +} + diff --git a/src/SDCCBBlock.h b/src/SDCCBBlock.h new file mode 100644 index 0000000..fcd1205 --- /dev/null +++ b/src/SDCCBBlock.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + + SDCCBBlock.h - header file for Basic Blocks + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCBBLOCK_H +#define SDCCBBLOCK_H 1 + +/* definition of a basic block */ +typedef struct eBBlock +{ + int dfnum; /* depth first number */ + int bbnum; /* index into array of numbers */ + int depth; /* loop depth of this block */ + int fSeq; /* sequence number of first iCode */ + int lSeq; /* sequence number of the last iCode */ + unsigned int visited:1; /* visitied flag */ + unsigned int hasFcall:1; /* has a function call */ + unsigned int noPath:1; /* there is no path from _entry to this block */ + unsigned int isLastInLoop:1; /* is the last block in a loop */ + struct eBBlock *isConditionalExitFrom; /* this block ends with a return or goto from a conditional block */ + symbol *entryLabel; /* entry label */ + + iCode *sch; /* pointer to start of code chain */ + iCode *ech; /* pointer to last of code chain */ + + struct eBBlock *preHeader; /* preheader if this is a loop entry */ + struct region *partOfLoop; /* pointer to the loop region this block is part of */ + + /* control flow analysis */ + set *succList; /* list eBBlocks which are successors */ + bitVect *succVect; /* bitVector of successors (index is bbnum) */ + set *predList; /* predecessors of this basic block */ + bitVect *domVect; /* list of nodes this is dominated by (index is bbnum) */ + + /* data flow analysis */ + set *inExprs; /* in coming common expressions */ + set *outExprs; /* out going common expressions */ + set *killedExprs; /* killed common expressions */ + bitVect *inDefs; /* in coming defintions */ + bitVect *outDefs; /* out going defintions */ + bitVect *defSet; /* symbols defined in block */ + bitVect *ldefs; /* local definitions only */ + bitVect *usesDefs; /* which definitions are used in this block */ + bitVect *ptrsSet; /* pointers assigned values in the block */ + bitVect *inPtrsSet; /* in coming pointers assigned values */ + bitVect *ndompset; /* pointers set by non-dominating basic blocks */ + set *addrOf; /* symbols for which addres has been taken in the block */ + bitVect *linds; /* if loop exit this contains defNumbers + for the inductions */ +} +eBBlock; + +typedef struct ebbIndex +{ + int count; /* number of blocks in the index */ + eBBlock **bbOrder; /* blocks in bbnum order */ + eBBlock **dfOrder; /* blocks in dfnum (depth first) order */ +} +ebbIndex; + +typedef struct edge +{ + + eBBlock *from; /* from basic block */ + eBBlock *to; /* to Basic Block */ +} +edge; + + +extern int eBBNum; +extern set *graphEdges; + + +DEFSETFUNC (printEntryLabel); +eBBlock *neweBBlock (); +edge *newEdge (eBBlock *, eBBlock *); +eBBlock *eBBWithEntryLabel (ebbIndex *, const symbol *); +DEFSETFUNC (ifFromIs); +set *edgesTo (eBBlock *); +void remiCodeFromeBBlock (eBBlock *, iCode *); +void addiCodeToeBBlock (eBBlock *, iCode *, iCode *); +ebbIndex *iCodeBreakDown (iCode *); +void replaceSymBySym (set *, operand *, operand *); +iCode *iCodeFromeBBlock (eBBlock **, int); +int otherPathsPresent (eBBlock **, eBBlock *); +void replaceLabel (eBBlock *, symbol *, symbol *); +void dumpEbbsToFileExt (int, ebbIndex *); +void dumpLiveRanges (int, hTab * liveRanges); +void closeDumpFiles (); +void freeeBBlockData(ebbIndex *); + +#endif diff --git a/src/SDCCargs.h b/src/SDCCargs.h new file mode 100644 index 0000000..af36bdf --- /dev/null +++ b/src/SDCCargs.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- + SDCCargs.c - command line arguments handling + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +/** Definition of the structures used by the options parser. The port + may implement one of these for any options it wants parsed + automatically. +*/ +#ifndef SDCCARGS_H +#define SDCCARGS_H + +/** Specifies option argument types. */ +enum cl_opt_arg_type { + CLAT_BOOLEAN = 0, /* has to be zero! */ + CLAT_INTEGER, + CLAT_STRING, + CLAT_SET, + CLAT_ADD_SET +}; + +/** Table of all options supported by all ports. + This table provides: + * A reference for all options. + * An easy way to maintain help for the options. + * Automatic support for setting flags on simple options. +*/ +typedef struct + { + /** The short option character e.g. 'h' for -h. 0 for none. */ + char shortOpt; + /** Long option e.g. "--help". Includes the -- prefix. NULL for + none. */ + const char *longOpt; + /** Pointer to an int that will be incremented every time the + option is encountered. May be NULL. + */ + void *pparameter; + /** Help text to go with this option. May be NULL. */ + const char *help; + /** Option argument type */ + enum cl_opt_arg_type arg_type; + } OPTION; + +char *getStringArg(const char *szStart, char **argv, int *pi, int argc); +long getIntArg(const char *szStart, char **argv, int *pi, int argc); + +#endif diff --git a/src/SDCCasm.c b/src/SDCCasm.c new file mode 100644 index 0000000..73f9e65 --- /dev/null +++ b/src/SDCCasm.c @@ -0,0 +1,623 @@ +/*------------------------------------------------------------------------- + SDCCasm.c - header file for all types of stuff to support different assemblers. + + Copyright (C) 2000, Michael Hope + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* Provides output functions that modify the output string + based on the input tokens and the assembler token mapping + specification loaded. + + Note that the functions below only handle digit format modifiers. + eg %02X is ok, but %lu and %.4u will fail. + + A 'token' is like !blah or %24f and is under the programmers + control. */ + +#include + +#include "common.h" +#include "dbuf_string.h" + +static hTab *_h; + +const char * +FileBaseName (const char *fileFullName) +{ + const char *p; + + if (!fileFullName) + { + return "unknown"; + } + + for (p = fileFullName + strlen (fileFullName) - 1; + p >= fileFullName && (*p != '/' && *p != '\\' && *p != ':'); + --p) + ; + + return p + 1; +} + +void +dbuf_tvprintf (struct dbuf_s *dbuf, const char *format, va_list ap) +{ + /* + Under Linux PPC va_list is a structure instead of a primitive type, + and doesn't like being passed around. This version turns everything + into one function. + + Supports: + !tokens + %[CIFN] - special formats with no argument (ie list isnt touched) + All of the system formats + + This is acheived by expanding the tokens and zero arg formats into + one big format string, which is passed to the native printf. + */ + static int count; + struct dbuf_s tmpDBuf; + const char *noTokens; + const char *sz = format; + const char *begin = NULL; + + /* First pass: expand all of the macros */ + dbuf_init (&tmpDBuf, INITIAL_INLINEASM); + + while (*sz) + { + if (*sz == '!') + { + /* Start of a token. Search until the first + [non alpha, *] and call it a token. */ + const char *t; + struct dbuf_s token; + + if (begin) + { + /* copy what we have until now */ + dbuf_append (&tmpDBuf, begin, sz - begin); + begin = NULL; + } + + dbuf_init (&token, 64); + ++sz; + while (isalpha ((unsigned char) *sz) || *sz == '*') + { + dbuf_append (&token, sz++, 1); + } + /* Now find the token in the token list */ + if ((t = shash_find (_h, dbuf_c_str (&token)))) + { + dbuf_append_str (&tmpDBuf, t); + } + else + { + /* Token not recognized as a valid macro: macro is not expanded */ + dbuf_append_char (&tmpDBuf, '!'); + dbuf_append (&tmpDBuf, dbuf_get_buf (&token), dbuf_get_length (&token)); + } + dbuf_destroy (&token); + } + else + { + if (!begin) + begin = sz; + ++sz; + } + } + + if (begin) + { + /* copy what we have until now */ + dbuf_append (&tmpDBuf, begin, sz - begin); + begin = NULL; + } + + /* Second pass: Expand any macros that we own */ + sz = noTokens = dbuf_detach_c_str (&tmpDBuf); + + /* recycle tmpDBuf */ + dbuf_init (&tmpDBuf, INITIAL_INLINEASM); + + while (*sz) + { + if (*sz == '%') + { + if (begin) + { + /* copy what we have until now */ + dbuf_append (&tmpDBuf, begin, sz - begin); + begin = NULL; + } + + // See if its one that we handle. + ++sz; + switch (*sz) + { + case 'C': + // Code segment name. + dbuf_append_str (&tmpDBuf, CODE_NAME); + ++sz; + break; + + case 'F': + // Source file name. + dbuf_append_str (&tmpDBuf, fullSrcFileName); + ++sz; + break; + + case 'N': + // Current function name. + dbuf_append_str (&tmpDBuf, currFunc->rname); + ++sz; + break; + + case 'I': + // Unique ID. + dbuf_printf (&tmpDBuf, "%u", ++count); + ++sz; + break; + + default: + // Not one of ours. Copy until the end. + dbuf_append_char (&tmpDBuf, '%'); + while (!isalpha ((unsigned char) *sz)) + dbuf_append_char (&tmpDBuf, *sz++); + + dbuf_append_char (&tmpDBuf, *sz++); + break; + } + } + else + { + if (!begin) + begin = sz; + ++sz; + } + } + + if (begin) + { + /* copy what we have until now */ + dbuf_append (&tmpDBuf, begin, sz - begin); + begin = NULL; + } + + dbuf_free (noTokens); + + dbuf_vprintf (dbuf, dbuf_c_str (&tmpDBuf), ap); + + dbuf_destroy (&tmpDBuf); +} + +void +dbuf_tprintf (struct dbuf_s *dbuf, const char *szFormat, ...) +{ + va_list ap; + va_start (ap, szFormat); + dbuf_tvprintf (dbuf, szFormat, ap); + va_end (ap); +} + +void +tfprintf (FILE *fp, const char *szFormat, ...) +{ + va_list ap; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, INITIAL_INLINEASM); + + va_start (ap, szFormat); + dbuf_tvprintf (&dbuf, szFormat, ap); + va_end (ap); + + fwrite (dbuf_get_buf (&dbuf), 1, dbuf_get_length (&dbuf), fp); + dbuf_destroy (&dbuf); +} + +void +asm_addTree (const ASM_MAPPINGS * pMappings) +{ + const ASM_MAPPING *pMap; + + /* Traverse down first */ + if (pMappings->pParent) + asm_addTree (pMappings->pParent); + pMap = pMappings->pMappings; + while (pMap->szKey && pMap->szValue) + { + shash_add (&_h, pMap->szKey, pMap->szValue); + pMap++; + } +} + +/*-----------------------------------------------------------------*/ +/* printILine - return the readable i-code for this ic */ +/*-----------------------------------------------------------------*/ +const char * +printILine (iCode * ic) +{ + struct dbuf_s tmpBuf; + iCodeTable *icTab = getTableEntry (ic->op); + + wassert (icTab); + + dbuf_init (&tmpBuf, 1024); + + if (INLINEASM == ic->op) + dbuf_append_str (&tmpBuf, "inline"); + else + { + /* stuff the temporary file with the readable icode */ + icTab->iCodePrint (&tmpBuf, ic, icTab->printName); + } + + /* null terminate the buffer */ + dbuf_chomp (&tmpBuf); + + return dbuf_detach_c_str (&tmpBuf); +} + +/*-----------------------------------------------------------------*/ +/* skipLine - skip the line from file infp */ +/*-----------------------------------------------------------------*/ +static int +skipLine (FILE * infp) +{ + int c; + static char is_eof = 0; + size_t len = 0; + + if (is_eof) + return 0; + + while ((c = getc (infp)) != '\n' && EOF != c) + ++len; + + if (EOF == c) + { + if (len) + { + /* EOF in the middle of the line */ + is_eof = 1; + return 1; + } + else + return 0; + } + else + return 1; +} + +/*-----------------------------------------------------------------*/ +/* printCLine - return the c-code for this lineno */ +/*-----------------------------------------------------------------*/ +/* int rewinds=0; */ +const char * +printCLine (const char *srcFile, int lineno) +{ + static FILE *inFile = NULL; + static struct dbuf_s line; + static struct dbuf_s lastSrcFile; + static char dbufInitialized = 0; + static int inLineNo = 0; + size_t len; + + if (!dbufInitialized) + { + dbuf_init (&line, 1024); + dbuf_init (&lastSrcFile, PATH_MAX); + dbufInitialized = 1; + } + else + { + /* empty the dynamic buffer */ + dbuf_set_length (&line, 0); + } + + if (inFile) + { + if (strcmp (dbuf_c_str (&lastSrcFile), srcFile) != 0) + { + fclose (inFile); + inFile = NULL; + inLineNo = 0; + dbuf_set_length (&lastSrcFile, 0); + dbuf_append_str (&lastSrcFile, srcFile); + } + } + + if (!inFile) + { + if (!(inFile = fopen (srcFile, "r"))) + { + /* can't open the file: + don't panic, just return the error message */ + dbuf_printf (&line, "ERROR: %s", strerror (errno)); + + return dbuf_detach_c_str (&line); + } + else + { + dbuf_set_length (&lastSrcFile, 0); + dbuf_append_str (&lastSrcFile, srcFile); + } + } + + if (inLineNo > lineno) + { + /* past the lineno: rewind the file pointer */ + rewind (inFile); + inLineNo = 0; + /* rewinds++; */ + } + + /* skip lines until lineno */ + while (inLineNo + 1 < lineno) + { + if (!skipLine (inFile)) + goto err_no_line; + ++inLineNo; + } + + /* get the line */ + if (0 != (len = dbuf_getline (&line, inFile))) + { + const char *inLineString; + + ++inLineNo; + + /* remove the trailing NL */ + dbuf_chomp (&line); + + inLineString = dbuf_detach_c_str (&line); + + /* skip leading spaces */ + while (isspace (*inLineString)) + ++inLineString; + + return inLineString; + } + +err_no_line: + dbuf_printf (&line, "ERROR: no line number %d in file %s", lineno, srcFile); + + return dbuf_detach_c_str (&line); +} + +static const ASM_MAPPING _asxxxx_mapping[] = { + {"labeldef", "%s::"}, + {"slabeldef", "%s:"}, + {"tlabeldef", "%05d$:"}, + {"tlabel", "%05d$"}, + {"immed", "#"}, + {"zero", "#0x00"}, + {"one", "#0x01"}, + {"area", ".area %s"}, + {"areacode", ".area %s"}, + {"areadata", ".area %s"}, + {"areahome", ".area %s"}, + {"ascii", ".ascii \"%s\""}, + {"ds", ".ds %d"}, + {"db", ".db"}, + {"dbs", ".db %s"}, + {"dw", ".dw"}, + {"dws", ".dw %s"}, + {"constbyte", "0x%02x"}, + {"constword", "0x%04x"}, + {"immedword", "#0x%04x"}, + {"immedbyte", "#0x%02x"}, + {"hashedstr", "#%s"}, + {"lsbimmeds", "#<(%s)"}, + {"msbimmeds", "#>(%s)"}, + {"module", ".module %s"}, + {"global", ".globl %s"}, + {"fileprelude", ""}, + {"functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------"}, + {"functionlabeldef", "%s:"}, + {"globalfunctionlabeldef", "%s::"}, + {"bankimmeds", "0 ; PENDING: bank support"}, + {"los", "(%s & 0xFF)"}, + {"his", "(%s >> 8)"}, + {"hihis", "(%s >> 16)"}, + {"hihihis", "(%s >> 24)"}, + {"lod", "(%d & 0xFF)"}, + {"hid", "(%d >> 8)"}, + {"hihid", "(%d >> 16)"}, + {"hihihid", "(%d >> 24)"}, + {"lol", "(%05d$ & 0xFF)"}, + {"hil", "(%05d$ >> 8)"}, + {"hihil", "(%05d$ >> 16)"}, + {"hihihil", "(%05d$ >> 24)"}, + {"equ", "="}, + {"org", ".org 0x%04X"}, + {NULL, NULL} +}; + +static const ASM_MAPPING _asxxxx_smallpdk_mapping[] = { + {"labeldef", "%s::"}, + {"slabeldef", "%s:"}, + {"tlabeldef", "%05d$:"}, + {"tlabel", "%05d$"}, + {"immed", "#"}, + {"zero", "#0x00"}, + {"one", "#0x01"}, + {"area", ".area %s"}, + {"areacode", ".area %s"}, + {"areadata", ".area %s"}, + {"areahome", ".area %s"}, + {"ascii", ".ascii \"%s\""}, + {"ds", ".ds %d"}, + {"db", "ret"}, + {"dbs", "ret %s"}, + {"dw", ".dw"}, + {"dws", ".dw %s"}, + {"constbyte", "#0x%02x"}, + {"constword", "0x%04x"}, + {"immedword", "#0x%04x"}, + {"immedbyte", "#0x%02x"}, + {"hashedstr", "#%s"}, + {"lsbimmeds", "#<(%s)"}, + {"msbimmeds", "#>(%s)"}, + {"module", ".module %s"}, + {"global", ".globl %s"}, + {"fileprelude", ""}, + {"functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------"}, + {"functionlabeldef", "%s:"}, + {"globalfunctionlabeldef", "%s::"}, + {"bankimmeds", "0 ; PENDING: bank support"}, + {"los", "(%s & 0xFF)"}, + {"his", "(%s >> 8)"}, + {"hihis", "(%s >> 16)"}, + {"hihihis", "(%s >> 24)"}, + {"lod", "(%d & 0xFF)"}, + {"hid", "(%d >> 8)"}, + {"hihid", "(%d >> 16)"}, + {"hihihid", "(%d >> 24)"}, + {"lol", "(%05d$ & 0xFF)"}, + {"hil", "(%05d$ >> 8)"}, + {"hihil", "(%05d$ >> 16)"}, + {"hihihil", "(%05d$ >> 24)"}, + {"equ", "="}, + {"org", ".org 0x%04X"}, + {NULL, NULL} +}; + +#if 0 +/* not used */ +static const ASM_MAPPING _gas_mapping[] = { + {"labeldef", "%s::"}, + {"slabeldef", "%s:"}, + {"tlabeldef", "%05d$:"}, + {"tlabel", "%05d$"}, + {"immed", "#"}, + {"zero", "#0x00"}, + {"one", "#0x01"}, + {"area", ".section %s"}, + {"areacode", ".section %s"}, + {"areadata", ".section %s"}, + {"areahome", ".section %s"}, + {"ascii", ".ascii \"%s\""}, + {"ds", ".ds %d"}, + {"db", ".db"}, + {"dbs", ".db %s"}, + {"dw", ".dw"}, + {"dws", ".dw %s"}, + {"constbyte", "0x%02X"}, + {"constword", "0x%04X"}, + {"immedword", "#0x%04X"}, + {"immedbyte", "#0x%02X"}, + {"hashedstr", "#%s"}, + {"lsbimmeds", "#<%s"}, + {"msbimmeds", "#>%s"}, + {"module", ".file \"%s.c\""}, + {"global", ".globl %s"}, + {"extern", ".globl %s"}, + {"fileprelude", ""}, + {"functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------"}, + {"functionlabeldef", "%s:"}, + {"globalfunctionlabeldef", "%s::"}, + {"bankimmeds", "0 ; PENDING: bank support"}, + {NULL, NULL} +}; +#endif + +static const ASM_MAPPING _a390_mapping[] = { + {"labeldef", "%s:"}, + {"slabeldef", "%s:"}, + {"tlabeldef", "L%05d:"}, + {"tlabel", "L%05d"}, + {"immed", "#"}, + {"zero", "#0"}, + {"one", "#1"}, + {"area", "; SECTION NOT SUPPORTED"}, + {"areacode", "; SECTION NOT SUPPORTED"}, + {"areadata", "; SECTION NOT SUPPORTED"}, + {"areahome", "; SECTION NOT SUPPORTED"}, + {"ascii", "db \"%s\""}, + {"ds", "; STORAGE NOT SUPPORTED"}, + {"db", "db"}, + {"dbs", "db \"%s\""}, + {"dw", "dw"}, + {"dws", "dw %s"}, + {"constbyte", "0%02xh"}, + {"constword", "0%04xh"}, + {"immedword", "#0%04Xh"}, + {"immedbyte", "#0%02Xh"}, + {"hashedstr", "#%s"}, + {"lsbimmeds", "#<%s"}, + {"msbimmeds", "#>%s"}, + {"module", "; .file \"%s.c\""}, + {"global", "; .globl %s"}, + {"fileprelude", ""}, + {"functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------"}, + {"functionlabeldef", "%s:"}, + {"globalfunctionlabeldef", "%s::"}, + {"bankimmeds", "0 ; PENDING: bank support"}, + {"los", "(%s & 0FFh)"}, + {"his", "((%s / 256) & 0FFh)"}, + {"hihis", "((%s / 65536) & 0FFh)"}, + {"hihihis", "((%s / 16777216) & 0FFh)"}, + {"lod", "(%d & 0FFh)"}, + {"hid", "((%d / 256) & 0FFh)"}, + {"hihid", "((%d / 65536) & 0FFh)"}, + {"hihihid", "((%d / 16777216) & 0FFh)"}, + {"lol", "(L%05d & 0FFh)"}, + {"hil", "((L%05d / 256) & 0FFh)"}, + {"hihil", "((L%05d / 65536) & 0FFh)"}, + {"hihihil", "((L%09d / 16777216) & 0FFh)"}, + {"equ", " equ"}, + {"org", ".org 0x%04X"}, + {NULL, NULL} +}; + +const ASM_MAPPINGS asm_asxxxx_mapping = { + NULL, + _asxxxx_mapping +}; + +const ASM_MAPPINGS asm_asxxxx_smallpdk_mapping = { + NULL, + _asxxxx_smallpdk_mapping +}; + +#if 0 +/* not used */ +const ASM_MAPPINGS asm_gas_mapping = { + NULL, + _gas_mapping +}; +#endif + +const ASM_MAPPINGS asm_a390_mapping = { + NULL, + _a390_mapping +}; + diff --git a/src/SDCCasm.h b/src/SDCCasm.h new file mode 100644 index 0000000..4bad706 --- /dev/null +++ b/src/SDCCasm.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + SDCCasm.h - header file for all types of stuff to support different assemblers. + + Copyright (C) 2000, Michael Hope + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCASM_H +#define SDCCASM_H + +#include "dbuf.h" + +void tfprintf (FILE * fp, const char *szFormat, ...); +void dbuf_tprintf (struct dbuf_s *dbuf, const char *szFormat, ...); +void dbuf_tvprintf (struct dbuf_s *dbuf, const char *szFormat, va_list ap); + +typedef struct +{ + const char *szKey; + const char *szValue; +} +ASM_MAPPING; + +typedef struct _ASM_MAPPINGS ASM_MAPPINGS; + +/* PENDING: could include the peephole rules here as well. + */ +struct _ASM_MAPPINGS +{ + const ASM_MAPPINGS *pParent; + const ASM_MAPPING *pMappings; +}; + +/* The default asxxxx token mapping. + */ +extern const ASM_MAPPINGS asm_asxxxx_mapping; + +/* The default mapping for small (<16 bit word) Padauk architectures. + */ +extern const ASM_MAPPINGS asm_asxxxx_smallpdk_mapping; +#if 0 +/* not used */ +extern const ASM_MAPPINGS asm_gas_mapping; +#endif +extern const ASM_MAPPINGS asm_a390_mapping; + +/** Last entry has szKey = NULL. + */ +void asm_addTree (const ASM_MAPPINGS * pMappings); + +const char *FileBaseName (const char *fileFullName); + +const char *printILine (iCode * ic); +const char *printCLine (const char *srcFile, int lineno); +#endif diff --git a/src/SDCCast.c b/src/SDCCast.c new file mode 100644 index 0000000..36f7e26 --- /dev/null +++ b/src/SDCCast.c @@ -0,0 +1,8612 @@ +/*------------------------------------------------------------------------- + SDCCast.c - source file for parser support & all ast related routines + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#define DEBUG_CF(x) /* puts(x); */ + +#include + +#include "common.h" +#include "dbuf_string.h" +#include "SDCCbtree.h" +#include "SDCCset.h" + +int currLineno = 0; +set *astList = NULL; +set *operKeyReset = NULL; +ast *staticAutos = NULL; +int labelKey = 1; +static struct +{ + int count; /* number of inline functions inserted */ + symbol *retsym; /* variable for inlined function return value */ + symbol *retlab; /* label ending inlined function (virtual return) */ +} inlineState; + +#define LRVAL(x) x->left->rvalue +#define RRVAL(x) x->right->rvalue +#define TRVAL(x) x->rvalue +#define LLVAL(x) x->left->lvalue +#define RLVAL(x) x->right->lvalue +#define TLVAL(x) x->lvalue +#define RTYPE(x) x->right->ftype +#define RETYPE(x) x->right->etype +#define LTYPE(x) x->left->ftype +#define LETYPE(x) x->left->etype +#define TTYPE(x) x->ftype +#define TETYPE(x) x->etype +#define ALLOCATE 1 +#define DEALLOCATE 2 + +#define IS_AND(ex) (ex->type == EX_OP && ex->opval.op == AND_OP ) +#define IS_OR(ex) (ex->type == EX_OP && ex->opval.op == OR_OP ) +#define IS_NOT(ex) (ex->type == EX_OP && ex->opval.op == '!' ) +#define IS_ANDORNOT(ex) (IS_AND(ex) || IS_OR(ex) || IS_NOT(ex)) +#define IS_IFX(ex) (ex->type == EX_OP && ex->opval.op == IFX ) +#define IS_LT(ex) (ex->type == EX_OP && ex->opval.op == '<' ) +#define IS_GT(ex) (ex->type == EX_OP && ex->opval.op == '>') + +int noLineno = 0; +int noAlloc = 0; +symbol *currFunc = NULL; +static ast *createIval (ast *, sym_link *, initList *, ast *, ast *, int); +static ast *createIvalCharPtr (ast *, sym_link *, ast *, ast *); +static ast *optimizeCompare (ast *); +ast *optimizeRRCRLC (ast *); +ast *optimizeSWAP (ast *); +ast *optimizeGetHbit (ast *, RESULT_TYPE); +ast *optimizeGetAbit (ast *, RESULT_TYPE); +ast *optimizeGetByte (ast *, RESULT_TYPE); +ast *optimizeGetWord (ast *, RESULT_TYPE); +static ast *backPatchLabels (ast *, symbol *, symbol *); +static void copyAstLoc (ast *, ast *); +void PA (ast * t); +int inInitMode = 0; +memmap *GcurMemmap = NULL; /* points to the memmap that's currently active */ +struct dbuf_s *codeOutBuf; +extern int inCriticalFunction; +extern int inCriticalBlock; + +int +ptt (ast * tree) +{ + printTypeChain (tree->ftype, stdout); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* newAst - creates a fresh node for an expression tree */ +/*-----------------------------------------------------------------*/ +static ast * +newAst_ (unsigned type) +{ + ast *ex; + + ex = Safe_alloc (sizeof (ast)); + + ex->type = type; + ex->lineno = (noLineno ? 0 : lexLineno); + ex->filename = lexFilename; + ex->level = NestLevel; + ex->block = currBlockno; + ex->initMode = inInitMode; + ex->seqPoint = seqPointNo; + return ex; +} + +ast * +newAst_VALUE (value * val) +{ + ast *ex = newAst_ (EX_VALUE); + ex->opval.val = val; + return ex; +} + +ast * +newAst_OP (unsigned op) +{ + ast *ex = newAst_ (EX_OP); + ex->opval.op = op; + return ex; +} + +ast * +newAst_LINK (sym_link * val) +{ + ast *ex = newAst_ (EX_LINK); + ex->opval.lnk = val; + return ex; +} + +/*-----------------------------------------------------------------*/ +/* newNode - creates a new node */ +/*-----------------------------------------------------------------*/ +ast * +newNode (long op, ast * left, ast * right) +{ + ast *ex; + + ex = newAst_OP (op); + ex->left = left; + ex->right = right; + + return ex; +} + +/*-----------------------------------------------------------------*/ +/* newIfxNode - creates a new Ifx Node */ +/*-----------------------------------------------------------------*/ +ast * +newIfxNode (ast * condAst, symbol * trueLabel, symbol * falseLabel) +{ + ast *ifxNode; + + /* if this is a literal then we already know the result */ + if (condAst->etype && IS_LITERAL (condAst->etype) && IS_AST_VALUE (condAst)) + { + /* then depending on the expression value */ + if (floatFromVal (condAst->opval.val)) + ifxNode = newNode (GOTO, newAst_VALUE (symbolVal (trueLabel)), NULL); + else + ifxNode = newNode (GOTO, newAst_VALUE (symbolVal (falseLabel)), NULL); + } + else + { + ifxNode = newNode (IFX, condAst, NULL); + ifxNode->trueLabel = trueLabel; + ifxNode->falseLabel = falseLabel; + } + + return ifxNode; +} + +/*-----------------------------------------------------------------*/ +/* copyAstValues - copies value portion of ast if needed */ +/*-----------------------------------------------------------------*/ +void +copyAstValues (ast * dest, ast * src) +{ + switch (src->opval.op) + { + case BLOCK: + dest->values.sym = copySymbolChain (src->values.sym); + break; + + case SWITCH: + dest->values.switchVals.swVals = copyValueChain (src->values.switchVals.swVals); + dest->values.switchVals.swDefault = src->values.switchVals.swDefault; + dest->values.switchVals.swNum = src->values.switchVals.swNum; + dest->values.switchVals.swSuffix = src->values.switchVals.swSuffix; + break; + + case INLINEASM: + dest->values.inlineasm = Safe_strdup (src->values.inlineasm); + break; + + case ARRAYINIT: + dest->values.constlist = copyLiteralList (src->values.constlist); + break; + + case FOR: + AST_FOR (dest, trueLabel) = copySymbol (AST_FOR (src, trueLabel)); + AST_FOR (dest, continueLabel) = copySymbol (AST_FOR (src, continueLabel)); + AST_FOR (dest, falseLabel) = copySymbol (AST_FOR (src, falseLabel)); + AST_FOR (dest, condLabel) = copySymbol (AST_FOR (src, condLabel)); + AST_FOR (dest, initExpr) = copyAst (AST_FOR (src, initExpr)); + AST_FOR (dest, condExpr) = copyAst (AST_FOR (src, condExpr)); + AST_FOR (dest, loopExpr) = copyAst (AST_FOR (src, loopExpr)); + break; + + case CAST: + dest->values.cast.literalFromCast = src->values.cast.literalFromCast; + dest->values.cast.removedCast = src->values.cast.removedCast; + dest->values.cast.implicitCast = src->values.cast.implicitCast; + } +} + +/*-----------------------------------------------------------------*/ +/* copyAst - makes a copy of a given astession */ +/*-----------------------------------------------------------------*/ +ast * +copyAst (ast * src) +{ + ast *dest = NULL; + static long level = -1; + static set *pset; + + if (!src) + return NULL; + + if (++level == 0) + if ((pset = newSet ()) == NULL) + goto exit_1; + + /* check if it is a infinate recursive call */ + if (isinSet (pset, src)) + goto exit_1; + else + addSet (&pset, src); + + dest = Safe_alloc (sizeof (ast)); + dest->type = src->type; + dest->filename = src->filename; + dest->lineno = src->lineno; + dest->level = src->level; + dest->funcName = src->funcName; + dest->reversed = src->reversed; + + if (src->ftype) + dest->etype = getSpec (dest->ftype = copyLinkChain (src->ftype)); + + /* if this is a leaf */ + /* if value */ + if (src->type == EX_VALUE) + { + dest->opval.val = copyValue (src->opval.val); + goto exit; + } + + /* if link */ + if (src->type == EX_LINK) + { + dest->opval.lnk = copyLinkChain (src->opval.lnk); + goto exit; + } + + dest->opval.op = src->opval.op; + + /* if this is a node that has special values */ + copyAstValues (dest, src); + + dest->trueLabel = copySymbol (src->trueLabel); + dest->falseLabel = copySymbol (src->falseLabel); + dest->left = copyAst (src->left); + dest->right = copyAst (src->right); + +exit: + if (pset != NULL && isinSet (pset, src)) + deleteSetItem (&pset, src); +exit_1: + if (level-- == 0) + if (pset != NULL) + deleteSet (&pset); + return dest; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* removeIncDecOps: remove for side effects in *_ASSIGN's */ +/* "*s++ += 3" -> "*s++ = *s++ + 3" */ +/*-----------------------------------------------------------------*/ +ast * +removeIncDecOps (ast * tree) +{ + + // traverse the tree and remove inc/dec ops + + if (!tree) + return NULL; + + if (tree->type == EX_OP && (tree->opval.op == INC_OP || tree->opval.op == DEC_OP)) + { + if (tree->left) + tree = tree->left; + else + tree = tree->right; + } + + tree->left = removeIncDecOps (tree->left); + tree->right = removeIncDecOps (tree->right); + + return tree; +} + +/*-----------------------------------------------------------------*/ +/* removePreIncDecOps: remove for side effects in *_ASSIGN's */ +/* "*++s += 3" -> "*++s = *++s + 3" */ +/*-----------------------------------------------------------------*/ +ast * +removePreIncDecOps (ast * tree) +{ + + // traverse the tree and remove pre-inc/dec ops + + if (!tree) + return NULL; + + if (tree->type == EX_OP && (tree->opval.op == INC_OP || tree->opval.op == DEC_OP)) + { + if (tree->right) + tree = tree->right; + } + + tree->left = removePreIncDecOps (tree->left); + tree->right = removePreIncDecOps (tree->right); + + return tree; +} + +/*-----------------------------------------------------------------*/ +/* removePostIncDecOps: remove for side effects in *_ASSIGN's */ +/* "*s++ += 3" -> "*s++ = *s++ + 3" */ +/*-----------------------------------------------------------------*/ +ast * +removePostIncDecOps (ast * tree) +{ + + // traverse the tree and remove pre-inc/dec ops + + if (!tree) + return NULL; + + if (tree->type == EX_OP && (tree->opval.op == INC_OP || tree->opval.op == DEC_OP)) + { + if (tree->left) + tree = tree->left; + } + + tree->left = removePostIncDecOps (tree->left); + tree->right = removePostIncDecOps (tree->right); + + return tree; +} +#endif + +/*-----------------------------------------------------------------*/ +/* replaceAstWithTemporary: Replace the AST pointed to by the arg */ +/* with a reference to a new temporary variable. Returns*/ +/* an AST which assigns the original value to the */ +/* temporary. */ +/*-----------------------------------------------------------------*/ +static ast * +replaceAstWithTemporary (ast ** treeptr) +{ + symbol *sym = newSymbol (genSymName (NestLevel), NestLevel); + ast *tempvar; + + /* Tell gatherImplicitVariables() to automatically give the + symbol the correct type */ + sym->infertype = 1; + sym->type = NULL; + sym->etype = NULL; + + tempvar = newNode ('=', newAst_VALUE (symbolVal (sym)), *treeptr); + *treeptr = newAst_VALUE (symbolVal (sym)); + + addSymChain (&sym); + + return tempvar; +} + +/*-----------------------------------------------------------------*/ +/* createRMW: Create a read-modify-write expression, using a */ +/* temporary variable if necessary to avoid duplicating */ +/* any side effects, for use in e.g. */ +/* foo()->count += 5; becomes */ +/* tmp = foo(); tmp->count = tmp->count + 5; */ +/*-----------------------------------------------------------------*/ +ast * +createRMW (ast * target, unsigned op, ast * operand) +{ + ast *readval, *writeval; + ast *tempvar1 = NULL; + ast *tempvar2 = NULL; + ast *result; + + if (!target || !operand) + { + return NULL; + } + + /* we need to create two copies of target: one to read from and + one to write to. but we need to do this without duplicating + any side effects that may be contained in the tree. */ + + if (IS_AST_OP (target)) + { + /* if this is a dereference, put the referenced item in the temporary */ + if (IS_DEREF_OP (target)) + { + /* create a new temporary containing the item being dereferenced */ + if (hasSEFcalls (target->left)) + tempvar1 = replaceAstWithTemporary (&(target->left)); + } + else if (target->opval.op == '[') + { + /* Array access is similar, but we have to avoid side effects in + both values [WIML: Why not transform a[b] to *(a+b) in parser?] */ + if (hasSEFcalls (target->left)) + tempvar1 = replaceAstWithTemporary (&(target->left)); + if (hasSEFcalls (target->right)) + tempvar2 = replaceAstWithTemporary (&(target->right)); + } + else if ((target->opval.op == INC_OP) || (target->opval.op == DEC_OP)) + { + /* illegal pre/post-increment/decrement */ + werrorfl (target->filename, target->lineno, E_LVALUE_REQUIRED, "="); + } + else + { + /* we would have to handle '.', but it is not generated any more */ + wassertl (target->opval.op != '.', "obsolete opcode in tree"); + + /* no other kinds of ASTs are lvalues and can contain side effects */ + } + } + + readval = target; + writeval = copyAst (target); + + result = newNode ('=', writeval, newNode (op, readval, operand)); + if (tempvar2) + result = newNode (',', tempvar2, result); + if (tempvar1) + result = newNode (',', tempvar1, result); + + return result; +} + +/*-----------------------------------------------------------------*/ +/* hasSEFcalls - returns TRUE if tree has a function call, */ +/* inc/decrement, or other side effect */ +/*-----------------------------------------------------------------*/ +bool +hasSEFcalls (ast * tree) +{ + if (!tree) + return FALSE; + + if (tree->type == EX_OP && + (tree->opval.op == CALL || + tree->opval.op == PCALL || tree->opval.op == '=' || tree->opval.op == INC_OP || tree->opval.op == DEC_OP)) + return TRUE; + + if (astHasVolatile(tree)) + return TRUE; + + return (hasSEFcalls (tree->left) | hasSEFcalls (tree->right)); +} + +/*-----------------------------------------------------------------*/ +/* isAstEqual - compares two asts & returns 1 if they are equal */ +/*-----------------------------------------------------------------*/ +static int +isAstEqual (ast * t1, ast * t2) +{ + if (!t1 && !t2) + return 1; + + if (!t1 || !t2) + return 0; + + /* match type */ + if (t1->type != t2->type) + return 0; + + switch (t1->type) + { + case EX_OP: + if (t1->opval.op != t2->opval.op) + return 0; + return (isAstEqual (t1->left, t2->left) && isAstEqual (t1->right, t2->right)); + break; + + case EX_VALUE: + if (t1->opval.val->sym) + { + if (!t2->opval.val->sym) + return 0; + else + return isSymbolEqual (t1->opval.val->sym, t2->opval.val->sym); + } + else + { + if (t2->opval.val->sym) + return 0; + else + return (floatFromVal (t1->opval.val) == floatFromVal (t2->opval.val)); + } + break; + + /* only compare these two types */ + default: + return 0; + } +} + +/*-----------------------------------------------------------------*/ +/* resolveSymbols - resolve symbols from the symbol table */ +/*-----------------------------------------------------------------*/ +ast * +resolveSymbols (ast * tree) +{ + /* walk the entire tree and check for values */ + /* with symbols if we find one then replace */ + /* symbol with that from the symbol table */ + + if (tree == NULL) + return tree; + +#if 0 + /* print the line */ + /* if not block & function */ + if (tree->type == EX_OP && (tree->opval.op != FUNCTION && tree->opval.op != BLOCK && tree->opval.op != NULLOP)) + { + filename = tree->filename; + lineno = tree->lineno; + } +#endif + + /* make sure we resolve the true & false labels for ifx */ + if (tree->type == EX_OP && tree->opval.op == IFX) + { + symbol *csym; + + if (tree->trueLabel) + { + if ((csym = findSym (LabelTab, tree->trueLabel, tree->trueLabel->name))) + tree->trueLabel = csym; + else + werrorfl (tree->filename, tree->lineno, E_LABEL_UNDEF, tree->trueLabel->name); + } + + if (tree->falseLabel) + { + if ((csym = findSym (LabelTab, tree->falseLabel, tree->falseLabel->name))) + tree->falseLabel = csym; + else + werrorfl (tree->filename, tree->lineno, E_LABEL_UNDEF, tree->falseLabel->name); + } + } + + if (tree->type == EX_OP && tree->opval.op == FOR) + { + symbol *csym; + + if (AST_FOR (tree, trueLabel)) + { + if ((csym = findSym (LabelTab, AST_FOR (tree, trueLabel), AST_FOR (tree, trueLabel)->name))) + AST_FOR (tree, trueLabel) = csym; + } + if (AST_FOR (tree, falseLabel)) + { + if ((csym = findSym (LabelTab, AST_FOR (tree, falseLabel), AST_FOR (tree, falseLabel)->name))) + AST_FOR (tree, falseLabel) = csym; + } + if (AST_FOR (tree, continueLabel)) + { + if ((csym = findSym (LabelTab, AST_FOR (tree, continueLabel), AST_FOR (tree, continueLabel)->name))) + AST_FOR (tree, continueLabel) = csym; + } + if (AST_FOR (tree, condLabel)) + { + if ((csym = findSym (LabelTab, AST_FOR (tree, condLabel), AST_FOR (tree, condLabel)->name))) + AST_FOR (tree, condLabel) = csym; + } + AST_FOR (tree, initExpr) = resolveSymbols (AST_FOR (tree, initExpr)); + AST_FOR (tree, condExpr) = resolveSymbols (AST_FOR (tree, condExpr)); + AST_FOR (tree, loopExpr) = resolveSymbols (AST_FOR (tree, loopExpr)); + } + + /* if this is a label resolve it from the labelTab */ + if (IS_AST_VALUE (tree) && tree->opval.val->sym && tree->opval.val->sym->islbl) + { + symbol *csym = findSym (LabelTab, tree->opval.val->sym, + tree->opval.val->sym->name); + + if (!csym) + werrorfl (tree->filename, tree->lineno, E_LABEL_UNDEF, tree->opval.val->sym->name); + else + tree->opval.val->sym = csym; + + goto resolveChildren; + } + + /* do only for leafs */ + if (IS_AST_VALUE (tree) && tree->opval.val->sym && !tree->opval.val->sym->implicit) + { + symbol *csym = findSymWithLevel (SymbolTab, tree->opval.val->sym); + + /* if found in the symbol table & they are not the same */ + if (csym && tree->opval.val->sym != csym) + { + tree->opval.val->sym = csym; + tree->opval.val->type = csym->type; + tree->opval.val->etype = csym->etype; + if (*csym->rname) + { + SNPRINTF (tree->opval.val->name, sizeof (tree->opval.val->name), "%s", csym->rname); + } + else + { + SNPRINTF (tree->opval.val->name, sizeof (tree->opval.val->name), "_%s", csym->name); + } + } + + /* if not found in the symbol table */ + /* mark it as undefined & assume it */ + /* is an integer in data space */ + if (!csym && !tree->opval.val->sym->implicit && !tree->opval.val->type) + { + /* if this is a function name then */ + /* mark it as returning an int */ + if (tree->funcName) + { + tree->opval.val->sym->type = newLink (DECLARATOR); + DCL_TYPE (tree->opval.val->sym->type) = FUNCTION; + tree->opval.val->sym->type->next = tree->opval.val->sym->etype = newIntLink (); + tree->opval.val->etype = tree->opval.val->etype; + tree->opval.val->type = tree->opval.val->sym->type; + werrorfl (tree->filename, tree->lineno, W_IMPLICIT_FUNC, tree->opval.val->sym->name); + //tree->opval.val->sym->undefined = 1; + allocVariables (tree->opval.val->sym); + } + else + { + tree->opval.val->sym->undefined = 1; + tree->opval.val->type = tree->opval.val->etype = newIntLink (); + tree->opval.val->sym->type = tree->opval.val->sym->etype = newIntLink (); + } + } + } + + /* If entering a block with symbols defined, mark the symbols in-scope */ + /* before continuing down the tree, and mark them out-of-scope again */ + /* on the way back up */ + if (tree->type == EX_OP && tree->opval.op == BLOCK && tree->values.sym) + { + symbol * sym = tree->values.sym; + while (sym) + { + sym->isinscope = 1; + sym = sym->next; + } + resolveSymbols (tree->left); + resolveSymbols (tree->right); + sym = tree->values.sym; + while (sym) + { + sym->isinscope = 0; + sym = sym->next; + } + return tree; + } + +resolveChildren: + resolveSymbols (tree->left); + resolveSymbols (tree->right); + + return tree; +} + +/*------------------------------------------------------------------------*/ +/* setAstFileLine - walks a ast tree & sets the file name and line number */ +/*------------------------------------------------------------------------*/ +int +setAstFileLine (ast * tree, char *filename, int lineno) +{ + if (!tree) + return 0; + + tree->filename = filename; + tree->lineno = lineno; + setAstFileLine (tree->left, filename, lineno); + setAstFileLine (tree->right, filename, lineno); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* funcOfType :- function of type with name */ +/*-----------------------------------------------------------------*/ +symbol * +funcOfType (const char *name, sym_link * type, sym_link * argType, int nArgs, int rent) +{ + symbol *sym; + /* create the symbol */ + sym = newSymbol (name, 0); + + /* setup return value */ + sym->type = newLink (DECLARATOR); + DCL_TYPE (sym->type) = FUNCTION; + sym->type->next = copyLinkChain (type); + sym->etype = getSpec (sym->type); + FUNC_ISREENT (sym->type) = rent ? 1 : 0; + FUNC_NONBANKED (sym->type) = 1; + + /* if arguments required */ + if (nArgs) + { + value *args; + args = FUNC_ARGS (sym->type) = newValue (); + + while (nArgs--) + { + args->type = copyLinkChain (argType); + args->etype = getSpec (args->type); + SPEC_EXTR (args->etype) = 1; + if (!nArgs) + break; + args = args->next = newValue (); + } + } + + /* save it */ + addSymChain (&sym); + sym->cdef = 1; + allocVariables (sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* funcOfTypeVarg :- function of type with name and argtype */ +/*-----------------------------------------------------------------*/ +symbol * +funcOfTypeVarg (const char *name, const char *rtype, int nArgs, const char **atypes) +{ + symbol *sym; + int i; + /* create the symbol */ + sym = newSymbol (name, 0); + + /* setup return value */ + sym->type = newLink (DECLARATOR); + DCL_TYPE (sym->type) = FUNCTION; + sym->type->next = typeFromStr (rtype); + sym->etype = getSpec (sym->type); + + /* if arguments required */ + if (nArgs) + { + value *args; + args = FUNC_ARGS (sym->type) = newValue (); + + for (i = 0; i < nArgs; i++) + { + args->type = typeFromStr (atypes[i]); + args->etype = getSpec (args->type); + SPEC_EXTR (args->etype) = 1; + if ((i + 1) == nArgs) + break; + args = args->next = newValue (); + } + } + + /* save it */ + addSymChain (&sym); + sym->cdef = 1; + allocVariables (sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* reverseParms - will reverse a parameter tree */ +/*-----------------------------------------------------------------*/ +static void +reverseParms (ast * ptree, int r) +{ + ast *ttree; + if (!ptree) + return; + + /* top down if we find a nonParm tree then quit */ + if (ptree->type == EX_OP && ptree->opval.op == PARAM && ptree->reversed != r) + { + /* The various functions expect the parameter tree to be right heavy. */ + /* Rotate the tree to be left heavy so that after reversal it is */ + /* right heavy again. */ + while ((ttree = ptree->right) && ttree->type == EX_OP && ttree->opval.op == PARAM) + { + ptree->right = ttree->right; + ttree->right = ttree->left; + ttree->left = ptree->left; + ptree->left = ttree; + } + + /* Now reverse */ + ttree = ptree->left; + ptree->left = ptree->right; + ptree->right = ttree; + ptree->reversed = r; + reverseParms (ptree->left, r); + reverseParms (ptree->right, r); + } + + return; +} + +/*-----------------------------------------------------------------*/ +/* processParms - makes sure the parameters are okay and do some */ +/* processing with them */ +/*-----------------------------------------------------------------*/ +static int +processParms (ast * func, value * defParm, ast ** actParm, int *parmNumber, /* unused, although updated */ + bool rightmost) +{ + RESULT_TYPE resultType; + sym_link *functype; + + /* if none of them exist */ + if (!defParm && !*actParm) + return 0; + + if (defParm) + { + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "processParms: %s ", defParm->name); + } + /* make sure the type is complete and sane */ + checkTypeSanity (defParm->etype, defParm->name); + } + + if (IS_FUNCPTR (func->ftype)) + functype = func->ftype->next; + else + functype = func->ftype; + + /* if defined parameters ended but actual parameters */ + /* exist and this is not defined as a variable arg */ + if (!defParm && *actParm && !IFFUNC_HASVARARGS (functype)) + { + werror (E_TOO_MANY_PARMS); + return 1; + } + + /* if defined parameters present but no actual parameters */ + if (defParm && !*actParm) + { + werror (E_TOO_FEW_PARMS); + return 1; + } + + /* if this is a PARAM node then match left & right */ + if (IS_AST_PARAM (*actParm)) + { + (*actParm)->decorated = 1; + if ((*actParm)->reversed) + { + return (processParms (func, defParm, &(*actParm)->right, parmNumber, FALSE) || + processParms (func, defParm ? defParm->next : NULL, &(*actParm)->left, parmNumber, rightmost)); + } + else + { + return (processParms (func, defParm, &(*actParm)->left, parmNumber, FALSE) || + processParms (func, defParm ? defParm->next : NULL, &(*actParm)->right, parmNumber, rightmost)); + } + } + else if (defParm) /* not vararg */ + { + /* If we have found a value node by following only right-hand links, + * then we know that there are no more values after us. + * + * Therefore, if there are more defined parameters, the caller didn't + * supply enough. + */ + if (rightmost && defParm->next) + { + werror (E_TOO_FEW_PARMS); + return 1; + } + } + + /* decorate parameter */ + resultType = defParm ? getResultTypeFromType (defParm->type) : RESULT_TYPE_NONE; + *actParm = decorateType (*actParm, resultType); + + if (IS_VOID ((*actParm)->ftype)) + { + werror (E_VOID_VALUE_USED); + return 1; + } + + /* If this is a varargs function... */ + if (!defParm && *actParm && IFFUNC_HASVARARGS (functype)) + { + ast *newType = NULL; + sym_link *ftype; + + /* don't perform integer promotion of explicitly typecasted variable arguments + * if sdcc extensions are enabled */ + if (options.std_sdcc && !TARGET_PDK_LIKE && + (IS_CAST_OP (*actParm) || + (IS_AST_SYM_VALUE (*actParm) && AST_VALUES (*actParm, cast.removedCast)) || + (IS_AST_LIT_VALUE (*actParm) && AST_VALUES (*actParm, cast.literalFromCast)))) + { + /* Parameter was explicitly typecast; don't touch it. */ + return 0; + } + + ftype = (*actParm)->ftype; + + resultType = RESULT_TYPE_NONE; + + /* If it's a char, upcast to int. */ + if (IS_INTEGRAL (ftype) && (getSize (ftype) < (unsigned) INTSIZE)) + { + newType = newAst_LINK (INTTYPE); + } + + if (IS_PTR (ftype) && !IS_GENPTR (ftype)) + { + newType = newAst_LINK (copyLinkChain (ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; + resultType = RESULT_TYPE_GPTR; + } + + if (IS_STRUCT (ftype)) + { + werrorfl ((*actParm)->filename, (*actParm)->lineno, E_STRUCT_AS_ARG, (*actParm)->opval.val->name); + return 1; + } + + if (IS_ARRAY (ftype)) + { + newType = newAst_LINK (copyLinkChain (ftype)); + DCL_TYPE (newType->opval.lnk) = port->unqualified_pointer; + resultType = RESULT_TYPE_GPTR; + } + + if (newType) + { + /* cast required; change this op to a cast. */ + (*actParm)->decorated = 0; + *actParm = newNode (CAST, newType, *actParm); + (*actParm)->filename = (*actParm)->right->filename; + (*actParm)->lineno = (*actParm)->right->lineno; + AST_VALUES (*actParm, cast.implicitCast) = 1; + + *actParm = decorateType (*actParm, resultType); + } + return 0; + } /* vararg */ + + /* if defined parameters ended but actual has not & */ + /* reentrant */ + if (!defParm && *actParm && (options.stackAuto || IFFUNC_ISREENT (functype))) + { + return 0; + } + resolveSymbols (*actParm); + + /* the parameter type must be at least castable */ + if (compareType (defParm->type, (*actParm)->ftype) == 0) + { + if (IS_STRUCT ((*actParm)->ftype)) + { + if (IS_AST_VALUE (*actParm)) + werrorfl ((*actParm)->filename, (*actParm)->lineno, E_STRUCT_AS_ARG, (*actParm)->opval.val->name); + else + werrorfl ((*actParm)->filename, (*actParm)->lineno, E_STRUCT_AS_ARG, ""); + } + else + { + werror (E_INCOMPAT_TYPES); + printFromToType ((*actParm)->ftype, defParm->type); + } + return 1; + } + + /* if the parameter is castable then add the cast */ + if ((IS_ARRAY((*actParm)->ftype) && IS_PTR(defParm->type)) || + (compareType (defParm->type, (*actParm)->ftype) == -1)) + { + ast *pTree; + + resultType = getResultTypeFromType (defParm->type); + pTree = resolveSymbols (copyAst (*actParm)); + + /* now change the current one to a cast */ + *actParm = newNode (CAST, newAst_LINK (defParm->type), pTree); + (*actParm)->filename = (*actParm)->right->filename; + (*actParm)->lineno = (*actParm)->right->lineno; + AST_VALUES (*actParm, cast.implicitCast) = 1; + *actParm = decorateType (*actParm, IS_GENPTR (defParm->type) ? RESULT_TYPE_GPTR : resultType); + } + + /* make a copy and change the regparm type to the defined parm */ + (*actParm)->etype = getSpec ((*actParm)->ftype = copyLinkChain ((*actParm)->ftype)); + SPEC_REGPARM ((*actParm)->etype) = SPEC_REGPARM (defParm->etype); + SPEC_ARGREG ((*actParm)->etype) = SPEC_ARGREG (defParm->etype); + + /* if the function is being called via a pointer & */ + /* this parameter is not passed in registers */ + /* then the function must be defined reentrant */ + if (IS_FUNCPTR (func->ftype) && !SPEC_REGPARM ((*actParm)->etype) && !IFFUNC_ISREENT (functype) && !options.stackAuto) + { + werror (E_NONRENT_ARGS); + fatalError++; + return 1; + } + + (*parmNumber)++; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createIvalType - generates ival for basic types */ +/*-----------------------------------------------------------------*/ +static ast * +createIvalType (ast * sym, sym_link * type, initList * ilist) +{ + ast *iExpr; + + /* if initList is deep */ + if (ilist && ilist->type == INIT_DEEP) + ilist = ilist->init.deep; + + if (ilist) + iExpr = decorateType (resolveSymbols (list2expr (ilist)), RESULT_TYPE_NONE); + else + iExpr = newAst_VALUE (valueFromLit (0)); + return decorateType (newNode ('=', sym, iExpr), RESULT_TYPE_NONE); +} + +/*------------------------------------------------------------------*/ +/* moveNestedInit - rewrites an initList node with a nested */ +/* designator to remove one level of nesting. */ +/*------------------------------------------------------------------*/ +static initList * +moveNestedInit (initList *src) +{ + initList *dst; + + /** Create new initList element */ + switch (src->type) + { + case INIT_NODE: + dst = newiList(INIT_NODE, src->init.node); + break; + case INIT_DEEP: + dst = newiList(INIT_DEEP, src->init.deep); + break; + default: + return NULL; + } + dst->filename = src->filename; + dst->lineno = src->lineno; + /* remove one level of nesting from the designation */ + dst->designation = src->designation->next; + + dst = newiList(INIT_DEEP, dst); + dst->filename = src->filename; + dst->lineno = src->lineno; + dst->next = src->next; + return dst; +} + +/*-----------------------------------------------------------------*/ +/* findStructField - find a specific field in a struct definition */ +/*-----------------------------------------------------------------*/ +static symbol * +findStructField (symbol *fields, symbol *target) +{ + for ( ; fields; fields = fields->next) + { + if (strcmp(fields->name, target->name) == 0) + return fields; + } + return NULL; /* not found */ +} + +static int aggregateIsAutoVar = 0; + +/*-----------------------------------------------------------------*/ +/* createIvalStruct - generates initial value for structures */ +/*-----------------------------------------------------------------*/ +static ast * +createIvalStruct (ast * sym, sym_link * type, initList * ilist, ast * rootValue) +{ + ast *rast = NULL; + ast *lAst; + symbol *sflds, *old_sflds, *ps; + initList *iloop; + sym_link *etype = getSpec (type); + + if (ilist && ilist->type != INIT_DEEP) + { + werror (E_INIT_STRUCT, ""); + return NULL; + } + + iloop = ilist ? ilist->init.deep : NULL; + + for (sflds = SPEC_STRUCT (type)->fields; ; sflds = sflds->next) + { + /* skip past unnamed bitfields */ + if (sflds && IS_BITFIELD (sflds->type) && SPEC_BUNNAMED (sflds->etype)) + continue; + + old_sflds = sflds; + /* designated initializer? */ + if (iloop && iloop->designation) + { + if (iloop->designation->type != DESIGNATOR_STRUCT) + { + werrorfl (iloop->filename, iloop->lineno, E_BAD_DESIGNATOR); + } + else /* find this designated element */ + { + sflds = findStructField(SPEC_STRUCT (type)->fields, + iloop->designation->designator.tag); + if (sflds) + { + if (iloop->designation->next) + { + iloop = moveNestedInit(iloop); + } + } + else + { + werrorfl (iloop->filename, iloop->lineno, E_NOT_MEMBER, + iloop->designation->designator.tag->name); + sflds = SPEC_STRUCT (type)->fields; /* fixup */ + } + } + } + + /* if we have come to end */ + if (!sflds) + break; + if (!iloop && (!AST_SYMBOL (rootValue)->islocal || SPEC_STAT (etype))) + break; + + if (aggregateIsAutoVar && (SPEC_STRUCT (type)->type != UNION)) + for (ps = old_sflds; ps != sflds && ps != NULL; ps = ps->next) + { + ps->implicit = 1; + lAst = newNode (PTR_OP, newNode ('&', sym, NULL), newAst_VALUE (symbolVal (ps))); + lAst = decorateType (resolveSymbols (lAst), RESULT_TYPE_NONE); + rast = decorateType (resolveSymbols (createIval (lAst, ps->type, NULL, rast, rootValue, 1)), RESULT_TYPE_NONE); + } + + /* initialize this field */ + sflds->implicit = 1; + lAst = newNode (PTR_OP, newNode ('&', sym, NULL), newAst_VALUE (symbolVal (sflds))); + lAst = decorateType (resolveSymbols (lAst), RESULT_TYPE_NONE); + rast = decorateType (resolveSymbols (createIval (lAst, sflds->type, iloop, rast, rootValue, 1)), RESULT_TYPE_NONE); + iloop = iloop ? iloop->next : NULL; + + /* Unions can only initialize a single field */ + if (SPEC_STRUCT (type)->type == UNION) + break; + } + + if (iloop) + { + if (IS_AST_VALUE (sym)) + werrorfl (sym->opval.val->sym->fileDef, sym->opval.val->sym->lineDef, + W_EXCESS_INITIALIZERS, "struct", sym->opval.val->sym->name); + else + werrorfl (sym->filename, sym->lineno, E_INIT_COUNT); + } + + return rast; +} + +/*-----------------------------------------------------------------*/ +/* createIvalArray - generates code for array initialization */ +/*-----------------------------------------------------------------*/ +static ast * +createIvalArray (ast * sym, sym_link * type, initList * ilist, ast * rootValue) +{ + ast *rast = NULL; + initList *iloop; + int lcnt = 0, size = 0, idx = 0; + literalList *literalL; + sym_link *etype = getSpec (type); + + /* take care of the special case */ + /* array of characters can be init */ + /* by a string */ + /* char *p = "abc"; */ + if ((IS_CHAR (type->next) || IS_INT (type->next) && IS_UNSIGNED (type->next)) && + ilist && ilist->type == INIT_NODE) + if ((rast = createIvalCharPtr (sym, + type, + decorateType (resolveSymbols (list2expr (ilist)), RESULT_TYPE_NONE), + rootValue))) + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); + /* char *p = {"abc"}; */ + if ((IS_CHAR (type->next) || IS_INT (type->next) && IS_UNSIGNED (type->next)) && + ilist && ilist->type == INIT_DEEP && ilist->init.deep && ilist->init.deep->type == INIT_NODE) + if ((rast = createIvalCharPtr (sym, + type, + decorateType (resolveSymbols (list2expr (ilist->init.deep)), RESULT_TYPE_NONE), + rootValue))) + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); + + /* not the special case */ + if (ilist && ilist->type != INIT_DEEP) + { + werror (E_INIT_STRUCT, ""); + return NULL; + } + + iloop = ilist ? ilist->init.deep : NULL; + lcnt = DCL_ELEM (type); + + if (!iloop && (!lcnt || !DCL_ELEM (type) || !AST_SYMBOL (rootValue)->islocal || SPEC_STAT (etype))) + { + return NULL; + } + + if (port->arrayInitializerSuppported && convertIListToConstList (reorderIlist(type,ilist), &literalL, lcnt)) + { + ast *aSym; + + aSym = decorateType (resolveSymbols (sym), RESULT_TYPE_NONE); + + rast = newNode (ARRAYINIT, aSym, NULL); + rast->values.constlist = literalL; + + // Make sure size is set to length of initializer list. + size = getNelements (type, ilist); + + if (lcnt && size > lcnt) + { + // Array size was specified, and we have more initializers than needed. + werrorfl (sym->opval.val->sym->fileDef, sym->opval.val->sym->lineDef, + W_EXCESS_INITIALIZERS, "array", sym->opval.val->sym->name); + } + } + else + { + for (;;) + { + ast *aSym; + if (!iloop && ((lcnt && size >= lcnt) || !DCL_ELEM (type) || !AST_SYMBOL (rootValue)->islocal)) + { + break; + } + + if (iloop && iloop->designation) + { + if (iloop->designation->type != DESIGNATOR_ARRAY) + { + werrorfl (iloop->filename, iloop->lineno, E_BAD_DESIGNATOR); + } + else + { + idx = iloop->designation->designator.elemno; + if (iloop->designation->next) + { + iloop = moveNestedInit(iloop); + } + } + } + /* track array size based on the initializers seen */ + if (size <= idx) + { + size = idx + 1; + } + /* too many initializers? */ + if (iloop && (lcnt && size > lcnt)) + { + // is this a better way? at least it won't crash + char *name = (IS_AST_SYM_VALUE (sym)) ? AST_SYMBOL (sym)->name : ""; + werrorfl (iloop->filename, iloop->lineno, W_EXCESS_INITIALIZERS, "array", name); + + break; + } + + aSym = newNode ('[', sym, newAst_VALUE (valueFromLit ((float) (idx)))); + aSym = decorateType (resolveSymbols (aSym), RESULT_TYPE_NONE); + rast = createIval (aSym, type->next, iloop, rast, rootValue, 0); + idx++; + iloop = (iloop ? iloop->next : NULL); + } + } + + /* if we have not been given a size */ + if (!DCL_ELEM (type)) + { + /* check, if it's a flexible array */ + if (IS_STRUCT (AST_VALUE (rootValue)->type)) + AST_SYMBOL (rootValue)->flexArrayLength = size * getSize (type->next); + else + DCL_ELEM (type) = size; + } + + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); +} + +/*-----------------------------------------------------------------*/ +/* createIvalCharPtr - generates initial values for char pointers */ +/*-----------------------------------------------------------------*/ +static ast * +createIvalCharPtr (ast * sym, sym_link * type, ast * iexpr, ast * rootVal) +{ + ast *rast = NULL; + unsigned size = 0; + + /* if this is a pointer & right is a literal array then */ + /* just assignment will do */ + if (IS_PTR (type) && ((IS_LITERAL (iexpr->etype) || (IS_SPEC (iexpr->etype) && SPEC_SCLS (iexpr->etype) == S_CODE)) && IS_ARRAY (iexpr->ftype))) + return newNode ('=', sym, iexpr); + + /* left side is an array so we have to assign each element */ + if (!iexpr) + { + /* for each character generate an assignment */ + /* to the array element */ + unsigned int i = 0; + unsigned int symsize = DCL_ELEM (type); + + if (!AST_SYMBOL (rootVal)->islocal || SPEC_STAT (getSpec (type))) + return NULL; + + for (i = 0; i < symsize; ++i) + { + rast = newNode (NULLOP, + rast, + newNode ('=', + newNode ('[', sym, + newAst_VALUE (valueFromLit ((float) i))), newAst_VALUE (valueFromLit (0)))); + } + + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); + } + + if ((IS_LITERAL (iexpr->etype) || (IS_SPEC (iexpr->etype) && SPEC_SCLS (iexpr->etype) == S_CODE)) && IS_ARRAY (iexpr->ftype)) + { + /* for each character generate an assignment */ + /* to the array element */ + unsigned int i = 0; + unsigned int symsize = DCL_ELEM (type); + + size = DCL_ELEM (iexpr->ftype); + if (symsize && size > symsize) + { + if (size > symsize) + { + char *name = (IS_AST_SYM_VALUE (sym)) ? AST_SYMBOL (sym)->name : ""; + + TYPE_TARGET_ULONG c; + if (IS_CHAR (type->next)) + c = SPEC_CVAL (iexpr->etype).v_char[symsize]; + else if (!IS_LONG (type->next)) + c = SPEC_CVAL (iexpr->etype).v_char16[symsize]; + else + c = SPEC_CVAL (iexpr->etype).v_char32[symsize]; + if (options.std_c99 && c == '\0' && size == symsize + 1) + { + if (!options.lessPedantic) + werrorfl (iexpr->filename, iexpr->lineno, W_STRING_CANNOT_BE_TERMINATED, name); + } + else + werrorfl (iexpr->filename, iexpr->lineno, W_EXCESS_INITIALIZERS, "string", name); + } + size = symsize; + } + + for (i = 0; i < size; i++) + { + TYPE_TARGET_ULONG c; + if (IS_CHAR (type->next)) + c = SPEC_CVAL (iexpr->etype).v_char[i]; + else if (!IS_LONG (type->next)) + c = SPEC_CVAL (iexpr->etype).v_char16[i]; + else + c = SPEC_CVAL (iexpr->etype).v_char32[i]; + rast = newNode (NULLOP, + rast, + newNode ('=', + newNode ('[', sym, + newAst_VALUE (valueFromLit ((float) i))), newAst_VALUE (valueFromLit (c)))); + } + + /* assign zero to others */ + for (i = size; i < symsize; i++) + { + rast = newNode (NULLOP, + rast, + newNode ('=', + newNode ('[', sym, + newAst_VALUE (valueFromLit ((float) i))), newAst_VALUE (valueFromLit (0)))); + } + + // now WE don't need iexpr's symbol anymore + freeStringSymbol (AST_SYMBOL (iexpr)); + + /* if we have not been given a size */ + if (!DCL_ELEM (type)) + { + /* check, if it's a flexible array */ + if (IS_STRUCT (AST_VALUE (rootVal)->type)) + AST_SYMBOL (rootVal)->flexArrayLength = size * getSize (type->next); + else + DCL_ELEM (type) = size; + } + + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* createIvalPtr - generates initial value for pointers */ +/*-----------------------------------------------------------------*/ +static ast * +createIvalPtr (ast * sym, sym_link * type, initList * ilist, ast * rootVal) +{ + ast *rast; + ast *iexpr; + + /* if deep then */ + if (ilist && ilist->type == INIT_DEEP) + ilist = ilist->init.deep; + + if (ilist) + iexpr = decorateType (resolveSymbols (list2expr (ilist)), RESULT_TYPE_NONE); + else + iexpr = newAst_VALUE (valueFromLit (0)); + + /* if character pointer */ + if (IS_CHAR (type->next) || IS_INT (type->next) && IS_UNSIGNED (type->next)) + if ((rast = createIvalCharPtr (sym, type, iexpr, rootVal))) + return rast; + + return newNode ('=', sym, iexpr); +} + +/*-----------------------------------------------------------------*/ +/* createIval - generates code for initial value */ +/*-----------------------------------------------------------------*/ +static ast * +createIval (ast * sym, sym_link * type, initList * ilist, ast * wid, ast * rootValue, int omitStatic) +{ + ast *rast = NULL; + + if (!ilist && (!AST_SYMBOL (rootValue)->islocal || (omitStatic && SPEC_STAT (getSpec (type))))) + return NULL; + + /* if structure then */ + if (IS_STRUCT (type)) + rast = createIvalStruct (sym, type, ilist, rootValue); + else + /* if this is a pointer */ + if (IS_PTR (type)) + rast = createIvalPtr (sym, type, ilist, rootValue); + else + /* if this is an array */ + if (IS_ARRAY (type)) + rast = createIvalArray (sym, type, ilist, rootValue); + else + /* if type is SPECIFIER */ + if (IS_SPEC (type)) + rast = createIvalType (sym, type, ilist); + + if (wid) + return decorateType (resolveSymbols (newNode (NULLOP, wid, rast)), RESULT_TYPE_NONE); + else + return decorateType (resolveSymbols (rast), RESULT_TYPE_NONE); +} + +/*-----------------------------------------------------------------*/ +/* initAggregates - initialises aggregate variables with initv */ +/*-----------------------------------------------------------------*/ +ast * +initAggregates (symbol *sym, initList *ival, ast *wid) +{ + ast *newAst = newAst_VALUE (symbolVal (sym)); + return createIval (newAst, sym->type, ival, wid, newAst, 1); +} + +/*-----------------------------------------------------------------*/ +/* gatherAutoInit - creates assignment expressions for initial */ +/* values */ +/*-----------------------------------------------------------------*/ +static ast * +gatherAutoInit (symbol * autoChain) +{ + ast *init = NULL; + ast *work; + symbol *sym; + + inInitMode = 1; + for (sym = autoChain; sym; sym = sym->next) + { + /* resolve the symbols in the ival */ + if (sym->ival) + resolveIvalSym (sym->ival, sym->type); + +#if 1 + /* if we are PIC16 port, + * and this is a static, + * and have initial value, + * and not S_CODE, don't emit in gs segment, + * but allow glue.c:pic16emitRegularMap to put symbol + * in idata section */ + if (TARGET_IS_PIC16 && IS_STATIC (sym->etype) && sym->ival && SPEC_SCLS (sym->etype) != S_CODE) + { + SPEC_SCLS (sym->etype) = S_DATA; + continue; + } +#endif + + /* if this is a static variable & has an */ + /* initial value the code needs to be lifted */ + /* here to the main portion since they can be */ + /* initialised only once at the start */ + if (IS_STATIC (sym->etype) && sym->ival && SPEC_SCLS (sym->etype) != S_CODE) + { + symbol *newSym; + + /* insert the symbol into the symbol table */ + /* with level = 0 & name = rname */ + newSym = copySymbol (sym); + addSym (SymbolTab, newSym, newSym->rname, 0, 0, 1); + + /* now lift the code to main */ + if (IS_AGGREGATE (sym->type)) + { + work = initAggregates (sym, sym->ival, NULL); + } + else + { + if (getNelements (sym->type, sym->ival) > 1) + { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", sym->name); + } + work = newNode ('=', newAst_VALUE (symbolVal (newSym)), list2expr (sym->ival)); + } + + setAstFileLine (work, sym->fileDef, sym->lineDef); + + sym->ival = NULL; + if (staticAutos) + staticAutos = newNode (NULLOP, staticAutos, work); + else + staticAutos = work; + + continue; + } + + /* if there is an initial value */ + if (sym->ival && SPEC_SCLS (sym->etype) != S_CODE) + { + initList *ilist = sym->ival; + + while (ilist->type == INIT_DEEP) + { + ilist = ilist->init.deep; + } + + /* update lineno for error msg */ + filename = sym->fileDef; + lineno = sym->lineDef; + setAstFileLine (ilist->init.node, sym->fileDef, sym->lineDef); + + if (IS_AGGREGATE (sym->type)) + { + aggregateIsAutoVar = 1; + work = initAggregates (sym, sym->ival, NULL); + aggregateIsAutoVar = 0; + } + else + { + if (getNelements (sym->type, sym->ival) > 1) + { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", sym->name); + } + work = newNode ('=', newAst_VALUE (symbolVal (sym)), list2expr (sym->ival)); + } + + // just to be sure + setAstFileLine (work, sym->fileDef, sym->lineDef); + + sym->ival = NULL; + if (init) + init = newNode (NULLOP, init, work); + else + init = work; + } + } + inInitMode = 0; + return init; +} + +/*-----------------------------------------------------------------*/ +/* freeStringSymbol - delete a literal string if no more usage */ +/*-----------------------------------------------------------------*/ +void +freeStringSymbol (symbol *sym) +{ + /* make sure this is a literal string */ + assert (sym->isstrlit); + if (--sym->isstrlit == 0) + { // lower the usage count + memmap *segment = SPEC_OCLS (sym->etype); + if (segment) + { + deleteSetItem (&segment->syms, sym); + } + } +} + +/*-----------------------------------------------------------------*/ +/* stringToSymbol - creates a symbol from a literal string */ +/*-----------------------------------------------------------------*/ +value * +stringToSymbol (value *val) +{ + struct dbuf_s dbuf; + static int charLbl = 0; + symbol *sym; + set *sp; + unsigned int size; + + // have we heard this before? + for (sp = statsg->syms; sp; sp = sp->next) + { + sym = sp->item; + size = getSize (sym->type); + if (sym->isstrlit && size == getSize (val->type) && + !memcmp (SPEC_CVAL (sym->etype).v_char, SPEC_CVAL (val->etype).v_char, size)) + { + // yes, this is old news. Don't publish it again. + sym->isstrlit++; // but raise the usage count + return symbolVal (sym); + } + } + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "__str_%d", charLbl++); + sym = newSymbol (dbuf_c_str (&dbuf), 0); /* make it @ level 0 */ + strncpyz (sym->rname, dbuf_c_str (&dbuf), SDCC_NAME_MAX); + dbuf_destroy (&dbuf); + + /* copy the type from the value passed */ + sym->type = copyLinkChain (val->type); + sym->etype = getSpec (sym->type); + /* change to storage class & output class */ + SPEC_SCLS (sym->etype) = S_CODE; + SPEC_CVAL (sym->etype).v_char = SPEC_CVAL (val->etype).v_char; + SPEC_STAT (sym->etype) = 1; + /* make the level & block = 0 */ + sym->block = sym->level = 0; + sym->isstrlit = 1; + /* create an ival */ + sym->ival = newiList (INIT_NODE, newAst_VALUE (val)); + if (noAlloc == 0) + { + /* allocate it */ + addSymChain (&sym); + allocVariables (sym); + } + else + { + defaultOClass (sym); + addSet (&strSym, sym); + addSet (&statsg->syms, sym); + } + sym->ival = NULL; + return symbolVal (sym); +} + +/*-----------------------------------------------------------------*/ +/* processBlockVars - will go thru the ast looking for block if */ +/* a block is found then will allocate the syms */ +/* will also gather the auto inits present */ +/*-----------------------------------------------------------------*/ +ast * +processBlockVars (ast * tree, int *stack, int action) +{ + if (!tree) + return NULL; + + /* if this is a block */ + if (tree->type == EX_OP && tree->opval.op == BLOCK) + { + ast *autoInit; + symbol * sym = tree->values.sym; + + while (sym) + { + sym->isinscope = 1; + sym = sym->next; + } + + if (action == ALLOCATE) + { + *stack += allocVariables (tree->values.sym); + autoInit = gatherAutoInit (tree->values.sym); + + /* if there are auto inits then do them */ + if (autoInit) + tree->left = newNode (NULLOP, autoInit, tree->left); + } + else /* action is deallocate */ + { + deallocLocal (tree->values.sym); + } + } + + if (IS_FOR_STMT (tree)) + { + processBlockVars (AST_FOR (tree, initExpr), stack, action); + processBlockVars (AST_FOR (tree, condExpr), stack, action); + processBlockVars (AST_FOR (tree, loopExpr), stack, action); + } + processBlockVars (tree->left, stack, action); + processBlockVars (tree->right, stack, action); + + /* if this is a block */ + if (tree->type == EX_OP && tree->opval.op == BLOCK) + { + symbol * sym = tree->values.sym; + + while (sym) + { + sym->isinscope = 0; + sym = sym->next; + } + } + + return tree; +} + +/*-------------------------------------------------------------*/ +/* constExprTree - returns TRUE if this tree is a constant */ +/* expression */ +/*-------------------------------------------------------------*/ +bool +constExprTree (ast * cexpr) +{ + if (!cexpr) + { + return TRUE; + } + + cexpr = decorateType (resolveSymbols (cexpr), RESULT_TYPE_NONE); + + switch (cexpr->type) + { + case EX_VALUE: + if (IS_AST_LIT_VALUE (cexpr)) + { + // this is a literal + return TRUE; + } + if (IS_AST_SYM_VALUE (cexpr) && IS_FUNC (AST_SYMBOL (cexpr)->type)) + { + // a function's address will never change + return TRUE; + } + if (IS_AST_SYM_VALUE (cexpr) && IS_ARRAY (AST_SYMBOL (cexpr)->type)) + { + // an array's address will never change + return TRUE; + } + if (IS_AST_SYM_VALUE (cexpr) && !AST_SYMBOL (cexpr)->etype) + { + // the offset of a struct field will never change + return TRUE; + } +#if 0 + if (IS_AST_SYM_VALUE (cexpr) && IN_CODESPACE (SPEC_OCLS (AST_SYMBOL (cexpr)->etype))) + { + // a symbol in code space will never change + // This is only for the 'char *s="hallo"' case and will have to leave + //printf(" code space symbol"); + return TRUE; + } +#endif + return FALSE; + case EX_LINK: + wassertl (0, "unexpected link in expression tree"); + return FALSE; + case EX_OP: + if (cexpr->opval.op == ARRAYINIT) + { + // this is a list of literals + return TRUE; + } + if (cexpr->opval.op == '=') + { + return constExprTree (cexpr->right); + } + if (cexpr->opval.op == CAST) + { + // cast ignored, maybe we should throw a warning here? + return constExprTree (cexpr->right); + } + if (cexpr->opval.op == '&') + { + return TRUE; + } + if (cexpr->opval.op == CALL || cexpr->opval.op == PCALL) + { + return FALSE; + } + if (constExprTree (cexpr->left) && constExprTree (cexpr->right)) + { + return TRUE; + } + return FALSE; + case EX_OPERAND: + return IS_CONSTANT (operandType (cexpr->opval.oprnd)); + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* constExprValue - returns the value of a constant expression */ +/* or NULL if it is not a constant expression */ +/*-----------------------------------------------------------------*/ +value * +constExprValue (ast * cexpr, int check) +{ + cexpr = decorateType (resolveSymbols (cexpr), RESULT_TYPE_NONE); + + /* if this is not a constant then */ + if (!IS_LITERAL (cexpr->ftype)) + { + /* then check if this is a literal array + in code segment */ + if (SPEC_SCLS (cexpr->etype) == S_CODE && SPEC_CVAL (cexpr->etype).v_char && IS_ARRAY (cexpr->ftype)) + { + value *val = valFromType (cexpr->ftype); + SPEC_SCLS (val->etype) = S_LITERAL; + val->sym = cexpr->opval.val->sym; + val->sym->type = copyLinkChain (cexpr->ftype); + val->sym->etype = getSpec (val->sym->type); + strncpyz (val->name, cexpr->opval.val->sym->rname, SDCC_NAME_MAX); + return val; + } + + /* if we are casting a literal value then */ + if (IS_CAST_OP (cexpr) && IS_LITERAL (cexpr->right->ftype)) + { + return valCastLiteral (cexpr->ftype, floatFromVal (cexpr->right->opval.val), (TYPE_TARGET_ULONGLONG) ullFromVal (cexpr->right->opval.val)); + } + + if (IS_AST_VALUE (cexpr)) + { + return cexpr->opval.val; + } + + if (check) + werrorfl (cexpr->filename, cexpr->lineno, E_CONST_EXPECTED, "found expression"); + + return NULL; + } + + /* return the value */ + if (IS_AST_VALUE (cexpr)) + { + return cexpr->opval.val; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* isLabelInAst - will return true if a given label is found */ +/*-----------------------------------------------------------------*/ +bool +isLabelInAst (symbol * label, ast * tree) +{ + if (!tree || IS_AST_VALUE (tree) || IS_AST_LINK (tree)) + return FALSE; + + if (IS_AST_OP (tree) && tree->opval.op == LABEL && isSymbolEqual (AST_SYMBOL (tree->left), label)) + return TRUE; + + return isLabelInAst (label, tree->right) && isLabelInAst (label, tree->left); +} + +/*-----------------------------------------------------------------*/ +/* isLoopCountable - return true if the loop count can be */ +/* determined at compile time . */ +/*-----------------------------------------------------------------*/ +static bool +isLoopCountable (ast * initExpr, ast * condExpr, ast * loopExpr, symbol ** sym, ast ** init, ast ** end) +{ + /* the loop is considered countable if the following + conditions are true :- + + a) initExpr :- = + b) condExpr :- < + c) loopExpr :- ++ + */ + + /* first check the initExpr */ + if (IS_AST_OP (initExpr) && initExpr->opval.op == '=' && /* is assignment */ + IS_AST_SYM_VALUE (initExpr->left)) + { /* left is a symbol */ + + *sym = AST_SYMBOL (initExpr->left); + *init = initExpr->right; + } + else + return FALSE; + + /* don't reverse loop with volatile counter */ + if (IS_VOLATILE ((*sym)->type)) + return FALSE; + + /* for now the symbol has to be of + integral type */ + if (!IS_INTEGRAL ((*sym)->type)) + return FALSE; + + /* now check condExpr */ + if (IS_AST_OP (condExpr)) + { + switch (condExpr->opval.op) + { + case '<': + if (IS_AST_SYM_VALUE (condExpr->left) && + isSymbolEqual (*sym, AST_SYMBOL (condExpr->left)) && IS_AST_LIT_VALUE (condExpr->right)) + { + *end = condExpr->right; + break; + } + return FALSE; + + case '!': + if (IS_AST_OP (condExpr->left) && + condExpr->left->opval.op == '>' && + IS_AST_LIT_VALUE (condExpr->left->right) && + IS_AST_SYM_VALUE (condExpr->left->left) && isSymbolEqual (*sym, AST_SYMBOL (condExpr->left->left))) + { + + *end = newNode ('+', condExpr->left->right, newAst_VALUE (constVal ("1"))); + break; + } + return FALSE; + + default: + return FALSE; + } + } + else + return FALSE; + + /* check loop expression is of the form ++ */ + if (!IS_AST_OP (loopExpr)) + return FALSE; + + /* check if ++ */ + if (loopExpr->opval.op == INC_OP) + { + if (loopExpr->left) + { + /* pre */ + if (IS_AST_SYM_VALUE (loopExpr->left) && isSymbolEqual (*sym, AST_SYMBOL (loopExpr->left))) + return TRUE; + } + else + { + /* post */ + if (IS_AST_SYM_VALUE (loopExpr->right) && isSymbolEqual (*sym, AST_SYMBOL (loopExpr->right))) + return TRUE; + } + } + else + { + /* check for += */ + if (loopExpr->opval.op == ADD_ASSIGN) /* seems to never happen, createRMW() absorbed */ + { + wassertl (0, "obsolete opcode in tree"); + + if (IS_AST_SYM_VALUE (loopExpr->left) && + isSymbolEqual (*sym, AST_SYMBOL (loopExpr->left)) && + IS_AST_LIT_VALUE (loopExpr->right) && AST_ULONG_VALUE (loopExpr->right) == 1) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* astHasVolatile - returns true if ast contains any volatile */ +/*-----------------------------------------------------------------*/ +bool +astHasVolatile (ast *tree) +{ + if (!tree) + return FALSE; + + if (TETYPE (tree) && IS_VOLATILE (TETYPE (tree))) + return TRUE; + + if (IS_AST_OP (tree)) + return astHasVolatile (tree->left) || astHasVolatile (tree->right); + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* astHasPointer - return true if the ast contains any ptr variable */ +/*-----------------------------------------------------------------*/ +bool +astHasPointer (ast * tree) +{ + if (!tree) + return FALSE; + + if (IS_AST_LINK (tree)) + return TRUE; + + /* if we hit an array expression then check + only the left side */ + if (IS_AST_OP (tree) && tree->opval.op == '[') + return astHasPointer (tree->left); + + if (IS_AST_VALUE (tree)) + return IS_PTR (tree->ftype) || IS_ARRAY (tree->ftype); + + return astHasPointer (tree->left) || astHasPointer (tree->right); +} + +/*-----------------------------------------------------------------*/ +/* astHasSymbol - return true if the ast has the given symbol */ +/*-----------------------------------------------------------------*/ +bool +astHasSymbol (ast * tree, symbol * sym) +{ + if (!tree || IS_AST_LINK (tree)) + return FALSE; + + if (IS_AST_VALUE (tree)) + { + if (IS_AST_SYM_VALUE (tree)) + return isSymbolEqual (AST_SYMBOL (tree), sym); + else + return FALSE; + } + + return astHasSymbol (tree->left, sym) || astHasSymbol (tree->right, sym); +} + +/*-----------------------------------------------------------------*/ +/* astHasDeref - return true if the ast has an indirect access */ +/*-----------------------------------------------------------------*/ +static bool +astHasDeref (ast * tree) +{ + if (!tree || IS_AST_LINK (tree) || IS_AST_VALUE (tree)) + return FALSE; + + if (tree->opval.op == '*' && tree->right == NULL) + return TRUE; + + return astHasDeref (tree->left) || astHasDeref (tree->right); +} + +/*-----------------------------------------------------------------*/ +/* isConformingBody - the loop body has to conform to a set of */ +/* rules for the loop to be considered reversible read on for rules*/ +/*-----------------------------------------------------------------*/ +bool +isConformingBody (ast * pbody, symbol * sym, ast * body) +{ + /* we are going to do a pre-order traversal of the + tree && check for the following conditions. (essentially + a set of very shallow tests ) + a) the sym passed does not participate in any arithmetic operation + b) There are no function calls + c) all jumps are within the body + d) address of loop control variable not taken + e) if an assignment has a pointer on the left hand side make sure + right does not have loop control variable + */ + + /* if we reach the end or a leaf then true */ + if (!pbody || IS_AST_LINK (pbody) || IS_AST_VALUE (pbody)) + return TRUE; + + /* if anything else is "volatile" */ + if (IS_VOLATILE (TETYPE (pbody))) + return FALSE; + + /* we will walk the body in a pre-order traversal for + efficiency sake */ + switch (pbody->opval.op) + { +/*------------------------------------------------------------------*/ + case '[': + /* if the loopvar is used as an index */ + /* array op is commutative -- must check both left & right */ + if (astHasSymbol (pbody->right, sym) || astHasSymbol (pbody->left, sym)) + { + return FALSE; + } + return isConformingBody (pbody->right, sym, body) && isConformingBody (pbody->left, sym, body); + +/*------------------------------------------------------------------*/ + case PTR_OP: + /* '->' right: is a symbol + left: check if the loopvar is used as an index */ + if (astHasSymbol (pbody->left, sym)) + return FALSE; + return isConformingBody (pbody->left, sym, body); + + case '.': + wassertl (0, "obsolete opcode in tree"); + break; + +/*------------------------------------------------------------------*/ + case INC_OP: + case DEC_OP: + + if (astHasSymbol (pbody->right, sym) || astHasSymbol (pbody->left, sym)) + return FALSE; + + return isConformingBody (pbody->right, sym, body) && isConformingBody (pbody->left, sym, body); + +/*------------------------------------------------------------------*/ + + case '*': /* can be unary : if right is null then unary operation */ + case '+': + case '-': + case '&': + + /* if right is NULL then unary operation */ +/*------------------------------------------------------------------*/ + /*----------------------------*/ + /* address of */ + /*----------------------------*/ + if (!pbody->right) + { + if (IS_AST_SYM_VALUE (pbody->left) && isSymbolEqual (AST_SYMBOL (pbody->left), sym)) + return FALSE; + else + return isConformingBody (pbody->left, sym, body); + } + else + { + if (astHasSymbol (pbody->left, sym) || astHasSymbol (pbody->right, sym)) + return FALSE; + } + +/*------------------------------------------------------------------*/ + case '|': + case '^': + case '/': + case '%': + case LEFT_OP: + case RIGHT_OP: + case GETABIT: + case GETBYTE: + case GETWORD: + + if (IS_AST_SYM_VALUE (pbody->left) && isSymbolEqual (AST_SYMBOL (pbody->left), sym)) + return FALSE; + + if (IS_AST_SYM_VALUE (pbody->right) && isSymbolEqual (AST_SYMBOL (pbody->right), sym)) + return FALSE; + + return isConformingBody (pbody->left, sym, body) && isConformingBody (pbody->right, sym, body); + + case '~': + case '!': + case RRC: + case RLC: + case GETHBIT: + case SWAP: + if (IS_AST_SYM_VALUE (pbody->left) && isSymbolEqual (AST_SYMBOL (pbody->left), sym)) + return FALSE; + return isConformingBody (pbody->left, sym, body); + +/*------------------------------------------------------------------*/ + + case AND_OP: + case OR_OP: + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case '?': + case ':': + case SIZEOF: /* evaluate wihout code generation */ + + if (IS_AST_SYM_VALUE (pbody->left) && isSymbolEqual (AST_SYMBOL (pbody->left), sym)) + return FALSE; + + if (IS_AST_SYM_VALUE (pbody->right) && isSymbolEqual (AST_SYMBOL (pbody->right), sym)) + return FALSE; + + return isConformingBody (pbody->left, sym, body) && isConformingBody (pbody->right, sym, body); + +/*------------------------------------------------------------------*/ + case '=': + + /* if left has a pointer & right has loop + control variable then we cannot */ + if (astHasPointer (pbody->left) && astHasSymbol (pbody->right, sym)) + return FALSE; + + if (astHasVolatile (pbody->left)) + return FALSE; + + if (IS_AST_SYM_VALUE (pbody->left)) + { + // if the loopvar has an assignment + if (isSymbolEqual (AST_SYMBOL (pbody->left), sym)) + return FALSE; + // if the loopvar is used in another (maybe conditional) block + if (astHasSymbol (pbody->right, sym) && (pbody->level >= body->level)) + { + return FALSE; + } + } + + if (astHasDeref (pbody->right)) + return FALSE; + + return isConformingBody (pbody->left, sym, body) && isConformingBody (pbody->right, sym, body); + + case MUL_ASSIGN: + case DIV_ASSIGN: + case AND_ASSIGN: + case OR_ASSIGN: + case XOR_ASSIGN: + case RIGHT_ASSIGN: + case LEFT_ASSIGN: + case SUB_ASSIGN: + case ADD_ASSIGN: + assert ("Parser should not have generated this\n"); + +/*------------------------------------------------------------------*/ + /*----------------------------*/ + /* comma operator */ + /*----------------------------*/ + case ',': + return isConformingBody (pbody->left, sym, body) && isConformingBody (pbody->right, sym, body); + +/*------------------------------------------------------------------*/ + /*----------------------------*/ + /* function call */ + /*----------------------------*/ + case CALL: + /* if local & no parameters & + not used to find the function then ok */ + if (sym->level && !pbody->right && !astHasSymbol (pbody->left, sym)) + { + return TRUE; + } + return FALSE; + +/*------------------------------------------------------------------*/ + /*----------------------------*/ + /* return statement */ + /*----------------------------*/ + case RETURN: + return FALSE; + + case GOTO: + if (isLabelInAst (AST_SYMBOL (pbody->left), body)) + return TRUE; + else + return FALSE; + + case SWITCH: + if (astHasSymbol (pbody->left, sym)) + return FALSE; + break; + + default: + break; + } + + return isConformingBody (pbody->left, sym, body) && isConformingBody (pbody->right, sym, body); +} + +/*-----------------------------------------------------------------*/ +/* isLoopReversible - takes a for loop as input && returns true */ +/* if the for loop is reversible. If yes will set the value of */ +/* the loop control var & init value & termination value */ +/*-----------------------------------------------------------------*/ +static bool +isLoopReversible (ast * loop, symbol ** loopCntrl, ast ** init, ast ** end) +{ + /* if option says don't do it then don't */ + if (optimize.noLoopReverse) + return 0; + /* there are several tests to determine this */ + + /* for loop has to be of the form + for ( = ; + [ < ] ; + [++] | [ += 1] | [ = + 1] ) + forBody */ + if (!isLoopCountable (AST_FOR (loop, initExpr), AST_FOR (loop, condExpr), AST_FOR (loop, loopExpr), loopCntrl, init, end)) + return 0; + + /* now do some serious checking on the body of the loop + */ + + return isConformingBody (loop->left, *loopCntrl, loop->left); +} + +/*-----------------------------------------------------------------*/ +/* replLoopSym - replace the loop sym by loop sym -1 */ +/*-----------------------------------------------------------------*/ +static void +replLoopSym (ast * body, symbol * sym) +{ + /* reached end */ + if (!body || IS_AST_LINK (body)) + return; + + if (IS_AST_SYM_VALUE (body)) + { + if (isSymbolEqual (AST_SYMBOL (body), sym)) + { + + body->type = EX_OP; + body->opval.op = '-'; + body->left = newAst_VALUE (symbolVal (sym)); + body->right = newAst_VALUE (constVal ("1")); + } + return; + } + + replLoopSym (body->left, sym); + replLoopSym (body->right, sym); +} + +/*-----------------------------------------------------------------*/ +/* reverseLoop - do the actual loop reversal */ +/*-----------------------------------------------------------------*/ +ast * +reverseLoop (ast * loop, symbol * sym, ast * init, ast * end) +{ + ast *rloop; + + /* create the following tree + = loopCount ; + for_continue: + forbody + -= 1; + if (sym) goto for_continue ; + = end */ + + /* put it together piece by piece */ + rloop = newNode (NULLOP, + createIf (newAst_VALUE (symbolVal (sym)), + newNode (GOTO, + newAst_VALUE (symbolVal (AST_FOR (loop, continueLabel))), + NULL), NULL), newNode ('=', newAst_VALUE (symbolVal (sym)), end)); + + replLoopSym (loop->left, sym); + setAstFileLine (rloop, init->filename, init->lineno); + + rloop = newNode (NULLOP, + newNode ('=', + newAst_VALUE (symbolVal (sym)), + newNode ('-', end, init)), + createLabel (AST_FOR (loop, continueLabel), + newNode (NULLOP, + loop->left, + newNode (NULLOP, + newNode (SUB_ASSIGN, + newAst_VALUE (symbolVal (sym)), + newAst_VALUE (constVal ("1"))), rloop)))); + + rloop->lineno = init->lineno; + return decorateType (rloop, RESULT_TYPE_NONE); +} + +/*-----------------------------------------------------------------*/ +/* replLoopSymByVal - replace the loop sym by a value */ +/*-----------------------------------------------------------------*/ +static int +replLoopSymByVal (ast * body, symbol * sym, value * val) +{ + int changed; + /* reached end */ + if (!body || IS_AST_LINK (body)) + return 0; + + if (IS_AST_SYM_VALUE (body)) + { + if (isSymbolEqual (AST_SYMBOL (body), sym)) + { + + body->type = EX_VALUE; + AST_VALUE (body) = copyValue (val); + body->decorated = 0; + return 1; + } + return 0; + } + + changed = replLoopSymByVal (body->left, sym, val); + changed |= replLoopSymByVal (body->right, sym, val); + if (changed) + body->decorated = 0; + return changed; +} + +/*-----------------------------------------------------------------*/ +/* isInitiallyTrue - check if a for loop's condition is true for */ +/* the initial iteration */ +/*-----------------------------------------------------------------*/ +static bool +isInitiallyTrue (ast *initExpr, ast * condExpr) +{ + symbol * sym; + ast * init; + + if (!condExpr) + return TRUE; + if (!initExpr) + return FALSE; + + /* first check the initExpr */ + if (IS_AST_OP (initExpr) && initExpr->opval.op == '=' && /* is assignment */ + IS_AST_SYM_VALUE (initExpr->left)) + { /* left is a symbol */ + + sym = AST_SYMBOL (initExpr->left); + init = initExpr->right; + } + else + return FALSE; + + /* don't defer condition test if volatile */ + if (IS_VOLATILE ((sym)->type)) + return FALSE; + + if (!IS_AST_LIT_VALUE (init)) + return FALSE; + + /* Cannot move the condition if the condition has side-effects */ + if (hasSEFcalls (condExpr)) + return FALSE; + + /* Cast the initial value to the type of the loop symbol so that */ + /* we have the actual value that we would read out of that symbol */ + /* rather than just the value assigned to it. */ + initExpr = copyAst (initExpr); + initExpr->opval.op = CAST; + initExpr->left = newAst_LINK (LTYPE (initExpr)); + initExpr->decorated = 0; + decorateType (initExpr, RESULT_TYPE_NONE); + if (!IS_AST_LIT_VALUE (initExpr)) + return FALSE; + + /* Replace the symbol with its initial value and see if the condition */ + /* simplifies to a non-zero (TRUE) literal value */ + condExpr = copyAst (condExpr); + if (replLoopSymByVal (condExpr, sym, AST_VALUE (initExpr))) + { + int original; + + /* We are speculating that the condition is always true */ + /* or false for the first loop iteration; no need to */ + /* trigger a warning that may not apply to the original */ + /* source code. */ + original = setWarningDisabledState (W_COMP_RANGE, TRUE); + condExpr = decorateType (condExpr, RESULT_TYPE_NONE); + setWarningDisabledState (W_COMP_RANGE, original); + } + if (!IS_AST_LIT_VALUE (condExpr)) + return FALSE; + return !isEqualVal (AST_VALUE (condExpr), 0); +} + +/*-----------------------------------------------------------------*/ +/* createDoFor - creates parse tree for 'for' statement */ +/* */ +/* When we know that the condition is always true for the initial */ +/* iteration, we can build a more optimal tree by testing the */ +/* condition at the end of the loop (like do-while). */ +/* */ +/* initExpr */ +/* _forbody_n: */ +/* statements */ +/* _forcontinue_n: */ +/* loopExpr */ +/* condExpr +-> trueLabel -> _forbody_n */ +/* | */ +/* +-> falseLabel-> _forbreak_n */ +/* _forbreak_n: */ +/*-----------------------------------------------------------------*/ +ast * +createDoFor (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, + symbol * condLabel, ast * initExpr, ast * condExpr, + ast * loopExpr, ast * forBody, ast * continueLabelAst) +{ + ast *forTree; + + if (condExpr) + { + condExpr = backPatchLabels (condExpr, trueLabel, falseLabel); + if (condExpr && !IS_IFX (condExpr)) + condExpr = newIfxNode (condExpr, trueLabel, falseLabel); + } + else /* if no condition specified, it is considered always TRUE */ + condExpr = newNode (GOTO, newAst_VALUE (symbolVal (trueLabel)), NULL); + + /* attach body label to body */ + forBody = createLabel (trueLabel, forBody); + + /* attach continue to forLoop expression and condition */ + loopExpr = newNode (NULLOP, loopExpr, condExpr); + if (continueLabelAst) + { + continueLabelAst->right = loopExpr; + loopExpr = continueLabelAst; + } + else + loopExpr = createLabel (continueLabel, loopExpr); + + /* now start putting them together */ + forTree = newNode (NULLOP, initExpr, forBody); + forTree = newNode (NULLOP, forTree, loopExpr); + + /* the break label is already in the tree as a sibling */ + /* to the original FOR node this tree is replacing */ + return forTree; +} + +/*-----------------------------------------------------------------*/ +/* searchLitOp - search tree (*ops only) for an ast with literal */ +/*-----------------------------------------------------------------*/ +static ast * +searchLitOp (ast * tree, ast ** parent, const char *ops) +{ + ast *ret; + + if (tree && optimize.global_cse) + { + /* is there a literal operand? */ + if (tree->right && + IS_AST_OP (tree->right) && + tree->right->right && (tree->right->opval.op == (unsigned) ops[0] || tree->right->opval.op == (unsigned) ops[1])) + { + if (IS_LITERAL (RTYPE (tree->right)) != IS_LITERAL (LTYPE (tree->right))) + { + tree->right->decorated = 0; + tree->decorated = 0; + *parent = tree; + return tree->right; + } + ret = searchLitOp (tree->right, parent, ops); + if (ret) + return ret; + } + if (tree->left && + IS_AST_OP (tree->left) && + tree->left->right && (tree->left->opval.op == (unsigned) ops[0] || tree->left->opval.op == (unsigned) ops[1])) + { + if (IS_LITERAL (RTYPE (tree->left)) != IS_LITERAL (LTYPE (tree->left))) + { + tree->left->decorated = 0; + tree->decorated = 0; + *parent = tree; + return tree->left; + } + ret = searchLitOp (tree->left, parent, ops); + if (ret) + return ret; + } + } + return NULL; +} + +const char * +getResultTypeName (RESULT_TYPE resultType) +{ + switch (resultType) + { + case RESULT_TYPE_NONE: return "RESULT_TYPE_NONE"; + case RESULT_TYPE_BOOL: return "RESULT_TYPE_BOOL"; + case RESULT_TYPE_CHAR: return "RESULT_TYPE_CHAR"; + case RESULT_TYPE_INT: return "RESULT_TYPE_INT"; + case RESULT_TYPE_OTHER: return "RESULT_TYPE_OTHER"; + case RESULT_TYPE_IFX: return "RESULT_TYPE_IFX"; + case RESULT_TYPE_GPTR: return "RESULT_TYPE_GPTR"; + } + return "invalid result type"; +} + +/*-----------------------------------------------------------------*/ +/* getResultFromType */ +/*-----------------------------------------------------------------*/ +RESULT_TYPE +getResultTypeFromType (sym_link * type) +{ + /* type = getSpec (type); */ + if (IS_BOOLEAN (type)) + return RESULT_TYPE_BOOL; + if (IS_BITFIELD (type)) + { + unsigned blen = SPEC_BLEN (type); + +/* BOOL and single bit BITFIELD are not interchangeable! + * There must be a cast to do this safely, in which case + * the previous IS_BOOLEAN test will handle it. + + if (blen <= 1) + return RESULT_TYPE_BOOL; +*/ + if (blen <= 8) + return RESULT_TYPE_CHAR; + return RESULT_TYPE_INT; + } + if (IS_CHAR (type)) + return RESULT_TYPE_CHAR; + if (IS_INT (type) && !IS_LONG (type) && !IS_LONGLONG (type)) + return RESULT_TYPE_INT; + return RESULT_TYPE_OTHER; +} + +/*-----------------------------------------------------------------*/ +/* addCast - adds casts to a type specified by RESULT_TYPE */ +/*-----------------------------------------------------------------*/ +static ast * +addCast (ast * tree, RESULT_TYPE resultType, bool promote) +{ + sym_link *newLink; + bool upCasted = FALSE; + + switch (resultType) + { + case RESULT_TYPE_NONE: + /* if thing smaller than int must be promoted to int */ + if (!promote || getSize (tree->etype) >= INTSIZE) + /* promotion not necessary or already an int */ + return tree; + /* char and bits: promote to int */ + newLink = newIntLink (); + upCasted = TRUE; + break; + case RESULT_TYPE_BOOL: + if (!promote || + /* already an int */ + bitsForType (tree->etype) >= 16 || + /* bit to bit operation: don't promote, the code generators + hopefully know everything about promotion rules */ + bitsForType (tree->etype) == 1) + return tree; + newLink = newIntLink (); + upCasted = TRUE; + break; + case RESULT_TYPE_CHAR: + if (IS_CHAR (tree->etype) || IS_FLOAT (tree->etype) || IS_FIXED (tree->etype)) + return tree; + newLink = newCharLink (); + break; + case RESULT_TYPE_INT: + case RESULT_TYPE_GPTR: +#if 0 + if (getSize (tree->etype) > INTSIZE) + { + /* warn ("Loosing significant digits"); */ + return; + } +#endif + /* char: promote to int */ + if (!promote || getSize (tree->etype) >= INTSIZE) + return tree; + newLink = newIntLink (); + upCasted = TRUE; + break; + case RESULT_TYPE_IFX: + case RESULT_TYPE_OTHER: + if (!promote || + /* return type is ifx, long, float: promote char to int */ + getSize (tree->etype) >= INTSIZE) + return tree; + newLink = newIntLink (); + upCasted = TRUE; + break; + default: + return tree; + } + tree->decorated = 0; + tree = newNode (CAST, newAst_LINK (newLink), tree); + tree->filename = tree->right->filename; + tree->lineno = tree->right->lineno; + /* keep unsigned type during cast to smaller type, + but not when promoting from char to int */ + if (!upCasted) + SPEC_USIGN (tree->left->opval.lnk) = IS_UNSIGNED (tree->right->etype) ? 1 : 0; + return decorateType (tree, resultType); +} + +/*-----------------------------------------------------------------*/ +/* resultTypePropagate - decides if resultType can be propagated */ +/*-----------------------------------------------------------------*/ +static RESULT_TYPE +resultTypePropagate (ast *tree, RESULT_TYPE resultType) +{ + switch (tree->opval.op) + { + case AND_OP: + case OR_OP: + case '!': + /* Logical operators should always propagate to boolean */ + return RESULT_TYPE_BOOL; + case '=': + case '?': + case ':': + case '|': + case '^': + case '~': + case LEFT_OP: + case LABEL: + case GETHBIT: + case GETABIT: + case GETBYTE: + case GETWORD: + return resultType; + case '*': + case '+': + case '-': + if ((IS_AST_VALUE (tree->left) && !IS_INTEGRAL (tree->left->opval.val->etype)) || + (IS_AST_VALUE (tree->right) && !IS_INTEGRAL (tree->right->opval.val->etype))) + return RESULT_TYPE_NONE; + return resultType; + case '&': + if (!tree->right) + /* can be unary */ + return RESULT_TYPE_NONE; + else + return resultType; + case IFX: + return RESULT_TYPE_IFX; + default: + return RESULT_TYPE_NONE; + } +} + +/*-----------------------------------------------------------------*/ +/* getLeftResultType - gets type from left branch for propagation */ +/*-----------------------------------------------------------------*/ +static RESULT_TYPE +getLeftResultType (ast * tree, RESULT_TYPE resultType) +{ + switch (tree->opval.op) + { + case '=': + case CAST: + if (IS_GENPTR (LTYPE (tree))) + return RESULT_TYPE_GPTR; + else if (IS_PTR (LTYPE (tree))) + return RESULT_TYPE_NONE; + else + return getResultTypeFromType (LETYPE (tree)); + case RETURN: + if (IS_PTR (currFunc->type->next)) + return RESULT_TYPE_NONE; + else + return getResultTypeFromType (currFunc->type->next); + case '[': + if (!IS_ARRAY (LTYPE (tree))) + return resultType; + if (DCL_ELEM (LTYPE (tree)) > 0 && getSize (tree->left->ftype) < 128) + return RESULT_TYPE_CHAR; // TODO: Instead of doing this optimization here, do it later on the iCode (where it probably could use a more efficient unsigned char). + return resultType; + default: + return resultType; + } +} + +/*------------------------------------------------------------------*/ +/* gatherImplicitVariables: assigns correct type information to */ +/* symbols and values created by replaceAstWithTemporary */ +/* and adds the symbols to the declarations list of the */ +/* innermost block that contains them */ +/*------------------------------------------------------------------*/ +void +gatherImplicitVariables (ast * tree, ast * block) +{ + if (!tree) + return; + + if (tree->type == EX_OP && tree->opval.op == BLOCK) + { + /* keep track of containing scope */ + block = tree; + } + if (tree->type == EX_OP && tree->opval.op == '=' && tree->left->type == EX_VALUE && tree->left->opval.val->sym) + { + symbol *assignee = tree->left->opval.val->sym; + + /* special case for assignment to compiler-generated temporary variable: + compute type of RHS, and set the symbol's type to match */ + if (assignee->type == NULL && assignee->infertype) + { + ast *dtr = decorateType (resolveSymbols (tree->right), RESULT_TYPE_NONE); + + if (dtr != tree->right) + tree->right = dtr; + + assignee->type = copyLinkChain (TTYPE (dtr)); + assignee->etype = getSpec (assignee->type); + SPEC_ADDRSPACE (assignee->etype) = 0; + SPEC_SCLS (assignee->etype) = S_AUTO; + SPEC_OCLS (assignee->etype) = NULL; + SPEC_EXTR (assignee->etype) = 0; + SPEC_STAT (assignee->etype) = 0; + SPEC_VOLATILE (assignee->etype) = 0; + SPEC_ABSA (assignee->etype) = 0; + SPEC_CONST (assignee->etype) = 0; + + wassertl (block != NULL, "implicit variable not contained in block"); + wassert (assignee->next == NULL); + if (block != NULL) + { + symbol **decl = &(block->values.sym); + + while (*decl) + { + wassert (*decl != assignee); /* should not already be in list */ + decl = &((*decl)->next); + } + + *decl = assignee; + } + } + } + if (tree->type == EX_VALUE && !(IS_LITERAL (tree->opval.val->etype)) && + tree->opval.val->type == NULL && tree->opval.val->sym && tree->opval.val->sym->infertype) + { + /* fixup type of value for compiler-inferred temporary var */ + tree->opval.val->type = tree->opval.val->sym->type; + tree->opval.val->etype = tree->opval.val->sym->etype; + } + + gatherImplicitVariables (tree->left, block); + gatherImplicitVariables (tree->right, block); +} + +/*-----------------------------------------------------------------*/ +/* CodePtrPointsToConst - if code memory is read-only, then */ +/* pointers to code memory implicitly point to constants. */ +/* Make this explicit. */ +/*-----------------------------------------------------------------*/ +void +CodePtrPointsToConst (sym_link * t) +{ + if (port->mem.code_ro) + { + while (t && t->next) + { + if (IS_CODEPTR (t)) + { + sym_link *t2 = t; + /* find the first non-array link */ + while (IS_ARRAY (t2->next)) + t2 = t2->next; + if (IS_SPEC (t2->next)) + SPEC_CONST (t2->next) = 1; + else + DCL_PTR_CONST (t2->next) = 1; + } + t = t->next; + } + } +} + +/*-----------------------------------------------------------------*/ +/* checkPtrCast - if casting to/from pointers, do some checking */ +/*-----------------------------------------------------------------*/ +void +checkPtrCast (sym_link *newType, sym_link *orgType, bool implicit, bool orgIsNullPtrConstant) +{ + int errors = 0; + + if (IS_ARRAY (orgType)) + { + value *val; + val = aggregateToPointer (valFromType (orgType)); + orgType = val->type; + Safe_free (val); + } + + if (IS_PTR (newType)) // to a pointer + { + if (!IS_PTR (orgType) && !IS_FUNC (orgType) && !IS_AGGREGATE (orgType)) // from a non pointer + { + if (IS_INTEGRAL (orgType)) + { + // maybe this is NULL, then it's ok. + if (!(IS_LITERAL (orgType) && (SPEC_CVAL (orgType).v_ulong == 0))) + { + if (GPTRSIZE > FARPTRSIZE && IS_GENPTR (newType) && !IS_FUNCPTR (newType)) + { + // no way to set the storage + if (IS_LITERAL (orgType)) + { + errors += werror (W_LITERAL_GENERIC); + } + else + { + errors += werror (W_NONPTR2_GENPTR); + } + } + else if (implicit) + { + errors += werror (W_INTEGRAL2PTR_NOCAST); + } + } + } + else + { + // shouldn't do that with float, array or structure unless to void + if (!IS_VOID (getSpec (newType)) && !(IS_CODEPTR (newType) && IS_FUNC (newType->next) && IS_FUNC (orgType))) + { + errors += werror (E_INCOMPAT_TYPES); + } + } + } + else // from a pointer to a pointer + { + if (implicit && getAddrspace (newType->next) != getAddrspace (orgType->next)) + { + errors += werror (E_INCOMPAT_PTYPES); + } + else if (IS_GENPTR (newType) && IS_VOID (newType->next)) // cast to void* is always allowed + { + if (IS_FUNCPTR (orgType)) + errors += werror (FUNCPTRSIZE > GPTRSIZE ? E_INCOMPAT_PTYPES : W_INCOMPAT_PTYPES); + } + else if (IS_GENPTR (orgType) && IS_VOID (orgType->next)) // cast from void* is always allowed - as long as we cast to a pointer to an object type + { + if (IS_FUNCPTR (newType) && !orgIsNullPtrConstant) // cast to pointer to function is only allowed for null pointer constants + errors += werror (W_INCOMPAT_PTYPES); + } + else if (GPTRSIZE > FARPTRSIZE /*!TARGET_IS_Z80 && !TARGET_IS_GBZ80 */ ) + { + // if not a pointer to a function + if (!(IS_CODEPTR (newType) && IS_FUNC (newType->next) && IS_FUNC (orgType))) + { + if (implicit) // if not to generic, they have to match + { + if (!IS_GENPTR (newType) && + !((DCL_TYPE (orgType) == DCL_TYPE (newType)) || + ((DCL_TYPE (orgType) == POINTER) && (DCL_TYPE (newType) == IPOINTER)))) + { + errors += werror (E_INCOMPAT_PTYPES); + } + } + } + } + } + } + else // to a non pointer + { + if (IS_PTR (orgType)) // from a pointer + { + if (implicit) // sneaky + { + if (IS_INTEGRAL (newType)) + { + errors += werror (W_PTR2INTEGRAL_NOCAST); + } + else // shouldn't do that with float, array or structure + { + errors += werror (E_INCOMPAT_TYPES); + } + } + } + } + if (errors) + { + printFromToType (orgType, newType); + } +} + +/*-----------------------------*/ +/* check if div or mod by zero */ +/*-----------------------------*/ +static void +checkZero (value *val) +{ + if (!val) + return; + + if (IS_FLOAT (val->type) || IS_FIXED16X16 (val->type)) + { + if (floatFromVal (val) == 0.0) + werror (E_DIVIDE_BY_ZERO); + } + else if (SPEC_LONGLONG (val->type)) + { + if (ullFromVal (val) == 0LL) + werror (E_DIVIDE_BY_ZERO); + } + else if (ulFromVal (val) == 0L) + { + werror (E_DIVIDE_BY_ZERO); + } +} + +/*--------------------------------------------------------*/ +/* return true if subtree is somewhere in */ +/*--------------------------------------------------------*/ +static bool +isAstInAst (ast *tree, ast *search) +{ + if (tree == search) + return TRUE; + if (!tree) + return FALSE; + if (tree->type == EX_OP) + { + if (isAstInAst (tree->left, search)) + return TRUE; + if (isAstInAst (tree->right, search)) + return TRUE; + } + return FALSE; +} + +/*---------------------------------------*/ +/* make a simple copy of single ast node */ +/*---------------------------------------*/ +static ast * +copyAstNode (ast *tree) +{ + ast * newtree; + + newtree = newAst_(tree->type); + newtree->lineno = tree->lineno; + newtree->filename = tree->filename; + newtree->level = tree->level; + newtree->block = tree->block; + newtree->initMode = tree->initMode; + newtree->seqPoint = tree->seqPoint; + newtree->funcName = tree->funcName; + newtree->reversed = tree->reversed; + newtree->decorated = tree->decorated; + + if (tree->ftype) + newtree->etype = getSpec (newtree->ftype = copyLinkChain (tree->ftype)); + + if (tree->type == EX_VALUE) + { + newtree->opval.val = tree->opval.val; + } + else if (tree->type == EX_LINK) + { + newtree->opval.lnk = tree->opval.lnk; + } + else + { + newtree->opval.op = tree->opval.op; + copyAstValues (newtree, tree); + newtree->left = tree->left; + newtree->right = tree->right; + + newtree->trueLabel = tree->trueLabel; + newtree->falseLabel = tree->falseLabel; + } + + return newtree; +} + +/*-------------------------------------------------*/ +/* extract the ast subtrees that have side-effects */ +/*-------------------------------------------------*/ +static void +rewriteAstGatherSideEffects (ast *tree, set ** sideEffects) +{ + if (!tree) + return; + + if (tree->type == EX_OP) + { + switch (tree->opval.op) + { + case '=': + case INC_OP: + case DEC_OP: + case CALL: + case PCALL: + addSetHead (sideEffects, tree); + break; + default: + if (tree->left) + rewriteAstGatherSideEffects (tree->left, sideEffects); + if (tree->right) + rewriteAstGatherSideEffects (tree->right, sideEffects); + break; + } + return; + } + if (TETYPE (tree) && IS_VOLATILE (TETYPE (tree))) + { + addSetHead (sideEffects, tree); + return; + } +} + +/*-------------------------------------------------------------------*/ +/* after rewriting a node, rejoin the portions of any old left/right */ +/* subtree that had side effects with a comma operator */ +/*-------------------------------------------------------------------*/ +static void +rewriteAstJoinSideEffects (ast *tree, ast *oLeft, ast *oRight) +{ + set * sideEffects = NULL; + ast * sefTree; + + /* If the old left or right subtree has been orphaned, */ + /* gather any side-effects in it */ + if (oLeft && !isAstInAst (tree, oLeft)) + rewriteAstGatherSideEffects (oLeft, &sideEffects); + if (oRight && !isAstInAst (tree, oRight)) + rewriteAstGatherSideEffects (oRight, &sideEffects); + + /* Join any side-effects found */ + for (sefTree = setFirstItem (sideEffects); sefTree; sefTree = setNextItem (sideEffects)) + { + tree->right = copyAstNode (tree); + tree->left = sefTree; + tree->type = EX_OP; + tree->opval.op = ','; + } + + deleteSet (&sideEffects); +} + +static void +optStdLibCall (ast *tree, RESULT_TYPE resulttype) +{ + ast *parms = tree->right; + ast *func = tree->left; + + if (!TARGET_IS_STM8 && !TARGET_Z80_LIKE && !TARGET_PDK_LIKE) // Regression test gcc-torture-execute-20121108-1.c fails to build for hc08 and mcs51 (without --stack-auto) + return; + + if (!IS_FUNC (func->ftype) || IS_LITERAL (func->ftype) || func->type != EX_VALUE || !func->opval.val->sym) + return; + + const char *funcname = func->opval.val->sym->name; + + unsigned int nparms = 0; + ast *parm; + for (parm = parms; parm && parm->type == EX_OP && parm->opval.op == PARAM; parm = parm->right) + if (parm->left) + nparms++; + if (parm) + nparms++; + + // Optimize printf() to puts(). + if (!strcmp(funcname, "printf") && nparms == 1 && resulttype == RESULT_TYPE_NONE) + { + ast *parm = parms; + + if (parm->type == EX_OP && parm->opval.op == CAST) + parm = parm->right; + + if (parm->type != EX_VALUE || !IS_ARRAY (parm->opval.val->type) || !parm->opval.val->sym) + return; + + size_t strlength = DCL_ELEM (parm->opval.val->type); + symbol *strsym = parm->opval.val->sym; + sym_link *strlink = strsym->etype; + + if (strsym->isstrlit != 1 || !strlink || !IS_SPEC(strlink) || SPEC_NOUN (strlink) != V_CHAR) + return; + + for (size_t i = 0; i < strlength; i++) + if (SPEC_CVAL (strlink).v_char[i] == '%') + return; + if(strlength < 2 || SPEC_CVAL (strlink).v_char[strlength - 2] != '\n') + return; + + symbol *puts_sym = findSym (SymbolTab, NULL, "puts"); + + if(!puts_sym) + return; + + // Do the equivalent of + // DCL_ELEM (strsym->type)--; + // but in a way that works better with the reuse of string symbols + { + struct dbuf_s dbuf; + dbuf_init (&dbuf, strlength - 1); + wassert (dbuf_append (&dbuf, SPEC_CVAL (strlink).v_char, strlength - 1)); + ((char *)(dbuf_get_buf (&dbuf)))[strlength - 2] = 0; + + parm->opval.val = stringToSymbol (rawStrVal (dbuf_get_buf (&dbuf), strlength - 1)); + dbuf_destroy (&dbuf); + + freeStringSymbol (strsym); + } + + func->opval.val->sym = puts_sym; + } + // Optimize strcpy() to memcpy(). + else if (!strcmp(funcname, "strcpy") && nparms == 2) + { + ast *parm = parms->right; + + if (parm->type == EX_OP && parm->opval.op == CAST) + parm = parm->right; + + if (parm->type != EX_VALUE || !IS_ARRAY (parm->opval.val->type) || !parm->opval.val->sym) + return; + + size_t strlength = DCL_ELEM (parm->opval.val->type); + symbol *strsym = parm->opval.val->sym; + sym_link *strlink = strsym->etype; + + if (!strsym->isstrlit || !strlink || !IS_SPEC(strlink) || SPEC_NOUN (strlink) != V_CHAR) + return; + + for (size_t i = 0; i < strlength; i++) + if (!SPEC_CVAL (strlink).v_char[i]) + { + strlength = i + 1; + break; + } + + size_t minlength; // Minimum string length for replacement. + if (TARGET_IS_STM8) + minlength = optimize.codeSize ? SIZE_MAX : 12; + else // TODO:Check for other targets when memcpy() is a better choice than strcpy; + minlength = SIZE_MAX; + + if (strlength < minlength) + return; + + symbol *memcpy_sym = findSym (SymbolTab, NULL, "memcpy"); + + if(!memcpy_sym) + return; + + ast *lengthparm = newAst_VALUE (valCastLiteral (newIntLink(), strlength, strlength)); + decorateType (lengthparm, RESULT_TYPE_NONE); + ast *node = newAst_OP (PARAM); + node->left = parm; + node->right = lengthparm; + node->decorated = 1; + parms->right = node; + func->opval.val->sym = memcpy_sym; + } +} + +/*--------------------------------------------------------------*/ +/* rewrite ast node as an operator node and rejoin any orphaned */ +/* side-effects with a comma operator */ +/*--------------------------------------------------------------*/ +static void +rewriteAstNodeOp (ast *tree, int op, ast *left, ast *right) +{ + ast *oLeft = (tree->type == EX_OP) ? tree->left : NULL; + ast *oRight = (tree->type == EX_OP) ? tree->right : NULL; + + tree->type = EX_OP; + tree->opval.op = op; + tree->left = left; + tree->right = right; + tree->decorated = 0; + + rewriteAstJoinSideEffects (tree, oLeft, oRight); +} + +/*----------------------------------------------------------*/ +/* rewrite ast node as a value node and rejoin any orphaned */ +/* side-effects with a comma operator */ +/*----------------------------------------------------------*/ +static void +rewriteAstNodeVal (ast *tree, value *val) +{ + ast *oLeft = (tree->type == EX_OP) ? tree->left : NULL; + ast *oRight = (tree->type == EX_OP) ? tree->right : NULL; + + tree->type = EX_VALUE; + tree->opval.val = val; + tree->left = NULL; + tree->right = NULL; + TETYPE (tree) = getSpec (TTYPE (tree) = tree->opval.val->type); + tree->decorated = 0; + + rewriteAstJoinSideEffects (tree, oLeft, oRight); +} + +/*-----------------------------------------------------------*/ +/* rewrite struct assignment "a = b" to something similar to */ +/* "__builtin_memcpy (&a, &b, sizeof (a)), a" or, if a has */ +/* side effects, "*(__builtin_memcpy (&a, &b, sizeof (a)))" */ +/*-----------------------------------------------------------*/ +ast * +rewriteStructAssignment (ast *tree) +{ + /* prepare pointer to destination */ + ast *dest = newNode ('&', tree->left, NULL); + copyAstLoc (dest, tree->left); + + /* prepare remaining arguments */ + ast *src = newNode ('&', tree->right, NULL); + copyAstLoc (src, tree->right); + ast *size = newNode (SIZEOF, NULL, tree->left); + copyAstLoc (size, tree->left); + ast *srcsize = newNode (PARAM, src, size); + copyAstLoc (srcsize, tree); + ast *params = newNode (PARAM, dest, srcsize); + copyAstLoc (params, tree); + + /* create call to the appropriate memcpy function */ + ast *memcpy_ast = newAst_VALUE (symbolVal (memcpy_builtin)); + copyAstLoc (memcpy_ast, tree); + ast *call = newNode (CALL, memcpy_ast, params); + copyAstLoc (call, tree); + + /* assemble the result expression depending on side effects */ + ast *newTree; + if (hasSEFcalls (dest)) + { + /* memcpy returns the dest pointer -> dereference it */ + newTree = newNode ('*', call, NULL); + } + else + { + /* no side effects -> dereference dest pointer itself */ + ast *destderef = newNode ('*', dest, NULL); + copyAstLoc (destderef, dest); + newTree = newNode (',', call, destderef); + } + + /* copy source location and return decorated result */ + copyAstLoc (newTree, tree); + return decorateType (newTree, RESULT_TYPE_OTHER); +} + +/*--------------------------------------------------------------------*/ +/* decorateType - compute type for this tree, also does type checking.*/ +/* This is done bottom up, since type has to flow upwards. */ +/* resultType flows top-down and forces e.g. char-arithmetic, if the */ +/* result is a char and the operand(s) are int's. */ +/* It also does constant folding, and parameter checking. */ +/*--------------------------------------------------------------------*/ +ast * +decorateType (ast *tree, RESULT_TYPE resultType) +{ + int parmNumber; + sym_link *p; + RESULT_TYPE resultTypeProp; + + if (!tree) + return tree; + + /* if already has type then do nothing */ + if (tree->decorated) + return tree; + + tree->decorated = 1; + +#if 0 + /* print the line */ + /* if not block & function */ + if (tree->type == EX_OP && (tree->opval.op != FUNCTION && tree->opval.op != BLOCK && tree->opval.op != NULLOP)) + { + filename = tree->filename; + lineno = tree->lineno; + } +#endif + + /* if any child is an error | this one is an error do nothing */ + if (tree->isError || (tree->left && tree->left->isError) || (tree->right && tree->right->isError)) + return tree; + +/*------------------------------------------------------------------*/ +/*----------------------------*/ +/* leaf has been reached */ +/*----------------------------*/ + filename = tree->filename; + lineno = tree->lineno; + /* if this is of type value */ + /* just get the type */ + if (tree->type == EX_VALUE) + { + if (IS_LITERAL (tree->opval.val->etype)) + { + /* if this is a character array then declare it */ + if (IS_ARRAY (tree->opval.val->type)) + tree->opval.val = stringToSymbol (tree->opval.val); + + /* otherwise just copy the type information */ + COPYTYPE (TTYPE (tree), TETYPE (tree), tree->opval.val->type); + return tree; + } + + if (tree->opval.val->sym) + { + /* if the undefined flag is set then give error message */ + if (tree->opval.val->sym->undefined) + { + werrorfl (tree->filename, tree->lineno, E_ID_UNDEF, tree->opval.val->sym->name); + /* assume int */ + TTYPE (tree) = TETYPE (tree) = + tree->opval.val->type = tree->opval.val->sym->type = + tree->opval.val->etype = tree->opval.val->sym->etype = copyLinkChain (INTTYPE); + } + else if (tree->opval.val->sym->implicit) + { + /* if implicit i.e. struct/union member then no type */ + TTYPE (tree) = TETYPE (tree) = NULL; + } + else + { + /* copy the type from the value into the ast */ + COPYTYPE (TTYPE (tree), TETYPE (tree), tree->opval.val->type); + + /* and mark the symbol as referenced */ + tree->opval.val->sym->isref = 1; + } + } + else + { + /* unreached: all values are literals or symbols */ + wassert (0); + } + + return tree; + } + + /* if type link for the case of cast */ + if (tree->type == EX_LINK) + { + COPYTYPE (TTYPE (tree), TETYPE (tree), tree->opval.lnk); + return tree; + } + + { + ast *dtl, *dtr; + +#if 0 + if (tree->opval.op == NULLOP || tree->opval.op == BLOCK) + { + if (tree->left && tree->left->type == EX_OPERAND + && (tree->left->opval.op == INC_OP || tree->left->opval.op == DEC_OP) && tree->left->left) + { + tree->left->right = tree->left->left; + tree->left->left = NULL; + } + if (tree->right && tree->right->type == EX_OPERAND + && (tree->right->opval.op == INC_OP || tree->right->opval.op == DEC_OP) && tree->right->left) + { + tree->right->right = tree->right->left; + tree->right->left = NULL; + } + } +#endif + + /* Before decorating the left branch we've to decide in dependence + upon tree->opval.op, if resultType can be propagated */ + resultTypeProp = resultTypePropagate (tree, resultType); + + if ((tree->opval.op == '?') && (resultTypeProp != RESULT_TYPE_BOOL)) + dtl = decorateType (tree->left, RESULT_TYPE_IFX); + else + dtl = decorateType (tree->left, resultTypeProp); + + /* if an array node, we may need to swap branches */ + if (tree->opval.op == '[') + { + /* determine which is the array & which the index */ + if ((IS_ARRAY (RTYPE (tree)) || IS_PTR (RTYPE (tree))) && IS_INTEGRAL (LTYPE (tree))) + { + ast *tempTree = tree->left; + tree->left = tree->right; + tree->right = tempTree; + } + } + + /* After decorating the left branch there's type information available + in tree->left->?type. If the op is e.g. '=' we extract the type + information from there and propagate it to the right branch. */ + resultTypeProp = getLeftResultType (tree, resultTypeProp); + + switch (tree->opval.op) + { + case '?': + /* delay right side for '?' operator since conditional macro + expansions might rely on this */ + dtr = tree->right; + break; + case CALL: + /* decorate right side for CALL (parameter list) in processParms(); + there is resultType available */ + dtr = tree->right; + break; + case SIZEOF: + /* don't allocate string if it is a sizeof argument */ + ++noAlloc; + dtr = decorateType (tree->right, resultTypeProp); + --noAlloc; + break; + default: + dtr = decorateType (tree->right, resultTypeProp); + break; + } + + /* this is to take care of situations + when the tree gets rewritten */ + if (dtl != tree->left) + tree->left = dtl; + if (dtr != tree->right) + tree->right = dtr; + if ((dtl && dtl->isError) || (dtr && dtr->isError)) + return tree; + } + + /* depending on type of operator do */ + + switch (tree->opval.op) + { + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* array node */ + /*----------------------------*/ + case '[': + /* Swap trees if right side is array or a pointer */ + if (IS_ARRAY (RTYPE (tree)) || IS_PTR (RTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* check if this is an array or a pointer */ + if ((!IS_ARRAY (LTYPE (tree))) && (!IS_PTR (LTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_NEED_ARRAY_PTR, "[]"); + goto errorTreeReturn; + } + + /* check the type of the idx */ + if (!IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_IDX_NOT_INT); + goto errorTreeReturn; + } + + /* if the left is an rvalue then error */ + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "array access"); + goto errorTreeReturn; + } + + if (IS_LITERAL (RTYPE (tree))) + { + int arrayIndex = (int) ulFromVal (valFromType (RETYPE (tree))); + int arraySize = DCL_ELEM (LTYPE (tree)); + if (arraySize && arrayIndex >= arraySize) + { + werrorfl (tree->filename, tree->lineno, W_IDX_OUT_OF_BOUNDS, arrayIndex, arraySize); + } + } + + RRVAL (tree) = 1; + TTYPE (tree) = copyLinkChain (LTYPE (tree)->next); + TETYPE (tree) = getSpec (TTYPE (tree)); + + if (IS_PTR (LTYPE (tree)) /* && !IS_LITERAL (TETYPE (tree)) caused bug #2850 */) + { + SPEC_SCLS (TETYPE (tree)) = sclsFromPtr (LTYPE (tree)); + } + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* struct/union */ + /*----------------------------*/ + case '.': + /* if this is not a structure */ + if (!IS_STRUCT (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_STRUCT_UNION, "."); + goto errorTreeReturn; + } + TTYPE (tree) = structElemType (LTYPE (tree), (tree->right->type == EX_VALUE ? tree->right->opval.val : NULL)); + TETYPE (tree) = getSpec (TTYPE (tree)); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* struct/union pointer */ + /*----------------------------*/ + case PTR_OP: + /* if not pointer to a structure */ + if (!IS_PTR (LTYPE (tree)) && !IS_ARRAY (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PTR_REQD); + goto errorTreeReturn; + } + + if (!IS_STRUCT (LTYPE (tree)->next)) + { + werrorfl (tree->filename, tree->lineno, E_STRUCT_UNION, "->"); + goto errorTreeReturn; + } + + TTYPE (tree) = structElemType (LTYPE (tree)->next, (tree->right->type == EX_VALUE ? tree->right->opval.val : NULL)); + TETYPE (tree) = getSpec (TTYPE (tree)); + + /* adjust the storage class */ + if (DCL_TYPE (LTYPE (tree)) != ARRAY) + { + setOClass (LTYPE (tree), TETYPE (tree)); + SPEC_SCLS (TETYPE (tree)) = sclsFromPtr (LTYPE (tree)); + SPEC_ADDRSPACE (TETYPE (tree)) = DCL_PTR_ADDRSPACE (LTYPE (tree)); + } + /* This breaks with extern declarations, bit-fields, and perhaps other */ + /* cases (gcse). Let's leave this optimization disabled for now and */ + /* ponder if there's a safe way to do this. -- EEP */ +#if 0 + if (IS_ADDRESS_OF_OP (tree->left) && IS_AST_SYM_VALUE (tree->left->left) + && SPEC_ABSA (AST_SYMBOL (tree->left->left)->etype)) + { + /* If defined struct type at addr var + then rewrite (&struct var)->member + as temp + and define membertype at (addr+offsetof(struct var,member)) temp + */ + symbol *sym; + symbol *element = getStructElement (SPEC_STRUCT (LETYPE (tree)), + AST_SYMBOL (tree->right)); + + sym = newSymbol (genSymName (0), 0); + sym->type = TTYPE (tree); + sym->etype = getSpec (sym->type); + sym->lineDef = tree->lineno; + sym->cdef = 1; + sym->isref = 1; + SPEC_STAT (sym->etype) = 1; + SPEC_ADDR (sym->etype) = SPEC_ADDR (AST_SYMBOL (tree->left->left)->etype) + element->offset; + SPEC_ABSA (sym->etype) = 1; + addSym (SymbolTab, sym, sym->name, 0, 0, 0); + allocGlobal (sym); + + AST_VALUE (tree) = symbolVal (sym); + TLVAL (tree) = 1; + TRVAL (tree) = 0; + tree->type = EX_VALUE; + tree->left = NULL; + tree->right = NULL; + } +#endif + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* ++/-- operation */ + /*----------------------------*/ + case INC_OP: + case DEC_OP: + { + sym_link *ltc = (tree->right ? RTYPE (tree) : LTYPE (tree)); + COPYTYPE (TTYPE (tree), TETYPE (tree), ltc); + if (!tree->initMode && IS_CONSTANT (TTYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, tree->opval.op == INC_OP ? "++" : "--"); + + if (tree->right) + RLVAL (tree) = 1; + else + LLVAL (tree) = 1; + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* bitwise and */ + /*----------------------------*/ + case '&': /* can be unary */ + /* if right is NULL then unary operation */ + if (tree->right) /* not a unary operation */ + { + ast *otree; + + if (!IS_INTEGRAL (LTYPE (tree)) || !IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_BITWISE_OP); + werrorfl (tree->filename, tree->lineno, W_CONTINUE, "left & right types are "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, ","); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + + /* if they are both literal */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valBitwise (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)), '&'); + + tree->right = tree->left = NULL; + TETYPE (tree) = tree->opval.val->etype; + TTYPE (tree) = tree->opval.val->type; + return tree; + } + + /* if left is a literal exchange left & right */ + if (IS_LITERAL (LTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* if right is a literal and */ + /* we can find a 2nd literal in an and-tree then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree))) + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "&"); + if (litTree) + { + DEBUG_CF ("&") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tTree; + /* both operands in litTree are literal now */ + decorateType (parent, resultType); + } + } + + /* if ANDing boolean with literal then reduce literal LSB to boolean */ + if (IS_LITERAL (RTYPE (tree)) && IS_BOOLEAN (LTYPE (tree))) + { + unsigned long litval = AST_ULONG_VALUE (tree->right); + tree->right = decorateType (newAst_VALUE (constBoolVal (litval & 1)), resultType); + } + + /* see if this is a GETHBIT operation if yes + then return that */ + otree = optimizeGetHbit (tree, resultType); + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + + /* see if this is a GETABIT operation if yes + then return that */ + otree = optimizeGetAbit (tree, resultType); + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + + /* see if this is a GETBYTE operation if yes + then return that */ + otree = optimizeGetByte (tree, resultType); + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + + /* see if this is a GETWORD operation if yes + then return that */ + otree = optimizeGetWord (tree, resultType); + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + + /* if right is a literal and has the same size with left, + then also sync their signess to avoid unecessary cast */ + if (IS_LITERAL (RTYPE (tree)) && getSize (RTYPE (tree)) == getSize (LTYPE (tree))) + SPEC_USIGN (RTYPE (tree)) = SPEC_USIGN (LTYPE (tree)); + + LRVAL (tree) = RRVAL (tree) = 1; + + TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op); + TETYPE (tree) = getSpec (TTYPE (tree)); + + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* address of */ + /*----------------------------*/ + if (IS_FUNC (LTYPE (tree))) + { + // this ought to be ignored + return (tree->left); + } + + /* if bit field then error */ + if (IS_BITFIELD (tree->left->etype) || (IS_BITVAR (tree->left->etype) && TARGET_MCS51_LIKE)) + { + werrorfl (tree->filename, tree->lineno, E_ILLEGAL_ADDR, "address of bit variable"); + goto errorTreeReturn; + } + + if (LETYPE (tree) && SPEC_SCLS (tree->left->etype) == S_REGISTER) + { + werrorfl (tree->filename, tree->lineno, E_ILLEGAL_ADDR, "address of register variable"); + goto errorTreeReturn; + } + + if (IS_LITERAL (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_ILLEGAL_ADDR, "address of literal"); + goto errorTreeReturn; + } + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "address of"); + goto errorTreeReturn; + } + + p = newLink (DECLARATOR); + if (!LETYPE (tree)) + DCL_TYPE (p) = POINTER; + else if (SPEC_SCLS (LETYPE (tree)) == S_CODE) + DCL_TYPE (p) = CPOINTER; + else if (SPEC_SCLS (LETYPE (tree)) == S_XDATA) + DCL_TYPE (p) = FPOINTER; + else if (SPEC_SCLS (LETYPE (tree)) == S_XSTACK) + DCL_TYPE (p) = PPOINTER; + else if (SPEC_SCLS (LETYPE (tree)) == S_IDATA) + DCL_TYPE (p) = IPOINTER; + else if (SPEC_SCLS (LETYPE (tree)) == S_EEPROM) + DCL_TYPE (p) = EEPPOINTER; + else if (SPEC_OCLS (LETYPE (tree))) + DCL_TYPE (p) = PTR_TYPE (SPEC_OCLS (LETYPE (tree))); + else + DCL_TYPE (p) = POINTER; + + if (IS_AST_SYM_VALUE (tree->left)) + { + AST_SYMBOL (tree->left)->addrtaken = 1; + AST_SYMBOL (tree->left)->allocreq = 1; + } + + p->next = LTYPE (tree); + TTYPE (tree) = p; + TETYPE (tree) = getSpec (TTYPE (tree)); + LLVAL (tree) = 1; + TLVAL (tree) = 1; + +#if 0 + if (IS_AST_OP (tree->left) && tree->left->opval.op == PTR_OP + && IS_AST_VALUE (tree->left->left) && !IS_AST_SYM_VALUE (tree->left->left)) + { + symbol *element = getStructElement (SPEC_STRUCT (LETYPE (tree->left)), + AST_SYMBOL (tree->left->right)); + AST_VALUE (tree) = valPlus (AST_VALUE (tree->left->left), valueFromLit (element->offset)); + tree->left = NULL; + tree->right = NULL; + tree->type = EX_VALUE; + tree->values.cast.literalFromCast = 1; + } +#endif + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* bitwise or */ + /*----------------------------*/ + case '|': + /* if the rewrite succeeds then don't go any further */ + { + ast *wtree = optimizeRRCRLC (tree); + if (wtree != tree) + return decorateType (wtree, RESULT_TYPE_NONE); + + wtree = optimizeSWAP (tree); + if (wtree != tree) + return decorateType (wtree, RESULT_TYPE_NONE); + } + + /* if left is a literal exchange left & right */ + if (IS_LITERAL (LTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* if right is a literal and */ + /* we can find a 2nd literal in an or-tree then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree))) + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "|"); + if (litTree) + { + DEBUG_CF ("|") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tTree; + /* both operands in tTree are literal now */ + decorateType (parent, resultType); + } + } + + /* if ORing boolean with literal then reduce literal to boolean */ + if (IS_LITERAL (RTYPE (tree)) && + IS_BOOLEAN (LTYPE (tree)) && + IS_INTEGRAL (RTYPE (tree)) && + resultType == RESULT_TYPE_BOOL) + { + unsigned long litval = AST_ULONG_VALUE (tree->right); + tree->right = decorateType (newAst_VALUE (constBoolVal (litval != 0)), resultType); + } + + /* fall through */ + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* bitwise xor */ + /*----------------------------*/ + case '^': + if (!IS_INTEGRAL (LTYPE (tree)) || !IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_BITWISE_OP); + werrorfl (tree->filename, tree->lineno, W_CONTINUE, "left & right types are "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, ","); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + + /* if they are both literal then rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valBitwise (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)), tree->opval.op)); + return decorateType (tree, resultType); + } + + /* if left is a literal exchange left & right */ + if (IS_LITERAL (LTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* if right is a literal and */ + /* we can find a 2nd literal in a xor-tree then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree)) && tree->opval.op == '^') /* the same source is used by 'bitwise or' */ + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "^"); + if (litTree) + { + DEBUG_CF ("^") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tTree; + /* both operands in litTree are literal now */ + decorateType (parent, resultType); + } + } + + /* if XORing boolean with literal then reduce literal to boolean */ + if (IS_LITERAL (RTYPE (tree)) && + IS_BOOLEAN (LTYPE (tree)) && + IS_INTEGRAL (RTYPE (tree)) && + resultType == RESULT_TYPE_BOOL && + tree->opval.op == '^') /* the same source is used by 'bitwise or' */ + { + unsigned long litval = AST_ULONG_VALUE (tree->right); + if (litval == 0 || litval == 1) + { + tree->right = decorateType (newAst_VALUE (constBoolVal (litval != 0)), resultType); + } + else + { + tree->opval.op = '|'; + tree->right = newAst_VALUE (constBoolVal (1)); + tree->decorated = 0; + return decorateType (tree, resultType); + } + } + + /* if right is a literal and has the same size with left, + then also sync their signess to avoid unecessary cast */ + if (IS_LITERAL (RTYPE (tree)) && getSize (RTYPE (tree)) == getSize (LTYPE (tree))) + SPEC_USIGN (RTYPE (tree)) = SPEC_USIGN (LTYPE (tree)); + + LRVAL (tree) = RRVAL (tree) = 1; + + TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op); + TETYPE (tree) = getSpec (TTYPE (tree)); + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* division */ + /*----------------------------*/ + case '/': + if (!IS_ARITHMETIC (LTYPE (tree)) || !IS_ARITHMETIC (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_INVALID_OP, "divide"); + goto errorTreeReturn; + } + /* check if div by zero */ + if (IS_LITERAL (RTYPE (tree)) && !IS_LITERAL (LTYPE (tree))) + checkZero (valFromType (RETYPE (tree))); + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valDiv (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)))); + return decorateType (tree, resultType); + } + + LRVAL (tree) = RRVAL (tree) = 1; + + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); + + /* if right is a literal and */ + /* left is also a division by a literal then */ + /* rearrange the tree */ +#if 0 + /* This converts (a/b)/c into a/(b*c)/1, where b and c are literals. */ + /* Algebraically, this is fine, but may fail as an optimization if */ + /* b*c overflows or causes the expression to have a different resultant */ + /* type. I don't think it's worth the effort to sort out the cases of */ + /* when this is safe or not safe, so I am just going to leave this */ + /* disabled. -- EEP -- 15 Nov 2012 */ + if (IS_LITERAL (RTYPE (tree)) + /* avoid infinite loop */ + && (TYPE_TARGET_ULONG) ulFromVal (tree->right->opval.val) != 1) + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "/"); + if (litTree) + { + if (IS_LITERAL (RTYPE (litTree))) + { + /* foo_div */ + DEBUG_CF ("div r") litTree->right = newNode ('*', litTree->right, copyAst (tree->right)); + litTree->right->filename = tree->filename; + litTree->right->lineno = tree->lineno; + + tree->right->opval.val = constCharVal (1); + decorateType (parent, resultType); + } + else + { + /* litTree->left is literal: no gcse possible. + We can't call decorateType(parent, RESULT_TYPE_NONE), because + this would cause an infinit loop. */ + parent->decorated = 1; + decorateType (litTree, resultType); + } + } + } +#endif + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* modulus */ + /*----------------------------*/ + case '%': + if (!IS_INTEGRAL (LTYPE (tree)) || !IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_BITWISE_OP); + werrorfl (tree->filename, tree->lineno, W_CONTINUE, "left & right types are "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, ","); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + /* check if div by zero */ + if (IS_LITERAL (RTYPE (tree)) && !IS_LITERAL (LTYPE (tree))) + checkZero (valFromType (RETYPE (tree))); + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valMod (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)))); + return decorateType (tree, resultType); + } + LRVAL (tree) = RRVAL (tree) = 1; + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* address dereference */ + /*----------------------------*/ + case '*': /* can be unary : if right is null then unary operation */ + if (!tree->right) + { + if (!IS_PTR (LTYPE (tree)) && !IS_ARRAY (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PTR_REQD); + goto errorTreeReturn; + } + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "pointer deref"); + goto errorTreeReturn; + } + if (IS_ADDRESS_OF_OP (tree->left)) + { + /* replace *&obj with obj */ + return tree->left->left; + } + TTYPE (tree) = copyLinkChain (LTYPE (tree)->next); + TETYPE (tree) = getSpec (TTYPE (tree)); + /* adjust the storage class */ + if (DCL_TYPE (tree->left->ftype) != ARRAY && DCL_TYPE (tree->left->ftype) != FUNCTION) + SPEC_SCLS (TETYPE (tree)) = sclsFromPtr (tree->left->ftype); + + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* multiplication */ + /*----------------------------*/ + if (!IS_ARITHMETIC (LTYPE (tree)) || !IS_ARITHMETIC (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_INVALID_OP, "multiplication"); + goto errorTreeReturn; + } + + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valMult (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)))); + return decorateType (tree, resultType); + } + + /* if left is a literal exchange left & right */ + if (IS_LITERAL (LTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* if right is a literal and */ + /* we can find a 2nd literal in a mul-tree then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree))) + { + ast *parent; + ast *litTree = searchLitOp (tree, &parent, "*"); + if (litTree) + { + DEBUG_CF ("mul") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tTree; + /* both operands in litTree are literal now */ + decorateType (parent, resultType); + } + } + + LRVAL (tree) = RRVAL (tree) = 1; + + { // cast happen only if both left and right can be casted to result type + ast *l = addCast (tree->left, resultTypeProp, FALSE); + ast *r = addCast (tree->right, resultTypeProp, FALSE); + if (l != tree->left && r != tree->right) + { + tree->left = l; + tree->right = r; + } + } + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* unary '+' operator */ + /*----------------------------*/ + case '+': + /* if unary plus */ + if (!tree->right) + { + if (!IS_ARITHMETIC (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_UNARY_OP, '+'); + goto errorTreeReturn; + } + + /* if left is a literal then do it */ + if (IS_LITERAL (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valFromType (LETYPE (tree)); + tree->left = NULL; + TETYPE (tree) = TTYPE (tree) = tree->opval.val->type; + return tree; + } + LRVAL (tree) = 1; + COPYTYPE (TTYPE (tree), TETYPE (tree), LTYPE (tree)); + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* addition */ + /*----------------------------*/ + + /* this is not a unary operation */ + /* if both pointers then problem */ + if ((IS_PTR (LTYPE (tree)) || IS_ARRAY (LTYPE (tree))) && (IS_PTR (RTYPE (tree)) || IS_ARRAY (RTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PTR_PLUS_PTR); + goto errorTreeReturn; + } + + if (!IS_ARITHMETIC (LTYPE (tree)) && !IS_PTR (LTYPE (tree)) && !IS_ARRAY (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "+"); + goto errorTreeReturn; + } + + if (!IS_ARITHMETIC (RTYPE (tree)) && !IS_PTR (RTYPE (tree)) && !IS_ARRAY (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "+"); + goto errorTreeReturn; + } + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + tree->left = addCast (tree->left, resultTypeProp, TRUE); + tree->right = addCast (tree->right, resultTypeProp, TRUE); + rewriteAstNodeVal (tree, valPlus (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)))); + return decorateType (tree, resultType); + } + + /* if the right is a pointer or left is a literal + xchange left & right */ + if (IS_ARRAY (RTYPE (tree)) || IS_PTR (RTYPE (tree)) || IS_LITERAL (LTYPE (tree))) + { + ast *tTree = tree->left; + tree->left = tree->right; + tree->right = tTree; + } + + /* if right is a literal and */ + /* left is also an addition/subtraction with a literal then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree))) + { + ast *litTree, *parent; + litTree = searchLitOp (tree, &parent, "+-"); + if (litTree) + { + if (litTree->opval.op == '+') + { + /* foo_aa */ + DEBUG_CF ("+ 1 AA") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tree->left; + tree->left = tTree; + } + else if (litTree->opval.op == '-') + { + if (IS_LITERAL (RTYPE (litTree))) + { + DEBUG_CF ("+ 2 ASR") + /* foo_asr */ + ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = tTree; + } + else + { + DEBUG_CF ("+ 3 ASL") + /* foo_asl */ + ast *tTree = litTree->right; + litTree->right = tree->right; + tree->right = tTree; + litTree->opval.op = '+'; + tree->opval.op = '-'; + } + } + decorateType (parent, resultType); + } + } + + LRVAL (tree) = RRVAL (tree) = 1; + + /* if the left is a pointer */ + if (IS_PTR (LTYPE (tree)) || IS_AGGREGATE (LTYPE (tree))) + TETYPE (tree) = getSpec (TTYPE (tree) = LTYPE (tree)); + else + { + tree->left = addCast (tree->left, resultTypeProp, TRUE); + tree->right = addCast (tree->right, resultTypeProp, TRUE); + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); + } + if (IS_LITERAL (TETYPE (tree))) + { + if (TTYPE (tree) == LTYPE (tree)) + TETYPE (tree) = getSpec (TTYPE (tree) = copyLinkChain (TTYPE (tree))); + SPEC_SCLS (TETYPE (tree)) = 0; + } + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* unary '-' */ + /*----------------------------*/ + case '-': /* can be unary */ + /* if right is null then unary */ + if (!tree->right) + { + if (!IS_ARITHMETIC (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_UNARY_OP, tree->opval.op); + goto errorTreeReturn; + } + + /* if left is a literal then do it */ + if (IS_LITERAL (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valUnaryPM (valFromType (LETYPE (tree))); + tree->left = NULL; + TETYPE (tree) = TTYPE (tree) = tree->opval.val->type; + return tree; + } + tree->left = addCast (tree->left, resultTypeProp, TRUE); + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), NULL, resultType, tree->opval.op)); + LRVAL (tree) = 1; + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* subtraction */ + /*----------------------------*/ + + if (!(IS_PTR (LTYPE (tree)) || IS_ARRAY (LTYPE (tree)) || IS_ARITHMETIC (LTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "-"); + goto errorTreeReturn; + } + + if (!(IS_PTR (RTYPE (tree)) || IS_ARRAY (RTYPE (tree)) || IS_ARITHMETIC (RTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "-"); + goto errorTreeReturn; + } + + if ((IS_PTR (LTYPE (tree)) || IS_ARRAY (LTYPE (tree))) && + !(IS_PTR (RTYPE (tree)) || IS_ARRAY (RTYPE (tree)) || IS_INTEGRAL (RTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "-"); + goto errorTreeReturn; + } + + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + tree->left = addCast (tree->left, resultTypeProp, TRUE); + tree->right = addCast (tree->right, resultTypeProp, TRUE); + rewriteAstNodeVal (tree, valMinus (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)))); + return decorateType (tree, resultType); + } + + /* if the left & right are equal then zero */ + if (!hasSEFcalls(tree->left) && !hasSEFcalls(tree->right) && + isAstEqual (tree->left, tree->right)) + { + tree->type = EX_VALUE; + tree->left = tree->right = NULL; + tree->opval.val = constVal ("0"); + TETYPE (tree) = TTYPE (tree) = tree->opval.val->type; + return tree; + } + + /* if both of them are pointers or arrays then */ + /* the result is going to be an integer */ + if ((IS_ARRAY (LTYPE (tree)) || IS_PTR (LTYPE (tree))) && (IS_ARRAY (RTYPE (tree)) || IS_PTR (RTYPE (tree)))) + TETYPE (tree) = TTYPE (tree) = newIntLink (); + else + /* if only the left is a pointer */ + /* then result is a pointer */ + if (IS_PTR (LTYPE (tree)) || IS_ARRAY (LTYPE (tree))) + TETYPE (tree) = getSpec (TTYPE (tree) = LTYPE (tree)); + else + { + tree->left = addCast (tree->left, resultTypeProp, TRUE); + tree->right = addCast (tree->right, resultTypeProp, TRUE); + + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op)); + } + if (IS_LITERAL (TETYPE (tree))) + { + if (TTYPE (tree) == LTYPE (tree)) + TETYPE (tree) = getSpec (TTYPE (tree) = copyLinkChain (TTYPE (tree))); + SPEC_SCLS (TETYPE (tree)) = 0; + } + + LRVAL (tree) = RRVAL (tree) = 1; + + /* if right is a literal and */ + /* left is also an addition/subtraction with a literal then */ + /* rearrange the tree */ + if (IS_LITERAL (RTYPE (tree)) + /* avoid infinite loop */ + && (TYPE_TARGET_ULONG) ulFromVal (tree->right->opval.val) != 0) + { + ast *litTree, *litParent; + litTree = searchLitOp (tree, &litParent, "+-"); + if (litTree) + { + if (litTree->opval.op == '+') + { + /* foo_sa */ + DEBUG_CF ("- 1 SA") ast *tTree = litTree->left; + litTree->left = litTree->right; + litTree->right = tree->right; + tree->right = tTree; + tree->opval.op = '+'; + litTree->opval.op = '-'; + } + else if (litTree->opval.op == '-') + { + if (IS_LITERAL (RTYPE (litTree))) + { + /* foo_ssr */ + DEBUG_CF ("- 2 SSR") ast *tTree = litTree->left; + litTree->left = tree->right; + tree->right = litParent->left; + litParent->left = tTree; + litTree->opval.op = '+'; + + tree->decorated = 0; + decorateType (tree, resultType); + } + else + { + /* foo_ssl */ + DEBUG_CF ("- 3 SSL") ast *tTree = litTree->right; + litTree->right = tree->right; + tree->right = tTree; + } + } + decorateType (litParent, resultType); + } + } + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* complement */ + /*----------------------------*/ + case '~': + /* can be only integral type */ + if (!IS_INTEGRAL (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_UNARY_OP, tree->opval.op); + goto errorTreeReturn; + } + + /* if left is a literal then do it */ + if (IS_LITERAL (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valComplement (valFromType (LETYPE (tree))); + tree->left = NULL; + TETYPE (tree) = TTYPE (tree) = tree->opval.val->type; + return addCast (tree, resultTypeProp, TRUE); + } + + if (resultType == RESULT_TYPE_BOOL && IS_UNSIGNED (tree->left->etype) && getSize (tree->left->etype) < INTSIZE) + { + /* promotion rules are responsible for this strange result: + bit -> int -> ~int -> bit + uchar -> int -> ~int -> bit + */ + werrorfl (tree->filename, tree->lineno, W_COMPLEMENT); + + /* optimize bit-result, even if we optimize a buggy source */ + tree->type = EX_VALUE; + tree->opval.val = constBoolVal (1); + } + else + tree->left = addCast (tree->left, resultTypeProp, TRUE); + LRVAL (tree) = 1; + COPYTYPE (TTYPE (tree), TETYPE (tree), LTYPE (tree)); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* not */ + /*----------------------------*/ + case '!': + /* can be pointer */ + if (!IS_ARITHMETIC (LTYPE (tree)) && !IS_PTR (LTYPE (tree)) && !IS_ARRAY (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_UNARY_OP, tree->opval.op); + goto errorTreeReturn; + } + + /* if left is another '!' */ +#if 0 /* Disabled optimization due to bugs #2548, #2551. */ + if (IS_AST_NOT_OPER (tree->left)) + { + if (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) + { + /* replace double '!!X' by 'X' */ + return tree->left->left; + } + + /* remove double '!!X' by 'X ? 1 : 0' */ /* TODO: Casts to _Bools tend to result in far more efficient code than '?' */ + tree->opval.op = '?'; + tree->left = tree->left->left; + tree->right = newNode (':', newAst_VALUE (constBoolVal (1)), newAst_VALUE (constBoolVal (0))); + tree->right->filename = tree->filename; + tree->right->lineno = tree->lineno; + tree->decorated = 0; + return decorateType (tree, resultType); + } +#endif + + /* if left is a literal then do it */ + if (IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valNot (valFromType (LETYPE (tree)))); + return decorateType (tree, resultType); + } + LRVAL (tree) = 1; + TTYPE (tree) = TETYPE (tree) = (resultTypeProp == RESULT_TYPE_BOOL) ? newBoolLink () : newCharLink (); + if (IS_BOOLEAN (LTYPE (tree))) + SPEC_USIGN (TTYPE (tree)) = SPEC_USIGN (LTYPE (tree)); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* shift */ + /*----------------------------*/ + case RRC: + case RLC: + case SWAP: + TTYPE (tree) = LTYPE (tree); + TETYPE (tree) = LETYPE (tree); + return tree; + + case GETHBIT: + case GETABIT: + TTYPE (tree) = TETYPE (tree) = (resultTypeProp == RESULT_TYPE_BOOL) ? newBoolLink () : newCharLink (); + return tree; + + case GETBYTE: + TTYPE (tree) = TETYPE (tree) = newCharLink (); + return tree; + + case GETWORD: + TTYPE (tree) = TETYPE (tree) = newIntLink (); + return tree; + + case LEFT_OP: + case RIGHT_OP: + if (!IS_INTEGRAL (LTYPE (tree)) || !IS_INTEGRAL (tree->left->etype)) + { + werrorfl (tree->filename, tree->lineno, E_SHIFT_OP_INVALID); + werrorfl (tree->filename, tree->lineno, W_CONTINUE, "left & right types are "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, ","); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + + /* make smaller type only if it's a LEFT_OP */ + if (tree->opval.op == LEFT_OP) + tree->left = addCast (tree->left, resultTypeProp, TRUE); + + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valShift (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)), (tree->opval.op == LEFT_OP ? 1 : 0))); + return decorateType (tree, resultType); + } + + /* see if this is a GETBYTE operation if yes + then return that */ + { + ast *otree = optimizeGetByte (tree, resultType); + + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + } + + /* see if this is a GETWORD operation if yes + then return that */ + { + ast *otree = optimizeGetWord (tree, resultType); + + if (otree != tree) + return decorateType (otree, RESULT_TYPE_NONE); + } + + LRVAL (tree) = RRVAL (tree) = 1; + if (tree->opval.op == LEFT_OP) + { + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), NULL, resultType, tree->opval.op)); + } + else /* RIGHT_OP */ + { + /* no promotion necessary */ + TTYPE (tree) = TETYPE (tree) = copyLinkChain (LTYPE (tree)); + if (IS_LITERAL (TTYPE (tree))) + SPEC_SCLS (TTYPE (tree)) &= ~S_LITERAL; + } + + /* if only the right side is a literal & we are + shifting more than size of the left operand then zero */ + if (IS_LITERAL (RTYPE (tree)) && + ((TYPE_TARGET_ULONG) ulFromVal (valFromType (RETYPE (tree)))) >= (getSize (TETYPE (tree)) * 8)) + { + if (tree->opval.op == LEFT_OP || (tree->opval.op == RIGHT_OP && SPEC_USIGN (LETYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, W_SHIFT_CHANGED, (tree->opval.op == LEFT_OP ? "left" : "right")); + /* Change shift op to comma op and replace the right operand with 0. */ + /* This preserves the left operand in case there were side-effects. */ + tree->opval.op = ','; + tree->right->opval.val = constVal ("0"); + TETYPE (tree) = TTYPE (tree) = tree->right->opval.val->type; + return tree; + } + } + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* casting */ + /*----------------------------*/ + case CAST: /* change the type */ + /* cannot cast to an aggregate type */ + if (IS_AGGREGATE (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_CAST_ILLEGAL); + goto errorTreeReturn; + } + + /* make sure the type is complete and sane */ + if ((resultType == RESULT_TYPE_GPTR) && IS_FUNCPTR (LTYPE (tree))) + changePointer (LTYPE (tree)->next); + else + changePointer (LTYPE (tree)); + checkTypeSanity (LETYPE (tree), "(cast)"); + + /* if 'from' and 'to' are the same remove the superfluous cast, + * this helps other optimizations */ + if (compareTypeExact (LTYPE (tree), RTYPE (tree), -1) == 1) + { + /* mark that the explicit cast has been removed, + * for proper processing (no integer promotion) of explicitly typecasted variable arguments */ + tree->right->values.cast.removedCast = 1; + return tree->right; + } + + /* If code memory is read only, then pointers to code memory */ + /* implicitly point to constants -- make this explicit */ + CodePtrPointsToConst (LTYPE (tree)); + +#if 0 + /* if the right is a literal replace the tree */ + if (IS_LITERAL (RETYPE (tree))) + { + if (!IS_PTR (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), floatFromVal (valFromType (RETYPE (tree)))); + tree->left = NULL; + tree->right = NULL; + TTYPE (tree) = tree->opval.val->type; + tree->values.cast.literalFromCast = 1; + } + else if (IS_GENPTR (LTYPE (tree)) && !IS_PTR (RTYPE (tree)) && ((int) ulFromVal (valFromType (RETYPE (tree)))) != 0) /* special case of NULL */ + { + sym_link *rest = LTYPE (tree)->next; + werrorfl (tree->filename, tree->lineno, W_LITERAL_GENERIC); + TTYPE (tree) = newLink (DECLARATOR); + DCL_TYPE (TTYPE (tree)) = FPOINTER; + TTYPE (tree)->next = rest; + tree->left->opval.lnk = TTYPE (tree); + LRVAL (tree) = 1; + } + else + { + TTYPE (tree) = LTYPE (tree); + LRVAL (tree) = 1; + } + } + else + { + TTYPE (tree) = LTYPE (tree); + LRVAL (tree) = 1; + } +#else +#if 0 // this is already checked, now this could be explicit + /* if pointer to struct then check names */ + if (IS_PTR (LTYPE (tree)) && IS_STRUCT (LTYPE (tree)->next) && + IS_PTR (RTYPE (tree)) && IS_STRUCT (RTYPE (tree)->next) && + strcmp (SPEC_STRUCT (LETYPE (tree))->tag, SPEC_STRUCT (RETYPE (tree))->tag)) + { + werrorfl (tree->filename, tree->lineno, W_CAST_STRUCT_PTR, SPEC_STRUCT (RETYPE (tree))->tag, + SPEC_STRUCT (LETYPE (tree))->tag); + } +#endif +#if 0 // disabled to fix bug 2941749 + if (IS_ADDRESS_OF_OP (tree->right) + && IS_AST_SYM_VALUE (tree->right->left) && SPEC_ABSA (AST_SYMBOL (tree->right->left)->etype)) + { + symbol *sym = AST_SYMBOL (tree->right->left); + unsigned int gptype = 0; + unsigned int addr = SPEC_ADDR (sym->etype); + + if (IS_GENPTR (LTYPE (tree)) && ((GPTRSIZE > FARPTRSIZE) || TARGET_IS_PIC16)) + { + switch (SPEC_SCLS (sym->etype)) + { + case S_CODE: + gptype = GPTYPE_CODE; + break; + case S_XDATA: + gptype = GPTYPE_FAR; + break; + case S_DATA: + case S_IDATA: + gptype = GPTYPE_NEAR; + break; + case S_PDATA: + gptype = GPTYPE_XSTACK; + break; + default: + gptype = 0; + if (TARGET_IS_PIC16 && (SPEC_SCLS (sym->etype) == S_FIXED)) + gptype = GPTYPE_NEAR; + } + addr |= gptype << (8 * (GPTRSIZE - 1)); + } + + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), addr); + TTYPE (tree) = tree->opval.val->type; + TETYPE (tree) = getSpec (TTYPE (tree)); + tree->left = NULL; + tree->right = NULL; + tree->values.cast.literalFromCast = 1; + return tree; + } +#endif + + /* if the right is a literal replace the tree */ + if (IS_LITERAL (RETYPE (tree))) + { +#if 0 + if (IS_PTR (LTYPE (tree)) && !IS_GENPTR (LTYPE (tree))) + { + /* rewrite (type *)litaddr + as &temp + and define type at litaddr temp + (but only if type's storage class is not generic) + */ + ast *newTree = newNode ('&', NULL, NULL); + symbol *sym; + + TTYPE (newTree) = LTYPE (tree); + TETYPE (newTree) = getSpec (LTYPE (tree)); + + /* define a global symbol at the casted address */ + sym = newSymbol (genSymName (0), 0); + sym->type = LTYPE (tree)->next; + if (!sym->type) + sym->type = newLink (V_VOID); + sym->etype = getSpec (sym->type); + SPEC_SCLS (sym->etype) = sclsFromPtr (LTYPE (tree)); + sym->lineDef = tree->lineno; + sym->cdef = 1; + sym->isref = 1; + SPEC_STAT (sym->etype) = 1; + SPEC_ADDR (sym->etype) = floatFromVal (valFromType (RTYPE (tree))); + SPEC_ABSA (sym->etype) = 1; + addSym (SymbolTab, sym, sym->name, 0, 0, 0); + allocGlobal (sym); + + newTree->left = newAst_VALUE (symbolVal (sym)); + newTree->left->filename = tree->filename; + newTree->left->lineno = tree->lineno; + LTYPE (newTree) = sym->type; + LETYPE (newTree) = sym->etype; + LLVAL (newTree) = 1; + LRVAL (newTree) = 0; + TLVAL (newTree) = 1; + return newTree; + } +#endif + if (!IS_PTR (LTYPE (tree))) + { + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), floatFromVal (valFromType (RTYPE (tree))), (TYPE_TARGET_ULONGLONG) ullFromVal (valFromType (RTYPE (tree)))); + TTYPE (tree) = tree->opval.val->type; + tree->left = NULL; + tree->right = NULL; + tree->values.cast.literalFromCast = 1; + TETYPE (tree) = getSpec (TTYPE (tree)); + return tree; + } + else + { + unsigned long long gpVal = 0; + int size = getSize(LTYPE (tree)); + unsigned long long mask = (size >= sizeof(long long)) ? 0xffffffffffffffffull : (1ull << (size * 8)) - 1; + unsigned long long pVal = ullFromVal (valFromType (RTYPE (tree))) & mask; + + /* if casting literal specific pointer to generic pointer */ + if (IS_GENPTR (LTYPE (tree)) && IS_PTR (RTYPE (tree)) && !IS_GENPTR (RTYPE (tree))) + { + if (resultType != RESULT_TYPE_GPTR) + { + DCL_TYPE (LTYPE (tree)) = DCL_TYPE (RTYPE (tree)); + } + else + { + gpVal = pointerTypeToGPByte (DCL_TYPE (RTYPE (tree)), NULL, NULL); + gpVal <<= getSize (RTYPE (tree)) * 8; + gpVal &= mask; + } + } + checkPtrCast (LTYPE (tree), RTYPE (tree), tree->values.cast.implicitCast, !ullFromVal (valFromType (RTYPE (tree)))); + LRVAL (tree) = 1; + tree->type = EX_VALUE; + tree->opval.val = valCastLiteral (LTYPE (tree), gpVal | pVal, gpVal | pVal); + TTYPE (tree) = tree->opval.val->type; + tree->left = NULL; + tree->right = NULL; + tree->values.cast.literalFromCast = 1; + TETYPE (tree) = getSpec (TTYPE (tree)); + return tree; + } + } + checkPtrCast (LTYPE (tree), RTYPE (tree), tree->values.cast.implicitCast, FALSE); + if (IS_GENPTR (LTYPE (tree)) && (resultType != RESULT_TYPE_GPTR)) + { + if (IS_PTR (RTYPE (tree)) && !IS_GENPTR (RTYPE (tree))) + DCL_TYPE (LTYPE (tree)) = DCL_TYPE (RTYPE (tree)); + if (IS_ARRAY (RTYPE (tree)) && SPEC_OCLS (RETYPE (tree))) + DCL_TYPE (LTYPE (tree)) = PTR_TYPE (SPEC_OCLS (RETYPE (tree))); + } + TTYPE (tree) = LTYPE (tree); + LRVAL (tree) = 1; + +#endif + TETYPE (tree) = getSpec (TTYPE (tree)); + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* logical &&, || */ + /*----------------------------*/ + case AND_OP: + case OR_OP: + /* each must be arithmetic type or be a pointer */ + if (!IS_PTR (LTYPE (tree)) && !IS_ARRAY (LTYPE (tree)) && !IS_INTEGRAL (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_COMPARE_OP); + goto errorTreeReturn; + } + + if (!IS_PTR (RTYPE (tree)) && !IS_ARRAY (RTYPE (tree)) && !IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_COMPARE_OP); + goto errorTreeReturn; + } + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + { + rewriteAstNodeVal (tree, valLogicAndOr (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)), tree->opval.op)); + return decorateType (tree, resultType); + } + LRVAL (tree) = RRVAL (tree) = 1; + TTYPE (tree) = TETYPE (tree) = (resultTypeProp == RESULT_TYPE_BOOL) ? newBoolLink () : newCharLink (); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* comparison operators */ + /*----------------------------*/ + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + { + ast *lt = optimizeCompare (tree); + + if (tree != lt) + return lt; + } + + /* C does not allow comparison of struct or union. */ + if (IS_STRUCT (LTYPE (tree)) || IS_STRUCT (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_COMPARE_OP); + goto errorTreeReturn; + } + + /* if they are pointers they must be castable */ + else if (IS_PTR (LTYPE (tree)) && IS_PTR (RTYPE (tree))) + { + if (tree->opval.op == EQ_OP && !IS_GENPTR (LTYPE (tree)) && IS_GENPTR (RTYPE (tree))) + { + // we cannot cast a gptr to a !gptr: switch the leaves + struct ast *s = tree->left; + tree->left = tree->right; + tree->right = s; + } + if (compareType (LTYPE (tree), RTYPE (tree)) == 0) + { + werrorfl (tree->filename, tree->lineno, E_INCOMPAT_TYPES); + fprintf (stderr, "comparing type "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, " to type "); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + } + /* else they should be promotable to one another */ + else + { + if (!((IS_PTR (LTYPE (tree)) && IS_LITERAL (RTYPE (tree))) || (IS_PTR (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))))) + + if (compareType (LTYPE (tree), RTYPE (tree)) == 0) + { + if (compareType (RTYPE (tree), LTYPE (tree)) != 0) + { + struct ast *s = tree->left; + tree->left = tree->right; + tree->right = s; + if (tree->opval.op == '>') + tree->opval.op = '<'; + else if (tree->opval.op == '>') + tree->opval.op = '<'; + else if (tree->opval.op == LE_OP) + tree->opval.op = GE_OP; + else if (tree->opval.op == GE_OP) + tree->opval.op = LE_OP; + } + else + { + werrorfl (tree->filename, tree->lineno, E_INCOMPAT_TYPES); + fprintf (stderr, "comparing type "); + printTypeChain (LTYPE (tree), stderr); + fprintf (stderr, " to type "); + printTypeChain (RTYPE (tree), stderr); + fprintf (stderr, "\n"); + goto errorTreeReturn; + } + } + } + + { + CCR_RESULT ccr_result = CCR_OK; + ast * newResult; + + /* if left is integral and right is literal + then check constant range */ + if (IS_INTEGRAL (LTYPE (tree)) && !IS_LITERAL (LTYPE (tree)) && IS_LITERAL (RTYPE (tree))) + ccr_result = checkConstantRange (LTYPE (tree), RTYPE (tree), tree->opval.op, FALSE); + if (ccr_result == CCR_OK && IS_INTEGRAL (RTYPE (tree)) && !IS_LITERAL (RTYPE (tree)) && IS_LITERAL (LTYPE (tree))) + ccr_result = checkConstantRange (RTYPE (tree), LTYPE (tree), tree->opval.op, TRUE); + switch (ccr_result) + { + case CCR_ALWAYS_TRUE: + case CCR_ALWAYS_FALSE: + werrorfl (tree->filename, tree->lineno, W_COMP_RANGE, ccr_result == CCR_ALWAYS_TRUE ? "true" : "false"); + newResult = newAst_VALUE (constBoolVal ((unsigned char) (ccr_result == CCR_ALWAYS_TRUE))); + /* If there are side effects, join the non-literal side */ + /* to the boolean result with a comma operator */ + if (hasSEFcalls (tree)) + { + if (!IS_LITERAL (LTYPE (tree))) + newResult = newNode (',', tree->left, newResult); + else + newResult = newNode (',', tree->right, newResult); + } + return decorateType (newResult, resultType); + case CCR_OK: + default: + break; + } + } + + /* if (unsigned value) > 0 then '(unsigned value) ? 1 : 0' */ + if (tree->opval.op == '>' && + SPEC_USIGN (LETYPE (tree)) && IS_LITERAL (RTYPE (tree)) && ((int) ulFromVal (valFromType (RETYPE (tree)))) == 0) + { + if ((resultType == RESULT_TYPE_IFX) || (resultType == RESULT_TYPE_BOOL)) + { + /* the parent is an ifx: */ + /* if (unsigned value) */ + return tree->left; + } + + /* (unsigned value) ? 1 : 0 */ /* TODO: Casts to _Bools tend to result in far more efficient code than '?' */ + tree->opval.op = '?'; + tree->right = newNode (':', newAst_VALUE (constBoolVal (1)), tree->right); /* val 0 */ + tree->right->filename = tree->filename; + tree->right->lineno = tree->lineno; + tree->right->left->filename = tree->filename; + tree->right->left->lineno = tree->lineno; + tree->decorated = 0; + return decorateType (tree, resultType); + } + + /* 'ifx (0 == op)' -> 'ifx (!(op))' */ + if (IS_LITERAL (LETYPE (tree)) && + floatFromVal (valFromType (LTYPE (tree))) == 0 && + tree->opval.op == EQ_OP && (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) + { + rewriteAstNodeOp (tree, '!', tree->right, NULL); + return decorateType (tree, resultType); + } + + /* 'ifx (op == 0)' -> 'ifx (!(op))' */ + if (IS_LITERAL (RETYPE (tree)) && + floatFromVal (valFromType (RTYPE (tree))) == 0 && + tree->opval.op == EQ_OP && (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) + { + rewriteAstNodeOp (tree, '!', tree->left, NULL); + return decorateType (tree, resultType); + } + + /* 'ifx (op == 1)' -> 'ifx (op)' for bool */ + if (IS_LITERAL (RETYPE (tree)) && + floatFromVal (valFromType (RTYPE (tree))) == 1 && IS_BOOLEAN (LETYPE (tree)) && + tree->opval.op == EQ_OP && (resultType == RESULT_TYPE_IFX || resultType == RESULT_TYPE_BOOL)) + { + tree = tree->left; + return decorateType (tree, resultType); + } + + /* if they are both literal then */ + /* rewrite the tree */ + if (IS_LITERAL (RETYPE (tree)) && IS_LITERAL (LETYPE (tree))) + { + rewriteAstNodeVal (tree, valCompare (valFromType (LETYPE (tree)), valFromType (RETYPE (tree)), tree->opval.op)); + return decorateType (tree, resultType); + } + + /* if one is 'signed char ' and the other one is 'unsigned char' */ + /* it's necessary to promote to int */ + if (IS_CHAR (RTYPE (tree)) && IS_CHAR (LTYPE (tree)) && (IS_UNSIGNED (RTYPE (tree)) != IS_UNSIGNED (LTYPE (tree)))) + { + /* Small literal integers are 'optimized' to 'unsigned char' but chars in single quotes are 'char'. + Try to figure out, if it's possible to do without a cast to integer */ + + /* is right a small literal char? */ + if (IS_LITERAL (RTYPE (tree))) + { + int val = (int) ulFromVal (valFromType (RETYPE (tree))); + /* the overlapping value range of a '(un)signed char' is 0...127; + if 0 <= the actual value < 128 it can be changed to (un)signed */ + if (val >= 0 && val < 128) + { + /* now we've got 2 '(un)signed char'! */ + SPEC_USIGN (RETYPE (tree)) = SPEC_USIGN (LETYPE (tree)); + } + } + /* same test for the left operand: */ + else if (IS_LITERAL (LTYPE (tree))) + { + int val = (int) ulFromVal (valFromType (LETYPE (tree))); + if (val >= 0 && val < 128) + { + SPEC_USIGN (LETYPE (tree)) = SPEC_USIGN (RETYPE (tree)); + } + } + else + { + werrorfl (tree->filename, tree->lineno, W_CMP_SU_CHAR); + tree->left = addCast (tree->left, RESULT_TYPE_INT, TRUE); + tree->right = addCast (tree->right, RESULT_TYPE_INT, TRUE); + } + } + + LRVAL (tree) = RRVAL (tree) = 1; + TTYPE (tree) = TETYPE (tree) = (resultType == RESULT_TYPE_BOOL) ? newBoolLink () : newCharLink (); + + /* condition transformations */ + { + unsigned transformedOp = 0; + + switch (tree->opval.op) + { + case '<': /* transform (a < b) to !(a >= b) */ + if (port->lt_nge) + transformedOp = GE_OP; + break; + case '>': /* transform (a > b) to !(a <= b) */ + if (port->gt_nle) + transformedOp = LE_OP; + break; + case LE_OP: /* transform (a <= b) to !(a > b) */ + if (port->le_ngt) + transformedOp = '>'; + break; + case GE_OP: /* transform (a >= b) to !(a < b) */ + if (port->ge_nlt) + transformedOp = '<'; + break; + case NE_OP: /* transform (a != b) to !(a == b) */ + if (port->ne_neq) + transformedOp = EQ_OP; + break; + case EQ_OP: /* transform (a == b) to !(a != b) */ + if (port->eq_nne) + transformedOp = NE_OP; + break; + default: + break; + } + if (transformedOp) + { + tree->opval.op = transformedOp; + tree->decorated = 0; + tree = newNode ('!', tree, NULL); + tree->filename = tree->left->filename; + tree->lineno = tree->left->lineno; + return decorateType (tree, resultType); + } + } + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* sizeof */ + /*----------------------------*/ + case SIZEOF: /* evaluate without code generation */ + { + /* change the type to a integer */ + struct dbuf_s dbuf; + int size = getSize (tree->right->ftype); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%d", size); + if (!size && !IS_VOID (tree->right->ftype)) + werrorfl (tree->filename, tree->lineno, E_SIZEOF_INCOMPLETE_TYPE); + tree->type = EX_VALUE; + tree->opval.val = constVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + tree->right = tree->left = NULL; + TETYPE (tree) = getSpec (TTYPE (tree) = tree->opval.val->type); + + return tree; + } + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* typeof */ + /*----------------------------*/ + case TYPEOF: + /* return typeof enum value */ + tree->type = EX_VALUE; + { + int typeofv = 0; + struct dbuf_s dbuf; + + if (IS_SPEC (tree->right->ftype)) + { + switch (SPEC_NOUN (tree->right->ftype)) + { + case V_INT: + if (SPEC_LONG (tree->right->ftype)) + typeofv = TYPEOF_LONG; + else + typeofv = TYPEOF_INT; + break; + case V_FLOAT: + typeofv = TYPEOF_FLOAT; + break; + case V_FIXED16X16: + typeofv = TYPEOF_FIXED16X16; + break; + case V_BOOL: + typeofv = TYPEOF_BOOL; + break; + case V_CHAR: + typeofv = TYPEOF_CHAR; + break; + case V_VOID: + typeofv = TYPEOF_VOID; + break; + case V_STRUCT: + typeofv = TYPEOF_STRUCT; + break; + case V_BITFIELD: + typeofv = TYPEOF_BITFIELD; + break; + case V_BIT: + typeofv = TYPEOF_BIT; + break; + case V_SBIT: + typeofv = TYPEOF_SBIT; + break; + default: + break; + } + } + else + { + switch (DCL_TYPE (tree->right->ftype)) + { + case POINTER: + typeofv = TYPEOF_POINTER; + break; + case FPOINTER: + typeofv = TYPEOF_FPOINTER; + break; + case CPOINTER: + typeofv = TYPEOF_CPOINTER; + break; + case GPOINTER: + typeofv = TYPEOF_GPOINTER; + break; + case PPOINTER: + typeofv = TYPEOF_PPOINTER; + break; + case IPOINTER: + typeofv = TYPEOF_IPOINTER; + break; + case ARRAY: + typeofv = TYPEOF_ARRAY; + break; + case FUNCTION: + typeofv = TYPEOF_FUNCTION; + break; + default: + break; + } + } + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%d", typeofv); + tree->opval.val = constVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + tree->right = tree->left = NULL; + TETYPE (tree) = getSpec (TTYPE (tree) = tree->opval.val->type); + } + return tree; + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* conditional operator '?' */ + /*----------------------------*/ + case '?': + /* the type is value of the colon operator (on the right) */ + assert (IS_COLON_OP (tree->right)); + + /* If already known then replace the tree : optimizer will do it + but faster to do it here. If done before decorating tree->right + this can save generating unused const strings. */ + if (IS_LITERAL (LTYPE (tree))) + { + ast * heir; + + ++noAlloc; + tree->right = decorateType (tree->right, resultTypeProp); + --noAlloc; + + if (((int) ulFromVal (valFromType (LETYPE (tree)))) != 0) + heir = tree->right->left; + else + heir = tree->right->right; + + heir = decorateType (heir, resultTypeProp); + if (IS_LITERAL (TETYPE (heir))) + TTYPE (heir) = valRecastLitVal (TTYPE (tree->right), valFromType (TETYPE (heir)))->type; + else + TTYPE (heir) = TTYPE (tree->right); + TETYPE (heir) = getSpec (TTYPE (heir)); + return heir; + } + + tree->right = decorateType (tree->right, resultTypeProp); + + if (IS_AST_LIT_VALUE (tree->right->left) && IS_AST_LIT_VALUE (tree->right->right) && + ((resultType == RESULT_TYPE_IFX) || (resultType == RESULT_TYPE_BOOL))) + { + double valTrue = AST_FLOAT_VALUE (tree->right->left); + double valFalse = AST_FLOAT_VALUE (tree->right->right); + + if ((valTrue != 0) && (valFalse == 0)) + { + /* assign cond to result */ + tree->left->decorated = 0; + return decorateType (tree->left, resultTypeProp); + } + else if ((valTrue == 0) && (valFalse != 0)) + { + /* assign !cond to result */ + tree->opval.op = '!'; + tree->decorated = 0; + tree->right = NULL; + return decorateType (tree, resultTypeProp); + } + else + { + /* they have the same boolean value, make them equal */ + tree->right->left = tree->right->right; + } + } + + /* if they are equal then replace the tree */ + if (isAstEqual (tree->right->left, tree->right->right)) + { + return tree->right->left; + } + + TTYPE (tree) = RTYPE (tree); + TETYPE (tree) = getSpec (TTYPE (tree)); + return tree; + + case GENERIC: + { + sym_link *type = tree->left->ftype; + ast *assoc_list; + ast *default_expr = 0; + ast *found_expr = 0; + + for(assoc_list = tree->right; assoc_list; assoc_list = assoc_list->left) + { + ast *const assoc = assoc_list->right; + if (!assoc->left) + { + if (default_expr) + { + werror (E_MULTIPLE_DEFAULT_IN_GENERIC); + goto errorTreeReturn; + } + default_expr = assoc->right; + } + else + { + sym_link *assoc_type; + wassert (IS_AST_LINK (assoc->left)); + assoc_type = assoc->left->opval.lnk; + checkTypeSanity (assoc_type, "_Generic"); + + if (compareType (type, assoc->left->opval.lnk) > 0 && !(SPEC_NOUN (type) == V_CHAR && type->select.s.b_implicit_sign != assoc->left->opval.lnk->select.s.b_implicit_sign)) + { + if (found_expr) + { + werror (E_MULTIPLE_MATCHES_IN_GENERIC); + goto errorTreeReturn; + } + found_expr = assoc->right; + } + } + } + if (!found_expr) + found_expr = default_expr; + + if (!found_expr) + { + werror (E_NO_MATCH_IN_GENERIC); + goto errorTreeReturn; + } + + tree = found_expr; + } + return tree; + + case GENERIC_ASSOC_LIST: + return tree; + + case GENERIC_ASSOCIATION: + return tree; + + case ':': + if ((compareType (LTYPE (tree), RTYPE (tree)) == 0) && (compareType (RTYPE (tree), LTYPE (tree)) == 0)) + { + if (IS_PTR (LTYPE (tree)) && !IS_GENPTR (LTYPE (tree))) + DCL_TYPE (LTYPE(tree)) = GPOINTER; + if (IS_PTR (RTYPE (tree)) && !IS_GENPTR (RTYPE (tree))) + DCL_TYPE (RTYPE(tree)) = GPOINTER; + } + + if ((compareType (LTYPE (tree), RTYPE (tree)) == 0) && + (compareType (RTYPE (tree), LTYPE (tree)) == 0) && + !(IS_ARRAY(LTYPE (tree)) && IS_INTEGRAL(RTYPE (tree))) && + !(IS_ARRAY(RTYPE (tree)) && IS_INTEGRAL(LTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_TYPE_MISMATCH, "conditional operator", " "); + printFromToType (RTYPE (tree), LTYPE (tree)); + } + + TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), resultType, tree->opval.op); + TETYPE (tree) = getSpec (TTYPE (tree)); + + return tree; + +#if 0 // assignment operators are converted by the parser + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* assignment operators */ + /*----------------------------*/ + case MUL_ASSIGN: + case DIV_ASSIGN: + /* for these it must be both must be integral */ + if (!IS_ARITHMETIC (LTYPE (tree)) || !IS_ARITHMETIC (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_OPS_INTEGRAL); + goto errorTreeReturn; + } + RRVAL (tree) = 1; + TETYPE (tree) = getSpec (TTYPE (tree) = LTYPE (tree)); + + if (!tree->initMode && IS_CONSTANT (LTYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, tree->opval.op == MUL_ASSIGN ? "*=" : "/="); + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, tree->opval.op == MUL_ASSIGN ? "*=" : "/="); + goto errorTreeReturn; + } + LLVAL (tree) = 1; + + return tree; + + case AND_ASSIGN: + case OR_ASSIGN: + case XOR_ASSIGN: + case RIGHT_ASSIGN: + case LEFT_ASSIGN: + /* for these it must be both must be integral */ + if (!IS_INTEGRAL (LTYPE (tree)) || !IS_INTEGRAL (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_OPS_INTEGRAL); + goto errorTreeReturn; + } + RRVAL (tree) = 1; + TETYPE (tree) = getSpec (TTYPE (tree) = LTYPE (tree)); + + if (!tree->initMode && IS_CONSTANT (LETYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, "&= or |= or ^= or >>= or <<="); + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "&= or |= or ^= or >>= or <<="); + goto errorTreeReturn; + } + LLVAL (tree) = 1; + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* -= operator */ + /*----------------------------*/ + case SUB_ASSIGN: + if (!(IS_PTR (LTYPE (tree)) || IS_ARITHMETIC (LTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "-="); + goto errorTreeReturn; + } + + if (!(IS_PTR (RTYPE (tree)) || IS_ARITHMETIC (RTYPE (tree)))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "-="); + goto errorTreeReturn; + } + RRVAL (tree) = 1; + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), RESULT_TYPE_NOPROM, tree->opval.op)); + + if (!tree->initMode && IS_CONSTANT (LETYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, "-="); + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "-="); + goto errorTreeReturn; + } + LLVAL (tree) = 1; + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* += operator */ + /*----------------------------*/ + case ADD_ASSIGN: + /* this is not a unary operation */ + /* if both pointers then problem */ + if (IS_PTR (LTYPE (tree)) && IS_PTR (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PTR_PLUS_PTR); + goto errorTreeReturn; + } + + if (!IS_ARITHMETIC (LTYPE (tree)) && !IS_PTR (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "+="); + goto errorTreeReturn; + } + + if (!IS_ARITHMETIC (RTYPE (tree)) && !IS_PTR (RTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_PLUS_INVALID, "+="); + goto errorTreeReturn; + } + RRVAL (tree) = 1; + TETYPE (tree) = getSpec (TTYPE (tree) = computeType (LTYPE (tree), RTYPE (tree), RESULT_TYPE_NOPROM, tree->opval.op)); + + if (!tree->initMode && IS_CONSTANT (LETYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, "+="); + + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "+="); + goto errorTreeReturn; + } + + tree->right = decorateType (newNode ('+', copyAst (tree->left), tree->right), RESULT_TYPE_NONE); + tree->opval.op = '='; + + return tree; +#endif + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* straight assignemnt */ + /*----------------------------*/ + case '=': + /* cannot be an array */ + if (IS_ARRAY (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_ARRAY_ASSIGN); + goto errorTreeReturn; + } + + /* they should either match or be castable */ + if (compareType (LTYPE (tree), RTYPE (tree)) == 0) + { + if (IS_CODEPTR (LTYPE (tree)) && IS_FUNC (LTYPE (tree)->next)) /* function pointer */ + { + werrorfl (tree->filename, tree->lineno, E_INCOMPAT_TYPES); + printFromToType (RTYPE (tree), LTYPE (tree)->next); + } + else + { + int lineno = tree->lineno ? tree->lineno : tree->left->lineno ? tree->left->lineno : tree->right->lineno; + werrorfl (tree->filename, lineno, E_TYPE_MISMATCH, "assignment", " "); + printFromToType (RTYPE (tree), LTYPE (tree)); + } + } + + /* if the left side of the tree is of type void + then report error */ + if (IS_VOID (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_CAST_ZERO); + printFromToType (RTYPE (tree), LTYPE (tree)); + } + + TETYPE (tree) = getSpec (TTYPE (tree) = LTYPE (tree)); + if (IS_STRUCT (LTYPE (tree))) + tree = rewriteStructAssignment (tree); + else + { + RRVAL (tree) = 1; + LLVAL (tree) = 1; + } + if (!tree->initMode) + { + if (IS_CONSTANT (LTYPE (tree))) + werrorfl (tree->filename, tree->lineno, E_CODE_WRITE, "="); + } + if (tree->initMode && SPEC_STAT (getSpec (LTYPE (tree))) && !constExprTree (tree->right)) + werrorfl (tree->filename, tree->lineno, E_CONST_EXPECTED, "="); + if (LRVAL (tree)) + { + werrorfl (tree->filename, tree->lineno, E_LVALUE_REQUIRED, "="); + goto errorTreeReturn; + } + + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* comma operator */ + /*----------------------------*/ + case ',': + TETYPE (tree) = getSpec (TTYPE (tree) = RTYPE (tree)); + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* function call */ + /*----------------------------*/ + case CALL: + if (IFFUNC_ISCRITICAL (LTYPE(tree)) && (inCriticalFunction || inCriticalBlock)) + werror (E_INVALID_CRITICAL); + + /* undo any explicit pointer dereference; PCALL will handle it instead */ + if (IS_FUNC (LTYPE (tree)) && tree->left->type == EX_OP) + { + if (tree->left->opval.op == '*' && !tree->left->right) + tree->left = tree->left->left; + } + + /* require a function or pointer to function */ + if (!IS_FUNC (LTYPE (tree)) && !IS_FUNCPTR (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_FUNCTION_EXPECTED); + goto errorTreeReturn; + } + + /* if there are parms, make sure that + parms are decorate / process / reverse only once */ + if (!tree->right || !tree->right->decorated) + { + sym_link *functype; + parmNumber = 1; + + if (IS_FUNCPTR (LTYPE (tree))) + { + functype = LTYPE (tree)->next; + processFuncPtrArgs (functype); + } + else + functype = LTYPE (tree); + + if (tree->right && tree->right->reversed) + reverseParms (tree->right, 0); + + if (processParms (tree->left, FUNC_ARGS (functype), &tree->right, &parmNumber, TRUE)) + goto errorTreeReturn; + + if (!optimize.noStdLibCall) + optStdLibCall (tree, resultType); + + if ((options.stackAuto || IFFUNC_ISREENT (functype)) && !IFFUNC_ISBUILTIN (functype)) + reverseParms (tree->right, 1); + + TTYPE (tree) = copyLinkChain(functype->next); + TETYPE (tree) = getSpec (TTYPE (tree)); + SPEC_SCLS (TETYPE (tree)) = S_FIXED; + } + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* return statement */ + /*----------------------------*/ + case RETURN: + if (tree->right) + { + int typecompat; + + if (IS_VOID (currFunc->type->next) && tree->right) + { + if (!IS_VOID (RTYPE (tree)) || !options.std_sdcc) + { + werrorfl (tree->filename, tree->lineno, E_FUNC_VOID); + goto errorTreeReturn; + } + } + + typecompat = compareType (currFunc->type->next, RTYPE (tree)); + + /* if there is going to be a casting required then add it */ + if (typecompat == -1) + { + tree->right = newNode (CAST, + newAst_LINK (copyLinkChain (currFunc->type->next)), + tree->right); + tree->right->values.cast.implicitCast = 1; + tree->right->lineno = tree->right->left->lineno = tree->lineno; + tree->right->filename = tree->right->left->filename = tree->filename; + tree->right = decorateType (tree->right, IS_GENPTR (currFunc->type->next) ? RESULT_TYPE_GPTR : RESULT_TYPE_NONE); + } + else if (!typecompat) + { + werrorfl (tree->filename, tree->lineno, W_RETURN_MISMATCH); + printFromToType (RTYPE (tree), currFunc->type->next); + } + + RRVAL (tree) = 1; + } + else /* no return value specified */ + { + if (!IS_VOID (currFunc->type->next) && tree->right == NULL) + { + werrorfl (tree->filename, tree->lineno, W_VOID_FUNC, currFunc->name); + /* We will return an undefined value */ + } + + TTYPE (tree) = TETYPE (tree) = NULL; + } + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* switch statement */ + /*----------------------------*/ + case SWITCH: + /* the switch value must be an integer */ + if (!IS_INTEGRAL (LTYPE (tree))) + { + werrorfl (tree->filename, tree->lineno, E_SWITCH_NON_INTEGER); + goto errorTreeReturn; + } + LRVAL (tree) = 1; + TTYPE (tree) = TETYPE (tree) = NULL; + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* ifx Statement */ + /*----------------------------*/ + case IFX: + tree->left = backPatchLabels (tree->left, tree->trueLabel, tree->falseLabel); + TTYPE (tree) = TETYPE (tree) = NULL; + return tree; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* for Statement */ + /*----------------------------*/ + case FOR: + + AST_FOR (tree, initExpr) = decorateType (resolveSymbols (AST_FOR (tree, initExpr)), RESULT_TYPE_NONE); + AST_FOR (tree, condExpr) = decorateType (resolveSymbols (AST_FOR (tree, condExpr)), RESULT_TYPE_NONE); + AST_FOR (tree, loopExpr) = decorateType (resolveSymbols (AST_FOR (tree, loopExpr)), RESULT_TYPE_NONE); + + /* if the for loop is reversible then + reverse it otherwise do what we normally + do */ + { + symbol *sym; + ast *init, *end; + + if (!AST_FOR (tree, continueLabel)->isref && + !AST_FOR (tree, falseLabel)->isref && + isLoopReversible (tree, &sym, &init, &end)) + return reverseLoop (tree, sym, init, end); + else if (isInitiallyTrue (AST_FOR (tree, initExpr), AST_FOR (tree, condExpr))) + { + tree = createDoFor (AST_FOR (tree, trueLabel), + AST_FOR (tree, continueLabel), + AST_FOR (tree, falseLabel), + AST_FOR (tree, condLabel), + AST_FOR (tree, initExpr), + AST_FOR (tree, condExpr), + AST_FOR (tree, loopExpr), + tree->left, + tree->right); + return decorateType (tree, RESULT_TYPE_NONE); + } + else + { + tree = createFor (AST_FOR (tree, trueLabel), + AST_FOR (tree, continueLabel), + AST_FOR (tree, falseLabel), + AST_FOR (tree, condLabel), + AST_FOR (tree, initExpr), + AST_FOR (tree, condExpr), + AST_FOR (tree, loopExpr), + tree->left, + tree->right); + return decorateType (tree, RESULT_TYPE_NONE); + } + } + case PARAM: + werrorfl (tree->filename, tree->lineno, E_INTERNAL_ERROR, __FILE__, __LINE__, "node PARAM shouldn't be processed here"); + /* but in processParms() */ + return tree; + case INLINEASM: + formatInlineAsm (tree->values.inlineasm); + TTYPE (tree) = TETYPE (tree) = NULL; + return tree; + default: + TTYPE (tree) = TETYPE (tree) = NULL; + return tree; + } + + /* some error found this tree will be killed */ +errorTreeReturn: + TTYPE (tree) = TETYPE (tree) = newCharLink (); + tree->opval.op = NULLOP; + tree->isError = 1; + + return tree; +} + +/*-----------------------------------------------------------------*/ +/* sizeofOp - processes size of operation */ +/*-----------------------------------------------------------------*/ +value * +sizeofOp (sym_link *type) +{ + struct dbuf_s dbuf; + value *val; + int size; + + /* make sure the type is complete and sane */ + checkTypeSanity (type, "(sizeof)"); + + /* get the size and convert it to character */ + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%d", size = getSize (type)); + if (!size && !IS_VOID (type)) + werror (E_SIZEOF_INCOMPLETE_TYPE); + + /* now convert into value */ + val = constVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + return val; +} + +/*-----------------------------------------------------------------*/ +/* sizeofOp - processes alignment of operation */ +/*-----------------------------------------------------------------*/ +value * +alignofOp (sym_link *type) +{ + value *val; + + /* make sure the type is complete and sane */ + checkTypeSanity (type, "(_Alignof)"); + + val = constVal ("1"); + + return val; +} + +/*-----------------------------------------------------------------*/ +/* backPatchLabels - change and or not operators to flow control */ +/*-----------------------------------------------------------------*/ +static ast * +backPatchLabels (ast * tree, symbol * trueLabel, symbol * falseLabel) +{ + if (!tree) + return NULL; + + /* while-loops insert a label between the IFX and the condition, + therefore look behind the label too */ + if (tree->opval.op == LABEL && tree->right && IS_ANDORNOT (tree->right)) + { + tree->right = backPatchLabels (tree->right, trueLabel, falseLabel); + return tree; + } + + if (!(IS_ANDORNOT (tree))) + return tree; + + /* if this an and */ + if (IS_AND (tree)) + { + static int localLbl = 0; + symbol *localLabel; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_andif_%d", localLbl++); + localLabel = newSymbol (dbuf_c_str (&dbuf), NestLevel); + dbuf_destroy (&dbuf); + + tree->left = backPatchLabels (tree->left, localLabel, falseLabel); + + /* if left is already a IFX then just change the if true label in that */ + if (!IS_IFX (tree->left)) + tree->left = newIfxNode (tree->left, localLabel, falseLabel); + + tree->right = backPatchLabels (tree->right, trueLabel, falseLabel); + /* right is a IFX then just join */ + if (IS_IFX (tree->right)) + return newNode (NULLOP, tree->left, createLabel (localLabel, tree->right)); + + tree->right = newIfxNode (tree->right, trueLabel, falseLabel); + tree->right = createLabel (localLabel, tree->right); + + return newNode (NULLOP, tree->left, tree->right); + } + + /* if this is an or operation */ + if (IS_OR (tree)) + { + static int localLbl = 0; + symbol *localLabel; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_orif_%d", localLbl++); + localLabel = newSymbol (dbuf_c_str (&dbuf), NestLevel); + dbuf_destroy (&dbuf); + + tree->left = backPatchLabels (tree->left, trueLabel, localLabel); + + /* if left is already a IFX then just change the if true label in that */ + if (!IS_IFX (tree->left)) + tree->left = newIfxNode (tree->left, trueLabel, localLabel); + + tree->right = backPatchLabels (tree->right, trueLabel, falseLabel); + /* right is a IFX then just join */ + if (IS_IFX (tree->right)) + return newNode (NULLOP, tree->left, createLabel (localLabel, tree->right)); + + tree->right = newIfxNode (tree->right, trueLabel, falseLabel); + tree->right = createLabel (localLabel, tree->right); + + return newNode (NULLOP, tree->left, tree->right); + } + + /* change not */ + if (IS_NOT (tree)) + { + /* call with exchanged labels */ + tree->left = backPatchLabels (tree->left, falseLabel, trueLabel); + + /* if left isn't already a IFX */ + if (!IS_IFX (tree->left)) + { + tree->left = newNode (IFX, tree->left, NULL); + tree->left->trueLabel = falseLabel; + tree->left->falseLabel = trueLabel; + } + return tree->left; + } + + if (IS_IFX (tree)) + { + tree->trueLabel = trueLabel; + tree->falseLabel = falseLabel; + } + + return tree; +} + +/*-----------------------------------------------------------------*/ +/* createBlock - create expression tree for block */ +/*-----------------------------------------------------------------*/ +ast * +createBlock (symbol * decl, ast * body) +{ + ast *ex; + + /* if the block has nothing */ + if (!body && !decl) + return NULL; + + ex = newNode (BLOCK, NULL, body); + ex->values.sym = decl; + + ex->level += LEVEL_UNIT; + ex->filename = NULL; + ex->lineno = 0; + if (body) + ex->block = body->block; + return ex; +} + +/*-----------------------------------------------------------------*/ +/* createLabel - creates the expression tree for labels */ +/*-----------------------------------------------------------------*/ +ast * +createLabel (symbol * label, ast * stmnt) +{ + symbol *csym; + ast *rValue; + + /* must create fresh symbol if the symbol name */ + /* exists in the symbol table, since there can */ + /* be a variable with the same name as the labl */ + if ((csym = findSym (SymbolTab, NULL, label->name)) && (csym->level == label->level)) + label = newSymbol (label->name, label->level); + + /* put the label in the LabelSymbol table */ + /* but first check if a label of the same */ + /* name exists */ + if ((csym = findSym (LabelTab, NULL, label->name))) + werror (E_DUPLICATE_LABEL, label->name); + else + addSym (LabelTab, label, label->name, label->level, 0, 0); + + label->isitmp = 1; + label->islbl = 1; + label->key = labelKey++; + rValue = newNode (LABEL, newAst_VALUE (symbolVal (label)), stmnt); + rValue->filename = NULL; + rValue->lineno = 0; + + return rValue; +} + +/*-----------------------------------------------------------------*/ +/* createCase - generates the parsetree for a case statement */ +/*-----------------------------------------------------------------*/ +ast * +createCase (ast * swStat, ast * caseVal, ast * stmnt) +{ + struct dbuf_s dbuf; + ast *rexpr; + value *val; + + /* if the switch statement does not exist */ + /* then case is out of context */ + if (!swStat) + { + werrorfl (caseVal->filename, caseVal->lineno, E_CASE_CONTEXT); + return NULL; + } + + caseVal = decorateType (resolveSymbols (caseVal), RESULT_TYPE_NONE); + /* if not a constant then error */ + if (!IS_LITERAL (caseVal->ftype)) + { + werrorfl (caseVal->filename, caseVal->lineno, E_CASE_CONSTANT); + return NULL; + } + + /* if not a integer than error */ + if (!IS_INTEGRAL (caseVal->ftype)) + { + werrorfl (caseVal->filename, caseVal->lineno, E_CASE_NON_INTEGER); + return NULL; + } + + /* find the end of the switch values chain */ + if (!(val = swStat->values.switchVals.swVals)) + swStat->values.switchVals.swVals = caseVal->opval.val; + else + { + /* also order the cases according to value */ + value *pval = NULL; + int cVal = (int) ulFromVal (caseVal->opval.val); + while (val && (int) ulFromVal (val) < cVal) + { + pval = val; + val = val->next; + } + + /* if we reached the end then */ + if (!val) + { + pval->next = caseVal->opval.val; + } + else if ((int) ulFromVal (val) == cVal) + { + werrorfl (caseVal->filename, caseVal->lineno, E_DUPLICATE_LABEL, "case"); + return NULL; + } + else + { + /* we found a value greater than */ + /* the current value we must add this */ + /* before the value */ + caseVal->opval.val->next = val; + + /* if this was the first in chain */ + if (swStat->values.switchVals.swVals == val) + swStat->values.switchVals.swVals = caseVal->opval.val; + else + pval->next = caseVal->opval.val; + } + + } + + /* create the case label */ + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_case_%d_%d", swStat->values.switchVals.swNum, (int) ulFromVal (caseVal->opval.val)); + + rexpr = createLabel (newSymbol (dbuf_c_str (&dbuf), 0), stmnt); + dbuf_destroy (&dbuf); + rexpr->filename = 0; + rexpr->lineno = 0; + return rexpr; +} + +/*-----------------------------------------------------------------*/ +/* createDefault - creates the parse tree for the default statement */ +/*-----------------------------------------------------------------*/ +ast * +createDefault (ast * swStat, ast * defaultVal, ast * stmnt) +{ + struct dbuf_s dbuf; + ast *ret; + + /* if the switch statement does not exist */ + /* then case is out of context */ + if (!swStat) + { + werrorfl (defaultVal->filename, defaultVal->lineno, E_CASE_CONTEXT); + return NULL; + } + + if (swStat->values.switchVals.swDefault) + { + werrorfl (defaultVal->filename, defaultVal->lineno, E_DUPLICATE_LABEL, "default"); + return NULL; + } + + /* turn on the default flag */ + swStat->values.switchVals.swDefault = 1; + + /* create the label */ + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_default_%d", swStat->values.switchVals.swNum); + ret = createLabel (newSymbol (dbuf_c_str (&dbuf), 0), stmnt); + dbuf_destroy (&dbuf); + return ret; +} + +/*-----------------------------------------------------------------*/ +/* createIf - creates the parsetree for the if statement */ +/*-----------------------------------------------------------------*/ +ast * +createIf (ast * condAst, ast * ifBody, ast * elseBody) +{ + static int Lblnum = 0; + ast *ifTree; + symbol *ifTrue, *ifFalse, *ifEnd; + struct dbuf_s dbuf; + + /* if neither exists */ + if (!elseBody && !ifBody) + { + // if there are no side effects (i++, j() etc) + if (!hasSEFcalls (condAst)) + { + return condAst; + } + } + + /* create the labels */ + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "__iffalse_%d", Lblnum); + ifFalse = newSymbol (dbuf_c_str (&dbuf), NestLevel); + dbuf_destroy (&dbuf); + /* if no else body then end == false */ + if (!elseBody) + { + ifEnd = ifFalse; + } + else + { + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "__ifend_%d", Lblnum); + ifEnd = newSymbol (dbuf_c_str (&dbuf), NestLevel); + dbuf_destroy (&dbuf); + } + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "__iftrue_%d", Lblnum); + ifTrue = newSymbol (dbuf_c_str (&dbuf), NestLevel); + dbuf_destroy (&dbuf); + + Lblnum++; + + /* attach the ifTrue label to the top of it body */ + ifBody = createLabel (ifTrue, ifBody); + /* attach a goto end to the ifBody if else is present */ + if (elseBody) + { + ifBody = newNode (NULLOP, ifBody, newNode (GOTO, newAst_VALUE (symbolVal (ifEnd)), NULL)); + /* put the elseLabel on the else body */ + elseBody = createLabel (ifFalse, elseBody); + /* out the end at the end of the body */ + elseBody = newNode (NULLOP, elseBody, createLabel (ifEnd, NULL)); + } + else + { + ifBody = newNode (NULLOP, ifBody, createLabel (ifFalse, NULL)); + } + condAst = backPatchLabels (condAst, ifTrue, ifFalse); + if (IS_IFX (condAst)) + ifTree = condAst; + else + ifTree = newIfxNode (condAst, ifTrue, ifFalse); + + return newNode (NULLOP, ifTree, newNode (NULLOP, ifBody, elseBody)); + +} + +/*-----------------------------------------------------------------*/ +/* createDo - creates parse tree for do */ +/* _dobody_n: */ +/* statements */ +/* _docontinue_n: */ +/* condition_expression +-> trueLabel -> _dobody_n */ +/* | */ +/* +-> falseLabel-> _dobreak_n */ +/* _dobreak_n: */ +/*-----------------------------------------------------------------*/ +ast * +createDo (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, ast * condAst, ast * doBody) +{ + ast *doTree; + + /* if the body does not exist then it is simple */ + if (!doBody) + { + condAst = backPatchLabels (condAst, continueLabel, falseLabel); + if (condAst && !IS_IFX (condAst)) + { + condAst = newNode (IFX, condAst, NULL); + condAst->trueLabel = continueLabel; + condAst->falseLabel = NULL; + } + + doTree = createLabel (continueLabel, condAst); + doTree = newNode (NULLOP, doTree, createLabel (falseLabel, NULL)); + return doTree; + } + + /* otherwise we have a body */ + condAst = backPatchLabels (condAst, trueLabel, falseLabel); + + /* attach the body label to the top */ + doBody = createLabel (trueLabel, doBody); + /* attach the continue label to end of body */ + doBody = newNode (NULLOP, doBody, createLabel (continueLabel, NULL)); + + /* now put the break label at the end */ + if (IS_IFX (condAst)) + doTree = condAst; + else + doTree = newIfxNode (condAst, trueLabel, falseLabel); + + doTree = newNode (NULLOP, doTree, createLabel (falseLabel, NULL)); + + /* putting it together */ + return newNode (NULLOP, doBody, doTree); +} + +/*-----------------------------------------------------------------*/ +/* createFor - creates parse tree for 'for' statement */ +/* initExpr */ +/* _forcond_n: */ +/* condExpr +-> trueLabel -> _forbody_n */ +/* | */ +/* +-> falseLabel-> _forbreak_n */ +/* _forbody_n: */ +/* statements */ +/* _forcontinue_n: */ +/* loopExpr */ +/* goto _forcond_n ; */ +/* _forbreak_n: */ +/*-----------------------------------------------------------------*/ +ast * +createFor (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, + symbol * condLabel, ast * initExpr, ast * condExpr, ast * loopExpr, + ast * forBody, ast * continueLabelAst) +{ + ast *forTree; + + /* vanilla for statement */ + condExpr = backPatchLabels (condExpr, trueLabel, falseLabel); + + if (condExpr && !IS_IFX (condExpr)) + condExpr = newIfxNode (condExpr, trueLabel, falseLabel); + + /* attach body label to body */ + forBody = createLabel (trueLabel, forBody); + + /* attach condition label to condition */ + condExpr = createLabel (condLabel, condExpr); + + /* attach continue to forLoop expression & attach */ + /* goto the forcond @ and of loopExpression */ + loopExpr = newNode (NULLOP, loopExpr, newNode (GOTO, newAst_VALUE (symbolVal (condLabel)), NULL)); + if (continueLabelAst) + { + continueLabelAst->right = loopExpr; + loopExpr = continueLabelAst; + } + else + loopExpr = createLabel (continueLabel, loopExpr); + + /* now start putting them together */ + forTree = newNode (NULLOP, initExpr, condExpr); + forTree = newNode (NULLOP, forTree, forBody); + forTree = newNode (NULLOP, forTree, loopExpr); + + /* the break label is already in the tree as a sibling */ + /* to the original FOR node this tree is replacing */ + return forTree; +} + +/*-----------------------------------------------------------------*/ +/* createWhile - creates parse tree for while statement */ +/* the while statement will be created as follows */ +/* */ +/* _while_continue_n: */ +/* condition_expression +-> trueLabel -> _while_boby_n */ +/* | */ +/* +-> falseLabel -> _while_break_n */ +/* _while_body_n: */ +/* statements */ +/* goto _while_continue_n */ +/* _while_break_n: */ +/*-----------------------------------------------------------------*/ +ast * +createWhile (symbol * trueLabel, symbol * continueLabel, symbol * falseLabel, ast * condExpr, ast * whileBody) +{ + ast *whileTree; + + /* put the continue label */ + condExpr = backPatchLabels (condExpr, trueLabel, falseLabel); + if (condExpr && !IS_IFX (condExpr)) + { + condExpr = newNode (IFX, condExpr, NULL); + /* put the true & false labels in place */ + condExpr->trueLabel = trueLabel; + condExpr->falseLabel = falseLabel; + } + whileTree = createLabel (continueLabel, condExpr); + whileTree->filename = NULL; + whileTree->lineno = 0; + + /* put the body label in front of the body */ + whileBody = createLabel (trueLabel, whileBody); + whileBody->filename = NULL; + whileBody->lineno = 0; + /* put a jump to continue at the end of the body */ + /* and put break label at the end of the body */ + whileBody = newNode (NULLOP, + whileBody, newNode (GOTO, newAst_VALUE (symbolVal (continueLabel)), createLabel (falseLabel, NULL))); + + /* put it all together */ + return newNode (NULLOP, whileTree, whileBody); +} + +/*-----------------------------------------------------------------*/ +/* isShiftRightLitVal _BitAndLitVal - helper function */ +/*-----------------------------------------------------------------*/ +static ast * +isShiftRightLitVal_BitAndLitVal (ast * tree) +{ + /* if this is not a bit and */ + if (!IS_BITAND (tree)) + return NULL; + + /* will look for tree of the form + ( expr >> litval2) & litval1 */ + if (!IS_AST_LIT_VALUE (tree->right)) + return NULL; + + if (!IS_RIGHT_OP (tree->left)) + return NULL; + + if (!IS_AST_LIT_VALUE (tree->left->right)) + return NULL; + + return tree->left->left; +} + +/*-----------------------------------------------------------------*/ +/* isBitAndPowOf2 - helper function */ +/*-----------------------------------------------------------------*/ +static int +isBitAndPow2 (ast * tree) +{ + /* if this is not a bit and */ + if (!IS_BITAND (tree)) + return -1; + + /* will look for tree of the form + ( expr & (1 << litval) */ + if (!IS_AST_LIT_VALUE (tree->right)) + return -1; + + return powof2 (AST_ULONG_VALUE (tree->right)); +} + +/*-----------------------------------------------------------------*/ +/* optimizeGetHbit - get highest order bit of the expression */ +/*-----------------------------------------------------------------*/ +ast * +optimizeGetHbit (ast * tree, RESULT_TYPE resultType) +{ + unsigned int bit, msb; + ast *expr; + + expr = isShiftRightLitVal_BitAndLitVal (tree); + if (expr) + { + if ((AST_ULONG_VALUE (tree->right) != 1) || + ((bit = AST_ULONG_VALUE (tree->left->right)) != (msb = (bitsForType (TTYPE (expr)) - 1)))) + expr = NULL; + } + if (!expr && (resultType == RESULT_TYPE_BOOL)) + { + int bit = isBitAndPow2 (tree); + expr = tree->left; + msb = bitsForType (TTYPE (expr)) - 1; + if ((bit < 0) || (bit != (int) msb)) + expr = NULL; + } + if (!expr || IS_BOOLEAN (TTYPE (expr))) + return tree; + + /* make sure the port supports GETHBIT */ + if (port->hasExtBitOp && !port->hasExtBitOp (GETHBIT, getSize (TTYPE (expr)))) + return tree; + + return decorateType (newNode (GETHBIT, expr, NULL), resultType); +} + +/*-----------------------------------------------------------------*/ +/* optimizeGetAbit - get a single bit of the expression */ +/*-----------------------------------------------------------------*/ +ast * +optimizeGetAbit (ast * tree, RESULT_TYPE resultType) +{ + ast *expr; + ast *count = NULL; + + expr = isShiftRightLitVal_BitAndLitVal (tree); + if (expr) + { + if (AST_ULONG_VALUE (tree->right) != 1) + expr = NULL; + count = tree->left->right; + } + if (!expr && (resultType == RESULT_TYPE_BOOL)) + { + int p2 = isBitAndPow2 (tree); + if (p2 >= 0 && !IS_BOOLEAN (TTYPE (tree->left))) + { + expr = tree->left; + count = newAst_VALUE (valueFromLit (p2)); + } + } + if (!expr) + return tree; + + /* make sure the port supports GETABIT */ + if (port->hasExtBitOp && !port->hasExtBitOp (GETABIT, getSize (TTYPE (expr)))) + return tree; + + return decorateType (newNode (GETABIT, expr, count), resultType); +} + +/*-----------------------------------------------------------------*/ +/* optimizeGetByte - get a byte of the expression */ +/*-----------------------------------------------------------------*/ +ast * +optimizeGetByte (ast * tree, RESULT_TYPE resultType) +{ + unsigned int i = 1; + unsigned int size; + ast *expr; + ast *count = NULL; + + expr = isShiftRightLitVal_BitAndLitVal (tree); + if (expr) + { + i = AST_ULONG_VALUE (tree->left->right); + count = tree->left->right; + if (AST_ULONG_VALUE (tree->right) != 0xFF) + expr = NULL; + } + if (!expr && resultType == RESULT_TYPE_CHAR) + { + /* if this is a right shift over a multiple of 8 */ + if (IS_RIGHT_OP (tree) && IS_AST_LIT_VALUE (tree->right)) + { + i = AST_ULONG_VALUE (tree->right); + count = tree->right; + expr = tree->left; + } + } + if (!expr || (i % 8)) + return tree; + size = getSize (TTYPE (expr)); + if ((i >= size * 8) || (size <= 1)) + return tree; + + /* make sure the port supports GETBYTE */ + if (port->hasExtBitOp && !port->hasExtBitOp (GETBYTE, size)) + return tree; + + return decorateType (newNode (GETBYTE, expr, count), RESULT_TYPE_NONE); +} + +/*-----------------------------------------------------------------*/ +/* optimizeGetWord - get two bytes of the expression */ +/*-----------------------------------------------------------------*/ +ast * +optimizeGetWord (ast *tree, RESULT_TYPE resultType) +{ + unsigned int i = 1; + unsigned int size; + ast *expr; + ast *count = NULL; + + expr = isShiftRightLitVal_BitAndLitVal (tree); + + if (expr) + { + i = AST_ULONG_VALUE (tree->left->right); + count = tree->left->right; + if (AST_ULONG_VALUE (tree->right) != 0xFFFF) + expr = NULL; + } + if (!expr && resultType == RESULT_TYPE_INT) + { + /* if this is a right shift over a multiple of 8 */ + if (IS_RIGHT_OP (tree) && IS_AST_LIT_VALUE (tree->right)) + { + i = AST_ULONG_VALUE (tree->right); + count = tree->right; + expr = tree->left; + } + } + if (!expr || (i % 8)) + return tree; + size = getSize (TTYPE (expr)); + if ((i >= (size - 1) * 8) || (size <= 2)) + return tree; + + /* make sure the port supports GETWORD */ + if (port->hasExtBitOp && !port->hasExtBitOp (GETWORD, size)) + return tree; + + return decorateType (newNode (GETWORD, expr, count), RESULT_TYPE_NONE); +} + +/*-----------------------------------------------------------------*/ +/* optimizeRRCRLC :- optimize for Rotate Left/Right with carry */ +/*-----------------------------------------------------------------*/ +ast * +optimizeRRCRLC (ast * root) +{ + /* will look for trees of the form + (?expr << 1) | (?expr >> 7) or + (?expr >> 7) | (?expr << 1) will make that + into a RLC : operation .. + Will also look for + (?expr >> 1) | (?expr << 7) or + (?expr << 7) | (?expr >> 1) will make that + into a RRC operation + note : by 7 I mean (number of bits required to hold the + variable -1 ) */ + /* if the root operation is not a | operation then not */ + if (!IS_BITOR (root)) + return root; + + /* I have to think of a better way to match patterns this sucks */ + /* that aside let's start looking for the first case : I use a + negative check a lot to improve the efficiency */ + /* (?expr << 1) | (?expr >> 7) */ + if (IS_LEFT_OP (root->left) && IS_RIGHT_OP (root->right)) + { + + if (!SPEC_USIGN (TETYPE (root->left->left))) + return root; + + if (!IS_AST_LIT_VALUE (root->left->right) || !IS_AST_LIT_VALUE (root->right->right)) + goto tryNext0; + + /* make sure it is the same expression */ + if (!isAstEqual (root->left->left, root->right->left)) + goto tryNext0; + + if (AST_ULONG_VALUE (root->left->right) != 1) + goto tryNext0; + + if (AST_ULONG_VALUE (root->right->right) != (getSize (TTYPE (root->left->left)) * 8 - 1)) + goto tryNext0; + + /* make sure the port supports RLC */ + if (port->hasExtBitOp && !port->hasExtBitOp (RLC, getSize (TTYPE (root->left->left)))) + return root; + + /* whew got the first case : create the AST */ + return newNode (RLC, root->left->left, NULL); + } + +tryNext0: + /* check for second case */ + /* (?expr >> 7) | (?expr << 1) */ + if (IS_LEFT_OP (root->right) && IS_RIGHT_OP (root->left)) + { + + if (!SPEC_USIGN (TETYPE (root->left->left))) + return root; + + if (!IS_AST_LIT_VALUE (root->left->right) || !IS_AST_LIT_VALUE (root->right->right)) + goto tryNext1; + + /* make sure it is the same symbol */ + if (!isAstEqual (root->left->left, root->right->left)) + goto tryNext1; + + if (AST_ULONG_VALUE (root->right->right) != 1) + goto tryNext1; + + if (AST_ULONG_VALUE (root->left->right) != (getSize (TTYPE (root->left->left)) * 8 - 1)) + goto tryNext1; + + /* make sure the port supports RLC */ + if (port->hasExtBitOp && !port->hasExtBitOp (RLC, getSize (TTYPE (root->left->left)))) + return root; + + /* whew got the first case : create the AST */ + return newNode (RLC, root->left->left, NULL); + + } + +tryNext1: + /* third case for RRC */ + /* (?symbol >> 1) | (?symbol << 7) */ + if (IS_LEFT_OP (root->right) && IS_RIGHT_OP (root->left)) + { + + if (!SPEC_USIGN (TETYPE (root->left->left))) + return root; + + if (!IS_AST_LIT_VALUE (root->left->right) || !IS_AST_LIT_VALUE (root->right->right)) + goto tryNext2; + + /* make sure it is the same symbol */ + if (!isAstEqual (root->left->left, root->right->left)) + goto tryNext2; + + if (AST_ULONG_VALUE (root->left->right) != 1) + goto tryNext2; + + if (AST_ULONG_VALUE (root->right->right) != (getSize (TTYPE (root->left->left)) * 8 - 1)) + goto tryNext2; + + /* make sure the port supports RRC */ + if (port->hasExtBitOp && !port->hasExtBitOp (RRC, getSize (TTYPE (root->left->left)))) + return root; + + /* whew got the first case : create the AST */ + return newNode (RRC, root->left->left, NULL); + + } +tryNext2: + /* fourth and last case for now */ + /* (?symbol << 7) | (?symbol >> 1) */ + if (IS_RIGHT_OP (root->right) && IS_LEFT_OP (root->left)) + { + if (!SPEC_USIGN (TETYPE (root->left->left))) + return root; + + if (!IS_AST_LIT_VALUE (root->left->right) || !IS_AST_LIT_VALUE (root->right->right)) + return root; + + /* make sure it is the same symbol */ + if (!isAstEqual (root->left->left, root->right->left)) + return root; + + if (AST_ULONG_VALUE (root->right->right) != 1) + return root; + + if (AST_ULONG_VALUE (root->left->right) != (getSize (TTYPE (root->left->left)) * 8 - 1)) + return root; + + /* make sure the port supports RRC */ + if (port->hasExtBitOp && !port->hasExtBitOp (RRC, getSize (TTYPE (root->left->left)))) + return root; + + /* whew got the first case : create the AST */ + return newNode (RRC, root->left->left, NULL); + } + + /* not found return root */ + return root; +} + +/*-----------------------------------------------------------------*/ +/* optimizeSWAP :- optimize for nibble/byte/word swaps */ +/*-----------------------------------------------------------------*/ +ast * +optimizeSWAP (ast * root) +{ + /* will look for trees of the form + (?expr << 4) | (?expr >> 4) or + (?expr >> 4) | (?expr << 4) will make that + into a SWAP : operation .. + note : by 4 I mean (number of bits required to hold the + variable /2 ) */ + /* if the root operation is not a | operation then not */ + if (!IS_BITOR (root)) + return root; + + /* (?expr << 4) | (?expr >> 4) */ + if ((IS_LEFT_OP (root->left) && IS_RIGHT_OP (root->right)) || (IS_RIGHT_OP (root->left) && IS_LEFT_OP (root->right))) + { + + if (!SPEC_USIGN (TETYPE (root->left->left))) + return root; + + if (!IS_AST_LIT_VALUE (root->left->right) || !IS_AST_LIT_VALUE (root->right->right)) + return root; + + /* make sure it is the same expression */ + if (!isAstEqual (root->left->left, root->right->left)) + return root; + + if (AST_ULONG_VALUE (root->left->right) != (getSize (TTYPE (root->left->left)) * 4)) + return root; + + if (AST_ULONG_VALUE (root->right->right) != (getSize (TTYPE (root->left->left)) * 4)) + return root; + + /* make sure the port supports SWAP */ + if (port->hasExtBitOp && !port->hasExtBitOp (SWAP, getSize (TTYPE (root->left->left)))) + return root; + + /* found it : create the AST */ + return newNode (SWAP, root->left->left, NULL); + } + + /* not found return root */ + return root; +} + +/*-----------------------------------------------------------------*/ +/* optimizeCompare - optimizes compares for bit variables */ +/*-----------------------------------------------------------------*/ +static ast * +optimizeCompare (ast * root) +{ + ast *optExpr = NULL; + value *vleft; + value *vright; + unsigned int litValue; + + /* if nothing then return nothing */ + if (!root) + return NULL; + + /* if not a compare op then do leaves */ + if (!IS_COMPARE_OP (root)) + { + root->left = optimizeCompare (root->left); + root->right = optimizeCompare (root->right); + return root; + } + + /* if left & right are the same then depending + of the operation do */ + if (isAstEqual (root->left, root->right) && !hasSEFcalls (root)) + { + switch (root->opval.op) + { + case '>': + case '<': + case NE_OP: + optExpr = newAst_VALUE (constBoolVal (0)); + break; + case GE_OP: + case LE_OP: + case EQ_OP: + optExpr = newAst_VALUE (constBoolVal (1)); + break; + } + + return decorateType (optExpr, RESULT_TYPE_NONE); + } + + vleft = (root->left->type == EX_VALUE ? root->left->opval.val : NULL); + + vright = (root->right->type == EX_VALUE ? root->right->opval.val : NULL); + + /* if left is a BITVAR in BITSPACE */ + /* and right is a LITERAL then */ + /* optimize else do nothing */ + if (vleft && vright && IS_BITVAR (vleft->etype) && IN_BITSPACE (SPEC_OCLS (vleft->etype)) && IS_LITERAL (vright->etype)) + { + /* if right side > 1 then comparison may never succeed */ + if ((litValue = (int) ulFromVal (vright)) > 1) + { + werror (W_BAD_COMPARE); + goto noOptimize; + } + + if (litValue) + { + switch (root->opval.op) + { + case '>': /* bit value greater than 1 cannot be */ + werror (W_BAD_COMPARE); + goto noOptimize; + break; + + case '<': /* bit value < 1 means 0 */ + case NE_OP: + optExpr = newNode ('!', newAst_VALUE (vleft), NULL); + break; + + case LE_OP: /* bit value <= 1 means no check */ + optExpr = newAst_VALUE (vright); + break; + + case GE_OP: /* bit value >= 1 means only check for = */ + case EQ_OP: + optExpr = newAst_VALUE (vleft); + break; + } + } + else + { /* literal is zero */ + switch (root->opval.op) + { + case '<': /* bit value < 0 cannot be */ + werror (W_BAD_COMPARE); + goto noOptimize; + break; + + case '>': /* bit value > 0 means 1 */ + case NE_OP: + optExpr = newAst_VALUE (vleft); + break; + + case LE_OP: /* bit value <= 0 means no check */ + case GE_OP: /* bit value >= 0 means no check */ + werror (W_BAD_COMPARE); + goto noOptimize; + break; + + case EQ_OP: /* bit == 0 means ! of bit */ + optExpr = newNode ('!', newAst_VALUE (vleft), NULL); + break; + } + } + return decorateType (resolveSymbols (optExpr), RESULT_TYPE_NONE); + } /* end-of-if of BITVAR */ + +noOptimize: + return root; +} + +/*-----------------------------------------------------------------*/ +/* addSymToBlock : adds the symbol to the first block we find */ +/*-----------------------------------------------------------------*/ +void +addSymToBlock (symbol * sym, ast * tree) +{ + /* reached end of tree or a leaf */ + if (!tree || IS_AST_LINK (tree) || IS_AST_VALUE (tree)) + return; + + /* found a block */ + if (IS_AST_OP (tree) && tree->opval.op == BLOCK) + { + symbol *lsym = copySymbol (sym); + + lsym->next = AST_VALUES (tree, sym); + AST_VALUES (tree, sym) = lsym; + return; + } + + addSymToBlock (sym, tree->left); + addSymToBlock (sym, tree->right); +} + +/*-----------------------------------------------------------------*/ +/* processRegParms - do processing for register parameters */ +/*-----------------------------------------------------------------*/ +static void +processRegParms (value * args, ast * body) +{ + while (args) + { + if (IS_REGPARM (args->etype)) + addSymToBlock (args->sym, body); + args = args->next; + } +} + +/*-----------------------------------------------------------------*/ +/* resetParmKey - resets the operandkeys for the symbols */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (resetParmKey) +{ + symbol *sym = item; + + sym->key = 0; + sym->defs = NULL; + sym->uses = NULL; + sym->remat = 0; + return 1; +} + +/*------------------------------------------------------------------*/ +/* fixupInlineLabel - change a label in an inlined function so that */ +/* it is always unique no matter how many times */ +/* the function is inlined. */ +/*------------------------------------------------------------------*/ +static void +fixupInlineLabel (symbol * sym) +{ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%s_%d", sym->name, inlineState.count); + strncpyz (sym->name, dbuf_c_str (&dbuf), SDCC_NAME_MAX); + dbuf_destroy (&dbuf); +} + +/*------------------------------------------------------------------*/ +/* copyAstLoc - copy location information (file, line, block, etc.) */ +/* from one ast node to another */ +/*------------------------------------------------------------------*/ +static void +copyAstLoc (ast * dest, ast * src) +{ + dest->filename = src->filename; + dest->lineno = src->lineno; + dest->level = src->level; + dest->block = src->block; + dest->seqPoint = src->seqPoint; +} + +static void fixupInline (ast * tree, long level); + +/*-----------------------------------------------------------------*/ +/* fixupInlineInDeclarators - recursively perform various fixups */ +/* on an inline function tree */ +/*-----------------------------------------------------------------*/ +static void +fixupInlineInDeclarators (struct initList *ival, long level) +{ + for (; ival; ival = ival->next) + { + switch (ival->type) + { + case INIT_NODE: + fixupInline (ival->init.node, level); + break; + case INIT_DEEP: + fixupInlineInDeclarators (ival->init.deep, level); + break; + } + } +} + +/*-----------------------------------------------------------------*/ +/* fixupInline - perform various fixups on an inline function tree */ +/* to take into account that it is no longer a */ +/* stand-alone function. */ +/*-----------------------------------------------------------------*/ +static void +fixupInline (ast * tree, long level) +{ + int savedBlockno = currBlockno; + + if (IS_AST_OP (tree) && (tree->opval.op == BLOCK)) + { + symbol *decls; + + int thisBlockBlockno; + + btree_add_child(currBlockno, ++blockNo); + thisBlockBlockno = blockNo; + + level += LEVEL_UNIT; + + /* Add any declared variables back into the symbol table */ + for (decls = tree->values.sym; decls; decls = decls->next) + { + decls->level = level; + decls->block = currBlockno = thisBlockBlockno; + addSym (SymbolTab, decls, decls->name, decls->level, decls->block, 0); + + if (decls->ival) + fixupInlineInDeclarators (decls->ival, level); + } + + currBlockno = thisBlockBlockno; + } + + tree->level = level; + tree->block = currBlockno; + + /* Update symbols */ + if (IS_AST_VALUE (tree) && tree->opval.val->sym) + { + symbol *sym = tree->opval.val->sym; + + sym->level = level; + sym->block = currBlockno; + + sym->reqv = NULL; + SYM_SPIL_LOC (sym) = NULL; + sym->key = 0; + + /* If the symbol is a label, we need to renumber it */ + if (sym->islbl) + fixupInlineLabel (sym); + } + + /* Update IFX target labels */ + if (tree->type == EX_OP && tree->opval.op == IFX) + { + if (tree->trueLabel) + fixupInlineLabel (tree->trueLabel); + if (tree->falseLabel) + fixupInlineLabel (tree->falseLabel); + } + + /* Replace RETURN with optional assignment and a GOTO to the end */ + /* of the inlined function */ + if (tree->type == EX_OP && tree->opval.op == RETURN) + { + ast *assignTree = NULL; + ast *gotoTree; + + if (inlineState.retsym && tree->right) + { + assignTree = newNode ('=', newAst_VALUE (symbolVal (copySymbol (inlineState.retsym))), tree->right); + copyAstLoc (assignTree, tree); + } + + gotoTree = newNode (GOTO, newAst_VALUE (symbolVal (inlineState.retlab)), NULL); + copyAstLoc (gotoTree, tree); + + tree->opval.op = NULLOP; + tree->left = assignTree; + tree->right = gotoTree; + } + + /* Update any children */ + if (tree->left) + fixupInline (tree->left, level); + if (tree->right) + fixupInline (tree->right, level); + + /* Update SWITCH branches */ + if (tree->type == EX_OP && tree->opval.op == SWITCH) + { + struct dbuf_s dbuf; + const char *oldsuff = tree->values.switchVals.swSuffix; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%s_%d", oldsuff ? oldsuff : "", inlineState.count); + tree->values.switchVals.swSuffix = dbuf_detach (&dbuf); + } + + /* Update FOR expression */ + if (tree->type == EX_OP && tree->opval.op == FOR) + { + if (AST_FOR (tree, initExpr)) + fixupInline (AST_FOR (tree, initExpr), level); + if (AST_FOR (tree, condExpr)) + fixupInline (AST_FOR (tree, condExpr), level); + if (AST_FOR (tree, loopExpr)) + fixupInline (AST_FOR (tree, loopExpr), level); + + if (AST_FOR (tree, trueLabel)) + fixupInlineLabel (AST_FOR (tree, trueLabel)); + if (AST_FOR (tree, continueLabel)) + fixupInlineLabel (AST_FOR (tree, continueLabel)); + if (AST_FOR (tree, falseLabel)) + fixupInlineLabel (AST_FOR (tree, falseLabel)); + if (AST_FOR (tree, condLabel)) + fixupInlineLabel (AST_FOR (tree, condLabel)); + } + + if (IS_AST_OP (tree) && (tree->opval.op == LABEL)) + { + symbol *label = tree->left->opval.val->sym; + + label->key = labelKey++; + /* Add this label back into the symbol table */ + addSym (LabelTab, label, label->name, label->level, 0, 0); + } + + if (IS_AST_OP (tree) && (tree->opval.op == BLOCK)) + { + level -= LEVEL_UNIT; + currBlockno = savedBlockno; + } +} + +/*-----------------------------------------------------------------*/ +/* inlineAddDecl - add a variable declaration to an ast block. It */ +/* is also added to the symbol table if addSymTab */ +/* is nonzero. */ +/*-----------------------------------------------------------------*/ +static void +inlineAddDecl (symbol * sym, ast * block, int addSymTab, int toFront) +{ + sym->reqv = NULL; + SYM_SPIL_LOC (sym) = NULL; + sym->key = 0; + if (block != NULL) + { + symbol **decl = &(block->values.sym); + + sym->level = block->level; + sym->block = block->block; + + while (*decl) + { + if (strcmp ((*decl)->name, sym->name) == 0) + return; + decl = &((*decl)->next); + } + + if (toFront) + { + sym->next = block->values.sym; + block->values.sym = sym; + } + else + *decl = sym; + + if (addSymTab) + addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 0); + } +} + +/*-----------------------------------------------------------------*/ +/* inlineTempVar - create a temporary variable for inlining */ +/*-----------------------------------------------------------------*/ +static symbol * +inlineTempVar (sym_link * type, long level) +{ + symbol *sym; + + sym = newSymbol (genSymName (level), level); + sym->type = copyLinkChain (type); + sym->etype = getSpec (sym->type); + SPEC_SCLS (sym->etype) = S_AUTO; + SPEC_OCLS (sym->etype) = NULL; + SPEC_EXTR (sym->etype) = 0; + SPEC_STAT (sym->etype) = 0; + if (IS_SPEC (sym->type)) + { + SPEC_VOLATILE (sym->type) = 0; + SPEC_ADDRSPACE (sym->type) = 0; + } + else + { + DCL_PTR_VOLATILE (sym->type) = 0; + DCL_PTR_ADDRSPACE (sym->type) = 0; + } + SPEC_ABSA (sym->etype) = 0; + + return sym; +} + +/*-----------------------------------------------------------------*/ +/* inlineFindParm - search an ast tree of parameters to find one */ +/* at a particular index (0=first parameter). */ +/* Returns 0 if not found. */ +/*-----------------------------------------------------------------*/ +static ast * +inlineFindParm (ast *parms, int index) +{ + if (!parms) + return 0; + + if (parms->type == EX_OP && parms->opval.op == PARAM) + { + ast *p; + + if (p = inlineFindParm (parms->left, index)) + return p; + + if (p = inlineFindParm (parms->right, index - 1)) + return p; + } + + return (index ? 0 : parms); +} + +static void expandInlineFuncs (ast * tree, ast * block); + +/*-----------------------------------------------------------------*/ +/* expandInlineFuncsInDeclarators - recursively replace calls to */ +/* inline functions */ +/*-----------------------------------------------------------------*/ +static void +expandInlineFuncsInDeclarators (struct initList *ival, ast * block) +{ + for (; ival; ival = ival->next) + { + switch (ival->type) + { + case INIT_NODE: + expandInlineFuncs (ival->init.node, block); + break; + case INIT_DEEP: + expandInlineFuncsInDeclarators (ival->init.deep, block); + break; + } + } +} + +/*-----------------------------------------------------------------*/ +/* expandInlineFuncs - replace calls to inline functions with the */ +/* function itself */ +/*-----------------------------------------------------------------*/ +static void +expandInlineFuncs (ast * tree, ast * block) +{ + if (IS_AST_OP (tree) && (tree->opval.op == CALL) && tree->left && IS_AST_VALUE (tree->left) && tree->left->opval.val->sym) + { + int savedBlockno = currBlockno; + symbol *func = tree->left->opval.val->sym; + symbol *csym; + + currBlockno = tree->block; + + /* The symbol is probably not bound yet, so find the real one */ + csym = findSymWithLevel (SymbolTab, func); + if (csym) + func = csym; + + if ((inCriticalFunction || inCriticalBlock) && IFFUNC_ISCRITICAL (func->type)) + werrorfl (tree->left->filename, tree->left->lineno, E_INVALID_CRITICAL); + + /* Is this an inline function that we can inline? */ + if (IFFUNC_ISINLINE (func->type) && !IFFUNC_HASVARARGS(func->type) && func->funcTree) + { + symbol *retsym = NULL; + symbol *retlab; + ast *inlinetree; + ast *inlinetree2; + ast *temptree; + value *args; + int argIndex; + + /* Generate a label for the inlined function to branch to */ + /* in case it contains a return statement */ + retlab = newSymbol (genSymName (tree->level + LEVEL_UNIT), tree->level + LEVEL_UNIT); + retlab->isitmp = 1; + retlab->islbl = 1; + inlineState.retlab = retlab; + + /* Build the subtree for the inlined function in the form: */ + /* { //inlinetree block */ + /* { //inlinetree2 block */ + /* inline_function_code; */ + /* retlab: */ + /* } */ + /* } */ + temptree = newNode (LABEL, newAst_VALUE (symbolVal (retlab)), NULL); + copyAstLoc (temptree, tree); + temptree = newNode (NULLOP, copyAst (func->funcTree), temptree); + copyAstLoc (temptree, tree); + temptree = newNode (BLOCK, NULL, temptree); + copyAstLoc (temptree, tree); + inlinetree2 = temptree; + inlinetree2->level += 2 * LEVEL_UNIT; + inlinetree2->block = blockNo+2; + + /* Handle the return type */ + if (!IS_VOID (func->type->next)) + { + /* Create a temporary symbol to hold the return value and */ + /* join it with the inlined function using the comma */ + /* operator. The fixupInline function will take care of */ + /* changing return statements into assignments to retsym. */ + /* (parameter passing and return label omitted for clarity) */ + /* rettype retsym; */ + /* ... */ + /* {{inline_function_code}}, retsym */ + + retsym = inlineTempVar (func->type->next, block->level); + SPEC_SCLS (retsym->etype) = S_FIXED; + inlineAddDecl (retsym, block, TRUE, TRUE); + } + + inlinetree = newNode (BLOCK, NULL, inlinetree2); + copyAstLoc (inlinetree, tree); + inlinetree2->level += LEVEL_UNIT; + inlinetree2->block = blockNo+1; + + /* To pass parameters to the inlined function, we need some */ + /* intermediate variables. This avoids scoping problems */ + /* when the parameter declaration names are used differently */ + /* during the function call. For example, a function */ + /* declared as func(int x, int y) but called as func(y,x). */ + /* { //inlinetree block */ + /* type1 temparg1 = argument1; */ + /* ... */ + /* typen tempargn = argumentn; */ + /* { //inlinetree2 block */ + /* type1 param1 = temparg1; */ + /* ... */ + /* typen paramn = tempargn; */ + /* inline_function_code; */ + /* retlab: */ + /* } */ + /* } */ + args = FUNC_ARGS (func->type); + argIndex = 0; + + while (args) + { + symbol *temparg; + ast *assigntree; + symbol *parm; + ast *passedarg = inlineFindParm (tree->right, argIndex); + + if (!passedarg) + { + werror (E_TOO_FEW_PARMS); + break; + } + + temparg = inlineTempVar (args->sym->type, tree->level + LEVEL_UNIT); + inlineAddDecl (copySymbol (temparg), inlinetree, FALSE, FALSE); + + assigntree = newNode ('=', newAst_VALUE (symbolVal (temparg)), passedarg); + assigntree->initMode = 1; // tell that assignment is initializer + inlinetree->right = newNode (NULLOP, assigntree, inlinetree->right); + + parm = copySymbol (args->sym); + inlineAddDecl (parm, inlinetree2, FALSE, FALSE); + parm->_isparm = 0; + + assigntree = newNode ('=', newAst_VALUE (symbolVal (parm)), newAst_VALUE (symbolVal (temparg))); + assigntree->initMode = 1; // tell that assignment is initializer + inlinetree2->right = newNode (NULLOP, assigntree, inlinetree2->right); + parm->onStack = 0; // stack usage will be recomputed later + + args = args->next; + argIndex++; + } + + if (inlineFindParm (tree->right, argIndex) && !IFFUNC_HASVARARGS (func->type)) + werror (E_TOO_MANY_PARMS); + + /* Handle the return type */ + if (!IS_VOID (func->type->next)) + { + /* Generate return symbol coma statement; */ + /* ... */ + /* {{inline_function_code}}, retsym */ + + tree->opval.op = ','; + if (IFFUNC_ISCRITICAL (func->type)) + inlinetree = newNode (CRITICAL, inlinetree, NULL); + tree->left = inlinetree; + tree->right = newAst_VALUE (symbolVal (retsym)); + } + else + { + tree->opval.op = NULLOP; + if (IFFUNC_ISCRITICAL (func->type)) + inlinetree = newNode (CRITICAL, inlinetree, NULL); + tree->left = NULL; + tree->right = inlinetree; + } + inlineState.retsym = retsym; + + /* Renumber the various internal counters on the inlined */ + /* function's tree nodes and symbols. Add the inlined */ + /* function's local variables to the appropriate scope(s). */ + /* Convert inlined return statements to an assignment to */ + /* retsym (if needed) and a goto retlab. */ + fixupInline (inlinetree, inlinetree->level); + inlineState.count++; + } + currBlockno = savedBlockno; + } + + /* Recursively continue to search for functions to inline. */ + if (IS_AST_OP (tree)) + { + if (tree->opval.op == BLOCK) + { + struct symbol *decls; + + block = tree; + + for (decls = block->values.sym; decls; decls = decls->next) + { + if (decls->ival) + expandInlineFuncsInDeclarators (decls->ival, block); + } + } + + if (tree->opval.op == FOR) + { + if (AST_FOR (tree, initExpr)) + expandInlineFuncs (AST_FOR (tree, initExpr), block); + if (AST_FOR (tree, condExpr)) + expandInlineFuncs (AST_FOR (tree, condExpr), block); + if (AST_FOR (tree, loopExpr)) + expandInlineFuncs (AST_FOR (tree, loopExpr), block); + } + + if (tree->left) + expandInlineFuncs (tree->left, block); + if (tree->right) + expandInlineFuncs (tree->right, block); + } +} + +/*------------------------------------------------------------*/ +/* createFunctionDecl - Handle all of a function declaration */ +/* except for the function body. */ +/*------------------------------------------------------------*/ +symbol * +createFunctionDecl (symbol *name) +{ + symbol *csym; + value *args; + sym_link *type; + + /* This change would be done by addSymChain() below anyway. + But we need to do it here to avoid checkFunction() to report + a mismatch with an earlier declaration (that already underwent the change). + Fixed bug #2556. */ + changePointer (name->type); + + /* if check function return 0 then some problem */ + if (checkFunction (name, NULL) == 0) + return NULL; + + /* Find the arguments in this declaration, if any */ + type = name->type; + while (type && !IS_FUNC(type)) + type = type->next; + assert (type); + args = FUNC_ARGS (type); + + /* check if the function name already in the symbol table */ + if ((csym = findSym (SymbolTab, NULL, name->name))) + { + name = csym; + /* special case for compiler defined functions + we need to add the name to the publics list : this + actually means we are now compiling the compiler + support routine */ + if (name->cdef) + { + addSet (&publics, name); + } + } + else + { + addSymChain (&name); + allocVariables (name); + } + + /* Now that the function name is in the symbol table, */ + /* add the names of the arguments */ + while (args) + { + if (args->sym) + addSymChain (&args->sym); + args = args->next; + } + + return name; +} + +/*-----------------------------------------------------------------*/ +/* createFunction - This is the key node that calls the iCode for */ +/* generating the code for a function. Note code */ +/* is generated function by function, later when */ +/* add inter-procedural analysis this will change */ +/*-----------------------------------------------------------------*/ +ast * +createFunction (symbol * name, ast * body) +{ + ast *ex; + int stack = 0; + sym_link *fetype; + iCode *piCode = NULL; + + if (!name) + return NULL; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "SDCCast.c:createFunction(%s)\n", name->name); + + /* create a dummy block if none exists */ + if (!body) + { + body = newNode (BLOCK, NULL, NULL); + body->block = ++blockNo; + } + + noLineno++; + + name->lastLine = lexLineno; + currFunc = name; + + /* set the stack pointer */ + stackPtr = -port->stack.direction * port->stack.call_overhead; + xstackPtr = 0; + + if (IFFUNC_ISISR (name->type)) + stackPtr -= port->stack.direction * port->stack.isr_overhead; + + if (IFFUNC_ISREENT (name->type) || options.stackAuto) + { + if (options.useXstack) + xstackPtr -= port->stack.direction * port->stack.reent_overhead; + else + stackPtr -= port->stack.direction * port->stack.reent_overhead; + } + + if (IFFUNC_ISBANKEDCALL (name->type)) + stackPtr -= port->stack.direction * port->stack.banked_overhead; + + fetype = getSpec (name->type); /* get the specifier for the function */ + /* if this is a reentrant function then */ + if (IFFUNC_ISREENT (name->type)) + reentrant++; + + if (FUNC_ISINLINE (name->type) && FUNC_ISNAKED (name->type)) + werrorfl (name->fileDef, name->lineDef, W_INLINE_NAKED, name->name); + + inlineState.count = 0; + expandInlineFuncs (body, NULL); + + if (FUNC_ISINLINE (name->type)) + name->funcTree = copyAst (body); + + allocParms (FUNC_ARGS (name->type), IFFUNC_ISSMALLC (name->type)); /* allocate the parameters */ + + /* do processing for parameters that are passed in registers */ + processRegParms (FUNC_ARGS (name->type), body); + + /* set the stack pointer */ + stackPtr = 0; + xstackPtr = -1; + + gatherImplicitVariables (body, NULL); /* move implicit variables into blocks */ + + /* allocate & autoinit the block variables */ + processBlockVars (body, &stack, ALLOCATE); + + /* name needs to be mangled */ + SNPRINTF (name->rname, sizeof (name->rname), "%s%s", port->fun_prefix, name->name); + + body = resolveSymbols (body); /* resolve the symbols */ + body = decorateType (body, RESULT_TYPE_NONE); /* propagateType & do semantic checks */ + + /* save the stack information */ + if (options.useXstack) + name->xstack = SPEC_STAK (fetype) = stack; + else + name->stack = SPEC_STAK (fetype) = stack; + + ex = newAst_VALUE (symbolVal (name)); /* create name */ + ex = newNode (FUNCTION, ex, body); + ex->block = body->block; + ex->values.args = FUNC_ARGS (name->type); + ex->decorated = 1; + if (options.dump_ast) + PA (ex); + if (fatalError) + goto skipall; + + /* Do not generate code for inline functions unless extern also. */ +#if 0 + if (FUNC_ISINLINE (name->type) && !IS_EXTERN (fetype)) + goto skipall; +#else + /* Temporary hack: always generate code for static inline functions. */ + /* Ideally static inline functions should only be generated if needed. */ + if (FUNC_ISINLINE (name->type) && !IS_EXTERN (fetype) && !IS_STATIC (fetype)) + goto skipall; +#endif + + /* create the node & generate intermediate code */ + GcurMemmap = code; + codeOutBuf = &code->oBuf; + piCode = iCodeFromAst (ex); + name->generated = 1; + + if (fatalError) + goto skipall; + + eBBlockFromiCode (piCode); + + /* if there are any statics then do them */ + if (staticAutos) + { + GcurMemmap = statsg; + codeOutBuf = &statsg->oBuf; + eBBlockFromiCode (iCodeFromAst (decorateType (resolveSymbols (staticAutos), RESULT_TYPE_NONE))); + staticAutos = NULL; + } + +skipall: + /* dealloc the block variables */ + processBlockVars (body, &stack, DEALLOCATE); + if (!fatalError) + outputDebugStackSymbols (); + /* deallocate paramaters */ + deallocParms (FUNC_ARGS (name->type)); + + if (IFFUNC_ISREENT (name->type)) + reentrant--; + + /* we are done freeup memory & cleanup */ + noLineno--; + if (port->reset_labelKey) + labelKey = 1; + name->key = 0; + FUNC_HASBODY (name->type) = 1; + addSet (&operKeyReset, name); + applyToSet (operKeyReset, resetParmKey); + + if (options.debug) + cdbStructBlock (1); + + cleanUpLevel (LabelTab, 0); + cleanUpBlock (StructTab, 1); + cleanUpBlock (TypedefTab, 1); + cleanUpBlock (AddrspaceTab, 1); + + if (xstack) + xstack->syms = NULL; + istack->syms = NULL; + currFunc = NULL; + return NULL; +} + +#define INDENT(x,f) do { fprintf (f, "%s:%d:", tree->filename, tree->lineno); fprintf (f, "%*s", (x) & 0xff, ""); } while (0) +/*-----------------------------------------------------------------*/ +/* ast_print : prints the ast (for debugging purposes) */ +/*-----------------------------------------------------------------*/ + +void +ast_print (ast * tree, FILE * outfile, int indent) +{ + if (!tree) + return; + + /* can print only decorated trees */ + if (!tree->decorated) + { + fprintf (outfile, "tree (%p) not decorated\n", tree); + return; + } + + /* if any child is an error | this one is an error do nothing */ + if (tree->isError || (tree->left && tree->left->isError) || (tree->right && tree->right->isError)) + { + fprintf (outfile, "ERROR_NODE(%p)\n", tree); + } + + if (tree->opval.op == FUNCTION) + { + int arg = 0; + value *args; + + assert (tree->left != NULL); + assert (tree->left->opval.val != NULL); + + args = FUNC_ARGS (tree->left->opval.val->type); + fprintf (outfile, "FUNCTION (%s=%p) type (", tree->left->opval.val->name, tree); + printTypeChain (tree->left->opval.val->type->next, outfile); + fprintf (outfile, ") args ("); + do + { + if (arg) + { + fprintf (outfile, ", "); + } + printTypeChain (args ? args->type : NULL, outfile); + arg++; + args = args ? args->next : NULL; + } + while (args); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent); + ast_print (tree->right, outfile, indent); + return; + } + if (tree->opval.op == BLOCK) + { + symbol *decls = tree->values.sym; + INDENT (indent, outfile); + fprintf (outfile, "{ L%ld:%ld B%d\n", tree->level / LEVEL_UNIT, tree->level % LEVEL_UNIT, tree->block); + while (decls) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "DECLARE SYMBOL (L%ld:%ld B%d %s=%p) type (", + decls->level / LEVEL_UNIT, decls->level % LEVEL_UNIT, decls->block, decls->name, decls); + printTypeChain (decls->type, outfile); + fprintf (outfile, ")"); + if (decls->ival) + { + struct initList *ival = decls->ival; + fprintf (outfile, " = \n"); + for (; ival; ival = ival->next) + { + if (ival->type == INIT_NODE) + { + ast_print (ival->init.node, outfile, indent + 4); + } + else + { + INDENT (indent + 4, outfile); + fprintf (outfile, "HUGE initializer skipped\n"); + } + } + } + else + { + fprintf (outfile, "\n"); + } + + decls = decls->next; + } + ast_print (tree->right, outfile, indent + 2); + INDENT (indent, outfile); + fprintf (outfile, "}\n"); + return; + } + if (tree->opval.op == NULLOP) + { + ast_print (tree->left, outfile, indent); + ast_print (tree->right, outfile, indent); + return; + } + INDENT (indent, outfile); + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* leaf has been reached */ + /*----------------------------*/ + /* if this is of type value */ + /* just get the type */ + if (tree->type == EX_VALUE) + { + if (IS_LITERAL (tree->opval.val->etype)) + { + fprintf (outfile, "CONSTANT (%p) value = ", tree); + if (SPEC_LONGLONG (tree->opval.val->etype)) + { + unsigned long long ull = ullFromVal (tree->opval.val); + + if (SPEC_USIGN (tree->opval.val->etype)) + fprintf (outfile, "%llu, 0x%llx", ull, ull); + else + fprintf (outfile, "%lld, 0x%llx", (signed long long) ull, ull); + } + else + { + unsigned long ul = ulFromVal (tree->opval.val); + + if (SPEC_USIGN (tree->opval.val->etype)) + fprintf (outfile, "%lu", ul); + else + fprintf (outfile, "%ld", (signed long) ul); + fprintf (outfile, ", 0x%lx, %f", ul, floatFromVal (tree->opval.val)); + } + } + else if (tree->opval.val->sym) + { + /* if the undefined flag is set then give error message */ + if (tree->opval.val->sym->undefined) + { + fprintf (outfile, "UNDEFINED SYMBOL "); + } + else + { + fprintf (outfile, "SYMBOL "); + } + fprintf (outfile, "(L%ld:%ld B%d %s=%p @ %p)", + tree->opval.val->sym->level / LEVEL_UNIT, tree->opval.val->sym->level % LEVEL_UNIT, + tree->opval.val->sym->block, tree->opval.val->sym->name, tree, tree->opval.val->sym); + } + if (tree->ftype) + { + fprintf (outfile, " type ("); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + } + else + { + fprintf (outfile, "\n"); + } + return; + } + + /* if type link for the case of cast */ + if (tree->type == EX_LINK) + { + fprintf (outfile, "TYPENODE (%p) type = (", tree); + printTypeChain (tree->opval.lnk, outfile); + fprintf (outfile, ")\n"); + return; + } + + /* depending on type of operator do */ + switch (tree->opval.op) + { + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* array node */ + /*----------------------------*/ + case '[': + fprintf (outfile, "ARRAY_OP (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* struct/union */ + /*----------------------------*/ + case '.': + fprintf (outfile, "STRUCT_ACCESS (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* struct/union pointer */ + /*----------------------------*/ + case PTR_OP: + fprintf (outfile, "PTR_ACCESS (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* ++/-- operation */ + /*----------------------------*/ + case INC_OP: + if (tree->left) + fprintf (outfile, "post-"); + else + fprintf (outfile, "pre-"); + fprintf (outfile, "INC_OP (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); /* postincrement case */ + ast_print (tree->right, outfile, indent + 2); /* preincrement case */ + return; + + case DEC_OP: + if (tree->left) + fprintf (outfile, "post-"); + else + fprintf (outfile, "pre-"); + fprintf (outfile, "DEC_OP (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); /* postdecrement case */ + ast_print (tree->right, outfile, indent + 2); /* predecrement case */ + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* bitwise and */ + /*----------------------------*/ + case '&': + if (tree->right) + { + fprintf (outfile, "& (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + } + else + { + fprintf (outfile, "ADDRESS_OF (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + } + return; + + /*----------------------------*/ + /* bitwise or */ + /*----------------------------*/ + case '|': + fprintf (outfile, "OR (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* bitwise xor */ + /*----------------------------*/ + case '^': + fprintf (outfile, "XOR (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* division */ + /*----------------------------*/ + case '/': + fprintf (outfile, "DIV (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* modulus */ + /*----------------------------*/ + case '%': + fprintf (outfile, "MOD (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* address dereference */ + /*----------------------------*/ + case '*': /* can be unary : if right is null then unary operation */ + if (!tree->right) + { + fprintf (outfile, "DEREF (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + } + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* multiplication */ + /*----------------------------*/ + fprintf (outfile, "MULT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* unary '+' operator */ + /*----------------------------*/ + case '+': + /* if unary plus */ + if (!tree->right) + { + fprintf (outfile, "UPLUS (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + } + else + { + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* addition */ + /*----------------------------*/ + fprintf (outfile, "ADD (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + } + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* unary '-' */ + /*----------------------------*/ + case '-': /* can be unary */ + if (!tree->right) + { + fprintf (outfile, "UMINUS (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + } + else + { + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* subtraction */ + /*----------------------------*/ + fprintf (outfile, "SUB (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + } + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* complement */ + /*----------------------------*/ + case '~': + fprintf (outfile, "COMPL (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* not */ + /*----------------------------*/ + case '!': + fprintf (outfile, "NOT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* shift */ + /*----------------------------*/ + case RRC: + fprintf (outfile, "RRC (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + case RLC: + fprintf (outfile, "RLC (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + case SWAP: + fprintf (outfile, "SWAP (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + case GETHBIT: + fprintf (outfile, "GETHBIT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + return; + + case GETABIT: + fprintf (outfile, "GETABIT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case GETBYTE: + fprintf (outfile, "GETBYTE (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case GETWORD: + fprintf (outfile, "GETWORD (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case LEFT_OP: + fprintf (outfile, "LEFT_SHIFT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case RIGHT_OP: + fprintf (outfile, "RIGHT_SHIFT (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* casting */ + /*----------------------------*/ + case CAST: /* change the type */ + fprintf (outfile, "CAST (%p) from type (", tree); + assert (tree->right != NULL); + printTypeChain (tree->right->ftype, outfile); + fprintf (outfile, ") to type ("); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->right, outfile, indent + 2); + return; + + case AND_OP: + fprintf (outfile, "ANDAND (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case OR_OP: + fprintf (outfile, "OROR (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* comparison operators */ + /*----------------------------*/ + case '>': + fprintf (outfile, "GT(>) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case '<': + fprintf (outfile, "LT(<) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case LE_OP: + fprintf (outfile, "LE(<=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case GE_OP: + fprintf (outfile, "GE(>=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case EQ_OP: + fprintf (outfile, "EQ(==) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case NE_OP: + fprintf (outfile, "NE(!=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* sizeof */ + /*----------------------------*/ + case SIZEOF: /* evaluate wihout code generation */ + fprintf (outfile, "SIZEOF %d\n", (getSize (tree->right->ftype))); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* conditional operator '?' */ + /*----------------------------*/ + case '?': + fprintf (outfile, "QUEST(?) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case ':': + fprintf (outfile, "COLON(:) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* assignment operators */ + /*----------------------------*/ + case MUL_ASSIGN: + fprintf (outfile, "MULASS(*=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case DIV_ASSIGN: + fprintf (outfile, "DIVASS(/=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case AND_ASSIGN: + fprintf (outfile, "ANDASS(&=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case OR_ASSIGN: + fprintf (outfile, "ORASS(|=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case XOR_ASSIGN: + fprintf (outfile, "XORASS(^=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case RIGHT_ASSIGN: + fprintf (outfile, "RSHFTASS(>>=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case LEFT_ASSIGN: + fprintf (outfile, "LSHFTASS(<<=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* -= operator */ + /*----------------------------*/ + case SUB_ASSIGN: + fprintf (outfile, "SUBASS(-=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* += operator */ + /*----------------------------*/ + case ADD_ASSIGN: + fprintf (outfile, "ADDASS(+=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* straight assignemnt */ + /*----------------------------*/ + case '=': + fprintf (outfile, "ASSIGN(=) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* comma operator */ + /*----------------------------*/ + case ',': + fprintf (outfile, "COMMA(,) (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* function call */ + /*----------------------------*/ + case CALL: + case PCALL: + fprintf (outfile, "CALL (%p) type (", tree); + printTypeChain (tree->ftype, outfile); + fprintf (outfile, ")\n"); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + case PARAM: + fprintf (outfile, "PARMS\n"); + ast_print (tree->left, outfile, indent + 2); + if (tree->right /*&& !IS_AST_PARAM(tree->right) */ ) + { + ast_print (tree->right, outfile, indent + 2); + } + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* return statement */ + /*----------------------------*/ + case RETURN: + fprintf (outfile, "RETURN (%p) type (", tree); + if (tree->right) + { + printTypeChain (tree->right->ftype, outfile); + } + fprintf (outfile, ")\n"); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* label statement */ + /*----------------------------*/ + case LABEL: + fprintf (outfile, "LABEL (%p)\n", tree); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* switch statement */ + /*----------------------------*/ + case SWITCH: + { + value *val; + fprintf (outfile, "SWITCH (%p) ", tree); + ast_print (tree->left, outfile, 0); + for (val = tree->values.switchVals.swVals; val; val = val->next) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "CASE 0x%x GOTO _case_%d_%d%s\n", + (int) ulFromVal (val), + tree->values.switchVals.swNum, + (int) ulFromVal (val), tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + } + ast_print (tree->right, outfile, indent); + } + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* ifx Statement */ + /*----------------------------*/ + case IFX: + fprintf (outfile, "IF (%p) \n", tree); + ast_print (tree->left, outfile, indent + 2); + if (tree->trueLabel) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "NE(!=) 0 goto %s\n", tree->trueLabel->name); + } + if (tree->falseLabel) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "EQ(==) 0 goto %s\n", tree->falseLabel->name); + } + ast_print (tree->right, outfile, indent + 2); + return; + + /*----------------------------*/ + /* goto Statement */ + /*----------------------------*/ + case GOTO: + fprintf (outfile, "GOTO (%p) \n", tree); + ast_print (tree->left, outfile, indent + 2); + ast_print (tree->right, outfile, indent + 2); + return; + + /*------------------------------------------------------------------*/ + /*----------------------------*/ + /* for Statement */ + /*----------------------------*/ + case FOR: + fprintf (outfile, "FOR (%p) \n", tree); + if (AST_FOR (tree, initExpr)) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "INIT EXPR "); + ast_print (AST_FOR (tree, initExpr), outfile, indent + 2); + } + if (AST_FOR (tree, condExpr)) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "COND EXPR "); + ast_print (AST_FOR (tree, condExpr), outfile, indent + 2); + } + if (AST_FOR (tree, loopExpr)) + { + INDENT (indent + 2, outfile); + fprintf (outfile, "LOOP EXPR "); + ast_print (AST_FOR (tree, loopExpr), outfile, indent + 2); + } + fprintf (outfile, "FOR LOOP BODY \n"); + ast_print (tree->left, outfile, indent + 2); + return; + + case CRITICAL: + fprintf (outfile, "CRITICAL (%p) \n", tree); + ast_print (tree->left, outfile, indent + 2); + default: + return; + } +} + +void +PA (ast * t) +{ + ast_print (t, stdout, 0); +} + +/*-----------------------------------------------------------------*/ +/* astErrors : returns non-zero if errors present in tree */ +/*-----------------------------------------------------------------*/ +int +astErrors (ast * t) +{ + int errors = 0; + + if (t) + { + if (t->isError) + errors++; + + if (t->type == EX_VALUE && t->opval.val->sym && t->opval.val->sym->undefined) + errors++; + + errors += astErrors (t->left); + errors += astErrors (t->right); + } + + return errors; +} + +/*----------------------------------------------------------------------------- + * verbatim copy from the gnu gcc-4.1 info page + * > + * > info node: (gcc-4.1)Offsetof + * > + * > primary: + * > "__builtin_offsetof" "(" `typename' "," offsetof_member_designator ")" + * > + * > offsetof_member_designator: + * > `identifier' + * > | offsetof_member_designator "." `identifier' + * > | offsetof_member_designator "[" `expr' "]" + * > + * > This extension is sufficient such that + * > + * > #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) + * > ... + * + * Note: + * `expr', here is indeed any expr valid, not only constants. + * `typename', gcc supports typeof(...) here, but the sdcc typeof has + * a completely different semantic, more sort of typeid + * + */ + +static ast * +offsetofOp_rec (sym_link * type, ast * snd, sym_link ** result_type) +{ + /* make sure the type is complete and sane */ + checkTypeSanity (type, "(offsetofOp)"); + + /* offsetof can only be applied to structs/unions */ + if (!IS_STRUCT (type)) + { + werrorfl (snd->filename, snd->lineno, E_OFFSETOF_TYPE); + *result_type = NULL; + return NULL; + } + + /* offsetof(struct_type, symbol); */ + if (IS_AST_SYM_VALUE (snd)) + { + structdef *structdef = SPEC_STRUCT (type); + symbol *element = getStructElement (structdef, AST_SYMBOL (snd)); + *result_type = element->type; + return newAst_VALUE (valueFromLit (element->offset)); + } + + /* offsetof(struct_type, a.something); */ + if (IS_AST_OP (snd) && snd->opval.op == '.') + { + sym_link *tmp; + ast *o = offsetofOp_rec (type, snd->left, &tmp); + return newNode ('+', o, offsetofOp_rec (tmp, snd->right, result_type)); + } + + /* offsetof(struct_type, a[expr]); */ + if (IS_ARRAY_OP (snd)) + { + sym_link *tmp; + ast *o = offsetofOp_rec (type, snd->left, &tmp); + *result_type = tmp->next; + return newNode ('+', o, newNode ('*', newAst_VALUE (valueFromLit (getSize (tmp->next))), snd->right)); + } + + wassertl (0, "this should never have happened"); + exit (1); +} + +ast * +offsetofOp (sym_link * type, ast * snd) +{ + sym_link *result_type; + return offsetofOp_rec (type, snd, &result_type); +} diff --git a/src/SDCCast.h b/src/SDCCast.h new file mode 100644 index 0000000..e310846 --- /dev/null +++ b/src/SDCCast.h @@ -0,0 +1,241 @@ +/*------------------------------------------------------------------------- + SDCCast.h - header file for parser support & all ast related routines + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCAST_H +#define SDCCAST_H + +#include "SDCCglobl.h" +#include "SDCCsymt.h" +#include "SDCCval.h" +#include "SDCCset.h" +#include "SDCCmem.h" + +typedef enum +{ + EX_OP = 0, + EX_VALUE, + EX_LINK, + EX_OPERAND +} ASTTYPE; + +/* expression tree */ +typedef struct ast +{ + ASTTYPE type; + unsigned decorated:1; + unsigned isError:1; + unsigned funcName:1; + unsigned rvalue:1; + unsigned lvalue:1; + unsigned initMode:1; + unsigned reversed:1; + long level; /* level for expr */ + int block; /* block number */ + int seqPoint; /* sequence point */ + /* union of values expression can have */ + union + { + value *val; /* value if type = EX_VALUE */ + sym_link *lnk; /* sym_link * if type= EX_LINK */ + struct operand *oprnd; /* used only for side effecting function calls */ + unsigned op; /* operator if type= EX_OP */ + } + opval; + + /* union for special processing */ + union + { + char *inlineasm; /* pointer to inline assembler code */ + literalList *constlist; /* init list for array initializer. */ + symbol *sym; /* if block then -> symbols */ + value *args; /* if function then args */ + /* if switch then switch values */ + struct + { + value *swVals; /* switch comparison values */ + int swDefault; /* default if present */ + int swNum; /* switch number */ + char *swSuffix; + } + switchVals; + /* if for then for values */ + struct + { + struct ast *initExpr; /* init portion */ + struct ast *condExpr; /* conditional portion */ + struct ast *loopExpr; /* iteration portion */ + symbol *trueLabel; /* entry point into body */ + symbol *falseLabel; /* exit point */ + symbol *continueLabel; /* conditional check */ + symbol *condLabel; /* conditional label */ + } + forVals; + struct + { + unsigned literalFromCast:1; /* true if this is an EX_VALUE of LITERAL + * type resulting from a typecast. + */ + unsigned removedCast:1; /* true if the explicit cast has been removed */ + unsigned implicitCast:1; /* true if compiler added this cast */ + } cast; + int argreg; /* argreg number when operand type == EX_OPERAND */ + } + values; + + int lineno; /* source file line number */ + char *filename; /* filename of the source file */ + + sym_link *ftype; /* start of type chain for this subtree */ + sym_link *etype; /* end of type chain for this subtree */ + + struct ast *left; /* pointer to left tree */ + struct ast *right; /* pointer to right tree */ + symbol *trueLabel; /* if statement trueLabel */ + symbol *falseLabel; /* if statement falseLabel */ +} +ast; + + +/* easy access macros */ +#define IS_AST_OP(x) ((x) && (x)->type == EX_OP) +#define IS_CALLOP(x) (IS_AST_OP(x) && (x)->opval.op == CALL) +#define IS_BITOR(x) (IS_AST_OP(x) && (x)->opval.op == '|') +#define IS_BITAND(x) (IS_AST_OP(x) && (x)->opval.op == '&' && \ + (x)->left && (x)->right ) +#define IS_FOR_STMT(x) (IS_AST_OP(x) && (x)->opval.op == FOR) +#define IS_LEFT_OP(x) (IS_AST_OP(x) && (x)->opval.op == LEFT_OP) +#define IS_RIGHT_OP(x) (IS_AST_OP(x) && (x)->opval.op == RIGHT_OP) +#define IS_AST_VALUE(x) ((x) && (x)->type == EX_VALUE && (x)->opval.val) +#define IS_AST_LINK(x) ((x)->type == EX_LINK) +#define IS_AST_NOT_OPER(x) (x && IS_AST_OP(x) && (x)->opval.op == '!') +#define IS_ARRAY_OP(x) (IS_AST_OP(x) && (x)->opval.op == '[') +#define IS_COMPARE_OP(x) (IS_AST_OP(x) && \ + ((x)->opval.op == '>' || \ + (x)->opval.op == '<' || \ + (x)->opval.op == LE_OP || \ + (x)->opval.op == GE_OP || \ + (x)->opval.op == EQ_OP || \ + (x)->opval.op == NE_OP )) +#define IS_CAST_OP(x) (IS_AST_OP(x) && (x)->opval.op == CAST) +#define IS_TERNARY_OP(x) (IS_AST_OP(x) && (x)->opval.op == '?') +#define IS_COLON_OP(x) (IS_AST_OP(x) && (x)->opval.op == ':') +#define IS_ADDRESS_OF_OP(x) (IS_AST_OP(x) && \ + (x)->opval.op == '&' && \ + (x)->right == NULL ) +#define IS_AST_LIT_VALUE(x) (IS_AST_VALUE(x) && \ + IS_LITERAL((x)->opval.val->etype)) +#define IS_AST_SYM_VALUE(x) (IS_AST_VALUE(x) && (x)->opval.val->sym) +#define AST_FLOAT_VALUE(x) (floatFromVal((x)->opval.val)) +#define AST_ULONG_VALUE(x) (ulFromVal((x)->opval.val)) +#define AST_SYMBOL(x) ((x)->opval.val->sym) +#define AST_VALUE(x) ((x)->opval.val) +#define AST_VALUES(x,y) ((x)->values.y) +#define AST_FOR(x,y) ((x)->values.forVals.y) +#define AST_ARGREG(x) ((x)->values.argreg) + +#define IS_AST_PARAM(x) (IS_AST_OP(x) && (x)->opval.op == PARAM) + +#define CAN_EVAL(x) ( (x) == '[' || (x) == '.' || (x) == PTR_OP || \ + (x) == '&' || (x) == '|' || (x) == '^' || (x) == '*' || \ + (x) == '-' || (x) == '+' || (x) == '~' || \ + (x) == '!' || (x) == LEFT_OP || (x) == RIGHT_OP || \ + (x) == '/' || (x) == '%' || (x) == '>' || (x) == '<' || \ + (x) == LE_OP || (x) == GE_OP || (x) == EQ_OP || (x) == NE_OP || \ + (x) == AND_OP || (x) == OR_OP || (x) == '=' ) + +#define LEFT_FIRST(x) ( x == AND_OP || x == OR_OP ) + +#define SIDE_EFFECTS_CHECK(op,rVal) if (!sideEffects) { \ + werror(W_NO_SIDE_EFFECTS,op); \ + return rVal ; \ + } +#define IS_MODIFYING_OP(x) ( (x) == INC_OP || (x) == DEC_OP || (x) == '=' || \ + (x) == AND_ASSIGN || (x) == OR_ASSIGN || (x) == XOR_ASSIGN ) + +#define IS_ASSIGN_OP(x) ( (x) == '=' || (x) == ADD_ASSIGN || (x) == SUB_ASSIGN || \ + (x) == MUL_ASSIGN || (x) == DIV_ASSIGN || (x) == XOR_ASSIGN || \ + (x) == AND_ASSIGN || (x) == OR_ASSIGN || (x) == INC_OP || (x) == DEC_OP) +#define IS_DEREF_OP(x) ( ( (x)->opval.op == '*' && (x)->right == NULL) || \ + (x)->opval.op == '.' || \ + (x)->opval.op == PTR_OP ) + +/* forward declarations for global variables */ +extern ast *staticAutos; +extern struct dbuf_s *codeOutBuf; +extern struct memmap *GcurMemmap; + +/* forward definitions for functions */ +ast *newAst_VALUE (value * val); +ast *newAst_OP (unsigned op); +ast *newAst_LINK (sym_link * val); + +void initAst (); +ast *newNode (long, ast *, ast *); +ast *copyAst (ast *); +ast *removeIncDecOps (ast *); +ast *removePreIncDecOps (ast *); +ast *removePostIncDecOps (ast *); +value *sizeofOp (sym_link *); +value *alignofOp (sym_link *); +ast *offsetofOp (sym_link * type, ast * snd); +value *evalStmnt (ast *); +ast *createRMW (ast *, unsigned, ast *); +symbol * createFunctionDecl (symbol *); +ast *createFunction (symbol *, ast *); +ast *createBlock (symbol *, ast *); +ast *createLabel (symbol *, ast *); +ast *createCase (ast *, ast *, ast *); +ast *createDefault (ast *, ast *, ast *); +ast *forLoopOptForm (ast *); +ast *argAst (ast *); +ast *resolveSymbols (ast *); +void CodePtrPointsToConst (sym_link * t); +void checkPtrCast (sym_link * newType, sym_link * orgType, bool implicit, bool orgIsNullPtrConstant); +ast *decorateType (ast *, RESULT_TYPE); +ast *createWhile (symbol *, symbol *, symbol *, ast *, ast *); +ast *createIf (ast *, ast *, ast *); +ast *createDo (symbol *, symbol *, symbol *, ast *, ast *); +ast *createFor (symbol *, symbol *, symbol *, symbol *, ast *, ast *, ast *, ast *, ast *); +void eval2icode (ast *); +value *constExprValue (ast *, int); +bool constExprTree (ast *); +int setAstFileLine (ast *, char *, int); +symbol *funcOfType (const char *, sym_link *, sym_link *, int, int); +symbol *funcOfTypeVarg (const char *, const char *, int, const char **); +ast *initAggregates (symbol *, initList *, ast *); +bool astHasVolatile (ast *tree); +bool hasSEFcalls (ast *); +void addSymToBlock (symbol *, ast *); +void freeStringSymbol (symbol *); +value *stringToSymbol (value *val); +DEFSETFUNC (resetParmKey); +int astErrors (ast *); +RESULT_TYPE getResultTypeFromType (sym_link *); + +// exported variables +extern set *operKeyReset; +extern int noAlloc; +extern int inInitMode; + +#endif diff --git a/src/SDCCbitv.c b/src/SDCCbitv.c new file mode 100644 index 0000000..7b2f8a6 --- /dev/null +++ b/src/SDCCbitv.c @@ -0,0 +1,541 @@ +/*----------------------------------------------------------------- + SDCCbitv.c - contains support routines for bitvectors + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +#include "newalloc.h" + +int bitVectDefault = 1024; + +#define BYTE_SIZEOF_ELEMENT (sizeof(unsigned int)) +#define BIT_SIZEOF_ELEMENT (BYTE_SIZEOF_ELEMENT*8) + +/* genernal note about a bitvectors: + bit positions must start from 0 */ +/*-----------------------------------------------------------------*/ +/* newBitVect - returns a new bitvector of size */ +/*-----------------------------------------------------------------*/ +bitVect * +newBitVect (int size) +{ + bitVect *bvp; + + bvp = Safe_calloc (1, sizeof (bitVect)); + + bvp->size = size; + bvp->allocSize = (size+BIT_SIZEOF_ELEMENT-1) / BIT_SIZEOF_ELEMENT; + bvp->vect = Safe_calloc (BYTE_SIZEOF_ELEMENT, bvp->allocSize); + return bvp; +} + + +/*-----------------------------------------------------------------*/ +/* freeBitVect - frees the memory used by the bitVector */ +/*-----------------------------------------------------------------*/ +void +freeBitVect (bitVect * bvp) +{ + if (!bvp) + return; + + Safe_free (bvp->vect); + Safe_free (bvp); +} + +/*-----------------------------------------------------------------*/ +/* bitVectResize - changes the size of a bit vector */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectResize (bitVect * bvp, int size) +{ + int allocSize; + + if (!bvp) + return newBitVect (size); + + allocSize = (size+BIT_SIZEOF_ELEMENT-1) / BIT_SIZEOF_ELEMENT; + + /* if we already have enough space */ + if (bvp->allocSize >= allocSize) + { + if (size > bvp->size) + bvp->size = size; + return bvp; + } + + bvp->vect = Clear_realloc (bvp->vect, bvp->allocSize*BYTE_SIZEOF_ELEMENT, allocSize*BYTE_SIZEOF_ELEMENT); + bvp->size = size; + bvp->allocSize = allocSize; + + return bvp; +} + +/*-----------------------------------------------------------------*/ +/* bitVectSetBit - sets a given bit in the bit vector */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectSetBit (bitVect * bvp, int pos) +{ + unsigned int index; + unsigned int bitofs; + + assert (pos>=0); + /* if set is null then allocate it */ + if (!bvp) + bvp = newBitVect (bitVectDefault); /* allocate for twice the size */ + + if (bvp->size <= pos) + bvp = bitVectResize (bvp, pos + 2); /* conservatively resize */ + + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; + bvp->vect[index] |= 1u << bitofs; + return bvp; +} + +/*-----------------------------------------------------------------*/ +/* bitVectUnSetBit - unsets the value of a bit in a bitvector */ +/*-----------------------------------------------------------------*/ +void +bitVectUnSetBit (const bitVect *bvp, int pos) +{ + unsigned int index; + unsigned int bitofs; + + assert (pos>=0); + if (!bvp) + return; + + if (bvp->size <= pos) + return; + + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; + bvp->vect[index] &= ~(1u << bitofs); +} + +/*-----------------------------------------------------------------*/ +/* bitVectBitValue - returns value value at bit position */ +/*-----------------------------------------------------------------*/ +int +bitVectBitValue (const bitVect *bvp, int pos) +{ + unsigned int index; + unsigned int bitofs; + + assert (pos>=0); + if (!bvp) + return 0; + + index = pos / BIT_SIZEOF_ELEMENT; + bitofs = pos % BIT_SIZEOF_ELEMENT; + + if (bvp->size <= pos) + return 0; + + + return (bvp->vect[index] >> bitofs) & 1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectUnion - unions two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectUnion (bitVect * bvp1, bitVect * bvp2) +{ + bitVect *newBvp; + unsigned int *pn, *p1, *p2; + int elements; + + /* if both null */ + if (!bvp1 && !bvp2) + return NULL; + + /* if one of them null then return the other */ + if (!bvp1 && bvp2) + return bitVectCopy (bvp2); + + if (bvp1 && !bvp2) + return bitVectCopy (bvp1); + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize (bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize (bvp2, bvp1->size); + + newBvp = newBitVect (bvp1->size); + elements = bvp1->allocSize; + + pn = newBvp->vect; + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + *pn++ = *p1++ | *p2++; + } + + return newBvp; +} + +/*-----------------------------------------------------------------*/ +/* bitVectInplaceUnion - unions two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectInplaceUnion (bitVect * bvp1, bitVect * bvp2) +{ + unsigned int *p1, *p2; + int elements; + + /* if both null */ + if (!bvp1 && !bvp2) + return NULL; + + /* if one of them null then return the other */ + if (!bvp1 && bvp2) + return bitVectCopy (bvp2); + + if (bvp1 && !bvp2) + return bvp1; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize (bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize (bvp2, bvp1->size); + + elements = bvp1->allocSize; + + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + *p1 |= *p2; + p1++; p2++; + } + + return bvp1; +} + + +/*-----------------------------------------------------------------*/ +/* bitVectIntersect - intersect two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectIntersect (bitVect * bvp1, bitVect * bvp2) +{ + bitVect *newBvp; + unsigned int *pn, *p1, *p2; + int elements; + + if (!bvp2 || !bvp1) + return NULL; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize (bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize (bvp2, bvp1->size); + + newBvp = newBitVect (bvp1->size); + elements = bvp1->allocSize; + + pn = newBvp->vect; + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + *pn++ = *p1++ & *p2++; + } + + return newBvp; +} + +/*-----------------------------------------------------------------*/ +/* bitVectInplaceIntersect - intersect two bitvectors */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectInplaceIntersect (bitVect * bvp1, bitVect * bvp2) +{ + unsigned int *p1, *p2; + int elements; + + if (!bvp2 || !bvp1) + return NULL; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize (bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize (bvp2, bvp1->size); + + elements = bvp1->allocSize; + + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + *p1 &= *p2; + p1++; p2++; + } + + return bvp1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectBitsInCommon - special case of intersection determines */ +/* if the vectors have any common bits set */ +/*-----------------------------------------------------------------*/ +int +bitVectBitsInCommon (const bitVect * bvp1, const bitVect * bvp2) +{ + int elements; + unsigned int *p1, *p2; + + if (!bvp1 || !bvp2) + return 0; + + elements = min (bvp1->allocSize, bvp2->allocSize); + + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + if (*p1 & *p2) + return 1; + p1++; p2++; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* bitVectCplAnd - complement the second & and it with the first */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectCplAnd (bitVect * bvp1, bitVect * bvp2) +{ + unsigned int *p1, *p2; + int elements; + + if (!bvp2) + return bvp1; + + if (!bvp1) + return bvp1; + + /* if they are not the same size */ + /* make them the same size */ + if (bvp1->size < bvp2->size) + bvp1 = bitVectResize (bvp1, bvp2->size); + else if (bvp2->size < bvp1->size) + bvp2 = bitVectResize (bvp2, bvp1->size); + + elements = bvp1->allocSize; + p1 = bvp1->vect; + p2 = bvp2->vect; + + while (elements--) + { + *p1 = *p1 & ~ *p2; + p2++; p1++; + } + + return bvp1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectIsZero - bit vector has all bits turned off */ +/*-----------------------------------------------------------------*/ +int +bitVectIsZero (const bitVect * bvp) +{ + int i; + + if (!bvp) + return 1; + + for (i = 0; i < bvp->allocSize; i++) + if (bvp->vect[i] != 0) + return 0; + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectsEqual - returns 1 if the two bit vectors are equal */ +/*-----------------------------------------------------------------*/ +int +bitVectEqual (bitVect * bvp1, bitVect * bvp2) +{ + int i; + int elements; + + if (!bvp1 || !bvp2) + return 0; + + if (bvp1 == bvp2) + return 1; + + /* elements common to both allocations must match */ + elements = min (bvp1->allocSize, bvp2->allocSize); + for (i = 0; i < elements; i++) + if (bvp1->vect[i] != bvp2->vect[i]) + return 0; + + /* any extra elements allocated must be 0 */ + if (bvp1->allocSize > elements) + { + for (i = elements; i < bvp1->allocSize; i++) + if (bvp1->vect[i]) + return 0; + } + if (bvp2->allocSize > elements) + { + for (i = elements; i < bvp2->allocSize; i++) + if (bvp2->vect[i]) + return 0; + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectCopy - creates a bitvector from another bit Vector */ +/*-----------------------------------------------------------------*/ +bitVect * +bitVectCopy (const bitVect * bvp) +{ + bitVect *newBvp; + int i; + + if (!bvp) + return NULL; + + newBvp = newBitVect (bvp->size); + for (i = 0; i < bvp->allocSize; i++) + newBvp->vect[i] = bvp->vect[i]; + + return newBvp; +} + +/*-----------------------------------------------------------------*/ +/* bitVectnBitsOn - returns the number of bits that are on */ +/*-----------------------------------------------------------------*/ +int +bitVectnBitsOn (const bitVect * bvp) +{ + int count = 0; + unsigned int *p1; + int elements; + + if (!bvp) + return 0; + + p1 = bvp->vect; + elements = bvp->allocSize; + + while (elements--) + { + unsigned int word = *p1++; + while (word) + { + count++; + word &= word-1; + } + } + + return count; +} + +/*-----------------------------------------------------------------*/ +/* bitVectFirstBit - returns the key for the first bit that is on */ +/*-----------------------------------------------------------------*/ +int +bitVectFirstBit (const bitVect * bvp) +{ + int i; + int index; + + if (!bvp) + return -1; + + for (i = 0, index = 0; i < bvp->size; i+=BIT_SIZEOF_ELEMENT, index++) + if (bvp->vect[index]) + break; + + for (; i < bvp->size; i++) + if (bitVectBitValue (bvp, i)) + return i; + + return -1; +} + +/*-----------------------------------------------------------------*/ +/* bitVectClear - clear all bits */ +/*-----------------------------------------------------------------*/ +void +bitVectClear (bitVect *bvp) +{ + int i; + + if (!bvp) + return; + + for (i = 0; i < bvp->allocSize; i++) + bvp->vect[i] = 0; +} + +/*-----------------------------------------------------------------*/ +/* bitVectDebugOn - prints bits that are on */ +/*-----------------------------------------------------------------*/ +void +bitVectDebugOn (bitVect * bvp, FILE * of) +{ + int i; + + if (of == NULL) + of = stdout; + if (!bvp) + return; + + fprintf (of, "bitvector Size = %d allocSize = %d\n", bvp->size, bvp->allocSize); + fprintf (of, "Bits on { "); + for (i = 0; i < bvp->size; i++) + { + if (bitVectBitValue (bvp, i)) + fprintf (of, "(%d) ", i); + } + fprintf (of, "}\n"); +} + diff --git a/src/SDCCbitv.h b/src/SDCCbitv.h new file mode 100644 index 0000000..1b3c4a1 --- /dev/null +++ b/src/SDCCbitv.h @@ -0,0 +1,66 @@ +/*----------------------------------------------------------------- + SDCCbitv.h - contains support routines for bitVectors + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +/* #include "SDCCglobl.h" */ +/* #include "SDCCsymt.h" */ + +#ifndef SDCCBITV_H +#define SDCCBITV_H + +/* bitvector */ +typedef struct bitVect + { + int size; // number of bits + int allocSize; // number of int elements + unsigned int *vect; + } +bitVect; + + +extern int bitVectDefault; +/*-----------------------------------------------------------------*/ +/* Forward definition for functions */ +/*-----------------------------------------------------------------*/ +/* bitvector related functions */ +bitVect *newBitVect (int); +void freeBitVect (bitVect *); +bitVect *bitVectResize (bitVect *, int); +bitVect *bitVectSetBit (bitVect *, int); +void bitVectUnSetBit (const bitVect *, int); +int bitVectBitValue (const bitVect *, int); +bitVect *bitVectUnion (bitVect *, bitVect *); +bitVect *bitVectInplaceUnion (bitVect *, bitVect *); +bitVect *bitVectIntersect (bitVect *, bitVect *); +bitVect *bitVectInplaceIntersect (bitVect *, bitVect *); +int bitVectBitsInCommon (const bitVect *, const bitVect *); +bitVect *bitVectCplAnd (bitVect *, bitVect *); +int bitVectEqual (bitVect *, bitVect *); +bitVect *bitVectCopy (const bitVect *); +int bitVectIsZero (const bitVect *); +int bitVectnBitsOn (const bitVect *); +int bitVectFirstBit (const bitVect *); +void bitVectClear (bitVect *bvp); +void bitVectDebugOn (bitVect *, FILE *); +#endif + diff --git a/src/SDCCbtree.cc b/src/SDCCbtree.cc new file mode 100644 index 0000000..8266cfb --- /dev/null +++ b/src/SDCCbtree.cc @@ -0,0 +1,172 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011 +// +// (c) 2011 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include +#include +#include + +#include + +#include "common.h" + +extern "C" +{ +#include "SDCCbtree.h" +} + +#undef BTREE_DEBUG + +typedef boost::adjacency_list, int> > btree_t; + +typedef std::map bmap_t; +typedef std::map bmaprev_t; + +static btree_t btree; +static bmap_t bmap; +static bmaprev_t bmaprev; + +void btree_clear_subtree(btree_t::vertex_descriptor v) +{ + btree[v].first.clear(); + + boost::graph_traits::out_edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::out_edges(v, btree); e != e_end; ++e) + btree_clear_subtree(boost::target(*e, btree)); +} + +void btree_clear(void) +{ +#ifdef BTREE_DEBUG + std::cout << "Clearing.\n"; std::cout.flush(); +#endif + btree_clear_subtree(0); +} + +void btree_add_child(short parent, short child) +{ +#ifdef BTREE_DEBUG + std::cout << "Adding child " << child << " at parent " << parent << "\n"; std::cout.flush(); +#endif + + if(!boost::num_vertices(btree)) + { + boost::add_vertex(btree); + bmap[0] = 0; + bmaprev[0] = 0; + } + + wassert(parent != child); + wassert(bmap.find(parent) != bmap.end()); + + btree_t::vertex_descriptor c = boost::add_vertex(btree); + bmap[child] = c; + bmaprev[c] = child; + + wassert(bmap[parent] != c); + + boost::add_edge(bmap[parent], c, btree); +} + +static btree_t::vertex_descriptor btree_lowest_common_ancestor_impl(btree_t::vertex_descriptor a, btree_t::vertex_descriptor b) +{ + if(a == b) + return(a); + else if (a > b) + a = boost::source(*boost::in_edges(a, btree).first, btree); + else // (a < b) + b = boost::source(*boost::in_edges(b, btree).first, btree); + + return(btree_lowest_common_ancestor(a, b)); +} + +short btree_lowest_common_ancestor(short a, short b) +{ + return(bmaprev[btree_lowest_common_ancestor_impl(bmap[a], bmap[b])]); +} + +void btree_add_symbol(struct symbol *s) +{ + int block; + wassert(s); + block = s->_isparm ? 0 : s->block; // This is essentially a workaround. TODO: Ensure that the parameter block is placed correctly in the btree instead! + +#ifdef BTREE_DEBUG + std::cout << "Adding symbol " << s->name << " at " << block << "\n"; +#endif + + wassert(bmap.find(block) != bmap.end()); + wassert(bmap[block] < boost::num_vertices(btree)); + btree[bmap[block]].first.insert(s); +} + +static void btree_alloc_subtree(btree_t::vertex_descriptor v, int sPtr, int cssize, int *ssize) +{ + std::set::iterator s, s_end; + wassert(v < boost::num_vertices(btree)); + for(s = btree[v].first.begin(), s_end = btree[v].first.end(); s != s_end; ++s) + { + struct symbol *const sym = *s; + const int size = getSize (sym->type); + +#ifdef BTREE_DEBUG + std::cout << "Allocating symbol " << sym->name << " (" << v << ") of size " << size << " to " << sPtr << "\n"; +#endif + + if(port->stack.direction > 0) + { + SPEC_STAK (sym->etype) = sym->stack = (sPtr + !TARGET_PDK_LIKE); + sPtr += size; + } + else + { + sPtr -= size; + SPEC_STAK (sym->etype) = sym->stack = sPtr; + } + + cssize += size; + } + btree[v].second = cssize; + if(cssize > *ssize) + *ssize = cssize; + + boost::graph_traits::out_edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::out_edges(v, btree); e != e_end; ++e) + btree_alloc_subtree(boost::target(*e, btree), sPtr, cssize, ssize); +} + +void btree_alloc(void) +{ + int ssize = 0; + + if(!boost::num_vertices(btree)) + return; + + btree_alloc_subtree(0, 0, 0, &ssize); + + if(currFunc) + { +#ifdef BTREE_DEBUG + std::cout << "btree stack allocation used total of " << ssize << " bytes\n"; +#endif + if (TARGET_PDK_LIKE) // Stack pointer needs to be aligned to multiple of 2 + if (ssize & 1) ssize++; + currFunc->stack += ssize; + SPEC_STAK (currFunc->etype) += ssize; + } +} + diff --git a/src/SDCCbtree.h b/src/SDCCbtree.h new file mode 100644 index 0000000..09a87e9 --- /dev/null +++ b/src/SDCCbtree.h @@ -0,0 +1,39 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011 +// +// (c) 2011 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SDCCBTREE_H +#define SDCCBTREE_H + + +// Clear block tree. To be called after each function. +void btree_clear(void); + +// Add child as a sub-block of parent. +void btree_add_child(short parent, short child); + +// Gives the lowest common ancestor for blocks a and b. +short btree_lowest_common_ancestor(short a, short b); + +// Add symbol to block tree for allocation. +void btree_add_symbol(struct symbol *s); + +// Allocate all previously added symbols on the stack. +void btree_alloc(void); + +#endif + diff --git a/src/SDCCcflow.c b/src/SDCCcflow.c new file mode 100644 index 0000000..cc487d7 --- /dev/null +++ b/src/SDCCcflow.c @@ -0,0 +1,483 @@ +/*------------------------------------------------------------------------- + + SDCCcflow.c - source file for control flow analysis + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +static void computeDFOrdering (eBBlock *, int *); + +/*-----------------------------------------------------------------*/ +/* domSetFromVect - creates a domset from the vector */ +/*-----------------------------------------------------------------*/ +static set * +domSetFromVect (ebbIndex *ebbi, bitVect * domVect) +{ + int i = 0; + set *domSet = NULL; + + if (!domVect) + return NULL; + + for (i = 0; i < domVect->size; i++) + if (bitVectBitValue (domVect, i)) + addSet (&domSet, ebbi->bbOrder[i]); + return domSet; +} + + +/*-----------------------------------------------------------------*/ +/* addSuccessor - will add bb to succ also add it to the pred of */ +/* the next one : */ +/*-----------------------------------------------------------------*/ +static void +addSuccessor (eBBlock * thisBlock, eBBlock * succ) +{ + /* check for boundary conditions */ + if (!thisBlock || !succ) + return; + + /* add it to the succ of thisBlock */ + addSetIfnotP (&thisBlock->succList, succ); + + thisBlock->succVect = + bitVectSetBit (thisBlock->succVect, succ->bbnum); + /* add this edge to the list of edges */ + addSet (&graphEdges, newEdge (thisBlock, succ)); + +} + +/*-----------------------------------------------------------------*/ +/* eBBPredecessors - find the predecessors for each block */ +/*-----------------------------------------------------------------*/ +static void +eBBPredecessors (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i = 0, j; + + /* for each block do */ + for (i = 0; i < count; i++) + { + + /* if there is no path to this then continue */ + if (ebbs[i]->noPath) + continue; + + /* for each successor of this block if */ + /* it has depth first number > this block */ + /* then this block precedes the successor */ + for (j = 0; j < ebbs[i]->succVect->size; j++) + + if (bitVectBitValue (ebbs[i]->succVect, j) && + ebbs[j]->dfnum > ebbs[i]->dfnum) + + addSet (&ebbs[j]->predList, ebbs[i]); + } +} + +/*-----------------------------------------------------------------*/ +/* eBBSuccessors- find out the successors of all the nodes */ +/*-----------------------------------------------------------------*/ +static void +eBBSuccessors (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i = 0; + + /* for all the blocks do */ + for (; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + ebbs[i]->succVect = newBitVect (count); + + /* if the next on exists & this one does not */ + /* end in a GOTO or RETURN then the next is */ + /* a natural successor of this. Note we have */ + /* consider eBBlocks with no instructions */ + if (ebbs[i + 1]) + { + + if (ebbs[i]->ech) + { + bool foundNoReturn = FALSE; + if (ebbs[i]->ech->op == CALL || ebbs[i]->ech->op == PCALL) + { + sym_link *type = operandType (IC_LEFT (ebbs[i]->ech)); + if (IS_FUNCPTR (type)) + type = type->next; + if (type && FUNC_ISNORETURN (type)) + foundNoReturn = TRUE; + } + if (!foundNoReturn && + ebbs[i]->ech->op != GOTO && + ebbs[i]->ech->op != RETURN && + ebbs[i]->ech->op != JUMPTABLE) + { + int j = i + 1; + + while (ebbs[j] && ebbs[j]->noPath) + j++; + + addSuccessor (ebbs[i], ebbs[j]); /* add it */ + } + else + { + if (i && ebbs[i-1]->ech && ebbs[i-1]->ech->op==IFX) { + ebbs[i]->isConditionalExitFrom=ebbs[i-1]; + } + } + } /* no instructions in the block */ + /* could happen for dummy blocks */ + else + addSuccessor (ebbs[i], ebbs[i + 1]); + } + + /* go thru all the instructions: if we find a */ + /* goto or ifx or a return then we have a succ */ + if ((ic = ebbs[i]->ech)) + { + eBBlock *succ; + + /* special case for jumptable */ + if (ic->op == JUMPTABLE) + { + symbol *lbl; + for (lbl = setFirstItem (IC_JTLABELS (ic)); lbl; + lbl = setNextItem (IC_JTLABELS (ic))) + addSuccessor (ebbs[i], + eBBWithEntryLabel (ebbi, lbl)); + } + else + { + + succ = NULL; + /* depending on the instruction operator */ + switch (ic->op) + { + case GOTO: /* goto has edge to label */ + succ = eBBWithEntryLabel (ebbi, ic->label); + + /* Sometimes a block has a GOTO added after the original */ + /* final IFX (due to loop optimizations). If IFX found, */ + /* fall through to handle the IFX too. */ + if (ic->prev && ic->prev->op == IFX) + { + if (succ) + addSuccessor (ebbs[i], succ); /* add the GOTO target */ + ic = ic->prev; /* get ready to handle IFX too. */ + } + else + break; + + case IFX: /* conditional jump */ + /* if true label is present */ + if (IC_TRUE (ic)) + succ = eBBWithEntryLabel (ebbi, IC_TRUE (ic)); + else + succ = eBBWithEntryLabel (ebbi, IC_FALSE (ic)); + break; + + case RETURN: /* block with return */ + succ = eBBWithEntryLabel (ebbi, returnLabel); + break; + } + + /* if there is a successor add to the list */ + /* if it is not already present in the list */ + if (succ) + addSuccessor (ebbs[i], succ); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* computeDominance - computes the dominance graph */ +/* for algorithm look at Dragon book section 10.10, algo 10.16 */ +/*-----------------------------------------------------------------*/ +static void +computeDominance (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i, j; + + /* now do the initialisation */ + /* D(n0) := { n0 } */ + ebbs[0]->domVect = + bitVectSetBit (ebbs[0]->domVect = newBitVect (count), ebbs[0]->bbnum); + + + /* for n in N - { n0 } do D(n) = N */ + for (i = 1; i < count; i++) + { + ebbs[i]->domVect = newBitVect (count); + for (j = 0; j < count; j++) + { + ebbs[i]->domVect = + bitVectSetBit (ebbs[i]->domVect, ebbs[j]->bbnum); + } + } + + /* end of initialisation */ + + /* while changes to any D(n) occur do */ + /* for n in N - { n0 } do */ + /* D(n) := { n } U (intersection of D( all predecessors of n)) */ + while (1) + { + int change; + + change = 0; + for (i = 1; i < count; i++) + { + bitVect *cDomVect; + eBBlock *pred; + + cDomVect = NULL; + + /* get the intersection of the dominance of all predecessors */ + for (pred = setFirstItem (ebbs[i]->predList), + cDomVect = (pred ? bitVectCopy (pred->domVect) : NULL); + pred; + pred = setNextItem (ebbs[i]->predList)) + { + cDomVect = bitVectInplaceIntersect (cDomVect, pred->domVect); + } + if (!cDomVect) + cDomVect = newBitVect (count); + /* this node to the list */ + cDomVect = bitVectSetBit (cDomVect, ebbs[i]->bbnum); + + + if (!bitVectEqual (cDomVect, ebbs[i]->domVect)) + { + freeBitVect (ebbs[i]->domVect); + ebbs[i]->domVect = cDomVect; + change = 1; + } + } + + /* if no change then exit */ + if (!change) + break; + } + +} + +/*-----------------------------------------------------------------*/ +/* immedDom - returns the immediate dominator of a block */ +/*-----------------------------------------------------------------*/ +eBBlock * +immedDom (ebbIndex * ebbi, eBBlock * ebp) +{ + /* first delete self from the list */ + set *iset = domSetFromVect (ebbi, ebp->domVect); + eBBlock *loop; + eBBlock *idom = NULL; + + deleteSetItem (&iset, ebp); + /* then just return the one with the greatest */ + /* depthfirst number, this will be the immed dominator */ + if ((loop = setFirstItem (iset))) + idom = loop; + for (; loop; loop = setNextItem (iset)) + if (loop->dfnum > idom->dfnum) + idom = loop; + + setToNull ((void *) &iset); + return idom; + +} + +/*-----------------------------------------------------------------*/ +/* DFOrdering - is visited then nothing else call DFOrdering this */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (DFOrdering) +{ + eBBlock *ebbp = item; + V_ARG (int *, count); + + if (ebbp->visited) + return 0; + + computeDFOrdering (ebbp, count); /* depthfirst */ + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* computeDFOrdering - computes the depth first ordering of the */ +/* flowgraph */ +/*-----------------------------------------------------------------*/ +static void +computeDFOrdering (eBBlock * ebbp, int *count) +{ + + ebbp->visited = 1; + /* for each successor that is not visited */ + applyToSet (ebbp->succList, DFOrdering, count); + + /* set the depth first number */ + ebbp->dfnum = *count; + *count -= 1; +} + +/*-----------------------------------------------------------------*/ +/* disconBBlock - removes all control flow links for a block */ +/*-----------------------------------------------------------------*/ +void +disconBBlock (eBBlock * ebp, ebbIndex * ebbi) +{ + /* mark this block as noPath & recompute control flow */ + ebp->noPath = 1; + computeControlFlow (ebbi); +} + +/*-----------------------------------------------------------------*/ +/* markNoPath - marks those blocks which cannot be reached from top */ +/*-----------------------------------------------------------------*/ +static void +markNoPath (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i; + + /* for all blocks if the visited flag is not set : then there */ + /* is no path from _entry to this block push them down in the */ + /* depth first order */ + for (i = 0; i < count; i++) + if (!ebbs[i]->visited) + ebbs[i]->noPath = 1; +} + +/*-----------------------------------------------------------------*/ +/* dfNumCompare - used by qsort to sort by dfNumber */ +/*-----------------------------------------------------------------*/ +int +dfNumCompare (const void *a, const void *b) +{ + const eBBlock *const *i = a; + const eBBlock *const *j = b; + + if ((*i)->dfnum > (*j)->dfnum) + return 1; + + if ((*i)->dfnum < (*j)->dfnum) + return -1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* computeControlFlow - does the control flow computation */ +/*-----------------------------------------------------------------*/ +void +computeControlFlow (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int dfCount = ebbi->count; + int i; + + /* initialise some things */ + + for (i = 0; i < ebbi->count; i++) + { + deleteSet (&ebbs[i]->predList); + freeBitVect (ebbs[i]->domVect); ebbs[i]->domVect = NULL; + deleteSet (&ebbs[i]->succList); + freeBitVect (ebbs[i]->succVect); ebbs[i]->succVect = NULL; + ebbs[i]->visited = 0; + ebbs[i]->dfnum = 0; + } + + setToNull ((void *) &graphEdges); + /* this will put in the */ + /* successor information for each blk */ + eBBSuccessors (ebbi); + + /* compute the depth first ordering */ + computeDFOrdering (ebbi->bbOrder[0], &dfCount); + + /* mark blocks with no paths to them */ + markNoPath (ebbi); + + /* with the depth first info in place */ + /* add the predecessors for the blocks */ + eBBPredecessors (ebbi); + + /* compute the dominance graph */ + computeDominance (ebbi); + + /* sort it by dfnumber */ + if (!ebbi->dfOrder) + ebbi->dfOrder = Safe_alloc ((ebbi->count+1) * sizeof (eBBlock *)); + for (i = 0; i < (ebbi->count+1); i++) + { + ebbi->dfOrder[i] = ebbi->bbOrder[i]; + } + + qsort (ebbi->dfOrder, ebbi->count, sizeof (eBBlock *), dfNumCompare); + +} + +/*-----------------------------------------------------------------*/ +/* returnAtEnd - returns 1 if Basic Block has a return at the end */ +/* of it */ +/*-----------------------------------------------------------------*/ +int returnAtEnd (eBBlock *ebp) +{ + /* case 1. + This basic block ends in a return statment + */ + if (ebp->ech && ebp->ech->op == RETURN) return 1; + + /* case 2. + This basic block has only one successor and that + successor has only one return statement + */ + if (elementsInSet(ebp->succList) == 1) { + eBBlock *succ = setFirstItem(ebp->succList); + /* could happen for dummy blocks */ + if (!succ->sch || !succ->ech) return 0; + + /* first iCode is a return */ + if (succ->sch->op == RETURN) return 2; + + /* or first iCode is a label & the next & + last icode is a return */ + if (succ->sch->op == LABEL && succ->sch->next == succ->ech && + succ->ech->op == RETURN ) return 2; + } + + return 0; +} + diff --git a/src/SDCCcflow.h b/src/SDCCcflow.h new file mode 100644 index 0000000..ef0b330 --- /dev/null +++ b/src/SDCCcflow.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + + SDCCcflow.h - header file for control flow analysis + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "SDCCset.h" + +#ifndef SDCCCFLOW_H +#define SDCCCFLOW_H 1 + +eBBlock *immedDom (ebbIndex *, eBBlock *); +void computeControlFlow (ebbIndex *); +void disconBBlock (eBBlock *, ebbIndex *); +int returnAtEnd (eBBlock *) ; +#endif diff --git a/src/SDCCcse.c b/src/SDCCcse.c new file mode 100644 index 0000000..dc6d8aa --- /dev/null +++ b/src/SDCCcse.c @@ -0,0 +1,2736 @@ +/*------------------------------------------------------------------------- + SDCCcse.c - source file for Common Subexpressions and other utility + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "newalloc.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* newCseDef - new cseDef */ +/*-----------------------------------------------------------------*/ +cseDef * +newCseDef (operand * sym, iCode * ic) +{ + cseDef *cdp; + memmap *map; + + assert (sym); + cdp = Safe_alloc (sizeof (cseDef)); + + cdp->sym = sym; + cdp->diCode = ic; + cdp->key = sym->key; + cdp->ancestors = newBitVect(operandKey); + cdp->fromGlobal = 0; + cdp->fromAddrTaken = 0; + + if (ic->op != IF && ic->op != JUMPTABLE) + { + if (ic->op != ADDRESS_OF && IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic))) + { + bitVectSetBit (cdp->ancestors, IC_LEFT (ic)->key); + if (isOperandGlobal (IC_LEFT (ic))) + { + map = SPEC_OCLS (getSpec (operandType (IC_LEFT (ic)))); + if (map) + cdp->fromGlobal |= (1 << map->ptrType); + } + cdp->fromAddrTaken |= OP_SYMBOL (IC_LEFT (ic))->addrtaken; + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic))) + { + bitVectSetBit (cdp->ancestors, IC_RIGHT (ic)->key); + if (isOperandGlobal (IC_RIGHT (ic))) + { + map = SPEC_OCLS (getSpec (operandType (IC_RIGHT (ic)))); + if (map) + cdp->fromGlobal |= (1 << map->ptrType); + } + cdp->fromAddrTaken |= OP_SYMBOL (IC_RIGHT (ic))->addrtaken; + } + } + + return cdp; +} + +void +freeLocalCseDef (void *item) +{ + cseDef * cse = (cseDef *)item; + + /* If this CSE definition being deleted is not visible outside */ + /* its defining eBBlock, we can safely deallocate it completely */ + if (!cse->nonLocalCSE) + { + freeBitVect(cse->ancestors); + Safe_free(cse); + } +} + + +void +updateCseDefAncestors(cseDef *cdp, set * cseSet) +{ + cseDef *loop; + set *sl; + iCode *ic = cdp->diCode; + + if (ic->op!=IF && ic->op!=JUMPTABLE) + { + if (ic->op != ADDRESS_OF && IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic))) + { + bitVectSetBit (cdp->ancestors, IC_LEFT (ic)->key); + for (sl = cseSet; sl; sl = sl->next) + { + loop = sl->item; + if (loop->sym->key == IC_LEFT (ic)->key) + { + cdp->ancestors = bitVectInplaceUnion (cdp->ancestors, loop->ancestors); + cdp->fromGlobal |= loop->fromGlobal; + cdp->fromAddrTaken |= loop->fromAddrTaken; + break; + } + } + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic))) + { + bitVectSetBit (cdp->ancestors, IC_RIGHT (ic)->key); + for (sl = cseSet; sl; sl = sl->next) + { + loop = sl->item; + if (loop->sym->key == IC_RIGHT (ic)->key) + { + cdp->ancestors = bitVectInplaceUnion (cdp->ancestors, loop->ancestors); + cdp->fromGlobal |= loop->fromGlobal; + cdp->fromAddrTaken |= loop->fromAddrTaken; + break; + } + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* int isCseDefEqual - two definitions are equal */ +/*-----------------------------------------------------------------*/ +int +isCseDefEqual (void *vsrc, void *vdest) +{ + cseDef *src = vsrc; + cseDef *dest = vdest; + + if (src == dest) + return 1; + + return (src->key == dest->key && + src->diCode == dest->diCode); +} + +/*-----------------------------------------------------------------*/ +/* pcseDef - in the cseDef */ +/*-----------------------------------------------------------------*/ +int +pcseDef (void *item, va_list ap) +{ + cseDef *cdp = item; + iCodeTable *icTab; + struct dbuf_s dbuf; + + (void) ap; + + if (!cdp->sym) + fprintf (stdout, "**null op**"); + printOperand (cdp->sym, stdout); + icTab = getTableEntry (cdp->diCode->op); + dbuf_init (&dbuf, 1024); + icTab->iCodePrint (&dbuf, cdp->diCode, icTab->printName); + dbuf_write_and_destroy (&dbuf, stdout); + return 1; +} + +void ReplaceOpWithCheaperOp(operand **op, operand *cop) { +#ifdef RANGEHUNT + printf ("ReplaceOpWithCheaperOp\n\t"); + printOperand (*op, stdout); + printf ("\nwith\t"); + printOperand (cop, stdout); + + // if op is a register equivalent + if (IS_ITEMP(cop) && IS_SYMOP((*op)) && OP_SYMBOL((*op))->isreqv) { + operand **rop = &OP_SYMBOL((*op))->usl.spillLoc->reqv; + if (isOperandEqual(*rop, *op)) { + printf ("true"); + *rop=cop; + OP_SYMBOL((*op))->isreqv=0; + OP_SYMBOL(cop)->isreqv=1; + } else { + printf ("false"); + } + } + printf ("\n"); +#endif + *op=cop; +} + +/*-----------------------------------------------------------------*/ +/* replaceAllSymBySym - replaces all operands by operand in an */ +/* instruction chain */ +/*-----------------------------------------------------------------*/ +void +replaceAllSymBySym (iCode * ic, operand * from, operand * to, bitVect ** ndpset) +{ + iCode *lic; + +#ifdef RANGEHUNT + printf ("replaceAllSymBySym\n\t"); + printOperand (from, stdout); + printf ("\nwith\t"); + printOperand (to, stdout); + printf ("\n"); +#endif + for (lic = ic; lic; lic = lic->next) + { + int isaddr; + + /* do the special cases first */ + if (lic->op == IFX) + { + if (IS_SYMOP (to) && + IC_COND (lic)->key == from->key) + { + + bitVectUnSetBit (OP_USES (from), lic->key); + OP_USES(to)=bitVectSetBit (OP_USES (to), lic->key); + isaddr = IC_COND (lic)->isaddr; + IC_COND (lic) = operandFromOperand (to); + IC_COND (lic)->isaddr = isaddr; + + } + continue; + } + + if (lic->op == JUMPTABLE) + { + if (IS_SYMOP (to) && + IC_JTCOND (lic)->key == from->key) + { + + bitVectUnSetBit (OP_USES (from), lic->key); + OP_USES(to)=bitVectSetBit (OP_USES (to), lic->key); + isaddr = IC_COND (lic)->isaddr; + IC_JTCOND (lic) = operandFromOperand (to); + IC_JTCOND (lic)->isaddr = isaddr; + + } + continue; + } + + if (IS_SYMOP(to) && + IC_RESULT (lic) && IC_RESULT (lic)->key == from->key) + { + /* maintain du chains */ + if (POINTER_SET (lic)) + { + bitVectUnSetBit (OP_USES (from), lic->key); + OP_USES(to)=bitVectSetBit (OP_USES (to), lic->key); + + /* also check if the "from" was in the non-dominating + pointer sets and replace it with "to" in the bitVector */ + if (bitVectBitValue (*ndpset, from->key)) + { + bitVectUnSetBit (*ndpset, from->key); + bitVectSetBit (*ndpset, to->key); + } + + } + else + { + bitVectUnSetBit (OP_DEFS (from), lic->key); + OP_DEFS(to)=bitVectSetBit (OP_DEFS (to), lic->key); + } + isaddr = IC_RESULT (lic)->isaddr; + IC_RESULT (lic) = operandFromOperand (to); + IC_RESULT (lic)->isaddr = isaddr; + } + + if (IS_SYMOP (to) && + IC_RIGHT (lic) && IC_RIGHT (lic)->key == from->key) + { + bitVectUnSetBit (OP_USES (from), lic->key); + OP_USES(to)=bitVectSetBit (OP_USES (to), lic->key); + isaddr = IC_RIGHT (lic)->isaddr; + IC_RIGHT (lic) = operandFromOperand (to); + IC_RIGHT (lic)->isaddr = isaddr; + } + + if (IS_SYMOP (to) && + IC_LEFT (lic) && IC_LEFT (lic)->key == from->key) + { + bitVectUnSetBit (OP_USES (from), lic->key); + OP_USES(to)=bitVectSetBit (OP_USES (to), lic->key); + isaddr = IC_LEFT (lic)->isaddr; + IC_LEFT (lic) = operandFromOperand (to); + IC_LEFT (lic)->isaddr = isaddr; + } + } +} + +/*-----------------------------------------------------------------*/ +/* iCodeKeyIs - if the icode keys match then return 1 */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (iCodeKeyIs) +{ + cseDef *cdp = item; + V_ARG (int, key); + + if (cdp->diCode->key == key) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* removeFromInExprs - removes an icode from inexpressions */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (removeFromInExprs) +{ + eBBlock *ebp = item; + V_ARG (iCode *, ic); + V_ARG (operand *, from); + V_ARG (operand *, to); + V_ARG (eBBlock *, cbp); + + if (ebp->visited) + return 0; + + ebp->visited = 1; + deleteItemIf (&ebp->inExprs, iCodeKeyIs, ic->key); + if (ebp != cbp && !bitVectBitValue (cbp->domVect, ebp->bbnum)) + replaceAllSymBySym (ebp->sch, from, to, &ebp->ndompset); + + applyToSet (ebp->succList, removeFromInExprs, ic, from, to, cbp); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isGlobalInNearSpace - return TRUE if variable is a globalin data */ +/*-----------------------------------------------------------------*/ +static bool +isGlobalInNearSpace (operand * op) +{ + sym_link *type = getSpec (operandType (op)); + /* this is 8051 specific: optimization + suggested by Jean-Louis VERN, with 8051s we have no + advantage of putting variables in near space into + registers */ + if (isOperandGlobal (op) && !IN_FARSPACE (SPEC_OCLS (type)) && + IN_DIRSPACE (SPEC_OCLS (type))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* findCheaperOp - cseBBlock support routine, will check to see if */ +/* we have a operand previously defined */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (findCheaperOp) +{ + cseDef *cdp = item; + V_ARG (operand *, cop); + V_ARG (operand **, opp); + V_ARG (int, checkSign); + + /* if we have already found it */ + if (*opp) + return 1; + + /* not found it yet check if this is the one */ + /* and this is not the defining one */ + if (cop->key && cop->key == cdp->key) + { + /* do a special check this will help in */ + /* constant propagation & dead code elim */ + /* for assignments only */ + if (cdp->diCode->op == '=') + { + /* if the result is volatile then return result */ + if (IS_OP_VOLATILE (IC_RESULT (cdp->diCode))) + { + *opp = IC_RESULT (cdp->diCode); + } + else + { + /* if this is a straight assignment and + left is a temp then prefer the temporary to the + true symbol */ + if (!POINTER_SET (cdp->diCode) && + IS_ITEMP (IC_RESULT (cdp->diCode)) && + IS_TRUE_SYMOP (IC_RIGHT (cdp->diCode))) + { + *opp = IC_RESULT (cdp->diCode); + } + else + { + /* if straight assignment and both + are temps then prefer the one that + will not need extra space to spil, also + take into consideration if right side + is an induction variable + */ + if (!POINTER_SET (cdp->diCode) && + IS_ITEMP (IC_RESULT (cdp->diCode)) && + IS_ITEMP (IC_RIGHT (cdp->diCode)) && + !OP_SYMBOL (IC_RIGHT (cdp->diCode))->isind && + !OP_SYMBOL(IC_RIGHT (cdp->diCode))->isreqv && + ((!SPIL_LOC (IC_RIGHT (cdp->diCode)) && SPIL_LOC (IC_RESULT (cdp->diCode))) || + (SPIL_LOC (IC_RESULT (cdp->diCode)) && SPIL_LOC (IC_RESULT (cdp->diCode)) == SPIL_LOC (IC_RIGHT (cdp->diCode))))) + { + *opp = IC_RESULT (cdp->diCode); + } + else + { + *opp = IC_RIGHT (cdp->diCode); + } + } + } + } + else + { + *opp = IC_RESULT (cdp->diCode); + } + } + + /* if this is an assign to a temp. then check + if the right side is this then return this */ + if (IS_TRUE_SYMOP (cop) && + cdp->diCode->op == '=' && + !POINTER_SET (cdp->diCode) && + cop->key == IC_RIGHT (cdp->diCode)->key && + !isGlobalInNearSpace (IC_RIGHT (cdp->diCode)) && + IS_ITEMP (IC_RESULT (cdp->diCode))) + { + *opp = IC_RESULT (cdp->diCode); + } + + if ((*opp) && + (isOperandLiteral(*opp) || !checkSign || + (checkSign && + IS_SPEC(operandType (cop)) && IS_SPEC(operandType (*opp)) && + (SPEC_USIGN(operandType (cop))==SPEC_USIGN(operandType (*opp)) && + (SPEC_LONG(operandType (cop))==SPEC_LONG(operandType (*opp))))))) + { + if ((isGlobalInNearSpace (cop) && !isOperandLiteral (*opp)) || + isOperandVolatile (*opp, FALSE)) + { + *opp = NULL; + return 0; + } + + if (cop->key == (*opp)->key) + { + *opp = NULL; + return 0; + } + + if ((*opp)->isaddr != cop->isaddr && IS_ITEMP (cop)) + { + *opp = operandFromOperand (*opp); + (*opp)->isaddr = cop->isaddr; + } + + /* copy signedness to literal operands */ + if (IS_SPEC(operandType (cop)) && IS_SPEC(operandType (*opp)) + && isOperandLiteral(*opp) + && SPEC_NOUN(operandType(*opp)) == SPEC_NOUN(operandType(cop)) + && SPEC_USIGN(operandType(*opp)) != SPEC_USIGN(operandType(cop))) + { + SPEC_USIGN(operandType(*opp)) = SPEC_USIGN(operandType(cop)); + } + + if (IS_SPEC(operandType (cop)) && IS_SPEC(operandType (*opp)) && + SPEC_NOUN(operandType(cop)) != SPEC_NOUN(operandType(*opp))) + { + // special case: we can make an unsigned char literal + // into an int literal with no cost. + if (isOperandLiteral(*opp) && + SPEC_NOUN(operandType(*opp)) == V_CHAR && + SPEC_NOUN(operandType(cop)) == V_INT) + { + *opp = operandFromOperand (*opp); + SPEC_NOUN(operandType(*opp)) = V_INT; + } + else + { + // No clue... + *opp = NULL; + return 0; + } + } + return 1; + } + *opp=NULL; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findPointerSet - finds the right side of a pointer set op */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (findPointerSet) +{ + cseDef *cdp = item; + V_ARG (operand *, op); + V_ARG (operand **, opp); + V_ARG (operand *, rop); + + if (POINTER_SET (cdp->diCode) && + op->key && + IC_RESULT (cdp->diCode)->key == op->key && + !isOperandVolatile (IC_RESULT (cdp->diCode), TRUE) && + !isOperandVolatile (IC_RIGHT (cdp->diCode), TRUE) && + getSize (operandType (IC_RIGHT (cdp->diCode))) == + getSize (operandType (rop))) + { + if (IS_SPEC (operandType (IC_RIGHT (cdp->diCode))) && + SPEC_USIGN (operandType (IC_RIGHT (cdp->diCode))) != + SPEC_USIGN (operandType (rop))) + { + /* bug #1493710 + Reminder for Bernhard: check of signedness + could be unnecessary together with 'checkSign', if + signedness of operation is stored in ic */ + return 0; + } + *opp = IC_RIGHT (cdp->diCode); + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findPrevIc - cseBBlock support function will return the iCode */ +/* which matches the current one */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (findPrevIc) +{ + cseDef *cdp = item; + V_ARG (iCode *, ic); + V_ARG (iCode **, icp); + + /* if already found */ + if (*icp) + return 1; + + /* if the iCodes are the same */ + if (isiCodeEqual (ic, cdp->diCode) && + isOperandEqual (cdp->sym, IC_RESULT (cdp->diCode))) + { + *icp = cdp->diCode; + return 1; + } + + /* if iCodes are not the same */ + /* see the operands maybe interchanged */ + if (ic->op == cdp->diCode->op && + IS_COMMUTATIVE (ic) && + isOperandEqual (IC_LEFT (ic), IC_RIGHT (cdp->diCode)) && + isOperandEqual (IC_RIGHT (ic), IC_LEFT (cdp->diCode))) + { + *icp = cdp->diCode; + return 1; + } + + return 0; +} + +/*-------------------------------------------------------------------*/ +/* ifAssignedFromGlobal - if definition is an assignment from global */ +/*-------------------------------------------------------------------*/ +DEFSETFUNC (ifAssignedFromGlobal) +{ + cseDef *cdp = item; + iCode *dic=cdp->diCode; + + if (dic->op=='=' && isOperandGlobal(IC_RIGHT(dic))) { + return 1; + } + return 0; +} + +/*-------------------------------------------------------------------*/ +/* ifFromGlobal - if definition is derived from global */ +/*-------------------------------------------------------------------*/ +DEFSETFUNC (ifFromGlobal) +{ + cseDef *cdp = item; + + return cdp->fromGlobal; +} + +/*-------------------------------------------------------------------*/ +/* ifFromGlobalAliasableByPtr - if definition is derived from global */ +/* that may be aliasble by a particular pointer type */ +/*-------------------------------------------------------------------*/ +DEFSETFUNC (ifFromGlobalAliasableByPtr) +{ + cseDef *cdp = item; + V_ARG (DECLARATOR_TYPE, decl); + + if (decl == GPOINTER && cdp->fromGlobal) + return 1; + else if (cdp->fromGlobal & (1 << decl)) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifDefGlobal - if definition is global */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifDefGlobal) +{ + cseDef *cdp = item; + + return (isOperandGlobal (cdp->sym)); +} + +/*-----------------------------------------------------------------*/ +/* ifDefGlobalAliasableByPtr - if definition is global */ +/* and may be aliasble by a particular pointer type */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifDefGlobalAliasableByPtr) +{ + cseDef *cdp = item; + V_ARG (DECLARATOR_TYPE, decl); + memmap *map; + + if (!isOperandGlobal (cdp->sym)) + return 0; + if (decl == GPOINTER) + return 1; + map = SPEC_OCLS (getSpec (operandType (cdp->sym))); + return (map->ptrType == decl); +} + +/*-------------------------------------------------------------------*/ +/* ifFromAddrTaken - if definition is derived from a symbol whose */ +/* address was taken */ +/*-------------------------------------------------------------------*/ +DEFSETFUNC (ifFromAddrTaken) +{ + cseDef *cdp = item; + + if (OP_SYMBOL(cdp->sym)->addrtaken) + return 1; + return cdp->fromAddrTaken; +} + + +/*-----------------------------------------------------------------*/ +/* ifAnyGetPointer - if get pointer icode */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifAnyGetPointer) +{ + cseDef *cdp = item; + + if (cdp->diCode && POINTER_GET (cdp->diCode)) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifAnyUnrestrictedGetPointer - if get pointer icode */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifAnyUnrestrictedGetPointer) +{ + cseDef *cdp = item; + V_ARG (DECLARATOR_TYPE, decl); + + if (cdp->diCode && POINTER_GET (cdp->diCode)) + { + sym_link *ptype; + ptype = operandType (IC_LEFT (cdp->diCode)); + if (!IS_PTR_RESTRICT (ptype)) + { + if (DCL_TYPE (ptype) == decl || IS_GENPTR (ptype)) + return 1; + } + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifAnyUnrestrictedSetPointer - if set pointer icode */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifAnyUnrestrictedSetPointer) +{ + cseDef *cdp = item; + V_ARG (DECLARATOR_TYPE, decl); + + if (cdp->diCode && POINTER_SET (cdp->diCode)) + { + sym_link *ptype; + ptype = operandType (IC_RESULT (cdp->diCode)); + if (!IS_PTR_RESTRICT (ptype)) + { + if (DCL_TYPE (ptype) == decl || IS_GENPTR (ptype)) + return 1; + } + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifOperandsHave - if any of the operand are the same as this */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifOperandsHave) +{ + cseDef *cdp = item; + V_ARG (operand *, op); + + if (bitVectBitValue(cdp->ancestors, op->key)) + return 1; + + if (IC_LEFT (cdp->diCode) && + IS_SYMOP (IC_LEFT (cdp->diCode)) && + IC_LEFT (cdp->diCode)->key == op->key) + return 1; + + if (IC_RIGHT (cdp->diCode) && + IS_SYMOP (IC_RIGHT (cdp->diCode)) && + IC_RIGHT (cdp->diCode)->key == op->key) + return 1; + + if (POINTER_SET (cdp->diCode) && + IS_SYMOP (IC_RESULT (cdp->diCode)) && + IC_RESULT (cdp->diCode)->key == op->key) + return 1; + + /* or if any of the operands are volatile */ + if (IC_LEFT (cdp->diCode) && + IS_OP_VOLATILE (IC_LEFT (cdp->diCode))) + return 1; + + if (IC_RIGHT (cdp->diCode) && + IS_OP_VOLATILE (IC_RIGHT (cdp->diCode))) + return 1; + + + if (IC_RESULT (cdp->diCode) && + IS_OP_VOLATILE (IC_RESULT (cdp->diCode))) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifDefSymIs - if a definition is found in the set */ +/*-----------------------------------------------------------------*/ +int +ifDefSymIs (set * cseSet, operand * sym) +{ + cseDef *loop; + set *sl; + + if (!sym || !IS_SYMOP (sym)) + return 0; + for (sl = cseSet; sl; sl = sl->next) + { + loop = sl->item; + if (loop->sym->key == sym->key) + return 1; + } + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* ifDefSymIsX - will return 1 if the symbols match */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifDefSymIsX) +{ + cseDef *cdp = item; + V_ARG (operand *, op); + int match; + + if (op && cdp->sym && op->key) + match = cdp->sym->key == op->key; + else + match = (isOperandEqual (cdp->sym, op)); + #if 0 + if (match) + printf("%s ",OP_SYMBOL(cdp->sym)->name); + #endif + return match; +} + + +/*-----------------------------------------------------------------*/ +/* ifDiCodeIs - returns true if diCode is same */ +/*-----------------------------------------------------------------*/ +int +ifDiCodeIs (set * cseSet, iCode * ic) +{ + cseDef *loop; + set *sl; + + if (!ic) + return 0; + + for (sl = cseSet; sl; sl = sl->next) + { + loop = sl->item; + if (loop->diCode == ic) + return 1; + } + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* ifPointerGet - returns true if the icode is pointer get sym */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifPointerGet) +{ + cseDef *cdp = item; + V_ARG (operand *, op); + iCode *dic = cdp->diCode; + operand *left = IC_LEFT (cdp->diCode); + + if (POINTER_GET (dic) && op->key && left->key == op->key) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifPointerSet - returns true if the icode is pointer set sym */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifPointerSet) +{ + cseDef *cdp = item; + V_ARG (operand *, op); + + if (POINTER_SET (cdp->diCode) && op->key && + IC_RESULT (cdp->diCode)->key == op->key) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ifDiCodeIsX - will return 1 if the symbols match */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifDiCodeIsX) +{ + cseDef *cdp = item; + V_ARG (iCode *, ic); + + return cdp->diCode == ic; + +} + +/*-----------------------------------------------------------------*/ +/* findBackwardDef - scan backwards to find definition of operand */ +/*-----------------------------------------------------------------*/ +iCode *findBackwardDef(operand *op,iCode *ic) +{ + iCode *lic; + + for (lic = ic; lic ; lic = lic->prev) { + if (IC_RESULT(lic) && isOperandEqual(op,IC_RESULT(lic))) + return lic; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* algebraicOpts - does some algebraic optimizations */ +/*-----------------------------------------------------------------*/ +static void +algebraicOpts (iCode * ic, eBBlock * ebp) +{ + /* we don't deal with the following iCodes + here */ + if (ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == CALL || + ic->op == PCALL || + ic->op == RETURN || + POINTER_GET (ic)) + return; + + /* if both operands present & ! IFX */ + /* then if they are both literal we */ + /* perform the operation right now */ + if (IC_RESULT (ic) && + IC_RIGHT (ic) && + IC_LEFT (ic) && + IS_OP_LITERAL (IC_LEFT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) + { + IC_RIGHT (ic) = operandOperation (IC_LEFT (ic), + IC_RIGHT (ic), + ic->op, + operandType (IC_RESULT (ic))); + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* if not ifx & only one operand present */ + if (IC_RESULT (ic) && + IC_LEFT (ic) && + IS_OP_LITERAL (IC_LEFT (ic)) && + !IC_RIGHT (ic)) + { + IC_RIGHT (ic) = operandOperation (IC_LEFT (ic), + IC_RIGHT (ic), + ic->op, + operandType (IC_RESULT (ic))); + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + + /* a special case : or in short a kludgy solution will think + about a better solution over a glass of wine someday */ + if (ic->op == GET_VALUE_AT_ADDRESS) + { + if (IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic))) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromOperand (IC_LEFT (ic)); + IC_RIGHT (ic)->isaddr = 0; + IC_LEFT (ic) = NULL; + IC_RESULT (ic) = operandFromOperand (IC_RESULT (ic)); + IC_RESULT (ic)->isaddr = 0; + setOperandType (IC_RESULT (ic), operandType (IC_RIGHT (ic))); + if (IS_DECL (operandType (IC_RESULT (ic)))) + { + DCL_PTR_VOLATILE (operandType (IC_RESULT (ic))) = 0; + DCL_PTR_ADDRSPACE (operandType (IC_RESULT (ic))) = 0; + } + return; + } + + if (IS_ITEMP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + !IC_LEFT (ic)->isaddr) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromOperand (IC_LEFT (ic)); + IC_RIGHT (ic)->isaddr = 0; + IC_RESULT (ic) = operandFromOperand (IC_RESULT (ic)); + IC_RESULT (ic)->isaddr = 0; + IC_LEFT (ic) = NULL; + return; + } + } + + /* depending on the operation */ + switch (ic->op) + { + case '+': + /* if adding the same thing change to left shift by 1 */ + if (IC_LEFT (ic)->key == IC_RIGHT (ic)->key && + !IS_OP_VOLATILE (IC_LEFT (ic)) && + !(IS_FLOAT (operandType (IC_RESULT (ic))) + || IS_FIXED(operandType (IC_RESULT (ic))))) + { + ic->op = LEFT_OP; + IC_RIGHT (ic) = operandFromLit (1); + return; + } + /* if addition then check if one of them is a zero */ + /* if yes turn it into assignment or cast */ + if (IS_OP_LITERAL (IC_LEFT (ic)) && + isEqualVal (OP_VALUE (IC_LEFT (ic)), 0)) + { + int typematch; + typematch = compareType (operandType (IC_RESULT (ic)), + operandType (IC_RIGHT (ic))); + if ((typematch<0) || (IS_TRUE_SYMOP (IC_RIGHT (ic)))) + { + ic->op = CAST; + IC_LEFT (ic) = operandFromLink (operandType (IC_RESULT (ic))); + } + else + { + ic->op = '='; + IC_LEFT (ic) = NULL; + if (typematch==0) + { + /* for completely different types, preserve the source type */ + IC_RIGHT (ic) = operandFromOperand (IC_RIGHT (ic)); + setOperandType (IC_RIGHT (ic), operandType (IC_RESULT (ic))); + } + } + SET_ISADDR (IC_RESULT (ic), 0); + SET_ISADDR (IC_RIGHT (ic), 0); + return; + } + if (IS_OP_LITERAL (IC_RIGHT (ic)) && + isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + int typematch; + typematch = compareType (operandType (IC_RESULT (ic)), + operandType (IC_LEFT (ic))); + if ((typematch<0) || (IS_TRUE_SYMOP (IC_LEFT (ic)))) + { + ic->op = CAST; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = operandFromLink (operandType (IC_RESULT (ic))); + } + else + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + if (typematch==0) + { + /* for completely different types, preserve the source type */ + IC_RIGHT (ic) = operandFromOperand (IC_RIGHT (ic)); + setOperandType (IC_RIGHT (ic), operandType (IC_RESULT (ic))); + } + } + SET_ISADDR (IC_RIGHT (ic), 0); + SET_ISADDR (IC_RESULT (ic), 0); + return; + } + break; + case '-': + /* if subtracting the same thing then zero */ + if (IC_LEFT (ic)->key == IC_RIGHT (ic)->key && + !IS_OP_VOLATILE (IC_LEFT (ic))) + {printf("Sub. at %d\n", ic->key); + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (0); + IC_LEFT (ic) = NULL; + IC_RESULT (ic) = operandFromOperand (IC_RESULT (ic)); + IC_RESULT (ic)->isaddr = 0; + return; + } + + /* if subtraction then check if one of the operand */ + /* is zero then depending on which operand change */ + /* to assignment or unary minus */ + if (IS_OP_LITERAL (IC_RIGHT (ic)) && + isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + /* right size zero change to assignment */ + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_ISADDR (IC_RIGHT (ic), 0); + SET_ISADDR (IC_RESULT (ic), 0); + return; + } + if (IS_OP_LITERAL (IC_LEFT (ic)) && + isEqualVal (OP_VALUE (IC_LEFT (ic)), 0)) + { + /* left zero turn into an unary minus */ + ic->op = UNARYMINUS; + IC_LEFT (ic) = IC_RIGHT (ic); + IC_RIGHT (ic) = NULL; + return; + } + break; + /* if multiplication then check if either of */ + /* them is zero then the result is zero */ + /* if either of them is one then result is */ + /* the other one */ + case '*': + if (IS_OP_LITERAL (IC_LEFT (ic))) + { + if (isEqualVal (OP_VALUE (IC_LEFT (ic)), 0)) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + if (isEqualVal (OP_VALUE (IC_LEFT (ic)), 1)) + { + /* '*' can have two unsigned chars as operands */ + /* and an unsigned int as result. */ + if (compareType (operandType (IC_RESULT (ic)), + operandType (IC_RIGHT (ic))) == 1) + { + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + } + else + { + ic->op = CAST; + IC_LEFT (ic) = operandFromOperand (IC_LEFT (ic)); + IC_LEFT (ic)->type = TYPE; + IC_LEFT (ic)->isLiteral = 0; + setOperandType (IC_LEFT (ic), operandType (IC_RESULT (ic))); + } + return; + } + if (isEqualVal (OP_VALUE (IC_LEFT (ic)), -1)) + { + /* convert -1 * x to -x */ + ic->op = UNARYMINUS; + IC_LEFT (ic) = IC_RIGHT (ic); + IC_RIGHT (ic) = NULL; + return; + } + } + + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 1)) + { + /* '*' can have two unsigned chars as operands */ + /* and an unsigned int as result. */ + if (compareType (operandType (IC_RESULT (ic)), + operandType (IC_LEFT (ic))) == 1) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + } + else + { + operand *op; + + ic->op = CAST; + op = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = operandFromOperand (op); + IC_LEFT (ic)->type = TYPE; + IC_LEFT (ic)->isLiteral = 0; + setOperandType (IC_LEFT (ic), operandType (IC_RESULT (ic))); + } + return; + } + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), -1)) + { + /* '*' can have two unsigned chars as operands */ + /* and an unsigned int as result. */ + if (IS_INTEGRAL (operandType (IC_LEFT (ic)))) + { + if ((getSize (operandType (IC_LEFT (ic))) < (unsigned int) INTSIZE) && + (getSize (operandType (IC_LEFT (ic))) < getSize (operandType (IC_RESULT (ic))))) + { + operand * op; + iCode * newic; + /* Widen to int. */ + op = operandFromOperand (IC_RESULT (ic)); + op->type = TYPE; + setOperandType (op, INTTYPE); + newic = newiCode (CAST, op, IC_LEFT (ic)); + IC_RESULT (newic) = newiTempOperand (INTTYPE, TRUE); + addiCodeToeBBlock (ebp, newic, ic); + IC_LEFT (ic) = IC_RESULT (newic); + } + } + /* convert x * -1 to -x */ + ic->op = UNARYMINUS; + IC_RIGHT (ic) = NULL; + return; + } + } + break; + case '/': + /* if division by self then 1 */ + if (IC_LEFT (ic)->key == IC_RIGHT (ic)->key) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (1); + IC_LEFT (ic) = NULL; + IC_RESULT (ic) = operandFromOperand (IC_RESULT (ic)); + IC_RESULT (ic)->isaddr = 0; + return; + } + /* if this is a division then check if left is zero */ + /* then change it to an assignment */ + if (IS_OP_LITERAL (IC_LEFT (ic)) && isEqualVal (OP_VALUE (IC_LEFT (ic)), 0)) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* if this is a division then check if right */ + /* is one then change it to an assignment */ + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 1)) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), -1)) + { + /* '/' can have two unsigned chars as operands */ + /* and an unsigned int as result. */ + if (IS_INTEGRAL (operandType (IC_LEFT (ic)))) + { + if ((getSize (operandType (IC_LEFT (ic))) < (unsigned int) INTSIZE) && + (getSize (operandType (IC_LEFT (ic))) < getSize (operandType (IC_RESULT (ic))))) + { + operand * op; + iCode * newic; + /* Widen to int. */ + op = operandFromOperand (IC_RESULT (ic)); + op->type = TYPE; + setOperandType (op, INTTYPE); + newic = newiCode (CAST, op, IC_LEFT (ic)); + IC_RESULT (newic) = newiTempOperand (INTTYPE, TRUE); + addiCodeToeBBlock (ebp, newic, ic); + IC_LEFT (ic) = IC_RESULT (newic); + } + } + /* convert x / -1 to -x */ + ic->op = UNARYMINUS; + IC_RIGHT (ic) = NULL; + return; + } + } + break; + /* if both are the same for an comparison operators */ + case EQ_OP: + case LE_OP: + case GE_OP: + if (isOperandEqual (IC_LEFT (ic), IC_RIGHT (ic)) && + !IS_OP_VOLATILE (IC_LEFT (ic))) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (1); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + } + break; + case NE_OP: + case '>': + case '<': + if (isOperandEqual (IC_LEFT (ic), IC_RIGHT (ic)) && + !IS_OP_VOLATILE (IC_LEFT (ic))) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (0); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + } + break; + case CAST: + { + sym_link *otype = operandType(IC_RIGHT(ic)); + sym_link *ctype = operandType(IC_LEFT(ic)); + /* if this is a cast of a literal value */ + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + double litval = operandLitValue (IC_RIGHT (ic)); + if (IS_GENPTR(ctype) && IS_PTR(otype)) + { + unsigned long gpVal = 0; + const char *name = IS_SYMOP (IC_RESULT (ic)) ? OP_SYMBOL (IC_RESULT (ic))->name : NULL; + if (IS_FUNCPTR(otype)) + gpVal = pointerTypeToGPByte (DCL_TYPE (otype->next), NULL, name); + if (!IS_GENPTR(otype)) + gpVal = pointerTypeToGPByte (DCL_TYPE (otype), NULL, name); + gpVal <<= ((GPTRSIZE - 1) * 8); + gpVal |= (unsigned long)litval; + litval = gpVal; + } + ic->op = '='; + IC_RIGHT (ic) = operandFromValue (valCastLiteral (operandType (IC_LEFT (ic)), litval, litval)); + IC_LEFT (ic) = NULL; + SET_ISADDR (IC_RESULT (ic), 0); + } + /* if casting to the same */ + if (compareType (operandType (IC_RESULT (ic)), operandType (IC_RIGHT (ic))) == 1) + { + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_ISADDR (IC_RESULT (ic), 0); + } + } + break; + case '!': + if (IS_OP_LITERAL (IC_LEFT (ic))) + { + ic->op = '='; + IC_RIGHT (ic) = + (isEqualVal (OP_VALUE (IC_LEFT (ic)), 0) ? + operandFromLit (1) : operandFromLit (0)); + IC_LEFT (ic) = NULL; + SET_ISADDR (IC_RESULT (ic), 0); + } + break; + case BITWISEAND: + /* if both operands are equal */ + /* if yes turn it into assignment */ + if (isOperandEqual (IC_LEFT (ic), IC_RIGHT (ic))) + { + if (IS_OP_VOLATILE (IC_LEFT (ic))) + { + iCode *newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + } + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* swap literal to right ic */ + if (IS_OP_LITERAL (IC_LEFT (ic))) + { + operand *op; + + op = IC_LEFT (ic); + IC_LEFT (ic) = IC_RIGHT (ic); + IC_RIGHT (ic) = op; + } + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + unsigned val; + + /* if BITWISEAND then check if one of them is a zero */ + /* if yes turn it into 0 assignment */ + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + if (IS_OP_VOLATILE (IC_LEFT (ic))) + { + iCode *newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + } + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* if BITWISEAND then check if one of them is 0xff... */ + /* if yes turn it into assignment */ + if (IS_BOOLEAN (operandType (IC_RIGHT (ic)))) /* Special handling since _Bool is stored in 8 bits */ + goto boolcase; + switch (bitsForType (operandType (IC_RIGHT (ic)))) + { + case 1: + boolcase: + val = 0x01; + break; + case 8: + val = 0xff; + break; + case 16: + val = 0xffff; + break; + case 32: + val = 0xffffffff; + break; + default: + return; + } + if (((unsigned) double2ul (operandLitValue (IC_RIGHT (ic))) & val) == val) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + } + break; + case '|': + /* if both operands are equal */ + /* if yes turn it into assignment */ + if (isOperandEqual (IC_LEFT (ic), IC_RIGHT (ic))) + { + if (IS_OP_VOLATILE (IC_LEFT (ic))) + { + iCode *newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + } + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* swap literal to right ic */ + if (IS_OP_LITERAL (IC_LEFT (ic))) + { + operand *op; + + op = IC_LEFT (ic); + IC_LEFT (ic) = IC_RIGHT (ic); + IC_RIGHT (ic) = op; + } + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + unsigned val; + + /* if BITWISEOR then check if one of them is a zero */ + /* if yes turn it into assignment */ + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* if BITWISEOR then check if one of them is 0xff... */ + /* if yes turn it into 0xff... assignment */ + switch (bitsForType (operandType (IC_RIGHT (ic)))) + { + case 1: + val = 0x01; + break; + case 8: + val = 0xff; + break; + case 16: + val = 0xffff; + break; + case 32: + val = 0xffffffff; + break; + default: + return; + } + if (((unsigned) double2ul (operandLitValue (IC_RIGHT (ic))) & val) == val) + { + if (IS_OP_VOLATILE (IC_LEFT (ic))) + { + iCode *newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + } + ic->op = '='; + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + } + break; + case '^': + /* if both operands are equal */ + /* if yes turn it into 0 assignment */ + if (isOperandEqual (IC_LEFT (ic), IC_RIGHT (ic))) + { + if (IS_OP_VOLATILE (IC_LEFT (ic))) + { + iCode *newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + + newic = newiCode (DUMMY_READ_VOLATILE, NULL, IC_LEFT (ic)); + IC_RESULT (newic) = IC_LEFT (ic); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebp, newic, ic->next); + } + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (0); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + /* swap literal to right ic */ + if (IS_OP_LITERAL (IC_LEFT (ic))) + { + operand *op; + + op = IC_LEFT (ic); + IC_LEFT (ic) = IC_RIGHT (ic); + IC_RIGHT (ic) = op; + } + /* if XOR then check if one of them is a zero */ + /* if yes turn it into assignment */ + if (IS_OP_LITERAL (IC_RIGHT (ic))) + { + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 0)) + { + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + } + /* if XOR then check if one of them is a zero or one */ + /* if yes turn it into assignment or invert */ + if (IS_OP_LITERAL (IC_RIGHT (ic)) && + IS_BOOLEAN (operandType (IC_LEFT (ic))) && + IS_BOOLEAN (operandType (IC_RESULT (ic))) + ) + { + if (isEqualVal (OP_VALUE (IC_RIGHT (ic)), 1)) + { + ic->op = '!'; + IC_RIGHT (ic) = NULL; + return; + } + else + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (1); + IC_LEFT (ic) = NULL; + SET_RESULT_RIGHT (ic); + return; + } + } + break; + } + + return; +} + +#define OTHERS_PARM(s) (s->_isparm && !s->ismyparm) +/*-----------------------------------------------------------------*/ +/* updateSpillLocation - keeps track of register spill location */ +/*-----------------------------------------------------------------*/ +void +updateSpillLocation (iCode * ic, int induction) +{ + sym_link *setype; + + if (POINTER_SET (ic)) + return; + + if (ic->nosupdate) + return; + +#if 0 + /* for the form true_symbol := iTempNN */ + if (ASSIGN_ITEMP_TO_SYM (ic) && + !SPIL_LOC (IC_RIGHT (ic))) + { + setype = getSpec (operandType (IC_RESULT (ic))); + + if (!OP_SYMBOL(IC_RIGHT (ic))->noSpilLoc && + !IS_VOLATILE (setype) && + !IN_FARSPACE (SPEC_OCLS (setype)) && + !OTHERS_PARM (OP_SYMBOL (IC_RESULT (ic)))) + { + wassert(IS_SYMOP(IC_RESULT (ic))); + wassert(IS_SYMOP(IC_RIGHT (ic))); + SPIL_LOC (IC_RIGHT (ic)) = IC_RESULT (ic)->operand.symOperand; + } + } +#endif + +#if 0 /* this needs furthur investigation can save a lot of code */ + if (ASSIGN_SYM_TO_ITEMP(ic) && + !SPIL_LOC(IC_RESULT(ic))) + { + if (!OTHERS_PARM (OP_SYMBOL (IC_RIGHT (ic)))) + SPIL_LOC (IC_RESULT (ic)) = IC_RIGHT (ic)->operand.symOperand; + } +#endif + if (ASSIGN_ITEMP_TO_ITEMP (ic)) + { + if (!SPIL_LOC (IC_RIGHT (ic)) && + !bitVectBitsInCommon (OP_DEFS (IC_RIGHT (ic)), OP_USES (IC_RESULT (ic))) && + OP_SYMBOL (IC_RESULT (ic))->isreqv) + { + setype = getSpec (operandType (IC_RESULT (ic))); + + if (!OP_SYMBOL(IC_RIGHT (ic))->noSpilLoc && + !IS_VOLATILE (setype) && + !IN_FARSPACE (SPEC_OCLS (setype)) && + !OTHERS_PARM (OP_SYMBOL (IC_RESULT (ic)))) + { + SPIL_LOC (IC_RIGHT (ic)) = SPIL_LOC (IC_RESULT (ic)); + OP_SYMBOL (IC_RIGHT (ic))->prereqv = OP_SYMBOL (IC_RESULT (ic))->prereqv; + } + } + /* special case for inductions */ + if (induction && + OP_SYMBOL(IC_RIGHT(ic))->isreqv && + !OP_SYMBOL(IC_RESULT (ic))->noSpilLoc && + !SPIL_LOC(IC_RESULT(ic))) + { + SPIL_LOC (IC_RESULT (ic)) = SPIL_LOC (IC_RIGHT (ic)); + OP_SYMBOL (IC_RESULT (ic))->prereqv = OP_SYMBOL (IC_RIGHT (ic))->prereqv; + } + } +} + +/*-----------------------------------------------------------------*/ +/* setUsesDef - sets the uses def bitvector for a given operand */ +/*-----------------------------------------------------------------*/ +void +setUsesDefs (operand * op, bitVect * bdefs, + bitVect * idefs, bitVect ** oud) +{ + /* compute the definitions alive at this point */ + bitVect *adefs = bitVectUnion (bdefs, idefs); + + /* of these definitions find the ones that are */ + /* for this operand */ + adefs = bitVectInplaceIntersect (adefs, OP_DEFS (op)); + + /* these are the definitions that this operand can use */ + /* Nothing uses op->usesDefs, so why? EEP - 2018-06-10 */ + //op->usesDefs = adefs; + + /* the out defs is an union */ + *oud = bitVectInplaceUnion (*oud, adefs); + + /* If not assigning op->usesDefs, we can safely free adefs */ + freeBitVect(adefs); +} + +/*-----------------------------------------------------------------*/ +/* unsetDefsAndUses - clear this operation for the operands */ +/*-----------------------------------------------------------------*/ +void +unsetDefsAndUses (iCode * ic) +{ + if (ic->op == JUMPTABLE) + { + if (IS_SYMOP (IC_JTCOND (ic))) + bitVectUnSetBit (OP_USES (IC_JTCOND (ic)), ic->key); + return; + } + + /* take away this definition from the def chain of the */ + /* result & take away from use set of the operands */ + if (ic->op != IFX) + { + /* turn off def set */ + if (IS_SYMOP (IC_RESULT (ic))) + { + if (!POINTER_SET (ic)) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + else + bitVectUnSetBit (OP_USES (IC_RESULT (ic)), ic->key); + } + /* turn off the useSet for the operands */ + if (IS_SYMOP (IC_LEFT (ic))) + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + } + else + /* must be ifx turn off the use */ + if (IS_SYMOP (IC_COND (ic))) + bitVectUnSetBit (OP_USES (IC_COND (ic)), ic->key); +} + +/*-----------------------------------------------------------------*/ +/* ifxOptimize - changes ifx conditions if it can */ +/*-----------------------------------------------------------------*/ +void +ifxOptimize (iCode * ic, set * cseSet, + int computeOnly, + eBBlock * ebb, int *change, + ebbIndex * ebbi) +{ + operand *pdop; + symbol *label; + + /* if the condition can be replaced */ + if (!computeOnly) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_COND (ic), &pdop, 0); + if (pdop) + { + ReplaceOpWithCheaperOp(&IC_COND (ic), pdop); + (*change)++; + } + else if(ic->prev && /* Remove unnecessary casts */ + (ic->prev->op == '=' || ic->prev->op == CAST || ic->prev->op == '!') && IS_ITEMP (IC_RESULT (ic->prev)) && + IC_RESULT (ic->prev)->key == IC_COND (ic)->key && bitVectnBitsOn (OP_USES (IC_RESULT (ic->prev))) <= 1) + { + sym_link *type = operandType (IC_RESULT (ic->prev)); + if (ic->prev->op != CAST || IS_BOOL (type) || bitsForType (operandType (IC_RIGHT (ic->prev))) < bitsForType (type)) + { + if (!isOperandVolatile (ic->prev->op == '!' ? IC_LEFT (ic->prev) : IC_RIGHT (ic->prev), FALSE)) + { + if (ic->prev->op =='!') /* Invert jump logic */ + { + symbol *tmp = IC_TRUE (ic); + IC_TRUE (ic) = IC_FALSE (ic); + IC_FALSE (ic) = tmp; + } + bitVectUnSetBit (OP_USES (IC_COND (ic)), ic->key); + ReplaceOpWithCheaperOp(&IC_COND (ic), ic->prev->op == '!' ? IC_LEFT (ic->prev) : IC_RIGHT (ic->prev)); + (*change)++; + } +/* There's an optimization opportunity here, but OP_USES doesn't seem to be */ +/* initialized properly at this point. - EEP 2016-08-04 */ +#if 0 + else if (bitVectnBitsOn (OP_USES(IC_COND (ic))) == 1) + { + /* We can replace the iTemp with the original volatile symbol */ + /* but we must make sure the volatile symbol is still accessed */ + /* only once. */ + bitVectUnSetBit (OP_USES (IC_COND (ic)), ic->key); + ReplaceOpWithCheaperOp(&IC_COND (ic), IC_RIGHT (ic->prev)); + (*change)++; + /* Make previous assignment an assignment to self. */ + /* killDeadCode() will eliminiate it. */ + IC_RIGHT (ic->prev) = IC_RESULT (ic->prev); + IC_LEFT (ic->prev) = NULL; + ic->prev->op = '='; + } +#endif + } + } + } + + /* if the conditional is a literal then */ + if (IS_OP_LITERAL (IC_COND (ic))) + { + if ((operandLitValue (IC_COND (ic)) != 0.0) && IC_TRUE (ic)) + { + /* change to a goto */ + ic->op = GOTO; + IC_LABEL (ic) = IC_TRUE (ic); + (*change)++; + } + else + { + if (!operandLitValue (IC_COND (ic)) && IC_FALSE (ic)) + { + ic->op = GOTO; + IC_LABEL (ic) = IC_FALSE (ic); + (*change)++; + } + else + { + /* then kill this if condition */ + remiCodeFromeBBlock (ebb, ic); + } + } + + /* now we need to recompute the control flow */ + /* since the control flow has changed */ + /* this is very expensive but it does not happen */ + /* too often, if it does happen then the user pays */ + /* the price */ + computeControlFlow (ebbi); + werrorfl (ic->filename, ic->lineno, W_CONTROL_FLOW); + return; + } + + /* if there is only one successor and that successor + is the same one we are conditionally going to then + we can remove this conditional statement */ + label = (IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic)); + if (elementsInSet (ebb->succList) == 1 && + isinSet (ebb->succList, eBBWithEntryLabel (ebbi, label))) + { + werrorfl (ic->filename, ic->lineno, W_CONTROL_FLOW); + if (IS_OP_VOLATILE (IC_COND (ic))) + { + IC_RIGHT (ic) = IC_COND (ic); + IC_LEFT (ic) = NULL; + IC_RESULT (ic) = NULL; + ic->op = DUMMY_READ_VOLATILE; + } + else + { + remiCodeFromeBBlock (ebb, ic); + computeControlFlow (ebbi); + return; + } + } + + /* if it remains an IFX then update the use Set */ + if (ic->op == IFX) + { + OP_USES(IC_COND (ic))=bitVectSetBit (OP_USES (IC_COND (ic)), ic->key); + setUsesDefs (IC_COND (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + } + else if (ic->op == DUMMY_READ_VOLATILE) + { + OP_USES(IC_RIGHT (ic))=bitVectSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + setUsesDefs (IC_RIGHT (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + } + return; +} + +/*-----------------------------------------------------------------*/ +/* diCodeForSym - finds the definiting instruction for a symbol */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (diCodeForSym) +{ + cseDef *cdp = item; + V_ARG (operand *, sym); + V_ARG (iCode **, dic); + + /* if already found */ + if (*dic) + return 0; + + /* if not if this is the defining iCode */ + if (sym->key == cdp->key) + { + *dic = cdp->diCode; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* constFold - does some constant folding */ +/*-----------------------------------------------------------------*/ +int +constFold (iCode * ic, set * cseSet) +{ + iCode *dic = NULL; + iCode *ldic = NULL; + /* this routine will change + a = b + 10; + c = a + 10; + to + c = b + 20; */ + + /* deal with only + & - */ + if (ic->op != '+' && + ic->op != '-' && + ic->op != BITWISEAND) + return 0; + + /* check if operation with a literal */ + if (!IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + /* check if we can find a definition for the + left hand side */ + if (!(applyToSet (cseSet, diCodeForSym, IC_LEFT (ic), &dic))) + return 0; + + if (ic->op == BITWISEAND) /* Optimize out bitwise and of comparion results */ + { + /* check that this results in 0 or 1 only */ + if(dic->op != EQ_OP && dic->op != NE_OP && dic->op != LE_OP && dic->op != GE_OP && dic->op != '<' && dic->op != '>' && dic->op != '!') + return 0; + + IC_RIGHT (ic) = (operandLitValueUll (IC_RIGHT (ic)) & 1) ? IC_LEFT (ic) : operandFromLit (0); + + ic->op = '='; + IC_LEFT (ic) = 0; + + return 1; + } + + /* check that this is also a +/- */ + if (dic->op != '+' && dic->op != '-') + return 0; + + /* with a literal */ + if (!IS_OP_LITERAL (IC_RIGHT (dic))) + return 0; + + /* find the definition of the left operand + of dic.then check if this defined with a + get_pointer return 0 if the pointer size is + less than 2 (MCS51 specific) */ + if (!(applyToSet (cseSet, diCodeForSym, IC_LEFT (dic), &ldic))) + return 0; + + if (POINTER_GET (ldic) && getSize (operandType (IC_LEFT (ldic))) <= 1) + return 0; + + /* it is if the operations are the same */ + /* the literal parts need to be added */ + IC_LEFT (ic) = operandFromOperand (IC_LEFT (dic)); + if (ic->op == dic->op) + IC_RIGHT (ic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) + + operandLitValue (IC_RIGHT (dic))); + else + IC_RIGHT (ic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) - + operandLitValue (IC_RIGHT (dic))); + + if (IS_ITEMP (IC_RESULT (ic))) + { + SPIL_LOC (IC_RESULT (ic)) = NULL; + OP_SYMBOL(IC_RESULT (ic))->noSpilLoc = 1; + } + + return 1; +} + +/* Remove casts to bool from results of logical operations. */ +static int +boolCast (iCode * ic, set * cseSet) +{ + iCode *dic = NULL; + + /* Only casts to booleans are optimized away. */ + if (ic->op != CAST || !IS_BOOL ( operandType (IC_RESULT (ic)))) + return 0; + + /* Find the definition for the right hand side. */ + if (!(applyToSet (cseSet, diCodeForSym, IC_RIGHT (ic), &dic))) + return 0; + + /* Check that this is a logic op. */ + switch (dic->op) + { + case '!': + case '<': + case '>': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + case GETHBIT: + case GETABIT: + break; + case BITWISEAND: + if (IS_BOOL (operandType (IC_LEFT (dic))) || IS_BOOL (operandType (IC_RIGHT (dic)))) + break; + if (IS_OP_LITERAL (IC_RIGHT (dic)) && operandLitValue (IC_RIGHT (dic)) == 1) + break; + default: + return 0; + } + + /* Replace cast by assignment. */ + ic->op = '='; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* deleteGetPointers - called when a pointer is passed as parm */ +/* will delete from cseSet all get pointers computed from this */ +/* pointer. A simple ifOperandsHave is not good enough here */ +/*-----------------------------------------------------------------*/ +static void +deleteGetPointers (set ** cseSet, set ** pss, operand * op, eBBlock * ebb) +{ + set *compItems = NULL; + cseDef *cdp; + operand *cop; + int changes; + + /* easy return */ + if (!*cseSet && !*pss) + return; + + addSet (&compItems, op); + + /* Recursively find all items computed from this operand . + This done fairly simply go thru the list and find + those that are computed by arthimetic with these + ops */ + /* Also check for those computed from our computed + list . This will take care of situations like + iTemp1 = iTemp0 + 8; + iTemp2 = iTemp1 + 8; */ + do + { + changes = 0; + for (cdp = setFirstItem (*cseSet); cdp; cdp = setNextItem (*cseSet)) + { + if (IS_ARITHMETIC_OP (cdp->diCode) || POINTER_GET(cdp->diCode)) + { + if (isinSetWith (compItems, (void*)IC_LEFT (cdp->diCode), + (insetwithFunc)isOperandEqual) || + isinSetWith (compItems, (void*)IC_RIGHT (cdp->diCode), + (insetwithFunc)isOperandEqual)) + { + if (!isinSetWith (compItems, (void*)IC_RESULT (cdp->diCode), + (insetwithFunc)isOperandEqual)) + { + addSet (&compItems, IC_RESULT (cdp->diCode)); + changes++; + } + } + } + } + } + while (changes); + + /* now for the computed items */ + for (cop = setFirstItem (compItems); cop; cop = setNextItem (compItems)) + { + ebb->ptrsSet = bitVectSetBit (ebb->ptrsSet, cop->key); + deleteItemIf (cseSet, ifPointerGet, cop); + deleteItemIf (cseSet, ifDefSymIsX, cop); + deleteItemIf (pss, ifPointerSet, cop); + } + deleteSet (&compItems); +} + +/*-----------------------------------------------------------------*/ +/* delGetPointerSucc - delete get pointer from inExprs of succ with */ +/* dfnum > supplied */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (delGetPointerSucc) +{ + eBBlock *ebp = item; + V_ARG (operand *, op); + V_ARG (int, dfnum); + + if (ebp->visited) + return 0; + + ebp->visited = 1; + if (ebp->dfnum > dfnum) + { + deleteItemIf (&ebp->inExprs, ifPointerGet, op); + } + + return applyToSet (ebp->succList, delGetPointerSucc, op, dfnum); +} + +/*-----------------------------------------------------------------*/ +/* fixUpTypes - KLUGE HACK fixup a lowering problem */ +/*-----------------------------------------------------------------*/ +static void +fixUpTypes (iCode * ic) +{ + sym_link *t1 = operandType (IC_LEFT (ic)), *t2; + + /* if (TARGET_IS_DS390) */ + if (options.model == MODEL_FLAT24) + { + /* hack-o-matic! */ + return; + } + + /* for pointer_gets if the types of result & left r the + same then change it type of result to next */ + if (IS_PTR (t1) && + compareType (t2 = operandType (IC_RESULT (ic)), t1) == 1) + { + setOperandType (IC_RESULT (ic), t2->next); + } +} + +/*-----------------------------------------------------------------*/ +/* isSignedOp - will return 1 if sign is important to operation */ +/*-----------------------------------------------------------------*/ +static int isSignedOp (iCode *ic) +{ + switch (ic->op) { + case '!': + case '~': + case UNARYMINUS: + case IPUSH: + case IPOP: + case CALL: + case PCALL: + case RETURN: + case '+': + case '-': + case EQ_OP: + case AND_OP: + case OR_OP: + case '^': + case '|': + case BITWISEAND: + case INLINEASM: + case LEFT_OP: + case GET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case RECEIVE: + case SEND: + return 0; + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case NE_OP: + case RRC: + case RLC: + case GETHBIT: + case GETABIT: + case GETBYTE: + case GETWORD: + case RIGHT_OP: + case CAST: + case ARRAYINIT: + return 1; + default: + return 0; + } + } + +#if 0 +static void +dumpCseSet(set *cseSet) +{ + while (cseSet) + { + cseDef *item=cseSet->item; + printf("->"); + printOperand (item->sym, NULL); + printf(" "); + piCode (item->diCode, NULL); + cseSet = cseSet->next; + } +} +#endif + +/*-----------------------------------------------------------------*/ +/* cseBBlock - common subexpression elimination for basic blocks */ +/* this is the hackiest kludgiest routine in the whole */ +/* system. also the most important, since almost all */ +/* data flow related information is computed by it */ +/*-----------------------------------------------------------------*/ +int +cseBBlock (eBBlock * ebb, int computeOnly, ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + set *cseSet; + set *setnode; + iCode *ic; + int change = 0; + int i; + set *ptrSetSet = NULL; + cseDef *expr; + int replaced; + int recomputeDataFlow = 0; + + /* if this block is not reachable */ + if (ebb->noPath) + { + for (ic = ebb->sch; ic; ic = ic->next) + if (!SKIP_IC2 (ic)) + unsetDefsAndUses (ic); + return 0; + } + + /* Mark incoming subexpressions as non-local */ + for (setnode = ebb->inExprs; setnode; setnode = setnode->next) + { + expr = (cseDef *)setnode->item; + expr->nonLocalCSE = 1; + } + /* set of common subexpressions */ + cseSet = setFromSet (ebb->inExprs); + + + /* these will be computed by this routine */ + freeBitVect(ebb->outDefs); ebb->outDefs = NULL; + freeBitVect(ebb->defSet); ebb->defSet = NULL; + freeBitVect(ebb->usesDefs); ebb->usesDefs = NULL; + freeBitVect(ebb->ptrsSet); ebb->ptrsSet = NULL; + deleteSet(&ebb->addrOf); + freeBitVect(ebb->ldefs); ebb->ldefs = NULL; + ebb->outDefs = bitVectCopy (ebb->inDefs); + bitVectDefault = iCodeKey; + ebb->defSet = newBitVect (iCodeKey); + ebb->usesDefs = newBitVect (iCodeKey); + + /* for all the instructions in this block do */ + for (ic = ebb->sch; ic; ic = ic->next) + { + iCode *pdic; + operand *pdop; + iCode *defic; + int checkSign ; + + ic->eBBlockNum = ebb->bbnum; + + if (SKIP_IC2 (ic)) + continue; + + /* if this is an assignment from true symbol + to a temp then do pointer post inc/dec optimization */ + if (ic->op == '=' && !POINTER_SET (ic) && + IS_PTR (operandType (IC_RESULT (ic)))) + { + ptrPostIncDecOpt (ic, ebb); + } + + /* clear the def & use chains for the operands involved */ + /* in this operation since it can change due to opts */ + unsetDefsAndUses (ic); + + if (ic->op == PCALL || ic->op == CALL || ic->op == RECEIVE) + { + /* add to defSet of the symbol */ + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + /* add to the definition set of this block */ + ebb->defSet = bitVectSetBit (ebb->defSet, ic->key); + ebb->ldefs = bitVectSetBit (ebb->ldefs, ic->key); + ebb->outDefs = bitVectCplAnd (ebb->outDefs, OP_DEFS (IC_RESULT (ic))); + setUsesDefs (IC_RESULT (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + /* delete global variables from the cseSet + since they can be modified by the function call */ + destructItemIf (&cseSet, freeLocalCseDef, ifDefGlobal); + + /* and also iTemps derived from globals */ + destructItemIf (&cseSet, freeLocalCseDef, ifFromGlobal); + + /* Delete iTemps derived from symbols whose address */ + /* has been taken */ + destructItemIf (&cseSet, freeLocalCseDef, ifFromAddrTaken); + + /* delete all getpointer iCodes from cseSet, this should + be done only for global arrays & pointers but at this + point we don't know if globals, so to be safe do all */ + destructItemIf (&cseSet, freeLocalCseDef, ifAnyGetPointer); + + /* can't cache pointer set/get operations across a call */ + deleteSet (&ptrSetSet); + } + + /* for pcall & ipush we need to add to the useSet */ + if ((ic->op == PCALL || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == SEND) && + IS_SYMOP (IC_LEFT (ic))) + { + /* check if they can be replaced */ + if (!computeOnly) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_LEFT (ic), &pdop, 0); + if (pdop) + ReplaceOpWithCheaperOp(&IC_LEFT(ic), pdop); + } + /* the lookup could have changed it */ + if (IS_SYMOP (IC_LEFT (ic))) + { + OP_USES(IC_LEFT (ic))= + bitVectSetBit (OP_USES (IC_LEFT (ic)), ic->key); + setUsesDefs (IC_LEFT (ic), ebb->defSet, + ebb->outDefs, &ebb->usesDefs); + } + + /* if we are sending a pointer as a parameter + then kill all cse since the pointed to item + might be changed in the function being called */ + if ((ic->op == IPUSH || ic->op == SEND) && + IS_PTR (operandType (IC_LEFT (ic)))) + { + deleteGetPointers (&cseSet, &ptrSetSet, IC_LEFT (ic), ebb); + ebb->ptrsSet = bitVectSetBit (ebb->ptrsSet, IC_LEFT (ic)->key); + for (i = 0; i < count; ebbs[i++]->visited = 0); + applyToSet (ebb->succList, delGetPointerSucc, + IC_LEFT (ic), ebb->dfnum); + } + continue; + } + + /* if jumptable then mark the usage */ + if (ic->op == JUMPTABLE) + { + if (IS_SYMOP (IC_JTCOND (ic))) + { + OP_USES(IC_JTCOND (ic)) = + bitVectSetBit (OP_USES (IC_JTCOND (ic)), ic->key); + setUsesDefs (IC_JTCOND (ic), ebb->defSet, + ebb->outDefs, &ebb->usesDefs); + } + continue; + } + + if (SKIP_IC (ic)) + continue; + + if (!computeOnly) + { + /* do some algebraic optimizations if possible */ + algebraicOpts (ic, ebb); + while (constFold (ic, cseSet)); + while (boolCast (ic, cseSet)); + } + + /* small kludge */ + if (POINTER_GET (ic)) + { + if (!IS_PTR (operandType (IC_LEFT (ic)))) + { + setOperandType (IC_LEFT (ic), + aggrToPtr (operandType (IC_LEFT (ic)), FALSE)); + IC_LEFT (ic)->aggr2ptr = 0; + fixUpTypes (ic); + } + else if (IC_LEFT (ic)->aggr2ptr == 1) + {/* band aid for kludge */ + setOperandType (IC_LEFT (ic), + aggrToPtr (operandType (IC_LEFT (ic)), TRUE)); + IC_LEFT (ic)->aggr2ptr++; + fixUpTypes (ic); + } + } + + if (POINTER_SET (ic)) + { + if (!IS_PTR (operandType (IC_RESULT (ic)))) + { + setOperandType (IC_RESULT (ic), + aggrToPtr (operandType (IC_RESULT (ic)), FALSE)); + IC_RESULT (ic)->aggr2ptr = 0; + } + else if (IC_RESULT (ic)->aggr2ptr == 1) + {/* band aid for kludge */ + setOperandType (IC_RESULT (ic), + aggrToPtr (operandType (IC_RESULT (ic)), TRUE)); + IC_RESULT (ic)->aggr2ptr++; + } + } + + /* if this is a condition statement then */ + /* check if the condition can be replaced */ + if (ic->op == IFX) + { + ifxOptimize (ic, cseSet, computeOnly, ebb, &change, ebbi); + continue; + } + + /* if the assignment & result is a temp */ + /* see if we can replace it */ + if (!computeOnly && ic->op == '=') + { + /* update the spill location for this */ + updateSpillLocation (ic, 0); + + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) && + !(IS_BITFIELD (OP_SYMBOL (IC_RESULT (ic))->etype))) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_RESULT (ic), &pdop, 0); + if (pdop && !computeOnly && IS_ITEMP (pdop)) + { + ReplaceOpWithCheaperOp (&IC_RESULT(ic), pdop); + if (!IS_PTR (operandType (IC_RESULT (ic)))) + { + setOperandType (IC_RESULT (ic), + aggrToPtr (operandType (IC_RESULT (ic)), FALSE)); + } + } + } + } + + checkSign = isSignedOp(ic); + replaced = 0; + + /* do the operand lookup i.e. for both the */ + /* right & left operand : check the cseSet */ + /* to see if they have been replaced if yes */ + /* then replace them with those from cseSet */ + /* left operand */ + /* and left is a symbol */ + if (!computeOnly && + ic->op != ADDRESS_OF && + IS_SYMOP (IC_LEFT (ic)) && + !IS_BITFIELD (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_LEFT (ic), &pdop, checkSign); + if (pdop) + { + if (POINTER_GET (ic)) + { + if (IS_ITEMP (pdop) || IS_OP_LITERAL (pdop)) + { + /* some non dominating block does POINTER_SET with + this variable .. unsafe to remove any POINTER_GETs */ + if (bitVectBitValue (ebb->ndompset, IC_LEFT (ic)->key)) + ebb->ptrsSet = bitVectSetBit (ebb->ptrsSet, pdop->key); + ReplaceOpWithCheaperOp (&IC_LEFT (ic), pdop); + change = replaced = 1; + } + /* check if there is a pointer set + for the same pointer visible if yes + then change this into an assignment */ + pdop = NULL; + if (applyToSetFTrue (cseSet, findPointerSet, IC_LEFT (ic), &pdop, IC_RESULT (ic)) && + !bitVectBitValue (ebb->ptrsSet, pdop->key)) + { + ic->op = '='; + IC_LEFT (ic) = NULL; + ReplaceOpWithCheaperOp (&IC_RIGHT (ic), pdop); + SET_ISADDR (IC_RESULT (ic), 0); + replaced = 1; + } + } + else + { + ReplaceOpWithCheaperOp (&IC_LEFT (ic), pdop); + change = replaced = 1; + } + } + } + + /* right operand */ + if (!computeOnly && IS_SYMOP (IC_RIGHT (ic))) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_RIGHT (ic), &pdop, checkSign); + if (pdop) + { + ReplaceOpWithCheaperOp (&IC_RIGHT (ic), pdop); + change = replaced = 1; + } + } + + if (!computeOnly && + POINTER_SET (ic) && + IS_SYMOP (IC_RESULT (ic)) && + !IS_BITFIELD (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + pdop = NULL; + applyToSetFTrue (cseSet, findCheaperOp, IC_RESULT (ic), &pdop, checkSign); + if (pdop) + { + ReplaceOpWithCheaperOp (&IC_RESULT (ic), pdop); + change = 1; + } + } + + /* if left or right changed then do algebraic */ + if (!computeOnly && change) + { + algebraicOpts (ic, ebb); + while (constFold (ic, cseSet)); + while (boolCast (ic, cseSet)); + } + + /* if after all this it becomes an assignment to self + then delete it and continue */ + if (ASSIGNMENT_TO_SELF (ic) && !isOperandVolatile (IC_RIGHT(ic), FALSE)) + { + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + remiCodeFromeBBlock (ebb, ic); + continue; + } + + /* now we will check to see if the entire */ + /* operation has been performed before */ + /* and is available */ + /* don't do assignments they will be killed */ + /* by dead code elimination if required do */ + /* it only if result is a temporary */ + pdic = NULL; + if (!(POINTER_GET (ic) && + (IS_BITFIELD (OP_SYMBOL (IC_RESULT (ic))->etype) || + isOperandVolatile (IC_LEFT (ic), TRUE) || IS_VOLATILE (operandType (IC_LEFT (ic))->next) || + bitVectBitValue (ebb->ndompset, IC_LEFT (ic)->key))) && + !ASSIGNMENT (ic) && + IS_ITEMP (IC_RESULT (ic)) && + !computeOnly) + { + applyToSet (cseSet, findPrevIc, ic, &pdic); + if (pdic && compareType (operandType (IC_RESULT (pdic)), + operandType (IC_RESULT (ic))) != 1) + { + pdic = NULL; + } + if (pdic && port->cseOk && (*port->cseOk)(ic, pdic) == 0) + { + pdic = NULL; + } + } + + /* Alternate code */ + if (pdic && IS_ITEMP (IC_RESULT (ic))) + { + if (POINTER_GET (ic) && bitVectBitValue (ebb->ptrsSet, IC_LEFT (ic)->key)) + { + /* Mmm, found an equivalent pointer get at a lower level. + This could be a loop however with the same pointer set + later on */ + } + else + { + /* if previous definition found change this to an assignment */ + ic->op = '='; + IC_LEFT (ic) = NULL; + IC_RIGHT (ic) = operandFromOperand (IC_RESULT (pdic)); + SET_ISADDR (IC_RESULT (ic), 0); + SET_ISADDR (IC_RIGHT (ic), 0); + } + } + + if (!(POINTER_SET (ic)) && IC_RESULT (ic)) + { + cseDef *csed; + destructItemIf (&cseSet, freeLocalCseDef, ifDefSymIsX, IC_RESULT (ic)); + csed = newCseDef (IC_RESULT (ic), ic); + updateCseDefAncestors (csed, cseSet); + addSetHead (&cseSet, csed); + } + defic = ic; + + /* if assignment to a parameter which is not + mine and type is a pointer then delete + pointerGets to take care of aliasing */ + if (ASSIGNMENT (ic) && + IS_SYMOP (IC_RESULT (ic)) && + OTHERS_PARM (OP_SYMBOL (IC_RESULT (ic))) && + IS_PTR (operandType (IC_RESULT (ic)))) + { + deleteGetPointers (&cseSet, &ptrSetSet, IC_RIGHT (ic), ebb); + for (i = 0; i < count; ebbs[i++]->visited = 0); + applyToSet (ebb->succList, delGetPointerSucc, IC_RIGHT (ic), ebb->dfnum); + ebb->ptrsSet = bitVectSetBit (ebb->ptrsSet, IC_RIGHT (ic)->key); + } + + /* if this is a pointerget then see if we can replace + this with a previously assigned pointer value */ + if (POINTER_GET (ic) && + !(IS_BITFIELD (OP_SYMBOL (IC_RESULT (ic))->etype) || + isOperandVolatile (IC_LEFT (ic), TRUE))) + { + pdop = NULL; + applyToSet (ptrSetSet, findPointerSet, IC_LEFT (ic), &pdop, IC_RESULT (ic)); + /* if we find it then locally replace all + references to the result with what we assigned */ + if (pdop) + { + replaceAllSymBySym (ic->next, IC_RESULT (ic), pdop, &ebb->ndompset); + } + } + + /* delete from the cseSet anything that has */ + /* operands matching the result of this */ + /* except in case of pointer access */ + if (!(POINTER_SET (ic)) && IS_SYMOP (IC_RESULT (ic))) + { + destructItemIf (&cseSet, freeLocalCseDef, ifOperandsHave, IC_RESULT (ic)); + deleteItemIf (&ptrSetSet, ifOperandsHave, IC_RESULT (ic)); + /* delete any previous definitions */ + ebb->defSet = bitVectCplAnd (ebb->defSet, OP_DEFS (IC_RESULT (ic))); + + /* Until pointer tracking is complete, by conservative and delete all */ + /* pointer accesses that might alias this symbol. */ + if (isOperandGlobal (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + memmap *map = SPEC_OCLS (getSpec (operandType (IC_RESULT (ic)))); + destructItemIf (&cseSet, freeLocalCseDef, ifAnyUnrestrictedGetPointer, map->ptrType); + deleteItemIf (&ptrSetSet, ifAnyUnrestrictedSetPointer, map->ptrType); + } + } + + /* add the left & right to the defUse set */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic))) + { + OP_USES (IC_LEFT (ic)) = + bitVectSetBit (OP_USES (IC_LEFT (ic)), ic->key); + setUsesDefs (IC_LEFT (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + } + + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic))) + { + OP_USES (IC_RIGHT (ic)) = + bitVectSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + setUsesDefs (IC_RIGHT (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + } + + /* for the result it is special case, put the result */ + /* in the defuseSet if it is a pointer or array access */ + if (POINTER_SET (defic) && + (IS_SYMOP (IC_RESULT (ic)) || IS_OP_LITERAL (IC_RESULT (ic)))) + { + sym_link *ptype = operandType (IC_RESULT (ic)); + + if (IS_SYMOP (IC_RESULT (ic))) + { + OP_USES (IC_RESULT (ic)) = + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + setUsesDefs (IC_RESULT (ic), ebb->defSet, ebb->outDefs, &ebb->usesDefs); + } + deleteItemIf (&cseSet, ifPointerGet, IC_RESULT (ic)); + ebb->ptrsSet = bitVectSetBit (ebb->ptrsSet, IC_RESULT (ic)->key); + /* delete from inexpressions of all successors which + have dfNum > than this block */ + for (i = 0; i < count; ebbs[i++]->visited = 0); + applyToSet (ebb->succList, delGetPointerSucc, IC_RESULT (ic), ebb->dfnum); + + /* delete from cseSet all other pointer sets + for this operand */ + deleteItemIf (&ptrSetSet, ifPointerSet, IC_RESULT (ic)); + /* add to the local pointerset set */ + addSetHead (&ptrSetSet, newCseDef (IC_RESULT (ic), ic)); + + /* A write via a non-restrict pointer may modify a global */ + /* variable used by this function, so delete them */ + /* and any derived symbols from cseSet. */ + if (!IS_PTR_RESTRICT (ptype)) + { + destructItemIf (&cseSet, freeLocalCseDef, ifDefGlobalAliasableByPtr, DCL_TYPE(ptype)); + destructItemIf (&cseSet, freeLocalCseDef, ifFromGlobalAliasableByPtr, DCL_TYPE(ptype)); + } + + /* This could be made more specific for better optimization, but */ + /* for safety, delete anything this write may have modified. */ + destructItemIf (&cseSet, freeLocalCseDef, ifFromAddrTaken); + destructItemIf (&cseSet, freeLocalCseDef, ifAnyGetPointer); + } + else + { + /* add the result to definition set */ + if (IS_SYMOP (IC_RESULT (ic))) + { + OP_DEFS(IC_RESULT (ic))= + bitVectSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + ebb->defSet = bitVectSetBit (ebb->defSet, ic->key); + ebb->outDefs = bitVectCplAnd (ebb->outDefs, OP_DEFS (IC_RESULT (ic))); + ebb->ldefs = bitVectSetBit (ebb->ldefs, ic->key); + } + } + + /* if this is an addressof instruction then */ + /* put the symbol in the address of list & */ + /* delete it from the cseSet */ + if (defic->op == ADDRESS_OF) + { + addSetHead (&ebb->addrOf, IC_LEFT (ic)); + destructItemIf (&cseSet, freeLocalCseDef, ifDefSymIsX, IC_LEFT (ic)); + } + + /* If this was previously in the out expressions in the */ + /* original form, it might need to be killed by another block */ + /* in the new form if we have replaced operands, so recompute */ + /* the data flow after we finish this block */ + if (replaced && ifDiCodeIs (ebb->outExprs, ic)) + recomputeDataFlow = 1; + } + + for (expr=setFirstItem (ebb->inExprs); expr; expr=setNextItem (ebb->inExprs)) + if (!isinSetWith (cseSet, expr, isCseDefEqual) && + !isinSetWith (ebb->killedExprs, expr, isCseDefEqual)) + { + addSetHead (&ebb->killedExprs, expr); + } + + deleteSet (&ptrSetSet); + deleteSet (&ebb->outExprs); + ebb->outExprs = cseSet; + ebb->outDefs = bitVectInplaceUnion (ebb->outDefs, ebb->defSet); + ebb->ptrsSet = bitVectInplaceUnion (ebb->ptrsSet, ebb->inPtrsSet); + + for (setnode = ebb->outExprs; setnode; setnode = setnode->next) + { + expr = (cseDef *)setnode->item; + expr->nonLocalCSE = 1; + } + + if (recomputeDataFlow) + computeDataFlow (ebbi); + + return change; +} + +/*------------------------------------------------------------------*/ +/* cseAllBlocks - will sequentially go thru & do cse for all blocks */ +/*------------------------------------------------------------------*/ +int +cseAllBlocks (ebbIndex * ebbi, int computeOnly) +{ + eBBlock ** ebbs = ebbi->dfOrder; + int count = ebbi->count; + int i; + int change = 0; + + /* if optimization turned off */ + + for (i = 0; i < count; i++) + change += cseBBlock (ebbs[i], computeOnly, ebbi); + + return change; +} + + +/*------------------------------------------------------------------*/ +/* freeCSEdata - free data created by cseBBlock */ +/*------------------------------------------------------------------*/ +void +freeCSEdata (eBBlock * ebb) +{ + set * s; + + /* We should really free the cseDefs too, but I haven't */ + /* found a good way to do this yet. For the moment, at */ + /* least free up the associated bitVects - EEP */ + for (s = ebb->outExprs; s; s = s->next) + { + cseDef *cdp = s->item; + if (!cdp) continue; + if (cdp->ancestors) + { + freeBitVect (cdp->ancestors); + cdp->ancestors = NULL; + } + } + + deleteSet (&ebb->inExprs); + deleteSet (&ebb->outExprs); + deleteSet (&ebb->killedExprs); + + freeBitVect (ebb->inDefs); + freeBitVect (ebb->outDefs); + freeBitVect (ebb->defSet); + freeBitVect (ebb->ldefs); + freeBitVect (ebb->usesDefs); + freeBitVect (ebb->ptrsSet); + freeBitVect (ebb->inPtrsSet); + freeBitVect (ebb->ndompset); + deleteSet (&ebb->addrOf); + +} diff --git a/src/SDCCcse.h b/src/SDCCcse.h new file mode 100644 index 0000000..bcd8257 --- /dev/null +++ b/src/SDCCcse.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- + + SDCCcse.h - header file for Common Subexpressions + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "SDCCicode.h" + +#ifndef SDCCCSE_H +#define SDCCCSE_H 1 + +typedef struct cseDef + { + + int key; + operand *sym; /* defining symbol */ + iCode *diCode; /* defining instruction */ + bitVect *ancestors; /* keys of the symbol's ancestors */ + int fromGlobal; /* defining symbol's value computed from a global */ + int fromAddrTaken:1; /* defining symbol's value computed from a */ + /* symbol whose address was taken */ + unsigned nonLocalCSE:1; /* CSE def visible outside of originating */ + /* basic block */ + } +cseDef; + + +cseDef *newCseDef (operand *, iCode *); +int isCseDefEqual (void *, void *); +int pcseDef (void *, va_list); +DEFSETFUNC (ifDiCodeIsX); +int ifDiCodeIs (set *, iCode *); +DEFSETFUNC (ifDefSymIsX); +int ifDefSymIs (set *, operand *); +DEFSETFUNC (findPrevIc); +DEFSETFUNC (ifOperandsHave); +DEFSETFUNC (findCheaperOp); +int cseBBlock (eBBlock *, int, ebbIndex *); +int cseAllBlocks (ebbIndex *, int computeOnly); +void unsetDefsAndUses (iCode *); +void updateSpillLocation (iCode * ic,int); +void setUsesDefs (operand *, bitVect *, bitVect *, bitVect **); +void replaceAllSymBySym (iCode *, operand *, operand *, bitVect **); +iCode *findBackwardDef(operand *,iCode *); +void ReplaceOpWithCheaperOp(operand **op, operand *cop); +void freeCSEdata (eBBlock *); + +#endif diff --git a/src/SDCCdebug.c b/src/SDCCdebug.c new file mode 100644 index 0000000..adc67d4 --- /dev/null +++ b/src/SDCCdebug.c @@ -0,0 +1,208 @@ +/*------------------------------------------------------------------------- + SDCCdebug.c - source file for debug infrastructure + + Copyright (C) 2003, Lenny Story and Bernhard Held + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "common.h" + +extern DEBUGFILE cdbDebugFile; + +DEBUGFILE *debugFile = &cdbDebugFile; + +void +outputDebugStackSymbols (void) +{ + if (getenv ("SDCC_DEBUG_VAR_STORAGE")) + { + dumpSymInfo ("XStack", xstack); + dumpSymInfo ("IStack", istack); + } + + if (options.debug && debugFile) + { + symbol *sym; + + if (xstack) + { + for (sym = setFirstItem (xstack->syms); sym; sym = setNextItem (xstack->syms)) + debugFile->writeSymbol (sym); + } + + if (istack) + { + for (sym = setFirstItem (istack->syms); sym; sym = setNextItem (istack->syms)) + debugFile->writeSymbol(sym); + } + } +} + + +void +outputDebugSymbols (void) +{ + if (getenv ("SDCC_DEBUG_VAR_STORAGE")) + { + dumpSymInfo ("Initialized", initialized); + dumpSymInfo ("Code", code); + dumpSymInfo ("Data", data); + dumpSymInfo ("PData", pdata); + dumpSymInfo ("XData", xdata); + dumpSymInfo ("XIData", xidata); + dumpSymInfo ("XInit", xinit); + dumpSymInfo ("IData", idata); + dumpSymInfo ("Bit", bit); + dumpSymInfo ("Statics", statsg); + dumpSymInfo ("SFR", sfr); + dumpSymInfo ("SFRBits", sfrbit); + dumpSymInfo ("Reg", reg); + dumpSymInfo ("Generic", generic); + dumpSymInfo ("Overlay", overlay); + dumpSymInfo ("EEProm", eeprom); + dumpSymInfo ("Home", home); + } + + if (options.debug && debugFile) + { + symbol *sym; + + if(initialized) + { + for (sym = setFirstItem (initialized->syms); sym; sym = setNextItem (initialized->syms)) + debugFile->writeSymbol (sym); + } + + if (data) + { + for (sym = setFirstItem (data->syms); sym; sym = setNextItem (data->syms)) + debugFile->writeSymbol (sym); + } + + if (idata) + { + for (sym = setFirstItem (idata->syms); sym; sym = setNextItem (idata->syms)) + debugFile->writeSymbol (sym); + } + + if (bit) + { + for (sym = setFirstItem (bit->syms); sym; sym = setNextItem (bit->syms)) + debugFile->writeSymbol (sym); + } + + if (pdata) + { + for (sym = setFirstItem (pdata->syms); sym; sym = setNextItem (pdata->syms)) + debugFile->writeSymbol (sym); + } + + if (xdata) + { + for (sym = setFirstItem (xdata->syms); sym; sym = setNextItem (xdata->syms)) + debugFile->writeSymbol (sym); + } + + if (port->genXINIT && xidata) + { + for (sym = setFirstItem (xidata->syms); sym; sym = setNextItem (xidata->syms)) + debugFile->writeSymbol (sym); + } + + if (sfr) + { + for (sym = setFirstItem (sfr->syms); sym; sym = setNextItem (sfr->syms)) + debugFile->writeSymbol (sym); + } + + if (sfrbit) + { + for (sym = setFirstItem (sfrbit->syms); sym; sym = setNextItem (sfrbit->syms)) + debugFile->writeSymbol (sym); + } + + if (home) + { + for (sym = setFirstItem (home->syms); sym; sym = setNextItem (home->syms)) + debugFile->writeSymbol (sym); + } + + if (code) + { + for (sym = setFirstItem (code->syms); sym; sym = setNextItem (code->syms)) + debugFile->writeSymbol (sym); + } + + if (statsg) + { + for (sym = setFirstItem (statsg->syms); sym; sym = setNextItem (statsg->syms)) + debugFile->writeSymbol (sym); + } + + if (port->genXINIT && xinit) + { + for (sym = setFirstItem (xinit->syms); sym; sym = setNextItem (xinit->syms)) + debugFile->writeSymbol (sym); + } + } + + return; +} + + +void +dumpSymInfo(const char *pcName, memmap *memItem) +{ + symbol *sym; + + if (!memItem) + return; + + printf ("--------------------------------------------\n"); + printf (" %s\n", pcName); + + for (sym = setFirstItem (memItem->syms); sym; sym = setNextItem (memItem->syms)) + { + printf (" %s, isReqv:%d, reqv:0x%p, onStack:%d, Stack:%d, nRegs:%d, [", + sym->rname, sym->isreqv, (void*)(sym->reqv), sym->onStack, sym->stack, sym->nRegs); + + if (sym->reqv) + { + int i; + symbol *TempSym = OP_SYMBOL (sym->reqv); + + for (i = 0; i < 4; i++) + if (TempSym->regs[i]) + printf ("%s,", port->getRegName (TempSym->regs[i])); + } + + printf ("]\n"); + } + + printf ("\n"); +} + + +/*------------------------------------------------------------------*/ +/* emitDebuggerSymbol - call the port specific routine to associate */ +/* the current code location with a debugger symbol */ +/*------------------------------------------------------------------*/ +void +emitDebuggerSymbol (const char * debugSym) +{ + if (port->debugger.emitDebuggerSymbol) + port->debugger.emitDebuggerSymbol (debugSym); +} diff --git a/src/SDCCdebug.h b/src/SDCCdebug.h new file mode 100644 index 0000000..72ff34f --- /dev/null +++ b/src/SDCCdebug.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + SDCCdebug.h - header file debug infrastructure + + Copyright (C) 2003, Lenny Story and Bernhard Held + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef _DEBUG_INCLUDE_ +#define _DEBUG_INCLUDE_ + +#include "SDCCsymt.h" +#include "SDCCicode.h" + +typedef struct DebugFile +{ + int (*openFile) (const char *file); + int (*closeFile) (void); + int (*writeModule) (const char *name); + int (*writeFunction) (symbol *pSym, iCode *ic); + int (*writeEndFunction) (symbol *pSym, iCode *ic, int offset); + int (*writeLabel) (symbol *pSym, const iCode *ic); + int (*writeScope) (iCode *ic); + int (*writeSymbol) (symbol *pSym); + int (*writeType) (structdef *sdef, int block, int inStruct, const char *tag); + int (*writeCLine) (iCode *ic); + int (*writeALine) (const char *module, int Line); + int (*writeFrameAddress) (const char *variable, struct reg_info *reg, int offset); +} DEBUGFILE; + +extern DEBUGFILE *debugFile; + +void outputDebugStackSymbols (void); +void outputDebugSymbols (void); +void dumpSymInfo (const char *pcName, memmap *memItem); +void emitDebuggerSymbol (const char * debugSym); + +#endif diff --git a/src/SDCCdflow.c b/src/SDCCdflow.c new file mode 100644 index 0000000..019a07e --- /dev/null +++ b/src/SDCCdflow.c @@ -0,0 +1,405 @@ +/*------------------------------------------------------------------------- + + SDCCdflow.c - source file for data flow analysis and other utility + routines related to data flow. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +/*-----------------------------------------------------------------*/ +/* ifKilledInBlock - will return 1 if the symbol is redefined in B */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (ifKilledInBlock) +{ + cseDef *cdp = item; + V_ARG (eBBlock *, src); + bitVect *outs; + + /* if this is a global variable and this block + has a function call then delete it */ + if (isOperandGlobal (cdp->sym) && src->hasFcall) + return 1; + + /* if this is pointer get then it will be killed + if there is a pointer set for the same pointer + in this block */ + if (POINTER_GET (cdp->diCode) && + bitVectBitValue (src->ptrsSet, + IC_LEFT (cdp->diCode)->key)) + return 1; + + /* if assignment to iTmep then if right is defined + elsewhere kill this one */ + if (ASSIGNMENT (cdp->diCode) && + !POINTER_SET (cdp->diCode) && + IS_ITEMP (IC_RESULT (cdp->diCode)) && + IS_SYMOP (IC_RIGHT (cdp->diCode)) && + bitVectBitsInCommon (src->outDefs, OP_DEFS (IC_RIGHT (cdp->diCode)))) + return 1; + + /* if we find it in the defSet of this block */ + if (bitVectBitsInCommon (src->defSet, OP_DEFS (cdp->sym))) + return 1; + + /* if in the outdef we find a definition other than this one */ + /* to do this we make a copy of the out definitions and turn */ + /* this one off then check if there are other definitions */ + bitVectUnSetBit (outs = bitVectCopy (src->outDefs), + cdp->diCode->key); + if (bitVectBitsInCommon (outs, OP_DEFS (cdp->sym))) + { + freeBitVect (outs); + return 1; + } + + freeBitVect (outs); + + /* if the operands of this one was changed in the block */ + /* then delete it */ + if (cdp->diCode && + ((IS_SYMOP (IC_LEFT (cdp->diCode)) && + bitVectBitsInCommon (src->defSet, OP_DEFS (IC_LEFT (cdp->diCode)))) || + (IS_SYMOP (IC_RIGHT (cdp->diCode)) && + bitVectBitsInCommon (src->defSet, OP_DEFS (IC_RIGHT (cdp->diCode)))))) + return 1; + + /* kill if cseBBlock() found a case we missed here */ + if (isinSetWith(src->killedExprs, cdp, isCseDefEqual)) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* mergeInExprs - copy the in expression if it dominates */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (mergeInExprs) +{ + eBBlock *ebp = item; + V_ARG (eBBlock *, dest); + V_ARG (int *, firstTime); + + dest->killedExprs = unionSets (dest->killedExprs, ebp->killedExprs, THROW_DEST); + + /* if in the dominator list then */ + if (bitVectBitValue (dest->domVect, ebp->bbnum) && dest != ebp) + { + /* if already present then intersect */ + if (!dest->inExprs && *firstTime) + { + dest->inExprs = setFromSet (ebp->outExprs); + /* copy the pointer set from the dominator */ + dest->inPtrsSet = bitVectCopy (ebp->ptrsSet); + dest->ndompset = bitVectCopy (ebp->ndompset); + } + else + { + dest->inExprs = intersectSets (dest->inExprs, + ebp->outExprs, + THROW_DEST); + dest->inPtrsSet = bitVectInplaceUnion (dest->inPtrsSet, ebp->ptrsSet); + dest->ndompset = bitVectInplaceUnion (dest->ndompset, ebp->ndompset); + } + } + else + { + //if (dest != ebp) + // dest->inExprs = intersectSets (dest->inExprs, ebp->outExprs, THROW_DEST); + + /* delete only if killed in this block*/ + deleteItemIf (&dest->inExprs, ifKilledInBlock, ebp); + /* union the ndompset with pointers set in this block */ + dest->ndompset = bitVectInplaceUnion (dest->ndompset, ebp->ptrsSet); + } + *firstTime = 0; + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* mergeInDefs - merge in incoming definitions */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (mergeInDefs) +{ + eBBlock *ebp = item; + V_ARG (eBBlock *, dest); + V_ARG (int *, firstTime); + + /* the in definition is the union of the out */ + /* of all blocks that come to this block */ + if (!dest->inDefs && *firstTime) + dest->inDefs = bitVectCopy (ebp->outDefs); + else + dest->inDefs = bitVectInplaceUnion (dest->inDefs, ebp->outDefs); + + *firstTime = 0; + + return 0; +} + + +/*------------------------------------------------------------------*/ +/* computeDataFlow - does computations for data flow accross blocks */ +/*------------------------------------------------------------------*/ +void +computeDataFlow (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->dfOrder; + int count = ebbi->count; + int i; + int change; + + for (i = 0; i < count; i++) + deleteSet (&ebbs[i]->killedExprs); + + do + { + change = 0; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + set *pred; + set *oldOutExprs = NULL; + set *oldKilledExprs = NULL; + bitVect *oldOutDefs = NULL; + int firstTime; + eBBlock *pBlock; + + /* if this is the entry block then continue */ + /* since entry block can never have any inExprs */ + if (ebbs[i]->noPath) + continue; + + /* get blocks that can come to this block */ + pred = edgesTo (ebbs[i]); + + /* make a copy of the outExpressions and outDefs : to be */ + /* used for iteration */ + if (optimize.global_cse) + { + oldOutExprs = setFromSet (ebbs[i]->outExprs); + oldKilledExprs = setFromSet (ebbs[i]->killedExprs); + } + oldOutDefs = bitVectCopy (ebbs[i]->outDefs); + freeBitVect(ebbs[i]->inDefs); ebbs[i]->inDefs = NULL; + + /* indefitions are easy just merge them by union */ + /* these are the definitions that can possibly */ + /* reach this block */ + firstTime = 1; + applyToSet (pred, mergeInDefs, ebbs[i], &firstTime); + + /* if none of the edges coming to this block */ + /* dominate this block then add the immediate dominator */ + /* of this block to the list of predecessors */ + for (pBlock = setFirstItem (pred); pBlock; + pBlock = setNextItem (pred)) + { + if (bitVectBitValue (ebbs[i]->domVect, pBlock->bbnum)) + break; + } + + /* get the immediate dominator and put it there */ + if (!pBlock) + { + eBBlock *idom = immedDom (ebbi, ebbs[i]); + if (idom) + addSetHead (&pred, idom); + } + + /* figure out the incoming expressions */ + /* this is a little more complex */ + //setToNull ((void *) &ebbs[i]->inExprs); + deleteSet (&ebbs[i]->inExprs); + if (optimize.global_cse) + { + firstTime = 1; + applyToSet (pred, mergeInExprs, ebbs[i], &firstTime); + } + setToNull ((void *) &pred); + + /* do cse with computeOnly flag set to TRUE */ + /* this is by far the quickest way of computing */ + cseBBlock (ebbs[i], TRUE, ebbi); + + /* if it change we will need to iterate */ + if (optimize.global_cse) + { + change += !isSetsEqualWith (ebbs[i]->outExprs, oldOutExprs, isCseDefEqual); + change += !isSetsEqualWith (ebbs[i]->killedExprs, oldKilledExprs, isCseDefEqual); + } + change += !bitVectEqual (ebbs[i]->outDefs, oldOutDefs); + freeBitVect (oldOutDefs); + deleteSet (&oldOutExprs); + deleteSet (&oldKilledExprs); + } + } + while (change); /* iterate till no change */ + + return; +} + +/*-----------------------------------------------------------------*/ +/* usedBetweenPoints - used between start & end */ +/*-----------------------------------------------------------------*/ +int +usedBetweenPoints (operand * op, iCode * start, iCode * end) +{ + iCode *lic = start; + + for (; lic != end; lic = lic->next) + { + /* if the operand is a parameter */ + /* then check for calls and return */ + /* true if there is a call */ + if (IS_PARM (op) && + (lic->op == CALL || lic->op == PCALL)) + { + value *args; + if (lic->op == CALL) + { + args = FUNC_ARGS (OP_SYMBOL (IC_LEFT (lic))->type); + } + else + { + args = FUNC_ARGS (OP_SYMBOL (IC_LEFT (lic))->type->next); + } + if (isParameterToCall (args, op)) + return 1; + } + + if (SKIP_IC2 (lic)) + continue; + + /* if ifx then check the condition */ + if (lic->op == IFX && + IC_COND (lic)->key == op->key) + return 1; + + if (lic->op == JUMPTABLE && + IC_JTCOND (lic)->key == op->key) + return 1; + + if (IC_RIGHT (lic) && + IC_RIGHT (lic)->key == op->key) + return 1; + + if (IC_LEFT (lic) && + IC_LEFT (lic)->key == op->key) + return 1; + + /* for a pointer assignment usage */ + if (POINTER_SET (lic) && + op->key == IC_RESULT (lic)->key) + return 1; + else if (IC_RESULT (lic) && op->key == IC_RESULT (lic)->key) + return 0; + } + + return 0; +} + + +/*------------------------------------------------------------------*/ +/* usedInRemaining - returns point of usage for an operand if found */ +/*------------------------------------------------------------------*/ +iCode * +usedInRemaining (operand * op, iCode * ic) +{ + iCode *lic = ic; + + if (!IS_SYMOP (op)) + return 0; + + for (; lic; lic = lic->next) + { + /* if the operand is a parameter */ + /* then check for calls and return */ + /* true if there is a call */ + /* if this is a global variable then + return true */ + if (lic->op == CALL || lic->op == PCALL) + { + value *args; + if (lic->op == CALL) + args=FUNC_ARGS (operandType (IC_LEFT (lic))); + else + args=FUNC_ARGS (operandType (IC_LEFT (lic))->next); + if ((IS_PARM (op) && isParameterToCall (args, op)) || + isOperandGlobal (op)) + return lic; + } + + if (ic->op == SEND && + isOperandEqual (IC_LEFT (lic), op)) + return lic; + + if (SKIP_IC1 (lic)) + continue; + + /* if ifx then check the condition */ + if (lic->op == IFX && isOperandEqual (IC_COND (lic), op)) + return lic; + + if (lic->op == JUMPTABLE && isOperandEqual (IC_JTCOND (lic), op)) + return lic; + + if (IC_RIGHT (lic) && isOperandEqual (IC_RIGHT (lic), op)) + return lic; + + if (IC_LEFT (lic) && + isOperandEqual (IC_LEFT (lic), op)) + return lic; + + /* for a pointer assignment usage */ + if (POINTER_SET (lic) && isOperandEqual (op, IC_RESULT (lic))) + return lic; + else if (IC_RESULT (lic) && isOperandEqual (IC_RESULT (lic), op)) + return NULL; + } + + return NULL; +} + + +/*-------------------------------------------------------------------*/ +/* isDefAlive - will return true if definiton reaches a block & used */ +/*-------------------------------------------------------------------*/ +DEFSETFUNC (isDefAlive) +{ + eBBlock *ebp = item; + V_ARG (iCode *, diCode); + + if (ebp->visited) + return 0; + + ebp->visited = 1; + + /* if this definition is used in the block */ + if (bitVectBitValue (ebp->usesDefs, diCode->key)) + return 1; + + return applyToSet (ebp->succList, isDefAlive, diCode); +} diff --git a/src/SDCCdflow.h b/src/SDCCdflow.h new file mode 100644 index 0000000..6e484d4 --- /dev/null +++ b/src/SDCCdflow.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + + SDCCdflow.h - header file for data flow analysis & utility routines + related to data flow. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "SDCCset.h" + +#ifndef SDCCDFLOW_H +#define SDCCDFLOW_H 1 + +DEFSETFUNC (mergeInExprs); +DEFSETFUNC (ifKilledInBlock); +void computeDataFlow (ebbIndex *); +DEFSETFUNC (mergeInDefs); +DEFSETFUNC (isDefAlive); +iCode *usedInRemaining (operand *, iCode *); +int usedBetweenPoints (operand *, iCode *, iCode *); +#endif diff --git a/src/SDCCdwarf2.c b/src/SDCCdwarf2.c new file mode 100644 index 0000000..8041641 --- /dev/null +++ b/src/SDCCdwarf2.c @@ -0,0 +1,3247 @@ +/*------------------------------------------------------------------------- + SDCCdwarf2.c - generate DWARF2 debug information + + Written By - Erik Petrich . epetrich@users.sourceforge.net (2004) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + + +#include "common.h" +#include "SDCCdwarf2.h" + +/************************************************************* + * + * + * + * + *************************************************************/ + +extern set *includeDirsSet; + +int dwOpenFile (const char *file); +int dwCloseFile (void); +int dwWriteFunction (symbol *pSym, iCode *ic); +int dwWriteEndFunction (symbol *pSym, iCode *ic, int offset); +int dwWriteLabel (symbol *pSym, const iCode *ic); +int dwWriteScope (iCode *ic); +int dwWriteSymbol (symbol *pSym); +int dwWriteType (structdef *sdef, int block, int inStruct, const char *tag); +int dwWriteModule (const char *name); +int dwWriteCLine (iCode *ic); +int dwWriteALine (const char *module, int Line); +int dwWriteFrameAddress (const char *variable, struct reg_info *reg, int offset); +int dwWriteBasicSymbol (symbol *sym, int isStructSym, int isFunc); + + +DEBUGFILE dwarf2DebugFile = + { + &dwOpenFile, + &dwCloseFile, + &dwWriteModule, + &dwWriteFunction, + &dwWriteEndFunction, + &dwWriteLabel, + &dwWriteScope, + &dwWriteSymbol, + &dwWriteType, + &dwWriteCLine, + &dwWriteALine, + &dwWriteFrameAddress + }; + +FILE *dwarf2FilePtr = NULL; +char *dwModuleName = NULL; +dwtag *dwRootTag = NULL; +dwtag *dwFuncTag = NULL; +dwtag *dwScopeTag = NULL; +hTab * dwAbbrevTable; +int dwAbbrevNum = 0; +hTab * dwTypeTagTable; +int dwRefNum = 0; +int dwScopeBlock = 0; +long dwScopeLevel = 0; +int dwDebugSymbol = 0; +dwcfins * dwCIEins = NULL; +dwlocregion * dwFrameLastLoc = NULL; +dwloclist * dwRootLocList = NULL; +dwloclist * dwFrameLocList = NULL; +int dwLineBase = -5; +int dwLineRange = 15; +int dwLineOpcodeBase = 10; +set * dwFilenameSet = NULL; +dwline * dwLineFirst = NULL; +dwline * dwLineLast = NULL; +dwlocregion * dwCFILastLoc = NULL; +dwcfilist * dwCFIRoot = NULL; + + +/*----------------------------------------------------------------------*/ +/* dwNewDebugSymbol - returns the name for a new debug symbol */ +/*----------------------------------------------------------------------*/ +static char * +dwNewDebugSymbol (void) +{ + char debugSym[SDCC_NAME_MAX]; + + sprintf (debugSym, "S%s$%s$%d", dwModuleName, currFunc->name, dwDebugSymbol); + dwDebugSymbol++; + return Safe_strdup (debugSym); +} + + +/*----------------------------------------------------------------------*/ +/* dwWriteByte - generate a single byte assembler constant in the form: */ +/* */ +/* .db label+offset ; comment */ +/* */ +/* The label and comment parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteByte (const char * label, int offset, const char * comment) +{ + tfprintf (dwarf2FilePtr, "\t!db\t"); + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "%s+%d", label, offset); + else + fprintf (dwarf2FilePtr, "%s", label); + } + else + fprintf (dwarf2FilePtr, "%d", offset); + + if (comment) + fprintf (dwarf2FilePtr, "\t;%s\n", comment); + else + fprintf (dwarf2FilePtr, "\n"); +} + +/*----------------------------------------------------------------------*/ +/* dwWriteHalf - generate a two byte assembler constant in the form: */ +/* */ +/* .dw label+offset ; comment */ +/* */ +/* The label and comment parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteHalf (const char * label, int offset, char * comment) +{ + tfprintf (dwarf2FilePtr, "\t!dw\t"); + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "%s+%d", label, offset); + else + fprintf (dwarf2FilePtr, "%s", label); + } + else + fprintf (dwarf2FilePtr, "%d", offset); + + if (comment) + fprintf (dwarf2FilePtr, "\t;%s\n", comment); + else + fprintf (dwarf2FilePtr, "\n"); +} + +/*----------------------------------------------------------------------*/ +/* dwWriteWord - generate a four byte assembler constant in the form: */ +/* */ +/* .dd label+offset ; comment */ +/* */ +/* The label and comment parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteWord (const char * label, int offset, char * comment) +{ + /* FIXME: need to implement !dd pseudo-op in the assember. In the */ + /* meantime, we use dw with zero padding and hope the values fit */ + /* in only 16 bits. */ +#if 0 + tfprintf (dwarf2FilePtr, "\t!dd\t"); + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "%s+%d", label, offset); + else + fprintf (dwarf2FilePtr, "%s", label); + } + else + fprintf (dwarf2FilePtr, "%d", offset); +#else + tfprintf (dwarf2FilePtr, "\t!dw\t"); + if (port->little_endian) + { + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "(%s+%d),0", label, offset); + else + fprintf (dwarf2FilePtr, "(%s),0", label); + } + else + fprintf (dwarf2FilePtr, "%d,%d", offset, offset >> 16); + } + else + { + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "0,(%s+%d)", label, offset); + else + fprintf (dwarf2FilePtr, "0,(%s)", label); + } + else + fprintf (dwarf2FilePtr, "%d,%d", offset >> 16, offset); + } +#endif + + if (comment) + fprintf (dwarf2FilePtr, "\t;%s\n", comment); + else + fprintf (dwarf2FilePtr, "\n"); +} + + +/*----------------------------------------------------------------------*/ +/* dwWriteULEB128 - generate an unsigned variable length assembler */ +/* constant in the form: */ +/* */ +/* .uleb128 label+offset ; comment */ +/* */ +/* The label and comment parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteULEB128 (char * label, int offset, char * comment) +{ + tfprintf (dwarf2FilePtr, "\t.uleb128\t"); + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "%s+%d", label, offset); + else + fprintf (dwarf2FilePtr, "%s", label); + } + else + fprintf (dwarf2FilePtr, "%d", offset); + + if (comment) + fprintf (dwarf2FilePtr, "\t;%s\n", comment); + else + fprintf (dwarf2FilePtr, "\n"); +} + +/*----------------------------------------------------------------------*/ +/* dwWriteSLEB128 - generate a signed variable length assembler */ +/* constant in the form: */ +/* */ +/* .sleb128 label+offset ; comment */ +/* */ +/* The label and comment parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteSLEB128 (char * label, int offset, char * comment) +{ + tfprintf (dwarf2FilePtr, "\t.sleb128\t"); + if (label) + { + if (offset) + fprintf (dwarf2FilePtr, "%s+%d", label, offset); + else + fprintf (dwarf2FilePtr, "%s", label); + } + else + fprintf (dwarf2FilePtr, "%d", offset); + + if (comment) + fprintf (dwarf2FilePtr, "\t;%s\n", comment); + else + fprintf (dwarf2FilePtr, "\n"); +} + + +/*----------------------------------------------------------------------*/ +/* dwSizeofULEB128 - return the size (in bytes) of an unsigned variable */ +/* length constant */ +/*----------------------------------------------------------------------*/ +static int +dwSizeofULEB128 (int unsigned value) +{ + int size = 0; + + do + { + value >>= 7; + size++; + } + while (value); + + return size; +} + +/*----------------------------------------------------------------------*/ +/* dwSizeofSLEB128 - return the size (in bytes) of a signed variable */ +/* length constant */ +/*----------------------------------------------------------------------*/ +static int +dwSizeofSLEB128 (int value) +{ + int size = 0; + int negative = (value < 0); + int sign; + + while (1) + { + size++; + sign = value & 0x40; + value >>= 7; + if (negative) + value |= (0x7f << (sizeof(int)*8 - 7)); + if ((value == 0 && !sign) || (value == -1 && sign)) + break; + } + + return size; +} + +/*----------------------------------------------------------------------*/ +/* dwWriteString - generate a string constant in the form: */ +/* */ +/* .ascii /string/ ; comment */ +/* */ +/* The comment parameter is optional. The string may contain any */ +/* non-null characters */ +/*----------------------------------------------------------------------*/ +static void +dwWriteString (const char * string, const char * comment) +{ + /* FIXME: need to safely handle nonalphanumeric data in string */ + + tfprintf (dwarf2FilePtr, "\t!ascii\n", string); + dwWriteByte (NULL, 0, comment); +} + + +/*----------------------------------------------------------------------*/ +/* dwWriteAddress - generate an assembler constant in the form: */ +/* */ +/* .dw label+offset ; comment */ +/* or .dd label+offset ; comment */ +/* */ +/* depending on how the relevant ABI defines the address size (may be */ +/* larger than the CPU's actual address size). The label and comment */ +/* parameters are optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteAddress (const char * label, int offset, char * comment) +{ + switch (port->debugger.dwarf.addressSize) + { + case 2: + dwWriteHalf (label, offset, comment); + break; + case 4: + dwWriteWord (label, offset, comment); + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unsupported port->debugger.dwarf.addressSize"); + } +} + + +/*----------------------------------------------------------------------*/ +/* dwWriteHalfDelta - generate a two byte assembler constant in the */ +/* form: */ +/* */ +/* .dw offset+label1-label2 */ +/* */ +/* The offset parameter is optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteHalfDelta (char * label1, char * label2, int offset) +{ + if (offset) + tfprintf (dwarf2FilePtr, "\t!dw\t%d+%s-%s\n", offset, label1, label2); + else + tfprintf (dwarf2FilePtr, "\t!dw\t%s-%s\n", label1, label2); +} + +/*----------------------------------------------------------------------*/ +/* dwWriteWordDelta - generate a four byte assembler constant in the */ +/* form: */ +/* */ +/* .dd label1-label2 */ +/*----------------------------------------------------------------------*/ +static void +dwWriteWordDelta (char * label1, char * label2) +{ + /* FIXME: need to implement !dd pseudo-op; this hack only */ + /* works for positive offsets of less than 64k */ +#if 0 + tfprintf (dwarf2FilePtr, "\t!dd\t%s-%s\n", label1,label2); +#else + if (port->little_endian) + { + tfprintf (dwarf2FilePtr, "\t!dw\t%s-%s,%d\n", label1, label2, 0); + } + else + { + tfprintf (dwarf2FilePtr, "\t!dw\t%d,%s-%s\n", 0, label1, label2); + } +#endif +} + + +/*----------------------------------------------------------------------*/ +/* dwWriteAddressDelta - generate an assembler constant in the form: */ +/* */ +/* .dw label1-label2 */ +/* or .dd label1-label2 */ +/* */ +/* depending on how the relevant ABI defines the address size (may be */ +/* larger than the CPU's actual address size) */ +/*----------------------------------------------------------------------*/ +static void +dwWriteAddressDelta (char * label1, char * label2) +{ + switch (port->debugger.dwarf.addressSize) + { + case 2: + dwWriteHalfDelta (label1, label2, 0); + break; + case 4: + dwWriteWordDelta (label1, label2); + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unsupported port->debugger.dwarf.addressSize"); + } +} + +#if 0 +/*----------------------------------------------------------------------*/ +/* dwWriteULEB128Delta - generate an unsigned variable byte assembler */ +/* constant in the form: */ +/* */ +/* .uleb128 offset+label1-label2 */ +/* */ +/* The offset parameter is optional */ +/*----------------------------------------------------------------------*/ +static void +dwWriteULEB128Delta (char * label1, char * label2, int offset) +{ + if (offset) + tfprintf (dwarf2FilePtr, "\t.uleb128\t%d+%s-%s\n", offset, label1, label2); + else + tfprintf (dwarf2FilePtr, "\t.uleb128\t%s-%s\n", label1, label2); +} +#endif + +/*------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* dwNewCFIlist - allocates a new CFI list node */ +/*----------------------------------------------------------------------*/ +dwcfilist * +dwNewCFIlist () +{ + dwcfilist * p; + + p = Safe_alloc (sizeof (dwcfilist)); + + return p; +} + +/*----------------------------------------------------------------------*/ +/* dwNewLoc - allocates a new location expression node */ +/*----------------------------------------------------------------------*/ +dwloc * +dwNewLoc (int opcode, const char * label, int offset) +{ + dwloc * lp; + + lp = Safe_alloc (sizeof (dwloc)); + + lp->opcode = opcode; + lp->operand.label = label; + lp->operand.offset = offset; + + return lp; +} + +/*-------------------------------------------------------------------------*/ +/* dwSizeofLoc - returns the size (in bytes) of a chain of location */ +/* expression nodes as they would be encoded by dwWriteLoc() */ +/*-------------------------------------------------------------------------*/ +static int +dwSizeofLoc (dwloc * lp) +{ + int size = 0; + + while (lp) + { + size++; + switch (lp->opcode) + { + case DW_OP_addr: + size += port->debugger.dwarf.addressSize; + break; + + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_pick: + case DW_OP_const1u: + case DW_OP_const1s: + size += 1; + break; + + case DW_OP_skip: + case DW_OP_bra: + case DW_OP_const2u: + case DW_OP_const2s: + size += 2; + break; + + case DW_OP_const4u: + case DW_OP_const4s: + size += 4; + break; + + case DW_OP_const8u: + case DW_OP_const8s: + size += 8; + break; + + case DW_OP_piece: + case DW_OP_regx: + case DW_OP_plus_uconst: + size += dwSizeofULEB128 (lp->operand.offset); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + case DW_OP_fbreg: + size += dwSizeofSLEB128 (lp->operand.offset); + break; + } + + lp = lp->next; + } + + return size; +} + +/*------------------------------------------------------------------------*/ +/* dwWriteLoc - writes a chain of location expression nodes */ +/*------------------------------------------------------------------------*/ +static void +dwWriteLoc (dwloc *lp) +{ + while (lp) + { + dwWriteByte (NULL, lp->opcode, NULL); + switch (lp->opcode) + { + case DW_OP_addr: + dwWriteAddress (lp->operand.label, lp->operand.offset, NULL); + break; + + case DW_OP_deref_size: + case DW_OP_xderef_size: + case DW_OP_pick: + case DW_OP_const1u: + case DW_OP_const1s: + dwWriteByte (NULL, lp->operand.offset, NULL); + break; + + case DW_OP_skip: + case DW_OP_bra: + case DW_OP_const2u: + case DW_OP_const2s: + dwWriteHalf (NULL, lp->operand.offset, NULL); + break; + + case DW_OP_const4u: + case DW_OP_const4s: + dwWriteWord (NULL, lp->operand.offset, NULL); + break; + + case DW_OP_piece: + case DW_OP_regx: + case DW_OP_plus_uconst: + dwWriteULEB128 (NULL, lp->operand.offset, NULL); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + case DW_OP_fbreg: + dwWriteSLEB128 (NULL, lp->operand.offset, NULL); + break; + } + + lp = lp->next; + } +} + +/*----------------------------------------------------------------------*/ +/* dwNewLocList - allocates a new list of location expression node */ +/*----------------------------------------------------------------------*/ +static dwloclist * +dwNewLocList (void) +{ + dwloclist * llp; + + llp = Safe_alloc (sizeof (dwloclist)); + + return llp; +} + +/*----------------------------------------------------------------------*/ +/* dwSizeofLocRegion - returns the size (in bytes) of a chain of */ +/* location regions (inluding their location */ +/* expression nodes) as encoded by dwWriteLocLists */ +/*----------------------------------------------------------------------*/ +static int +dwSizeofLocRegion (dwlocregion * lrp) +{ + int size = 0; + + while (lrp) + { + size += 2 * port->debugger.dwarf.addressSize; + size += 2 + dwSizeofLoc (lrp->loc); + lrp = lrp->next; + } + + size += 2 * port->debugger.dwarf.addressSize; + return size; +} + +/*-----------------------------------------------------------------------*/ +/* dwAssignLocListAddresses - assign the address offsets of the location */ +/* lists so that they can be referenced from */ +/* the tag structure */ +/*-----------------------------------------------------------------------*/ +static void +dwAssignLocListAddresses (void) +{ + dwloclist * llp; + int address = 0; + + llp = dwRootLocList; + while (llp) + { + llp->baseOffset = address; + address += dwSizeofLocRegion (llp->region); + + llp = llp->next; + } +} + +/*-----------------------------------------------------------------------*/ +/* dwWriteLocLists - write all of the location lists in dwRootLocList to */ +/* the .debug_loc section */ +/*-----------------------------------------------------------------------*/ +static void +dwWriteLocLists (void) +{ + dwlocregion * lrp; + dwloclist * llp; + + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_loc (NOLOAD)"); + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_loc_start"); + + llp = dwRootLocList; + while (llp) + { + //fprintf (dwarf2FilePtr, "; baseOffset = 0x%x\n", llp->baseOffset); + lrp = llp->region; + while (lrp) + { + dwWriteAddress (lrp->startLabel, 0, NULL); + dwWriteAddress (lrp->endLabel, 0, NULL); + dwWriteHalf (NULL, dwSizeofLoc (lrp->loc), NULL); + dwWriteLoc (lrp->loc); + lrp = lrp ->next; + } + + dwWriteAddress (NULL, 0, NULL); + dwWriteAddress (NULL, 0, NULL); + + llp = llp->next; + } + +} + + +/*------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------*/ +/* dwNewAttr - allocate a new tag attribute node */ +/*----------------------------------------------------------------------*/ +static dwattr * +dwNewAttr (int attr) +{ + dwattr * ap; + + ap = Safe_alloc ( sizeof (dwattr)); + ap->attr = attr; + + return ap; +} + +/*----------------------------------------------------------------------*/ +/* dwFreeAttr - deallocate a tag attribute node */ +/*----------------------------------------------------------------------*/ +static void +dwFreeAttr (dwattr * ap) +{ + Safe_free (ap); +} + + +/*-------------------------------------------------------------------------*/ +/* dwNewAttrString - allocate a new tag attribute node with a string value */ +/*-------------------------------------------------------------------------*/ +static dwattr * +dwNewAttrString (int attr, const char * string) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_string; + ap->val.string = string; + return ap; +} + + +/*---------------------------------------------------------------------*/ +/* dwNewAttrConst - allocate a new tag attribute node with an unsigned */ +/* numeric constant value */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrConst (int attr, unsigned int data) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + if (data <= 0xffu) + ap->form = DW_FORM_data1; + else if (data <= 0xffffu) + ap->form = DW_FORM_data2; + else + ap->form = DW_FORM_data4; + + ap->val.data = data; + return ap; +} + +/* disabled to eliminiate unused function warning */ +#if 0 +/*---------------------------------------------------------------------*/ +/* dwNewAttrSignedConst - allocate a new tag attribute node with a */ +/* signed numeric constant value */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrSignedConst (int attr, int data) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + if (data <= 0x7f && data >= -0x80) + ap->form = DW_FORM_data1; + else if (data <= 0xffff && data >= -0x8000) + ap->form = DW_FORM_data2; + else + ap->form = DW_FORM_data4; + + ap->val.data = data; + return ap; +} +#endif + +/*---------------------------------------------------------------------*/ +/* dwNewAttrFlag - allocate a new tag attribute node with a boolean */ +/* flag value (zero/non-zero) */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrFlag (int attr, int data) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_flag; + + ap->val.data = data; + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwNewAttrAddrSymbol - allocate a new tag attribute node with the */ +/* address of a C symbol plus an offset */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrAddrSymbol (int attr, symbol * sym, int offset) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_addr; + + ap->val.symaddr.label = sym->rname; + ap->val.symaddr.offset = offset; + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwNewAttrAddrLabel - allocate a new tag attribute node with the */ +/* address of an assembler label plus an offset */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrAddrLabel (int attr, char * label, int offset) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_addr; + + ap->val.symaddr.label = label; + ap->val.symaddr.offset = offset; + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwNewAttrTagRef - allocate a new tag attribute node that references */ +/* a tag node */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrTagRef (int attr, dwtag * tp) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_ref4; + + ap->val.ref = tp; + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwNewAttrLocRef - allocate a new tag attribute node that references */ +/* a location list */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwNewAttrLocRef (int attr, dwloclist * llp) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_data4; + + ap->val.loclist = llp; + return ap; +} + +/*-----------------------------------------------------------------------*/ +/* dwNewAttrLabelRef - allocate a new tag attribute node that references */ +/* the address of an assembler label plus an offset */ +/*-----------------------------------------------------------------------*/ +static dwattr * +dwNewAttrLabelRef (int attr, char * label, int offset) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_data4; + + ap->val.symaddr.label = label; + ap->val.symaddr.offset = offset; + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwNewAttrLoc - allocate a new tag attribute node for a chain of */ +/* location expression nodes */ +/*---------------------------------------------------------------------*/ +dwattr * +dwNewAttrLoc (int attr, dwloc * lp) +{ + dwattr * ap; + + ap = dwNewAttr (attr); + ap->form = DW_FORM_block1; + ap->val.loc = lp; + + return ap; +} + +/*---------------------------------------------------------------------*/ +/* dwWriteAttr - write a tag attribute node */ +/*---------------------------------------------------------------------*/ +static void +dwWriteAttr (dwattr * ap) +{ + + switch (ap->form) + { + case DW_FORM_addr: + dwWriteAddress (ap->val.symaddr.label, ap->val.symaddr.offset, NULL); + break; + + case DW_FORM_block: + dwWriteULEB128 (NULL, dwSizeofLoc (ap->val.loc), NULL); + dwWriteLoc (ap->val.loc); + break; + + case DW_FORM_block1: + dwWriteByte (NULL, dwSizeofLoc (ap->val.loc), NULL); + dwWriteLoc (ap->val.loc); + break; + + case DW_FORM_block2: + dwWriteHalf (NULL, dwSizeofLoc (ap->val.loc), NULL); + dwWriteLoc (ap->val.loc); + break; + + case DW_FORM_block4: + dwWriteWord (NULL, dwSizeofLoc (ap->val.loc), NULL); + dwWriteLoc (ap->val.loc); + break; + + case DW_FORM_data1: + case DW_FORM_flag: + dwWriteByte (NULL, ap->val.data, NULL); + break; + + case DW_FORM_data2: + dwWriteHalf (NULL, ap->val.data, NULL); + break; + + case DW_FORM_data4: + switch (ap->attr) + { + case DW_AT_stmt_list: + dwWriteWord (ap->val.symaddr.label, ap->val.symaddr.offset, NULL); + break; + case DW_AT_location: + case DW_AT_frame_base: + dwWriteWord ("Ldebug_loc_start", ap->val.loclist->baseOffset, NULL); + break; + default: + dwWriteWord (NULL, ap->val.data, NULL); + } + break; + + case DW_FORM_udata: + dwWriteULEB128 (NULL, ap->val.data, NULL); + break; + + case DW_FORM_sdata: + dwWriteSLEB128 (NULL, ap->val.data, NULL); + break; + + case DW_FORM_string: + dwWriteString (ap->val.string, NULL); + break; + + case DW_FORM_ref1: + dwWriteByte (NULL, ap->val.ref->baseOffset, NULL); + break; + + case DW_FORM_ref2: + dwWriteHalf (NULL, ap->val.ref->baseOffset, NULL); + break; + + case DW_FORM_ref4: + dwWriteWord (NULL, ap->val.ref->baseOffset, NULL); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unsupported DWARF form"); + exit (1); + } +} + +/*---------------------------------------------------------------------*/ +/* dwSizeofAttr - returns the size (in bytes) of a tag attribute node */ +/* as encoded by dwWriteAttr */ +/*---------------------------------------------------------------------*/ +static int +dwSizeofAttr (dwattr * ap) +{ + int size; + + switch (ap->form) + { + case DW_FORM_addr: + return port->debugger.dwarf.addressSize; + + case DW_FORM_block: + size = dwSizeofLoc (ap->val.loc); + return size + dwSizeofULEB128 (size); + + case DW_FORM_block1: + size = dwSizeofLoc (ap->val.loc); + return size + 1; + + case DW_FORM_block2: + size = dwSizeofLoc (ap->val.loc); + return size + 2; + + case DW_FORM_block4: + size = dwSizeofLoc (ap->val.loc); + return size + 4; + + case DW_FORM_data1: + case DW_FORM_flag: + return 1; + + case DW_FORM_data2: + return 2; + + case DW_FORM_data4: + return 4; + + case DW_FORM_udata: + return dwSizeofULEB128 (ap->val.data); + + case DW_FORM_sdata: + return dwSizeofSLEB128 (ap->val.data); + + case DW_FORM_string: + return 1 + strlen (ap->val.string); + + case DW_FORM_ref1: + return 1; + + case DW_FORM_ref2: + return 2; + + case DW_FORM_ref4: + return 4; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unsupported DWARF form"); + exit (1); + } + +} + + +/*---------------------------------------------------------------------*/ +/* dwFindAttr - for a tag node, return a pointer to a particular */ +/* attribute node, or NULL if not found */ +/*---------------------------------------------------------------------*/ +static dwattr * +dwFindAttr (dwtag * tp, int attr) +{ + dwattr * ap; + + ap = tp->attribs; + while (ap) + { + if (ap->attr == attr) + return ap; + ap = ap->next; + } + + return NULL; +} + + + +/*------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------*/ +/* dwNewTag - allocate a new tag node */ +/*----------------------------------------------------------------------*/ +static dwtag * +dwNewTag (int tag) +{ + dwtag * tp; + + tp = Safe_alloc ( sizeof (dwtag)); + tp->tag = tag; + + return tp; +} + +/*----------------------------------------------------------------------*/ +/* dwAddTagAttr - add an attribute to a tag */ +/*----------------------------------------------------------------------*/ +static void +dwAddTagAttr (dwtag * tp, dwattr * ap) +{ + dwattr * curap; + + if (!tp->attribs) + tp->attribs = ap; + else if (ap->attr < tp->attribs->attr) + { + ap->next = tp->attribs; + tp->attribs = ap; + } + else + { + curap = tp->attribs; + while (curap->next && curap->next->attr < ap->attr) + curap = curap->next; + ap->next = curap->next; + curap->next = ap; + } +} + +/*----------------------------------------------------------------------*/ +/* dwSetTagAttr - repleace an existing attribute of a tag with a new */ +/* attribute or add if non-existent */ +/*----------------------------------------------------------------------*/ +static void +dwSetTagAttr (dwtag *tp, dwattr * ap) +{ + dwattr * curap; + + curap = dwFindAttr (tp, ap->attr); + if (curap) + { + ap->next = curap->next; + *curap = *ap; + dwFreeAttr (ap); + } + else + dwAddTagAttr (tp, ap); +} + + +/*----------------------------------------------------------------------*/ +/* dwAddTagChild - add a tag as a child of another tag */ +/*----------------------------------------------------------------------*/ +static dwtag * +dwAddTagChild (dwtag * parent, dwtag * child) +{ + child->parent = parent; + if (parent->lastChild) + { + parent->lastChild->siblings = child; + parent->lastChild = child; + } + else + { + parent->firstChild = child; + parent->lastChild = child; + } + return parent; +} + +/*----------------------------------------------------------------------*/ +/* dwMatchTagAttr - returns true if two tags are equal in value, */ +/* attributes, and offspring status (the child tags */ +/* need not match, but they must both have children or */ +/* both not have children) */ +/*----------------------------------------------------------------------*/ +static int +dwMatchTagAttr (const void * tp1v, const void * tp2v) +{ + const dwtag * tp1 = tp1v; + const dwtag * tp2 = tp2v; + dwattr * ap1; + dwattr * ap2; + + if (!tp1 || !tp2) + return 0; + + ap1 = tp1->attribs; + ap2 = tp2->attribs; + + if (tp1->tag != tp2->tag) + return 0; + + if (tp1->firstChild && !tp2->lastChild) + return 0; + if (!tp1->firstChild && tp2->lastChild) + return 0; + + while (ap1 && ap2) + { + if (ap1->attr != ap2->attr) + return 0; + if (ap1->form != ap2->form) + return 0; + + ap1 = ap1->next; + ap2 = ap2->next; + } + + return 1; +} + +/*----------------------------------------------------------------------*/ +/* dwHashTag - return a hash code for a tag based on its value and */ +/* attributes */ +/*----------------------------------------------------------------------*/ +static int +dwHashTag (dwtag * tp) +{ + dwattr * ap = tp->attribs; + int hash = tp->tag; + + while (ap) + { + hash = (hash << 6) ^ ((hash >> 11) & 0xff); + hash ^= (ap->attr) | (ap->form << 8); + + ap = ap->next; + } + if (hash<0) + return -hash; + else + return hash; +} + +/*----------------------------------------------------------------------*/ +/* dwTraverseTag - perform a depth-first preorder traversal of a tag */ +/* tree, calling the user function at each node. The */ +/* user function is also called with a NULL tag pointer */ +/* after the last sibling of each immediate family is */ +/* processed. */ +/*----------------------------------------------------------------------*/ +static int +dwTraverseTag (dwtag *tp, int (*somefunc)(dwtag *tp, void * info), void * info) +{ + int rvalue = 0; + + while (tp) + { + rvalue += (*somefunc)(tp, info); + if (tp->firstChild) + rvalue += dwTraverseTag (tp->firstChild, somefunc, info); + tp = tp->siblings; + } + rvalue += (*somefunc)(NULL, info); + + return rvalue; +} + +/*----------------------------------------------------------------------*/ +/* dwAssignAbbrev - find a matching abbreviation for a tag or create a */ +/* a new one and assign it */ +/*----------------------------------------------------------------------*/ +static int +dwAssignAbbrev (dwtag *tp, void *info) +{ + dwtag * oldtp; + int * anp = info; /* pointer to current abbreviation number */ + int key; + + if (!tp) + return 0; + + key = dwHashTag (tp) % dwAbbrevTable->size; + oldtp = hTabFindByKey (dwAbbrevTable, key, tp, dwMatchTagAttr); + if (oldtp) + { + tp->abbrev = oldtp->abbrev; + return 0; + } + else + { + tp->abbrev = ++(*anp); + hTabAddItemLong (&dwAbbrevTable, key, tp, tp); + return 1; + } +} + +/*-----------------------------------------------------------------------*/ +/* dwWriteAbbrevs - write the abbreviations to the .debug_abbrev section */ +/*-----------------------------------------------------------------------*/ +static void +dwWriteAbbrevs (void) +{ + dwtag * tp; + dwattr * ap; + int key; + + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_abbrev (NOLOAD)"); + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_abbrev"); + + tp = hTabFirstItem (dwAbbrevTable, &key); + for (; tp; tp = hTabNextItem (dwAbbrevTable, &key)) + { + dwWriteULEB128 (NULL, tp->abbrev, NULL); + dwWriteULEB128 (NULL, tp->tag, NULL); + dwWriteByte (NULL, tp->firstChild ? DW_CHILDREN_yes : DW_CHILDREN_no, + NULL); + ap = tp->attribs; + while (ap) + { + dwWriteULEB128 (NULL, ap->attr, NULL); + dwWriteULEB128 (NULL, ap->form, NULL); + ap = ap->next; + } + dwWriteULEB128 (NULL, 0, NULL); + dwWriteULEB128 (NULL, 0, NULL); + + } + dwWriteULEB128 (NULL, 0, NULL); + + hTabDeleteAll (dwAbbrevTable); +} + + + +/*-----------------------------------------------------------------------*/ +/* dwWriteTag - write the encoded tag information */ +/*-----------------------------------------------------------------------*/ +static int +dwWriteTag (dwtag *tp, void *info) +{ + dwattr * ap; + + if (!tp) + { + /* mark the end of this series of siblings */ + dwWriteULEB128 (NULL, 0, NULL); + return 0; + } + + //fprintf (dwarf2FilePtr, "; baseOffset = 0x%x\n", tp->baseOffset); + + /* write the tag abbreviation */ + dwWriteULEB128 (NULL, tp->abbrev, NULL); + + /* write the values of the attributes */ + ap = tp->attribs; + while (ap) + { + dwWriteAttr (ap); + ap = ap->next; + } + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteTags - write all the tags to the .debug_info section */ +/*-----------------------------------------------------------------------*/ +static void +dwWriteTags (void) +{ + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_info (NOLOAD)"); + + dwWriteWordDelta ("Ldebug_info_end", "Ldebug_info_start"); + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_info_start"); + + dwWriteHalf (NULL, 2, NULL); /* DWARF version */ + + dwWriteWord ("Ldebug_abbrev", 0, NULL); + + dwWriteByte (NULL, port->debugger.dwarf.addressSize, NULL); + + dwTraverseTag (dwRootTag, dwWriteTag, NULL); + + dwWriteULEB128 (NULL, 0, NULL); + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_info_end"); + +} + +/*-----------------------------------------------------------------------*/ +/* dwAssignTagAddress - assign the current address to the current tag. */ +/* Compute the next address based on the tag size */ +/*-----------------------------------------------------------------------*/ +static int +dwAssignTagAddress (dwtag *tp, void *info) +{ + int * tap = info; + dwattr * ap; + + if (!tp) + { + *tap += 1; + return 0; + } + + tp->baseOffset = *tap; + + *tap += dwSizeofULEB128 (tp->abbrev); + + ap = tp->attribs; + while (ap) + { + *tap += dwSizeofAttr (ap); + ap = ap->next; + } + + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* dwAddSibAttr - if a tag has children and a sibling, add a sibling */ +/* attribute (it allows debuggers to jump to the sibling */ +/* and skip the child data) */ +/*-----------------------------------------------------------------------*/ +static int +dwAddSibAttr (dwtag *tp, void *info) +{ + if (!tp) + return 0; + if (tp == dwRootTag) + return 0; + + if (tp->firstChild && tp->siblings) + dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_sibling, tp->siblings)); + + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* dwDeleteTagAttr - given a pointer to an attribute type, delete any */ +/* matching attribute */ +/*-----------------------------------------------------------------------*/ +static int +dwDeleteTagAttr (dwtag *tp, void *info) +{ + int attr = *((int *) info); + dwattr * ap; + + if (!tp) + return 0; + + ap = tp->attribs; + if (ap && ap->attr == attr) + { + tp->attribs = ap->next; + return 1; + } + + while (ap) + { + if (ap->next && ap->next->attr == attr) + { + ap->next = ap->next->next; + return 1; + } + ap = ap->next; + } + + return 0; +} + + +/*------------------------------------------------------------------------*/ + +/*-----------------------------------------------------------------------*/ +/* dwWritePubnames - write all the public names to the .debug_pubnames */ +/* section. Externally visible functions and variables */ +/* are considered to have public names. */ +/*-----------------------------------------------------------------------*/ +static void +dwWritePubnames (void) +{ + dwtag * tp; + dwattr * ap1; + dwattr * ap2; + + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_pubnames (NOLOAD)"); + + dwWriteWordDelta ("Ldebug_pubnames_end", "Ldebug_pubnames_start"); + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_pubnames_start"); + + dwWriteHalf (NULL, 2, NULL); /* DWARF version */ + + dwWriteWord ("Ldebug_info_start-4", 0, NULL); + dwWriteWordDelta ("4+Ldebug_info_end", "Ldebug_info_start"); + + if (dwRootTag && dwRootTag->firstChild) + { + tp = dwRootTag->firstChild; + while (tp) + { + if (tp->tag == DW_TAG_variable || tp->tag == DW_TAG_subprogram) + { + /* If it has a name and is externally visible, it's a pubname */ + ap1 = dwFindAttr (tp, DW_AT_external); + ap2 = dwFindAttr (tp, DW_AT_name); + if (ap1 && ap1->val.data && ap2) + { + dwWriteWord (NULL, tp->baseOffset, NULL); + dwWriteString (ap2->val.string, NULL); + } + } + + tp = tp->siblings; + } + } + dwWriteWord (NULL, 0, NULL); + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_pubnames_end"); +} + +/*------------------------------------------------------------------------*/ + +/*-----------------------------------------------------------------------*/ +/* dwFindFileIndex - find the index of a filename in dwFilenameSet; if */ +/* it does not exist, it is added */ +/*-----------------------------------------------------------------------*/ +static int +dwFindFileIndex (char * filename) +{ + char * includeDir; + dwfile * srcfile; + int fileIndex = 1; + int dirIndex = 1; + + /* Just do a linear search for the file. There should be hardly */ + /* a penalty since 1) most calls search for the first file, and */ + /* 2) the file set is usually small (often just 1 item) */ + for (srcfile = setFirstItem (dwFilenameSet); + srcfile; + srcfile = setNextItem(dwFilenameSet), fileIndex++ ) + { + if (!strcmp (srcfile->name, filename)) + return fileIndex; + } + + for (includeDir = setFirstItem (includeDirsSet); + includeDir; + includeDir = setNextItem(includeDirsSet), dirIndex++ ) + { + if (!strncmp (includeDir, filename, strlen (includeDir)) + && strlen (filename) > strlen (includeDir)) + { + if (IS_DIR_SEPARATOR(filename[strlen (includeDir)])) + break; + } + } + if (!includeDir) + dirIndex = 0; + + srcfile = Safe_alloc (sizeof (dwfile)); + srcfile->name = filename; + srcfile->dirIndex = dirIndex; + srcfile->timestamp = 0; + srcfile->length = 0; + + addSet (&dwFilenameSet, srcfile); + return fileIndex; +} + +/*-----------------------------------------------------------------------*/ +/* dwWriteLineNumber - write line number (and related position info) to */ +/* address corespondence data for a single node */ +/*-----------------------------------------------------------------------*/ +static void +dwWriteLineNumber (dwline * lp) +{ + static int curFileIndex = 1; + static int curLine = 1; + static char * curLabel = NULL; + static int curOffset = 0; + int deltaLine = lp->line - curLine; + int deltaAddr = lp->offset - curOffset; + int deltaAddrValid = curLabel && lp->label && !strcmp (lp->label, curLabel); + + //fprintf (dwarf2FilePtr, "; line %d\n", lp->line); + if (lp->begin_sequence) + { + curFileIndex = 1; + curLine = 1; + curLabel = NULL; + curOffset = 0; + + if (lp->end_sequence) + return; + } + + if (lp->fileIndex != curFileIndex) + { + dwWriteByte (NULL, DW_LNS_set_file, NULL); + dwWriteULEB128 (NULL, lp->fileIndex, NULL); + curFileIndex = lp->fileIndex; + } + + if (lp->basic_block) + { + dwWriteByte (NULL, DW_LNS_set_basic_block, NULL); + } + + if (lp->begin_sequence) + { + dwWriteByte (NULL, 0, NULL); + dwWriteULEB128 (NULL, 1+port->debugger.dwarf.addressSize, NULL); + dwWriteByte (NULL, DW_LNE_set_address, NULL); + dwWriteAddress (lp->label, lp->offset, NULL); + curLabel = lp->label; + curOffset = lp->offset; + + dwWriteByte (NULL, DW_LNS_advance_line, NULL); + dwWriteSLEB128 (NULL, lp->line - 1, NULL); + curLine = lp->line; + + dwWriteByte (NULL, DW_LNS_copy, NULL); + } + else if (lp->end_sequence) + { + if (deltaAddrValid) + { + dwWriteByte (NULL, DW_LNS_advance_pc, NULL); + dwWriteULEB128 (NULL, deltaAddr, NULL); + } + else + { + dwWriteByte (NULL, DW_LNS_fixed_advance_pc, NULL); + dwWriteHalfDelta (lp->label, curLabel, lp->offset-curOffset); + curLabel = lp->label; + curOffset = lp->offset; + } + + dwWriteByte (NULL, 0, NULL); + dwWriteULEB128 (NULL, 1, NULL); + dwWriteByte (NULL, DW_LNE_end_sequence, NULL); + } + else + { + int usedSpecial = 0; + + /* Metrowerks CW08 V3.0 gets confused by this. Just use the long */ + /* encoding until we can find a more compatible phrasing. */ + #if 0 + if (deltaLine >= dwLineBase && deltaLine < (dwLineBase+dwLineRange)) + { + int opcode; + + /* try to build a "special" opcode */ + opcode = dwLineOpcodeBase + (deltaLine - dwLineBase); + if (deltaAddrValid) + opcode += deltaAddr*dwLineRange; + + if (opcode >= dwLineOpcodeBase && opcode <= 255) + { + /* ok, we can use a "special" opcode */ + + /* If the deltaAddr value was symbolic, it can't be part */ + /* of the "special" opcode, so encode it seperately */ + if (!deltaAddrValid) + { + dwWriteByte (NULL, DW_LNS_advance_pc, NULL); + dwWriteULEB128Delta (lp->label, curLabel, lp->offset-curOffset); + curLabel = lp->label; + curOffset = lp->offset; + } + + /* Write the "special" opcode */ + dwWriteByte (NULL, opcode, NULL); + curLine = lp->line; + usedSpecial = 1; + } + } + #endif + + /* If we couldn't use the "special" opcode, we will have to */ + /* encode this the long way. */ + if (!usedSpecial) + { + dwWriteByte (NULL, DW_LNS_fixed_advance_pc, NULL); + dwWriteHalfDelta (lp->label, curLabel, lp->offset-curOffset); + curLabel = lp->label; + curOffset = lp->offset; + + dwWriteByte (NULL, DW_LNS_advance_line, NULL); + dwWriteSLEB128 (NULL, deltaLine, NULL); + curLine = lp->line; + + dwWriteByte (NULL, DW_LNS_copy, NULL); + } + + } + +} + +/*-----------------------------------------------------------------------*/ +/* dwWriteLineNumbers - write all the source line number position data */ +/* to the .debug_line section */ +/*-----------------------------------------------------------------------*/ +static void +dwWriteLineNumbers (void) +{ + char * includeDir; + dwfile * srcfile; + dwline * lp; + + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_line (NOLOAD)"); + + dwWriteWordDelta ("Ldebug_line_end", "Ldebug_line_start"); + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_start"); + + dwWriteHalf (NULL, 2, NULL); /* DWARF version */ + + dwWriteWordDelta ("Ldebug_line_stmt-6", "Ldebug_line_start"); + + dwWriteByte (NULL, 1, NULL); /* we track everything in 1 byte increments */ + + dwWriteByte (NULL, 1, NULL); /* assume every line is a new statement */ + + dwWriteByte (NULL, dwLineBase, NULL); + dwWriteByte (NULL, dwLineRange, NULL); + + dwWriteByte (NULL, 9+1, NULL); /* there are 9 standard opcodes */ + + dwWriteByte (NULL, 0, NULL); /* number of DW_LNS_copy arguments */ + dwWriteByte (NULL, 1, NULL); /* number of DW_LNS_advance_pc arguments */ + dwWriteByte (NULL, 1, NULL); /* number of DW_LNS_advance_line arguments */ + dwWriteByte (NULL, 1, NULL); /* number of DW_LNS_set_file arguments */ + dwWriteByte (NULL, 1, NULL); /* number of DW_LNS_set_column arguments */ + dwWriteByte (NULL, 0, NULL); /* number of DW_LNS_negate_stmt arguments */ + dwWriteByte (NULL, 0, NULL); /* number of DW_LNS_set_basic_block arguments */ + dwWriteByte (NULL, 0, NULL); /* number of DW_LNS_const_add_pc arguments */ + dwWriteByte (NULL, 1, NULL); /* number of DW_LNS_fixed_advance_pc arguments */ + + /* Write the list of source directories searched */ + for (includeDir = setFirstItem (includeDirsSet); + includeDir; + includeDir = setNextItem(includeDirsSet) ) + dwWriteString (includeDir, NULL); + dwWriteByte (NULL, 0, NULL); + + /* Write the list of source files used */ + for (srcfile = setFirstItem (dwFilenameSet); + srcfile; + srcfile = setNextItem(dwFilenameSet) ) + { + dwWriteString (srcfile->name, NULL); + dwWriteULEB128 (NULL, srcfile->dirIndex, NULL); + dwWriteULEB128 (NULL, srcfile->timestamp, NULL); + dwWriteULEB128 (NULL, srcfile->length, NULL); + } + dwWriteByte (NULL, 0, NULL); + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_stmt"); + + lp = dwLineFirst; + if (lp) + lp->begin_sequence = 1; + while (lp) + { + fprintf(dwarf2FilePtr, ";sdccrm - label [\"%s\"], line %d\n", lp->label, lp->line); + + dwWriteLineNumber (lp); + if (lp->end_sequence && lp->next) + lp->next->begin_sequence = 1; + lp = lp->next; + } + + tfprintf (dwarf2FilePtr, "!slabeldef\n", "Ldebug_line_end"); +} + +/*------------------------------------------------------------------------*/ + + +static void +dwWriteCFAinstructions (dwcfins *ip) +{ + dwcfop * op = ip->first; + + while (op) + { + dwWriteByte (NULL, op->opcode, NULL); + switch (op->opcode >> 6) + { + case 0: + switch (op->opcode) + { + case DW_CFA_set_loc: + dwWriteAddress (op->label, op->operand1, NULL); + break; + + case DW_CFA_advance_loc1: + dwWriteByte (NULL, op->operand1, NULL); + break; + + case DW_CFA_advance_loc2: + dwWriteHalf (NULL, op->operand1, NULL); + break; + + case DW_CFA_advance_loc4: + dwWriteWord (NULL, op->operand1, NULL); + break; + + case DW_CFA_def_cfa: + case DW_CFA_register: + case DW_CFA_offset_extended: + dwWriteULEB128 (NULL, op->operand1, NULL); + dwWriteULEB128 (NULL, op->operand2, NULL); + break; + + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_restore_extended: + dwWriteULEB128 (NULL, op->operand1, NULL); + break; + } + break; + + case DW_CFA_restore >> 6: + case DW_CFA_advance_loc >> 6: + break; + + case DW_CFA_offset >> 6: + dwWriteULEB128 (NULL, op->operand1, NULL); + break; + } + op = op->next; + } +} + +static int +dwSizeofCFAinstructions (dwcfins *ip) +{ + int size = 0; + dwcfop * op = ip->first; + + while (op) + { + size++; + switch (op->opcode >> 6) + { + case 0: + switch (op->opcode) + { + case DW_CFA_set_loc: + size += port->debugger.dwarf.addressSize; + break; + + case DW_CFA_advance_loc1: + size += 1; + break; + + case DW_CFA_advance_loc2: + size += 2; + break; + + case DW_CFA_advance_loc4: + size += 4; + break; + + case DW_CFA_def_cfa: + case DW_CFA_register: + case DW_CFA_offset_extended: + size += dwSizeofULEB128 (op->operand1); + size += dwSizeofULEB128 (op->operand2); + break; + + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_restore_extended: + size += dwSizeofULEB128 (op->operand1); + break; + } + break; + + case DW_CFA_restore >> 6: + case DW_CFA_advance_loc >> 6: + break; + + case DW_CFA_offset >> 6: + size += dwSizeofULEB128 (op->operand1); + break; + } + op = op->next; + } + return size; +} + +static dwcfop * +dwNewCFop (int opcode) +{ + dwcfop * op; + + op = Safe_alloc (sizeof (dwcfop)); + op->opcode = opcode; + + return op; +} + +static dwcfins * +dwNewCFins (void) +{ + return (dwcfins *) Safe_alloc (sizeof (dwcfins)); +} + +static void +dwAddCFinsOp (dwcfins * ip, dwcfop *op) +{ + if (ip->last) + ip->last->next = op; + else + ip->first = op; + ip->last = op; +} + +static void +dwGenCFIins (int callsize, int id) +{ + dwcfins * ip; + dwcfop * op; + int i; + char s[32]; + + tfprintf (dwarf2FilePtr, "\n\t!area\n", ".debug_frame (NOLOAD)"); + + /* FIXME: these two dw should be combined into a dd */ + tfprintf (dwarf2FilePtr, "\t!dw\t0\n"); + tfprintf (dwarf2FilePtr, "\t!dw\t"); + tfprintf (dwarf2FilePtr, "Ldebug_CIE%d_end-Ldebug_CIE%d_start\n", id, id); + + snprintf(s, sizeof(s), "Ldebug_CIE%d_start", id); + tfprintf (dwarf2FilePtr, "!slabeldef\n", s); + + tfprintf (dwarf2FilePtr, "\t!dw\t0xffff\n"); + tfprintf (dwarf2FilePtr, "\t!dw\t0xffff\n"); /* CIE_id */ + + tfprintf (dwarf2FilePtr, "\t!db\t%d\n",1); /* CIE version number */ + + tfprintf (dwarf2FilePtr, "\t!db\t%d\n",0); /* augmentation (none) */ + + dwWriteULEB128 (NULL, 1, NULL); /* code alignment factor */ + + dwWriteSLEB128 (NULL, (port->stack.direction > 0) ? 1 : -1, NULL); /* data alignment factor */ + + dwWriteByte (NULL, port->debugger.dwarf.regNumRet, NULL); + + ip = dwNewCFins (); + + /* Define the CFA as the SP at the previous frame (call site) */ + /* The return address is then at CFA-1 (for stm8) */ + op = dwNewCFop (DW_CFA_def_cfa); + op->operand1 = port->debugger.dwarf.regNumSP; + op->operand2 = port->debugger.dwarf.offsetSP; + if (callsize == 3) + op->operand2 = port->debugger.dwarf.offsetSP + port->stack.isr_overhead; + dwAddCFinsOp (ip, op); + + op = dwNewCFop (DW_CFA_offset + port->debugger.dwarf.regNumRet); + op->operand1 = 1; /* perhaps we need a way to configure this relative position for the ret pos? ARE*/ + dwAddCFinsOp (ip, op); + + if (port->debugger.dwarf.cfiUndef) + for (i=0; i < port->debugger.dwarf.cfiUndef->size; i++) + { + if (bitVectBitValue (port->debugger.dwarf.cfiUndef, i)) + { + op = dwNewCFop (DW_CFA_undefined); + op->operand1 = i; + dwAddCFinsOp (ip, op); + } + } + + if (port->debugger.dwarf.cfiSame) + for (i=0; i < port->debugger.dwarf.cfiSame->size; i++) + { + if (bitVectBitValue (port->debugger.dwarf.cfiSame, i)) + { + op = dwNewCFop (DW_CFA_same_value); + op->operand1 = i; + dwAddCFinsOp (ip, op); + } + } + + dwWriteCFAinstructions (ip); + + op = ip->first; + while (op) + { + dwcfop * next; + next = op->next; + Safe_free(op); + op = next; + } + Safe_free(ip); + + snprintf(s, sizeof(s), "Ldebug_CIE%d_end", id); + tfprintf (dwarf2FilePtr, "!slabeldef\n",s); +} + + +static void +dwWriteFDE (dwfde * fp, int id) +{ + //length + dwWriteWord (NULL, dwSizeofCFAinstructions(fp->ins) + 4 + + port->debugger.dwarf.addressSize * 2 , NULL); + + //CIE ptr + char s[32]; + snprintf(s, sizeof(s), "Ldebug_CIE%d_start-4", id); + dwWriteWord (s, 0, NULL); + + //initial loc + dwWriteAddress(fp->startLabel, 0, "initial loc"); + + //address range + dwWriteAddressDelta (fp->endLabel, fp->startLabel); + + //instructions + dwWriteCFAinstructions (fp->ins); +} + +static void +dwWriteFrames (void) +{ + dwfde fp; + dwcfop * op; + dwcfilist * cfip; + dwlocregion * lrp; + + int id = 0; + cfip = dwCFIRoot; + while (cfip) + { + fp.startLabel=cfip->startLabel; + fp.endLabel=cfip->endLabel; + + fp.ins = dwNewCFins(); + + lrp = cfip->region; + + /* emit fde records */ + while (lrp) + { + op = dwNewCFop (DW_CFA_set_loc); + op->label = lrp->startLabel; + op->operand1 = 0; + dwAddCFinsOp (fp.ins, op); + op = dwNewCFop (DW_CFA_def_cfa_offset); + if (cfip->callsize == 3) + op->operand1 = lrp->loc->operand.offset + port->debugger.dwarf.offsetSP - 1 + port->stack.isr_overhead; + else + op->operand1 = lrp->loc->operand.offset + port->debugger.dwarf.offsetSP - 1; /* another -1 constant that needs attention ARE*/ + dwAddCFinsOp (fp.ins, op); + + lrp = lrp ->next; + } + + dwGenCFIins(cfip->callsize, id); + dwWriteFDE(&fp, id++); + + op = fp.ins->first; + while (op) + { + dwcfop * next; + next = op->next; + Safe_free(op); + op = next; + } + + cfip = cfip->next; + } +} + + + +/*------------------------------------------------------------------------*/ + + + + +/*-----------------------------------------------------------------------*/ +/* dwHashType - return a hash code for a type chain */ +/*-----------------------------------------------------------------------*/ +static int +dwHashType (sym_link * type) +{ + int hash = 0; + + while (type) + { + hash = (hash << 5) ^ ((hash >> 8) & 0xff); + if (IS_DECL (type)) + { + hash ^= DCL_TYPE (type); + } + else + { + hash ^= SPEC_NOUN (type) + | (SPEC_CONST (type) << 4) + | (SPEC_VOLATILE (type) << 5) + | (SPEC_LONG (type) << 6) + | (SPEC_LONGLONG (type) << 7); + } + + type = type->next; + } + + if (hash<0) + return -hash; + else + return hash; +} + +/*-----------------------------------------------------------------------*/ +/* dwMatchType - returns true if two types match exactly (including type */ +/* qualifiers) */ +/*-----------------------------------------------------------------------*/ +static int +dwMatchTypes (const void * type1v, const void * type2v) +{ + sym_link * type1 = (sym_link *)type1v; + sym_link * type2 = (sym_link *)type2v; + + if (!type1 || !type2) + return 0; + + while (type1 && type2) + { + if (IS_SPEC(type1)) + { + if (IS_SPEC (type2)) + { + if (SPEC_NOUN (type1) != SPEC_NOUN (type2)) + return 0; + if (SPEC_NOUN (type1) == V_STRUCT + && SPEC_STRUCT (type1) != SPEC_STRUCT (type2)) + return 0; + if (SPEC_CONST (type1) != SPEC_CONST (type2)) + return 0; + if (SPEC_VOLATILE (type1) != SPEC_VOLATILE (type2)) + return 0; + if (SPEC_SHORT (type1) != SPEC_SHORT (type2)) + return 0; + if (SPEC_LONG (type1) != SPEC_LONG (type2)) + return 0; + if (SPEC_LONGLONG (type1) != SPEC_LONGLONG (type2)) + return 0; + if (SPEC_USIGN (type1) != SPEC_USIGN (type2)) + return 0; + } + else + return 0; + } + else + { + if (IS_DECL (type2)) + { + if (DCL_TYPE (type1) != DCL_TYPE (type2)) + return 0; + if (DCL_PTR_CONST (type1) != DCL_PTR_CONST (type2)) + return 0; + if (DCL_PTR_VOLATILE (type1) != DCL_PTR_VOLATILE (type2)) + return 0; + if (DCL_TYPE (type1) == ARRAY + && DCL_ELEM (type1) != DCL_ELEM (type2)) + return 0; + /* FIXME: need to match function pointer parameters */ + } + else + return 0; + } + + type1 = type1->next; + type2 = type2->next; + } + + if (!type1 && !type2) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------------*/ +/* dwTagFromType - returns the tag describing a type. If new tags need */ +/* to be created, they will be added under the specified */ +/* parent tag */ +/*-----------------------------------------------------------------------*/ +static dwtag * +dwTagFromType (sym_link * type, dwtag * parent) +{ + dwtag * oldtp; + dwtag * tp = NULL; + dwtag * modtp; + dwtag * subtp; + int key; + int tableUpdated = 0; + + key = dwHashType (type) % dwTypeTagTable->size; + oldtp = hTabFindByKey (dwTypeTagTable, key, type, dwMatchTypes); + if (oldtp) + return oldtp; + else + { + if (IS_DECL (type)) + { + switch (DCL_TYPE (type)) + { + case POINTER: + case FPOINTER: + case CPOINTER: + case GPOINTER: + case PPOINTER: + case IPOINTER: + case EEPPOINTER: + case UPOINTER: + tp = dwNewTag (DW_TAG_pointer_type); + if (type->next && !IS_VOID (type->next)) + { + subtp = dwTagFromType (type->next, parent); + dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp)); + } + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (parent, tp); + if (DCL_PTR_VOLATILE (type)) + { + modtp = dwNewTag (DW_TAG_volatile_type); + dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp)); + dwAddTagChild (parent, modtp); + tp = modtp; + } + if (DCL_PTR_CONST (type)) + { + modtp = dwNewTag (DW_TAG_const_type); + dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp)); + dwAddTagChild (parent, modtp); + tp = modtp; + } + break; + + case ARRAY: + tp = dwNewTag (DW_TAG_array_type); + subtp = dwTagFromType (type->next, parent); + dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp)); + if (!subtp->parent) + dwAddTagChild (tp, subtp); + if (DCL_ELEM (type)) + { + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + subtp = dwNewTag (DW_TAG_subrange_type); + dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_upper_bound, + DCL_ELEM (type)-1)); + dwAddTagChild (tp, subtp); + } + + break; + + case FUNCTION: + tp = dwNewTag (DW_TAG_subroutine_type); + if (type->next && !IS_VOID (type->next)) + { + subtp = dwTagFromType (type->next, parent); + dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp)); + } + /* FIXME: need to handle function parameters */ + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unknown DCL_TYPE"); + exit (1); + } + } + else + { + if (IS_STRUCT (type)) + { + struct structdef * sdp = SPEC_STRUCT (type); + symbol * field; + + tp = dwNewTag (sdp->type == STRUCT ? DW_TAG_structure_type + : DW_TAG_union_type); + if (*(sdp->tag)) + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sdp->tag)); + + /* FIXME: should only specify the size if we know this */ + /* is a complete type */ + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + + /* Must add this before processing the struct fields */ + /* in case there is a recursive definition. */ + hTabAddItemLong (&dwTypeTagTable, key, type, tp); + tableUpdated = 1; + + field = sdp->fields; + while (field) + { + dwtag * memtp; + dwloc * lp; + + if (IS_BITFIELD (field->type) && !SPEC_BLEN(field->type)) + { + field = field->next; + continue; + } + + memtp = dwNewTag (DW_TAG_member); + if (*(field->name)) + dwAddTagAttr (memtp, dwNewAttrString (DW_AT_name, + field->name)); + if (IS_BITFIELD (field->type)) + { + unsigned blen = SPEC_BLEN (field->type); + unsigned bstr = SPEC_BSTR (field->type); + sym_link * type; + + dwAddTagAttr (memtp, + dwNewAttrConst (DW_AT_byte_size, + (blen+7)/8)); + dwAddTagAttr (memtp, + dwNewAttrConst (DW_AT_bit_size, blen)); + dwAddTagAttr (memtp, + dwNewAttrConst (DW_AT_bit_offset, + ((blen+7) & ~7) + - (blen+bstr))); + if (blen < 8) + type = typeFromStr ("Uc"); + else + type = typeFromStr ("Ui"); + subtp = dwTagFromType (type, tp); + dwAddTagAttr (memtp, dwNewAttrTagRef (DW_AT_type, subtp)); + } + else + { + subtp = dwTagFromType (field->type, tp); + dwAddTagAttr (memtp, dwNewAttrTagRef (DW_AT_type, subtp)); + if (!subtp->parent) + dwAddTagChild (parent, subtp); + } + + lp = dwNewLoc (DW_OP_plus_uconst, NULL, field->offset); + dwAddTagAttr (memtp, + dwNewAttrLoc (DW_AT_data_member_location, lp)); + + dwAddTagChild (tp, memtp); + + field = field->next; + } + } + else if (SPEC_VOLATILE (type) || SPEC_CONST (type)) + { + sym_link temptype = *type; + + SPEC_VOLATILE (&temptype) = 0; + SPEC_CONST (&temptype) = 0; + tp = dwTagFromType (&temptype, parent); + if (SPEC_VOLATILE (type)) + { + modtp = dwNewTag (DW_TAG_volatile_type); + dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp)); + dwAddTagChild (parent, modtp); + tp = modtp; + } + if (SPEC_CONST (type)) + { + modtp = dwNewTag (DW_TAG_const_type); + dwAddTagAttr (modtp, dwNewAttrTagRef (DW_AT_type, tp)); + dwAddTagChild (parent, modtp); + tp = modtp; + } + } + else + { + switch (SPEC_NOUN (type)) + { + case V_INT: + tp = dwNewTag (DW_TAG_base_type); + if (SPEC_USIGN (type)) + { + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_unsigned)); + if (SPEC_LONGLONG (type)) + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, + "unsigned long long")); + else if (SPEC_LONG (type)) + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, + "unsigned long")); + else + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, + "unsigned int")); + } + else + { + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_signed)); + if (SPEC_LONGLONG (type)) + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "long long")); + else if (SPEC_LONG (type)) + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "long")); + else + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "int")); + } + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (dwRootTag, tp); + break; + + case V_FLOAT: + tp = dwNewTag (DW_TAG_base_type); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_float)); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "float")); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (dwRootTag, tp); + break; + + case V_FIXED16X16: + tp = dwNewTag (DW_TAG_base_type); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_float)); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "fixed16x16")); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (dwRootTag, tp); + break; + + case V_BOOL: + tp = dwNewTag (DW_TAG_base_type); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_boolean)); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "_Bool")); + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (dwRootTag, tp); + break; + + case V_CHAR: + tp = dwNewTag (DW_TAG_base_type); + if (SPEC_USIGN (type)) + { + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_unsigned_char)); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, + "unsigned char")); + } + else + { + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_encoding, + DW_ATE_signed)); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, "signed char")); + } + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_byte_size, + getSize (type))); + dwAddTagChild (dwRootTag, tp); + break; + + case V_VOID: + case V_BIT: + case V_BITFIELD: + case V_SBIT: + case V_DOUBLE: + default: + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "unhandled base type"); + printTypeChain (type, NULL); + exit (1); + + } + } + } + } + + if (!tableUpdated) + hTabAddItemLong (&dwTypeTagTable, key, type, tp); + if (!tp->parent) + dwAddTagChild (parent, tp); + return tp; +} +/*------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* dwOpenFile - open the debugging file (just initialize, since all */ +/* DWARF data goes into the assembly output file) */ +/*-----------------------------------------------------------------------*/ +int +dwOpenFile(const char *file) +{ + dwTypeTagTable = newHashTable (128); + + return 1; +} + +/*-----------------------------------------------------------------------*/ +/* dwCloseFile - close the debugging file (do nothing, since all DWARF */ +/* data goes into the assembly output file) */ +/*-----------------------------------------------------------------------*/ +int +dwCloseFile (void) +{ + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwGenerateScopes - recursively traverse an ast, generating lexical */ +/* block tags for block scopes found */ +/*-----------------------------------------------------------------------*/ +static void +dwGenerateScopes (dwtag *tp, ast * tree) +{ + dwtag *subtp; + + if (!tree) + return; + + if (!IS_AST_OP (tree)) + return; + + if (tree->opval.op == BLOCK) + { + subtp = dwNewTag (DW_TAG_lexical_block); + if (tree->right) + { + dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_user_block, tree->right->block)); + dwAddTagAttr (subtp, dwNewAttrConst (DW_AT_user_level, tree->right->level)); + + dwAddTagChild (tp, subtp); + } + dwGenerateScopes (subtp, tree->right); + } + else + { + dwGenerateScopes (tp, tree->left); + dwGenerateScopes (tp, tree->right); + } +} + +/*-----------------------------------------------------------------------*/ +/* dwFindScope - return the lexical block tag for a particular block */ +/* scope, or NULL if not found */ +/*-----------------------------------------------------------------------*/ +static dwtag * +dwFindScope (dwtag * tp, int block) +{ + dwtag * rettp; + dwattr * ap; + + if (!tp) + return NULL; + + while (tp) + { + if (tp->tag == DW_TAG_lexical_block) + { + ap = tp->attribs; + while (ap) + { + if (ap->attr == DW_AT_user_block) + { + if (ap->val.data == block) + return tp; + } + ap = ap->next; + } + + rettp = dwFindScope (tp->firstChild, block); + if (rettp) + return rettp; + } + tp = tp->siblings; + } + + return NULL; +} + +/*------------------------------------------------------------------------*/ +/* dwWriteSymbolInternal - create tag information for a variable or */ +/* parameter and place it in the correct position */ +/* within the tag tree */ +/*------------------------------------------------------------------------*/ +static int +dwWriteSymbolInternal (symbol *sym) +{ + dwtag *tp; + dwtag *subtp; + dwloc *lp; + dwtag *scopetp; + symbol *symloc; + dwtag *functp; + dwattr *funcap; + bool inregs = FALSE; + + if (!sym->level || IS_EXTERN (sym->etype)) + scopetp = dwRootTag; + else + { + assert(sym->localof); + if (!sym->localof) + return 0; + + /* Find the tag for the function this symbol is defined in */ + functp = dwRootTag->firstChild; + while (functp) + { + if (functp->tag == DW_TAG_subprogram) + { + funcap = dwFindAttr (functp, DW_AT_name); + if (funcap && !strcmp (funcap->val.string, sym->localof->name)) + break; + } + functp = functp->siblings; + } + + /* There might be no function found if it was only inlined and */ + /* not generated as a stand-alone function. In that case, */ + /* dwWriteSymbolInternal will be called again later in another */ + /* context where the function will be found. */ + if (!functp) + return 0; + + /* Find the correct scope within this function */ + scopetp = dwFindScope (functp->firstChild, sym->block); + if (!scopetp) + scopetp = functp; + } + + tp = dwNewTag (sym->_isparm ? DW_TAG_formal_parameter : DW_TAG_variable); + + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sym->name)); + + /* Find the ultimate symbol holding the value. */ + /* Might be: */ + /* a) original symbol, */ + /* b) register equivalent, */ + /* c) spill location */ + symloc = sym; + if (/*!sym->allocreq &&*/ sym->reqv) + { + symloc = OP_SYMBOL (symloc->reqv); + + for (int i = 0; i < symloc->nRegs; i++) + if (symloc->regs[i]) + { + inregs = TRUE; + break; + } + + if (!inregs && symloc->isspilt && !symloc->remat) + symloc = symloc->usl.spillLoc; + } + + lp = NULL; + if (inregs) /* Variable (partially) in registers*/ + { + dwloc *reglp; + dwloc *lastlp = NULL; + symbol *spillloc = NULL; + int regNum, i, stack = 0; + int stackchange = port->little_endian ? port->stack.direction : -port->stack.direction; + + if ((spillloc = symloc->usl.spillLoc) && spillloc->onStack) + stack = (port->little_endian ? spillloc->stack + symloc->nRegs - 1 : spillloc->stack); + + /* register allocation */ + for (i = (port->little_endian ? 0 : symloc->nRegs-1); + (port->little_endian ? (i < symloc->nRegs) : (i >= 0)); + (port->little_endian ? i++ : i--), stack += stackchange) + { + if (!symloc->regs[i]) /* Spilt byte of variable */ + { + if (!(spillloc && spillloc->onStack)) + { + lp = NULL; + break; + } + reglp = dwNewLoc (DW_OP_fbreg, NULL, stack); + } + else /* Byte in registers */ + { + regNum = port->debugger.dwarf.regNum (symloc->regs[i]); + if (regNum >= 0 && regNum <= 31) + reglp = dwNewLoc (DW_OP_reg0 + regNum, NULL, 0); + else if (regNum >= 0) + reglp = dwNewLoc (DW_OP_regx, NULL, regNum); + else + { + /* We are forced to give up if the ABI for this port */ + /* does not define a number for this register */ + lp = NULL; + break; + } + } + + if (lastlp) + lastlp->next = reglp; + else + lp = reglp; + lastlp = reglp; + + if (symloc->nRegs != 1) + { + reglp = dwNewLoc (DW_OP_piece, NULL, 1); + lastlp->next = reglp; + lastlp = reglp; + } + } + } + else if (symloc->onStack) + { + /* stack allocation */ + lp = dwNewLoc (DW_OP_fbreg, NULL, symloc->stack); + } + else + { + /* global allocation */ + if (sym->level && !sym->allocreq) + lp = NULL; + else + lp = dwNewLoc (DW_OP_addr, symloc->rname, 0); + } + + /* Only create the DW_AT_location if a known location exists. */ + /* It might not exist if the variable has been optimized away */ + /* or if the compiler has lost track of it (not good, but still */ + /* happens sometimes -- need to improve induction) */ + if (lp) + dwAddTagAttr (tp, dwNewAttrLoc (DW_AT_location, lp)); + + if (!IS_STATIC (sym->etype) && !sym->level) + dwAddTagAttr (tp, dwNewAttrFlag (DW_AT_external, 1)); + if (IS_EXTERN (sym->etype)) + dwAddTagAttr (tp, dwNewAttrFlag (DW_AT_declaration, 1)); + + subtp = dwTagFromType (sym->type, scopetp); + dwAddTagAttr (tp, dwNewAttrTagRef (DW_AT_type, subtp)); + if (!subtp->parent) + dwAddTagChild (scopetp, subtp); + + dwAddTagChild (scopetp, tp); + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteFunction - generate a tag for a function. */ +/*-----------------------------------------------------------------------*/ +int +dwWriteFunction (symbol *sym, iCode *ic) +{ + dwtag * tp; + value * args; + + /* Add this function to CFI list */ + dwcfilist * cfip; + cfip = dwNewCFIlist(); + cfip->callsize = 1; + if (FUNC_ISISR (sym->type)) + cfip->callsize = 3; + cfip->next = dwCFIRoot; + dwCFIRoot = cfip; + dwCFILastLoc = NULL; + + dwFuncTag = tp = dwNewTag (DW_TAG_subprogram); + + dwAddTagAttr (dwFuncTag, dwNewAttrString (DW_AT_name, sym->name)); + + dwAddTagAttr (dwFuncTag, dwNewAttrAddrSymbol (DW_AT_low_pc, sym, 0)); + + if (FUNC_ISISR (sym->type)) + dwAddTagAttr (dwFuncTag, dwNewAttrConst (DW_AT_calling_convention, + DW_CC_nocall)); + + dwAddTagAttr (dwFuncTag, dwNewAttrFlag (DW_AT_external, + !IS_STATIC (sym->etype))); + + if (sym->type->next && !IS_VOID (sym->type->next)) + { + dwtag * subtp; + + subtp = dwTagFromType (sym->type->next, dwRootTag); + dwAddTagAttr (dwFuncTag, dwNewAttrTagRef (DW_AT_type, subtp)); + } + dwAddTagChild (dwRootTag, dwFuncTag); + + args = FUNC_ARGS(sym->type); + while (args) + { + dwWriteSymbolInternal (args->sym); + args = args->next; + } + if (FUNC_HASVARARGS (sym->type)) + { + dwAddTagChild (dwFuncTag, dwNewTag (DW_TAG_unspecified_parameters)); + } + + while (ic && ic->op != FUNCTION) + ic = ic->next; + if (ic && ic->op == FUNCTION && ic->tree && ic->tree->right) + { + dwGenerateScopes (dwFuncTag, ic->tree->right->left); + dwGenerateScopes (dwFuncTag, ic->tree->right->right); + } + + dwScopeTag = NULL; + dwScopeLevel = 0; + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteEndFunction - write attributes to the current function tag */ +/* that are only known after code generation is */ +/* complete */ +/*-----------------------------------------------------------------------*/ +int +dwWriteEndFunction (symbol *sym, iCode *ic, int offset) +{ + char debugSym[SDCC_NAME_MAX + 1]; + + if (ic) + { + dwWriteCLine (ic); + dwLineLast->offset += offset; + dwLineLast->end_sequence = 1; + } + + if (IS_STATIC (sym->etype)) + sprintf (debugSym, "XF%s$%s$0$0", dwModuleName, sym->name); + else + sprintf (debugSym, "XG$%s$0$0", sym->name); + emitDebuggerSymbol (debugSym); + + dwAddTagAttr (dwFuncTag, dwNewAttrAddrLabel (DW_AT_high_pc, + Safe_strdup(debugSym), + offset)); + + if (dwFrameLocList) + { + dwAddTagAttr (dwFuncTag, dwNewAttrLocRef (DW_AT_frame_base, + dwFrameLocList)); + + dwFrameLocList->next = dwRootLocList; + dwRootLocList = dwFrameLocList; + dwFrameLocList = NULL; + } + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteLabel - generate a tag for a source level label */ +/*-----------------------------------------------------------------------*/ +int +dwWriteLabel (symbol *sym, const iCode *ic) +{ + char debugSym[SDCC_NAME_MAX + 1]; + dwtag * tp; + + /* ignore the compiler generated labels */ + if (sym->isitmp) + return 1; + + sprintf (debugSym, "L%s$%s$%s", dwModuleName, currFunc->name, sym->name); + emitDebuggerSymbol (debugSym); + + tp = dwNewTag (DW_TAG_label); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, sym->name)); + dwAddTagAttr (tp, dwNewAttrAddrLabel (DW_AT_low_pc, + Safe_strdup (debugSym), 0)); + + dwAddTagChild (dwFuncTag, tp); + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteScope - add the starting and ending address attributes to a */ +/* a lexical block tag (created during dwWriteFunction) */ +/*-----------------------------------------------------------------------*/ +int +dwWriteScope (iCode *ic) +{ + char * debugSym = NULL; + dwtag * scopetp; + dwattr * ap; + + scopetp = dwFindScope (dwFuncTag->firstChild, ic->block); + + if (dwScopeTag && ic->level <= dwScopeLevel) + { + debugSym = dwNewDebugSymbol (); + emitDebuggerSymbol (debugSym); + dwSetTagAttr (dwScopeTag, dwNewAttrAddrLabel (DW_AT_high_pc, debugSym, 0)); + + dwScopeTag = scopetp; + dwScopeLevel = ic->level; + } + if (scopetp) + { + ap = dwFindAttr (scopetp, DW_AT_low_pc); + if (ap) + return 1; + + if (!debugSym) + debugSym = dwNewDebugSymbol (); + emitDebuggerSymbol (debugSym); + dwAddTagAttr (scopetp, dwNewAttrAddrLabel (DW_AT_low_pc, debugSym, 0)); + + dwScopeTag = scopetp; + dwScopeLevel = ic->level; + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ +/* dwWriteSymbol - generate tags for global variables. This is actually */ +/* called for all variables and parameters, but we */ +/* process the non-global variables elsewhere. */ +/*-----------------------------------------------------------------------*/ +int +dwWriteSymbol (symbol *sym) +{ + if (IS_FUNC (sym->type)) + return 1; + + /* If it is an iTemp, then it is a local variable; ignore it */ + if (sym->isitmp) + return 1; + + /* If it is an unused extern ignore it, or it might produce link failure */ + if (IS_EXTERN (sym->etype) && !sym->used) + return 1; + + /* Ignore parameters; they must be handled specially so that they will */ + /* appear in the correct order */ + if (sym->_isparm) + return 1; + + return dwWriteSymbolInternal (sym); +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteType */ +/*-----------------------------------------------------------------------*/ +int +dwWriteType (structdef *sdef, int block, int inStruct, const char *tag) +{ + /* FIXME: needs implementation */ + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteModule - generates the root tag for this compilation unit */ +/*-----------------------------------------------------------------------*/ +int +dwWriteModule (const char *name) +{ + dwtag * tp; + char *verid = (char*)Safe_alloc(125); + + dwModuleName = Safe_strdup (name); + + sprintf(verid, "SDCC version %s #%s", SDCC_VERSION_STR, getBuildNumber()); + + tp = dwNewTag (DW_TAG_compile_unit); + dwAddTagAttr (tp, dwNewAttrString (DW_AT_producer, verid)); + + dwAddTagAttr (tp, dwNewAttrConst (DW_AT_language, DW_LANG_C89)); + + dwAddTagAttr (tp, dwNewAttrString (DW_AT_name, fullSrcFileName)); + + dwAddTagAttr (tp, dwNewAttrLabelRef (DW_AT_stmt_list, + "Ldebug_line_start", -4)); + + dwRootTag = tp; + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteCLine - generates a line number/position to address record for */ +/* C source */ +/*-----------------------------------------------------------------------*/ +int +dwWriteCLine (iCode *ic) +{ + dwline * lp; + char * debugSym; + + lp = Safe_alloc (sizeof (dwline)); + + lp->line = ic->lineno; + + debugSym = dwNewDebugSymbol (); + emitDebuggerSymbol (debugSym); + lp->label = debugSym; + lp->offset = 0; + + lp->fileIndex = dwFindFileIndex (ic->filename); + + if (!dwLineFirst) + dwLineFirst = lp; + else + dwLineLast->next = lp; + dwLineLast = lp; + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteFrameAddress - note the current position of the frame pointer */ +/* address. The base address can be specified by */ +/* either a register or pointer variable, leaving */ +/* the other as NULL. If both are NULL, there is */ +/* no current frame pointer address defined. */ +/*-----------------------------------------------------------------------*/ +int +dwWriteFrameAddress(const char *variable, struct reg_info *reg, int offset) +{ + char * debugSym = NULL; + dwlocregion * lrp; + dwlocregion * cfi_lrp; + dwloc * lp; + int regNum; + + /* If there was a region open, close it */ + if (dwFrameLastLoc) + { + debugSym = dwNewDebugSymbol (); + emitDebuggerSymbol (debugSym); + + dwFrameLastLoc->endLabel = debugSym; + dwFrameLastLoc = NULL; + } + + if (dwCFILastLoc) + { + dwCFILastLoc->endLabel = debugSym; + dwCFIRoot->endLabel = debugSym; + } + + + if (!variable && !reg) + return 1; + + /* Create a new debugger symbol for the start of the region if */ + /* we can't recycle the symbol at the end of the previous */ + if (!debugSym) + { + debugSym = dwNewDebugSymbol (); + emitDebuggerSymbol (debugSym); + } + + lrp = Safe_alloc (sizeof (dwlocregion)); + lrp->startLabel = debugSym; + + /*** Create a new loc region for CFI */ + cfi_lrp = Safe_alloc (sizeof (dwlocregion)); + cfi_lrp->startLabel = debugSym; + cfi_lrp->loc = dwNewLoc (0, NULL, offset); + if (dwCFILastLoc) + { + dwCFILastLoc->next = cfi_lrp; + } + else + { + dwCFIRoot->region = cfi_lrp; + dwCFIRoot->startLabel = debugSym; + } + dwCFILastLoc = cfi_lrp; + + if (variable) /* frame pointer based from a global variable */ + { + dwloc * lp; + + lrp->loc = dwNewLoc (DW_OP_addr, variable, 0); + lrp->loc->next = lp = dwNewLoc (DW_OP_deref_size, NULL, NEARPTRSIZE); + if (offset) + { + lp->next = dwNewLoc (DW_OP_consts, NULL, offset); + lp->next->next = dwNewLoc (DW_OP_plus, NULL, 0); + } + } + else if (reg) /* frame pointer based from a register */ + { + regNum = port->debugger.dwarf.regNum (reg); + assert (regNum>=0); + + if (regNum>=0 && regNum<=31) + { + if (offset) + lrp->loc = dwNewLoc (DW_OP_breg0 + regNum, NULL, offset); + else + lrp->loc = dwNewLoc (DW_OP_reg0 + regNum, NULL, 0); + } + else + { + lrp->loc = lp = dwNewLoc (DW_OP_regx, NULL, regNum); + if (offset) + { + lp->next = dwNewLoc (DW_OP_consts, NULL, offset); + lp->next->next = dwNewLoc (DW_OP_plus, NULL, 0); + } + } + } + dwFrameLastLoc = lrp; + + if (!dwFrameLocList) + dwFrameLocList = dwNewLocList(); + lrp->next = dwFrameLocList->region; + dwFrameLocList->region = lrp; + + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwWriteALine - generates a line number/position to address record for */ +/* assembly source */ +/*-----------------------------------------------------------------------*/ +int +dwWriteALine(const char *module, int Line) +{ + return 1; +} + + +/*-----------------------------------------------------------------------*/ +/* dwarf2FinalizeFile - write all of the DWARF debugging data to the */ +/* debug file */ +/*-----------------------------------------------------------------------*/ +int +dwarf2FinalizeFile (FILE *of) +{ + int tagAddress = 11; + int abbrevNum = 0; + int attr; + + dwarf2FilePtr = of; + + /* Write the .debug_line section */ + dwWriteLineNumbers (); + + /* Assign the location list addresses (for cross references) */ + dwAssignLocListAddresses (); + + /* Write the .debug_loc section */ + dwWriteLocLists (); + + /* Delete our scope related user attributes; they were only needed to help */ + /* build the tag tree and have no meaning to (and may confuse) debuggers */ + attr = DW_AT_user_block; + dwTraverseTag (dwRootTag, dwDeleteTagAttr, &attr); + attr = DW_AT_user_level; + dwTraverseTag (dwRootTag, dwDeleteTagAttr, &attr); + + /* Add a DW_AT_sibling attribute to all tags with children and siblings */ + dwTraverseTag (dwRootTag, dwAddSibAttr, NULL); + + /* Assign the tag abbreviations. The tags, attributes, and forms must */ + /* not change after this point. The attribute values may change as long */ + /* as the size of the value does not. */ + dwAbbrevTable = newHashTable (128); + dwTraverseTag (dwRootTag, dwAssignAbbrev, &abbrevNum); + + /* Assign the tag addresses (for cross references) */ + dwTraverseTag (dwRootTag, dwAssignTagAddress, &tagAddress); + + /* Write the .debug_abbrev section */ + dwWriteAbbrevs (); + + /* Write the .debug_info section */ + dwWriteTags (); + + /* Write the .debug_pubnames section */ + dwWritePubnames (); + + dwWriteFrames (); + + return 1; +} + diff --git a/src/SDCCdwarf2.h b/src/SDCCdwarf2.h new file mode 100644 index 0000000..5e5c595 --- /dev/null +++ b/src/SDCCdwarf2.h @@ -0,0 +1,432 @@ +/*------------------------------------------------------------------------- + SDCCdwarf2.h - DWARF2 debugging constants + + Written By - Erik Petrich . epetrich@users.sourceforge.net (2004) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCDWARF2_H +#define SDCCDWARF2_H + +#define DW_TAG_array_type 0x01 +#define DW_TAG_class_type 0x02 +#define DW_TAG_entry_point 0x03 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_imported_declaration 0x08 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_string_type 0x12 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_variant 0x19 +#define DW_TAG_common_block 0x1a +#define DW_TAG_common_inclusion 0x1b +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 01x1d +#define DW_TAG_module 0x1e +#define DW_TAG_ptr_to_member_type 0x1f +#define DW_TAG_set_type 0x20 +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_with_stmt 0x22 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_catch_block 0x25 +#define DW_TAG_const_type 0x26 +#define DW_TAG_constant 0x27 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_file_type 0x29 +#define DW_TAG_friend 0x2a +#define DW_TAG_namelist 0x2b +#define DW_TAG_namelist_item 0x2c +#define DW_TAG_packed_type 0x2d +#define DW_TAG_subprogram 0x2e +#define DW_TAG_template_type_param 0x2f +#define DW_TAG_template_value_param 0x30 +#define DW_TAG_thrown_type 0x31 +#define DW_TAG_try_block 0x32 +#define DW_TAG_variant_part 0x33 +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 + +#define DW_CHILDREN_no 0 +#define DW_CHILDREN_yes 1 + +#define DW_LANG_C89 0x0001 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_ordering 0x09 +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_discr 0x15 +#define DW_AT_discr_value 0x16 +#define DW_AT_visibility 0x17 +#define DW_AT_import 0x18 +#define DW_AT_string_length 0x19 +#define DW_AT_common_reference 0x1a +#define DW_AT_comp_dir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_default_value 0x1e +#define DW_AT_inline 0x20 +#define DW_AT_is_optional 0x21 +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_return_addr 0x2a +#define DW_AT_start_scope 0x2c +#define DW_AT_stride_size 0x2e +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_address_class 0x33 +#define DW_AT_artificial 0x34 +#define DW_AT_base_types 0x35 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_column 0x39 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_discr_list 0x3d +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_friend 0x41 +#define DW_AT_identifier_case 0x42 +#define DW_AT_macro_info 0x43 +#define DW_AT_namelist_item 0x44 +#define DW_AT_priority 0x45 +#define DW_AT_segment 0x46 +#define DW_AT_specification 0x47 +#define DW_AT_static_link 0x48 +#define DW_AT_type 0x49 +#define DW_AT_use_location 0x4a +#define DW_AT_variable_parameter 0x4b +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +#define DW_AT_lo_user 0x2000 +#define DW_AT_hi_user 0x3fff + +#define DW_AT_user_block 0x2650 +#define DW_AT_user_level 0x2651 + +#define DW_FORM_addr 0x01 +#define DW_FORM_block2 0x03 +#define DW_FORM_block4 0x04 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_data8 0x07 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref1 0x11 +#define DW_FORM_ref2 0x12 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_skip 0x2f +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_lit0 0x30 +#define DW_OP_reg0 0x50 +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 + +#define DW_ATE_address 0x1 +#define DW_ATE_boolean 0x2 +#define DW_ATE_complex_float 0x3 +#define DW_ATE_float 0x4 +#define DW_ATE_signed 0x5 +#define DW_ATE_signed_char 0x6 +#define DW_ATE_unsigned 0x7 +#define DW_ATE_unsigned_char 0x8 + +#define DW_CC_normal 0x1 +#define DW_CC_program 0x2 +#define DW_CC_nocall 0x3 + +#define DW_LNS_copy 1 +#define DW_LNS_advance_pc 2 +#define DW_LNS_advance_line 3 +#define DW_LNS_set_file 4 +#define DW_LNS_set_column 5 +#define DW_LNS_negate_stmt 6 +#define DW_LNS_set_basic_block 7 +#define DW_LNS_const_add_pc 8 +#define DW_LNS_fixed_advance_pc 9 + +#define DW_LNE_end_sequence 1 +#define DW_LNE_set_address 2 +#define DW_LNE_define_file 3 + +#define DW_MACINFO_define 1 +#define DW_MACINFO_undef 2 +#define DW_MACINFO_start_file 3 +#define DW_MACINFO_end_file 4 +#define DW_MACINFO_vendor_ext 255 + +#define DW_CFA_advance_loc (1 << 6) +#define DW_CFA_offset (2 << 6) +#define DW_CFA_restore (3 << 6) +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_nop 0x0f +#define DW_CFA_lo_user 0x1c +#define DW_CFA_hi_user 0x3f + + +typedef struct dwloc +{ + int opcode; + struct + { + symbol * sym; + const char * label; + int offset; + } operand; + struct dwloc * next; +} dwloc; + +typedef struct dwlocregion +{ + char * startLabel; + char * endLabel; + dwloc * loc; + struct dwlocregion * next; +} dwlocregion; + +typedef struct dwloclist +{ + int baseOffset; + dwlocregion * region; + struct dwloclist * next; +} dwloclist; + +typedef struct dwcfilist +{ + char * startLabel; + char * endLabel; + int callsize; + dwlocregion * region; + struct dwcfilist * next; +} dwcfilist; + +struct dwtag; + +typedef struct dwattr +{ + int attr; + int form; + union + { + struct + { + char * label; + int offset; + } symaddr; + struct + { + unsigned char * data; + int length; + } block; + int data; + const char * string; + struct dwtag * ref; + struct dwloc * loc; + struct dwloclist * loclist; + } val; + struct dwattr * next; +} dwattr; + +typedef struct dwtag +{ + int tag; + int abbrev; + int baseOffset; + struct dwattr * attribs; + struct dwtag * parent; + struct dwtag * firstChild; + struct dwtag * lastChild; + struct dwtag * siblings; +} dwtag; + +typedef struct dwfile +{ + char * name; + int dirIndex; + int timestamp; + int length; +} dwfile; + +typedef struct dwline +{ + char * label; + int offset; + int fileIndex; + int line; + unsigned is_stmt:1; + unsigned basic_block:1; + unsigned end_sequence:1; + unsigned begin_sequence:1; + struct dwline * next; +} dwline; + +typedef struct dwblock +{ + unsigned char * data; + int length; + int alloc; +} dwblock; + +typedef struct dwcfop +{ + int opcode; + char * label; + int operand1; + int operand2; + struct dwcfop * next; +} dwcfop; + +typedef struct dwcfins +{ + dwcfop * first; + dwcfop * last; +} dwcfins; + +typedef struct dwfde +{ + char * startLabel; + char * endLabel; + dwcfins * ins; +} dwfde; + +#endif + diff --git a/src/SDCCerr.c b/src/SDCCerr.c new file mode 100644 index 0000000..7ffd1d0 --- /dev/null +++ b/src/SDCCerr.c @@ -0,0 +1,796 @@ +/* + * SDCCerr - Standard error handler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include "SDCCglobl.h" +#ifdef HAVE_BACKTRACE_SYMBOLS_FD +#include +#include +#endif + +#include "SDCCerr.h" + +#define NELEM(x) (sizeof (x) / sizeof *(x)) + +#define USE_STDOUT_FOR_ERRORS 0 + +#if USE_STDOUT_FOR_ERRORS +#define DEFAULT_ERROR_OUT stdout +#else +#define DEFAULT_ERROR_OUT stderr +#endif + +struct SDCCERRG _SDCCERRG; + +extern char *filename; +extern int lineno; +extern int fatalError; + +/* Currently the errIndex field must match the position of the + * entry in the array. It is only included in order to make + * human error lookup easier. + */ +struct +{ + int errIndex; + ERROR_LOG_LEVEL errType; + const char *errText; + char disabled; +} ErrTab [] = +{ + { E_DUPLICATE, ERROR_LEVEL_ERROR, + "Duplicate symbol '%s', symbol IGNORED", 0 }, + { E_SYNTAX_ERROR, ERROR_LEVEL_ERROR, + "Syntax error, declaration ignored at '%s'", 0 }, + { E_CONST_EXPECTED, ERROR_LEVEL_ERROR, + "Initializer element is not a constant expression", 0 }, + { E_OUT_OF_MEM, ERROR_LEVEL_ERROR, + "'malloc' failed file '%s' for size %ld", 0 }, + { E_FILE_OPEN_ERR, ERROR_LEVEL_ERROR, + "'fopen' failed on file '%s'", 0 }, + { E_INVALID_OCLASS, ERROR_LEVEL_ERROR, + "Internal Error Oclass invalid '%s'", 0 }, + { E_CANNOT_ALLOC, ERROR_LEVEL_ERROR, + "Cannot allocate variable '%s'.", 0 }, + { E_OLD_STYLE, ERROR_LEVEL_ERROR, + "Old style C declaration. IGNORED '%s'", 0 }, + { E_STACK_OUT, ERROR_LEVEL_ERROR, + "Out of stack Space. '%s' not allocated", 0 }, + { E_INTERNAL_ERROR, ERROR_LEVEL_ERROR, + "FATAL Compiler Internal Error in file '%s' line number '%d' : %s \n" + "Contact Author with source code", 0 }, + { E_LVALUE_REQUIRED, ERROR_LEVEL_ERROR, + "'lvalue' required for '%s' operation.", 0 }, + { E_TMPFILE_FAILED, ERROR_LEVEL_ERROR, + "Creation of temp file failed", 0 }, + { E_FUNCTION_EXPECTED, ERROR_LEVEL_ERROR, + "called object is not a function", 0 }, + { E_USING_ERROR, ERROR_LEVEL_ERROR, + "'using', 'interrupt' or 'reentrant' must follow a function definition.'%s'", 0 }, + { E_SFR_INIT, ERROR_LEVEL_ERROR, + "Absolute address & initial value both cannot be specified for\n" + " a 'sfr','sbit' storage class, initial value ignored '%s'", 0 }, + { W_INIT_IGNORED, ERROR_LEVEL_WARNING, + "Variable in the storage class cannot be initialized.'%s'", 0 }, + { E_AUTO_ASSUMED, ERROR_LEVEL_ERROR, + "variable '%s' must be static to have storage class in reentrant function", 0 }, + { E_AUTO_ABSA, ERROR_LEVEL_ERROR, + "absolute address not allowed for automatic var '%s' in reentrant function ", 0 }, + { W_INIT_WRONG, ERROR_LEVEL_WARNING, + "Initializer different levels of indirections", 0 }, + { E_FUNC_REDEF, ERROR_LEVEL_ERROR, + "Function name '%s' redefined ", 0 }, + { E_ID_UNDEF, ERROR_LEVEL_ERROR, + "Undefined identifier '%s'", 0 }, + { W_STACK_OVERFLOW, ERROR_LEVEL_WARNING, + "stack exceeds 256 bytes for function '%s'", 0 }, + { E_NEED_ARRAY_PTR, ERROR_LEVEL_ERROR, + "Array or pointer required for '%s' operation ", 0 }, + { E_IDX_NOT_INT, ERROR_LEVEL_ERROR, + "Array index not an integer", 0 }, + { W_IDX_OUT_OF_BOUNDS, ERROR_LEVEL_WARNING, + "index %i is outside of the array bounds (array size is %i)", 0 }, + { E_STRUCT_UNION, ERROR_LEVEL_ERROR, + "Structure/Union expected left of '.%s'", 0 }, + { E_NOT_MEMBER, ERROR_LEVEL_ERROR, + "'%s' not a structure/union member", 0 }, + { E_PTR_REQD, ERROR_LEVEL_ERROR, + "Pointer required", 0 }, + { E_UNARY_OP, ERROR_LEVEL_ERROR, + "'unary %c': illegal operand", 0 }, + { E_CONV_ERR, ERROR_LEVEL_ERROR, + "conversion error: integral promotion failed", 0 }, + { E_BITFLD_TYPE, ERROR_LEVEL_ERROR, + "invalid type for bit-field", 0 }, + { E_BITFLD_SIZE, ERROR_LEVEL_ERROR, + "bit-field size too wide for type (max %d bits)", 0 }, + { W_TRUNCATION, ERROR_LEVEL_WARNING, + "high order truncation might occur", 0 }, + { E_CODE_WRITE, ERROR_LEVEL_ERROR, + "Attempt to assign value to a constant variable (%s)", 0 }, + { E_LVALUE_CONST, ERROR_LEVEL_ERROR, + "Lvalue specifies constant object", 0 }, + { E_ILLEGAL_ADDR, ERROR_LEVEL_ERROR, + "'&' illegal operand, %s", 0 }, + { E_CAST_ILLEGAL, ERROR_LEVEL_ERROR, + "illegal cast (cast cannot be aggregate)", 0 }, + { E_MULT_INTEGRAL, ERROR_LEVEL_ERROR, + "'*' bad operand", 0 }, + { E_ARG_ERROR, ERROR_LEVEL_ERROR, + "Argument count error, argument ignored", 0 }, + { E_ARG_COUNT, ERROR_LEVEL_ERROR, + "Function was expecting more arguments", 0 }, + { E_FUNC_EXPECTED, ERROR_LEVEL_ERROR, + "Function name expected '%s'. ANSI style declaration REQUIRED", 0 }, + { E_PLUS_INVALID, ERROR_LEVEL_ERROR, + "invalid operand '%s'", 0 }, + { E_PTR_PLUS_PTR, ERROR_LEVEL_ERROR, + "pointer + pointer invalid", 0 }, + { E_SHIFT_OP_INVALID, ERROR_LEVEL_ERROR, + "invalid operand for shift operator", 0 }, + { E_COMPARE_OP, ERROR_LEVEL_ERROR, + "operands are not comparable", 0 }, + { E_BITWISE_OP, ERROR_LEVEL_ERROR, + "operand invalid for bitwise operation", 0 }, + { E_ANDOR_OP, ERROR_LEVEL_ERROR, + "Invalid operand for '&&' or '||'", 0 }, + { E_TYPE_MISMATCH, ERROR_LEVEL_ERROR, + "indirections to different types %s %s ", 0 }, + { E_ARRAY_ASSIGN, ERROR_LEVEL_ERROR, + "cannot assign values to arrays", 0 }, + { E_ARRAY_DIRECT, ERROR_LEVEL_ERROR, + "bit Arrays can be accessed by literal index only", 0 }, + { E_BIT_ARRAY, ERROR_LEVEL_ERROR, + "Array or Pointer to bit|sbit|sfr not allowed.'%s'", 0 }, + { E_DUPLICATE_TYPEDEF, ERROR_LEVEL_ERROR, + "typedef/enum '%s' duplicate. Previous definition Ignored", 0 }, + { E_ARG_TYPE, ERROR_LEVEL_ERROR, + "Actual Argument type different from declaration %d", 0 }, + { E_RET_VALUE, ERROR_LEVEL_ERROR, + "Function return value mismatch", 0 }, + { E_FUNC_AGGR, ERROR_LEVEL_ERROR, + "Function cannot return aggregate. Func body ignored", 0 }, + { E_FUNC_DEF, ERROR_LEVEL_ERROR, + "ANSI Style declaration needed", 0 }, + { E_DUPLICATE_LABEL, ERROR_LEVEL_ERROR, + "Duplicate label '%s'", 0 }, + { E_LABEL_UNDEF, ERROR_LEVEL_ERROR, + "Label undefined '%s'", 0 }, + { E_FUNC_VOID, ERROR_LEVEL_ERROR, + "void function returning value", 0 }, + { W_VOID_FUNC, ERROR_LEVEL_WARNING, + "function '%s' must return value", 0 }, + { W_RETURN_MISMATCH, ERROR_LEVEL_WARNING, + "function return value mismatch", 0 }, + { E_CASE_CONTEXT, ERROR_LEVEL_ERROR, + "'case/default' found without 'switch'. Statement ignored", 0 }, + { E_CASE_CONSTANT, ERROR_LEVEL_ERROR, + "'case' expression not constant. Statement ignored", 0 }, + { E_BREAK_CONTEXT, ERROR_LEVEL_ERROR, + "'break/continue' statement out of context", 0 }, + { E_SWITCH_AGGR, ERROR_LEVEL_ERROR, + "nonintegral used in switch expression", 0 }, + { E_FUNC_BODY, ERROR_LEVEL_ERROR, + "function '%s' already has body", 0 }, + { E_UNKNOWN_SIZE, ERROR_LEVEL_ERROR, + "attempt to allocate variable of unknown size '%s'", 0 }, + { E_AUTO_AGGR_INIT, ERROR_LEVEL_ERROR, + "aggregate 'auto' variable '%s' cannot be initialized", 0 }, + { E_INIT_COUNT, ERROR_LEVEL_ERROR, + "too many initializers", 0 }, + { E_INIT_STRUCT, ERROR_LEVEL_ERROR, + "struct/union/array '%s': initialization needs curly braces", 0 }, + { E_INIT_NON_ADDR, ERROR_LEVEL_ERROR, + "non-address initialization expression", 0 }, + { E_INT_DEFINED, ERROR_LEVEL_ERROR, + "interrupt no '%d' already has a service routine '%s'", 0 }, + { E_INT_ARGS, ERROR_LEVEL_ERROR, + "interrupt routine cannot have arguments, arguments ignored", 0 }, + { E_INCLUDE_MISSING, ERROR_LEVEL_ERROR, + "critical compiler #include file missing. ", 0 }, + { E_NO_MAIN, ERROR_LEVEL_ERROR, + "function 'main' undefined", 0 }, + { E_UNUSED_75, ERROR_LEVEL_ERROR, + "this errorcode is no longer used", 0 }, + { E_PRE_PROC_FAILED, ERROR_LEVEL_ERROR, + "Pre-Processor %s", 0 }, + { E_DUP_FAILED, ERROR_LEVEL_ERROR, + "_dup call failed", 0 }, + { E_INCOMPAT_TYPES, ERROR_LEVEL_ERROR, + "incompatible types", 0 }, + { W_LOOP_ELIMINATE, ERROR_LEVEL_WARNING, + "'while' loop with 'zero' constant. Loop eliminated", 0 }, + { W_NO_SIDE_EFFECTS, ERROR_LEVEL_WARNING, + "%s expression has NO side effects. Expr eliminated", 0 }, + { W_CONST_TOO_LARGE, ERROR_LEVEL_PEDANTIC, + "constant value '%ld', out of range.", 0 }, + { W_BAD_COMPARE, ERROR_LEVEL_WARNING, + "comparison will either, ALWAYs succeed or ALWAYs fail", 0 }, + { E_TERMINATING, ERROR_LEVEL_ERROR, + "Compiler Terminating , contact author with source", 0 }, + { W_LOCAL_NOINIT, ERROR_LEVEL_WARNING, + "'auto' variable '%s' may be used before initialization", 0 }, + { W_NO_REFERENCE, ERROR_LEVEL_WARNING, + "in function %s unreferenced %s : '%s'", 0 }, + { E_OP_UNKNOWN_SIZE, ERROR_LEVEL_ERROR, + "unknown size for operand", 0 }, + { W_LONG_UNSUPPORTED, ERROR_LEVEL_WARNING, + "'%s' 'long' not supported , declared as 'int'.", 0 }, + { W_LITERAL_GENERIC, ERROR_LEVEL_WARNING, + "cast of LITERAL value to 'generic' pointer", 0 }, + { E_SFR_ADDR_RANGE, ERROR_LEVEL_ERROR, + "%s '%s' address out of range", 0 }, + { E_BITVAR_STORAGE, ERROR_LEVEL_ERROR, + "storage class CANNOT be specified for bit variable '%s'", 0 }, + { E_EXTERN_MISMATCH, ERROR_LEVEL_ERROR, + "extern definition for '%s' mismatches with declaration.", 0 }, + { E_NONRENT_ARGS, ERROR_LEVEL_ERROR, + "Functions called via pointers must be 'reentrant' to take this many (bytes for) arguments", 0 }, + { W_DOUBLE_UNSUPPORTED, ERROR_LEVEL_WARNING, + "type 'double' not supported assuming 'float'", 0 }, + { W_COMP_RANGE, ERROR_LEVEL_PEDANTIC, + "comparison is always %s due to limited range of data type", 0 }, + { W_FUNC_NO_RETURN, ERROR_LEVEL_WARNING, + "no 'return' statement found for function '%s'", 0 }, + { W_PRE_PROC_WARNING, ERROR_LEVEL_WARNING, + "Pre-Processor %s", 0 }, + { E_STRUCT_AS_ARG, ERROR_LEVEL_ERROR, + "SDCC cannot pass structure '%s' as function argument", 0 }, + { E_PREV_DECL_CONFLICT, ERROR_LEVEL_ERROR, + "conflict with previous declaration of '%s' for attribute '%s' at %s:%d", 0 }, + { E_CODE_NO_INIT, ERROR_LEVEL_WARNING, + "variable '%s' declared in code space must have initialiser", 0 }, + { E_OPS_INTEGRAL, ERROR_LEVEL_ERROR, + "operands not integral for assignment operation", 0 }, + { E_TOO_MANY_PARMS, ERROR_LEVEL_ERROR, + "too many parameters ", 0 }, + { E_TOO_FEW_PARMS, ERROR_LEVEL_ERROR, + "too few parameters", 0 }, + { E_FUNC_NO_CODE, ERROR_LEVEL_ERROR, + "code not generated for '%s' due to previous errors", 0 }, + { E_TYPE_MISMATCH_PARM, ERROR_LEVEL_ERROR, + "type mismatch for parameter number %d", 0 }, + { E_INVALID_FLOAT_CONST, ERROR_LEVEL_ERROR, + "invalid float constant '%s'", 0 }, + { E_INVALID_OP, ERROR_LEVEL_ERROR, + "invalid operand for '%s' operation", 0 }, + { E_SWITCH_NON_INTEGER, ERROR_LEVEL_ERROR, + "switch value not an integer", 0 }, + { E_CASE_NON_INTEGER, ERROR_LEVEL_ERROR, + "case label not an integer", 0 }, + { W_FUNC_TOO_LARGE, ERROR_LEVEL_WARNING, + "function '%s' too large for global optimization", 0 }, + { W_CONTROL_FLOW, ERROR_LEVEL_PEDANTIC, + "conditional flow changed by optimizer: so said EVELYN the modified DOG", 0 }, + { W_PTR_TYPE_INVALID, ERROR_LEVEL_WARNING, + "invalid type specifier for pointer type; specifier ignored", 0 }, + { W_IMPLICIT_FUNC, ERROR_LEVEL_WARNING, + "function '%s' implicit declaration", 0 }, + { W_CONTINUE, ERROR_LEVEL_WARNING, + "%s", 0 }, + { I_EXTENDED_STACK_SPILS, ERROR_LEVEL_INFO, + "extended stack by %d bytes for compiler temp(s) :in function '%s': %s ", 0 }, + { W_UNKNOWN_PRAGMA, ERROR_LEVEL_WARNING, + "unknown or unsupported #pragma directive '%s'", 0 }, + { W_SHIFT_CHANGED, ERROR_LEVEL_PEDANTIC, + "%s shifting more than size of object changed to zero", 0 }, + { W_UNKNOWN_OPTION, ERROR_LEVEL_WARNING, + "unknown compiler option '%s' ignored", 0 }, + { W_UNSUPP_OPTION, ERROR_LEVEL_WARNING, + "option '%s' no longer supported '%s' ", 0 }, + { E_UNKNOWN_FEXT, ERROR_LEVEL_ERROR, + "don't know what to do with file '%s'. file extension unsupported", 0 }, + { W_TOO_MANY_SRC, ERROR_LEVEL_WARNING, + "cannot compile more than one source file. file '%s' ignored", 0 }, + { I_CYCLOMATIC, ERROR_LEVEL_INFO, + "function '%s', # edges %d , # nodes %d , cyclomatic complexity %d", 0 }, + { E_DIVIDE_BY_ZERO, ERROR_LEVEL_WARNING, + "dividing by 0", 0 }, + { E_FUNC_BIT, ERROR_LEVEL_ERROR, + "function cannot return 'bit'", 0 }, + { E_CAST_ZERO, ERROR_LEVEL_ERROR, + "casting from to type 'void' is illegal", 0 }, + { W_CONST_RANGE, ERROR_LEVEL_WARNING, + "constant is out of range %s", 0 }, + { W_CODE_UNREACH, ERROR_LEVEL_PEDANTIC, + "unreachable code", 0 }, + { W_NONPTR2_GENPTR, ERROR_LEVEL_WARNING, + "non-pointer type cast to generic pointer", 0 }, + { W_POSSBUG, ERROR_LEVEL_WARNING, + "possible code generation error at line %d,\n" + " send source to sandeep.dutta@usa.net", 0 }, + { E_INCOMPAT_PTYPES, ERROR_LEVEL_ERROR, + "pointer types incompatible ", 0 }, + { W_UNKNOWN_MODEL, ERROR_LEVEL_WARNING, + "unknown memory model at %s : %d", 0 }, + { E_UNKNOWN_TARGET, ERROR_LEVEL_ERROR, + "cannot generate code for target '%s'", 0 }, + { W_INDIR_BANKED, ERROR_LEVEL_WARNING, + "Indirect call to a banked function not implemented.", 0 }, + { W_UNSUPPORTED_MODEL, ERROR_LEVEL_WARNING, + "Model '%s' not supported for %s, ignored.", 0 }, + { W_BANKED_WITH_NONBANKED, ERROR_LEVEL_WARNING, + "Both banked and nonbanked attributes used. nonbanked wins.", 0 }, + { W_BANKED_WITH_STATIC, ERROR_LEVEL_WARNING, // no longer used + "Both banked and static used. static wins.", 0 }, + { W_INT_TO_GEN_PTR_CAST, ERROR_LEVEL_WARNING, + "converting integer type to generic pointer: assuming XDATA", 0 }, + { W_ESC_SEQ_OOR_FOR_CHAR, ERROR_LEVEL_WARNING, + "escape sequence out of range for char.", 0 }, + { E_INVALID_HEX, ERROR_LEVEL_ERROR, + "\\x used with no following hex digits.", 0 }, + { W_FUNCPTR_IN_USING_ISR, ERROR_LEVEL_WARNING, + "call via function pointer in ISR using non-zero register bank.\n" + " Cannot determine which register bank to save.", 0 }, + { E_NO_SUCH_BANK, ERROR_LEVEL_ERROR, + "called function uses unknown register bank %d.", 0 }, + { E_TWO_OR_MORE_DATA_TYPES, ERROR_LEVEL_ERROR, + "two or more data types in declaration for '%s'", 0 }, + { E_LONG_OR_SHORT_INVALID, ERROR_LEVEL_ERROR, + "long or short specified for %s '%s'", 0 }, + { E_SIGNED_OR_UNSIGNED_INVALID, ERROR_LEVEL_ERROR, + "signed or unsigned specified for %s '%s'", 0 }, + { E_LONG_AND_SHORT_INVALID, ERROR_LEVEL_ERROR, + "both long and short specified for %s '%s'", 0 }, + { E_SIGNED_AND_UNSIGNED_INVALID, ERROR_LEVEL_ERROR, + "both signed and unsigned specified for %s '%s'", 0 }, + { E_TWO_OR_MORE_STORAGE_CLASSES, ERROR_LEVEL_ERROR, + "two or more storage classes in declaration for '%s'", 0 }, + { W_EXCESS_INITIALIZERS, ERROR_LEVEL_WARNING, + "excess elements in %s initializer after '%s'", 0 }, + { E_ARGUMENT_MISSING, ERROR_LEVEL_ERROR, + "Option %s requires an argument.", 0 }, + { W_STRAY_BACKSLASH, ERROR_LEVEL_WARNING, + "stray '\\' at column %d", 0 }, + { W_NEWLINE_IN_STRING, ERROR_LEVEL_WARNING, + "newline in string constant", 0 }, + { W_USING_GENERIC_POINTER, ERROR_LEVEL_WARNING, + "using generic pointer %s to initialize %s", 0 }, + { W_EXCESS_SHORT_OPTIONS, ERROR_LEVEL_WARNING, + "Only one short option can be specified at a time. Rest of %s ignored.", 0 }, + { E_VOID_VALUE_USED, ERROR_LEVEL_ERROR, + "void value not ignored as it ought to be", 0 }, + { W_INTEGRAL2PTR_NOCAST, ERROR_LEVEL_WARNING, + "converting integral to pointer without a cast", 0 }, + { W_PTR2INTEGRAL_NOCAST, ERROR_LEVEL_WARNING, + "converting pointer to integral without a cast", 0 }, + { W_SYMBOL_NAME_TOO_LONG, ERROR_LEVEL_WARNING, + "symbol name too long, truncated to %d chars", 0 }, + { W_CAST_STRUCT_PTR, ERROR_LEVEL_WARNING, + "cast of struct %s * to struct %s * ", 0 }, + { W_LIT_OVERFLOW, ERROR_LEVEL_PEDANTIC, + "overflow in implicit constant conversion", 0 }, + { E_PARAM_NAME_OMITTED, ERROR_LEVEL_ERROR, + "in function %s: name omitted for parameter %d", 0 }, + { W_NO_FILE_ARG_IN_C1, ERROR_LEVEL_WARNING, + "only standard input is compiled in c1 mode. file '%s' ignored", 0 }, + { E_NEED_OPT_O_IN_C1, ERROR_LEVEL_ERROR, + "must specify assembler file name with -o in c1 mode", 0 }, + { W_ILLEGAL_OPT_COMBINATION, ERROR_LEVEL_WARNING, + "illegal combination of options (--c1mode, -E, -S -c)", 0 }, + { E_DUPLICATE_MEMBER, ERROR_LEVEL_ERROR, + "duplicate %s member '%s'", 0 }, + { E_STACK_VIOLATION, ERROR_LEVEL_ERROR, + "'%s' internal stack %s", 0 }, + { W_INT_OVL, ERROR_LEVEL_PEDANTIC, + "integer overflow in expression", 0 }, + { W_USELESS_DECL, ERROR_LEVEL_WARNING, + "useless declaration (possible use of keyword as variable name)", 0 }, + { E_INT_BAD_INTNO, ERROR_LEVEL_ERROR, + "interrupt number '%u' is not valid", 0 }, + { W_BITFLD_NAMED, ERROR_LEVEL_WARNING, + "ignoring declarator of 0 length bitfield", 0 }, + { E_FUNC_ATTR, ERROR_LEVEL_ERROR, + "function attribute following non-function declaration"}, + { W_SAVE_RESTORE, ERROR_LEVEL_PEDANTIC, + "unmatched #pragma save and #pragma restore", 0 }, + { E_INVALID_CRITICAL, ERROR_LEVEL_ERROR, + "not allowed in a critical section or critical function", 0 }, + { E_NOT_ALLOWED, ERROR_LEVEL_ERROR, + "%s not allowed here", 0 }, + { E_BAD_TAG, ERROR_LEVEL_ERROR, + "'%s' is not a %s tag", 0 }, + { E_ENUM_NON_INTEGER, ERROR_LEVEL_ERROR, + "enumeration constant not an integer", 0 }, + { W_DEPRECATED_PRAGMA, ERROR_LEVEL_WARNING, + "pragma %s is deprecated, please see documentation for details", 0 }, + { E_SIZEOF_INCOMPLETE_TYPE, ERROR_LEVEL_ERROR, + "sizeof applied to an incomplete type", 0 }, + { E_PREVIOUS_DEF, ERROR_LEVEL_ERROR, + "previously defined here", 0 }, + { W_SIZEOF_VOID, ERROR_LEVEL_WARNING, + "size of void is zero", 0 }, + { W_POSSBUG2, ERROR_LEVEL_WARNING, + "possible code generation error at %s line %d,\n" + " please report problem and send source code at sdcc-user list on sourceforge.net"}, + { W_COMPLEMENT, ERROR_LEVEL_WARNING, + "using ~ on bit/bool/unsigned char variables can give unexpected results due to promotion to int", 0 }, + { E_SHADOWREGS_NO_ISR, ERROR_LEVEL_ERROR, + "ISR function attribute 'shadowregs' following non-ISR function '%s'", 0 }, + { W_SFR_ABSRANGE, ERROR_LEVEL_WARNING, + "absolute address for sfr '%s' probably out of range.", 0 }, + { E_BANKED_WITH_CALLEESAVES, ERROR_LEVEL_ERROR, + "Both banked and callee-saves cannot be used together.", 0 }, + { W_INVALID_INT_CONST, ERROR_LEVEL_WARNING, + "integer constant '%s' out of range, truncated to %.0lf.", 0 }, + { W_CMP_SU_CHAR, ERROR_LEVEL_PEDANTIC, + "comparison of 'signed char' with 'unsigned char' requires promotion to int", 0 }, + { W_INVALID_FLEXARRAY, ERROR_LEVEL_WARNING, + "invalid use of structure with flexible array member", 0 }, + { W_C89_NO_FLEXARRAY, ERROR_LEVEL_PEDANTIC, + "ISO C90 does not support flexible array members", 0 }, + { E_FLEXARRAY_NOTATEND, ERROR_LEVEL_ERROR, + "flexible array member '%s' not at end of struct", 0 }, + { E_FLEXARRAY_INEMPTYSTRCT, ERROR_LEVEL_ERROR, + "flexible array '%s' in otherwise empty struct", 0 }, + { W_EMPTY_SOURCE_FILE, ERROR_LEVEL_PEDANTIC, + "ISO C forbids an empty source file", 0 }, + { W_BAD_PRAGMA_ARGUMENTS, ERROR_LEVEL_WARNING, + "#pragma %s: bad argument(s); pragma ignored", 0 }, + { E_BAD_RESTRICT, ERROR_LEVEL_ERROR, + "Only object pointers may be qualified with 'restrict'", 0 }, + { E_BAD_INLINE, ERROR_LEVEL_ERROR, + "Only functions may be qualified with 'inline'", 0 }, + { E_BAD_INT_ARGUMENT, ERROR_LEVEL_ERROR, + "Bad integer argument for option %s", 0 }, + { E_NEGATIVE_ARRAY_SIZE, ERROR_LEVEL_ERROR, + "Size of array '%s' is negative", 0 }, + { W_TARGET_LOST_QUALIFIER, ERROR_LEVEL_WARNING, + "pointer target lost %s qualifier", 0 }, + { W_DEPRECATED_KEYWORD, ERROR_LEVEL_WARNING, + "keyword '%s' is deprecated, use '%s' instead", 0 }, + { E_STORAGE_CLASS_FOR_PARAMETER, ERROR_LEVEL_ERROR, + "storage class other than register specified for parameter '%s'", 0 }, + { E_OFFSETOF_TYPE, ERROR_LEVEL_ERROR, + "offsetof can only be applied to structs/unions", 0 }, + { E_INCOMPLETE_FIELD, ERROR_LEVEL_ERROR, + "field '%s' has incomplete type", 0 }, + { W_DEPRECATED_OPTION, ERROR_LEVEL_WARNING, + "deprecated compiler option '%s'", 0 }, + { E_BAD_DESIGNATOR, ERROR_LEVEL_ERROR, + "Invalid designator for designated initializer", 0 }, + { W_DUPLICATE_INIT, ERROR_LEVEL_WARNING, + "Duplicate initializer at position %d; ignoring previous.", 0 }, + { E_INVALID_UNIVERSAL, ERROR_LEVEL_ERROR, + "invalid universal character name \\%s.", 0 }, + { W_UNIVERSAL_C95, ERROR_LEVEL_WARNING, + "universal character names are only valid in C95 or later", 0 }, + { E_SHORTLONG, ERROR_LEVEL_ERROR, + "invalid combination of short / long", 0 }, + { E_INTEGERSUFFIX, ERROR_LEVEL_ERROR, + "Invalid integer suffix '%s' in integer constant", 0}, + { E_AUTO_ADDRSPACE, ERROR_LEVEL_ERROR, + "named address space not allowed for automatic var '%s'", 0}, + { W_NORETURNRETURN, ERROR_LEVEL_WARNING, + "return in _Noreturn function", 0}, + { E_STRUCT_REDEF, ERROR_LEVEL_ERROR, + "struct/union '%s' redefined", 0 }, + { W_STRING_CANNOT_BE_TERMINATED, ERROR_LEVEL_PEDANTIC, + "string '%s'cannot be terminated within array", 0 }, + { W_LONGLONG_LITERAL, ERROR_LEVEL_WARNING, + "support for large long long literals is incomplete", 1 }, + { S_SYNTAX_ERROR, ERROR_LEVEL_SYNTAX_ERROR, + "token -> '%s' ; column %d", 0 }, + { E_MIXING_CONFIG, ERROR_LEVEL_ERROR, + "mixing __CONFIG and CONFIG directives", 0 }, + { W_STATIC_ASSERTION, ERROR_LEVEL_WARNING, + "static assertion failed: %s", 0 }, + { E_ALIGNAS, ERROR_LEVEL_ERROR, + "invalid alignment specified: %d", 0 }, + { W_INTERNAL_ERROR, ERROR_LEVEL_WARNING, + "Non-fatal Compiler Internal Problem in file '%s' line number '%d' : %s \n" + "Contact Author with source code", 0 }, + { W_UNRECOGNIZED_ASM, ERROR_LEVEL_INFO, + "%s() failed to parse line node, assuming %d bytes\n'%s'\n", 0 }, + { W_FLEXARRAY_INSTRUCT, ERROR_LEVEL_WARNING, + "type of variable '%s' is struct with flexible array field", 0}, + { E_TYPE_IS_FUNCTION, ERROR_LEVEL_ERROR, + "'%s' has function type", 0}, + { W_INLINE_NAKED, ERROR_LEVEL_WARNING, + "inline function '%s' is __naked", 0}, + { E_Z88DK_FASTCALL_PARAMETERS, ERROR_LEVEL_ERROR, + "invalid number of parameters for __z88dk_fastcall", 0 }, + { E_Z88DK_FASTCALL_PARAMETER, ERROR_LEVEL_ERROR, + "ivalid parameter type in __z88dk_fastcall", 0 }, + { W_REPEAT_QUALIFIER, ERROR_LEVEL_WARNING, + "duplicate specifier '%s'", 0 }, + { W_NO_TYPE_SPECIFIER, ERROR_LEVEL_WARNING, + "no type specifier for '%s'", 0 }, + { E_NO_TYPE_SPECIFIER, ERROR_LEVEL_ERROR, + "no type specifier for '%s'", 0 }, + { E_MULTIPLE_DEFAULT_IN_GENERIC, ERROR_LEVEL_ERROR, + "multiple default expressions in generic association", 0 }, + { E_MULTIPLE_MATCHES_IN_GENERIC, ERROR_LEVEL_ERROR, + "multiple matching expressions in generic association", 0 }, + { E_NO_MATCH_IN_GENERIC, ERROR_LEVEL_ERROR, + "no matching expression in generic association", 0 }, + { W_LABEL_WITHOUT_STATEMENT, ERROR_LEVEL_WARNING, + "label without statement", 0}, + { E_WCHAR_CONST_C95, ERROR_LEVEL_ERROR, + "character constant of type wchar_t requires C95 or later", 0}, + { E_WCHAR_CONST_C11, ERROR_LEVEL_ERROR, + "character constant of type char16_t or char32_t requires C11 or later", 0}, + { E_WCHAR_STRING_C95, ERROR_LEVEL_ERROR, + "wide character string of type L requires C95 or later", 0}, + { E_WCHAR_STRING_C11, ERROR_LEVEL_ERROR, + "wide character string of type u8, u, U requires C11 or later", 0}, + { W_UNKNOWN_REG, ERROR_LEVEL_WARNING, + "unknown register specification %s", 0}, + { E_HEXFLOAT_C99, ERROR_LEVEL_ERROR, + "hexadecimal floating constant requires ISO C99 or later", 0}, + { E_ANONYMOUS_STRUCT_TAG, ERROR_LEVEL_ERROR, + "tagged anonymous struct/union '%s'", 0}, + { W_INLINE_FUNCATTR, ERROR_LEVEL_WARNING, + "inline function '%s' might lose function attributes", 0}, + { E_FOR_INITAL_DECLARATION_C99, ERROR_LEVEL_ERROR, + "initial declaration in for loop requires ISO C99 or later", 0}, + { E_QUALIFIED_ARRAY_PARAM_C99, ERROR_LEVEL_ERROR, + "qualifiers in array parameters require ISO C99 or later", 0}, + { E_QUALIFIED_ARRAY_NOPARAM, ERROR_LEVEL_ERROR, + "qualifier or static in array declarator that is not a parameter", 0}, + { E_STATIC_ARRAY_PARAM_C99, ERROR_LEVEL_ERROR, + "static in array parameters requires ISO C99 or later", 0}, + { E_INT_MULTIPLE, ERROR_LEVEL_ERROR, + "multiple interrupt numbers for '%s'", 0}, + { W_INCOMPAT_PTYPES, ERROR_LEVEL_WARNING, + "pointer types incompatible ", 0 }, + { E_STATIC_ASSERTION_C2X, ERROR_LEVEL_ERROR, + "static assertion with one argument requires C2X or later", 0 }, + { W_STATIC_ASSERTION_2, ERROR_LEVEL_WARNING, + "static assertion failed", 0 }, + { E_DECL_AFTER_STATEMENT_C99, ERROR_LEVEL_ERROR, + "declaration after statement requires ISO C99 or later", 0 }, + { E_SHORTCALL_INVALID_VALUE, ERROR_LEVEL_ERROR, + "invalid value for __z88dk_shortcall %s parameter: %x", 0}, + { E_DUPLICATE_PARAMTER_NAME, ERROR_LEVEL_ERROR, + "duplicate parameter name %s for function %s", 0}, +}; + +/* ------------------------------------------------------------------------------- + * SetErrorOut - Set the error output file + * ------------------------------------------------------------------------------- + */ +FILE * +SetErrorOut (FILE *NewErrorOut) +{ + _SDCCERRG.out = NewErrorOut; + + return NewErrorOut; +} + +/* ------------------------------------------------------------------------------- + * setErrorLogLevel - Set the error log level: + * which level has to be treated as an error + * ------------------------------------------------------------------------------- + */ +void setErrorLogLevel (ERROR_LOG_LEVEL level) +{ + _SDCCERRG.logLevel = level; +} + +/* ------------------------------------------------------------------------------- + * vwerror - Output a standard error message with variable number of arguments + * ------------------------------------------------------------------------------- + */ +int +vwerror (int errNum, va_list marker) +{ + if (_SDCCERRG.out == NULL) + { + _SDCCERRG.out = DEFAULT_ERROR_OUT; + } + + if (errNum > NELEM (ErrTab)) + { + fprintf (_SDCCERRG.out, + "Internal error: bad error number %d.", errNum); + return 0; + } + if (NELEM (ErrTab) != NUMBER_OF_ERROR_MESSAGES || ErrTab[errNum].errIndex != errNum) + { + fprintf (_SDCCERRG.out, + "Internal error: error table entry for %d inconsistent.", errNum); + return 0; + } + + if ((ErrTab[errNum].errType >= _SDCCERRG.logLevel) && (!ErrTab[errNum].disabled)) + { + if (ErrTab[errNum].errType >= ERROR_LEVEL_ERROR || _SDCCERRG.werror) + fatalError++; + + if (filename && lineno) + { + if (_SDCCERRG.style) + fprintf (_SDCCERRG.out, "%s(%d) : ", filename, lineno); + else + fprintf (_SDCCERRG.out, "%s:%d: ", filename, lineno); + } + else if (lineno) + { + fprintf (_SDCCERRG.out, "at %d: ", lineno); + } + else + { + fprintf (_SDCCERRG.out, "-:0: "); + } + + switch (ErrTab[errNum].errType) + { + case ERROR_LEVEL_SYNTAX_ERROR: + fprintf (_SDCCERRG.out, "syntax error: "); + break; + + case ERROR_LEVEL_ERROR: + fprintf (_SDCCERRG.out, "error %d: ", errNum); + break; + + case ERROR_LEVEL_WARNING: + case ERROR_LEVEL_PEDANTIC: + if (_SDCCERRG.werror) + fprintf (_SDCCERRG.out, "error %d: ", errNum); + else + fprintf (_SDCCERRG.out, "warning %d: ", errNum); + break; + + case ERROR_LEVEL_INFO: + fprintf (_SDCCERRG.out, "info %d: ", errNum); + break; + + default: + break; + } + + vfprintf (_SDCCERRG.out, ErrTab[errNum].errText, marker); + fprintf (_SDCCERRG.out, "\n"); + return 1; + } + else + { + /* Below the logging level, drop. */ + return 0; + } +} + +/* ------------------------------------------------------------------------------- + * werror - Output a standard error message with variable number of arguments + * ------------------------------------------------------------------------------- + */ +int +werror (int errNum, ...) +{ + int ret; + va_list marker; + va_start (marker, errNum); + ret = vwerror (errNum, marker); + va_end (marker); + return ret; +} + +/* ------------------------------------------------------------------------------- +werror_bt - like werror(), but als provide a backtrace + * ------------------------------------------------------------------------------- + */ +int +werror_bt (int errNum, ...) +{ +#ifdef HAVE_BACKTRACE_SYMBOLS_FD + void *callstack[16]; + int frames = backtrace (callstack, 16); + fprintf (stderr, "Backtrace:\n"); + backtrace_symbols_fd (callstack, frames, STDERR_FILENO); +#endif + + int ret; + va_list marker; + va_start (marker, errNum); + ret = vwerror (errNum, marker); + va_end (marker); + + return ret; +} + +/* ------------------------------------------------------------------------------- + * werrorfl - Output a standard error message with variable number of arguments. + * Use a specified filename and line number instead of the default. + * ------------------------------------------------------------------------------- + */ +int +werrorfl (char *newFilename, int newLineno, int errNum, ...) +{ + char *oldFilename = filename; + int oldLineno = lineno; + va_list marker; + int ret; + + filename = newFilename; + lineno = newLineno; + + va_start (marker,errNum); + ret = vwerror (errNum, marker); + va_end (marker); + + filename = oldFilename; + lineno = oldLineno; + return ret; +} + +/* ------------------------------------------------------------------------------- + * fatal - Output a standard error message with variable number of arguments and + * call exit() + * ------------------------------------------------------------------------------- + */ +void +fatal (int exitCode, int errNum, ...) +{ + va_list marker; + va_start (marker, errNum); + vwerror (errNum, marker); + va_end (marker); + + exit (exitCode); +} + +/* ------------------------------------------------------------------------------- + * style - Change the output error style to MSVC + * ------------------------------------------------------------------------------- + */ +void +MSVC_style (int style) +{ + _SDCCERRG.style = style; +} + +/* ------------------------------------------------------------------------------- + * disabled - Disable output of specified warning + * ------------------------------------------------------------------------------- + */ +void +setWarningDisabled (int errNum) +{ + if ((errNum >= 0) && (errNum < NELEM (ErrTab)) && (ErrTab[errNum].errType <= ERROR_LEVEL_WARNING)) + ErrTab[errNum].disabled = 1; +} + +/* ------------------------------------------------------------------------------- + * disabledState - Enable/Disable output of specified warning + * ------------------------------------------------------------------------------- + */ +int +setWarningDisabledState (int errNum, int disabled) +{ + if ((errNum >= 0) && (errNum < NELEM (ErrTab)) && (ErrTab[errNum].errType <= ERROR_LEVEL_WARNING)) + { + int originalState = ErrTab[errNum].disabled; + ErrTab[errNum].disabled = disabled; + return originalState; + } + return 0; +} + + +/* ------------------------------------------------------------------------------- + * Set the flag to treat warnings as errors + * ------------------------------------------------------------------------------- + */ +void +setWError (int flag) +{ + _SDCCERRG.werror = flag; +} diff --git a/src/SDCCerr.h b/src/SDCCerr.h new file mode 100644 index 0000000..ed8f3a5 --- /dev/null +++ b/src/SDCCerr.h @@ -0,0 +1,422 @@ +/* + * SDCCerr - Standard error handler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#if !defined(__SDCCERR_H) + +#define __SDCCERR_H + +#include +#include + +/* ERROR Message Definition */ + +enum { + E_DUPLICATE = 0, /* Duplicate variable */ + E_SYNTAX_ERROR = 1, /* Syntax Error */ + E_CONST_EXPECTED = 2, /* constant expected */ + E_OUT_OF_MEM = 3, /* malloc failed */ + E_FILE_OPEN_ERR = 4, /* File open failed */ + E_INVALID_OCLASS = 5, /* output class invalid */ + E_CANNOT_ALLOC = 6, /* cannot allocate space*/ + E_OLD_STYLE = 7, /* old style C ! allowed*/ + E_STACK_OUT = 8, /* v r out of stack */ + E_INTERNAL_ERROR = 9, /* unable to alloc tvar */ + E_LVALUE_REQUIRED = 10, /* lvalue required */ + E_TMPFILE_FAILED = 11, /* tmpfile creation failed */ + E_FUNCTION_EXPECTED = 12, /* function expected */ + E_USING_ERROR = 13, /* using in error */ + E_SFR_INIT = 14, /* init error for sbit */ + W_INIT_IGNORED = 15, /* initialiser ignored */ + E_AUTO_ASSUMED = 16, /* sclass auto assumed */ + E_AUTO_ABSA = 17, /* abs addr for auto var*/ + W_INIT_WRONG = 18, /* initializer type != */ + E_FUNC_REDEF = 19, /* func name redefined */ + E_ID_UNDEF = 20, /* identifer undefined */ + W_STACK_OVERFLOW = 21, /* stack overflow */ + E_NEED_ARRAY_PTR = 22, /* array or pointer reqd*/ + E_IDX_NOT_INT = 23, /* index not an integer */ + W_IDX_OUT_OF_BOUNDS = 24, /* array index out of bounds */ + E_STRUCT_UNION = 25, /* struct,union expected*/ + E_NOT_MEMBER = 26, /* !struct/union member */ + E_PTR_REQD = 27, /* pointer required */ + E_UNARY_OP = 28, /* unary operator bad op*/ + E_CONV_ERR = 29, /* conversion error */ + E_BITFLD_TYPE = 30, /* invalid type for bit-field */ + E_BITFLD_SIZE = 31, /* bit-field too wide for type */ + W_TRUNCATION = 32, /* high order trucation */ + E_CODE_WRITE = 33, /* trying 2 write to code */ + E_LVALUE_CONST = 34, /* lvalue is a const */ + E_ILLEGAL_ADDR = 35, /* address of bit */ + E_CAST_ILLEGAL = 36, /* cast illegal */ + E_MULT_INTEGRAL = 37, /* mult opernd must b integral */ + E_ARG_ERROR = 38, /* argument count error*/ + E_ARG_COUNT = 39, /* func expecting more */ + E_FUNC_EXPECTED = 40, /* func name expected */ + E_PLUS_INVALID = 41, /* plus invalid */ + E_PTR_PLUS_PTR = 42, /* pointer + pointer */ + E_SHIFT_OP_INVALID = 43, /* shft op op invalid */ + E_COMPARE_OP = 44, /* compare operand */ + E_BITWISE_OP = 45, /* bit op invalid op */ + E_ANDOR_OP = 46, /* && || op invalid */ + E_TYPE_MISMATCH = 47, /* type mismatch */ + E_ARRAY_ASSIGN = 48, /* array assign */ + E_ARRAY_DIRECT = 49, /* array indexing in */ + E_BIT_ARRAY = 50, /* bit array not allowed */ + E_DUPLICATE_TYPEDEF = 51, /* typedef name duplicate */ + E_ARG_TYPE = 52, /* arg type mismatch */ + E_RET_VALUE = 53, /* return value mismatch */ + E_FUNC_AGGR = 54, /* function returing aggr */ + E_FUNC_DEF = 55, /* ANSI Style def neede */ + E_DUPLICATE_LABEL = 56, /* duplicate label name */ + E_LABEL_UNDEF = 57, /* undefined label used */ + E_FUNC_VOID = 58, /* void func ret value */ + W_VOID_FUNC = 59, /* func must return value */ + W_RETURN_MISMATCH = 60, /* return value mismatch */ + E_CASE_CONTEXT = 61, /* case stmnt without switch */ + E_CASE_CONSTANT = 62, /* case expression ! const */ + E_BREAK_CONTEXT = 63, /* break statement invalid */ + E_SWITCH_AGGR = 64, /* non integral for switch */ + E_FUNC_BODY = 65, /* func has body already */ + E_UNKNOWN_SIZE = 66, /* variable has unknown size */ + E_AUTO_AGGR_INIT = 67, /* auto aggregates no init */ + E_INIT_COUNT = 68, /* too many initializers */ + E_INIT_STRUCT = 69, /* struct init wrong */ + E_INIT_NON_ADDR = 70, /* non address xpr for init */ + E_INT_DEFINED = 71, /* interrupt already over */ + E_INT_ARGS = 72, /* interrupt rtn cannot have args */ + E_INCLUDE_MISSING = 73, /* compiler include missing */ + E_NO_MAIN = 74, /* main function undefined */ + E_UNUSED_75 = 75, /* - removed error code - */ + E_PRE_PROC_FAILED = 76, /* preprocessor failed */ + E_DUP_FAILED = 77, /* file DUP failed */ + E_INCOMPAT_TYPES = 78, /* incompatible types casting */ + W_LOOP_ELIMINATE = 79, /* loop eliminated */ + W_NO_SIDE_EFFECTS = 80, /* expression has no side effects */ + W_CONST_TOO_LARGE = 81, /* constant out of range */ + W_BAD_COMPARE = 82, /* bad comparison */ + E_TERMINATING = 83, /* compiler terminating */ + W_LOCAL_NOINIT = 84, /* local reference before assignment */ + W_NO_REFERENCE = 85, /* no reference to local variable */ + E_OP_UNKNOWN_SIZE = 86, /* unknown size for operand */ + W_LONG_UNSUPPORTED = 87, /* 'long' not supported yet */ + W_LITERAL_GENERIC = 88, /* literal being cast to generic pointer */ + E_SFR_ADDR_RANGE = 89, /* sfr address out of range */ + E_BITVAR_STORAGE = 90, /* storage given for 'bit' variable */ + E_EXTERN_MISMATCH = 91, /* extern declaration mismatches */ + E_NONRENT_ARGS = 92, /* fptr non reentrant has args */ + W_DOUBLE_UNSUPPORTED = 93, /* 'double' not supported yet */ + W_COMP_RANGE = 94, /* comparison is always %s due to limited range of data type */ + W_FUNC_NO_RETURN = 95, /* no return statement found */ + W_PRE_PROC_WARNING = 96, /* preprocessor generated warning */ + E_STRUCT_AS_ARG = 97, /* structure passed as argument */ + E_PREV_DECL_CONFLICT = 98, /* previous declaration conflicts with current */ + E_CODE_NO_INIT = 99, /* vars in code space must have initializer */ + E_OPS_INTEGRAL = 100, /* operans must be integral for certain assignments */ + E_TOO_MANY_PARMS = 101, /* too many parameters */ + E_TOO_FEW_PARMS = 102, /* too few parameters */ + E_FUNC_NO_CODE = 103, /* fatalError */ + E_TYPE_MISMATCH_PARM = 104, /* type mismatch for parameter */ + E_INVALID_FLOAT_CONST = 105, /* invalid floating point literal string */ + E_INVALID_OP = 106, /* invalid operand for some operation */ + E_SWITCH_NON_INTEGER = 107, /* switch value not integer */ + E_CASE_NON_INTEGER = 108, /* case value not integer */ + W_FUNC_TOO_LARGE = 109, /* function too large */ + W_CONTROL_FLOW = 110, /* control flow changed due to optimization */ + W_PTR_TYPE_INVALID = 111, /* invalid type specifier for pointer */ + W_IMPLICIT_FUNC = 112, /* function declared implicitly */ + W_CONTINUE = 113, /* more than one line */ + I_EXTENDED_STACK_SPILS = 114, /* too many spils occured */ + W_UNKNOWN_PRAGMA = 115, /* #pragma directive unsupported */ + W_SHIFT_CHANGED = 116, /* shift changed to zero */ + W_UNKNOWN_OPTION = 117, /* don't know the option */ + W_UNSUPP_OPTION = 118, /* processor reset has been redifned */ + E_UNKNOWN_FEXT = 119, /* unknown file extension */ + W_TOO_MANY_SRC = 120, /* can only compile one .c file at a time */ + I_CYCLOMATIC = 121, /* information message */ + E_DIVIDE_BY_ZERO = 122, /* / 0 */ + E_FUNC_BIT = 123, /* function cannot return bit */ + E_CAST_ZERO = 124, /* casting to from size zero */ + W_CONST_RANGE = 125, /* constant too large */ + W_CODE_UNREACH = 126, /* unreachable code */ + W_NONPTR2_GENPTR = 127, /* non pointer cast to generic pointer */ + W_POSSBUG = 128, /* possible code generation error */ + E_INCOMPAT_PTYPES = 129, /* incompatible pointer assignment */ + W_UNKNOWN_MODEL = 130, /* Unknown memory model */ + E_UNKNOWN_TARGET = 131, /* target not defined */ + W_INDIR_BANKED = 132, /* Indirect call to a banked fun */ + W_UNSUPPORTED_MODEL = 133, /* Unsupported model, ignored */ + W_BANKED_WITH_NONBANKED = 134, /* banked and nonbanked attributes mixed */ + W_BANKED_WITH_STATIC = 135, /* banked and static mixed */ + W_INT_TO_GEN_PTR_CAST = 136, /* Converting integer type to generic pointer. */ + W_ESC_SEQ_OOR_FOR_CHAR = 137, /* Escape sequence of of range for char */ + E_INVALID_HEX = 138, /* \x used with no following hex digits */ + W_FUNCPTR_IN_USING_ISR = 139, /* Call via function pointer in ISR with using attribute. */ + E_NO_SUCH_BANK = 140, /* 'using' attribute specifies non-existant register bank. */ + E_TWO_OR_MORE_DATA_TYPES = 141, /* two or more data types in declaration for '%s' */ + E_LONG_OR_SHORT_INVALID = 142, /* long or short invalid for .. */ + E_SIGNED_OR_UNSIGNED_INVALID = 143, /* signed or unsigned invalid for .. */ + E_LONG_AND_SHORT_INVALID = 144, /* long and short invalid for .. */ + E_SIGNED_AND_UNSIGNED_INVALID = 145, /* signed and unsigned invalid for .. */ + E_TWO_OR_MORE_STORAGE_CLASSES = 146, /* two or more storage classes in declaration for '%s' */ + W_EXCESS_INITIALIZERS = 147, /* too much initializers for array */ + E_ARGUMENT_MISSING = 148, /* Option requires an argument. */ + W_STRAY_BACKSLASH = 149, /* stray '\\' at column %d" */ + W_NEWLINE_IN_STRING = 150, /* newline in string constant */ + W_USING_GENERIC_POINTER = 151, /* using generic pointer %s to initialize %s */ + W_EXCESS_SHORT_OPTIONS = 152, /* Only one short option can be specified at a time. Rest of %s ignored. */ + E_VOID_VALUE_USED = 153, /* void value not ignored as it ought to be */ + W_INTEGRAL2PTR_NOCAST = 154, /* converting integral to pointer without a cast */ + W_PTR2INTEGRAL_NOCAST = 155, /* converting pointer to integral without a cast */ + W_SYMBOL_NAME_TOO_LONG = 156, /* symbol name too long, truncated to %d chars */ + W_CAST_STRUCT_PTR = 157, /* pointer to different structure types */ + W_LIT_OVERFLOW = 158, /* overflow in implicit constant conversion */ + E_PARAM_NAME_OMITTED = 159, /* { in function %s: name omitted for parameter %d */ + W_NO_FILE_ARG_IN_C1 = 160, /* only standard input is compiled in c1 mode. file '%s' ignored */ + E_NEED_OPT_O_IN_C1 = 161, /* must specify assembler file name with -o in c1 mode */ + W_ILLEGAL_OPT_COMBINATION = 162, /* illegal combination of options (--c1mode, -E, -S -c) */ + E_DUPLICATE_MEMBER = 163, /* duplicate %s member '%s' */ + E_STACK_VIOLATION = 164, /* internal stack violation */ + W_INT_OVL = 165, /* integer overflow in expression */ + W_USELESS_DECL = 166, /* useless declaration */ + E_INT_BAD_INTNO = 167, /* invalid interrupt number */ + W_BITFLD_NAMED = 168, /* declarator used with 0 length bit-field */ + E_FUNC_ATTR = 169, /* function attribute without function */ + W_SAVE_RESTORE = 170, /* unmatched #pragma SAVE and #pragma RESTORE */ + E_INVALID_CRITICAL = 171, /* operation invalid in critical sequence */ + E_NOT_ALLOWED = 172, /* %s not allowed here */ + E_BAD_TAG = 173, /* '%s' is not a %s tag */ + E_ENUM_NON_INTEGER = 174, /* enumeration constant not an integer */ + W_DEPRECATED_PRAGMA = 175, /* deprecated pragma */ + E_SIZEOF_INCOMPLETE_TYPE = 176, /* sizeof applied to an incomplete type */ + E_PREVIOUS_DEF = 177, /* previously defined here */ + W_SIZEOF_VOID = 178, /* size of void is zero */ + W_POSSBUG2 = 179, /* possible bug, new format */ + W_COMPLEMENT = 180, /* ~bit can give unexpected results */ + E_SHADOWREGS_NO_ISR = 181, /* shadowregs keyword following non-ISR function */ + W_SFR_ABSRANGE = 182, /* sfr at address out of range */ + E_BANKED_WITH_CALLEESAVES = 183, /* banked and callee-saves mixed */ + W_INVALID_INT_CONST = 184, /* invalid integer literal string */ + W_CMP_SU_CHAR = 185, /* comparison of 'signed char' with 'unsigned char' requires promotion to int */ + W_INVALID_FLEXARRAY = 186, /* invalid use of structure with flexible array member */ + W_C89_NO_FLEXARRAY = 187, /* ISO C90 does not support flexible array members */ + E_FLEXARRAY_NOTATEND = 188, /* flexible array member not at end of struct */ + E_FLEXARRAY_INEMPTYSTRCT = 189, /* flexible array in otherwise empty struct */ + W_EMPTY_SOURCE_FILE = 190, /* ISO C forbids an empty source file */ + W_BAD_PRAGMA_ARGUMENTS = 191, /* #pragma %s: bad argument(s); pragma ignored */ + E_BAD_RESTRICT = 192, /* Only object pointers may be qualified with 'restrict' */ + E_BAD_INLINE = 193, /* Only functions may be qualified with 'inline' */ + E_BAD_INT_ARGUMENT = 194, /* Bad integer option argument */ + E_NEGATIVE_ARRAY_SIZE = 195, /* Size of array '%s' is negative */ + W_TARGET_LOST_QUALIFIER = 196, /* Pointer target lost qualifier */ + W_DEPRECATED_KEYWORD = 197, /* keyword '%s' is deprecated, use '%s' instead */ + E_STORAGE_CLASS_FOR_PARAMETER = 198, /* storage class specified for parameter '%s' */ + E_OFFSETOF_TYPE = 199, /* offsetof can only be applied to structs/unions */ + E_INCOMPLETE_FIELD = 200, /* struct field has incomplete type */ + W_DEPRECATED_OPTION = 201, /* deprecated compiler option '%s' */ + E_BAD_DESIGNATOR = 202, /* Bad designated initializer */ + W_DUPLICATE_INIT = 203, /* duplicate initializer */ + E_INVALID_UNIVERSAL = 204, /* invalid universal character name %s. */ + W_UNIVERSAL_C95 = 205, /* universal character names are only valid in C95 or later */ + E_SHORTLONG = 206, /* Invalid combination of short / long */ + E_INTEGERSUFFIX = 207, /* Invalid integer suffix */ + E_AUTO_ADDRSPACE = 208, /* named address space for auto var */ + W_NORETURNRETURN = 209, /* return in _noreturn function */ + E_STRUCT_REDEF = 210, /* struct or union tag redefined */ + W_STRING_CANNOT_BE_TERMINATED = 211, /* string cannot be terminated within array */ + W_LONGLONG_LITERAL = 212, /* long long literal */ + S_SYNTAX_ERROR = 213, /* syntax error */ + E_MIXING_CONFIG = 214, /* mixing __CONFIG and CONFIG directives */ + W_STATIC_ASSERTION = 215, /* static assertion failed */ + E_ALIGNAS = 216, /* invalid alignment specified */ + W_INTERNAL_ERROR = 217, /* warning for non-fatal internal errors - things that should not have happened, but can be handled */ + W_UNRECOGNIZED_ASM = 218, /* unrecognized asm instruction in peephole optimizer */ + W_FLEXARRAY_INSTRUCT = 219, /* using flexible arrays in a struct */ + E_TYPE_IS_FUNCTION = 220, /* typedef void foo_t(void); foo_t foo; */ + W_INLINE_NAKED = 221, /* inline function is naked */ + E_Z88DK_FASTCALL_PARAMETERS = 222, /* invalid number of parameters in __z88dk_fastcall */ + E_Z88DK_FASTCALL_PARAMETER = 223, /* invalid parameter type in __z88dk_fastcall */ + W_REPEAT_QUALIFIER = 224, /* the same qualifier appears more than once */ + W_NO_TYPE_SPECIFIER = 225, /* type specifier missing in declaration */ + E_NO_TYPE_SPECIFIER = 226, /* type specifier missing in declaration */ + E_MULTIPLE_DEFAULT_IN_GENERIC = 227, /* multiple default expressions in generic association */ + E_MULTIPLE_MATCHES_IN_GENERIC = 228, /* multiple matching expressions in generic association */ + E_NO_MATCH_IN_GENERIC = 229, /* no matching expression in generic association */ + W_LABEL_WITHOUT_STATEMENT = 230, /* label without statement, not allowed in standard C */ + E_WCHAR_CONST_C95 = 231, /* character constant of type wchar_t requires ISO C 95 or later */ + E_WCHAR_CONST_C11 = 232, /* character constant of type char16_t or char32_t equires ISO C 11 or later */ + E_WCHAR_STRING_C95 = 233, /* wide character string literal requires ISO C 95 or later */ + E_WCHAR_STRING_C11 = 234, /* wide character string literal requires ISO C 11 or later */ + W_UNKNOWN_REG = 235, /* unknown register specified */ + E_HEXFLOAT_C99 = 236, /* hexadecimal floating constant requires ISO C99 or later */ + E_ANONYMOUS_STRUCT_TAG = 237, /* anonymous struct/union should not have a tag */ + W_INLINE_FUNCATTR = 238, /* inline functions should not be z88dk_fastcall or z88dk_callee */ + E_FOR_INITAL_DECLARATION_C99 = 239, /* initial declaration in for loop requires ISO C99 or later */ + E_QUALIFIED_ARRAY_PARAM_C99 = 240, /* qualifiers in array parameters require ISO C99 or later */ + E_QUALIFIED_ARRAY_NOPARAM = 241, /* qualifier or static in array declarator that is not a parameter */ + E_STATIC_ARRAY_PARAM_C99 = 242, /* static in array parameters requires ISO C99 or later */ + E_INT_MULTIPLE = 243, /* multiple interrupt numbers */ + W_INCOMPAT_PTYPES = 244, /* incompatible pointer assignment (not allowed by the standard, but allowed in SDCC) */ + E_STATIC_ASSERTION_C2X = 245, /* static assertion with one argument requires C2X or later */ + W_STATIC_ASSERTION_2 = 246, /* static assertion failed */ + E_DECL_AFTER_STATEMENT_C99 = 247, /* declaration after statement requires ISO C99 or later */ + E_SHORTCALL_INVALID_VALUE = 248, /* Invalid value for a __z88dk_shortcall specifier */ + E_DUPLICATE_PARAMTER_NAME = 249, /* duplicate parameter name */ + + /* don't touch this! */ + NUMBER_OF_ERROR_MESSAGES /* Number of error messages */ +}; + +/** sdcc style assertion */ +#ifdef assert +#undef assert +#endif +#ifdef NDEBUG +# define assert(expr) (void)0 +#else +# define assert(expr) ((expr) ? (void)0 : fatal (1, E_INTERNAL_ERROR, __FILE__, __LINE__, #expr)) +#endif + +#define wassertl_bt(a,s) (void)((a) ? 0 : \ + (werror_bt (E_INTERNAL_ERROR, __FILE__, __LINE__, s), 0)) + +#define wassert_bt(a) wassertl_bt(a, "code generator internal error") + +/** Describes the maximum error level that will be logged. Any level + * includes all of the levels listed after it. + * + * + */ +enum _ERROR_LOG_LEVEL { + /** Everything. Currently the same as PEDANTIC. */ + ERROR_LEVEL_ALL, + /** All warnings, including those considered 'reasonable to use, + on occasion, in clean programs' (man 3 gcc). */ + ERROR_LEVEL_PEDANTIC, + /** 'informational' warnings */ + ERROR_LEVEL_INFO, + /** Most warnings. */ + ERROR_LEVEL_WARNING, + /** Errors only. */ + ERROR_LEVEL_ERROR, + /** Syntax error only. */ + ERROR_LEVEL_SYNTAX_ERROR +}; + +typedef enum _ERROR_LOG_LEVEL ERROR_LOG_LEVEL; + +struct SDCCERRG { + ERROR_LOG_LEVEL logLevel; + FILE *out; + int style; /* 1=MSVC */ + int werror; /* treat the warnings as errors */ +}; + +extern struct SDCCERRG _SDCCERRG; + +/** Sets the maximum error level to log. + See MAX_ERROR_LEVEL. The default is ERROR_LEVEL_ALL. +*/ +void setErrorLogLevel (ERROR_LOG_LEVEL level); + +/* +------------------------------------------------------------------------------- +SetErrorOut - Set the error output file + +------------------------------------------------------------------------------- +*/ + +FILE * SetErrorOut (FILE *NewErrorOut); + +/* +------------------------------------------------------------------------------- +vwerror - Output a standard eror message with variable number of arguements + +------------------------------------------------------------------------------- +*/ + +int vwerror (int errNum, va_list marker); + +/* +------------------------------------------------------------------------------- +werror - Output a standard eror message with variable number of arguements + +------------------------------------------------------------------------------- +*/ + +int werror (int errNum, ... ); + +/* +------------------------------------------------------------------------------- +werror_bt - like werror(), but als provide a backtrace + +------------------------------------------------------------------------------- +*/ + +int werror_bt (int errNum, ... ); + +/* +------------------------------------------------------------------------------- +werrorfl - Output a standard eror message with variable number of arguements. + Use a specified filename and line number instead of the default. + +------------------------------------------------------------------------------- +*/ + +int werrorfl (char *newFilename, int newLineno, int errNum, ...); + +/* +------------------------------------------------------------------------------- +fatal - Output a standard eror message with variable number of arguements and + call exit() +------------------------------------------------------------------------------- +*/ + +void fatal (int exitCode, int errNum, ... ); + +/* +------------------------------------------------------------------------------- +style - Change the output error style to MSVC +------------------------------------------------------------------------------- +*/ + +void MSVC_style (int style); + +/* +------------------------------------------------------------------------------- +disabled - Disable output of specified warning +------------------------------------------------------------------------------- +*/ + +void setWarningDisabled (int errNum); + +/* +------------------------------------------------------------------------------- +disabledState - Enable/Disable output of specified warning +------------------------------------------------------------------------------- +*/ + +int setWarningDisabledState (int errNum, int disabled); + +/* +------------------------------------------------------------------------------- +Set the flag to treat warnings as errors +------------------------------------------------------------------------------- +*/ +void setWError (int flag); + +#endif diff --git a/src/SDCCgas.c b/src/SDCCgas.c new file mode 100644 index 0000000..f2877fd --- /dev/null +++ b/src/SDCCgas.c @@ -0,0 +1,132 @@ +/*------------------------------------------------------------------------- + SDCCgas.c - source file for GAS (GNU Assembler) file generation + + Copyright (C) 2019, Xavier Del Campo Romero + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "SDCCgas.h" +#include "SDCCglobl.h" +#include "newalloc.h" +#include +#include +#include + +static char *set_dst(void); +static FILE *create_file(const char *const path); +static void emit_file_name(FILE *const f, const char *const dst); +static const char *get_filename(const char *const dst); + +void gas_glue(void) +{ + int clean_dst = 0; + const char *dst = fullDstFileName; + + if (!gasOutput) + { + werror(W_ILLEGAL_OPT_COMBINATION, __FILE__, __FUNCTION__, + "this function must only be called if GAS" + "(GNU Assembler) output has been specified"); + exit(1); + } + + if (!dst) + { + dst = set_dst(); + /* Set reminder flag so memory is free'd. */ + clean_dst = 1; + } + + FILE *const f = create_file(dst); + + emit_file_name(f, fullSrcFileName); + + if (clean_dst) + /* dst must not be modified by gas_glue(), but memory + * cleanup is needed if no output filepath was given. */ + Safe_free((void*)dst); + + fclose(f); +} + +static char *set_dst(void) +{ + char *dst = NULL; + size_t len; + + /* Set default output assembly file. */ + if (!fullSrcFileName) + { + werror(E_PARAM_NAME_OMITTED, __FILE__, __FUNCTION__, + "input filename has not been specified."); + exit(1); + } + + /* Calculate file name length. */ + for (len = 0; fullSrcFileName[len] != '.' && fullSrcFileName[len]; len++) + ; + + { + /* No output file name has been designated. Use default extension. */ + const char extension[] = ".s"; + const size_t total_len = len + strlen(extension) + 1; + + dst = Safe_calloc(total_len, sizeof *fullDstFileName); + + strncpy(dst, fullSrcFileName, len); + strcat(dst, extension); + } + + return dst; +} + +static FILE *create_file(const char *const path) +{ + FILE *const f = fopen(path, "wb"); + + if (!f) + { + werror(E_FILE_OPEN_ERR, __FILE__, __FUNCTION__); + exit(1); + } + + return f; +} + +static void emit_file_name(FILE *const f, const char *const dst) +{ + /* Get file name without path. */ + const char *filename = get_filename(dst); + + printf("%s\n", filename); + + fprintf(f, "\t.file\t\"%s\"\n", filename); +} + +static const char *get_filename(const char *const dst) +{ + const char *p; + + for (p = dst + strlen(dst); *p != '\\' && *p != '/' && (p - dst); p--) + ; + + if (p == dst) + /* No directories specified. */ + return dst; + else + /* Skip directory separator. */ + return ++p; +} diff --git a/src/SDCCgas.h b/src/SDCCgas.h new file mode 100644 index 0000000..67f96c7 --- /dev/null +++ b/src/SDCCgas.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + SDCCgas.c - header file for GAS (GNU Assembler) file generation + + Copyright (C) 2019, Xavier Del Campo Romero + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCGAS_H +#define SDCCGAS_H + +void gas_glue(void); + +#endif diff --git a/src/SDCCgen.c b/src/SDCCgen.c new file mode 100644 index 0000000..fb775fd --- /dev/null +++ b/src/SDCCgen.c @@ -0,0 +1,355 @@ +/*------------------------------------------------------------------------- + SDCCgen.c - source files for target code generation common functions + + Copyright (C) 2012, Borut Razem + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +/* Use the D macro for basic (unobtrusive) debugging messages */ +#define D(x) do if (options.verboseAsm) {x;} while(0) + +genLine_t genLine; + +/*-----------------------------------------------------------------*/ +/* newLineNode - creates a new peep line */ +/*-----------------------------------------------------------------*/ +lineNode * +newLineNode (const char *line) +{ + lineNode *pl; + + pl = Safe_alloc (sizeof (lineNode)); + pl->line = Safe_strdup (line); + pl->ic = NULL; + return pl; +} + +/*-----------------------------------------------------------------*/ +/* connectLine - connects two lines */ +/*-----------------------------------------------------------------*/ +lineNode * +connectLine (lineNode * pl1, lineNode * pl2) +{ + if (!pl1 || !pl2) + { + fprintf (stderr, "trying to connect null line\n"); + return NULL; + } + + pl2->prev = pl1; + pl1->next = pl2; + + return pl2; +} + +void +destroy_line_list (void) +{ + lineNode *pl; + + pl = genLine.lineCurr; + + while (pl) + { + lineNode *p; + + if (pl->line) + Safe_free (pl->line); + + if (pl->aln) + Safe_free (pl->aln); + + p = pl; + pl = pl->prev; + Safe_free (p); + } + genLine.lineHead = genLine.lineCurr = NULL; +} + +/*-----------------------------------------------------------------*/ +/* emit_raw - emit raw unformatted line */ +/*-----------------------------------------------------------------*/ +static void +add_line_node (const char *line) +{ + lineNode *pl; + + pl = Safe_alloc (sizeof (lineNode)); + + memcpy (pl, (lineElem_t *) & genLine.lineElement, sizeof (lineElem_t)); + + pl->line = Safe_strdup (line); + + if (genLine.lineCurr) + { + pl->next = NULL; + genLine.lineCurr->next = pl; + pl->prev = genLine.lineCurr; + genLine.lineCurr = pl; + } + else + { + pl->prev = pl->next = NULL; + genLine.lineCurr = genLine.lineHead = pl; + } +} + +void +emit_raw (const char *line) +{ + const char *p = line; + + while (isspace ((unsigned char) *p)) + p++; + + if (*p) + { + if (!port->rtrackUpdate || !port->rtrackUpdate(line)) + { + genLine.lineElement.isComment = (*p == ';'); + add_line_node (line); + } + } +} + +/*-----------------------------------------------------------------*/ +/* format_opcode - format the opcode and arguments for emitting */ +/*-----------------------------------------------------------------*/ +const char * +format_opcode (const char *inst, const char *fmt, va_list ap) +{ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, INITIAL_INLINEASM); + + if (inst && *inst) + { + dbuf_append_str (&dbuf, inst); + + if (fmt && *fmt) + { + dbuf_append_char (&dbuf, '\t'); + dbuf_tvprintf (&dbuf, fmt, ap); + } + } + else + { + if (fmt && *fmt) + { + dbuf_tvprintf (&dbuf, fmt, ap); + } + } + + return dbuf_detach_c_str (&dbuf); +} + +void +va_emitcode (const char *inst, const char *fmt, va_list ap) +{ + const char *line = format_opcode (inst, fmt, ap); + emit_raw (line); + dbuf_free (line); +} + +/*-----------------------------------------------------------------*/ +/* emitcode - writes the code into a file : for now it is simple */ +/*-----------------------------------------------------------------*/ +void +emitcode (const char *inst, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); +} + +void +emitLabel (symbol *tlbl) +{ + if (!tlbl) + return; + emitcode ("", "!tlabeldef", labelKey2num (tlbl->key)); + genLine.lineCurr->isLabel = 1; +} + +/*-----------------------------------------------------------------*/ +/* genInline - write the inline code out */ +/*-----------------------------------------------------------------*/ +void +genInline (iCode * ic) +{ + char *buf, *bp, *begin; + bool inComment = FALSE; + bool inLiteral = FALSE; + bool inLiteralString = FALSE; + + D (emitcode (";", "genInline")); + + genLine.lineElement.isInline += (!options.asmpeep); + + buf = bp = begin = Safe_strdup (IC_INLINE (ic)); + + /* Emit each line as a code */ + while (*bp) + { + switch (*bp) + { + case '\'': + inLiteral = !inLiteral; + ++bp; + break; + + case '"': + inLiteralString = !inLiteralString; + ++bp; + break; + + case ';': + if (!inLiteral && !inLiteralString) + { + inComment = TRUE; + } + ++bp; + break; + + case ':': + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && !inLiteral && !inLiteralString && (isspace ((unsigned char) bp[1]))) + { + ++bp; + *bp = '\0'; + ++bp; + emitcode (begin, NULL); + begin = bp; + } + else + { + ++bp; + } + break; + + case '\x87': + case '\n': + inLiteral = FALSE; + inLiteralString = FALSE; + inComment = FALSE; + *bp++ = '\0'; + + /* Don't emit leading whitespaces */ + while (isspace (*begin)) + ++begin; + + if (*begin) + emitcode (begin, NULL); + + begin = bp; + break; + + default: + ++bp; + break; + } + } + if (begin != bp) + { + /* Don't emit leading whitespaces */ + while (isspace (*begin)) + ++begin; + + if (*begin) + emitcode (begin, NULL); + } + + Safe_free (buf); + + /* consumed; we can free it here */ + dbuf_free (IC_INLINE (ic)); + + genLine.lineElement.isInline -= (!options.asmpeep); +} + +/*-----------------------------------------------------------------*/ +/* printLine - prints a line chain into a given file */ +/*-----------------------------------------------------------------*/ +void +printLine (lineNode * head, struct dbuf_s *oBuf) +{ + iCode *last_ic = NULL; + bool debug_iCode_tracking = (getenv ("DEBUG_ICODE_TRACKING") != NULL); + + while (head) + { + if (head->ic != last_ic) + { + last_ic = head->ic; + if (debug_iCode_tracking) + { + if (head->ic) + dbuf_printf (oBuf, "; block = %d, seq = %d\n", head->ic->block, head->ic->seq); + else + dbuf_append_str (oBuf, "; iCode lost\n"); + } + } + + /* don't indent comments & labels */ + if (head->line && (head->isComment || head->isLabel)) + { + dbuf_printf (oBuf, "%s\n", head->line); + } + else + { + if (head->isInline && *head->line == '#') + { + /* comment out preprocessor directives in inline asm */ + dbuf_append_char (oBuf, ';'); + } + dbuf_printf (oBuf, "\t%s\n", head->line); + } + head = head->next; + } +} + +/*-----------------------------------------------------------------*/ +/* ifxForOp - returns the icode containing the ifx for operand */ +/*-----------------------------------------------------------------*/ +iCode * +ifxForOp (const operand *op, const iCode *ic) +{ + iCode *ifxIc; + + /* if true symbol then needs to be assigned */ + if (!IS_TRUE_SYMOP (op)) + { + /* if this has register type condition and + while skipping ipop's (see bug 1509084), + the next instruction is ifx with the same operand + and live to of the operand is upto the ifx only then */ + for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next) + ; + + if (ifxIc && ifxIc->op == IFX && + IC_COND (ifxIc)->key == op->key && + OP_SYMBOL_CONST (op)->liveFrom >= ic->seq && + OP_SYMBOL_CONST (op)->liveTo <= ifxIc->seq) + return ifxIc; + } + + return NULL; +} diff --git a/src/SDCCgen.h b/src/SDCCgen.h new file mode 100644 index 0000000..de54101 --- /dev/null +++ b/src/SDCCgen.h @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------- + SDCCgen.h - header file for target code generation common functions + + Copyright (C) 2012, Borut Razem + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCGEN_H +#define SDCCGEN_H 1 + +#include + +#include + +#define initGenLineElement() memset (&genLine.lineElement, 0, sizeof (lineElem_t)) +#define labelKey2num(key) ((key) + 100) + +/* can be inherited by each port */ +typedef struct asmLineNodeBase +{ + int size; + bitVect *regsRead; + bitVect *regsWritten; +} +asmLineNodeBase; + +typedef struct lineElem_s +{ + char *line; + iCode *ic; + unsigned int isInline:1; + unsigned int isComment:1; + unsigned int isDebug:1; + unsigned int isLabel:1; + unsigned int visited:1; + asmLineNodeBase *aln; +} +lineElem_t; + +typedef struct lineNode_s +{ +#ifdef UNNAMED_STRUCT_TAG + struct lineElem_s; +#else + /* exactly the same members as of struct lineElem_s */ + char *line; + iCode *ic; + unsigned int isInline:1; + unsigned int isComment:1; + unsigned int isDebug:1; + unsigned int isLabel:1; + unsigned int visited:1; + asmLineNodeBase *aln; +#endif + struct lineNode_s *prev; + struct lineNode_s *next; +} +lineNode; + +typedef struct genLine_s +{ + /* double linked list of lines */ + lineNode *lineHead; + lineNode *lineCurr; + + /* global line */ + lineElem_t lineElement; +} genLine_t; + +extern genLine_t genLine; + +#ifdef __cplusplus +extern "C" { +#endif + +lineNode *newLineNode (const char *line); +lineNode *connectLine (lineNode * pl1, lineNode * pl2); +void destroy_line_list (void); +const char *format_opcode (const char *inst, const char *fmt, va_list ap); +void emit_raw (const char *line); +void va_emitcode (const char *inst, const char *fmt, va_list ap); +void emitcode (const char *inst, const char *fmt, ...); +void emitLabel (symbol * tlbl); +void genInline (iCode * ic); +void printLine (lineNode *, struct dbuf_s *); +iCode *ifxForOp (const operand *op, const iCode *ic); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/SDCCglobl.h b/src/SDCCglobl.h new file mode 100644 index 0000000..b2abbf2 --- /dev/null +++ b/src/SDCCglobl.h @@ -0,0 +1,421 @@ +/*------------------------------------------------------------------------- + SDCCglobl.h - global macros etc required by all files + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCGLOBL_H +#define SDCCGLOBL_H + +#include +#include +#include +#include + +# ifndef __cplusplus +# include +# endif + +# ifndef TRUE +# define TRUE true +# endif +# ifndef FALSE +# define FALSE false +# endif + +#include "SDCCset.h" + + +/* + * Define host port dependant constants etc. + */ + +#define UNIX_DIR_SEPARATOR_CHAR '/' + +#if defined(__BORLANDC__) || defined(_MSC_VER) +# define STRCASECMP stricmp +# define STRNCASECMP strnicmp +#else +# define STRCASECMP strcasecmp +# define STRNCASECMP strncasecmp +#endif + +#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) + +# ifndef HAVE_DOS_BASED_FILE_SYSTEM +# define HAVE_DOS_BASED_FILE_SYSTEM 1 +# endif + +# define IS_DIR_SEPARATOR(c) ((c) == DIR_SEPARATOR_CHAR || (c) == UNIX_DIR_SEPARATOR_CHAR) +/* Note that IS_ABSOLUTE_PATH accepts d:foo as well, although it is + only semi-absolute. This is because the users of IS_ABSOLUTE_PATH + want to know whether to prepend the current working directory to + a file name, which should not be done with a name like d:foo. */ +# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0]) || (((f)[0]) && ((f)[1] == ':'))) +# define FILENAME_CMP(s1, s2) STRCASECMP(s1, s2) + +#else /* not DOSish */ + +# define IS_DIR_SEPARATOR(c) ((c) == DIR_SEPARATOR_CHAR) +# define IS_ABSOLUTE_PATH(f) (IS_DIR_SEPARATOR((f)[0])) +# define FILENAME_CMP(s1, s2) strcmp(s1, s2) + +#endif /* not DOSish */ + +#ifdef WIN32 +# define NATIVE_WIN32 1 +# ifndef __MINGW32__ +# define PATH_MAX _MAX_PATH +# endif +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#elif defined(_WIN32) && !defined(__MINGW32__) +# include "sdcc_vc.h" +#else +# include "sdccconf.h" +#endif + +#include "SDCCerr.h" + +#define SPACE ' ' + +#include /* PATH_MAX */ +#if !defined(PATH_MAX) || (PATH_MAX < 2048) +# undef PATH_MAX +# define PATH_MAX 2048 /* define a reasonable value */ +#endif + +#define MAX_REG_PARMS 1 + +/* C++ doesn't like min and max macros */ +#ifndef __cplusplus +# ifndef max +# define max(a,b) (a > b ? a : b) +# endif +# ifndef min +# define min(a,b) (a < b ? a : b) +# endif +#endif /* __cplusplus */ + +#ifndef THROWS +# define THROWS +# define THROW_NONE 0 +# define THROW_SRC 1 +# define THROW_DEST 2 +# define THROW_BOTH 3 +#endif + +/* sizes in bytes */ +#define BOOLSIZE port->s.char_size +#define CHARSIZE port->s.char_size +#define SHORTSIZE port->s.short_size +#define INTSIZE port->s.int_size +#define LONGSIZE port->s.long_size +#define LONGLONGSIZE port->s.longlong_size +#define NEARPTRSIZE port->s.near_ptr_size +#define FARPTRSIZE port->s.far_ptr_size +#define GPTRSIZE port->s.ptr_size +#define FUNCPTRSIZE port->s.funcptr_size +#define BFUNCPTRSIZE port->s.banked_funcptr_size +#define BITSIZE port->s.bit_size +#define FLOATSIZE port->s.float_size + +#define INITIAL_INLINEASM (4 * 1024) +#define DEFPOOLSTACK(type,size) \ + type *type##Pool ; \ + type *type##FreeStack [size] ; \ + int type##StackPtr = 0 ; + +#define PUSH(x,y) x##FreeStack[x##StackPtr++] = y +#define PEEK(x) x##FreeStack[x##StackPtr - 1] +#define POP(type) type##FreeStack[--type##StackPtr] +/* #define POP(x) (x##StackPtr ? x##FreeStack[--x##StackPtr] : \ + (assert(x##StackPtr),0)) */ +#ifdef UNIX +#define EMPTY(x) (x##StackPtr <= 1 ? 1 : 0) +#else +#define EMPTY(x) (x##StackPtr == 0 ? 1 : 0) +#endif + + +#define COPYTYPE(start,end,from) (end = getSpec (start = from)) + + +/* general purpose stack related macros */ +#define STACK_DCL(stack, type, size) \ + typedef type t_##stack; \ + t_##stack stack[size]; \ + t_##stack (*p_##stack) = stack - 1; + +#define STACK_EMPTY(stack) ((p_##stack) < stack) +#define STACK_FULL(stack) ((p_##stack) >= (stack + \ + sizeof(stack) / sizeof(*stack) - 1) ) + +#define STACK_PUSH_(stack, x) (*++p_##stack = (x)) +#define STACK_POP_(stack) (*p_##stack--) + +#define STACK_PUSH(stack, x) (STACK_FULL(stack) \ + ? (STACK_ERR(1, stack), *p_##stack) \ + : STACK_PUSH_(stack,x) ) + +#define STACK_POP(stack) (STACK_EMPTY(stack) \ + ? (STACK_ERR(-1, stack), *stack) \ + : STACK_POP_(stack) ) + +#define STACK_PEEK(stack) (STACK_EMPTY(stack) \ + ? (STACK_ERR(0, stack), *stack) \ + : *p_##stack ) + +#define STACK_ERR(o, stack) (fatal(1, E_STACK_VIOLATION, #stack, \ + (o < 0) \ + ? "underflow" \ + : (o > 0) \ + ? "overflow" \ + : "empty")) + +/* for semantically partitioned nest level values */ +#define LEVEL_UNIT 65536 +#define SUBLEVEL_UNIT 1 + +/* optimization options */ +struct optimize + { + int global_cse; + int ptrArithmetic; + int label1; + int label2; + int label3; + int label4; + int loopInvariant; + int loopInduction; + int noLoopReverse; + int codeSpeed; + int codeSize; + int lospre; + int allow_unsafe_read; + int noStdLibCall; + }; + +/** Build model. + Used in options.model.A bit each as port.supported_models is an OR + of these. +*/ +enum + { + NO_MODEL = 0, /* no model applicable */ + MODEL_SMALL = 1, + MODEL_COMPACT = 2, + MODEL_MEDIUM = 4, + MODEL_LARGE = 8, + MODEL_FLAT24 = 16, +// MODEL_PAGE0 = 32, /* disabled, was for the xa51 port */ + MODEL_HUGE = 64 /* for banked support */ + }; + +/* overlay segment name and the functions + that belong to it. used by pragma overlay */ +typedef struct { + char *osname; /* overlay segment name */ + int nfuncs; /* number of functions in this overlay */ + char *funcs[128]; /* function name that belong to this */ +} olay; + +enum + { + NO_DEPENDENCY_FILE_OPT = 0, + SYSTEM_DEPENDENCY_FILE_OPT = 1, + USER_DEPENDENCY_FILE_OPT = 2 + }; + +/* other command line options */ +/* + * cloneOptions function in SDCC.lex should be updated every time + * a new set is added to the options structure! + */ +struct options + { + int model; /* see MODEL_* defines above */ + int stackAuto; /* Stack Automatic */ + int useXstack; /* use Xternal Stack */ + int stack10bit; /* use 10 bit stack (flat24 model only) */ + int dump_ast; /* dump front-end tree before lowering to iCode */ + int dump_i_code; /* dump iCode at various stages */ + int dump_graphs; /* Dump graphs in .dot format (control-flow, conflict, etc) */ + int cc_only; /* compile only flag */ + int intlong_rent; /* integer & long support routines reentrant */ + int float_rent; /* floating point routines are reentrant */ + int out_fmt; /* 0 = undefined, 'i' = intel Hex format, 's' = motorola S19 format, 'E' = elf format, 'e' = true ELF format, 'Z' = gb format */ + int cyclomatic; /* print cyclomatic information */ + int noOverlay; /* don't overlay local variables & parameters */ + int xram_movc; /* use movc instead of movx to read xram (mcs51) */ + int nopeep; /* no peep hole optimization */ + int asmpeep; /* pass inline assembler thru peep hole */ + int peepReturn; /* enable peephole optimization for return instructions */ + int debug; /* generate extra debug info */ + int c1mode; /* Act like c1 - no pre-proc, asm or link */ + char *peep_file; /* additional rules for peep hole */ + int nostdlib; /* Don't use standard lib files */ + int nostdinc; /* Don't use standard include files */ + int noRegParams; /* Disable passing some parameters in registers */ + int verbose; /* Show what the compiler is doing */ + int lessPedantic; /* disable some warnings */ + int profile; /* Turn on extra profiling information */ + int omitFramePtr; /* Turn off the frame pointer. */ + int useAccelerator; /* use ds390 Arithmetic Accelerator */ + int noiv; /* do not generate irq vector table entries */ + int all_callee_saves; /* callee saves for all functions */ + int stack_probe; /* insert call to function __stack_probe */ + int tini_libid; /* library ID for TINI */ + int protect_sp_update; /* DS390 - will disable interrupts during ESP:SP updates */ + int parms_in_bank1; /* DS390 - use reg bank1 to pass parameters */ + int stack_size; /* MCS51/DS390 - Tells the linker to allocate this space for stack */ + int no_pack_iram; /* MCS51/DS390 - Deprecated: Tells the linker not to pack variables in internal ram */ + int acall_ajmp; /* MCS51 - Use acall/ajmp instead of lcall/ljmp */ + int no_ret_without_call; /* MCS51 - Do not use ret independent of acall/lcall */ + int use_non_free; /* Search / include non-free licensed libraries and header files */ + /* starting address of the segments */ + int xstack_loc; /* initial location of external stack */ + int stack_loc; /* initial value of internal stack pointer */ + int xdata_loc; /* xternal ram starts at address */ + int data_loc; /* interram start location */ + int idata_loc; /* indirect address space */ + int code_loc; /* code location start */ + int iram_size; /* internal ram size (used only for error checking) */ + int xram_size; /* external ram size (used only for error checking) */ + bool xram_size_set; /* since xram_size=0 is a possibility */ + int code_size; /* code size (used only for error checking) */ + int verboseExec; /* show what we are doing */ + int noXinitOpt; /* don't optimize initialized xdata */ + int noCcodeInAsm; /* hide c-code from asm */ + int iCodeInAsm; /* show i-code in asm */ + int noPeepComments; /* hide peephole optimizer comments */ + int verboseAsm; /* include comments generated with gen.c */ + int printSearchDirs; /* display the directories in the compiler's search path */ + int vc_err_style; /* errors and warnings are compatible with Micro$oft visual studio */ + int use_stdout; /* send errors to stdout instead of stderr */ + int no_std_crt0; /* for the z80/gbz80 do not link default crt0.o*/ + int std_c95; /* enable C95 keywords/constructs */ + int std_c99; /* enable C99 keywords/constructs */ + int std_c11; /* enable C11 keywords/constructs */ + int std_c2x; /* enable C2X keywords/constructs */ + int std_sdcc; /* enable SDCC extensions to C */ + int dollars_in_ident; /* zero means dollar signs are punctuation */ + int signed_char; /* use signed for char without signed/unsigned modifier */ + char *code_seg; /* segment name to use instead of CSEG */ + char *const_seg; /* segment name to use instead of CONST */ + char *data_seg; /* segment name to use instead of DATA */ + int dependencyFileOpt; /* write dependencies to given file */ + /* sets */ + set *calleeSavesSet; /* list of functions using callee save */ + set *excludeRegsSet; /* registers excluded from saving */ +/* set *olaysSet; * not implemented yet: overlay segments used in #pragma OVERLAY */ + int max_allocs_per_node; /* Maximum number of allocations / combinations considered at each node in the tree-decomposition based algorithms */ + bool noOptsdccInAsm; /* Do not emit .optsdcc in asm */ + bool oldralloc; /* Use old register allocator */ + }; + +/* forward definition for variables accessed globally */ +extern int noAssemble; /* no assembly, stop after code generation */ +extern char *yytext; +extern char *lexFilename; /* lex idea of current file name */ +extern int lexLineno; /* lex idea of line number of the current file */ +extern const char *fullSrcFileName; /* full name for the source file; */ + /* can be NULL while linking without compiling */ +extern const char *fullDstFileName; /* full name for the output file; */ + /* only given by -o, otherwise NULL */ +extern const char *dstFileName; /* destination file name without extension */ +extern const char *moduleName; /* module name is source file without path and extension */ + /* can be NULL while linking without compiling */ +extern int seqPointNo; /* current sequence point */ +extern FILE *yyin; /* */ +extern FILE *asmFile; /* assembly output file */ +extern FILE *cdbFile; /* debugger symbol file */ +extern long NestLevel; /* NestLevel SDCC.y */ +extern int stackPtr; /* stack pointer SDCC.y */ +extern int xstackPtr; /* external stack pointer SDCC.y */ +extern int reentrant; /* /X flag has been sent SDCC.y */ +extern char buffer[PATH_MAX * 2];/* general buffer SDCCmain.c */ +extern int currRegBank; /* register bank being used SDCCgens.c */ +extern int RegBankUsed[4]; /* JCF: register banks used SDCCmain.c */ +extern int BitBankUsed; /* MB: overlayable bit bank SDCCmain.c */ +extern struct symbol *currFunc; /* current function SDCCgens.c */ +extern long cNestLevel; /* block nest level SDCCval.c */ +extern int blockNo; /* maximum sequential block number */ +extern int currBlockno; /* sequential block number */ +extern struct optimize optimize; +extern struct options options; +extern unsigned maxInterrupts; +extern int ignoreTypedefType; +extern int gasOutput; + +/* Visible from SDCCmain.c */ +extern set *preArgvSet; +extern set *relFilesSet; +extern set *libFilesSet; +extern set *libPathsSet; +extern set *libDirsSet; /* list of lib search directories */ + +void setParseWithComma (set **, const char *); + +/** An assert() macro that will go out through sdcc's error + system. +*/ +#define wassertl(a,s) (void)((a) ? 0 : \ + (werror (E_INTERNAL_ERROR, __FILE__, __LINE__, s), 0)) + +#define wassert(a) wassertl(a,"code generator internal error") + +enum { + DUMP_RAW0 = 1, + DUMP_RAW1, + DUMP_CSE, + DUMP_DFLOW, + DUMP_GCSE, + DUMP_DEADCODE, + DUMP_LOOP, + DUMP_LOOPG, + DUMP_LOOPD, + DUMP_RANGE, + DUMP_PACK, + DUMP_RASSGN, + DUMP_LRANGE, + DUMP_LOSPRE, + DUMP_CUSTOM /* For temporary dump points */ +}; + +struct _dumpFiles { + int id; + char *ext; + FILE *filePtr; +}; + +extern struct _dumpFiles dumpFiles[]; + +/* Define well known filenos if the system does not define them. */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#endif diff --git a/src/SDCCglue.c b/src/SDCCglue.c new file mode 100644 index 0000000..8251c43 --- /dev/null +++ b/src/SDCCglue.c @@ -0,0 +1,2674 @@ +/*------------------------------------------------------------------------- + SDCCglue.c - glues everything we have done together into one file. + + Copyright (C) 1998 Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "common.h" +#include +#include "newalloc.h" +#include +#include +#include "dbuf_string.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +symbol *interrupts[INTNO_MAX + 1]; + +void printIval (symbol *, sym_link *, initList *, struct dbuf_s *, bool check); +set *publics = NULL; /* public variables */ +set *externs = NULL; /* Variables that are declared as extern */ +set *strSym = NULL; /* string initializers */ +set *ccpStr = NULL; /* char * const pointers with a string literal initializer */ + +unsigned maxInterrupts = 0; +int allocInfo = 1; +symbol *mainf; +int noInit = 0; /* no initialization */ + + +char * +aopLiteralGptr (const char * name, value * val) +{ + unsigned long v = ulFromVal (val); + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + + v >>= ((GPTRSIZE - 1) * 8); + + if (IS_FUNCPTR (val->type)) + dbuf_tprintf (&dbuf, "!immedbyte", v | pointerTypeToGPByte (DCL_TYPE (val->type->next), val->name, name)); + else if (IS_PTR (val->type) && !IS_GENPTR (val->type)) + dbuf_tprintf (&dbuf, "!immedbyte", pointerTypeToGPByte (DCL_TYPE (val->type), val->name, name)); + else + dbuf_tprintf (&dbuf, "!immedbyte", (unsigned int) v & 0xff); + + return dbuf_detach_c_str (&dbuf); +} + +const char * +aopLiteralLong (value *val, int offset, int size) +{ + unsigned long v; + struct dbuf_s dbuf; + + if (!val) + { + // assuming we have been warned before + val = constCharVal (0); + } + + dbuf_init (&dbuf, 128); + + switch (size) + { + case 1: + v = byteOfVal (val, offset); + dbuf_tprintf (&dbuf, "!immedbyte", (unsigned int) v & 0xff); + break; + case 2: + v = byteOfVal (val, offset+1); + v = (v << 8) | byteOfVal (val, offset); + dbuf_tprintf (&dbuf, "!immedword", (unsigned int) v & 0xffff); + break; + case 3: + v = byteOfVal (val, offset+2); + v = (v << 8) | byteOfVal (val, offset+1); + v = (v << 8) | byteOfVal (val, offset); + // we don't have a !immedword24 yet for ds390 + dbuf_printf (&dbuf, "#0x%06X", (unsigned int) v & 0xffffff); + break; + default: + /* Hmm. Too big for now. */ + assert (0); + } + return dbuf_detach_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* aopLiteral - string from a literal value */ +/*-----------------------------------------------------------------*/ +const char * +aopLiteral (value *val, int offset) +{ + return aopLiteralLong (val, offset, 1); +} + +/*-----------------------------------------------------------------*/ +/* emitDebugSym - emit label for debug symbol */ +/*-----------------------------------------------------------------*/ +static void +emitDebugSym (struct dbuf_s *oBuf, symbol * sym) +{ + if (sym->level && sym->localof) /* symbol scope is local */ + { + dbuf_printf (oBuf, "L%s.%s$", moduleName, sym->localof->name); + } + else if (IS_STATIC (sym->etype)) /* symbol scope is file */ + { + dbuf_printf (oBuf, "F%s$", moduleName); + } + else /* symbol scope is global */ + { + dbuf_printf (oBuf, "G$"); + } + dbuf_printf (oBuf, "%s$%ld_%ld$%d", sym->name, sym->level / LEVEL_UNIT, sym->level % LEVEL_UNIT, sym->block); +} + +/*-----------------------------------------------------------------*/ +/* emitRegularMap - emit code for maps with no special cases */ +/*-----------------------------------------------------------------*/ +static void +emitRegularMap (memmap *map, bool addPublics, bool arFlag) +{ + symbol *sym; + ast *ival = NULL; + + if (!map) + return; + + if (addPublics) + { + /* PENDING: special case here - should remove */ + if (!strcmp (map->sname, CODE_NAME)) + dbuf_tprintf (&map->oBuf, "\t!areacode\n", map->sname); + else if (!strcmp (map->sname, DATA_NAME)) + { + dbuf_tprintf (&map->oBuf, "\t!areadata\n", map->sname); + if (options.data_seg && strcmp (DATA_NAME, options.data_seg)) + dbuf_tprintf (&map->oBuf, "\t!area\n", options.data_seg); + } + else if (!strcmp (map->sname, HOME_NAME)) + dbuf_tprintf (&map->oBuf, "\t!areahome\n", map->sname); + else + dbuf_tprintf (&map->oBuf, "\t!area\n", map->sname); + + if (map->regsp) + dbuf_tprintf (&map->oBuf, "\t!org\n", 0); + } + + for (sym = setFirstItem (map->syms); sym; sym = setNextItem (map->syms)) + { + symbol *newSym = NULL; + + /* if allocation required check is needed + then check if the symbol really requires + allocation only for local variables */ + + if (arFlag && !IS_AGGREGATE (sym->type) && !(sym->_isparm && !IS_REGPARM (sym->etype)) && !sym->allocreq && sym->level) + continue; + + /* for bitvar locals and parameters */ + if (!arFlag && !sym->allocreq && sym->level && !SPEC_ABSA (sym->etype)) + { + continue; + } + + /* if global variable & not static or extern + and addPublics allowed then add it to the public set */ + if ((sym->level == 0 || + (sym->_isparm && !IS_REGPARM (sym->etype) && !IS_STATIC (sym->localof->etype))) && + addPublics && + !IS_STATIC (sym->etype) && + (IS_FUNC (sym->type) ? (sym->used || IFFUNC_HASBODY (sym->type)) : (!IS_EXTERN (sym->etype) || sym->ival)) && + !(IFFUNC_ISINLINE (sym->type) && !IS_STATIC (sym->etype) && !IS_EXTERN (sym->etype))) + { + addSetHead (&publics, sym); + } + + /* if extern then add it into the extern list */ + if (IS_EXTERN (sym->etype) && !sym->ival) + { + addSetHead (&externs, sym); + continue; + } + + /* if extern then do nothing or is a function + then do nothing */ + if (IS_FUNC (sym->type) && !(sym->isitmp)) + continue; + + /* if it has an initial value then do it only if + it is a global variable */ + if (sym->ival && sym->level == 0) + { + if ((SPEC_OCLS (sym->etype) == xidata || SPEC_OCLS (sym->etype) == initialized) && !SPEC_ABSA (sym->etype) && !SPEC_ADDRSPACE (sym->etype)) + { + sym_link *t; + ast *ival = NULL; + symbol *tsym = copySymbol (sym); + + // check for constant + if (IS_AGGREGATE (tsym->type)) + { + ival = initAggregates (tsym, tsym->ival, NULL); + } + else + { + if (getNelements (tsym->type, tsym->ival) > 1) + { + werrorfl (tsym->fileDef, tsym->lineDef, W_EXCESS_INITIALIZERS, "scalar", tsym->name); + } + ival = newNode ('=', newAst_VALUE (symbolVal (tsym)), + decorateType (resolveSymbols (list2expr (tsym->ival)), RESULT_TYPE_NONE)); + } + if (ival) + { + // set ival's lineno to where the symbol was defined + setAstFileLine (ival, filename = tsym->fileDef, lineno = tsym->lineDef); + // check if this is not a constant expression + if (!constExprTree (ival)) + { + werrorfl (ival->filename, ival->lineno, E_CONST_EXPECTED, "found expression"); + // but try to do it anyway + } + } + + /* create a new "XINIT (CODE)" symbol, that will be emitted later + in the static seg */ + newSym = copySymbol (sym); + SPEC_OCLS (newSym->etype) = (SPEC_OCLS (sym->etype) == xidata) ? xinit : initializer; + SNPRINTF (newSym->name, sizeof (newSym->name), "__xinit_%s", sym->name); + SNPRINTF (newSym->rname, sizeof (newSym->rname), "__xinit_%s", sym->rname); + + /* find the first non-array link */ + t = newSym->type; + while (IS_ARRAY (t)) + t = t->next; + if (IS_SPEC (t)) + SPEC_CONST (t) = 1; + else + DCL_PTR_CONST (t) = 1; + SPEC_STAT (newSym->etype) = 1; + + strSym = NULL; + ++noAlloc; + resolveIvalSym (newSym->ival, newSym->type); + --noAlloc; + + // add it to the "XINIT (CODE)" segment + addSet ((SPEC_OCLS (sym->etype) == xidata) ? &xinit->syms : &initializer->syms, newSym); + + if (!SPEC_ABSA (sym->etype)) + { + struct dbuf_s tmpBuf; + symbol *ps = NULL; + set *tmpSym = NULL; + + wassert(dbuf_init (&tmpBuf, 4096)); + // before allocation we must parse the sym->ival tree + // but without actually generating initialization code + ++noAlloc; + resolveIvalSym (sym->ival, sym->type); + ++noInit; + printIval (sym, sym->type, sym->ival, &tmpBuf, TRUE); + --noInit; + --noAlloc; + + // delete redundant __str_%d symbols (initializer for char arrays) + for (ps = setFirstItem (statsg->syms); ps; ps = setNextItem (statsg->syms)) + if (!strstr (tmpBuf.buf, ps->name) && isinSet (strSym, ps)) + addSet (&tmpSym, ps); + for (ps = setFirstItem (tmpSym); ps; ps = setNextItem (tmpSym)) + deleteSetItem (&statsg->syms, ps); + + deleteSet (&tmpSym); + dbuf_destroy (&tmpBuf); + } + + if (strSym) + deleteSet (&strSym); + } + else + { + if (IS_AGGREGATE (sym->type)) + { + ival = initAggregates (sym, sym->ival, NULL); + } + else + { + if (getNelements (sym->type, sym->ival) > 1) + { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", sym->name); + } + ival = newNode ('=', newAst_VALUE (symbolVal (sym)), + decorateType (resolveSymbols (list2expr (sym->ival)), RESULT_TYPE_NONE)); + } + codeOutBuf = &statsg->oBuf; + + if (ival) + { + // set ival's lineno to where the symbol was defined + setAstFileLine (ival, filename = sym->fileDef, lineno = sym->lineDef); + // check if this is not a constant expression + if (!constExprTree (ival)) + { + werrorfl (ival->filename, ival->lineno, E_CONST_EXPECTED, "found expression"); + // but try to do it anyway + } + allocInfo = 0; + if (!astErrors (ival)) + eBBlockFromiCode (iCodeFromAst (ival)); + allocInfo = 1; + } + } + } + + /* if it has an absolute address then generate + an equate for this no need to allocate space */ + if (SPEC_ABSA (sym->etype) && !sym->ival) + { + char *equ = "="; + + /* print extra debug info if required */ + if (options.debug) + { + emitDebugSym (&map->oBuf, sym); + dbuf_printf (&map->oBuf, " == 0x%04x\n", SPEC_ADDR (sym->etype)); + } + dbuf_printf (&map->oBuf, "%s\t%s\t0x%04x\n", sym->rname, equ, SPEC_ADDR (sym->etype)); + } + else + { + int size = getSize (sym->type) + sym->flexArrayLength; + if (size == 0) + { + werrorfl (sym->fileDef, sym->lineDef, E_UNKNOWN_SIZE, sym->name); + } + /* allocate space */ + if (SPEC_ABSA (sym->etype)) + { + dbuf_tprintf (&map->oBuf, "\t!org\n", SPEC_ADDR (sym->etype)); + } + /* print extra debug info if required */ + if (options.debug) + { + emitDebugSym (&map->oBuf, sym); + dbuf_printf (&map->oBuf, "==.\n"); + } + if (IS_STATIC (sym->etype) || sym->level) + dbuf_tprintf (&map->oBuf, "!slabeldef\n", sym->rname); + else + dbuf_tprintf (&map->oBuf, "!labeldef\n", sym->rname); + dbuf_tprintf (&map->oBuf, "\t!ds\n", (unsigned int) size & 0xffff); + } + + sym->ival = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/* initValPointer - pointer initialization code massaging */ +/*-----------------------------------------------------------------*/ +value * +initValPointer (ast *expr) +{ + value *val; + + /* no then we have to do these kludgy checks */ + /* pointers can be initialized with address of + a variable or address of an array element */ + if (IS_ADDRESS_OF_OP (expr)) + { + /* address of symbol */ + if (IS_AST_SYM_VALUE (expr->left)) + { + STORAGE_CLASS sclass = SPEC_SCLS (expr->left->etype); + memmap *oclass = SPEC_OCLS (expr->left->etype); + + val = AST_VALUE (expr->left); + val->type = newLink (DECLARATOR); + if (sclass == S_CODE) + { + DCL_TYPE (val->type) = CPOINTER; + CodePtrPointsToConst (val->type); + } + else if (oclass) + DCL_TYPE (val->type) = oclass->ptrType; + else if (sclass == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (sclass == S_DATA) + DCL_TYPE (val->type) = POINTER; + else if (sclass == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (sclass == S_PDATA) + DCL_TYPE (val->type) = PPOINTER; + else if (sclass == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (sclass == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + val->type->next = expr->left->ftype; + val->etype = getSpec (val->type); + return val; + } + + /* if address of indexed array */ + if (IS_ARRAY_OP (expr->left)) + return valForArray (expr->left); + + /* if address of structure element then + case 1. a.b ; */ + if (IS_AST_OP (expr->left) && expr->left->opval.op == '.') + { + return valForStructElem (expr->left->left, expr->left->right); + } + + /* case 2. (&a)->b ; + (&some_struct)->element */ + if (IS_AST_OP (expr->left) && expr->left->opval.op == PTR_OP && IS_ADDRESS_OF_OP (expr->left->left)) + { + return valForStructElem (expr->left->left->left, expr->left->right); + } + + /* case 2.1. a->b */ + if (IS_AST_OP (expr->left) && expr->left->opval.op == PTR_OP) + { + return valForStructElem (expr->left->left, expr->left->right); + } + } + + /* case 3. (((char *) &a) +/- constant) */ + if (IS_AST_OP (expr) && + (expr->opval.op == '+' || expr->opval.op == '-') && + IS_CAST_OP (expr->left) && IS_ADDRESS_OF_OP (expr->left->right) && IS_AST_LIT_VALUE (expr->right)) + { + return valForCastAggr (expr->left->right->left, expr->left->left->opval.lnk, expr->right, expr->opval.op); + } + + /* case 4. (array type) */ + if (IS_AST_SYM_VALUE (expr) && IS_ARRAY (expr->ftype)) + { + STORAGE_CLASS sclass = SPEC_SCLS (expr->etype); + memmap *oclass = SPEC_OCLS (expr->etype); + + val = copyValue (AST_VALUE (expr)); + val->type = newLink (DECLARATOR); + if (SPEC_SCLS (expr->etype) == S_CODE) + { + DCL_TYPE (val->type) = CPOINTER; + CodePtrPointsToConst (val->type); + } + else if (oclass) + DCL_TYPE (val->type) = oclass->ptrType; + else if (sclass == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (sclass == S_DATA) + DCL_TYPE (val->type) = POINTER; + else if (sclass == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (sclass == S_PDATA) + DCL_TYPE (val->type) = PPOINTER; + else if (sclass == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (sclass == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + val->type->next = expr->ftype->next; + val->etype = getSpec (val->type); + return val; + } + + /* if structure element then + case 5. a.b ; */ + if (IS_AST_OP (expr) && expr->opval.op == '.') + { + return valForStructElem (expr->left, expr->right); + } + + /* case 6. a->b ; + some_struct->element */ + if (IS_AST_OP (expr) && expr->opval.op == PTR_OP) + { + ast *t = expr->left; + + /*if (expr->left->left) + if (expr->left->left->left) + if (expr->left->left->opval.op == '[') + t = expr->left->left; + else + t = expr->left->left->left; + else + t = expr->left->left; + else + t = expr->left;*/ + + /* a more generic way of above code */ + while (t->left != NULL && t->opval.op != '[') + t = t->left; + + return valForStructElem (t, expr->right); + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* initPointer - pointer initialization code massaging */ +/*-----------------------------------------------------------------*/ +value * +initPointer (initList *ilist, sym_link *toType, int showError) +{ + value *val; + ast *expr; + + if (!ilist) + { + return valCastLiteral (toType, 0.0, 0); + } + + expr = list2expr (ilist); + + if (!expr) + { + if (showError) + werror (E_CONST_EXPECTED); + return 0; + } + + /* try it the old way first */ + if ((val = constExprValue (expr, FALSE))) + return val; + + /* ( ptr + constant ) */ + if (IS_AST_OP (expr) && + (expr->opval.op == '+' || expr->opval.op == '-') && + IS_AST_SYM_VALUE (expr->left) && + (IS_ARRAY (expr->left->ftype) || IS_PTR (expr->left->ftype)) && + compareType (toType, expr->left->ftype) && IS_AST_LIT_VALUE (expr->right)) + { + return valForCastAggr (expr->left, expr->left->ftype, expr->right, expr->opval.op); + } + + /* (char *)(expr1) */ + if (IS_CAST_OP (expr)) + { + if (compareType (toType, expr->left->ftype) == 0 && showError) + { + werror (W_INIT_WRONG); + printFromToType (expr->left->ftype, toType); + } + val = initValPointer (expr->right); + if (val) + { + DECLARATOR_TYPE dcl_type = DCL_TYPE (val->type); + val->type = expr->left->ftype; + val->etype = getSpec (val->type); + if (IS_GENPTR (val->type)) + DCL_TYPE (val->type) = dcl_type; + } + } + else + { + val = initValPointer (expr); + } + + if (val) + return val; + + if (showError) + { + if (expr) + werrorfl (expr->filename, expr->lineno, E_CONST_EXPECTED); + else + werror (E_CONST_EXPECTED); + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* printChar - formats and prints a UTF-8 character string with DB */ +/*-----------------------------------------------------------------*/ +void +printChar (struct dbuf_s *oBuf, const char *s, int plen) +{ + int i; + int pplen = 0; + char buf[100]; + char *p = buf; + + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) // Assembler does not support .ascii + { + while (pplen < plen) + { + if (isprint((unsigned char) *s)) + dbuf_tprintf (oBuf, "\t!db !constbyte\t; %c\n", (unsigned char) *s, (unsigned char) *s); + else + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (unsigned char) *s); + + s++; + pplen++; + } + return; + } + + while (pplen < plen) + { + i = 60; + while (i && pplen < plen) + { + if (!isprint((unsigned char) *s) || *s == '\"' || *s == '\\') + { + *p = '\0'; + if (p != buf) + dbuf_tprintf (oBuf, "\t!ascii\n", buf); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (unsigned char) *s); + p = buf; + i = 60; + } + else + { + *p = *s; + p++; + i--; + } + s++; + pplen++; + } + if (p != buf) + { + *p = '\0'; + dbuf_tprintf (oBuf, "\t!ascii\n", buf); + p = buf; + i = 60; + } + } +} + +/*-----------------------------------------------------------------*/ +/* printChar16 - formats and prints a UTF-16 character string with DB*/ +/*-----------------------------------------------------------------*/ +void +printChar16 (struct dbuf_s *oBuf, const TYPE_TARGET_UINT *s, int plen) +{ + int pplen = 0; + + while (pplen < plen) + { + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 0) & 0xff); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 8) & 0xff); + } + else if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %d,%d\n", (*s >> 0) & 0xff, (*s >> 8) & 0xff); + else + dbuf_printf (oBuf, "\t.byte %d,%d\n", (*s >> 8) & 0xff, (*s >> 0) & 0xff); + + s++; + pplen++; + } + while (pplen < plen) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", 0); + pplen++; + } +} + +/*-----------------------------------------------------------------*/ +/* printChar32 - formats and prints a UTF-32 character string with DB*/ +/*-----------------------------------------------------------------*/ +void +printChar32 (struct dbuf_s *oBuf, const TYPE_TARGET_ULONG *s, int plen) +{ + int pplen = 0; + + while (pplen < plen) + { + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 0) & 0xff); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 8) & 0xff); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 16) & 0xff); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (*s >> 24) & 0xff); + } + else if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %d,%d,%d,%d\n", (*s >> 0) & 0xff, (*s >> 8) & 0xff, (*s >> 16) & 0xff, (*s >> 24) & 0xff); + else + dbuf_printf (oBuf, "\t.byte %d,%d,%d,%d\n", (*s >> 24) & 0xff, (*s >> 16) & 0xff, (*s >> 8) & 0xff,(*s >> 0) & 0xff); + + s++; + pplen++; + } + while (pplen < plen) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", 0); + pplen++; + } +} + +/*-----------------------------------------------------------------*/ +/* return the generic pointer high byte for a given pointer type. */ +/*-----------------------------------------------------------------*/ +int +pointerTypeToGPByte (const int p_type, const char *iname, const char *oname) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + return GPTYPE_NEAR; + case GPOINTER: + werror (W_USING_GENERIC_POINTER, iname ? iname : "", oname ? oname : ""); + return -1; + case FPOINTER: + return GPTYPE_FAR; + case CPOINTER: + case FUNCTION: + return GPTYPE_CODE; + case PPOINTER: + return GPTYPE_XSTACK; + default: + fprintf (stderr, "*** internal error: unknown pointer type %d in GPByte.\n", p_type); + exit (EXIT_FAILURE); + } + return -1; +} + +/*-----------------------------------------------------------------*/ +/* printIvalVal - generate ival according from value */ +/*-----------------------------------------------------------------*/ +static void printIvalVal(struct dbuf_s *oBuf, value *val, int size, bool newline) +{ + if (size == 2 && port->use_dw_for_init) + { + dbuf_tprintf (oBuf, "\t!dws\n", aopLiteralLong (val, 0, 2)); + return; + } + + const bool use_ret = TARGET_PDK_LIKE && !TARGET_IS_PDK16; + + if (!use_ret) + dbuf_printf (oBuf, "\t.byte "); + + for (int i = 0; i < size; i++) + { + const char *inst; + const char *byte = aopLiteral (val, port->little_endian ? i : size - 1 - i); + if (use_ret) + inst = i != size - 1 ? "\tret %s\n" : "\tret %s"; + else + inst = i != size - 1 ? "%s, " : "%s"; + dbuf_printf (oBuf, inst, byte); + } + if (newline) + dbuf_printf (oBuf, "\n"); +} + +/*-----------------------------------------------------------------*/ +/* _printPointerType - generates ival for pointer type */ +/*-----------------------------------------------------------------*/ +static void +_printPointerType (struct dbuf_s *oBuf, const char *name, int size) +{ + wassert (!TARGET_PDK_LIKE); + + if (size == 4) + { + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s, (%s >> 8), (%s >> 16), (%s >> 24)", name, name, name, name); + else + dbuf_printf (oBuf, "\t.byte (%s >> 24), (%s >> 16), (%s >> 8), %s", name, name, name, name); + } + else if (size == 3) + { + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s, (%s >> 8), (%s >> 16)", name, name, name); + else + dbuf_printf (oBuf, "\t.byte (%s >> 16), (%s >> 8), %s", name, name, name); + } + else + { + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s, (%s >> 8)", name, name); + else + dbuf_printf (oBuf, "\t.byte (%s >> 8), %s", name, name); + } +} + +/*-----------------------------------------------------------------*/ +/* printPointerType - generates ival for pointer type */ +/*-----------------------------------------------------------------*/ +static void +printPointerType (struct dbuf_s *oBuf, const char *name) +{ + _printPointerType (oBuf, name, (options.model == MODEL_FLAT24) ? 3 : 2); + dbuf_printf (oBuf, "\n"); +} + +/*-----------------------------------------------------------------*/ +/* printGPointerType - generates ival for generic pointer type */ +/*-----------------------------------------------------------------*/ +static void +printGPointerType (struct dbuf_s *oBuf, const char *iname, const char *oname, int type) +{ + int byte = pointerTypeToGPByte (type, iname, oname); + int size = (options.model == MODEL_FLAT24) ? 3 : 2; + if (byte == -1) + { + _printPointerType (oBuf, iname, size + 1); + dbuf_printf (oBuf, "\n"); + } + else + { + _printPointerType (oBuf, iname, size); + dbuf_printf (oBuf, ",#0x%02x\n", byte); + } +} + +/*-----------------------------------------------------------------*/ +/* printIvalType - generates ival for int/char */ +/*-----------------------------------------------------------------*/ +void +printIvalType (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf) +{ + value *val; + unsigned long ulVal = 0; + + /* if initList is deep */ + if (ilist && (ilist->type == INIT_DEEP)) + ilist = ilist->init.deep; + + if (!(val = list2val (ilist, FALSE))) + { + if (!!(val = initPointer (ilist, type, 0))) + { + int i, size = getSize (type), le = port->little_endian, top = (options.model == MODEL_FLAT24) ? 3 : 2;; + dbuf_printf (oBuf, "\t.byte "); + for (i = (le ? 0 : size - 1); le ? (i < size) : (i > -1); i += (le ? 1 : -1)) + { + if (i == 0) + if (val->name && strlen (val->name) > 0) + dbuf_printf (oBuf, "%s", val->name); + else + dbuf_printf (oBuf, "#0x00"); + else if (0 < i && i < top) + if (val->name && strlen (val->name) > 0) + dbuf_printf (oBuf, "(%s >> %d)", val->name, i * 8); + else + dbuf_printf (oBuf, "#0x00"); + else + dbuf_printf (oBuf, "#0x00"); + if (i == (le ? (size - 1) : 0)) + dbuf_printf (oBuf, "\n"); + else + dbuf_printf (oBuf, ", "); + } + return; + } + else + { + werrorfl (ilist->filename, ilist->lineno, E_CONST_EXPECTED); + val = constCharVal (0); + } + } + + if (val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werrorfl (ilist->filename, ilist->lineno, E_CONST_EXPECTED); + val = constCharVal (0); + } + + /* check if the literal value is within bounds */ + if (checkConstantRange (type, val->etype, '=', FALSE) == CCR_OVL) + { + werror (W_LIT_OVERFLOW); + } + + if (val->type != type) + { + val = valCastLiteral (type, floatFromVal (val), (TYPE_TARGET_ULONGLONG) ullFromVal (val)); + } + + if (IS_INTEGRAL (val->type)) + ulVal = ulFromVal (val); + + switch (getSize (type)) + { + case 1: + if (!val) + dbuf_tprintf (oBuf, "\t!db !constbyte\n", 0); + else + { + if (IS_UNSIGNED (val->type)) + { + dbuf_tprintf (oBuf, "\t!dbs\t; %u", aopLiteral (val, 0), (unsigned int) ulVal); + } + else + { + dbuf_tprintf (oBuf, "\t!dbs\t; % d", aopLiteral (val, 0), (int) ulVal); + } + if (isalnum ((int) ulVal)) + dbuf_tprintf (oBuf, "\t'%c'\n", (int) ulVal); + else + dbuf_tprintf (oBuf, "\n"); + } + break; + + case 2: + if (port->use_dw_for_init) + { + printIvalVal (oBuf, val, 2, true); + break; + } + printIvalVal (oBuf, val, 2, false); + if (IS_UNSIGNED (val->type)) + dbuf_printf (oBuf, "\t; %u\n", (unsigned int) ulVal); + else + dbuf_printf (oBuf, "\t; % d\n", (int) ulVal); + break; + + case 4: + if (!val) + { + dbuf_tprintf (oBuf, "\t!dw !constword\n", 0); + dbuf_tprintf (oBuf, "\t!dw !constword\n", 0); + } + else + { + printIvalVal (oBuf, val, 4, false); + + if (IS_FLOAT (val->type)) + { + dbuf_printf (oBuf, "\t; % e\n", floatFromVal (val)); + } + else + { + if (IS_UNSIGNED (val->type)) + dbuf_printf (oBuf, "\t; %u\n", (unsigned int) ulVal); + else + dbuf_printf (oBuf, "\t; % d\n", (int) ulVal); + } + } + break; + case 8: + printIvalVal (oBuf, val, 8, true); // TODO: Print value as comment. Does dbuf_printf support long long even on MSVC? + break; + default: + wassertl (0, "Attempting to initialize integer of non-handled size."); + } +} + +/*-----------------------------------------------------------------*/ +/* printIvalBitFields - generate initializer for bitfields */ +/* return the number of bytes written */ +/*-----------------------------------------------------------------*/ +static unsigned int +printIvalBitFields (symbol ** sym, initList ** ilist, struct dbuf_s *oBuf) +{ + symbol *lsym = *sym; + initList *lilist = *ilist; + unsigned long ival = 0; + unsigned size = 0; + unsigned bit_start = 0; + unsigned long int bytes_written = 0; + + while (lsym && IS_BITFIELD (lsym->type)) + { + unsigned bit_length = SPEC_BLEN (lsym->etype); + if (0 == bit_length) + { + /* bit-field structure member with a width of 0 */ + lsym = lsym->next; + break; + } + else if (!SPEC_BUNNAMED (lsym->etype)) + { + /* not an unnamed bit-field structure member */ + value *val = list2val (lilist, TRUE); + + if (val && val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werrorfl (lilist->filename, lilist->lineno, E_CONST_EXPECTED); + val = constCharVal (0); + } + if (size) + { + if (bit_length > 8) + size += (bit_length + 7) / 8; + } + else + size = (bit_length + 7) / 8; + + /* check if the literal value is within bounds */ + if (val && checkConstantRange (lsym->etype, val->etype, '=', FALSE) == CCR_OVL) + { + werror (W_LIT_OVERFLOW); + } + + ival |= (ulFromVal (val) & ((1ul << bit_length) - 1ul)) << bit_start; + lilist = lilist ? lilist->next : NULL; + } + bit_start += bit_length; + lsym = lsym->next; + if (lsym && IS_BITFIELD (lsym->type) && (0 == SPEC_BSTR (lsym->etype))) + { + /* a new integer */ + break; + } + } + + switch (size) + { + case 0: + dbuf_tprintf (oBuf, "\n"); + break; + + case 1: + dbuf_tprintf (oBuf, "\t!db !constbyte\n", ival); + bytes_written++; + break; + + case 2: + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (ival & 0xff)); + dbuf_tprintf (oBuf, "\t!db !constbyte\n", ((ival >> 8) & 0xff)); + } + else + dbuf_tprintf (oBuf, "\t!db !constbyte, !constbyte\n", (ival & 0xff), (ival >> 8) & 0xff); + bytes_written += 2; + break; + + case 4: + dbuf_tprintf (oBuf, "\t!db !constbyte, !constbyte, !constbyte, !constbyte\n", + (ival & 0xff), (ival >> 8) & 0xff, (ival >> 16) & 0xff, (ival >> 24) & 0xff); + bytes_written += 4; + break; + default: + wassert (0); + } + *sym = lsym; + *ilist = lilist; + + return (bytes_written); +} + +/*-----------------------------------------------------------------*/ +/* printIvalStruct - generates initial value for structures */ +/*-----------------------------------------------------------------*/ +static void +printIvalStruct (symbol *sym, sym_link *type, initList *ilist, struct dbuf_s *oBuf) +{ + symbol *sflds; + initList *iloop = NULL; + unsigned int skip_holes = 0; + + sflds = SPEC_STRUCT (type)->fields; + + if (ilist) + { + if (ilist->type != INIT_DEEP) + { + werrorfl (sym->fileDef, sym->lineDef, E_INIT_STRUCT, sym->name); + return; + } + + iloop = ilist->init.deep; + } + + if (SPEC_STRUCT (type)->type == UNION) + { + int size; + /* skip past holes, print value */ + while (iloop && iloop->type == INIT_HOLE) + { + iloop = iloop->next; + sflds = sflds->next; + } + printIval (sym, sflds->type, iloop, oBuf, 1); + /* pad out with zeros if necessary */ + size = getSize(type) - getSize(sflds->type); + for ( ; size > 0 ; size-- ) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", 0); + } + /* advance past holes to find out if there were excess initializers */ + do + { + iloop = iloop ? iloop->next : NULL; + sflds = sflds->next; + } + while (iloop && iloop->type == INIT_HOLE); + } + else + { + while (sflds) + { + unsigned int oldoffset = sflds->offset; + + if (IS_BITFIELD (sflds->type)) + printIvalBitFields (&sflds, &iloop, oBuf); + else + { + printIval (sym, sflds->type, iloop, oBuf, 1); + sflds = sflds->next; + iloop = iloop ? iloop->next : NULL; + } + + // Handle members from anonymous unions. Just a hack to fix bug #2643. + while (sflds && sflds->offset == oldoffset) + { + sflds = sflds->next; + skip_holes++; + } + } + + while (skip_holes && iloop && iloop->type == INIT_HOLE) + { + skip_holes--; + iloop = iloop ? iloop->next : NULL; + } + } + + if (iloop) + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "struct", sym->name); +} + +/*-----------------------------------------------------------------*/ +/* printIvalChar - generates initital value for character array */ +/*-----------------------------------------------------------------*/ +int +printIvalChar (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf, const char *s, bool check) +{ + value *val; + size_t size = DCL_ELEM(type); + char *p; + size_t asz; + + if (!s) + { + val = list2val (ilist, TRUE); + /* if the value is a character string */ + if (IS_ARRAY (val->type) && IS_CHAR (val->etype)) + { + if (!size) + { + /* we have not been given a size, but now we know it */ + size = strlen (SPEC_CVAL (val->etype).v_char) + 1; + /* but first check, if it's a flexible array */ + if (sym && IS_STRUCT (sym->type)) + sym->flexArrayLength = size; + else + DCL_ELEM (type) = size; + } + + if (check && DCL_ELEM (val->type) > size) + werror (W_EXCESS_INITIALIZERS, "array of chars", sym->name, sym->lineDef); + + if (size > (asz = DCL_ELEM (val->type)) && !!(p = malloc (size))) + { + memcpy (p, SPEC_CVAL (val->etype).v_char, asz); + memset (p + asz, 0x00, size - asz); + printChar (oBuf, p, size); + free (p); + } + else + printChar (oBuf, SPEC_CVAL (val->etype).v_char, size); + + return 1; + } + else + return 0; + } + else + printChar (oBuf, s, strlen (s) + 1); + return 1; +} + +static size_t strLen16(const TYPE_TARGET_UINT *s) +{ + size_t l = 0; + while(*s++) + l++; + + return l; +} + +/*-----------------------------------------------------------------*/ +/* printIvalChar16 - generates initital value for character array */ +/*-----------------------------------------------------------------*/ +int +printIvalChar16 (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf, const TYPE_TARGET_UINT *s, bool check) +{ + value *val; + size_t size = DCL_ELEM(type); + TYPE_TARGET_UINT *p; + size_t asz; + + if (!s) + { + val = list2val (ilist, TRUE); + /* if the value is a character string */ + if (IS_ARRAY (val->type) && IS_INT (val->etype) && IS_UNSIGNED (val->etype) && !IS_LONG (val->etype)) + { + if (!size) + { + /* we have not been given a size, but now we know it */ + size = strLen16 (SPEC_CVAL (val->etype).v_char16) + 1; + /* but first check, if it's a flexible array */ + if (sym && IS_STRUCT (sym->type)) + sym->flexArrayLength = size; + else + DCL_ELEM (type) = size; + } + + if (check && DCL_ELEM (val->type) > size) + werror (W_EXCESS_INITIALIZERS, "array of chars", sym->name, sym->lineDef); + + if (size > (asz = DCL_ELEM (val->type)) && !!(p = malloc (size * 2))) + { + memcpy (p, SPEC_CVAL (val->etype).v_char16, asz * 2); + memset (p + asz, 0x00, size * 2 - asz * 2); + printChar16 (oBuf, p, size); + free (p); + } + else + printChar16 (oBuf, SPEC_CVAL (val->etype).v_char16, size); + + return 1; + } + else + return 0; + } + else + printChar16 (oBuf, s, strLen16 (s) + 1); + return 1; +} + +static size_t strLen32(const TYPE_TARGET_ULONG *s) +{ + size_t l = 0; + while(*s++) + l++; + return l; +} + +/*-----------------------------------------------------------------*/ +/* printIvalChar32 - generates initital value for character array */ +/*-----------------------------------------------------------------*/ +int +printIvalChar32 (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf, const TYPE_TARGET_ULONG *s, bool check) +{ + value *val; + size_t size = DCL_ELEM(type); + TYPE_TARGET_ULONG *p; + size_t asz; + + if (!s) + { + val = list2val (ilist, TRUE); + /* if the value is a character string */ + if (IS_ARRAY (val->type) && IS_INT (val->etype) && IS_UNSIGNED (val->etype) && IS_LONG (val->etype)) + { + if (!size) + { + /* we have not been given a size, but now we know it */ + size = strLen32 (SPEC_CVAL (val->etype).v_char32) + 1; + /* but first check, if it's a flexible array */ + if (sym && IS_STRUCT (sym->type)) + sym->flexArrayLength = size; + else + DCL_ELEM (type) = size; + } + + if (check && DCL_ELEM (val->type) > size) + werror (W_EXCESS_INITIALIZERS, "array of chars", sym->name, sym->lineDef); + + if (size > (asz = DCL_ELEM (val->type)) && !!(p = malloc (size * 4))) + { + memcpy (p, SPEC_CVAL (val->etype).v_char32, asz * 4); + memset (p + asz, 0x00, size * 4 - asz * 4); + printChar32 (oBuf, p, size); + free (p); + } + else + printChar32 (oBuf, SPEC_CVAL (val->etype).v_char32, size); + + return 1; + } + else + return 0; + } + else + printChar32 (oBuf, s, strLen32 (s) + 1); + return 1; +} + +/*-----------------------------------------------------------------*/ +/* printIvalArray - generates code for array initialization */ +/*-----------------------------------------------------------------*/ +void +printIvalArray (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf, bool check) +{ + value *val; + initList *iloop; + unsigned int size = 0; + + if (ilist) + { + /* take care of the special case */ + /* array of characters can be init */ + /* by a string */ + /* char *p = "abc"; */ + if ((IS_CHAR (type->next) || IS_INT (type->next)) && ilist->type == INIT_NODE) + { + val = list2val (ilist, TRUE); + if (!val) + { + werrorfl (ilist->filename, ilist->lineno, E_INIT_STRUCT, sym->name); + return; + } + if (!IS_LITERAL (val->etype)) + { + werrorfl (ilist->filename, ilist->lineno, E_CONST_EXPECTED); + return; + } + if (IS_CHAR (type->next) && printIvalChar (sym, type, ilist, oBuf, SPEC_CVAL (sym->etype).v_char, check)) + return; + if (IS_INT (type->next) && IS_UNSIGNED (type->next)) + { + if (!IS_LONG (type->next) && printIvalChar16 (sym, type, ilist, oBuf, SPEC_CVAL (sym->etype).v_char16, check)) + return; + if (IS_LONG (type->next) && printIvalChar32 (sym, type, ilist, oBuf, SPEC_CVAL (sym->etype).v_char32, check)) + return; + } + } + /* char *p = {"abc"}; */ + if ((IS_CHAR (type->next) || IS_INT (type->next)) && ilist->type == INIT_DEEP && ilist->init.deep && ilist->init.deep->type == INIT_NODE) + { + val = list2val (ilist->init.deep, TRUE); + if (!val) + { + werrorfl (ilist->init.deep->filename, ilist->init.deep->lineno, E_INIT_STRUCT, sym->name); + return; + } + if (!IS_LITERAL (val->etype)) + { + werrorfl (ilist->init.deep->filename, ilist->init.deep->lineno, E_CONST_EXPECTED); + return; + } + if (IS_CHAR (type->next) && printIvalChar (sym, type, ilist->init.deep, oBuf, SPEC_CVAL (sym->etype).v_char, check)) + return; + if (IS_INT (type->next) && IS_UNSIGNED (type->next)) + { + if (!IS_LONG (type->next) && printIvalChar16 (sym, type, ilist->init.deep, oBuf, SPEC_CVAL (sym->etype).v_char16, check)) + return; + if (IS_LONG (type->next) && printIvalChar32 (sym, type, ilist->init.deep, oBuf, SPEC_CVAL (sym->etype).v_char32, check)) + return; + } + } + + /* not the special case */ + if (ilist->type != INIT_DEEP) + { + werrorfl (ilist->filename, ilist->lineno, E_INIT_STRUCT, sym->name); + return; + } + + for (iloop = ilist->init.deep; iloop; iloop = iloop->next) + { + if ((++size > DCL_ELEM (type)) && DCL_ELEM (type)) + { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "array", sym->name); + break; + } + printIval (sym, type->next, iloop, oBuf, TRUE); + } + } + + if (DCL_ELEM (type)) + { + // pad with zeros if needed + if (size < DCL_ELEM (type)) + { + size = (DCL_ELEM (type) - size) * getSize (type->next); + while (size--) + { + dbuf_tprintf (oBuf, "\t!db !constbyte\n", 0); + } + } + } + else + { + /* we have not been given a size, but now we know it */ + /* but first check, if it's a flexible array */ + if (IS_STRUCT (sym->type)) + sym->flexArrayLength = size * getSize (type->next); + else + DCL_ELEM (type) = size; + } + + return; +} + +/*-----------------------------------------------------------------*/ +/* printIvalFuncPtr - generate initial value for function pointers */ +/*-----------------------------------------------------------------*/ +void +printIvalFuncPtr (sym_link * type, initList * ilist, struct dbuf_s *oBuf) +{ + value *val; + char *name; + int size; + + if (ilist) + val = list2val (ilist, TRUE); + else + val = valCastLiteral (type, 0.0, 0); + + if (!val) + { + // an error has been thrown already + val = constCharVal (0); + } + + if (IS_LITERAL (val->etype)) + { + if (compareType (type, val->type) == 0) + { + if (ilist) + werrorfl (ilist->filename, ilist->lineno, E_INCOMPAT_TYPES); + else + werror (E_INCOMPAT_TYPES); + printFromToType (val->type, type); + } + printIvalCharPtr (NULL, type, val, oBuf); + return; + } + + /* now generate the name */ + if (!val->sym) + name = val->name; + else + name = val->sym->rname; + + size = getSize (type); + + if (size == FUNCPTRSIZE) + { + if (TARGET_PDK_LIKE) + { + dbuf_printf (oBuf, "\tret #<%s\n", name); + dbuf_printf (oBuf, "\tret #>%s\n", name); + } + else if (TARGET_IS_STM8 && FUNCPTRSIZE == 3) + { + _printPointerType (oBuf, name, size); + dbuf_printf (oBuf, "\n"); + } + else if (port->use_dw_for_init) + { + dbuf_tprintf (oBuf, "\t!dws\n", name); + } + else + { + printPointerType (oBuf, name); + } + } + else if (size == BFUNCPTRSIZE) + { + _printPointerType (oBuf, name, size); + dbuf_printf (oBuf, "\n"); + } + else + { + wassertl (0, "Invalid function pointer size."); + } + + return; +} + +/*--------------------------------------------------------------------*/ +/* printIvalCharPtr - generates initial values for character pointers */ +/*--------------------------------------------------------------------*/ +int +printIvalCharPtr (symbol * sym, sym_link * type, value * val, struct dbuf_s *oBuf) +{ + int size = 0; + char *p; + + if (val && !!(p = (char *) malloc (strlen (val->name) + 1))) + { + strcpy (p, val->name); + addSet (&ccpStr, p); + } + + /* PENDING: this is _very_ mcs51 specific, including a magic + number... + It's also endian specific. + */ + size = getSize (type); + + if (val->name && strlen (val->name)) + { + if (size == 1) /* This appears to be Z80 specific?? */ + { + dbuf_tprintf (oBuf, "\t!dbs\n", val->name); + } + else if (size == FARPTRSIZE) + { + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) + { + dbuf_printf (oBuf, "\tret #<%s\n", val->name); + dbuf_printf (oBuf, IN_CODESPACE (SPEC_OCLS (val->etype)) ? "\tret #>(%s + 0x8000)\n" : "\tret #0\n", val->name); + } + else if (port->use_dw_for_init) + dbuf_tprintf (oBuf, "\t!dws\n", val->name); + else + printPointerType (oBuf, val->name); + } + else if (size == GPTRSIZE) + { + int type; + if (IS_PTR (val->type)) + { + type = DCL_TYPE (val->type); + } + else + { + type = PTR_TYPE (SPEC_OCLS (val->etype)); + } + if (val->sym && val->sym->isstrlit) + { + // this is a literal string + type = CPOINTER; + } + printGPointerType (oBuf, val->name, sym->name, type); + } + else + { + fprintf (stderr, "*** internal error: unknown size in " "printIvalCharPtr.\n"); + } + } + else + { + // these are literals assigned to pointers + switch (size) + { + case 1: + dbuf_tprintf (oBuf, "\t!dbs\n", aopLiteral (val, 0)); + break; + case 2: + if (TARGET_PDK_LIKE && !TARGET_IS_PDK16) + { + dbuf_tprintf (oBuf, "\tret %s\n", aopLiteral (val, 0)); + dbuf_tprintf (oBuf, "\tret %s\n", aopLiteral (val, 1)); + } + else if (port->use_dw_for_init) + dbuf_tprintf (oBuf, "\t!dws\n", aopLiteralLong (val, 0, size)); + else if (port->little_endian) + dbuf_tprintf (oBuf, "\t.byte %s,%s\n", aopLiteral (val, 0), aopLiteral (val, 1)); + else + dbuf_tprintf (oBuf, "\t.byte %s,%s\n", aopLiteral (val, 1), aopLiteral (val, 0)); + break; + case 3: + if (IS_GENPTR (type) && GPTRSIZE > FARPTRSIZE && floatFromVal (val) != 0) + { + if (!IS_PTR (val->type) && !IS_FUNC (val->type)) + { + // non-zero mcs51 generic pointer + werrorfl (sym->fileDef, sym->lineDef, W_LITERAL_GENERIC); + } + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s,%s,%s\n", aopLiteral (val, 0), aopLiteral (val, 1), aopLiteralGptr (sym->name, val)); + else + dbuf_printf (oBuf, "\t.byte %s,%s,%s\n", aopLiteralGptr (sym->name, val), aopLiteral (val, 1), aopLiteral (val, 0)); + } + else + { + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s,%s,%s\n", aopLiteral (val, 0), aopLiteral (val, 1), aopLiteral (val, 2)); + else + dbuf_printf (oBuf, "\t.byte %s,%s,%s\n", aopLiteral (val, 2), aopLiteral (val, 1), aopLiteral (val, 0)); + } + break; + case 4: + if (IS_GENPTR (type) && GPTRSIZE > FARPTRSIZE && floatFromVal (val) != 0) + { + if (!IS_PTR (val->type) && !IS_FUNC (val->type)) + { + // non-zero ds390 generic pointer + werrorfl (sym->fileDef, sym->lineDef, W_LITERAL_GENERIC); + } + if (port->little_endian) + { + dbuf_printf (oBuf, "\t.byte %s,%s,%s", aopLiteral (val, 0), aopLiteral (val, 1), aopLiteral (val, 2)); + if (IS_PTR (val->type) && !IS_GENPTR (val->type)) + dbuf_tprintf (oBuf, ",!immedbyte\n", pointerTypeToGPByte (DCL_TYPE (val->type), val->name, sym->name)); + else + dbuf_printf (oBuf, ",%s\n", aopLiteral (val, 3)); + } + else + { + if (IS_PTR (val->type) && !IS_GENPTR (val->type)) + dbuf_tprintf (oBuf, "\t.byte !immedbyte\n", pointerTypeToGPByte (DCL_TYPE (val->type), val->name, sym->name)); + else + dbuf_printf (oBuf, "\t.byte %s\n", aopLiteral (val, 3)); + dbuf_printf (oBuf, ",%s,%s,%s", aopLiteral (val, 2), aopLiteral (val, 1), aopLiteral (val, 0)); + } + } + else + { + if (port->little_endian) + { + dbuf_printf (oBuf, "\t.byte %s,%s,%s,%s\n", + aopLiteral (val, 0), aopLiteral (val, 1), aopLiteral (val, 2), aopLiteral (val, 3)); + } + else + { + dbuf_printf (oBuf, "\t.byte %s,%s,%s,%s\n", + aopLiteral (val, 3), aopLiteral (val, 2), aopLiteral (val, 1), aopLiteral (val, 0)); + } + } + break; + default: + assert (0); + } + } + + if (!noInit && val->sym && val->sym->isstrlit && !isinSet (statsg->syms, val->sym)) + { + addSet (&statsg->syms, val->sym); + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* printIvalPtr - generates initial value for pointers */ +/*-----------------------------------------------------------------*/ +void +printIvalPtr (symbol *sym, sym_link *type, initList *ilist, struct dbuf_s *oBuf) +{ + value *val; + int size; + + /* if deep then */ + if (ilist && (ilist->type == INIT_DEEP)) + ilist = ilist->init.deep; + + /* function pointer */ + if (IS_FUNC (type->next)) + { + printIvalFuncPtr (type, ilist, oBuf); + return; + } + + if (!(val = initPointer (ilist, type, 1))) + return; + + /* if character pointer */ + if (IS_CHAR (type->next) || IS_INT (type->next) && IS_UNSIGNED (type->next)) + if (printIvalCharPtr (sym, type, val, oBuf)) + return; + + /* check the type */ + if (compareType (type, val->type) == 0) + { + assert (ilist != NULL); + werrorfl (ilist->filename, ilist->lineno, W_INIT_WRONG); + printFromToType (val->type, type); + } + + /* if val is literal */ + if (IS_LITERAL (val->etype)) + { + switch (getSize (type)) + { + case 1: + dbuf_tprintf (oBuf, "\t!db !constbyte\n", (unsigned int) ulFromVal (val) & 0xff); + break; + case 2: + if (port->use_dw_for_init) + dbuf_tprintf (oBuf, "\t!dws\n", aopLiteralLong (val, 0, 2)); + else if (port->little_endian) + dbuf_tprintf (oBuf, "\t.byte %s,%s\n", aopLiteral (val, 0), aopLiteral (val, 1)); + else + dbuf_tprintf (oBuf, "\t.byte %s,%s\n", aopLiteral (val, 1), aopLiteral (val, 0)); + break; + case 3: + dbuf_printf (oBuf, "; generic printIvalPtr\n"); + if (port->little_endian) + dbuf_printf (oBuf, "\t.byte %s,%s", aopLiteral (val, 0), aopLiteral (val, 1)); + else + dbuf_printf (oBuf, "\t.byte %s,%s", aopLiteral (val, 1), aopLiteral (val, 0)); + if (IS_GENPTR (val->type)) + dbuf_printf (oBuf, ",%s\n", aopLiteral (val, 2)); + else if (IS_PTR (val->type)) + dbuf_tprintf (oBuf, ",!immedbyte\n", pointerTypeToGPByte (DCL_TYPE (val->type), val->name, sym->name)); + else + dbuf_printf (oBuf, ",%s\n", aopLiteral (val, 2)); + break; + case 4: + if (TARGET_IS_DS390 || TARGET_IS_DS400) + dbuf_printf (oBuf, "\t.byte %s,%s,%s,%s\n", aopLiteral (val, 0), aopLiteral (val, 1), aopLiteral (val, 2), aopLiteral (val, 3)); + else + wassertl(0, "Printing pointer of invalid size"); + break; + default: + wassertl(0, "Printing pointer of invalid size"); + break; + } + return; + } + + size = getSize (type); + + if (TARGET_PDK_LIKE && size == 2) + { + if (IN_CODESPACE (SPEC_OCLS (val->etype))) + { + dbuf_printf (oBuf, "\tret #<%s\n", val->name); + dbuf_printf (oBuf, "\tret #>(%s + 0x8000)\n", val->name); + } + else + { + dbuf_printf (oBuf, "\tret #%s\n", val->name); + dbuf_printf (oBuf, "\tret #0\n"); + } + } + else if (size == 1) /* Z80 specific?? */ + { + dbuf_tprintf (oBuf, "\t!dbs\n", val->name); + } + else if (size == FARPTRSIZE) + { + if (port->use_dw_for_init) + dbuf_tprintf (oBuf, "\t!dws\n", val->name); + else + printPointerType (oBuf, val->name); + } + else if (size == GPTRSIZE) + { + printGPointerType (oBuf, val->name, sym->name, + (IS_PTR (val->type) ? DCL_TYPE (val->type) : PTR_TYPE (SPEC_OCLS (val->etype)))); + } + + return; +} + +/*-----------------------------------------------------------------*/ +/* printIval - generates code for initial value */ +/*-----------------------------------------------------------------*/ +void +printIval (symbol * sym, sym_link * type, initList * ilist, struct dbuf_s *oBuf, bool check) +{ + sym_link *itype; + + /* Handle designated initializers */ + if (ilist && ilist->type==INIT_DEEP) + ilist = reorderIlist (type, ilist); + + /* If this is a hole, substitute an appropriate initializer. */ + if (ilist && ilist->type == INIT_HOLE) + { + if (IS_AGGREGATE (type)) + { + ilist = newiList(INIT_DEEP, NULL); /* init w/ {} */ + } + else + { + ast *ast = newAst_VALUE (constVal("0")); + ast = decorateType (ast, RESULT_TYPE_NONE); + ilist = newiList(INIT_NODE, ast); + } + } + + /* if structure then */ + if (IS_STRUCT (type)) + { + printIvalStruct (sym, type, ilist, oBuf); + return; + } + + /* if this is an array */ + if (IS_ARRAY (type)) + { + printIvalArray (sym, type, ilist, oBuf, check); + return; + } + + if (ilist) + { + // not an aggregate, ilist must be a node + if (ilist->type != INIT_NODE) + { + // or a 1-element list + if (ilist->init.deep->next) + { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", sym->name); + } + else + { + ilist = ilist->init.deep; + } + } + + // and the type must match + itype = ilist->init.node->ftype; + + if (compareType (type, itype) == 0) + { + // special case for literal strings + if (IS_ARRAY (itype) && IS_CHAR (getSpec (itype)) && + // which are really code pointers + IS_CODEPTR (type)) + { + // no sweat + } + else if (IS_CODEPTR (type) && IS_FUNC (type->next)) /* function pointer */ + { + if (ilist) + werrorfl (ilist->filename, ilist->lineno, E_INCOMPAT_TYPES); + else + werror (E_INCOMPAT_TYPES); + printFromToType (itype, type->next); + } + else + { + werrorfl (ilist->filename, ilist->lineno, E_TYPE_MISMATCH, "assignment", " "); + printFromToType (itype, type); + } + } + } + + /* if this is a pointer */ + if (IS_PTR (type)) + { + printIvalPtr (sym, type, ilist, oBuf); + return; + } + + /* if type is SPECIFIER */ + if (IS_SPEC (type)) + { + printIvalType (sym, type, ilist, oBuf); + return; + } +} + +/*-----------------------------------------------------------------*/ +/* emitStaticSeg - emitcode for the static segment */ +/*-----------------------------------------------------------------*/ +void +emitStaticSeg (memmap *map, struct dbuf_s *oBuf) +{ + symbol *sym; + set *tmpSet = NULL; + + /* fprintf(out, "\t.area\t%s\n", map->sname); */ + + /* eliminate redundant __str_%d (generated in stringToSymbol(), SDCCast.c) */ + for (sym = setFirstItem (map->syms); sym; sym = setNextItem (map->syms)) + addSet (&tmpSet, sym); + + /* for all variables in this segment do */ + for (sym = setFirstItem (map->syms); sym; sym = setNextItem (map->syms)) + { + /* if it is "extern" then do nothing */ + if (IS_EXTERN (sym->etype) && !sym->ival) + continue; + + /* eliminate redundant __str_%d (generated in stringToSymbol(), SDCCast.c) */ + if (!isinSet (tmpSet, sym)) + { + const char *p; + if (!ccpStr) + continue; + for (p = setFirstItem (ccpStr); p; p = setNextItem (ccpStr)) + if (strcmp (p, sym->name) == 0) + break; + if (!p) + continue; + } + + /* if it is not static add it to the public table */ + if (!IS_STATIC (sym->etype)) + { + addSetHead (&publics, sym); + } + + /* if it has an absolute address and no initializer */ + if (SPEC_ABSA (sym->etype) && !sym->ival) + { + if (options.debug) + { + emitDebugSym (oBuf, sym); + dbuf_printf (oBuf, " == 0x%04x\n", SPEC_ADDR (sym->etype)); + } + dbuf_printf (oBuf, "%s\t=\t0x%04x\n", sym->rname, SPEC_ADDR (sym->etype)); + } + else + { + int size = getSize (sym->type); + + if (size == 0) + { + werrorfl (sym->fileDef, sym->lineDef, E_UNKNOWN_SIZE, sym->name); + } + /* if it has an initial value */ + if (sym->ival) + { + if (SPEC_ABSA (sym->etype)) + { + dbuf_tprintf (oBuf, "\t!org\n", SPEC_ADDR (sym->etype)); + } + if (options.debug) + { + emitDebugSym (oBuf, sym); + dbuf_printf (oBuf, " == .\n"); + } + dbuf_printf (oBuf, "%s:\n", sym->rname); + ++noAlloc; + resolveIvalSym (sym->ival, sym->type); + printIval (sym, sym->type, sym->ival, oBuf, (map != xinit && map != initializer)); + --noAlloc; + /* if sym is a simple string and sym->ival is a string, + WE don't need it anymore */ + if (IS_ARRAY (sym->type) && IS_CHAR (sym->type->next) && + IS_AST_SYM_VALUE (list2expr (sym->ival)) && list2val (sym->ival, TRUE)->sym->isstrlit) + { + freeStringSymbol (list2val (sym->ival, TRUE)->sym); + } + } + else + { + /* allocate space */ + if (options.debug) + { + emitDebugSym (oBuf, sym); + dbuf_printf (oBuf, " == .\n"); + } + /* special case for character strings */ + if (IS_ARRAY (sym->type) && + (IS_CHAR (sym->type->next) && SPEC_CVAL (sym->etype).v_char || + IS_INT (sym->type->next) && !IS_LONG (sym->type->next) && SPEC_CVAL (sym->etype).v_char16 || + IS_INT (sym->type->next) && IS_LONG (sym->type->next) && SPEC_CVAL (sym->etype).v_char32)) + { + if (options.const_seg) + dbuf_tprintf(&code->oBuf, "\t!area\n", options.const_seg); + dbuf_printf (oBuf, "%s:\n", sym->rname); + if (IS_CHAR (sym->type->next)) + printChar (oBuf, SPEC_CVAL (sym->etype).v_char, size); + else if (IS_INT (sym->type->next) && !IS_LONG (sym->type->next)) + printChar16 (oBuf, SPEC_CVAL (sym->etype).v_char16, size / 2); + else if (IS_INT (sym->type->next) && IS_LONG (sym->type->next)) + printChar32 (oBuf, SPEC_CVAL (sym->etype).v_char32, size / 4); + else + wassert (0); + if (options.const_seg) + dbuf_tprintf(oBuf, "\t!areacode\n", options.code_seg); + } + else + { + dbuf_printf (oBuf, "%s:\n", sym->rname); + dbuf_tprintf (oBuf, "\t!ds\n", (unsigned int) size & 0xffff); + } + } + } + } + + if (tmpSet) + deleteSet (&tmpSet); + if (ccpStr) + { + char *p; + for (p = setFirstItem (ccpStr); p; p = setNextItem (ccpStr)) + if (p) + free (p); + deleteSet (&ccpStr); + } +} + +/*-----------------------------------------------------------------*/ +/* emitMaps - emits the code for the data portion the code */ +/*-----------------------------------------------------------------*/ +void +emitMaps (void) +{ + namedspacemap *nm; + int publicsfr = TARGET_IS_MCS51 || TARGET_PDK_LIKE; /* Ideally, this should be true for all */ + /* ports but let's be conservative - EEP */ + + inInitMode++; + /* no special considerations for the following + data, idata & bit & xdata */ + emitRegularMap (data, TRUE, TRUE); + emitRegularMap (initialized, TRUE, TRUE); + for (nm = namedspacemaps; nm; nm = nm->next) + if (nm->is_const) + { + dbuf_tprintf (&nm->map->oBuf, "\t!areacode\n", nm->map->sname); + emitStaticSeg (nm->map, &nm->map->oBuf); + } + else + emitRegularMap (nm->map, TRUE, TRUE); + emitRegularMap (idata, TRUE, TRUE); + emitRegularMap (d_abs, TRUE, TRUE); + emitRegularMap (i_abs, TRUE, TRUE); + emitRegularMap (bit, TRUE, TRUE); + emitRegularMap (pdata, TRUE, TRUE); + emitRegularMap (xdata, TRUE, TRUE); + emitRegularMap (x_abs, TRUE, TRUE); + if (port->genXINIT) + { + emitRegularMap (xidata, TRUE, TRUE); + } + emitRegularMap (sfr, publicsfr, FALSE); + emitRegularMap (sfrbit, publicsfr, FALSE); + emitRegularMap (home, TRUE, FALSE); + emitRegularMap (code, TRUE, FALSE); + + if (options.const_seg) + dbuf_tprintf (&code->oBuf, "\t!area\n", options.const_seg); + emitStaticSeg (statsg, &code->oBuf); + + if (port->genXINIT) + { + dbuf_tprintf (&code->oBuf, "\t!area\n", xinit->sname); + emitStaticSeg (xinit, &code->oBuf); + } + if (initializer) + { + dbuf_tprintf (&code->oBuf, "\t!area\n", initializer->sname); + emitStaticSeg (initializer, &code->oBuf); + } + dbuf_tprintf (&code->oBuf, "\t!area\n", c_abs->sname); + emitStaticSeg (c_abs, &code->oBuf); + inInitMode--; +} + +/*-----------------------------------------------------------------*/ +/* flushStatics - flush all currently defined statics out to file */ +/* and delete. Temporary function */ +/*-----------------------------------------------------------------*/ +void +flushStatics (void) +{ + emitStaticSeg (statsg, codeOutBuf); + statsg->syms = NULL; +} + +/*-----------------------------------------------------------------*/ +/* createInterruptVect - creates the interrupt vector */ +/*-----------------------------------------------------------------*/ +void +createInterruptVect (struct dbuf_s *vBuf) +{ + mainf = newSymbol ("main", 0); + mainf->block = 0; + + /* only if the main function exists */ + if (!(mainf = findSymWithLevel (SymbolTab, mainf))) + { + if (!options.cc_only && !noAssemble && !options.c1mode) + werror (E_NO_MAIN); + return; + } + + /* if the main is only a prototype ie. no body then do nothing */ + if (!IFFUNC_HASBODY (mainf->type)) + { + /* if ! compile only then main function should be present */ + if (!options.cc_only && !noAssemble) + werror (E_NO_MAIN); + return; + } + + dbuf_tprintf (vBuf, "\t!areacode\n", HOME_NAME); + dbuf_printf (vBuf, "__interrupt_vect:\n"); + + + if (!port->genIVT || !(port->genIVT (vBuf, interrupts, maxInterrupts))) + { + /* There's no such thing as a "generic" interrupt table header. */ + wassert (0); + } +} + +char *iComments1 = { + ";--------------------------------------------------------\n" + "; File Created by SDCC : free open source ANSI-C Compiler\n" +}; + +char *iComments2 = { + ";--------------------------------------------------------\n" +}; + + +/*-----------------------------------------------------------------*/ +/* initialComments - puts in some initial comments */ +/*-----------------------------------------------------------------*/ +void +initialComments (FILE * afile) +{ + fprintf (afile, "%s", iComments1); + fprintf (afile, "; Version " SDCC_VERSION_STR " #%s (%s)\n", getBuildNumber (), getBuildEnvironment ()); + fprintf (afile, "%s", iComments2); +} + +/*-----------------------------------------------------------------*/ +/* printPublics - generates .global for publics */ +/*-----------------------------------------------------------------*/ +void +printPublics (FILE * afile) +{ + symbol *sym; + + fprintf (afile, "%s", iComments2); + fprintf (afile, "; Public variables in this module\n"); + fprintf (afile, "%s", iComments2); + + for (sym = setFirstItem (publics); sym; sym = setNextItem (publics)) + tfprintf (afile, "\t!global\n", sym->rname); +} + +/*-----------------------------------------------------------------*/ +/* printExterns - generates .global for externs */ +/*-----------------------------------------------------------------*/ +void +printExterns (FILE * afile) +{ + symbol *sym; + + fprintf (afile, "%s", iComments2); + fprintf (afile, "; Externals used\n"); + fprintf (afile, "%s", iComments2); + + for (sym = setFirstItem (externs); sym; sym = setNextItem (externs)) + tfprintf (afile, "\t!extern\n", sym->rname); +} + +/*-----------------------------------------------------------------*/ +/* emitOverlay - will emit code for the overlay stuff */ +/*-----------------------------------------------------------------*/ +static void +emitOverlay (struct dbuf_s *aBuf) +{ + set *ovrset; + + /* for each of the sets in the overlay segment do */ + for (ovrset = setFirstItem (ovrSetSets); ovrset; ovrset = setNextItem (ovrSetSets)) + { + symbol *sym; + + if (elementsInSet (ovrset)) + { + /* output the area information */ + dbuf_printf (aBuf, "\t.area\t%s\n", port->mem.overlay_name); /* MOF */ + } + + for (sym = setFirstItem (ovrset); sym; sym = setNextItem (ovrset)) + { + /* if extern then it is in the publics table: do nothing */ + if (IS_EXTERN (sym->etype)) + continue; + + /* if allocation required check is needed + then check if the symbol really requires + allocation only for local variables */ + if (!IS_AGGREGATE (sym->type) && !(sym->_isparm && !IS_REGPARM (sym->etype)) && !sym->allocreq && sym->level) + continue; + + /* if global variable & not static or extern + and addPublics allowed then add it to the public set */ + if ((sym->_isparm && !IS_REGPARM (sym->etype)) && !IS_STATIC (sym->etype) && !IS_STATIC (sym->localof->etype)) + { + addSetHead (&publics, sym); + } + + /* if extern then do nothing or is a function + then do nothing */ + if (IS_FUNC (sym->type)) + continue; + + /* if is has an absolute address then generate + an equate for this no need to allocate space */ + if (SPEC_ABSA (sym->etype)) + { + /* print extra debug info if required */ + if (options.debug) + { + emitDebugSym (aBuf, sym); + dbuf_printf (aBuf, " == 0x%04x\n", SPEC_ADDR (sym->etype)); + } + dbuf_printf (aBuf, "%s\t=\t0x%04x\n", sym->rname, SPEC_ADDR (sym->etype)); + } + else + { + int size = getSize (sym->type); + + if (size == 0) + { + werrorfl (sym->fileDef, sym->lineDef, E_UNKNOWN_SIZE, sym->name); + } + /* print extra debug info if required */ + if (options.debug) + { + emitDebugSym (aBuf, sym); + dbuf_printf (aBuf, "==.\n"); + } + + /* allocate space */ + dbuf_tprintf (aBuf, "!slabeldef\n", sym->rname); + dbuf_tprintf (aBuf, "\t!ds\n", (unsigned int) getSize (sym->type) & 0xffff); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* glue - the final glue that hold the whole thing together */ +/*-----------------------------------------------------------------*/ +void +glue (void) +{ + struct dbuf_s vBuf; + struct dbuf_s ovrBuf; + struct dbuf_s asmFileName; + FILE *asmFile; + int mcs51_like; + namedspacemap *nm; + + dbuf_init (&vBuf, 4096); + dbuf_init (&ovrBuf, 4096); + + mcs51_like = (port->general.glue_up_main && + (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400)); + + /* print the global struct definitions */ + if (options.debug) + cdbStructBlock (0); + + /* PENDING: this isn't the best place but it will do */ + if (port->general.glue_up_main) + { + /* create the interrupt vector table */ + createInterruptVect (&vBuf); + } + + /* emit code for the all the variables declared */ + emitMaps (); + /* do the overlay segments */ + emitOverlay (&ovrBuf); + + outputDebugSymbols (); + + /* now put it all together into the assembler file */ + /* create the assembler file name */ + + /* -o option overrides default name? */ + dbuf_init (&asmFileName, PATH_MAX); + if ((noAssemble || options.c1mode) && fullDstFileName) + { + dbuf_append_str (&asmFileName, fullDstFileName); + } + else + { + dbuf_append_str (&asmFileName, dstFileName); + dbuf_append_str (&asmFileName, port->assembler.file_ext); + } + + if (!(asmFile = fopen (dbuf_c_str (&asmFileName), "w"))) + { + werror (E_FILE_OPEN_ERR, dbuf_c_str (&asmFileName)); + dbuf_destroy (&asmFileName); + exit (EXIT_FAILURE); + } + dbuf_destroy (&asmFileName); + + /* initial comments */ + initialComments (asmFile); + + if (TARGET_IS_S08) + fprintf (asmFile, "\t.cs08\n"); + else if (TARGET_IS_Z180) + fprintf (asmFile, "\t.hd64\n"); + else if (TARGET_IS_R3KA) + fprintf (asmFile, "\t.r3k\n"); + else if (TARGET_IS_EZ80_Z80) + fprintf (asmFile, "\t.ez80\n"); + + /* print module name */ + tfprintf (asmFile, "\t!module\n", moduleName); + if (mcs51_like) + { + if(!options.noOptsdccInAsm) + fprintf (asmFile, "\t.optsdcc -m%s", port->target); + + switch (options.model) + { + case MODEL_SMALL: + fprintf (asmFile, " --model-small"); + break; + case MODEL_COMPACT: + fprintf (asmFile, " --model-compact"); + break; + case MODEL_MEDIUM: + fprintf (asmFile, " --model-medium"); + break; + case MODEL_LARGE: + fprintf (asmFile, " --model-large"); + break; + case MODEL_FLAT24: + fprintf (asmFile, " --model-flat24"); + break; + case MODEL_HUGE: + fprintf (asmFile, " --model-huge"); + break; + default: + break; + } + /*if(options.stackAuto) fprintf (asmFile, " --stack-auto"); */ + if (options.useXstack) + fprintf (asmFile, " --xstack"); + /*if(options.intlong_rent) fprintf (asmFile, " --int-long-rent"); */ + /*if(options.float_rent) fprintf (asmFile, " --float-rent"); */ + if (options.noRegParams) + fprintf (asmFile, " --no-reg-params"); + if (options.parms_in_bank1) + fprintf (asmFile, " --parms-in-bank1"); + if (options.all_callee_saves) + fprintf (asmFile, " --all-callee-saves"); + fprintf (asmFile, "\n"); + } + else if (!TARGET_PIC_LIKE && !options.noOptsdccInAsm) + { + fprintf (asmFile, "\t.optsdcc -m%s\n", port->target); + } + + tfprintf (asmFile, "\t!fileprelude\n"); + + /* Let the port generate any global directives, etc. */ + if (port->genAssemblerPreamble) + { + port->genAssemblerPreamble (asmFile); + } + + /* print the global variables in this module */ + printPublics (asmFile); + if (port->assembler.externGlobal) + printExterns (asmFile); + + if ((mcs51_like) || (TARGET_IS_Z80 || TARGET_IS_GBZ80 || TARGET_IS_Z180 || TARGET_IS_RABBIT || TARGET_IS_EZ80_Z80) || TARGET_PDK_LIKE) /*.p.t.20030924 need to output SFR table for Z80 as well */ + { + /* copy the sfr segment */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; special function registers\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&sfr->oBuf, asmFile); + } + + if (mcs51_like) + { + /* copy the sbit segment */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; special function bits\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&sfrbit->oBuf, asmFile); + + /*JCF: Create the areas for the register banks */ + if (RegBankUsed[0] || RegBankUsed[1] || RegBankUsed[2] || RegBankUsed[3]) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; overlayable register banks\n"); + fprintf (asmFile, "%s", iComments2); + if (RegBankUsed[0]) + fprintf (asmFile, "\t.area REG_BANK_0\t(REL,OVR,DATA)\n\t.ds 8\n"); + if (RegBankUsed[1] || options.parms_in_bank1) + fprintf (asmFile, "\t.area REG_BANK_1\t(REL,OVR,DATA)\n\t.ds 8\n"); + if (RegBankUsed[2]) + fprintf (asmFile, "\t.area REG_BANK_2\t(REL,OVR,DATA)\n\t.ds 8\n"); + if (RegBankUsed[3]) + fprintf (asmFile, "\t.area REG_BANK_3\t(REL,OVR,DATA)\n\t.ds 8\n"); + } + if (BitBankUsed) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; overlayable bit register bank\n"); + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "\t.area BIT_BANK\t(REL,OVR,DATA)\n"); + fprintf (asmFile, "bits:\n\t.ds 1\n"); + fprintf (asmFile, "\tb0 = bits[0]\n"); + fprintf (asmFile, "\tb1 = bits[1]\n"); + fprintf (asmFile, "\tb2 = bits[2]\n"); + fprintf (asmFile, "\tb3 = bits[3]\n"); + fprintf (asmFile, "\tb4 = bits[4]\n"); + fprintf (asmFile, "\tb5 = bits[5]\n"); + fprintf (asmFile, "\tb6 = bits[6]\n"); + fprintf (asmFile, "\tb7 = bits[7]\n"); + } + } + + /* copy the data segment */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, ";%s ram data\n", mcs51_like ? " internal" : ""); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&data->oBuf, asmFile); + + /* copy the initialized segment */ + if (initialized) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, ";%s ram data\n", mcs51_like ? " internal" : ""); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&initialized->oBuf, asmFile); + } + + /* copy segments for named address spaces */ + for (nm = namedspacemaps; nm; nm = nm->next) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; %s %s data\n", nm->name, nm->is_const ? "rom" : "ram"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&nm->map->oBuf, asmFile); + } + + /* create the overlay segments */ + if (overlay) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; overlayable items in%s ram \n", mcs51_like ? " internal" : ""); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&ovrBuf, asmFile); + } + + /* create the stack segment MOF */ + if (mainf && IFFUNC_HASBODY (mainf->type)) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; Stack segment in internal ram \n"); + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "\t.area\tSSEG\n" "__start__stack:\n\t.ds\t1\n\n"); + } + + /* create the idata segment */ + if (idata) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; indirectly addressable internal ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&idata->oBuf, asmFile); + } + + /* create the absolute idata/data segment */ + if (d_abs || i_abs) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; absolute%s ram data\n", mcs51_like ? " internal" : ""); + fprintf (asmFile, "%s", iComments2); + if (d_abs) + dbuf_write_and_destroy (&d_abs->oBuf, asmFile); + if (i_abs) + dbuf_write_and_destroy (&i_abs->oBuf, asmFile); + } + + /* copy the bit segment */ + if (bit) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; bit data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&bit->oBuf, asmFile); + } + + /* copy paged external ram data */ + if (pdata) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; paged external ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&pdata->oBuf, asmFile); + } + + /* if external stack then reserve space for it */ + if (mainf && IFFUNC_HASBODY (mainf->type) && options.useXstack) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; external stack \n"); + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "\t.area XSTK (PAG,XDATA)\n" "__start__xstack:\n\t.ds\t1\n\n"); + } + + /* copy external ram data */ + if (xdata && mcs51_like) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; external ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&xdata->oBuf, asmFile); + } + + /* create the absolute xdata segment */ + if (x_abs) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; absolute external ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&x_abs->oBuf, asmFile); + } + + /* copy external initialized ram data */ + if (xidata) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; external initialized ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&xidata->oBuf, asmFile); + } + + /* If the port wants to generate any extra areas, let it do so. */ + if (port->extraAreas.genExtraAreaDeclaration) + { + port->extraAreas.genExtraAreaDeclaration (asmFile, mainf && IFFUNC_HASBODY (mainf->type)); + } + + /* copy the interrupt vector table */ + if (mainf && IFFUNC_HASBODY (mainf->type)) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; interrupt vector \n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&vBuf, asmFile); + } + + /* copy global & static initialisations */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; global & static initialisations\n"); + fprintf (asmFile, "%s", iComments2); + + /* Everywhere we generate a reference to the static_name area, + * (which is currently only here), we immediately follow it with a + * definition of the post_static_name area. This guarantees that + * the post_static_name area will immediately follow the static_name + * area. + */ + tfprintf (asmFile, "\t!area\n", port->mem.home_name); + tfprintf (asmFile, "\t!area\n", port->mem.static_name); /* MOF */ + tfprintf (asmFile, "\t!area\n", port->mem.post_static_name); + tfprintf (asmFile, "\t!area\n", port->mem.static_name); + + if (mainf && IFFUNC_HASBODY (mainf->type)) + { + if (port->genInitStartup) + { + port->genInitStartup (asmFile); + } + else + { + assert (0); + } + } + dbuf_write_and_destroy (&statsg->oBuf, asmFile); + + /* STM8 / PDK14 note: there are no such instructions supported. + Also, we don't need this logic as well. */ + if (port->general.glue_up_main && mainf && IFFUNC_HASBODY (mainf->type)) + { + /* This code is generated in the post-static area. + * This area is guaranteed to follow the static area + * by the ugly shucking and jiving about 20 lines ago. + */ + tfprintf (asmFile, "\t!area\n", port->mem.post_static_name); + if(TARGET_IS_STM8) + fprintf (asmFile, "\tjp\t__sdcc_program_startup\n"); + else if(TARGET_PDK_LIKE) + fprintf (asmFile, "\tgoto\t__sdcc_program_startup\n"); + else + fprintf (asmFile, "\t%cjmp\t__sdcc_program_startup\n", options.acall_ajmp ? 'a' : 'l'); + } + + fprintf (asmFile, "%s" "; Home\n" "%s", iComments2, iComments2); + tfprintf (asmFile, "\t!areahome\n", HOME_NAME); + dbuf_write_and_destroy (&home->oBuf, asmFile); + + if (mainf && IFFUNC_HASBODY (mainf->type)) + { + /* STM8 note: there is no need to call main(). + Instead of that, it's address is specified in the + interrupts table and always equals to 0x8080. + */ + + /* entry point @ start of HOME */ + fprintf (asmFile, "__sdcc_program_startup:\n"); + + /* put in jump or call to main */ + if(TARGET_IS_STM8) + fprintf (asmFile, options.model == MODEL_LARGE ? "\tjpf\t_main\n" : "\tjp\t_main\n"); + else if(TARGET_PDK_LIKE) + fprintf (asmFile, "\tgoto\t_main\n"); + else + fprintf (asmFile, "\t%cjmp\t_main\n", options.acall_ajmp ? 'a' : 'l'); /* needed? */ + fprintf (asmFile, ";\treturn from main will return to caller\n"); + } + /* copy over code */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; code\n"); + fprintf (asmFile, "%s", iComments2); + tfprintf (asmFile, "\t!areacode\n", options.code_seg); + dbuf_write_and_destroy (&code->oBuf, asmFile); + + if (port->genAssemblerEnd) + { + port->genAssemblerEnd (asmFile); + } + fclose (asmFile); +} + +/* will return 1 if the string is a part + of a target specific keyword */ +int +isTargetKeyword (const char *s) +{ + int i; + + if (port->keywords == NULL) + return 0; + + if (s[0] == '_' && s[1] == '_') + { + /* Keywords in the port's array have either 0 or 1 underscore, */ + /* so skip over the appropriate number of chars when comparing */ + for (i = 0 ; port->keywords[i] ; i++ ) + { + if (port->keywords[i][0] == '_' && + strcmp(port->keywords[i],s+1) == 0) + return 1; + else if (strcmp(port->keywords[i],s+2) == 0) + return 1; + } + } + else + { + for (i = 0 ; port->keywords[i] ; i++ ) + { + if (strcmp(port->keywords[i],s) == 0) + return 1; + } + } + + return 0; +} + diff --git a/src/SDCCglue.h b/src/SDCCglue.h new file mode 100644 index 0000000..f7755fc --- /dev/null +++ b/src/SDCCglue.h @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------- + + SDCCglue.h - glues everything we have done together into one file. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCglobl.h" +#include "SDCCmem.h" + +#ifndef SDCCGLUE_H +#define SDCCGLUE_H 1 + +void glue (void); +const char *aopLiteral (value *, int offset); +const char *aopLiteralLong (value *val, int offset, int size); +void flushStatics (void); +int printIvalCharPtr (symbol *, sym_link *, value *, struct dbuf_s *); + +extern symbol *interrupts[]; +extern set *publics; +extern set *strSym; + +int pointerTypeToGPByte (const int p_type, const char *iname, const char *oname); + +int isTargetKeyword (const char *s); + +#endif diff --git a/src/SDCChasht.c b/src/SDCChasht.c new file mode 100644 index 0000000..d3ab030 --- /dev/null +++ b/src/SDCChasht.c @@ -0,0 +1,586 @@ +/*----------------------------------------------------------------- + SDCChast.c - contains support routines for hashtables + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include +#include +#include "SDCCerr.h" +#include "SDCCglobl.h" +#include "SDCChasht.h" +#include "newalloc.h" + +#define DEFAULT_HTAB_SIZE 128 + +/*-----------------------------------------------------------------*/ +/* newHashtItem - creates a new hashtable Item */ +/*-----------------------------------------------------------------*/ +static hashtItem * +_newHashtItem (int key, void *pkey, void *item) +{ + hashtItem *htip; + + htip = Safe_alloc ( sizeof (hashtItem)); + + htip->key = key; + htip->pkey = pkey; + htip->item = item; + htip->next = NULL; + return htip; +} + +/*-----------------------------------------------------------------*/ +/* newHashTable - allocates a new hashtable of size */ +/*-----------------------------------------------------------------*/ +hTab * +newHashTable (int size) +{ + hTab *htab; + + htab = Safe_alloc ( sizeof (hTab)); + + if (!(htab->table = Safe_alloc ((size + 1) * sizeof (hashtItem *)))) + { + fprintf (stderr, "out of virtual memory %s %d\n", + __FILE__, (size + 1) * (int) sizeof (hashtItem *)); + exit (1); + } + htab->minKey = htab->size = size; + return htab; +} + + +void +hTabAddItemLong (hTab ** htab, int key, void *pkey, void *item) +{ + hashtItem *htip; + hashtItem *last; + + if (!(*htab)) + *htab = newHashTable (DEFAULT_HTAB_SIZE); + + if (key > (*htab)->size) + { + int i; + (*htab)->table = Safe_realloc ((*htab)->table, + (key * 2 + 2) * sizeof (hashtItem *)); + for (i = (*htab)->size + 1; i <= (key * 2 + 1); i++) + (*htab)->table[i] = NULL; + (*htab)->size = key * 2 + 1; + } + + /* update the key */ + if ((*htab)->maxKey < key) + (*htab)->maxKey = key; + + if ((*htab)->minKey > key) + (*htab)->minKey = key; + + /* create the item */ + htip = _newHashtItem (key, pkey, item); + + /* if there is a clash then goto end of chain */ + if ((last = (*htab)->table[key])) + { + while (last->next) + last = last->next; + last->next = htip; + } + else + /* else just add it */ + (*htab)->table[key] = htip; + (*htab)->nItems++; +} + +/*-----------------------------------------------------------------*/ +/* hTabAddItem - adds an item to the hash table */ +/*-----------------------------------------------------------------*/ +void +hTabAddItem (hTab ** htab, int key, void *item) +{ + hTabAddItemLong (htab, key, NULL, item); +} + +/*-----------------------------------------------------------------*/ +/* hTabDeleteItem - either delete an item */ +/*-----------------------------------------------------------------*/ +void +hTabDeleteItem (hTab ** htab, int key, + const void *item, DELETE_ACTION action, + int (*compareFunc) (const void *, const void *)) +{ + hashtItem *htip, **htipp; + + if (!(*htab)) + return; + + /* first check if anything exists in the slot */ + if (!(*htab)->table[key]) + return; + + /* if delete chain */ + if (action == DELETE_CHAIN) + (*htab)->table[key] = NULL; + else + { + + /* delete specific item */ + /* if a compare function is given then use the compare */ + /* function to find the item, else just compare the items */ + + htipp = &((*htab)->table[key]); + htip = (*htab)->table[key]; + for (; htip; htip = htip->next) + { + + if (compareFunc ? compareFunc (item, htip->item) : + (item == htip->item)) + { + *htipp = htip->next; + break; + } + + htipp = &(htip->next); + } + + } + + (*htab)->nItems--; + + if (!(*htab)->nItems) + { + *htab = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/* hTabDeleteAll - deletes all items in a hash table to reduce mem */ +/* leaks written by */ +/* "BESSIERE Jerome" */ +/*-----------------------------------------------------------------*/ +void +hTabDeleteAll (hTab * p) +{ + if (p && p->table) + { + register int i; + register hashtItem *jc, *jn; + for (i = 0; i < p->size; i++) + { + + if (!(jc = p->table[i])) + continue; + jn = jc->next; + while (jc) + { + Safe_free (jc); + if ((jc = jn)) + jn = jc->next; + } + p->table[i] = NULL; + } + Safe_free (p->table); + } +} + +/*-----------------------------------------------------------------*/ +/* hTabClearAll - clear all entries in the table (does not free) */ +/*-----------------------------------------------------------------*/ +void +hTabClearAll (hTab * htab) +{ + + if (!htab || !htab->table) + { + printf ("null table\n"); + return; + } + memset (htab->table, 0, htab->size * sizeof (hashtItem *)); + + htab->minKey = htab->size; + htab->currKey = htab->nItems = htab->maxKey = 0; +} + +static const hashtItem * +_findItem (hTab * htab, int key, void *item, int (*compareFunc) (void *, void *)) +{ + hashtItem *htip; + + for (htip = htab->table[key]; htip; htip = htip->next) + { + /* if a compare function is given use it */ + if (compareFunc && compareFunc (item, htip->item)) + break; + else if (item == htip->item) + break; + } + return htip; +} + +static const hashtItem * +_findByKey (hTab * htab, int key, const void *pkey, int (*compare) (const void *, const void *)) +{ + hashtItem *htip; + + assert (compare); + + if (!htab) + return NULL; + + for (htip = htab->table[key]; htip; htip = htip->next) + { + /* if a compare function is given use it */ + if (compare && compare (pkey, htip->pkey)) + { + break; + } + else + { + if (pkey == htip->pkey) + { + break; + } + } + } + return htip; +} + +void * +hTabFindByKey (hTab * h, int key, const void *pkey, int (*compare) (const void *, const void *)) +{ + const hashtItem *item; + + if ((item = _findByKey (h, key, pkey, compare))) + return item->item; + return NULL; +} + +int +hTabDeleteByKey (hTab ** h, int key, const void *pkey, int (*compare) (const void *, const void *)) +{ + hashtItem *htip, **htipp; + bool found = FALSE; + + if (!(*h)) + return 0; + + /* first check if anything exists in the slot */ + if (!(*h)->table[key]) + return 0; + + /* delete specific item */ + /* if a compare function is given then use the compare */ + /* function to find the item, else just compare the items */ + + htipp = &((*h)->table[key]); + htip = (*h)->table[key]; + for (; htip; htip = htip->next) + { + if ( + (compare && compare (pkey, htip->pkey)) || + pkey == htip->pkey) + { + *htipp = htip->next; + found = TRUE; + break; + } + htipp = &(htip->next); + } + + if (found == TRUE) + { + (*h)->nItems--; + + if (!(*h)->nItems) + { + *h = NULL; + } + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* hTabIsInTable - will determine if an Item is in the hasht */ +/*-----------------------------------------------------------------*/ +int +hTabIsInTable (hTab * htab, int key, + void *item, int (*compareFunc) (void *, void *)) +{ + if (_findItem (htab, key, item, compareFunc)) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hTabFirstItem - returns the first Item in the hTab */ +/*-----------------------------------------------------------------*/ +void * +hTabFirstItem (hTab * htab, int *k) +{ + int key; + + if (!htab) + return NULL; + + for (key = htab->minKey; key <= htab->maxKey; key++) + { + if (htab->table[key]) + { + htab->currItem = htab->table[key]; + htab->currKey = key; + *k = key; + return htab->table[key]->item; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* hTabNextItem - returns the next item in the hTab */ +/*-----------------------------------------------------------------*/ +void * +hTabNextItem (hTab * htab, int *k) +{ + int key; + + if (!htab) + return NULL; + + /* if this chain not ended then */ + if (htab->currItem->next) + { + *k = htab->currItem->key; + return (htab->currItem = htab->currItem->next)->item; + } + + /* find the next chain which has something */ + for (key = htab->currKey + 1; key <= htab->maxKey; key++) + { + if (htab->table[key]) + { + htab->currItem = htab->table[key]; + *k = htab->currKey = key; + return htab->table[key]->item; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* hTabFirstItemWK - returns the first Item in the hTab for a key */ +/*-----------------------------------------------------------------*/ +void * +hTabFirstItemWK (hTab * htab, int wk) +{ + + if (!htab) + return NULL; + + if (wk < htab->minKey || wk > htab->maxKey) + return NULL; + + htab->currItem = htab->table[wk]; + htab->currKey = wk; + + return (htab->table[wk] ? htab->table[wk]->item : NULL); +} + +/*-----------------------------------------------------------------*/ +/* hTabNextItem - returns the next item in the hTab for a key */ +/*-----------------------------------------------------------------*/ +void * +hTabNextItemWK (hTab * htab) +{ + + if (!htab) + return NULL; + + /* if this chain not ended then */ + if (htab->currItem->next) + { + return (htab->currItem = htab->currItem->next)->item; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* hTabFromTable - hash Table from a hash table */ +/*-----------------------------------------------------------------*/ +hTab * +hTabFromTable (hTab * htab) +{ + hTab *nhtab; + hashtItem *htip; + int key; + + if (!htab) + return NULL; + + nhtab = newHashTable (htab->size); + + for (key = htab->minKey; key <= htab->maxKey; key++) + { + + for (htip = htab->table[key]; htip; htip = htip->next) + hTabAddItem (&nhtab, htip->key, htip->item); + } + + return nhtab; +} + +/*-----------------------------------------------------------------*/ +/* isHtabsEqual - returns 1 if all items in htab1 is found in htab2 */ +/*-----------------------------------------------------------------*/ +int +isHtabsEqual (hTab * htab1, hTab * htab2, + int (*compareFunc) (void *, void *)) +{ + void *item; + int key; + + if (htab1 == htab2) + return 1; + + if (htab1 == NULL || htab2 == NULL) + return 0; + + /* if they are different sizes then */ + if (htab1->nItems != htab2->nItems) + return 0; + + /* now do an item by item check */ + for (item = hTabFirstItem (htab1, &key); item; + item = hTabNextItem (htab1, &key)) + if (!hTabIsInTable (htab2, key, item, compareFunc)) + return 0; + + return 1; +} + + +/*-----------------------------------------------------------------*/ +/* hTabSearch - returns the first Item with the specified key */ +/*-----------------------------------------------------------------*/ +hashtItem * +hTabSearch (hTab * htab, int key) +{ + if (!htab) + return NULL; + + if ((key < htab->minKey) || (key > htab->maxKey)) + return NULL; + + if (!htab->table[key]) + return NULL; + + return htab->table[key]; +} + +/*-----------------------------------------------------------------*/ +/* hTabItemWithKey - returns the first item with the given key */ +/*-----------------------------------------------------------------*/ +void * +hTabItemWithKey (hTab * htab, int key) +{ + hashtItem *htip; + + if (!(htip = hTabSearch (htab, key))) + return NULL; + + return htip->item; +} + +/*-----------------------------------------------------------------*/ +/* hTabMaxKey - return the maxKey of item in the hashTable */ +/*-----------------------------------------------------------------*/ +int hTabMaxKey (hTab *htab) +{ + return (htab ? htab->maxKey : 0); +} + +/*-----------------------------------------------------------------*/ +/*hTabAddItemIfNotP - adds an item with nothing found with key */ +/*-----------------------------------------------------------------*/ +void +hTabAddItemIfNotP (hTab ** htab, int key, void *item) +{ + if (!*htab) + { + hTabAddItem (htab, key, item); + return; + } + + if (hTabItemWithKey (*htab, key)) + return; + + hTabAddItem (htab, key, item); +} + +/** Simple implementation of a hash table which uses + string (key, value) pairs. If a key already exists in the + table, the newly added value will replace it. + This is used for the assembler token table. The replace existing + condition is used to implement inheritance. +*/ +static int +_compare (const void *s1, const void *s2) +{ + return !strcmp (s1, s2); +} + +static int +_hash (const char *sz) +{ + /* Dumb for now */ + return *sz; +} + +void +shash_add (hTab ** h, const char *szKey, const char *szValue) +{ + char *val; + int key = _hash (szKey); + + /* Find value of the item */ + val = (char *)hTabFindByKey(*h, key, szKey, _compare); + /* Delete any that currently exist */ + hTabDeleteByKey(h, key, szKey, _compare); + /* Deallocate old value in not NULL */ + if (val != NULL) + Safe_free(val); + /* Duplicate new value if not NULL */ + if (szValue != NULL) + szValue = Safe_strdup(szValue); + /* Now add in ours */ + hTabAddItemLong (h, key, Safe_strdup (szKey), (void *)szValue); +} + +const char * +shash_find (hTab * h, const char *szKey) +{ + int key = _hash (szKey); + return (char *) hTabFindByKey (h, key, szKey, _compare); +} diff --git a/src/SDCChasht.h b/src/SDCChasht.h new file mode 100644 index 0000000..ba7b9cb --- /dev/null +++ b/src/SDCChasht.h @@ -0,0 +1,124 @@ +/*----------------------------------------------------------------- + SDCChast.h - contains support routines for hashtables/sets . + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + + +#ifndef SDCCHASHT_H +#define SDCCHASHT_H + + + +/* hashtable item */ +typedef struct hashtItem + { + int key; + /* Pointer to the key that was hashed for key. + Used for a hash table with unique keys. */ + void *pkey; + void *item; + struct hashtItem *next; + } +hashtItem; + +/* hashtable */ +typedef struct hTab + { + int size; /* max number of items */ + int minKey; /* minimum key value */ + int maxKey; /* maximum key value */ + hashtItem **table; /* the actual table */ + int currKey; /* used for iteration */ + hashtItem *currItem; /* current item within the list */ + int nItems; + } +hTab; + +typedef enum + { + DELETE_CHAIN = 1, + DELETE_ITEM + } +DELETE_ACTION; + + +/*-----------------------------------------------------------------*/ +/* Forward definition for functions */ +/*-----------------------------------------------------------------*/ + +/* hashtable related functions */ +hTab *newHashTable (int); +void hTabAddItem (hTab **, int key, void *item); +/** Adds a new item to the hash table. + @param h The hash table to add to + @param key A hashed version of pkey + @param pkey A copy of the key. Owned by the + hash table after this function. + @param item Value for this key. +*/ +void hTabAddItemLong (hTab ** h, int key, void *pkey, void *item); +/** Finds a item by exact key. + Searches all items in the key 'key' for a key that + according to 'compare' matches pkey. + @param h The hash table to search + @param key A hashed version of pkey. + @param pkey The key to search for + @param compare Returns 0 if pkey == this +*/ +void *hTabFindByKey (hTab * h, int key, const void *pkey, int (*compare) (const void *, const void *)); +/** Deletes an item with the exact key 'pkey' + @see hTabFindByKey +*/ +int hTabDeleteByKey (hTab ** h, int key, const void *pkey, int (*compare) (const void *, const void *)); + +void hTabDeleteItem (hTab **, int key, + const void *item, DELETE_ACTION action, + int (*compareFunc) (const void *, const void *)); +int hTabIsInTable (hTab *, int, void *, + int (*compareFunc) (void *, void *)); +void *hTabFirstItem (hTab *, int *); +void *hTabNextItem (hTab *, int *); +hTab *hTabFromTable (hTab *); +int isHtabsEqual (hTab *, hTab *, int (*compareFunc) (void *, void *)); +hashtItem *hTabSearch (hTab *, int); + +/* return the first item with the given key */ +void *hTabItemWithKey (hTab *, int); + +void hTabAddItemIfNotP (hTab **, int, void *); +void hTabDeleteAll (hTab *); +void *hTabFirstItemWK (hTab * htab, int wk); +void *hTabNextItemWK (hTab * htab); +void hTabClearAll (hTab * htab); +int hTabMaxKey (hTab *htab); + +/** Find the first item that either is 'item' or which + according to 'compareFunc' is the same as item. + @param compareFunc strcmp like compare function, may be null. +*/ +void *hTabFindItem (hTab * htab, int key, + void *item, int (*compareFunc) (void *, void *)); + +void shash_add (hTab ** h, const char *szKey, const char *szValue); +const char *shash_find (hTab * h, const char *szKey); + +#endif diff --git a/src/SDCCicode.c b/src/SDCCicode.c new file mode 100644 index 0000000..ccb9461 --- /dev/null +++ b/src/SDCCicode.c @@ -0,0 +1,4678 @@ +/*------------------------------------------------------------------------- + + SDCCicode.c - intermediate code generation etc. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "newalloc.h" +#include "math.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* global variables */ + +set *iCodeChain = NULL; +int iTempNum = 0; +int iTempLblNum = 0; +int operandKey = 0; +int iCodeKey = 0; +char *filename; /* current file name */ +int lineno = 1; /* current line number */ +int block; +long scopeLevel; +int seqPoint; +int inCriticalPair = 0; + +symbol *returnLabel; /* function return label */ +symbol *entryLabel; /* function entry label */ + +/*-----------------------------------------------------------------*/ +/* forward definition of some functions */ +operand *geniCodeAssign (operand *, operand *, int, int); +static operand *geniCodeArray (operand *, operand *, int); +static operand *geniCodeArray2Ptr (operand *); +operand *geniCodeRValue (operand *, bool); +operand *geniCodeDerefPtr (operand *, int); +static int isLvaluereq (int lvl); +static operand *geniCodeCast (sym_link *, operand *, bool); + +#define PRINTFUNC(x) void x (struct dbuf_s *dbuf, iCode *ic, char *s) +/* forward definition of ic print functions */ +PRINTFUNC (picGetValueAtAddr); +PRINTFUNC (picSetValueAtAddr); +PRINTFUNC (picAddrOf); +PRINTFUNC (picGeneric); +PRINTFUNC (picGenericOne); +PRINTFUNC (picCast); +PRINTFUNC (picAssign); +PRINTFUNC (picLabel); +PRINTFUNC (picGoto); +PRINTFUNC (picIfx); +PRINTFUNC (picJumpTable); +PRINTFUNC (picInline); +PRINTFUNC (picReceive); +PRINTFUNC (picDummyRead); +PRINTFUNC (picCritical); +PRINTFUNC (picEndCritical); + +iCodeTable codeTable[] = { + {'!', "not", picGenericOne, NULL}, + {'~', "~", picGenericOne, NULL}, + {RRC, "rrc", picGenericOne, NULL}, + {RLC, "rlc", picGenericOne, NULL}, + {GETHBIT, "ghbit", picGenericOne, NULL}, + {GETABIT, "gabit", picGenericOne, NULL}, + {GETBYTE, "gbyte", picGenericOne, NULL}, + {GETWORD, "gword", picGenericOne, NULL}, + {UNARYMINUS, "-", picGenericOne, NULL}, + {IPUSH, "push", picGenericOne, NULL}, + {IPOP, "pop", picGenericOne, NULL}, + {CALL, "call", picGenericOne, NULL}, + {PCALL, "pcall", picGenericOne, NULL}, + {FUNCTION, "proc", picGenericOne, NULL}, + {ENDFUNCTION, "eproc", picGenericOne, NULL}, + {RETURN, "ret", picGenericOne, NULL}, + {'+', "+", picGeneric, NULL}, + {'-', "-", picGeneric, NULL}, + {'*', "*", picGeneric, NULL}, + {'/', "/", picGeneric, NULL}, + {'%', "%", picGeneric, NULL}, + {'>', ">", picGeneric, NULL}, + {'<', "<", picGeneric, NULL}, + {LE_OP, "<=", picGeneric, NULL}, + {GE_OP, ">=", picGeneric, NULL}, + {EQ_OP, "==", picGeneric, NULL}, + {NE_OP, "!=", picGeneric, NULL}, + {AND_OP, "&&", picGeneric, NULL}, + {OR_OP, "||", picGeneric, NULL}, + {'^', "^", picGeneric, NULL}, + {'|', "|", picGeneric, NULL}, + {BITWISEAND, "&", picGeneric, NULL}, + {LEFT_OP, "<<", picGeneric, NULL}, + {RIGHT_OP, ">>", picGeneric, NULL}, + {GET_VALUE_AT_ADDRESS, "@", picGetValueAtAddr, NULL}, + {SET_VALUE_AT_ADDRESS, "@", picSetValueAtAddr, NULL}, + {ADDRESS_OF, "&", picAddrOf, NULL}, + {CAST, "<>", picCast, NULL}, + {'=', ":=", picAssign, NULL}, + {LABEL, "", picLabel, NULL}, + {GOTO, "", picGoto, NULL}, + {JUMPTABLE, "jtab", picJumpTable, NULL}, + {IFX, "if", picIfx, NULL}, + {INLINEASM, "", picInline, NULL}, + {RECEIVE, "recv", picReceive, NULL}, + {SEND, "send", picGenericOne, NULL}, + {ARRAYINIT, "arrayInit", picGenericOne, NULL}, + {DUMMY_READ_VOLATILE, "dummy = (volatile)", picDummyRead, NULL}, + {CRITICAL, "critical_start", picCritical, NULL}, + {ENDCRITICAL, "critical_end", picEndCritical, NULL}, + {SWAP, "swap", picGenericOne, NULL} +}; + +/*-----------------------------------------------------------------*/ +/* operandName - returns the name of the operand */ +/*-----------------------------------------------------------------*/ +int +printOperand (operand * op, FILE * file) +{ + struct dbuf_s dbuf; + int ret; + int pnl = 0; + + if (!file) + { + file = stdout; + pnl = 1; + } + dbuf_init (&dbuf, 1024); + ret = dbuf_printOperand (op, &dbuf); + dbuf_write_and_destroy (&dbuf, file); + + if (pnl) + putc ('\n', file); + + return ret; +} + +int +dbuf_printOperand (operand * op, struct dbuf_s *dbuf) +{ + sym_link *opetype; + + if (!op) + return 1; + + switch (op->type) + { + case VALUE: + opetype = getSpec (operandType (op)); + if (IS_FLOAT (opetype)) + dbuf_printf (dbuf, "%g {", SPEC_CVAL (opetype).v_float); + else if (IS_FIXED16X16 (opetype)) + dbuf_printf (dbuf, "%g {", doubleFromFixed16x16 (SPEC_CVAL (opetype).v_fixed16x16)); + else if (IS_LONGLONG (opetype)) + dbuf_printf (dbuf, "0x%llx {", (unsigned long long) SPEC_CVAL (OP_VALUE (op)->etype).v_ulonglong); + else + dbuf_printf (dbuf, "0x%x {", (unsigned int) ulFromVal (OP_VALUE (op))); + dbuf_printTypeChain (operandType (op), dbuf); + dbuf_append_char (dbuf, '}'); + break; + + case SYMBOL: +#define REGA 1 +//#if REGA /* { */ + if (REGA && !getenv ("PRINT_SHORT_OPERANDS")) + { + dbuf_printf (dbuf, "%s [k%d lr%d:%d so:%d]{ ia%d a2p%d re%d rm%d nos%d ru%d dp%d}", /*{ar%d rm%d ru%d p%d a%d u%d i%d au%d k%d ks%d}" , */ + (OP_SYMBOL (op)->rname[0] ? OP_SYMBOL (op)->rname : OP_SYMBOL (op)->name), + op->key, + OP_LIVEFROM (op), OP_LIVETO (op), + OP_SYMBOL (op)->stack, + op->isaddr, op->aggr2ptr, OP_SYMBOL (op)->isreqv, + OP_SYMBOL (op)->remat, OP_SYMBOL (op)->noSpilLoc, OP_SYMBOL (op)->ruonly, OP_SYMBOL (op)->dptr); + { + dbuf_append_char (dbuf, '{'); + dbuf_printTypeChain (operandType (op), dbuf); + if (SPIL_LOC (op) && IS_ITEMP (op)) + dbuf_printf (dbuf, "}{ sir@ %s", SPIL_LOC (op)->rname); + dbuf_append_char (dbuf, '}'); + } + + /* if assigned to registers */ + if (OP_SYMBOL (op)->nRegs) + { + if (OP_SYMBOL (op)->isspilt) + { + if (!OP_SYMBOL (op)->remat) + if (OP_SYMBOL (op)->usl.spillLoc) + dbuf_printf (dbuf, "[%s]", (OP_SYMBOL (op)->usl.spillLoc->rname[0] ? + OP_SYMBOL (op)->usl.spillLoc->rname : OP_SYMBOL (op)->usl.spillLoc->name)); + else + dbuf_append_str (dbuf, "[err]"); + else + dbuf_append_str (dbuf, "[remat]"); + } + else + { + int i; + dbuf_append_char (dbuf, '['); + for (i = 0; i < OP_SYMBOL (op)->nRegs; i++) + dbuf_printf (dbuf, "%s ", port->getRegName (OP_SYMBOL (op)->regs[i])); + dbuf_append_char (dbuf, ']'); + } + } +//#else /* } else { */ + } + else + { + /* (getenv("PRINT_SHORT_OPERANDS") != NULL) */ + dbuf_printf (dbuf, "%s ", (OP_SYMBOL (op)->rname[0] ? OP_SYMBOL (op)->rname : OP_SYMBOL (op)->name)); + + if (getenv ("PRINT_SHORT_OPERANDS")[0] < '1') + { + dbuf_printf (dbuf, "[lr%d:%d so:%d]", OP_LIVEFROM (op), OP_LIVETO (op), OP_SYMBOL (op)->stack); + } + + if (getenv ("PRINT_SHORT_OPERANDS")[0] < '2') + { + dbuf_append_char (dbuf, '{'); + dbuf_printTypeChain (operandType (op), dbuf); + if (SPIL_LOC (op) && IS_ITEMP (op)) + dbuf_printf (dbuf, "}{ sir@ %s", SPIL_LOC (op)->rname); + dbuf_append_char (dbuf, '}'); + } + + /* if assigned to registers */ + if (OP_SYMBOL (op)->nRegs) + { + if (OP_SYMBOL (op)->isspilt) + { + if (!OP_SYMBOL (op)->remat) + if (OP_SYMBOL (op)->usl.spillLoc) + dbuf_printf (dbuf, "[%s]", (OP_SYMBOL (op)->usl.spillLoc->rname[0] ? + OP_SYMBOL (op)->usl.spillLoc->rname : OP_SYMBOL (op)->usl.spillLoc->name)); + else + dbuf_append_str (dbuf, "[err]"); + else + dbuf_append_str (dbuf, "[remat]"); + } + else + { + int i; + dbuf_append_char (dbuf, '['); + for (i = 0; i < OP_SYMBOL (op)->nRegs; i++) + dbuf_printf (dbuf, "%s ", port->getRegName (OP_SYMBOL (op)->regs[i])); + dbuf_append_char (dbuf, ']'); + } + } +//#endif /* } */ + } + break; + + case TYPE: + dbuf_append_char (dbuf, '('); + dbuf_printTypeChain (OP_TYPE (op), dbuf); + dbuf_append_char (dbuf, ')'); + break; + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* print functions */ +/*-----------------------------------------------------------------*/ +PRINTFUNC (picGetValueAtAddr) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printOperand (IC_RESULT (ic), dbuf); + dbuf_append_str (dbuf, " = "); + dbuf_append_str (dbuf, "@["); + dbuf_printOperand (IC_LEFT (ic), dbuf); + if (IC_RIGHT (ic)) + { + dbuf_append_str (dbuf, " + "); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + } + dbuf_append_str (dbuf, "]\n"); +} + +PRINTFUNC (picSetValueAtAddr) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_append_str (dbuf, "*["); + dbuf_printOperand (IC_LEFT (ic), dbuf); + dbuf_append_str (dbuf, "] = "); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picAddrOf) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printOperand (IC_RESULT (ic), dbuf); + if (IS_ITEMP (IC_LEFT (ic))) + dbuf_append_str (dbuf, " = "); + else + dbuf_append_str (dbuf, " = &["); + dbuf_printOperand (IC_LEFT (ic), dbuf); + if (IC_RIGHT (ic)) + { + if (IS_ITEMP (IC_LEFT (ic))) + dbuf_append_str (dbuf, " offsetAdd "); + else + dbuf_append_str (dbuf, " , "); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + } + if (IS_ITEMP (IC_LEFT (ic))) + dbuf_append_char (dbuf, '\n'); + else + dbuf_append_str (dbuf, "]\n"); +} + +PRINTFUNC (picJumpTable) +{ + symbol *sym; + + dbuf_append_char (dbuf, '\t'); + dbuf_printf (dbuf, "%s\t", s); + dbuf_printOperand (IC_JTCOND (ic), dbuf); + for (sym = setFirstItem (IC_JTLABELS (ic)); sym; sym = setNextItem (IC_JTLABELS (ic))) + dbuf_printf (dbuf, "; %s", sym->name); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picGeneric) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printOperand (IC_RESULT (ic), dbuf); + dbuf_append_str (dbuf, " = "); + dbuf_printOperand (IC_LEFT (ic), dbuf); + dbuf_printf (dbuf, " %s ", s); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picGenericOne) +{ + dbuf_append_char (dbuf, '\t'); + if (IC_RESULT (ic)) + { + dbuf_printOperand (IC_RESULT (ic), dbuf); + dbuf_append_str (dbuf, " = "); + } + + if (IC_LEFT (ic)) + { + dbuf_printf (dbuf, "%s ", s); + dbuf_printOperand (IC_LEFT (ic), dbuf); + } + + if (!IC_RESULT (ic) && !IC_LEFT (ic)) + dbuf_append_str (dbuf, s); + + if (ic->op == SEND || ic->op == RECEIVE) + { + dbuf_printf (dbuf, "{argreg = %d}", ic->argreg); + } + if (ic->op == IPUSH) + { + dbuf_printf (dbuf, "{parmPush = %d}", ic->parmPush); + } + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picCast) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printOperand (IC_RESULT (ic), dbuf); + dbuf_append_str (dbuf, " = "); + dbuf_printOperand (IC_LEFT (ic), dbuf); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + dbuf_append_char (dbuf, '\n'); +} + + +PRINTFUNC (picAssign) +{ + dbuf_append_char (dbuf, '\t'); + + if (IC_RESULT (ic)->isaddr && IS_ITEMP (IC_RESULT (ic))) + dbuf_append_str (dbuf, "*("); + + dbuf_printOperand (IC_RESULT (ic), dbuf); + + if (IC_RESULT (ic)->isaddr && IS_ITEMP (IC_RESULT (ic))) + dbuf_append_char (dbuf, ')'); + + dbuf_printf (dbuf, " %s ", s); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picLabel) +{ + dbuf_printf (dbuf, " %s($%d) :\n", IC_LABEL (ic)->name, IC_LABEL (ic)->key); +} + +PRINTFUNC (picGoto) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printf (dbuf, " goto %s($%d)\n", IC_LABEL (ic)->name, IC_LABEL (ic)->key); +} + +PRINTFUNC (picIfx) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_append_str (dbuf, "if "); + dbuf_printOperand (IC_COND (ic), dbuf); + + if (!IC_TRUE (ic)) + dbuf_printf (dbuf, " == 0 goto %s($%d)\n", IC_FALSE (ic)->name, IC_FALSE (ic)->key); + else + { + dbuf_printf (dbuf, " != 0 goto %s($%d)", IC_TRUE (ic)->name, IC_TRUE (ic)->key); + if (IC_FALSE (ic)) + dbuf_printf (dbuf, "; zzgoto %s\n", IC_FALSE (ic)->name); + dbuf_append_char (dbuf, '\n'); + } +} + +PRINTFUNC (picInline) +{ + dbuf_append_str (dbuf, IC_INLINE (ic)); +} + +PRINTFUNC (picReceive) +{ + dbuf_printOperand (IC_RESULT (ic), dbuf); + dbuf_printf (dbuf, " = %s ", s); + dbuf_printOperand (IC_LEFT (ic), dbuf); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picDummyRead) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printf (dbuf, "%s ", s); + dbuf_printOperand (IC_RIGHT (ic), dbuf); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picCritical) +{ + dbuf_append_char (dbuf, '\t'); + if (IC_RESULT (ic)) + dbuf_printOperand (IC_RESULT (ic), dbuf); + else + dbuf_append_str (dbuf, "(stack)"); + dbuf_printf (dbuf, " = %s ", s); + dbuf_append_char (dbuf, '\n'); +} + +PRINTFUNC (picEndCritical) +{ + dbuf_append_char (dbuf, '\t'); + dbuf_printf (dbuf, "%s = ", s); + if (IC_RIGHT (ic)) + dbuf_printOperand (IC_RIGHT (ic), dbuf); + else + dbuf_append_str (dbuf, "(stack)"); + dbuf_append_char (dbuf, '\n'); +} + +/*-----------------------------------------------------------------*/ +/* piCode - prints one iCode */ +/*-----------------------------------------------------------------*/ +int +piCode (void *item, FILE * of) +{ + iCode *ic = item; + iCodeTable *icTab; + struct dbuf_s dbuf; + + if (!of) + of = stdout; + + icTab = getTableEntry (ic->op); + fprintf (of, "%s(%d:%d:%d:%d:%d:%d)\t", ic->filename, ic->lineno, ic->seq, ic->key, ic->depth, ic->supportRtn, ic->block); + dbuf_init (&dbuf, 1024); + icTab->iCodePrint (&dbuf, ic, icTab->printName); + dbuf_write_and_destroy (&dbuf, of); + return 1; +} + +void +PICC (iCode * ic) +{ + printiCChain (ic, stdout); +} + +/*-----------------------------------------------------------------*/ +/* printiCChain - prints intermediate code for humans */ +/*-----------------------------------------------------------------*/ +void +printiCChain (iCode * icChain, FILE * of) +{ + iCode *loop; + iCodeTable *icTab; + + if (!of) + of = stdout; + for (loop = icChain; loop; loop = loop->next) + { + if ((icTab = getTableEntry (loop->op))) + { + struct dbuf_s dbuf; + + fprintf (of, "%s(l%d:s%d:k%d:d%d:s%d:b%d)\t", + loop->filename, loop->lineno, loop->seq, loop->key, loop->depth, loop->supportRtn, loop->block); + + dbuf_init (&dbuf, 1024); + icTab->iCodePrint (&dbuf, loop, icTab->printName); + dbuf_write_and_destroy (&dbuf, of); + + fflush (of); + } + } +} + + +/*-----------------------------------------------------------------*/ +/* newOperand - allocate, init & return a new iCode */ +/*-----------------------------------------------------------------*/ +operand * +newOperand () +{ + operand *op; + + op = Safe_alloc (sizeof (operand)); + + op->key = 0; + return op; +} + +/*-----------------------------------------------------------------*/ +/* newiCode - create and return a new iCode entry initialised */ +/*-----------------------------------------------------------------*/ +iCode * +newiCode (int op, operand * left, operand * right) +{ + iCode *ic; + + ic = Safe_alloc (sizeof (iCode)); + + ic->seqPoint = seqPoint; + ic->filename = filename; + ic->lineno = lineno; + ic->block = block; + ic->level = scopeLevel; + ic->op = op; + ic->key = iCodeKey++; + IC_LEFT (ic) = left; + IC_RIGHT (ic) = right; + + return ic; +} + +/*-----------------------------------------------------------------*/ +/* newiCode for conditional statements */ +/*-----------------------------------------------------------------*/ +iCode * +newiCodeCondition (operand * condition, symbol * trueLabel, symbol * falseLabel) +{ + iCode *ic; + + if (IS_VOID (operandType (condition))) + { + werror (E_VOID_VALUE_USED); + } + + ic = newiCode (IFX, NULL, NULL); + IC_COND (ic) = condition; + IC_TRUE (ic) = trueLabel; + IC_FALSE (ic) = falseLabel; + return ic; +} + +/*-----------------------------------------------------------------*/ +/* newiCodeLabelGoto - unconditional goto statement| label stmnt */ +/*-----------------------------------------------------------------*/ +iCode * +newiCodeLabelGoto (int op, symbol * label) +{ + iCode *ic; + + ic = newiCode (op, NULL, NULL); + ic->op = op; + ic->label = label; + IC_LEFT (ic) = NULL; + IC_RIGHT (ic) = NULL; + IC_RESULT (ic) = NULL; + return ic; +} + +/*-----------------------------------------------------------------*/ +/* newiTemp - allocate & return a newItemp Variable */ +/*-----------------------------------------------------------------*/ +symbol * +newiTemp (const char *s) +{ + struct dbuf_s dbuf; + symbol *itmp; + + dbuf_init (&dbuf, 128); + if (s) + dbuf_append_str (&dbuf, s); + else + dbuf_printf (&dbuf, "iTemp%d", iTempNum++); + + itmp = newSymbol (dbuf_c_str (&dbuf), 1); + dbuf_destroy (&dbuf); + strncpyz (itmp->rname, itmp->name, SDCC_NAME_MAX); + itmp->isitmp = 1; + + return itmp; +} + +/*-----------------------------------------------------------------*/ +/* newiTempLabel - creates a temp variable label */ +/*-----------------------------------------------------------------*/ +symbol * +newiTempLabel (const char *s) +{ + symbol *itmplbl; + + /* check if this already exists */ + if (s && (itmplbl = findSym (LabelTab, NULL, s))) + return itmplbl; + + if (s) + { + itmplbl = newSymbol (s, 1); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "iTempLbl%d", iTempLblNum++); + itmplbl = newSymbol (dbuf_c_str (&dbuf), 1); + dbuf_destroy (&dbuf); + } + + itmplbl->isitmp = 1; + itmplbl->islbl = 1; + itmplbl->key = labelKey++; + addSym (LabelTab, itmplbl, itmplbl->name, 0, 0, 0); + return itmplbl; +} + +/*-----------------------------------------------------------------*/ +/* newiTempLoopHeaderLabel - creates a new loop header label */ +/*-----------------------------------------------------------------*/ +symbol * +newiTempLoopHeaderLabel (bool pre) +{ + symbol *itmplbl; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, pre ? "preHeaderLbl%d" : LOOPEXITLBL "%d", iTempLblNum++); + itmplbl = newSymbol (dbuf_c_str (&dbuf), 1); + dbuf_destroy (&dbuf); + + itmplbl->isitmp = 1; + itmplbl->islbl = 1; + itmplbl->key = labelKey++; + addSym (LabelTab, itmplbl, itmplbl->name, 0, 0, 0); + return itmplbl; +} + + +/*-----------------------------------------------------------------*/ +/* initiCode - initialises some iCode related stuff */ +/*-----------------------------------------------------------------*/ +void +initiCode () +{ + +} + +/*-----------------------------------------------------------------*/ +/* copyiCode - make a copy of the iCode given */ +/*-----------------------------------------------------------------*/ +iCode * +copyiCode (iCode * ic) +{ + iCode *nic = newiCode (ic->op, NULL, NULL); + + nic->filename = ic->filename; + nic->lineno = ic->lineno; + nic->block = ic->block; + nic->level = ic->level; + nic->parmBytes = ic->parmBytes; + + /* deal with the special cases first */ + switch (ic->op) + { + case IFX: + IC_COND (nic) = operandFromOperand (IC_COND (ic)); + IC_TRUE (nic) = IC_TRUE (ic); + IC_FALSE (nic) = IC_FALSE (ic); + break; + + case JUMPTABLE: + IC_JTCOND (nic) = operandFromOperand (IC_JTCOND (ic)); + IC_JTLABELS (nic) = IC_JTLABELS (ic); + break; + + case CALL: + case PCALL: + IC_RESULT (nic) = operandFromOperand (IC_RESULT (ic)); + IC_LEFT (nic) = operandFromOperand (IC_LEFT (ic)); + break; + + case INLINEASM: + IC_INLINE (nic) = IC_INLINE (ic); + break; + + case ARRAYINIT: + IC_ARRAYILIST (nic) = IC_ARRAYILIST (ic); + break; + + default: + IC_RESULT (nic) = operandFromOperand (IC_RESULT (ic)); + IC_LEFT (nic) = operandFromOperand (IC_LEFT (ic)); + IC_RIGHT (nic) = operandFromOperand (IC_RIGHT (ic)); + } + + return nic; +} + +/*-----------------------------------------------------------------*/ +/* getTableEntry - gets the table entry for the given operator */ +/*-----------------------------------------------------------------*/ +iCodeTable * +getTableEntry (int oper) +{ + unsigned i; + + for (i = 0; i < (sizeof (codeTable) / sizeof (iCodeTable)); i++) + if (oper == codeTable[i].icode) + return &codeTable[i]; + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* newiTempOperand - new intermediate temp operand */ +/*-----------------------------------------------------------------*/ +operand * +newiTempOperand (sym_link * type, char throwType) +{ + symbol *itmp; + operand *op = newOperand (); + sym_link *etype; + + op->type = SYMBOL; + itmp = newiTemp (NULL); + + etype = getSpec (type); + + if (IS_LITERAL (etype)) + throwType = 0; + + /* copy the type information */ + if (type) + itmp->etype = getSpec (itmp->type = (throwType ? type : copyLinkChain (type))); + + SPEC_SCLS (itmp->etype) = S_FIXED; + + /* iTemps always live in the default address space */ + if (IS_DECL (itmp->type)) + DCL_PTR_ADDRSPACE (itmp->type) = 0; + else + SPEC_ADDRSPACE (itmp->etype) = 0; + + op->svt.symOperand = itmp; + op->key = itmp->key = ++operandKey; + return op; +} + +/*-----------------------------------------------------------------*/ +/* operandType - returns the type chain for an operand */ +/*-----------------------------------------------------------------*/ +sym_link * +operandType (const operand *op) +{ + wassert (op); + + /* depending on type of operand */ + switch (op->type) + { + case VALUE: + return op->svt.valOperand->type; + + case SYMBOL: + return op->svt.symOperand->type; + + case TYPE: + return op->svt.typeOperand; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, " operand type not known "); + assert (0); /* should never come here */ + /* Just to keep the compiler happy */ + return (sym_link *) 0; + } +} + +/*-----------------------------------------------------------------*/ +/* operandSize - returns size of an operand in bytes */ +/*-----------------------------------------------------------------*/ +unsigned int +operandSize (operand * op) +{ + sym_link *type; + + /* if nothing return 0 */ + if (!op) + return 0; + + type = operandType (op); + if (op->aggr2ptr == 2) + type = type->next; + return getSize (type); +} + +/*-----------------------------------------------------------------*/ +/* isParamterToCall - will return 1 if op is a parameter to args */ +/*-----------------------------------------------------------------*/ +int +isParameterToCall (value * args, operand * op) +{ + value *tval = args; + + wassert (IS_SYMOP (op)); + + while (tval) + { + if (tval->sym && isSymbolEqual (OP_SYMBOL (op), tval->sym)) + return 1; + tval = tval->next; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isOperandGlobal - return 1 if operand is a global variable */ +/*-----------------------------------------------------------------*/ +int +isOperandGlobal (const operand *op) +{ + if (!op) + return 0; + + if (IS_ITEMP (op)) + return 0; + + if (IS_SYMOP (op) && + (op->svt.symOperand->level == 0 || IS_STATIC (op->svt.symOperand->etype) || IS_EXTERN (op->svt.symOperand->etype))) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isOperandVolatile - return 1 if the operand is volatile */ +/*-----------------------------------------------------------------*/ +int +isOperandVolatile (const operand *op, bool chkTemp) +{ + if (!op) + return 0; + + if (IS_ITEMP (op) && !chkTemp) + return 0; + + return IS_VOLATILE (operandType (op)); +} + +/*-----------------------------------------------------------------*/ +/* isOperandLiteral - returns 1 if an operand contains a literal */ +/*-----------------------------------------------------------------*/ +int +isOperandLiteral (const operand *const op) +{ + sym_link *opetype; + + if (!op) + return 0; + + opetype = getSpec (operandType (op)); + + if (IS_LITERAL (opetype)) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isOperandInFarSpace - will return true if operand is in farSpace */ +/*-----------------------------------------------------------------*/ +bool +isOperandInFarSpace (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + if (!IS_TRUE_SYMOP (op)) + { + if (SPIL_LOC (op)) + etype = SPIL_LOC (op)->etype; + else + return FALSE; + } + else + { + etype = getSpec (operandType (op)); + } + return (IN_FARSPACE (SPEC_OCLS (etype)) ? TRUE : FALSE); +} + +/*-----------------------------------------------------------------*/ +/* isOperandInPagedSpace - return true if operand is in pagedSpace */ +/*-----------------------------------------------------------------*/ +bool +isOperandInPagedSpace (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + if (!IS_TRUE_SYMOP (op)) + { + if (SPIL_LOC (op)) + etype = SPIL_LOC (op)->etype; + else + return FALSE; + } + else + { + etype = getSpec (operandType (op)); + } + return (IN_PAGEDSPACE (SPEC_OCLS (etype)) ? TRUE : FALSE); +} + +/*------------------------------------------------------------------*/ +/* isOperandInDirSpace - will return true if operand is in dirSpace */ +/*------------------------------------------------------------------*/ +bool +isOperandInDirSpace (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + if (!IS_TRUE_SYMOP (op)) + { + if (SPIL_LOC (op)) + etype = SPIL_LOC (op)->etype; + else + return FALSE; + } + else + { + etype = getSpec (operandType (op)); + } + return (IN_DIRSPACE (SPEC_OCLS (etype)) ? TRUE : FALSE); +} + +/*-----------------------------------------------------------------*/ +/* isOperandInBitSpace - will return true if operand is in bitSpace */ +/*-----------------------------------------------------------------*/ +bool +isOperandInBitSpace (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + if (!IS_TRUE_SYMOP (op)) + { + if (SPIL_LOC (op)) + etype = SPIL_LOC (op)->etype; + else + return FALSE; + } + else + { + etype = getSpec (operandType (op)); + } + return (IN_BITSPACE (SPEC_OCLS (etype)) ? TRUE : FALSE); +} + +/*--------------------------------------------------------------------*/ +/* isOperandInCodeSpace - will return true if operand is in codeSpace */ +/*--------------------------------------------------------------------*/ +bool +isOperandInCodeSpace (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + etype = getSpec (operandType (op)); + + if (!IS_TRUE_SYMOP (op)) + { + if (SPIL_LOC (op)) + etype = SPIL_LOC (op)->etype; + else + return FALSE; + } + else + { + etype = getSpec (operandType (op)); + } + return (IN_CODESPACE (SPEC_OCLS (etype)) ? TRUE : FALSE); +} + +/*-----------------------------------------------------------------*/ +/* isOperandOnStack - will return true if operand is on stack */ +/*-----------------------------------------------------------------*/ +bool +isOperandOnStack (operand * op) +{ + sym_link *etype; + + if (!op) + return FALSE; + + if (!IS_SYMOP (op)) + return FALSE; + + etype = getSpec (operandType (op)); + if (IN_STACK (etype) || OP_SYMBOL (op)->onStack || (SPIL_LOC (op) && SPIL_LOC (op)->onStack)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* isOclsExpensive - will return true if accesses to an output */ +/* storage class are expensive */ +/*-----------------------------------------------------------------*/ +bool +isOclsExpensive (struct memmap * oclass) +{ + if (port->oclsExpense) + return port->oclsExpense (oclass) > 0; + + /* In the absence of port specific guidance, assume only */ + /* farspace is expensive. */ + return IN_FARSPACE (oclass); +} + +/*-----------------------------------------------------------------*/ +/* isiCodeInFunctionCall - return TRUE if an iCode is between a */ +/* CALL/PCALL and the first IPUSH/SEND associated with the call */ +/*-----------------------------------------------------------------*/ +int +isiCodeInFunctionCall (iCode * ic) +{ + iCode *lic = ic; + + /* Find the next CALL/PCALL */ + while (lic) + { + if (lic->op == CALL || lic->op == PCALL) + break; + lic = lic->next; + } + + if (!lic) + return FALSE; + + /* A function call was found. Scan backwards and see if an */ + /* IPUSH or SEND is encountered */ + while (ic) + { + if (lic != ic && (ic->op == CALL || ic->op == PCALL)) + return FALSE; + if (ic->op == SEND || (ic->op == IPUSH && ic->parmPush)) + return TRUE; + ic = ic->prev; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandLitValueUll - unsigned long long value of an operand */ +/*-----------------------------------------------------------------*/ +unsigned long long +operandLitValueUll (const operand * op) +{ + assert (isOperandLiteral (op)); + + return ullFromVal (OP_VALUE_CONST (op)); +} + +/*-----------------------------------------------------------------*/ +/* operandLitValue - literal value of an operand */ +/*-----------------------------------------------------------------*/ +double +operandLitValue (const operand * op) +{ + assert (isOperandLiteral (op)); + + return floatFromVal (OP_VALUE_CONST (op)); +} + +extern bool regalloc_dry_run; + +/*-----------------------------------------------------------------*/ +/* getBuiltInParms - returns parameters to a builtin function */ +/*-----------------------------------------------------------------*/ +iCode * +getBuiltinParms (iCode * fic, int *pcount, operand ** parms) +{ + sym_link *ftype; + iCode *ic = fic; + + *pcount = 0; + /* builtin function uses only SEND for parameters */ + while (ic->op != CALL) + { + assert (ic->op == SEND && ic->builtinSEND); + if(!regalloc_dry_run || ic != fic) + ic->generated = 1; /* mark the icode as generated */ + parms[*pcount] = IC_LEFT (ic); + ic = ic->next; + (*pcount)++; + } + + ic->generated = 1; + /* make sure this is a builtin function call */ + assert (IS_SYMOP (IC_LEFT (ic))); + ftype = operandType (IC_LEFT (ic)); + assert (IFFUNC_ISBUILTIN (ftype)); + return ic; +} + +/*-----------------------------------------------------------------*/ +/* operandOperation - performs operations on operands */ +/*-----------------------------------------------------------------*/ +operand * +operandOperation (operand * left, operand * right, int op, sym_link * type) +{ + sym_link *let, *ret = NULL; + operand *retval = (operand *) 0; + + assert (isOperandLiteral (left)); + let = getSpec (operandType (left)); + if (right) + { + assert (isOperandLiteral (right)); + ret = getSpec (operandType (right)); + } + + /* FIXME: most of these are not long long safe yet */ + switch (op) + { + case '+': + retval = operandFromValue (valCastLiteral (type, operandLitValue (left) + operandLitValue (right), operandLitValueUll (left) + operandLitValueUll (right))); + break; + case '-': + retval = operandFromValue (valCastLiteral (type, operandLitValue (left) - operandLitValue (right), operandLitValueUll (left) - operandLitValueUll (right))); + break; + case '*': + /* + retval = operandFromValue (valCastLiteral (type, + operandLitValue (left) * + operandLitValue (right))); + This could be all we've to do, but with gcc we've to take care about + overflows. Two examples: + ULONG_MAX * ULONG_MAX doesn't fit into a double, some of the least + significant bits are lost (52 in fraction, 63 bits would be + necessary to keep full precision). + If the resulting double value is greater than ULONG_MAX (resp. + USHRT_MAX, ...), then 0 will be assigned to v_ulong (resp. u_uint, ...)! + */ + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (IS_INT (type) || !IS_SPEC (type)) + { + /* long long is handled here, because it can overflow with double */ + if (IS_LONGLONG (type) || !IS_SPEC (type)) + /* signed and unsigned mul are the same, as long as the precision + of the result isn't bigger than the precision of the operands. */ + retval = operandFromValue (valCastLiteral (type, + operandLitValue (left) * + operandLitValue (right), + operandLitValueUll (left) * + operandLitValueUll (right))); + /* long is handled here, because it can overflow with double */ + else if (IS_LONG (type) || !IS_SPEC (type)) + /* signed and unsigned mul are the same, as long as the precision + of the result isn't bigger than the precision of the operands. */ + retval = operandFromValue (valCastLiteral (type, + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) * + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)), + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) * + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)))); + else if (IS_UNSIGNED (type)) /* unsigned int */ + { + /* unsigned int is handled here in order to detect overflow */ + TYPE_TARGET_ULONG ul = (TYPE_TARGET_UINT) double2ul (operandLitValue (left)) * + (TYPE_TARGET_UINT) double2ul (operandLitValue (right)); + + retval = operandFromValue (valCastLiteral (type, (TYPE_TARGET_UINT) ul, (TYPE_TARGET_UINT) ul)); + if (ul != (TYPE_TARGET_UINT) ul) + werror (W_INT_OVL); + } + else /* signed int */ + { + /* signed int is handled here in order to detect overflow */ + TYPE_TARGET_LONG l = (TYPE_TARGET_INT) operandLitValue (left) * (TYPE_TARGET_INT) operandLitValue (right); + + retval = operandFromValue (valCastLiteral (type, (TYPE_TARGET_INT) l, (TYPE_TARGET_INT) l)); + if (l != (TYPE_TARGET_INT) l) + werror (W_INT_OVL); + } + } + else + /* all others go here: */ + retval = operandFromValue (valCastLiteral (type, operandLitValue (left) * operandLitValue (right), operandLitValueUll (left) * operandLitValueUll (right))); + break; + case '/': + if ((TYPE_TARGET_ULONG) double2ul (operandLitValue (right)) == 0 && operandLitValueUll (right) == 0) + { + werror (E_DIVIDE_BY_ZERO); + retval = right; + break; + } + if (IS_UNSIGNED (type)) + { + SPEC_USIGN (let) = 1; + SPEC_USIGN (ret) = 1; + if (IS_LONGLONG (type)) + retval = operandFromValue (valCastLiteral (type, + 0.0, + operandLitValueUll (left) / + operandLitValueUll (right))); + else + retval = operandFromValue (valCastLiteral (type, + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) / + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)), + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) / + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)))); + } + else + retval = operandFromValue (valCastLiteral (type, operandLitValue (left) / operandLitValue (right), operandLitValueUll (left) / operandLitValueUll (right))); + break; + case '%': + if ((TYPE_TARGET_ULONG) double2ul (operandLitValue (right)) == 0 && operandLitValueUll (right) == 0) + { + werror (E_DIVIDE_BY_ZERO); + retval = right; + } + else + { + if (IS_UNSIGNED (type)) + { + if (IS_LONGLONG (type)) + retval = operandFromValue (valCastLiteral (type, + 0.0, + operandLitValueUll (left) % + operandLitValueUll (right))); + else + retval = operandFromLit ((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) % (TYPE_TARGET_ULONG) double2ul (operandLitValue (right))); + } + else + retval = operandFromLit ((TYPE_TARGET_LONG) operandLitValue (left) % (TYPE_TARGET_LONG) operandLitValue (right)); + } + break; + case LEFT_OP: + /* The number of left shifts is always unsigned. Signed doesn't make + sense here. Shifting by a negative number is impossible. */ + if (IS_LONGLONG (type)) + retval = operandFromValue (valCastLiteral (type, + (operandLitValueUll (left) << + operandLitValueUll (right)), + (operandLitValueUll (left) << + operandLitValueUll (right)))); + else + retval = operandFromValue (valCastLiteral (type, + ((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) << + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right))), + ((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) << + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right))))); + break; + case RIGHT_OP: + /* The number of right shifts is always unsigned. Signed doesn't make + sense here. Shifting by a negative number is impossible. */ + retval = operandFromValue (valRecastLitVal (type, valShift (OP_VALUE (left), OP_VALUE (right), 0))); + break; + case EQ_OP: + if (IS_FLOAT (let) || IS_FLOAT (ret)) + { + retval = operandFromLit (operandLitValue (left) == operandLitValue (right)); + } + else if (IS_FIXED16X16 (let) || IS_FIXED16X16 (ret)) + { + retval = operandFromLit (operandLitValue (left) == operandLitValue (right)); + } + else if (IS_PTR (operandType (left)) || IS_PTR (operandType (right))) + { + retval = operandFromLit (operandLitValue (left) == operandLitValue (right)); + } + else + { + /* this op doesn't care about signedness */ + TYPE_TARGET_ULONG l, r; + + l = (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)); + r = (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)); + /* In order to correctly compare 'signed int' and 'unsigned int' it's + neccessary to strip them to 16 bit. + Literals are reduced to their cheapest type, therefore left and + right might have different types. It's neccessary to find a + common type: int (used for char too) or long */ + if (!IS_LONG (let) && !IS_LONG (ret)) + { + r = (TYPE_TARGET_UINT) r; + l = (TYPE_TARGET_UINT) l; + } + retval = operandFromLit (l == r); + } + break; + case '<': + retval = operandFromLit (operandLitValue (left) < operandLitValue (right)); + break; + case LE_OP: + retval = operandFromLit (operandLitValue (left) <= operandLitValue (right)); + break; + case NE_OP: + retval = operandFromLit (operandLitValue (left) != operandLitValue (right)); + break; + case '>': + retval = operandFromLit (operandLitValue (left) > operandLitValue (right)); + break; + case GE_OP: + retval = operandFromLit (operandLitValue (left) >= operandLitValue (right)); + break; + case BITWISEAND: + retval = operandFromValue (valCastLiteral (type, + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) & + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)), + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) & + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)))); + break; + case '|': + retval = operandFromValue (valCastLiteral (type, + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) | + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)), + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) | + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)))); + break; + case '^': + retval = operandFromValue (valCastLiteral (type, + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) ^ + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)), + (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) ^ + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)))); + break; + case AND_OP: + retval = operandFromLit (operandLitValue (left) && operandLitValue (right)); + break; + case OR_OP: + retval = operandFromLit (operandLitValue (left) || operandLitValue (right)); + break; + case RRC: + { + TYPE_TARGET_ULONG i = (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)); + + retval = operandFromLit ((i >> (getSize (operandType (left)) * 8 - 1)) | (i << 1)); + } + break; + case RLC: + { + TYPE_TARGET_ULONG i = (TYPE_TARGET_ULONG) double2ul (operandLitValue (left)); + + retval = operandFromLit ((i << (getSize (operandType (left)) * 8 - 1)) | (i >> 1)); + } + break; + case GETABIT: + retval = operandFromLit (((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) >> + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right))) & 1); + break; + case GETBYTE: + retval = operandFromLit (((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) >> + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)) & 0xFF)); + break; + case GETWORD: + retval = operandFromLit (((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) >> + (TYPE_TARGET_ULONG) double2ul (operandLitValue (right)) & 0xFFFF)); + break; + + case GETHBIT: + retval = operandFromLit (((TYPE_TARGET_ULONG) double2ul (operandLitValue (left)) >> ((getSize (let) * 8) - 1)) & 1); + break; + + case UNARYMINUS: + retval = operandFromValue (valCastLiteral (type, -1 * operandLitValue (left), (-1ll) * operandLitValueUll (left))); + break; + + case '~': + retval = operandFromValue (valCastLiteral (type, ~((TYPE_TARGET_ULONG) double2ul (operandLitValue (left))), ~((TYPE_TARGET_ULONGLONG) operandLitValueUll (left)))); + break; + + case '!': + retval = operandFromLit (!operandLitValue (left)); + break; + + case ADDRESS_OF: + retval = operandFromValue (valCastLiteral (type, operandLitValue (left), (TYPE_TARGET_ULONGLONG) operandLitValueUll (left))); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, " operandOperation invalid operator "); + assert (0); + } + + return retval; +} + +/*-----------------------------------------------------------------*/ +/* isOperandEqual - compares two operand & return 1 if they are = */ +/*-----------------------------------------------------------------*/ +int +isOperandEqual (const operand * left, const operand * right) +{ + /* if the pointers are equal then they are equal */ + if (left == right) + return 1; + + /* if either of them is null then false */ + if (!left || !right) + return 0; + + if (left->type != right->type) + return 0; + + if (IS_SYMOP (left) && IS_SYMOP (right)) + return left->key == right->key; + + /* if types are the same */ + switch (left->type) + { + case SYMBOL: + return isSymbolEqual (left->svt.symOperand, right->svt.symOperand); + case VALUE: + return (compareType (left->svt.valOperand->type, right->svt.valOperand->type) && + (!IS_FLOAT (getSpec (left->svt.valOperand->type)) ? + (operandLitValueUll (left) == operandLitValueUll (right)) : + (operandLitValue (left) == operandLitValue (right)))); + case TYPE: + if (compareType (left->svt.typeOperand, right->svt.typeOperand) == 1) + return 1; + } + + return 0; +} + +/*-------------------------------------------------------------------*/ +/* isiCodeEqual - compares two iCodes are equal, returns true if yes */ +/*-------------------------------------------------------------------*/ +int +isiCodeEqual (iCode * left, iCode * right) +{ + /* if the same pointer */ + if (left == right) + return 1; + + /* if either of them null */ + if (!left || !right) + return 0; + + /* if operand are the same */ + if (left->op == right->op) + { + /* compare all the elements depending on type */ + if (left->op != IFX) + { + if (!isOperandEqual (IC_LEFT (left), IC_LEFT (right))) + return 0; + if (!isOperandEqual (IC_RIGHT (left), IC_RIGHT (right))) + return 0; + } + else + { + if (!isOperandEqual (IC_COND (left), IC_COND (right))) + return 0; + if (!isSymbolEqual (IC_TRUE (left), IC_TRUE (right))) + return 0; + if (!isSymbolEqual (IC_FALSE (left), IC_FALSE (right))) + return 0; + } + + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* newiTempFromOp - create a temp Operand with same attributes */ +/*-----------------------------------------------------------------*/ +operand * +newiTempFromOp (operand * op) +{ + operand *nop; + + if (!op) + return NULL; + + if (!IS_ITEMP (op)) + return op; + + nop = newiTempOperand (operandType (op), TRUE); + nop->isaddr = op->isaddr; + nop->isvolatile = op->isvolatile; + nop->isGlobal = op->isGlobal; + nop->isLiteral = op->isLiteral; + nop->usesDefs = op->usesDefs; + nop->isParm = op->isParm; + return nop; +} + +/*-----------------------------------------------------------------*/ +/* operand from operand - creates an operand holder for the type */ +/*-----------------------------------------------------------------*/ +operand * +operandFromOperand (operand * op) +{ + operand *nop; + + if (!op) + return NULL; + nop = newOperand (); + nop->type = op->type; + nop->isaddr = op->isaddr; + nop->key = op->key; + nop->isvolatile = op->isvolatile; + nop->isGlobal = op->isGlobal; + nop->isLiteral = op->isLiteral; + nop->usesDefs = op->usesDefs; + nop->isParm = op->isParm; + nop->isConstElimnated = op->isConstElimnated; + + switch (nop->type) + { + case SYMBOL: + nop->svt.symOperand = op->svt.symOperand; + break; + case VALUE: + nop->svt.valOperand = op->svt.valOperand; + break; + case TYPE: + nop->svt.typeOperand = op->svt.typeOperand; + break; + } + + return nop; +} + +/*-----------------------------------------------------------------*/ +/* opFromOpWithDU - makes a copy of the operand and DU chains */ +/*-----------------------------------------------------------------*/ +operand * +opFromOpWithDU (operand * op, bitVect * defs, bitVect * uses) +{ + operand *nop = operandFromOperand (op); + + if (nop->type == SYMBOL) + { + OP_DEFS (nop) = bitVectCopy (defs); + OP_USES (nop) = bitVectCopy (uses); + } + + return nop; +} + +/*-----------------------------------------------------------------*/ +/* operandFromSymbol - creates an operand from a symbol */ +/*-----------------------------------------------------------------*/ +operand * +operandFromSymbol (symbol * sym) +{ + operand *op; + iCode *ic; + int ok = 1; + /* if the symbol's type is a literal */ + /* then it is an enumerator type */ + if (IS_LITERAL (sym->etype) && SPEC_ENUM (sym->etype)) + return operandFromValue (valFromType (sym->etype)); + + if (!sym->key) + sym->key = ++operandKey; + + /* if this an implicit variable, means struct/union */ + /* member so just return it */ + if (sym->implicit || IS_FUNC (sym->type)) + { + op = newOperand (); + op->type = SYMBOL; + op->svt.symOperand = sym; + op->key = sym->key; + op->isvolatile = isOperandVolatile (op, TRUE); + op->isGlobal = isOperandGlobal (op); + return op; + } + + /* under the following conditions create a + register equivalent for a local symbol */ + if (sym->level && sym->etype && SPEC_OCLS (sym->etype) && + (IN_FARSPACE (SPEC_OCLS (sym->etype)) && !TARGET_HC08_LIKE && (!(options.model == MODEL_FLAT24))) && options.stackAuto == 0) + { + ok = 0; + } + + if (!IS_AGGREGATE (sym->type) && /* not an aggregate */ + !IS_FUNC (sym->type) && /* not a function */ + !sym->_isparm && /* not a parameter */ + IS_AUTO (sym) && /* is a local auto variable */ + !sym->addrtaken && /* whose address has not been taken */ + !sym->reqv && /* does not already have a reg equivalence */ + !IS_VOLATILE (sym->etype) && /* not declared as volatile */ + !sym->islbl && /* not a label */ + !(TARGET_HC08_LIKE && (getSize (sym->type) > 2)) && /* will fit in regs */ + ok /* farspace check */ + ) + { + /* we will use it after all optimizations + and before liveRange calculation */ + sym->reqv = newiTempOperand (sym->type, 0); + sym->reqv->key = sym->key; + OP_SYMBOL (sym->reqv)->prereqv = sym; + OP_SYMBOL (sym->reqv)->key = sym->key; + OP_SYMBOL (sym->reqv)->isreqv = 1; + OP_SYMBOL (sym->reqv)->islocal = 1; + OP_SYMBOL (sym->reqv)->onStack = sym->onStack; + SPIL_LOC (sym->reqv) = sym; + } + + if (!IS_AGGREGATE (sym->type)) + { + op = newOperand (); + op->type = SYMBOL; + op->svt.symOperand = sym; + op->isaddr = 1; + op->key = sym->key; + op->isvolatile = isOperandVolatile (op, TRUE); + op->isGlobal = isOperandGlobal (op); + op->isPtr = IS_PTR (operandType (op)); + op->isParm = sym->_isparm; + return op; + } + + /* create :- */ + /* itemp = &[_symbol] */ + + ic = newiCode (ADDRESS_OF, newOperand (), operandFromLit (0)); + IC_LEFT (ic)->type = SYMBOL; + IC_LEFT (ic)->svt.symOperand = sym; + IC_LEFT (ic)->key = sym->key; + (IC_LEFT (ic))->isvolatile = isOperandVolatile (IC_LEFT (ic), TRUE); + (IC_LEFT (ic))->isGlobal = isOperandGlobal (IC_LEFT (ic)); + IC_LEFT (ic)->isPtr = IS_PTR (operandType (IC_LEFT (ic))); + + /* create result */ + IC_RESULT (ic) = newiTempOperand (sym->type, 0); + if (IS_ARRAY (sym->type)) + { + IC_RESULT (ic) = geniCodeArray2Ptr (IC_RESULT (ic)); + } + else + { + IC_RESULT (ic)->isaddr = (!IS_AGGREGATE (sym->type)); + } + + ADDTOCHAIN (ic); + + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* operandFromValue - creates an operand from value */ +/*-----------------------------------------------------------------*/ +operand * +operandFromValue (value *val) +{ + operand *op; + + /* if this is a symbol then do the symbol thing */ + if (val->sym) + return operandFromSymbol (val->sym); + + /* this is not a symbol */ + op = newOperand (); + op->type = VALUE; + op->svt.valOperand = val; + op->isLiteral = isOperandLiteral (op); + return op; +} + +/*-----------------------------------------------------------------*/ +/* operandFromLink - operand from typeChain */ +/*-----------------------------------------------------------------*/ +operand * +operandFromLink (sym_link * type) +{ + operand *op; + + /* operand from sym_link */ + if (!type) + return NULL; + + op = newOperand (); + op->type = TYPE; + op->svt.typeOperand = copyLinkChain (type); + return op; +} + +/*-----------------------------------------------------------------*/ +/* operandFromLit - makes an operand from a literal value */ +/*-----------------------------------------------------------------*/ +operand * +operandFromLit (double i) +{ + return operandFromValue (valueFromLit (i)); +} + +/*-----------------------------------------------------------------*/ +/* operandFromAst - creates an operand from an ast */ +/*-----------------------------------------------------------------*/ +operand * +operandFromAst (ast * tree, int lvl) +{ + if (!tree) + return NULL; + + /* depending on type do */ + switch (tree->type) + { + case EX_OP: + return ast2iCode (tree, lvl + 1); + break; + + case EX_VALUE: + return operandFromValue (tree->opval.val); + break; + + case EX_LINK: + return operandFromLink (tree->opval.lnk); + break; + + default: + assert (0); + } + + /* Just to keep the compiler happy */ + return (operand *) 0; +} + +/*-----------------------------------------------------------------*/ +/* setOperandType - sets the operand's type to the given type */ +/*-----------------------------------------------------------------*/ +void +setOperandType (operand * op, sym_link * type) +{ + /* depending on the type of operand */ + switch (op->type) + { + case VALUE: + op->svt.valOperand->etype = getSpec (op->svt.valOperand->type = copyLinkChain (type)); + return; + + case SYMBOL: + if (op->svt.symOperand->isitmp) + { + op->svt.symOperand->etype = getSpec (op->svt.symOperand->type = copyLinkChain (type)); + if (IS_SPEC (op->svt.symOperand->type)) + { + SPEC_SCLS (op->svt.symOperand->etype) = S_REGISTER; + SPEC_OCLS (op->svt.symOperand->etype) = reg; + } + } + else + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "attempt to modify type of source"); + return; + + case TYPE: + op->svt.typeOperand = copyLinkChain (type); + return; + } +} + +/*-----------------------------------------------------------------*/ +/* Get size in byte of ptr need to access an array */ +/*-----------------------------------------------------------------*/ +static unsigned int +getArraySizePtr (operand * op) +{ + sym_link *ltype = operandType (op); + + if (IS_PTR (ltype)) + { + int size = getSize (ltype); + return ((IS_GENPTR (ltype) && GPTRSIZE > FARPTRSIZE) ? (size - 1) : size); + } + + if (IS_ARRAY (ltype)) + { + sym_link *letype = getSpec (ltype); + switch (PTR_TYPE (SPEC_OCLS (letype))) + { + case IPOINTER: + case PPOINTER: + case POINTER: + return (NEARPTRSIZE); + case EEPPOINTER: + case FPOINTER: + case CPOINTER: + case FUNCTION: + return (FARPTRSIZE); + case GPOINTER: + if (GPTRSIZE > FARPTRSIZE) + return (GPTRSIZE - 1); + else + return (FARPTRSIZE); + + default: + return (FARPTRSIZE); + } + } + return (FARPTRSIZE); +} + +/*-----------------------------------------------------------------*/ +/* perform "usual unary conversions" */ +/*-----------------------------------------------------------------*/ +#if 0 +static operand * +usualUnaryConversions (operand * op) +{ + if (IS_INTEGRAL (operandType (op))) + { + if (getSize (operandType (op)) < (unsigned int) INTSIZE) + { + /* Widen to int. */ + return geniCodeCast (INTTYPE, op, TRUE); + } + } + return op; +} +#endif + +/*-----------------------------------------------------------------*/ +/* perform "usual binary conversions" */ +/*-----------------------------------------------------------------*/ + +static sym_link * +usualBinaryConversions (operand ** op1, operand ** op2, RESULT_TYPE resultType, int op) +{ + sym_link *ctype; + sym_link *rtype = operandType (*op2); + sym_link *ltype = operandType (*op1); + + ctype = computeType (ltype, rtype, resultType, op); + + switch (op) + { + case '*': + case '/': + case '%': + if (IS_CHAR (getSpec (ltype)) && IS_CHAR (getSpec (rtype))) + { + /* one byte operations: keep signedness for code generator */ + return ctype; + } + break; + default: + break; + } + + *op1 = geniCodeCast (ctype, *op1, TRUE); + *op2 = geniCodeCast (ctype, *op2, TRUE); + + return ctype; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeValueAtAddress - generate intermediate code for value */ +/* at address */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeRValue (operand * op, bool force) +{ + iCode *ic; + sym_link *type = operandType (op); + sym_link *etype = getSpec (type); + + /* if this is an array & already */ + /* a resolved address then return this */ + if ((IS_ARRAY (type) && !IS_FUNCPTR (type->next)) || IS_STRUCT (type) || (IS_PTR (type) && !force && !op->isaddr)) + return operandFromOperand (op); + + /* if this is not an address then must be */ + /* rvalue already so return this one */ + if (!op->isaddr) + return op; + + /* if this is not a temp symbol then */ + if (!IS_ITEMP (op) && !force && !(IN_FARSPACE (SPEC_OCLS (etype)) && !TARGET_HC08_LIKE)) + { + op = operandFromOperand (op); + op->isaddr = 0; + return op; + } + + if (IS_SPEC (type) && + IS_TRUE_SYMOP (op) && (!(IN_FARSPACE (SPEC_OCLS (etype)) && !TARGET_HC08_LIKE) || (options.model == MODEL_FLAT24))) + { + op = operandFromOperand (op); + op->isaddr = 0; + return op; + } + + ic = newiCode (GET_VALUE_AT_ADDRESS, op, operandFromLit (0)); + if ((IS_PTR (type) && op->isaddr && force) || IS_ARRAY (type)) + type = type->next; + + type = copyLinkChain (type); + + IC_RESULT (ic) = newiTempOperand (type, 1); + IC_RESULT (ic)->isaddr = 0; + +/* ic->supportRtn = ((IS_GENPTR(type) | op->isGptr) & op->isaddr); */ + + ADDTOCHAIN (ic); + + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* checkPtrQualifiers - check for lost pointer qualifers */ +/*-----------------------------------------------------------------*/ +static void +checkPtrQualifiers (sym_link * ltype, sym_link * rtype, int warn_const) +{ + if (IS_PTR (ltype) && IS_PTR (rtype) && !IS_FUNCPTR (ltype) && warn_const) + { + if (!IS_CONSTANT (ltype->next) && IS_CONSTANT (rtype->next)) + werror (W_TARGET_LOST_QUALIFIER, "const"); +#if 0 + // disabled because SDCC will make all union fields volatile + // but your ptr to it need not be + if (!IS_VOLATILE (ltype->next) && IS_VOLATILE (rtype->next)) + werror (W_TARGET_LOST_QUALIFIER, "volatile"); +#endif + if (!IS_RESTRICT (ltype->next) && IS_RESTRICT (rtype->next)) + werror (W_TARGET_LOST_QUALIFIER, "restrict"); + } +} + +/*-----------------------------------------------------------------*/ +/* geniCodeCast - changes the value from one type to another */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeCast (sym_link *type, operand *op, bool implicit) +{ + iCode *ic; + sym_link *optype; + sym_link *opetype = getSpec (optype = operandType (op)); + sym_link *restype; + + /* one of them has size zero then error */ + if (IS_VOID (optype)) + { + werror (E_CAST_ZERO); + return op; + } + + if (IS_ITEMP (op) && IS_ARRAY (OP_SYMBOL (op)->type)) + { + geniCodeArray2Ptr (op); + } + + /* if the operand is already the desired type then do nothing */ + if (compareType (type, optype) == 1) + { + if (IS_PTR (type) && IS_CONSTANT (opetype) && !IS_CONSTANT (getSpec(type))) + op->isConstElimnated = 1; + return op; + } + + /* if this is a literal then just change the type & return */ + if (IS_LITERAL (opetype) && op->type == VALUE && !IS_PTR (type) && !IS_PTR (optype)) + { + return operandFromValue (valCastLiteral (type, operandLitValue (op), operandLitValueUll (op))); + } + + checkPtrCast (type, optype, implicit, IS_LITERAL (opetype) && !operandLitValue (op)); + + ic = newiCode (CAST, operandFromLink (type), geniCodeRValue (op, FALSE)); + IC_RESULT (ic) = newiTempOperand (type, 0); + + restype = getSpec (operandType (IC_RESULT (ic))); + /* Convert cast to _Bool bitfield members to casts to _Bool. */ + if (SPEC_NOUN (restype) == V_BBITFIELD) + SPEC_NOUN (restype) = V_BOOL; + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeLabel - will create a Label */ +/*-----------------------------------------------------------------*/ +void +geniCodeLabel (symbol * label) +{ + iCode *ic; + + ic = newiCodeLabelGoto (LABEL, label); + ADDTOCHAIN (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeGoto - will create a Goto */ +/*-----------------------------------------------------------------*/ +void +geniCodeGoto (symbol * label) +{ + iCode *ic; + + ic = newiCodeLabelGoto (GOTO, label); + ADDTOCHAIN (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeMultiply - gen intermediate code for multiplication */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeMultiply (operand * left, operand * right, RESULT_TYPE resultType) +{ + iCode *ic; + int p2 = 0; + sym_link *resType; + LRTYPE; + + /* if they are both literal then we know the result */ + if (IS_LITERAL (letype) && IS_LITERAL (retype)) + return operandFromValue (valMult (OP_VALUE (left), OP_VALUE (right))); + + if (IS_LITERAL (retype)) + { + p2 = powof2 ((TYPE_TARGET_ULONG) ulFromVal (OP_VALUE (right))); + } + + resType = usualBinaryConversions (&left, &right, resultType, '*'); + rtype = operandType (right); + retype = getSpec (rtype); + ltype = operandType (left); + letype = getSpec (ltype); + + /* if the right is a literal & power of 2 */ + /* then make it a left shift */ + /* code generated for 1 byte * 1 byte literal = 2 bytes result is more + efficient in most cases than 2 bytes result = 2 bytes << literal + if port has 1 byte muldiv */ + if ((p2 > 0) && !IS_FLOAT (letype) && !IS_FIXED (letype) && + !((resultType == RESULT_TYPE_INT) && (getSize (resType) != getSize (ltype)) && !(TARGET_Z80_LIKE || TARGET_IS_STM8 && p2 == 1) /* Mimic old behaviour that tested port->muldiv, which was zero for stm8 and z80-like only. Someone should look into what really makes sense here. */) && + !TARGET_PIC_LIKE) /* don't shift for pic */ + { + if ((resultType == RESULT_TYPE_INT) && (getSize (resType) != getSize (ltype))) + { + /* LEFT_OP need same size for left and result, */ + left = geniCodeCast (resType, left, TRUE); + ltype = operandType (left); + } + ic = newiCode (LEFT_OP, left, operandFromLit (p2)); /* left shift */ + } + else + { + /* if the size left or right > 1 then support routine */ + if (getSize (ltype) > 1 || getSize (rtype) > 1) + { + if (IS_LITERAL (retype)) + ic = newiCode ('*', right, left); /* multiplication by support routine with one literal */ + else + ic = newiCode ('*', left, right); /* multiplication by support routine */ + ic->supportRtn = 1; + } + else + { + ic = newiCode ('*', left, right); /* normal multiplication */ + } + } + IC_RESULT (ic) = newiTempOperand (resType, 1); + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +static operand * +geniCodeAdd (operand *left, operand *right, RESULT_TYPE resultType, int lvl); +static operand * +geniCodeLogic (operand *left, operand *right, int op, ast *tree); +operand * +geniCodeRightShift (operand *left, operand *right); + +/*-----------------------------------------------------------------*/ +/* geniCodeDivision - gen intermediate code for division */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeDivision (operand *left, operand *right, RESULT_TYPE resultType, bool ptrdiffdiv) +{ + iCode *ic; + int p2 = 0; + sym_link *resType = usualBinaryConversions (&left, &right, resultType, '/'); + sym_link *rtype = operandType (right); + sym_link *retype = getSpec (rtype); + sym_link *ltype = operandType (left); + sym_link *letype = getSpec (ltype); + +/* if the right is a literal & power of 2 and left is unsigned then + make it a right shift. + For pointer division, there can be no remainder, so we can make + it a right shift, too. */ + + if (IS_LITERAL (retype) && + (!IS_FLOAT (letype) && !IS_FIXED (letype) && IS_UNSIGNED (letype) || ptrdiffdiv) && + ((p2 = powof2 ((TYPE_TARGET_ULONG) ulFromVal (OP_VALUE (right)))) > 0)) + { + ic = newiCode (RIGHT_OP, left, operandFromLit (p2)); /* right shift */ + } + /* if the right is a literal & power of 2 + and left is signed then make it a conditional addition + followed by right shift */ + else if (IS_LITERAL (retype) && + !IS_FLOAT (letype) && + !IS_FIXED (letype) && !IS_UNSIGNED (letype) && + ((p2 = powof2 ((TYPE_TARGET_ULONG) ulFromVal (OP_VALUE (right)))) > 0) && + (TARGET_Z80_LIKE || TARGET_HC08_LIKE)) + { + operand *tmp; + symbol *label = newiTempLabel (NULL); + + tmp = newiTempOperand (ltype, 0); + geniCodeAssign (tmp, left, 0, 0); + + ic = newiCodeCondition (geniCodeLogic (tmp, operandFromLit (0), '<', 0), 0, label); + ADDTOCHAIN (ic); + + geniCodeAssign (tmp, geniCodeAdd (tmp, operandFromLit ((1 << p2) - 1), 0, 0), 0, 0); + geniCodeLabel (label); + return (geniCodeCast (resType, geniCodeRightShift (tmp, operandFromLit (p2)), TRUE)); + } + + else + { + ic = newiCode ('/', left, right); /* normal division */ + /* if the size left or right > 1 then support routine */ + if (getSize (ltype) > 1 || getSize (rtype) > 1) + ic->supportRtn = 1; + } + IC_RESULT (ic) = newiTempOperand (resType, 0); + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeModulus - gen intermediate code for modulus */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeModulus (operand * left, operand * right, RESULT_TYPE resultType) +{ + iCode *ic; + sym_link *resType; + LRTYPE; + + /* if they are both literal then we know the result */ + if (IS_LITERAL (letype) && IS_LITERAL (retype)) + return operandFromValue (valMod (OP_VALUE (left), OP_VALUE (right))); + + resType = usualBinaryConversions (&left, &right, resultType, '%'); + + /* now they are the same size */ + ic = newiCode ('%', left, right); + + /* if the size left or right > 1 then support routine */ + if (getSize (ltype) > 1 || getSize (rtype) > 1) + ic->supportRtn = 1; + IC_RESULT (ic) = newiTempOperand (resType, 0); + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodePtrPtrSubtract - subtracts pointer from pointer */ +/*-----------------------------------------------------------------*/ +operand * +geniCodePtrPtrSubtract (operand * left, operand * right) +{ + iCode *ic; + operand *result; + LRTYPE; + + /* if they are both literals then */ + if (IS_LITERAL (letype) && IS_LITERAL (retype)) + { + result = operandFromValue (valMinus (OP_VALUE (left), OP_VALUE (right))); + goto subtractExit; + } + + ic = newiCode ('-', left, right); + + IC_RESULT (ic) = result = newiTempOperand (newIntLink (), 1); + ADDTOCHAIN (ic); + +subtractExit: + if (IS_VOID (ltype->next) || IS_VOID (rtype->next)) + { + return result; + } + + return geniCodeDivision (result, operandFromLit (getSize (ltype->next)), FALSE, true); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeSubtract - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeSubtract (operand * left, operand * right, RESULT_TYPE resultType) +{ + iCode *ic; + int isarray = 0; + sym_link *resType; + LRTYPE; + + /* if they are both pointers then */ + if ((IS_PTR (ltype) || IS_ARRAY (ltype)) && (IS_PTR (rtype) || IS_ARRAY (rtype))) + return geniCodePtrPtrSubtract (left, right); + + /* if they are both literal then we know the result */ + if (IS_LITERAL (letype) && IS_LITERAL (retype) && left->isLiteral && right->isLiteral) + return operandFromValue (valMinus (OP_VALUE (left), OP_VALUE (right))); + + /* if left is an array or pointer */ + if (IS_PTR (ltype) || IS_ARRAY (ltype)) + { + isarray = left->isaddr; + right = geniCodeMultiply (right, + operandFromLit (getSize (ltype->next)), + (getArraySizePtr (left) >= INTSIZE) ? RESULT_TYPE_INT : RESULT_TYPE_CHAR); + resType = copyLinkChain (IS_ARRAY (ltype) ? ltype->next : ltype); + } + else + { /* make them the same size */ + resType = usualBinaryConversions (&left, &right, resultType, '-'); + } + + ic = newiCode ('-', left, right); + + IC_RESULT (ic) = newiTempOperand (resType, 1); + IC_RESULT (ic)->isaddr = (isarray ? 1 : 0); + + /* if left or right is a float */ + if (IS_FLOAT (ltype) || IS_FLOAT (rtype) || IS_FIXED (ltype) || IS_FIXED (rtype)) + ic->supportRtn = 1; + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeAdd - generates iCode for addition */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeAdd (operand *left, operand *right, RESULT_TYPE resultType, int lvl) +{ + iCode *ic; + sym_link *resType; + unsigned int nBytes; + operand *size; + int isarray = 0; + bool indexUnsigned; + LRTYPE; + + /* if the right side is LITERAL zero */ + /* return the left side */ + if (IS_LITERAL (retype) && right->isLiteral && !floatFromVal (valFromType (rtype))) + return left; + + /* if left is literal zero return right */ + if (!IS_PTR (ltype) && IS_LITERAL (letype) && left->isLiteral && !floatFromVal (valFromType (ltype))) + return right; + + /* if left is a pointer then size */ + if (IS_PTR (ltype) || IS_ARRAY (ltype)) + { + unsigned int ptrSize; + isarray = left->isaddr; + nBytes = getSize (ltype->next); + ptrSize = getArraySizePtr (left); // works for both arrays and pointers + + if (nBytes == 0 && !IS_VOID (ltype->next)) + werror (E_UNKNOWN_SIZE, IS_SYMOP (left) ? OP_SYMBOL (left)->name : ""); + // there is no need to multiply with 1 + if (nBytes != 1) + { + size = operandFromLit (nBytes); + SPEC_USIGN (getSpec (operandType (size))) = 1; + indexUnsigned = IS_UNSIGNED (getSpec (operandType (right))); + if (!indexUnsigned && ptrSize > INTSIZE) + { + SPEC_LONG (getSpec (operandType (size))) = 1; + SPEC_CVAL (getSpec (operandType (size))).v_ulong = nBytes; + } + right = geniCodeMultiply (right, size, (ptrSize >= INTSIZE) ? RESULT_TYPE_INT : RESULT_TYPE_CHAR); + /* Even if right is a 'unsigned char', + the result will be a 'signed int' due to the promotion rules. + It doesn't make sense when accessing arrays, so let's fix it here: */ + if (indexUnsigned) + SPEC_USIGN (getSpec (operandType (right))) = 1; + } + + if (ptrSize > getSize (rtype) && !IS_UNSIGNED (retype)) + { + sym_link *type = 0; + + switch(ptrSize) + { + case 2: + type = newIntLink(); + break; + case 3: + case 4: + type = newLongLink(); + break; + default: + wassert(0); + } + right = geniCodeCast (type, right, TRUE); + } + + resType = copyLinkChain (ltype); + } + else + { // make them the same size + resType = usualBinaryConversions (&left, &right, resultType, '+'); + } + + /* if they are both literals then we know */ + if (IS_LITERAL (letype) && IS_LITERAL (retype) && left->isLiteral && right->isLiteral) + { + value *scaledRight = valFromType (rtype); + if (IS_PTR (ltype)) + scaledRight = valMult (scaledRight, valueFromLit (getSize (ltype->next))); + return operandFromValue (valPlus (valFromType (ltype), scaledRight)); + } + + ic = newiCode ('+', left, right); + + IC_RESULT (ic) = newiTempOperand (resType, 1); + IC_RESULT (ic)->isaddr = (isarray ? 1 : 0); + + /* if left or right is a float then support routine */ + if (IS_FLOAT (ltype) || IS_FLOAT (rtype) || IS_FIXED (ltype) || IS_FIXED (rtype)) + ic->supportRtn = 1; + + ADDTOCHAIN (ic); + + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* aggrToPtr - changes an "aggregate" to a "pointer to aggregate" */ +/*-----------------------------------------------------------------*/ +sym_link * +aggrToPtr (sym_link * type, bool force) +{ + sym_link *etype; + sym_link *ptype; + + if (IS_PTR (type) && !force) + return type; + + etype = getSpec (type); + ptype = newLink (DECLARATOR); + + ptype->next = type; + + /* set the pointer depending on the storage class */ + DCL_TYPE (ptype) = PTR_TYPE (SPEC_OCLS (etype)); + return ptype; +} + +/*------------------------------------------------------------------*/ +/* aggrToPtrDclType - like aggrToPtr, but returns only the DCL_TYPE */ +/*------------------------------------------------------------------*/ +int +aggrToPtrDclType (sym_link * type, bool force) +{ + if (IS_PTR (type) && !force) + return DCL_TYPE (type); + + /* return the pointer depending on the storage class */ + return PTR_TYPE (SPEC_OCLS (getSpec (type))); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeArray2Ptr - array to pointer */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeArray2Ptr (operand * op) +{ + sym_link *optype = operandType (op); + sym_link *opetype = getSpec (optype); + + /* set the pointer depending on the storage class */ + DCL_TYPE (optype) = PTR_TYPE (SPEC_OCLS (opetype)); + /* now remove the storage class from this itemp */ + SPEC_SCLS (opetype) = S_FIXED; + SPEC_OCLS (opetype) = NULL; + + op->isaddr = 0; + return op; +} + + +/*-----------------------------------------------------------------*/ +/* geniCodeArray - array access */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeArray (operand * left, operand * right, int lvl) +{ + iCode *ic; + operand *size; + sym_link *ltype = operandType (left); + bool indexUnsigned; + RESULT_TYPE resultType; + + resultType = (getArraySizePtr (left) >= INTSIZE) ? RESULT_TYPE_INT : RESULT_TYPE_CHAR; + if (DCL_ELEM (ltype)) + { + if (DCL_ELEM (ltype) * getSize (ltype->next) <= 255) + resultType = RESULT_TYPE_CHAR; + } + + if (IS_PTR (ltype)) + { + if (IS_PTR (ltype->next) && left->isaddr) + { + left = geniCodeRValue (left, FALSE); + } + + return geniCodeDerefPtr (geniCodeAdd (left, right, resultType, lvl), lvl); + } + size = operandFromLit (getSize (ltype->next)); + SPEC_USIGN (getSpec (operandType (size))) = 1; + indexUnsigned = IS_UNSIGNED (getSpec (operandType (right))); + right = geniCodeMultiply (right, size, resultType); + /* Even if right is a 'unsigned char', the result will be a 'signed int' due to the promotion rules. + It doesn't make sense when accessing arrays, so let's fix it here: */ + if (indexUnsigned) + SPEC_USIGN (getSpec (operandType (right))) = 1; + /* we can check for limits here */ + /* already done in SDCCast.c + if (isOperandLiteral (right) && + IS_ARRAY (ltype) && + DCL_ELEM (ltype) && + (operandLitValue (right) / getSize (ltype->next)) >= DCL_ELEM (ltype)) + { + werror (W_IDX_OUT_OF_BOUNDS, + (int) operandLitValue (right) / getSize (ltype->next), + DCL_ELEM (ltype)); + } + */ + + ic = newiCode ('+', left, right); + + IC_RESULT (ic) = newiTempOperand (((IS_PTR (ltype) && !IS_AGGREGATE (ltype->next) && !IS_PTR (ltype->next)) || + (IS_ARRAY (ltype) && IS_FUNCPTR (ltype->next))) ? ltype : ltype->next, 0); + + if (!IS_AGGREGATE (ltype->next)) + { + IC_RESULT (ic)->isaddr = 1; + IC_RESULT (ic)->aggr2ptr = 1; + } + ADDTOCHAIN (ic); + + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeStruct - generates intermediate code for structures */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeStruct (operand * left, operand * right, bool islval) +{ + iCode *ic; + sym_link *type = operandType (left); + sym_link *etype = getSpec (type); + sym_link *rtype, *retype; + symbol *element = getStructElement (SPEC_STRUCT (etype), OP_SYMBOL (right)); + + wassert (IS_SYMOP (right)); + + wassert (IS_STRUCT (type) || ((IS_PTR (type) || IS_ARRAY (type)) && IS_STRUCT (type->next))); + + /* add the offset */ + ic = newiCode ('+', left, operandFromLit (element->offset)); + + IC_RESULT (ic) = newiTempOperand (element->type, 0); + + /* preserve the storage & output class of the struct */ + /* as well as the volatile attribute */ + rtype = operandType (IC_RESULT (ic)); + retype = getSpec (rtype); + SPEC_SCLS (retype) = SPEC_SCLS (etype); + SPEC_OCLS (retype) = SPEC_OCLS (etype); + + if (IS_PTR (element->type)) + { + DCL_PTR_CONST (rtype) |= DCL_PTR_CONST (element->type); + DCL_PTR_VOLATILE (rtype) |= DCL_PTR_VOLATILE (element->type); + DCL_PTR_RESTRICT (rtype) |= DCL_PTR_RESTRICT (element->type); + setOperandType (IC_RESULT (ic), aggrToPtr (operandType (IC_RESULT (ic)), TRUE)); + } + else + { + SPEC_CONST (retype) |= SPEC_CONST (etype); + /*Do not preserve volatile */ + SPEC_RESTRICT (retype) |= SPEC_RESTRICT (etype); + } + + IC_RESULT (ic)->isaddr = (!IS_AGGREGATE (element->type)); + + ADDTOCHAIN (ic); + return (islval ? IC_RESULT (ic) : geniCodeRValue (IC_RESULT (ic), TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* geniCodePostInc - generate int code for Post increment */ +/*-----------------------------------------------------------------*/ +operand * +geniCodePostInc (operand * op) +{ + iCode *ic; + operand *rOp; + sym_link *optype = operandType (op); + operand *result; + operand *rv = (IS_ITEMP (op) ? geniCodeRValue (op, (!op->aggr2ptr && IS_PTR (optype)) ? TRUE : FALSE) : op); + sym_link *rvtype = operandType (rv); + int size = 0; + operand *srcOp = rv; + + /* if this is not an address we have trouble */ + if (!op->isaddr) + { + werror (E_LVALUE_REQUIRED, "++"); + return op; + } + + rOp = newiTempOperand (rvtype, 0); + OP_SYMBOL (rOp)->noSpilLoc = 1; + + if (IS_ITEMP (rv)) + OP_SYMBOL (rv)->noSpilLoc = 1; + + geniCodeAssign (rOp, rv, 0, 0); + + /* If rv is volatile, we can only read it once, and we've just */ + /* done that, so use the copy in rOp instead to avoid reading */ + /* it again. */ + if (isOperandVolatile (rv, FALSE)) + srcOp = rOp; + + size = (IS_PTR (rvtype) ? getSize (rvtype->next) : 1); + if (size == 0) + werror (W_SIZEOF_VOID); + if (IS_FLOAT (rvtype)) + ic = newiCode ('+', srcOp, operandFromValue (constFloatVal ("1.0"))); + else if (IS_FIXED16X16 (rvtype)) + ic = newiCode ('+', srcOp, operandFromValue (constFixed16x16Val ("1.0"))); + else if (IS_BOOL (rvtype)) + ic = newiCode ('=', NULL, operandFromLit (1)); + else + ic = newiCode ('+', srcOp, operandFromLit (size)); + + IC_RESULT (ic) = result = newiTempOperand (rvtype, 0); + ADDTOCHAIN (ic); + + geniCodeAssign (op, result, 0, 0); + + return rOp; + +} + +/*-----------------------------------------------------------------*/ +/* geniCodePreInc - generate code for preIncrement */ +/*-----------------------------------------------------------------*/ +operand * +geniCodePreInc (operand * op, bool lvalue) +{ + iCode *ic; + sym_link *optype = operandType (op); + operand *rop = (IS_ITEMP (op) ? geniCodeRValue (op, ((!op->aggr2ptr && IS_PTR (optype)) ? TRUE : FALSE)) : op); + sym_link *roptype = operandType (rop); + operand *result; + int size = 0; + + if (!op->isaddr) + { + werror (E_LVALUE_REQUIRED, "++"); + return op; + } + + size = (IS_PTR (roptype) ? getSize (roptype->next) : 1); + if (size == 0) + werror (W_SIZEOF_VOID); + if (IS_FLOAT (roptype)) + ic = newiCode ('+', rop, operandFromValue (constFloatVal ("1.0"))); + else if (IS_FIXED16X16 (roptype)) + ic = newiCode ('+', rop, operandFromValue (constFixed16x16Val ("1.0"))); + else if (IS_BOOL (roptype)) + ic = newiCode ('=', NULL, operandFromLit (1)); + else + ic = newiCode ('+', rop, operandFromLit (size)); + IC_RESULT (ic) = result = newiTempOperand (roptype, 0); + ADDTOCHAIN (ic); + + (void) geniCodeAssign (op, result, 0, 0); + if (lvalue || (IS_TRUE_SYMOP (op) && !isOperandVolatile (op, FALSE)) || IS_BITVAR (optype)) + return op; + else + return result; +} + +/*-----------------------------------------------------------------*/ +/* geniCodePostDec - generates code for Post decrement */ +/*-----------------------------------------------------------------*/ +operand * +geniCodePostDec (operand * op) +{ + iCode *ic; + operand *rOp; + sym_link *optype = operandType (op); + operand *result; + operand *rv = (IS_ITEMP (op) ? geniCodeRValue (op, ((!op->aggr2ptr && IS_PTR (optype)) ? TRUE : FALSE)) : op); + sym_link *rvtype = operandType (rv); + int size = 0; + operand *srcOp = rv; + + /* if this is not an address we have trouble */ + if (!op->isaddr) + { + werror (E_LVALUE_REQUIRED, "--"); + return op; + } + + rOp = newiTempOperand (rvtype, 0); + OP_SYMBOL (rOp)->noSpilLoc = 1; + + if (IS_ITEMP (rv)) + OP_SYMBOL (rv)->noSpilLoc = 1; + + geniCodeAssign (rOp, rv, 0, 0); + + /* If rv is volatile, we can only read it once, and we've just */ + /* done that, so use the copy in rOp instead to avoid reading */ + /* it again. */ + if (isOperandVolatile (rv, FALSE)) + srcOp = rOp; + + size = (IS_PTR (rvtype) ? getSize (rvtype->next) : 1); + if (size == 0) + werror (W_SIZEOF_VOID); + if (IS_FLOAT (rvtype)) + ic = newiCode ('-', srcOp, operandFromValue (constFloatVal ("1.0"))); + else if (IS_FIXED16X16 (rvtype)) + ic = newiCode ('-', srcOp, operandFromValue (constFixed16x16Val ("1.0"))); + else if (IS_BOOL (rvtype)) + ic = newiCode ('!', srcOp, 0); + else + ic = newiCode ('-', srcOp, operandFromLit (size)); + + IC_RESULT (ic) = result = newiTempOperand (rvtype, 0); + ADDTOCHAIN (ic); + + geniCodeAssign (op, result, 0, 0); + + return rOp; + +} + +/*-----------------------------------------------------------------*/ +/* geniCodePreDec - generate code for pre decrement */ +/*-----------------------------------------------------------------*/ +operand * +geniCodePreDec (operand * op, bool lvalue) +{ + iCode *ic; + sym_link *optype = operandType (op); + operand *rop = (IS_ITEMP (op) ? geniCodeRValue (op, ((!op->aggr2ptr && IS_PTR (optype)) ? TRUE : FALSE)) : op); + sym_link *roptype = operandType (rop); + operand *result; + int size = 0; + + if (!op->isaddr) + { + werror (E_LVALUE_REQUIRED, "--"); + return op; + } + + size = (IS_PTR (roptype) ? getSize (roptype->next) : 1); + if (size == 0) + werror (W_SIZEOF_VOID); + if (IS_FLOAT (roptype)) + ic = newiCode ('-', rop, operandFromValue (constFloatVal ("1.0"))); + else if (IS_FIXED16X16 (roptype)) + ic = newiCode ('-', rop, operandFromValue (constFixed16x16Val ("1.0"))); + else if (IS_BOOL (roptype)) + ic = newiCode ('!', rop, 0); + else + ic = newiCode ('-', rop, operandFromLit (size)); + IC_RESULT (ic) = result = newiTempOperand (roptype, 0); + ADDTOCHAIN (ic); + + (void) geniCodeAssign (op, result, 0, 0); + if (lvalue || (IS_TRUE_SYMOP (op) && !isOperandVolatile (op, FALSE)) || IS_BITVAR (optype)) + return op; + else + return result; +} + + +/*-----------------------------------------------------------------*/ +/* geniCodeBitwise - gen int code for bitWise operators */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeBitwise (operand * left, operand * right, int oper, sym_link * resType) +{ + iCode *ic; + + left = geniCodeCast (resType, left, TRUE); + right = geniCodeCast (resType, right, TRUE); + + ic = newiCode (oper, left, right); + IC_RESULT (ic) = newiTempOperand (resType, 0); + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeAddressOf - gens icode for '&' address of operator */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeAddressOf (operand * op) +{ + iCode *ic; + sym_link *p; + sym_link *optype = operandType (op); + sym_link *opetype = getSpec (optype); + + if (IS_ITEMP (op) && IS_PTR (optype)) + { + op = operandFromOperand (op); + op->isaddr = 0; + return op; + } + + /* lvalue check already done in decorateType */ + /* this must be a lvalue */ +/* if (!op->isaddr && !IS_AGGREGATE(optype)) { */ +/* werror (E_LVALUE_REQUIRED,"&"); */ +/* return op; */ +/* } */ + + p = newLink (DECLARATOR); + + /* set the pointer depending on the storage class */ + DCL_TYPE (p) = PTR_TYPE (SPEC_OCLS (opetype)); + + p->next = copyLinkChain (optype); + + /* if already a temp */ + if (IS_ITEMP (op)) + { + setOperandType (op, p); + op->isaddr = 0; + return op; + } + + /* otherwise make this of the type coming in */ + ic = newiCode (ADDRESS_OF, op, operandFromLit (0)); + IC_RESULT (ic) = newiTempOperand (p, 1); + IC_RESULT (ic)->isaddr = 0; + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* setOClass - sets the output class depending on the pointer type */ +/*-----------------------------------------------------------------*/ +void +setOClass (sym_link * ptr, sym_link * spec) +{ + switch (DCL_TYPE (ptr)) + { + case POINTER: + SPEC_OCLS (spec) = data; + break; + + case GPOINTER: + SPEC_OCLS (spec) = generic; + break; + + case FPOINTER: + SPEC_OCLS (spec) = xdata; + break; + + case CPOINTER: + SPEC_OCLS (spec) = code; + break; + + case IPOINTER: + SPEC_OCLS (spec) = idata; + break; + + case PPOINTER: + SPEC_OCLS (spec) = xstack; + break; + + case EEPPOINTER: + SPEC_OCLS (spec) = eeprom; + break; + + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* geniCodeDerefPtr - dereference pointer with '*' */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeDerefPtr (operand * op, int lvl) +{ + sym_link *rtype, *retype; + sym_link *optype = operandType (op); + + // if this is an array then array access + if (IS_ARRAY (optype)) + { + // don't worry, this will be optimized out later + return geniCodeArray (op, operandFromLit (0), lvl); + } + + // just in case someone screws up + wassert (IS_PTR (optype)); + + if (IS_TRUE_SYMOP (op)) + { + op->isaddr = 1; + op = geniCodeRValue (op, TRUE); + } + else if (IS_OP_LITERAL (op)) + { + /* To avoid problems converting a dereferenced literal pointer */ + /* back and forth between lvalue and rvalue formats, replace */ + /* the literal pointer with an iTemp and assign the literal */ + /* value to the iTemp. */ + iCode *ic; + operand *iop = newiTempOperand (optype, 0); + SPEC_SCLS (OP_SYM_ETYPE (iop)) = S_AUTO; /* remove S_LITERAL */ + iop->isaddr = 0; /* assign to the iTemp itself */ + ic = newiCode ('=', NULL, op); + IC_RESULT (ic) = iop; + ADDTOCHAIN (ic); + op = operandFromOperand (iop); /* now use the iTemp as operand */ + optype = operandType (op); + } + + /* now get rid of the pointer part */ + if (isLvaluereq (lvl) && IS_ITEMP (op)) + { + retype = getSpec (rtype = copyLinkChain (optype)); + } + else + { + retype = getSpec (rtype = copyLinkChain (optype->next)); + /* outputclass needs 2b updated */ + setOClass (optype, retype); + } + + op->isGptr = IS_GENPTR (optype); + + op->isaddr = (IS_PTR (rtype) || + IS_STRUCT (rtype) || IS_INT (rtype) || IS_BOOL (rtype) || IS_CHAR (rtype) || IS_FLOAT (rtype) || IS_FIXED (rtype)); + + if (!isLvaluereq (lvl)) + op = geniCodeRValue (op, TRUE); + + if (IS_DECL (rtype)) + { + DCL_PTR_ADDRSPACE (rtype) = 0; + DCL_PTR_VOLATILE (rtype) = 0; + } + else + { + SPEC_ADDRSPACE (rtype) = 0; + SPEC_VOLATILE (rtype) = 0; + } + setOperandType (op, rtype); + + return op; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeUnaryMinus - does a unary minus of the operand */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeUnaryMinus (operand * op) +{ + iCode *ic; + sym_link *optype = operandType (op); + + if (IS_LITERAL (optype)) + return operandFromLit (-floatFromVal (OP_VALUE (op))); + + ic = newiCode (UNARYMINUS, op, NULL); + IC_RESULT (ic) = newiTempOperand (optype, 0); + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeLeftShift - gen i code for left shift */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeLeftShift (operand * left, operand * right, RESULT_TYPE resultType) +{ + iCode *ic; + sym_link *resType; + + resType = usualBinaryConversions (&left, &right, resultType, LEFT_OP); + ic = newiCode(LEFT_OP, left, right); + IC_RESULT (ic) = newiTempOperand (resType, 0); + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeRightShift - gen i code for right shift */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeRightShift (operand * left, operand * right) +{ + iCode *ic; + + ic = newiCode (RIGHT_OP, left, right); + IC_RESULT (ic) = newiTempOperand (operandType (left), 0); + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeLogic- logic code */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeLogic (operand * left, operand * right, int op, ast * tree) +{ + iCode *ic; + sym_link *ctype, *ttype; + sym_link *rtype = operandType (right); + sym_link *ltype = operandType (left); + + /* left is integral type and right is literal then + check if the literal value is within bounds */ + if (IS_INTEGRAL (ltype) && IS_VALOP (right) && IS_LITERAL (rtype)) + { + CCR_RESULT ccr_result = checkConstantRange (ltype, rtype, op, FALSE); + switch (ccr_result) + { + case CCR_ALWAYS_TRUE: + case CCR_ALWAYS_FALSE: + werror (W_COMP_RANGE, "true resp. false"); + return operandFromLit (ccr_result == CCR_ALWAYS_TRUE ? 1 : 0); + default: + break; + } + } + + /* Avoid expensive comparisons when the type of the constant is bigger than the type of the non-const operand */ + if (IS_INTEGRAL (ltype) && IS_LITERAL (rtype) && getSize (ltype) < getSize (rtype)) + right->svt.valOperand = valCastLiteral (ltype, operandLitValue (right), operandLitValueUll (right)); + if (IS_INTEGRAL (rtype) && IS_LITERAL (ltype) && getSize (rtype) < getSize (ltype)) + left->svt.valOperand = valCastLiteral (rtype, operandLitValue (left), operandLitValueUll (left)); + + /* if one operand is a pointer and the other is a literal generic void pointer, + change the type of the literal generic void pointer to match the other pointer */ + if (IS_GENPTR (ltype) && IS_VOID (ltype->next) && IS_ITEMP (left) && IS_PTR (rtype) && !IS_GENPTR (rtype)) + { + /* find left's definition */ + ic = (iCode *) setFirstItem (iCodeChain); + while (ic) + { + if (((ic->op == CAST) || (ic->op == '=')) && isOperandEqual (left, IC_RESULT (ic))) + break; + else + ic = setNextItem (iCodeChain); + } + /* if casting literal to generic pointer, then cast to rtype instead */ + if (ic && (ic->op == CAST) && isOperandLiteral (IC_RIGHT (ic))) + { + left = operandFromValue (valCastLiteral (rtype, operandLitValue (IC_RIGHT (ic)),operandLitValueUll (IC_RIGHT (ic)))); + ltype = operandType (left); + } + } + if (IS_GENPTR (rtype) && IS_VOID (rtype->next) && IS_ITEMP (right) && IS_PTR (ltype) && !IS_GENPTR (ltype)) + { + /* find right's definition */ + ic = (iCode *) setFirstItem (iCodeChain); + while (ic) + { + if (((ic->op == CAST) || (ic->op == '=')) && isOperandEqual (right, IC_RESULT (ic))) + break; + else + ic = setNextItem (iCodeChain); + } + /* if casting literal to generic pointer, then cast to rtype instead */ + if (ic && (ic->op == CAST) && isOperandLiteral (IC_RIGHT (ic))) + { + right = operandFromValue (valCastLiteral (ltype, operandLitValue (IC_RIGHT (ic)), operandLitValueUll (IC_RIGHT (ic)))); + rtype = operandType (right); + } + } + + ctype = usualBinaryConversions (&left, &right, RESULT_TYPE_BOOL, op); + + ic = newiCode (op, left, right); + /* store 0 or 1 in result */ + ttype = (tree && IS_BOOLEAN (tree->ftype)) ? newBoolLink () : newCharLink (); + IC_RESULT (ic) = newiTempOperand (ttype, 1); + + /* if comparing float + and not a '==' || '!=' || '&&' || '||' (these + will be inlined */ + if (IS_FLOAT (ctype) && op != EQ_OP && op != NE_OP && op != AND_OP && op != OR_OP) + ic->supportRtn = 1; + + /* if comparing a fixed type use support functions */ + if (IS_FIXED (ctype)) + ic->supportRtn = 1; + + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeLogicAndOr - && || operations */ +/*-----------------------------------------------------------------*/ +static operand * +geniCodeLogicAndOr (ast * tree, int lvl) +{ + iCode *ic; + sym_link *type; + symbol *falseLabel = newiTempLabel (NULL); + symbol *trueLabel = newiTempLabel (NULL); + symbol *exitLabel = newiTempLabel (NULL); + operand *op, *result, *condition; + + /* AND_OP and OR_OP are no longer generated because of bug-905492. + They can be reenabled by executing the following block. If you find + a decent optimization you could start right here: + */ +#if 0 + if (0) + { + operand *leftOp, *rightOp; + + leftOp = geniCodeRValue (ast2iCode (tree->left, lvl + 1), FALSE); + rightOp = geniCodeRValue (ast2iCode (tree->right, lvl + 1), FALSE); + + return geniCodeLogic (leftOp, rightOp, tree->opval.op); + } +#endif + + /* generate two IFX for the '&&' or '||' op */ + + /* evaluate left operand */ + condition = ast2iCode (tree->left, lvl + 1); + op = geniCodeRValue (condition, FALSE); + + /* test left operand */ + if (tree->opval.op == AND_OP) + ic = newiCodeCondition (op, NULL, falseLabel); + else /* OR_OP */ + ic = newiCodeCondition (op, trueLabel, NULL); + ADDTOCHAIN (ic); + + /* evaluate right operand */ + condition = ast2iCode (tree->right, lvl + 1); + op = geniCodeRValue (condition, FALSE); + + /* test right operand */ + ic = newiCodeCondition (op, trueLabel, NULL); + ADDTOCHAIN (ic); + + /* store 0 or 1 in result */ + type = (IS_BOOLEAN (tree->ftype)) ? newBoolLink () : newCharLink (); + result = newiTempOperand (type, 1); + + geniCodeLabel (falseLabel); + geniCodeAssign (result, operandFromLit (0), 0, 0); + /* generate an unconditional goto */ + geniCodeGoto (exitLabel); + + geniCodeLabel (trueLabel); + geniCodeAssign (result, operandFromLit (1), 0, 0); + + geniCodeLabel (exitLabel); + + return result; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeUnary - for a generic unary operation */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeUnary (operand * op, int oper, sym_link * resType) +{ + iCode *ic = newiCode (oper, op, NULL); + + IC_RESULT (ic) = newiTempOperand (resType, 0); + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeBinary - for a generic binary operation */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeBinary (operand * left, operand * right, int oper, sym_link * resType) +{ + iCode *ic = newiCode (oper, left, right); + + IC_RESULT (ic) = newiTempOperand (resType, 0); + ADDTOCHAIN (ic); + return IC_RESULT (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeConditional - geniCode for '?' ':' operation */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeConditional (ast * tree, int lvl) +{ + iCode *ic; + symbol *falseLabel = newiTempLabel (NULL); + symbol *exitLabel = newiTempLabel (NULL); + ast *astTrue = tree->right->left; + ast *astFalse = tree->right->right; + operand *cond = ast2iCode (tree->left, lvl + 1); + operand *result = newiTempOperand (tree->ftype, 0); + operand *opTrue, *opFalse; + + ic = newiCodeCondition (geniCodeRValue (cond, FALSE), NULL, falseLabel); + ADDTOCHAIN (ic); + + opTrue = ast2iCode (astTrue, lvl + 1); + + /* move the value to the new operand */ + geniCodeAssign (result, geniCodeRValue (opTrue, FALSE), 0, 0); + + /* generate an unconditional goto */ + geniCodeGoto (exitLabel); + + /* now for the right side */ + geniCodeLabel (falseLabel); + + opFalse = ast2iCode (astFalse, lvl + 1); + geniCodeAssign (result, geniCodeRValue (opFalse, FALSE), 0, 0); + + /* create the exit label */ + geniCodeLabel (exitLabel); + + return result; +} + +/*-----------------------------------------------------------------*/ +/* checkTypes - check types for assignment */ +/*-----------------------------------------------------------------*/ +static operand * +checkTypes (operand * left, operand * right) +{ + sym_link *ltype = operandType (left); + sym_link *rtype = operandType (right); + bool always_cast = FALSE; + + /* if the left & right type don't exactly match */ + /* if pointer set then make sure the check is + done with the type & not the pointer */ + /* then cast rights type to left */ + + /* first check the type for pointer assignement */ + if (left->isaddr && IS_PTR (ltype) && IS_ITEMP (left) && compareType (ltype, rtype) <= 0) + { + if (left->aggr2ptr) + { + always_cast = TRUE; + } + else + { + ltype = ltype->next; + } + } + + /* left is integral type and right is literal then + check if the literal value is within bounds */ + if (IS_INTEGRAL (ltype) && right->type == VALUE && IS_LITERAL (rtype) && + checkConstantRange (ltype, rtype, '=', FALSE) == CCR_OVL) + { + werror (W_LIT_OVERFLOW); + } + + if (always_cast || compareType (ltype, rtype) == -1) + right = geniCodeCast (ltype, right, TRUE); + checkPtrQualifiers (ltype, rtype, !right->isConstElimnated); + return right; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeAssign (operand * left, operand * right, int nosupdate, int strictLval) +{ + iCode *ic; + sym_link *ltype; + + if (!left->isaddr && (!IS_ITEMP (left) || strictLval)) + { + werror (E_LVALUE_REQUIRED, "assignment"); + return left; + } + + right = checkTypes (left, right); + + /* If left is a true symbol & ! volatile + create an assignment to temporary for + the right & then assign this temporary + to the symbol. This is SSA (static single + assignment). Isn't it simple and folks have + published mountains of paper on it */ + if (IS_TRUE_SYMOP (left) && !isOperandVolatile (left, FALSE) && isOperandGlobal (left)) + { + symbol *sym = NULL; + operand *newRight; + sym_link *ltype = operandType (left); + + if (IS_TRUE_SYMOP (right)) + sym = OP_SYMBOL (right); + ic = newiCode ('=', NULL, right); + IC_RESULT (ic) = newRight = newiTempOperand (ltype, 0); + /* avoid double fetch from volatile right, see bug 1369874 */ + if (!isOperandVolatile (right, FALSE)) + SPIL_LOC (newRight) = sym; + right = newRight; + ADDTOCHAIN (ic); + } + + ic = newiCode ('=', NULL, right); + IC_RESULT (ic) = left; + ADDTOCHAIN (ic); + + /* if left isgptr flag is set then support + routine will be required */ + if (left->isGptr) + ic->supportRtn = 1; + + ic->nosupdate = nosupdate; + /* left could be a pointer assignment, + return the properly casted right instead */ + ltype = operandType (left); + if ((IS_PTR (ltype) && IS_BITVAR (ltype->next)) || IS_BITVAR (ltype)) + return left; + else + return right; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeDummyRead - generate code for dummy read */ +/*-----------------------------------------------------------------*/ +static void +geniCodeDummyRead (operand * op) +{ + iCode *ic; + sym_link *type = operandType (op); + + if (!IS_VOLATILE (type)) + return; + + ic = newiCode (DUMMY_READ_VOLATILE, NULL, op); + ADDTOCHAIN (ic); + + ic->nosupdate = 1; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeSEParms - generate code for side effecting fcalls */ +/*-----------------------------------------------------------------*/ +static void +geniCodeSEParms (ast * parms, int lvl) +{ + if (!parms) + return; + + if (IS_AST_PARAM (parms)) + { + geniCodeSEParms (parms->left, lvl); + geniCodeSEParms (parms->right, lvl); + return; + } + + /* hack don't like this but too lazy to think of + something better */ + if (IS_ADDRESS_OF_OP (parms)) + parms->left->lvalue = 1; + + if (IS_CAST_OP (parms) && IS_PTR (parms->ftype) && IS_ADDRESS_OF_OP (parms->right)) + parms->right->left->lvalue = 1; + + parms->opval.oprnd = geniCodeRValue (ast2iCode (parms, lvl + 1), FALSE); + + parms->type = EX_OPERAND; + AST_ARGREG (parms) = parms->etype ? SPEC_ARGREG (parms->etype) : SPEC_ARGREG (parms->ftype); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeParms - generates parameters */ +/*-----------------------------------------------------------------*/ +value * +geniCodeParms (ast * parms, value * argVals, int *iArg, int *stack, sym_link * ftype, int lvl) +{ + iCode *ic; + operand *pval; + + if (!parms) + return argVals; + + /* if this is a param node then do the left & right */ + if (parms->type == EX_OP && parms->opval.op == PARAM) + { + argVals = geniCodeParms (parms->left, argVals, iArg, stack, ftype, lvl); + argVals = geniCodeParms (parms->right, argVals, iArg, stack, ftype, lvl); + return argVals; + } + + /* get the parameter value */ + if (parms->type == EX_OPERAND) + pval = parms->opval.oprnd; + else + { + /* maybe this else should go away ?? */ + /* hack don't like this but too lazy to think of + something better */ + if (IS_ADDRESS_OF_OP (parms)) + parms->left->lvalue = 1; + + if (IS_CAST_OP (parms) && IS_PTR (parms->ftype) && IS_ADDRESS_OF_OP (parms->right)) + parms->right->left->lvalue = 1; + + pval = geniCodeRValue (ast2iCode (parms, lvl + 1), FALSE); + } + + /* if register parm then make it a send */ + if ((IS_REGPARM (parms->etype) && !IFFUNC_HASVARARGS (ftype)) || IFFUNC_ISBUILTIN (ftype)) + { + pval = checkTypes (operandFromValue (argVals), pval); + ic = newiCode (SEND, pval, NULL); + ic->argreg = SPEC_ARGREG (parms->etype); + ic->builtinSEND = FUNC_ISBUILTIN (ftype); + ADDTOCHAIN (ic); + } + else + { + /* now decide whether to push or assign */ + if (!(options.stackAuto || IFFUNC_ISREENT (ftype))) + { + /* assign */ + operand *top = operandFromValue (argVals); + /* clear useDef and other bitVectors */ + OP_USES (top) = OP_DEFS (top) = OP_SYMBOL (top)->clashes = NULL; + geniCodeAssign (top, pval, 1, 0); + } + else + { + sym_link *p; + if (argVals && (*iArg >= 0)) + { + pval = checkTypes (operandFromValue (argVals), pval); + } + p = operandType (pval); + /* push */ + ic = newiCode (IPUSH, pval, NULL); + ic->parmPush = 1; + /* update the stack adjustment */ + *stack += getSize (IS_AGGREGATE (p) ? aggrToPtr (p, FALSE) : p); + if ((IFFUNC_ISSMALLC (ftype) || TARGET_PDK_LIKE) && !IS_AGGREGATE (p) && getSize (p) == 1) /* SmallC calling convention passes 8-bit paramters as 16-bit values. So does pdk due to stack alignment requirements */ + (*stack)++; + ADDTOCHAIN (ic); + } + } + + if (*iArg >= 0) + { + assert (argVals != NULL); + argVals = argVals->next; + } + (*iArg)++; + return argVals; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeCall - generates temp code for calling */ +/*-----------------------------------------------------------------*/ +operand * +geniCodeCall (operand * left, ast * parms, int lvl) +{ + iCode *ic; + operand *result; + sym_link *type, *etype; + sym_link *ftype; + int stack = 0; + int iArg = 0; + + if (IS_ARRAY (operandType (left))) + { + iCode *tic; + sym_link *ttype; + + tic = newiCode (GET_VALUE_AT_ADDRESS, left, operandFromLit (0)); + ttype = copyLinkChain (operandType (left)->next); + IC_RESULT (tic) = newiTempOperand (ttype, 1); + IC_RESULT (tic)->isaddr = IS_FUNCPTR (ttype) ? 1 : 0; + ADDTOCHAIN (tic); + left = IC_RESULT (tic); + } + + ftype = operandType (left); + if (!IS_FUNC (ftype) && !IS_FUNCPTR (ftype)) + { + werror (E_FUNCTION_EXPECTED); + return operandFromValue (valueFromLit (0)); + } + + // not allow call a critical function + if (inCriticalPair && FUNC_ISCRITICAL (ftype)) + werror (E_INVALID_CRITICAL); + + /* take care of parameters with side-effecting + function calls in them, this is required to take care + of overlaying function parameters */ + geniCodeSEParms (parms, lvl); + + if (IS_FUNCPTR (ftype)) + ftype = ftype->next; + + /* first the parameters */ + if ((options.stackAuto || IFFUNC_ISREENT (ftype)) && !IFFUNC_ISBUILTIN (ftype)) + { + value *argVals; + int nArgs = 0; + ast *parm; + int nParms = 0; + + //count expected arguments except varargs + for (argVals = FUNC_ARGS (ftype); argVals; argVals = argVals->next) + nArgs++; + //count actual parameters including varargs + for (parm = parms; parm && parm->type == EX_OP && parm->opval.op == PARAM; parm = parm->right) + { + if (parm->left) + nParms++; + } + if (parm) + nParms++; + argVals = FUNC_ARGS (ftype); + iArg = nArgs - nParms; + + // reverse the argVals to match the parms + argVals = reverseVal (argVals); + geniCodeParms (parms, argVals, &iArg, &stack, ftype, lvl); + argVals = reverseVal (argVals); + } + else + { + geniCodeParms (parms, FUNC_ARGS (ftype), &iArg, &stack, ftype, lvl); + } + + /* now call : if symbol then pcall */ + if (IS_OP_POINTER (left) || IS_ITEMP (left)) + { + ic = newiCode (PCALL, left, NULL); + } + else + { + ic = newiCode (CALL, left, NULL); + } + + type = copyLinkChain (ftype->next); + etype = getSpec (type); + SPEC_EXTR (etype) = 0; + IC_RESULT (ic) = result = newiTempOperand (type, 1); + + ADDTOCHAIN (ic); + + /* stack adjustment after call */ + ic->parmBytes = stack; + + return result; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeReceive - generate intermediate code for "receive" */ +/*-----------------------------------------------------------------*/ +static void +geniCodeReceive (value * args, operand * func) +{ + unsigned char paramByteCounter = 0; + + /* for all arguments that are passed in registers */ + while (args) + { + if (IS_REGPARM (args->etype)) + { + operand *opr = operandFromValue (args); + operand *opl; + symbol *sym = OP_SYMBOL (opr); + iCode *ic; + + /* we will use it after all optimizations + and before liveRange calculation */ + if (!sym->addrtaken && !IS_VOLATILE (sym->etype)) + { + + if ((IN_FARSPACE (SPEC_OCLS (sym->etype)) && !TARGET_HC08_LIKE) && + options.stackAuto == 0 && (!(options.model == MODEL_FLAT24))) + { + } + else + { + opl = newiTempOperand (args->type, 0); + sym->reqv = opl; + sym->reqv->key = sym->key; + OP_SYMBOL (sym->reqv)->key = sym->key; + OP_SYMBOL (sym->reqv)->isreqv = 1; + OP_SYMBOL (sym->reqv)->islocal = 0; + SPIL_LOC (sym->reqv) = sym; + } + } + + ic = newiCode (RECEIVE, func, NULL); + ic->argreg = SPEC_ARGREG (args->etype); + if (ic->argreg == 1) + { + currFunc->recvSize = getSize (sym->type); + } + IC_RESULT (ic) = opr; + + /* misuse of parmBytes (normally used for functions) + * to save estimated stack position of this argument. + * Normally this should be zero for RECEIVE iCodes. + * No idea if this causes side effects on other ports. - dw + */ + ic->parmBytes = paramByteCounter; + + /* what stack position do we have? */ + paramByteCounter += getSize (sym->type); + + ADDTOCHAIN (ic); + } + + args = args->next; + } +} + +/*-----------------------------------------------------------------*/ +/* geniCodeFunctionBody - create the function body */ +/*-----------------------------------------------------------------*/ +void +geniCodeFunctionBody (ast * tree, int lvl) +{ + iCode *ic; + operand *func; + char *savefilename; + int savelineno; + short functionBlock; + + /* reset the auto generation */ + /* numbers */ + iTempNum = 0; + iTempLblNum = 0; + operandKey = 0; + iCodeKey = 0; + func = ast2iCode (tree->left, lvl + 1); + + savefilename = filename; + savelineno = lineno; + filename = OP_SYMBOL (func)->fileDef; + lineno = OP_SYMBOL (func)->lineDef; + /* create an entry label */ + geniCodeLabel (entryLabel); + filename = savefilename; + lineno = savelineno; + + /* create a proc icode */ + functionBlock = block; + ic = newiCode (FUNCTION, func, NULL); + filename = ic->filename = OP_SYMBOL (func)->fileDef; + lineno = ic->lineno = OP_SYMBOL (func)->lineDef; + ic->tree = tree; + + ADDTOCHAIN (ic); + + /* for all parameters that are passed + on registers add a "receive" */ + geniCodeReceive (tree->values.args, func); + + /* generate code for the body */ + ast2iCode (tree->right, lvl + 1); + + /* create a label for return */ + block = functionBlock; + geniCodeLabel (returnLabel); + + /* now generate the end proc */ + ic = newiCode (ENDFUNCTION, func, NULL); + ic->filename = OP_SYMBOL (func)->fileDef; + ic->lineno = OP_SYMBOL (func)->lastLine; + ic->tree = tree; + ADDTOCHAIN (ic); + return; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeReturn - gen icode for 'return' statement */ +/*-----------------------------------------------------------------*/ +void +geniCodeReturn (operand * op) +{ + iCode *ic; + + /* return in _Noreturn function */ + if (currFunc && IFFUNC_ISNORETURN (currFunc->type)) + werror (W_NORETURNRETURN); + + /* check if a cast is needed */ + if (op && currFunc && currFunc->type && currFunc->type->next) + checkPtrQualifiers (currFunc->type->next, operandType (op), !op->isConstElimnated); + + /* if the operand is present force an rvalue */ + if (op) + op = geniCodeRValue (op, FALSE); + + ic = newiCode (RETURN, op, NULL); + ADDTOCHAIN (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeIfx - generates code for extended if statement */ +/*-----------------------------------------------------------------*/ +void +geniCodeIfx (ast * tree, int lvl) +{ + iCode *ic; + operand *condition = ast2iCode (tree->left, lvl + 1); + sym_link *cetype; + + /* if condition is null then exit */ + if (!condition) + goto exit; + else + condition = geniCodeRValue (condition, FALSE); + + cetype = getSpec (operandType (condition)); + /* if the condition is a literal */ + if (IS_LITERAL (cetype)) + { + if (floatFromVal (OP_VALUE (condition))) + { + if (tree->trueLabel) + geniCodeGoto (tree->trueLabel); + else + assert (0); + } + else + { + if (tree->falseLabel) + geniCodeGoto (tree->falseLabel); + } + goto exit; + } + + if (tree->trueLabel) + { + ic = newiCodeCondition (condition, tree->trueLabel, NULL); + ADDTOCHAIN (ic); + + if (tree->falseLabel) + geniCodeGoto (tree->falseLabel); + } + else + { + ic = newiCodeCondition (condition, NULL, tree->falseLabel); + ADDTOCHAIN (ic); + } + +exit: + if (tree->right && tree->right->type == EX_VALUE) + geniCodeDummyRead (ast2iCode (tree->right, lvl + 1)); + else + ast2iCode (tree->right, lvl + 1); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeJumpTable - tries to create a jump table for switch */ +/*-----------------------------------------------------------------*/ +int +geniCodeJumpTable (operand * cond, value * caseVals, ast * tree) +{ + int min, max, cnt = 1; + int i, t; + value *vch, *maxVal; + iCode *ic; + symbol *falseLabel; + set *labels = NULL; + sym_link *cetype = getSpec (operandType (cond)); + int sizeofMinCost, sizeofZeroMinCost, sizeofMaxCost; + int sizeofMatchJump, sizeofJumpTable; + int sizeIndex; + struct dbuf_s dbuf; + + if (!tree || !caseVals) + return 0; + + /* the criteria for creating a jump table is */ + /* all integer numbers between the maximum & minimum must */ + /* be present, the maximum value should not exceed 255 */ + /* If not all integer numbers are present the algorithm */ + /* inserts jumps to the default label for the missing numbers */ + /* and decides later whether it is worth it */ + min = (int) ulFromVal (vch = caseVals); + + while (vch->next) + { + cnt++; + vch = vch->next; + } + max = (int) ulFromVal (vch); + maxVal = vch; + + /* Exit if the range is too large to handle with a jump table. */ + if (1 + max - min > port->jumptableCost.maxCount) + return 0; + + switch (getSize (operandType (cond))) + { + case 1: + sizeIndex = 0; + break; + case 2: + sizeIndex = 1; + break; + case 4: + sizeIndex = 2; + break; + default: + return 0; + } + + /* Compute the size cost of the range check and subtraction. */ + sizeofMinCost = 0; + sizeofZeroMinCost = 0; + sizeofMaxCost = 0; + + if (!(min == 0 && IS_UNSIGNED (cetype))) + sizeofMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + if (!IS_UNSIGNED (cetype)) + sizeofZeroMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + sizeofMaxCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + + if (min) + sizeofMinCost += port->jumptableCost.sizeofSubtract; + + /* If the size cost of handling a non-zero minimum exceeds the */ + /* cost of extending the range down to zero, then it might be */ + /* better to extend the range to zero. */ + if (min > 0 && (sizeofMinCost - sizeofZeroMinCost) >= (min * port->jumptableCost.sizeofElement)) + { + /* Only extend the jump table if it would still be manageable. */ + if (1 + max <= port->jumptableCost.maxCount) + { + min = 0; + if (IS_UNSIGNED (cetype)) + sizeofMinCost = 0; + else + sizeofMinCost = port->jumptableCost.sizeofRangeCompare[sizeIndex]; + } + } + + /* Compute the total size cost of a jump table. */ + sizeofJumpTable = (1 + max - min) * port->jumptableCost.sizeofElement + + port->jumptableCost.sizeofDispatch + sizeofMinCost + sizeofMaxCost; + + /* Compute the total size cost of a match & jump sequence */ + sizeofMatchJump = cnt * port->jumptableCost.sizeofMatchJump[sizeIndex]; + + /* If the size cost of the jump table is uneconomical then exit */ + if (sizeofMatchJump < sizeofJumpTable) + return 0; + + /* The jump table is preferable. */ + + /* First, a label for the default or missing cases. */ + dbuf_init (&dbuf, 128); + if (tree->values.switchVals.swDefault) + { + dbuf_printf (&dbuf, "_default_%d%s", tree->values.switchVals.swNum, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + } + else + { + dbuf_printf (&dbuf, "_swBrk_%d%s", tree->values.switchVals.swNum, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + } + falseLabel = newiTempLabel (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* Build the list of labels for the jump table. */ + vch = caseVals; + t = (int) ulFromVal (vch); + for (i = min; i <= max; i++) + { + if (vch && t == i) + { + dbuf_init (&dbuf, 128); + /* Explicit case: make a new label for it. */ + dbuf_printf (&dbuf, "_case_%d_%d%s", tree->values.switchVals.swNum, i, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + addSet (&labels, newiTempLabel (dbuf_c_str (&dbuf))); + dbuf_destroy (&dbuf); + vch = vch->next; + if (vch) + t = (int) ulFromVal (vch); + } + else + { + /* Implicit case: use the default label. */ + addSet (&labels, falseLabel); + } + } + + /* first we rule out the boundary conditions */ + { + operand *lit; + operand *boundary; + sym_link *cetype = getSpec (operandType (cond)); + /* no need to check the lower bound if + the condition is always >= min or + the condition is unsigned & minimum value is zero */ + if ((checkConstantRange (cetype, caseVals->etype, '<', FALSE) != CCR_ALWAYS_FALSE) && + (!(min == 0 && IS_UNSIGNED (cetype)))) + { + lit = operandFromValue (valCastLiteral (cetype, min, min)); + boundary = geniCodeLogic (cond, lit, '<', NULL); + ic = newiCodeCondition (boundary, falseLabel, NULL); + ADDTOCHAIN (ic); + } + + /* now for upper bounds */ + if (checkConstantRange (cetype, maxVal->etype, '>', FALSE) != CCR_ALWAYS_FALSE) + { + lit = operandFromValue (valCastLiteral (cetype, max, max)); + boundary = geniCodeLogic (cond, lit, '>', NULL); + ic = newiCodeCondition (boundary, falseLabel, NULL); + ADDTOCHAIN (ic); + } + } + + /* if the min is not zero then we now make it zero */ + if (min) + { + cond = geniCodeSubtract (cond, operandFromLit (min), RESULT_TYPE_CHAR); + if (!IS_LITERAL (getSpec (operandType (cond)))) + setOperandType (cond, UCHARTYPE); + } + + /* now create the jumptable */ + ic = newiCode (JUMPTABLE, NULL, NULL); + IC_JTCOND (ic) = cond; + IC_JTLABELS (ic) = labels; + ADDTOCHAIN (ic); + return 1; +} + +/*-----------------------------------------------------------------*/ +/* geniCodeSwitch - changes a switch to a if statement */ +/*-----------------------------------------------------------------*/ +void +geniCodeSwitch (ast * tree, int lvl) +{ + iCode *ic; + operand *cond = geniCodeRValue (ast2iCode (tree->left, lvl + 1), FALSE); + value *caseVals = tree->values.switchVals.swVals; + symbol *trueLabel, *falseLabel; + struct dbuf_s dbuf; + + /* If the condition is a literal, then just jump to the */ + /* appropriate case label. */ + if (IS_LITERAL (getSpec (operandType (cond)))) + { + int switchVal, caseVal; + + switchVal = (int) ulFromVal (OP_VALUE (cond)); + while (caseVals) + { + caseVal = (int) ulFromVal (caseVals); + if (caseVal == switchVal) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_case_%d_%d%s", tree->values.switchVals.swNum, caseVal, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + trueLabel = newiTempLabel (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + geniCodeGoto (trueLabel); + goto jumpTable; + } + caseVals = caseVals->next; + } + goto defaultOrBreak; + } + + /* If cond is volatile, it might change while we are trying to */ + /* find the matching case. To avoid this possibility, make a */ + /* non-volatile copy to use instead. */ + if (IS_OP_VOLATILE (cond)) + { + operand *newcond; + iCode *ic; + + newcond = newiTempOperand (operandType (cond), TRUE); + newcond->isvolatile = 0; + ic = newiCode ('=', NULL, cond); + IC_RESULT (ic) = newcond; + ADDTOCHAIN (ic); + cond = newcond; + } + + /* if we can make this a jump table */ + if (geniCodeJumpTable (cond, caseVals, tree)) + goto jumpTable; /* no need for the comparison */ + + /* for the cases defined do */ + while (caseVals) + { + operand *compare = geniCodeLogic (cond, operandFromValue (caseVals), EQ_OP, NULL); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_case_%d_%d%s", tree->values.switchVals.swNum, (int) ulFromVal (caseVals), + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + trueLabel = newiTempLabel (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + ic = newiCodeCondition (compare, trueLabel, NULL); + ADDTOCHAIN (ic); + caseVals = caseVals->next; + } + +defaultOrBreak: + /* if default is present then goto break else break */ + dbuf_init (&dbuf, 128); + if (tree->values.switchVals.swDefault) + { + dbuf_printf (&dbuf, "_default_%d%s", tree->values.switchVals.swNum, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + } + else + { + dbuf_printf (&dbuf, "_swBrk_%d%s", tree->values.switchVals.swNum, + tree->values.switchVals.swSuffix ? tree->values.switchVals.swSuffix : ""); + } + + falseLabel = newiTempLabel (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + geniCodeGoto (falseLabel); + +jumpTable: + ast2iCode (tree->right, lvl + 1); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeInline - intermediate code for inline assembler */ +/*-----------------------------------------------------------------*/ +static void +geniCodeInline (ast * tree) +{ + iCode *ic; + + ic = newiCode (INLINEASM, NULL, NULL); + IC_INLINE (ic) = tree->values.inlineasm; + ADDTOCHAIN (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeArrayInit - intermediate code for array initializer */ +/*-----------------------------------------------------------------*/ +static void +geniCodeArrayInit (ast * tree, operand * array) +{ + iCode *ic; + + if (!getenv ("TRY_THE_NEW_INITIALIZER")) + { + ic = newiCode (ARRAYINIT, array, NULL); + IC_ARRAYILIST (ic) = tree->values.constlist; + } + else + { + operand *left = newOperand (), *right = newOperand (); + left->type = right->type = SYMBOL; + OP_SYMBOL (left) = AST_SYMBOL (tree->left); + OP_SYMBOL (right) = AST_SYMBOL (tree->right); + ic = newiCode (ARRAYINIT, left, right); + } + ADDTOCHAIN (ic); +} + +/*-----------------------------------------------------------------*/ +/* geniCodeCritical - intermediate code for a critical statement */ +/*-----------------------------------------------------------------*/ +static void +geniCodeCritical (ast * tree, int lvl) +{ + iCode *ic; + operand *op = NULL; + sym_link *type; + + if (!options.stackAuto && !TARGET_HC08_LIKE) + { + type = newLink (SPECIFIER); + SPEC_VOLATILE (type) = 1; + SPEC_NOUN (type) = V_BIT; + SPEC_SCLS (type) = S_BIT; + SPEC_BLEN (type) = 1; + SPEC_BSTR (type) = 0; + op = newiTempOperand (type, 1); + } + + /* If op is NULL, the original interrupt state will saved on */ + /* the stack. Otherwise, it will be saved in op. */ + + /* Generate a save of the current interrupt state & disable */ + inCriticalPair = 1; + ic = newiCode (CRITICAL, NULL, NULL); + IC_RESULT (ic) = op; + ADDTOCHAIN (ic); + + /* Generate the critical code sequence */ + if (tree->left && tree->left->type == EX_VALUE) + geniCodeDummyRead (ast2iCode (tree->left, lvl + 1)); + else + ast2iCode (tree->left, lvl + 1); + + /* Generate a restore of the original interrupt state */ + ic = newiCode (ENDCRITICAL, NULL, op); + ADDTOCHAIN (ic); + inCriticalPair = 0; +} + +/*-----------------------------------------------------------------*/ +/* Stuff used in ast2iCode to modify geniCodeDerefPtr in some */ +/* particular case. Ie : assigning or dereferencing array or ptr */ +/*-----------------------------------------------------------------*/ +set *lvaluereqSet = NULL; +typedef struct lvalItem +{ + int req; + int lvl; +} +lvalItem; + +/*-----------------------------------------------------------------*/ +/* addLvaluereq - add a flag for lvalreq for current ast level */ +/*-----------------------------------------------------------------*/ +static void +addLvaluereq (int lvl) +{ + lvalItem *lpItem = (lvalItem *) Safe_alloc (sizeof (lvalItem)); + lpItem->req = 1; + lpItem->lvl = lvl; + addSetHead (&lvaluereqSet, lpItem); +} + +/*-----------------------------------------------------------------*/ +/* delLvaluereq - del a flag for lvalreq for current ast level */ +/*-----------------------------------------------------------------*/ +static void +delLvaluereq () +{ + lvalItem *lpItem = getSet (&lvaluereqSet); + if (lpItem) + Safe_free (lpItem); +} + +/*-----------------------------------------------------------------*/ +/* clearLvaluereq - clear lvalreq flag */ +/*-----------------------------------------------------------------*/ +static void +clearLvaluereq () +{ + lvalItem *lpItem = peekSet (lvaluereqSet); + if (lpItem) + lpItem->req = 0; +} + +/*-----------------------------------------------------------------*/ +/* getLvaluereq - get the last lvalreq level */ +/*-----------------------------------------------------------------*/ +#if 0 +int +getLvaluereqLvl () +{ + lvalItem *lpItem = peekSet (lvaluereqSet); + if (lpItem) + return lpItem->lvl; + return 0; +} +#endif +/*-----------------------------------------------------------------*/ +/* isLvaluereq - is lvalreq valid for this level ? */ +/*-----------------------------------------------------------------*/ +static int +isLvaluereq (int lvl) +{ + lvalItem *lpItem = peekSet (lvaluereqSet); + if (lpItem) + return ((lpItem->req) && (lvl <= (lpItem->lvl + 1))); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* ast2iCode - creates an icodeList from an ast */ +/*-----------------------------------------------------------------*/ +operand * +ast2iCode (ast * tree, int lvl) +{ + operand *left = NULL; + operand *right = NULL; + if (!tree) + return NULL; + + /* set the global variables for filename & line number */ + if (tree->filename) + filename = tree->filename; + if (tree->lineno) + lineno = tree->lineno; + if (tree->block) + block = tree->block; + if (tree->level) + scopeLevel = tree->level; + if (tree->seqPoint) + seqPoint = tree->seqPoint; + + if (tree->type == EX_VALUE) + return operandFromValue (tree->opval.val); + + if (tree->type == EX_LINK) + return operandFromLink (tree->opval.lnk); + + /* if we find a nullop */ + if (tree->type == EX_OP && (tree->opval.op == NULLOP || tree->opval.op == BLOCK)) + { + if (tree->left && tree->left->type == EX_VALUE) + geniCodeDummyRead (ast2iCode (tree->left, lvl + 1)); + else + ast2iCode (tree->left, lvl + 1); + if (tree->right && tree->right->type == EX_VALUE) + geniCodeDummyRead (ast2iCode (tree->right, lvl + 1)); + else + ast2iCode (tree->right, lvl + 1); + return NULL; + } + + /* special cases for not evaluating */ + if (tree->opval.op != ':' && + tree->opval.op != '?' && + tree->opval.op != CALL && + tree->opval.op != IFX && + tree->opval.op != AND_OP && + tree->opval.op != OR_OP && + tree->opval.op != LABEL && + tree->opval.op != GOTO && + tree->opval.op != SWITCH && tree->opval.op != FUNCTION && tree->opval.op != INLINEASM && tree->opval.op != CRITICAL) + { + if (IS_ASSIGN_OP (tree->opval.op) || IS_DEREF_OP (tree) || IS_ADDRESS_OF_OP (tree)) + { + addLvaluereq (lvl); + if ((!IS_ADDRESS_OF_OP (tree) && IS_ARRAY_OP (tree->left) && IS_ARRAY_OP (tree->left->left) && + tree->left->left->ftype && IS_ARRAY (tree->left->left->ftype) && + tree->left->left->ftype->next && IS_ARRAY (tree->left->left->ftype->next)) || + (IS_DEREF_OP (tree) && IS_ARRAY_OP (tree->left))) + clearLvaluereq (); + + left = operandFromAst (tree->left, lvl); + delLvaluereq (); + if (IS_DEREF_OP (tree) && IS_DEREF_OP (tree->left)) + left = geniCodeRValue (left, TRUE); + } + else + { + left = operandFromAst (tree->left, lvl); + } + if (tree->opval.op == INC_OP || tree->opval.op == DEC_OP) + { + addLvaluereq (lvl); + right = operandFromAst (tree->right, lvl); + delLvaluereq (); + } + else + { + right = operandFromAst (tree->right, lvl); + } + } + + /* now depending on the type of operand */ + /* this will be a biggy */ + switch (tree->opval.op) + { + case '[': /* array operation */ + { + //sym_link *ltype = operandType (left); + //left = geniCodeRValue (left, IS_PTR (ltype->next) ? TRUE : FALSE); + left = geniCodeRValue (left, FALSE); + right = geniCodeRValue (right, TRUE); + } + + return geniCodeArray (left, right, lvl); + + case '.': /* structure dereference */ + if (IS_PTR (operandType (left))) + left = geniCodeRValue (left, TRUE); + else + left = geniCodeRValue (left, FALSE); + + return geniCodeStruct (left, right, tree->lvalue); + + case PTR_OP: /* structure pointer dereference */ + { + sym_link *pType; + pType = operandType (left); + left = geniCodeRValue (left, TRUE); + + setOClass (pType, getSpec (operandType (left))); + } + + return geniCodeStruct (left, right, tree->lvalue); + + case INC_OP: /* increment operator */ + if (left) + return geniCodePostInc (left); + else + return geniCodePreInc (right, tree->lvalue); + + case DEC_OP: /* decrement operator */ + if (left) + return geniCodePostDec (left); + else + return geniCodePreDec (right, tree->lvalue); + + case '&': /* bitwise and or address of operator */ + if (right) + { /* this is a bitwise operator */ + left = geniCodeRValue (left, FALSE); + right = geniCodeRValue (right, FALSE); + return geniCodeBitwise (left, right, BITWISEAND, tree->ftype); + } + else + return geniCodeAddressOf (left); + + case '|': /* bitwise or & xor */ + case '^': + return geniCodeBitwise (geniCodeRValue (left, FALSE), geniCodeRValue (right, FALSE), tree->opval.op, tree->ftype); + + case '/': + return geniCodeDivision (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype), false); + + case '%': + return geniCodeModulus (geniCodeRValue (left, FALSE), geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)); + case '*': + if (right) + return geniCodeMultiply (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)); + else + return geniCodeDerefPtr (geniCodeRValue (left, FALSE), lvl); + + case '-': + if (right) + return geniCodeSubtract (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)); + else + return geniCodeUnaryMinus (geniCodeRValue (left, FALSE)); + + case '+': + if (right) + return geniCodeAdd (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype), lvl); + else + return geniCodeRValue (left, FALSE); /* unary '+' has no meaning */ + + case LEFT_OP: + return geniCodeLeftShift (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)); + + case RIGHT_OP: + return geniCodeRightShift (geniCodeRValue (left, FALSE), geniCodeRValue (right, FALSE)); + case CAST: +#if 0 // this indeed needs a second thought + { + operand *op; + + // let's keep this simple: get the rvalue we need + op = geniCodeRValue (right, FALSE); + // now cast it to whatever we want + op = geniCodeCast (operandType (left), op, FALSE); + // if this is going to be used as an lvalue, make it so + if (tree->lvalue) + { + op->isaddr = 1; + } + return op; + } +#else // bug #604575, is it a bug ???? + return geniCodeCast (operandType (left), geniCodeRValue (right, FALSE), FALSE); +#endif + + case '~': + case RRC: + case RLC: + case SWAP: + return geniCodeUnary (geniCodeRValue (left, FALSE), tree->opval.op, tree->ftype); + + case '!': + case GETHBIT: + { + operand *op = geniCodeUnary (geniCodeRValue (left, FALSE), tree->opval.op, tree->ftype); + return op; + } + case GETABIT: + { + operand *op = geniCodeBinary (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), + tree->opval.op, tree->ftype); + return op; + } + case GETBYTE: + { + operand *op = geniCodeBinary (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), + tree->opval.op, tree->ftype); + setOperandType (op, UCHARTYPE); + return op; + } + case GETWORD: + { + operand *op = geniCodeBinary (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), + tree->opval.op, tree->ftype); + setOperandType (op, UINTTYPE); + return op; + } + case AND_OP: + case OR_OP: + return geniCodeLogicAndOr (tree, lvl); + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + /* different compilers (even different gccs) evaluate + the two calls in a different order. to get the same + result on all machines we have to specify a clear sequence. + return geniCodeLogic (geniCodeRValue (left, FALSE), + geniCodeRValue (right, FALSE), + tree->opval.op); + */ + { + operand *leftOp, *rightOp; + + leftOp = geniCodeRValue (left, FALSE); + rightOp = geniCodeRValue (right, FALSE); + + return geniCodeLogic (leftOp, rightOp, tree->opval.op, tree); + } + case '?': + return geniCodeConditional (tree, lvl); + + case SIZEOF: + return operandFromLit (getSize (tree->right->ftype)); + + case '=': + { + sym_link *rtype = operandType (right); + sym_link *ltype = operandType (left); + if (IS_PTR (rtype) && IS_ITEMP (right) && right->isaddr && compareType (rtype->next, ltype) == 1) + right = geniCodeRValue (right, TRUE); + else + right = geniCodeRValue (right, FALSE); + return geniCodeAssign (left, right, 0, 1); + } + case MUL_ASSIGN: + return + geniCodeAssign (left, + geniCodeMultiply (geniCodeRValue (operandFromOperand (left), + FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)), 0, 1); + + case DIV_ASSIGN: + return + geniCodeAssign (left, + geniCodeDivision (geniCodeRValue (operandFromOperand (left), FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype), false), 0, 1); + case MOD_ASSIGN: + return + geniCodeAssign (left, + geniCodeModulus (geniCodeRValue (operandFromOperand (left), + FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)), 0, 1); + case ADD_ASSIGN: + { + sym_link *rtype = operandType (right); + sym_link *ltype = operandType (left); + if (IS_PTR (rtype) && IS_ITEMP (right) && right->isaddr && compareType (rtype->next, ltype) == 1) + right = geniCodeRValue (right, TRUE); + else + right = geniCodeRValue (right, FALSE); + + + return geniCodeAssign (left, + geniCodeAdd (geniCodeRValue (operandFromOperand (left), + FALSE), right, getResultTypeFromType (tree->ftype), lvl), 0, 1); + } + case SUB_ASSIGN: + { + sym_link *rtype = operandType (right); + sym_link *ltype = operandType (left); + if (IS_PTR (rtype) && IS_ITEMP (right) && right->isaddr && compareType (rtype->next, ltype) == 1) + { + right = geniCodeRValue (right, TRUE); + } + else + { + right = geniCodeRValue (right, FALSE); + } + return + geniCodeAssign (left, + geniCodeSubtract (geniCodeRValue (operandFromOperand (left), + FALSE), right, getResultTypeFromType (tree->ftype)), 0, 1); + } + case LEFT_ASSIGN: + return + geniCodeAssign (left, + geniCodeLeftShift (geniCodeRValue (operandFromOperand (left), FALSE), + geniCodeRValue (right, FALSE), getResultTypeFromType (tree->ftype)), 0, 1); + case RIGHT_ASSIGN: + return + geniCodeAssign (left, + geniCodeRightShift (geniCodeRValue (operandFromOperand (left), FALSE), + geniCodeRValue (right, FALSE)), 0, 1); + case AND_ASSIGN: + return + geniCodeAssign (left, + geniCodeBitwise (geniCodeRValue (operandFromOperand (left), + FALSE), + geniCodeRValue (right, FALSE), BITWISEAND, operandType (left)), 0, 1); + case XOR_ASSIGN: + return + geniCodeAssign (left, + geniCodeBitwise (geniCodeRValue (operandFromOperand (left), + FALSE), geniCodeRValue (right, FALSE), '^', operandType (left)), 0, 1); + case OR_ASSIGN: + return + geniCodeAssign (left, + geniCodeBitwise (geniCodeRValue (operandFromOperand (left), FALSE), + geniCodeRValue (right, FALSE), '|', operandType (left)), 0, 1); + case ',': + return geniCodeRValue (right, FALSE); + + case CALL: + return geniCodeCall (ast2iCode (tree->left, lvl + 1), tree->right, lvl); + + case LABEL: + geniCodeLabel (OP_SYMBOL (ast2iCode (tree->left, lvl + 1))); + if (tree->right && tree->right->type == EX_VALUE) + { + geniCodeDummyRead (ast2iCode (tree->right, lvl + 1)); + return NULL; + } + else + return ast2iCode (tree->right, lvl + 1); + + case GOTO: + geniCodeGoto (OP_SYMBOL (ast2iCode (tree->left, lvl + 1))); + return ast2iCode (tree->right, lvl + 1); + + case FUNCTION: + geniCodeFunctionBody (tree, lvl); + return NULL; + + case RETURN: + geniCodeReturn (right); + return NULL; + + case IFX: + geniCodeIfx (tree, lvl); + return NULL; + + case SWITCH: + geniCodeSwitch (tree, lvl); + return NULL; + + case INLINEASM: + geniCodeInline (tree); + return NULL; + + case ARRAYINIT: + geniCodeArrayInit (tree, ast2iCode (tree->left, lvl + 1)); + return NULL; + + case CRITICAL: + geniCodeCritical (tree, lvl); + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* reverseICChain - gets from the list and creates a linkedlist */ +/*-----------------------------------------------------------------*/ +iCode * +reverseiCChain () +{ + iCode *loop = NULL; + iCode *prev = NULL; + + while ((loop = getSet (&iCodeChain))) + { + loop->next = prev; + if (prev) + prev->prev = loop; + prev = loop; + } + + return prev; +} + + +/*-----------------------------------------------------------------*/ +/* iCodeFromAst - given an ast will convert it to iCode */ +/*-----------------------------------------------------------------*/ +iCode * +iCodeFromAst (ast * tree) +{ + returnLabel = newiTempLabel ("_return"); + entryLabel = newiTempLabel ("_entry"); + ast2iCode (tree, 0); + return reverseiCChain (); +} + +static const char * +opTypeToStr (OPTYPE op) +{ + switch (op) + { + case SYMBOL: + return "symbol"; + case VALUE: + return "value"; + case TYPE: + return "type"; + } + return "undefined type"; +} + + +operand * +validateOpType (operand * op, const char *macro, const char *args, OPTYPE type, const char *file, unsigned line) +{ + if (op && op->type == type) + { + return op; + } + fprintf (stderr, + "Internal error: validateOpType failed in %s(%s) @ %s:%u:" + " expected %s, got %s\n", macro, args, file, line, opTypeToStr (type), op ? opTypeToStr (op->type) : "null op"); + exit (EXIT_FAILURE); + return op; // never reached, makes compiler happy. +} + +const operand * +validateOpTypeConst (const operand * op, const char *macro, const char *args, OPTYPE type, const char *file, unsigned line) +{ + if (op && op->type == type) + { + return op; + } + fprintf (stderr, + "Internal error: validateOpType failed in %s(%s) @ %s:%u:" + " expected %s, got %s\n", macro, args, file, line, opTypeToStr (type), op ? opTypeToStr (op->type) : "null op"); + exit (EXIT_FAILURE); + return op; // never reached, makes compiler happy. +} diff --git a/src/SDCCicode.h b/src/SDCCicode.h new file mode 100644 index 0000000..46fcea9 --- /dev/null +++ b/src/SDCCicode.h @@ -0,0 +1,364 @@ +/*------------------------------------------------------------------------- + + SDCCicode.h - intermediate code generation etc. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCbitv.h" +#include "SDCCset.h" +#include "SDCCast.h" + +#ifndef SDCCICODE_H +#define SDCCICODE_H 1 + +extern symbol *returnLabel; +extern symbol *entryLabel; +extern int iCodeKey; +extern int operandKey; + +enum +{ + CONDITIONAL = 0, + EXPRESSION, + STATEMENT, + LEAF +}; + +typedef enum +{ + SYMBOL = 1, + VALUE, + TYPE +} +OPTYPE; + +#define IS_SYMOP(op) (op && op->type == SYMBOL) +#define IS_VALOP(op) (op && op->type == VALUE) +#define IS_TYPOP(op) (op && op->type == TYPE) + +#define ADDTOCHAIN(x) addSetHead(&iCodeChain,x) + +#define LRFTYPE sym_link *ltype = operandType(left), \ + *rtype = operandType(right) ; +#define LRETYPE sym_link *letype= getSpec(ltype) , \ + *retype= getSpec(rtype); +#define LRTYPE LRFTYPE LRETYPE +#define IS_ITEMP(op) (IS_SYMOP(op) && op->svt.symOperand->isitmp == 1) +#define IS_PARM(op) (IS_SYMOP(op) && op->svt.symOperand->_isparm) +#define IS_ITEMPLBL(op) (IS_ITEMP(op) && op->svt.symOperand->isilbl == 1) +#define IS_OP_VOLATILE(op) (IS_SYMOP(op) && op->isvolatile) +#define IS_OP_LITERAL(op) (op && op->isLiteral) +#define IS_OP_GLOBAL(op) (IS_SYMOP(op) && op->isGlobal) +#define IS_OP_POINTER(op) (IS_SYMOP(op) && op->isPtr) +#define IS_OP_PARM(op) (IS_SYMOP(op) && op->isParm) +#define OP_ISLIVE_FCALL(op) (IS_ITEMP(op) && OP_SYMBOL(op)->isLiveFcall) +#define SYM_SPIL_LOC(sym) sym->usl.spillLoc + +/* typedef for operand */ +typedef struct operand +{ + OPTYPE type; /* type of operand */ + unsigned int isaddr:1; /* is an address */ + unsigned int aggr2ptr:2; /* 1: must change aggregate to pointer to aggregate */ + /* 2: aggregate has been changed to pointer to aggregate */ + unsigned int isvolatile:1; /* is a volatile operand */ + unsigned int isGlobal:1; /* is a global operand */ + unsigned int isPtr:1; /* is assigned a pointer */ + unsigned int isGptr:1; /* is a generic pointer */ + unsigned int isParm:1; /* is a parameter */ + unsigned int isLiteral:1; /* operand is literal */ + unsigned int isConstElimnated:1; /* if original const casted to non-const */ + + int key; + union + { + struct symbol *symOperand; /* operand is of type symbol */ + struct value *valOperand; /* operand is of type value */ + struct sym_link *typeOperand; /* operand is of type typechain */ + } + svt; + + bitVect *usesDefs; /* which definitions are used by this */ + struct asmop *aop; /* asm op for this operand */ +} +operand; + +extern operand *validateOpType (operand * op, + const char *macro, const char *args, OPTYPE type, const char *file, unsigned line); + +extern const operand *validateOpTypeConst (const operand * op, + const char *macro, const char *args, OPTYPE type, const char *file, unsigned line); + +#define OP_SYMBOL(op) validateOpType(op, "OP_SYMBOL", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand +#define OP_SYMBOL_CONST(op) validateOpTypeConst(op, "OP_SYMBOL", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand +#define OP_VALUE(op) validateOpType(op, "OP_VALUE", #op, VALUE, __FILE__, __LINE__)->svt.valOperand +#define OP_VALUE_CONST(op) validateOpTypeConst(op, "OP_VALUE", #op, VALUE, __FILE__, __LINE__)->svt.valOperand +#define OP_SYM_TYPE(op) validateOpType(op, "OP_SYM_TYPE", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->type +#define OP_SYM_ETYPE(op) validateOpType(op, "OP_SYM_ETYPE", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->etype +#define SPIL_LOC(op) validateOpType(op, "SPIL_LOC", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->usl.spillLoc +#define OP_LIVEFROM(op) validateOpType(op, "OP_LIVEFROM", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->liveFrom +#define OP_LIVETO(op) validateOpType(op, "OP_LIVETO", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->liveTo +#define OP_REQV(op) validateOpType(op, "OP_REQV", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->reqv +#define OP_KEY(op) validateOpType(op, "OP_REQV", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->key +#define OP_TYPE(op) validateOpType(op, "OP_TYPE", #op, TYPE, __FILE__, __LINE__)->svt.typeOperand + +/* definition for intermediate code */ +#define IC_RESULT(x) (x)->ulrrcnd.lrr.result +#define IC_LEFT(x) (x)->ulrrcnd.lrr.left +#define IC_RIGHT(x) (x)->ulrrcnd.lrr.right +#define IC_COND(x) (x)->ulrrcnd.cnd.condition +#define IC_TRUE(x) (x)->ulrrcnd.cnd.trueLabel +#define IC_FALSE(x) (x)->ulrrcnd.cnd.falseLabel +#define IC_LABEL(x) (x)->label +#define IC_JTCOND(x) (x)->ulrrcnd.jmpTab.condition +#define IC_JTLABELS(x) (x)->ulrrcnd.jmpTab.labels +#define IC_INLINE(x) (x)->inlineAsm +#define IC_ARRAYILIST(x) (x)->arrayInitList + +typedef struct iCode +{ + unsigned int op; /* operation defined */ + int key; /* running key for this iCode */ + int seq; /* sequence number within routine */ + int seqPoint; /* sequence point */ + short depth; /* loop depth of this iCode */ + long level; /* scope level */ + short block; /* sequential block number */ + unsigned nosupdate:1; /* don't update spillocation with this */ + unsigned generated:1; /* code generated for this one */ + unsigned parmPush:1; /* parameter push Vs spill push */ + unsigned supportRtn:1; /* will cause a call to a support routine */ + unsigned regsSaved:1; /* registers have been saved */ + unsigned bankSaved:1; /* register bank has been saved */ + unsigned builtinSEND:1; /* SEND for parameter of builtin function */ + + struct iCode *next; /* next in chain */ + struct iCode *prev; /* previous in chain */ + set *movedFrom; /* if this iCode gets moved to another block */ + bitVect *rlive; /* ranges that are live at this point */ + int defKey; /* key for the operand being defined */ + bitVect *uses; /* vector of key of used symbols */ + bitVect *rUsed; /* registers used by this instruction */ + bitVect *rMask; /* registers in use during this instruction */ + bitVect *rSurv; /* registers that survive this instruction (i.e. they are in use, it is not their last use and they are not in the return) */ + union + { + struct + { + operand *left; /* left if any */ + operand *right; /* right if any */ + operand *result; /* result of this op */ + } + lrr; + + struct + { + operand *condition; /* if this is a conditional */ + symbol *trueLabel; /* true for conditional */ + symbol *falseLabel; /* false for conditional */ + } + cnd; + + struct + { + operand *condition; /* condition for the jump */ + set *labels; /* ordered set of labels */ + } + jmpTab; + + } + ulrrcnd; + + symbol *label; /* for a goto statement */ + + const char *inlineAsm; /* pointer to inline assembler code */ + literalList *arrayInitList; /* point to array initializer list. */ + + int lineno; /* file & lineno for debug information */ + char *filename; + + int parmBytes; /* if call/pcall, count of parameter bytes + on stack */ + int argreg; /* argument regno for SEND/RECEIVE */ + int eBBlockNum; /* belongs to which eBBlock */ + char riu; /* after ralloc, the registers in use */ + float count; /* An execution count or probability */ + float pcount; /* For propagation of count */ + + struct ast *tree; /* ast node for this iCode (if not NULL) */ +} +iCode; + +/* various functions associated to iCode */ +typedef struct icodeFuncTable +{ + int icode; + char *printName; + void (*iCodePrint) (struct dbuf_s *, iCode *, char *); + iCode *(*iCodeCopy) (iCode *); +} +iCodeTable; + +/* useful macros */ +#define SKIP_IC2(x) (x->op == GOTO || \ + x->op == LABEL || \ + x->op == FUNCTION || \ + x->op == INLINEASM || \ + x->op == ENDFUNCTION ) + +#define SKIP_IC1(x) (x->op == CALL || \ + SKIP_IC2(x) ) + +#define SKIP_IC(x) (x->op == PCALL || \ + x->op == IPUSH || \ + x->op == IPOP || \ + x->op == JUMPTABLE || \ + x->op == RECEIVE || \ + x->op == ARRAYINIT || \ + SKIP_IC1(x) || \ + x->op == CRITICAL || \ + x->op == ENDCRITICAL || \ + x->op == SEND ) + +#define SKIP_IC3(x) (SKIP_IC2(x) || \ + x->op == JUMPTABLE ) + +#define IS_CONDITIONAL(x) (x->op == EQ_OP || \ + x->op == '<' || \ + x->op == '>' || \ + x->op == LE_OP || \ + x->op == GE_OP || \ + x->op == NE_OP ) + +#define IS_TRUE_SYMOP(op) (op && IS_SYMOP(op) && !IS_ITEMP(op)) + +#define POINTER_SET(ic) ( ic && ic->op == '=' \ + && (IS_ITEMP(IC_RESULT(ic)) || IS_OP_LITERAL(IC_RESULT(ic)))\ + && IC_RESULT(ic)->isaddr ) + +#define POINTER_GET(ic) ( ic && ic->op == GET_VALUE_AT_ADDRESS \ + && (IS_ITEMP(IC_LEFT(ic)) || IS_OP_LITERAL(IC_LEFT(ic)))\ + && IC_LEFT(ic)->isaddr ) + +#define IS_ARITHMETIC_OP(x) (x && (x->op == '+' || \ + x->op == '-' || \ + x->op == '/' || \ + x->op == '*' || \ + x->op == '%')) +#define IS_BITWISE_OP(x) (x && (x->op == BITWISEAND || \ + x->op == '|' || \ + x->op == '^')) + +#define IS_COMMUTATIVE(x) (x && (x->op == EQ_OP || \ + x->op == NE_OP || \ + x->op == '+' || \ + x->op == '*' || \ + x->op == BITWISEAND || \ + x->op == '|' || \ + x->op == '^')) + +#define ASSIGNMENT(ic) ( ic && ic->op == '=') + +#define ASSIGN_SYM_TO_ITEMP(ic) (ic && ic->op == '=' && \ + IS_TRUE_SYMOP(IC_RIGHT(ic)) && \ + IS_ITEMP(IC_RESULT(ic))) + +#define ASSIGN_ITEMP_TO_SYM(ic) (ic && ic->op == '=' && \ + IS_TRUE_SYMOP(IC_RESULT(ic)) && \ + IS_ITEMP(IC_RIGHT(ic))) + +#define ASSIGN_ITEMP_TO_ITEMP(ic) (ic && ic->op == '=' &&\ + !POINTER_SET(ic) &&\ + IS_ITEMP(IC_RIGHT(ic)) &&\ + IS_ITEMP(IC_RESULT(ic))) + +#define ADD_SUBTRACT_ITEMP(ic) (ic && (ic->op == '+' || ic->op == '-') && \ + IS_ITEMP(IC_RESULT(ic)) && \ + ( ( IS_ITEMP(IC_LEFT(ic)) ) || ( IS_SYMOP(IC_LEFT(ic)) ) ) && \ + IS_OP_LITERAL(IC_RIGHT(ic))) + +#define ASSIGNMENT_TO_SELF(ic) (!POINTER_SET(ic) && !POINTER_GET(ic) && \ + ic->op == '=' && IC_RESULT(ic)->key == IC_RIGHT(ic)->key ) + +#define IS_CAST_ICODE(ic) (ic && ic->op == CAST) +#define SET_ISADDR(op,v) {op = operandFromOperand(op); op->isaddr = v;} +#define SET_RESULT_RIGHT(ic) {SET_ISADDR(IC_RIGHT(ic),0); SET_ISADDR(IC_RESULT(ic),0);} +#define IS_ASSIGN_ICODE(ic) (ASSIGNMENT(ic) && !POINTER_SET(ic)) + +#define OP_DEFS(op) validateOpType(op, "OP_DEFS", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->defs +#define OP_USES(op) validateOpType(op, "OP_USES", #op, SYMBOL, __FILE__, __LINE__)->svt.symOperand->uses +/*-----------------------------------------------------------------*/ +/* forward references for functions */ +/*-----------------------------------------------------------------*/ +iCode *reverseiCChain (); +bool isOperandOnStack (operand *); +int isOperandVolatile (const operand *, bool); +int isOperandGlobal (const operand *); +void printiCChain (iCode *, FILE *); +operand *ast2iCode (ast *, int); +operand *geniCodePtrPtrSubtract (operand *, operand *); +void initiCode (); +iCode *iCodeFromAst (ast *); +int isiCodeEqual (iCode *, iCode *); +int isOperandEqual (const operand *, const operand *); +iCodeTable *getTableEntry (int); +int isOperandLiteral (const operand * const); +operand *operandOperation (operand *, operand *, int, sym_link *); +double operandLitValue (const operand *); +unsigned long long operandLitValueUll (const operand *); +operand *operandFromLit (double); +operand *operandFromOperand (operand *); +int isParameterToCall (value *, operand *); +iCode *newiCodeLabelGoto (int, symbol *); +symbol *newiTemp (const char *); +symbol *newiTempLabel (const char *); +#define LOOPEXITLBL "loopExitLbl" +symbol *newiTempLoopHeaderLabel (bool); +iCode *newiCode (int, operand *, operand *); +sym_link *operandType (const operand *); +unsigned int operandSize (operand *); +operand *operandFromValue (value *); +operand *operandFromSymbol (symbol *); +operand *operandFromLink (sym_link *); +sym_link *aggrToPtr (sym_link *, bool); +int aggrToPtrDclType (sym_link *, bool); +void setOClass (sym_link * ptr, sym_link * spec); +int piCode (void *, FILE *); +int dbuf_printOperand (operand *, struct dbuf_s *); +int printOperand (operand *, FILE *); +void setOperandType (operand *, sym_link *); +bool isOperandInFarSpace (operand *); +bool isOperandInPagedSpace (operand *); +bool isOperandInDirSpace (operand *); +bool isOperandInBitSpace (operand *); +bool isOperandInCodeSpace (operand *); +operand *opFromOpWithDU (operand *, bitVect *, bitVect *); +iCode *copyiCode (iCode *); +operand *newiTempOperand (sym_link *, char); +operand *newiTempFromOp (operand *); +iCode *getBuiltinParms (iCode *, int *, operand **); +int isiCodeInFunctionCall (iCode *); +/*-----------------------------------------------------------------*/ +/* declaration of exported variables */ +/*-----------------------------------------------------------------*/ +extern char *filename; +extern int lineno; +#endif + diff --git a/src/SDCClabel.c b/src/SDCClabel.c new file mode 100644 index 0000000..22ef84e --- /dev/null +++ b/src/SDCClabel.c @@ -0,0 +1,535 @@ +/*----------------------------------------------------------------- + SDCClabel.c - label optimizations on iCode (intermediate code) + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +hTab *labelRef = NULL; +hTab *labelDef = NULL; + +/*-----------------------------------------------------------------*/ +/* buildLabelRefTable - creates an hashTable of label references */ +/*-----------------------------------------------------------------*/ +void +buildLabelRefTable (iCode * ic) +{ + iCode *lic; + + setToNull ((void *) &labelRef); + setToNull ((void *) &labelDef); + labelRef = newHashTable (labelKey + 1); + labelDef = newHashTable (labelKey + 1); + + for (lic = ic; lic; lic = lic->next) + { + if (lic->op == GOTO) + hTabAddItem (&labelRef, (IC_LABEL (lic))->key, lic); + + if (lic->op == JUMPTABLE) + { + symbol *lbl; + for (lbl = setFirstItem (IC_JTLABELS (lic)); lbl; + lbl = setNextItem (IC_JTLABELS (lic))) + { + hTabAddItem (&labelRef, lbl->key, lic); + } + } + + if (lic->op == IFX) + { + if (IC_TRUE (lic)) + hTabAddItem (&labelRef, (IC_TRUE (lic))->key, lic); + else + hTabAddItem (&labelRef, (IC_FALSE (lic))->key, lic); + } + if (lic->op == LABEL) + hTabAddItem (&labelDef, (IC_LABEL (lic))->key, lic); + + } +} + +/*-----------------------------------------------------------------*/ +/* labelGotoNext - kills gotos to next statement */ +/*-----------------------------------------------------------------*/ +int +labelGotoNext (iCode * ic) +{ + iCode *loop; + int change = 0; + + for (loop = ic; loop; loop = loop->next) + { + + if (loop->op == GOTO && /* if this is a goto */ + loop->next && /* and we have a next one */ + loop->next->op == LABEL && /* next one is a label */ + loop->next->label->key == loop->label->key) /* same label */ + { + loop->prev->next = loop->next; /* get this out of the chain */ + loop->next->prev = loop->prev; + hTabDeleteItem (&labelRef, (IC_LABEL (loop))->key, loop, DELETE_ITEM, NULL); + change++; + } + } + + return change; +} + +/*-------------------------------------------------------------------*/ +/* deleteIfx - delete an IFX iCode or convert to DUMMY_READ_VOLATILE */ +/*-------------------------------------------------------------------*/ +static void +deleteIfx (iCode * loop, int key) +{ + werrorfl (loop->filename, loop->lineno, W_CONTROL_FLOW); + hTabDeleteItem (&labelRef, key, loop, DELETE_ITEM, NULL); + + /* If the condition was volatile, convert IFX to */ + /* DUMMY_READ_VOLATILE. Otherwise just delete the */ + /* IFX iCode */ + if (IS_OP_VOLATILE (IC_COND (loop))) + { + IC_RIGHT (loop) = IC_COND (loop); + IC_LEFT (loop) = NULL; + IC_RESULT (loop) = NULL; + loop->op = DUMMY_READ_VOLATILE; + } + else + { + loop->prev->next = loop->next; + loop->next->prev = loop->prev; + } +} + + +/*-----------------------------------------------------------------*/ +/* labelIfx - special case Ifx elimination */ +/*-----------------------------------------------------------------*/ +int +labelIfx (iCode * ic) +{ + iCode *loop; + iCode *stat; + int change = 0; + + for (loop = ic; loop; loop = loop->next) + { + /* if condition goto label */ + /* goto label */ + /* i.e. the flow is going to the same location + regardless of the condition in this case the + condition can be eliminated with a WARNING ofcource */ + if (loop->op == IFX && + loop->next && + loop->next->op == GOTO) + { + if (IC_TRUE (loop) && IC_TRUE (loop)->key == IC_LABEL (loop->next)->key) + { + deleteIfx (loop, IC_TRUE (loop)->key); + change++; + continue; + } + else + { + if (IC_FALSE (loop) && IC_FALSE (loop)->key == IC_LABEL (loop->next)->key) + { + deleteIfx (loop, IC_FALSE (loop)->key); + change++; + continue; + } + } + } + /* same as above but with a twist */ + /* if condition goto label */ + /* label: */ + if (loop->op == IFX && + loop->next && + loop->next->op == LABEL && + ((IC_TRUE (loop) && IC_TRUE (loop)->key == IC_LABEL (loop->next)->key) || + (IC_FALSE (loop) && IC_FALSE (loop)->key == IC_LABEL (loop->next)->key))) + { + deleteIfx (loop, IC_LABEL (loop->next)->key); + change++; + continue; + } + + /* we will eliminate certain special case situations */ + /* of the conditional statement :- */ + /* if cond != 0 goto _trueLabel */ + /* goto _falseLabel */ + /* _trueLabel : */ + /* ... */ + /* in these cases we can change it to :- */ + /* if cond == 0 goto _falseLabel */ + /* _trueLabel : */ + /* ... */ + /* similarly if we have a situation like :- */ + /* if cond == 0 goto _falseLabel */ + /* goto _someLabel */ + /* _falseLabel : */ + /* we can change this to */ + /* if cond != 0 goto _someLabel */ + /* _falseLabel : */ + /* ... */ + if (loop->op == IFX && loop->next && loop->next->op == GOTO && + loop->next->next && loop->next->next->op == LABEL) + { + if (IC_TRUE (loop) && (IC_TRUE (loop))->key != (IC_LABEL (loop->next->next))->key || + (IC_FALSE (loop) && (IC_FALSE (loop))->key != (IC_LABEL (loop->next->next))->key)) + continue; + + /* now make sure that this is the only */ + /* referenece to the _trueLabel */ + if (IC_TRUE (loop) && hTabItemWithKey (labelRef, (IC_TRUE (loop))->key)) + { + /* we just change the falseLabel */ + /* to the next goto statement */ + /* unreferenced label will take */ + /* care of removing the label */ + /* delete reference to the true label */ + + hTabDeleteItem (&labelRef, (IC_TRUE (loop))->key, loop, DELETE_ITEM, NULL); + IC_TRUE (loop) = NULL; + IC_FALSE (loop) = IC_LABEL (loop->next); + /* add reference to the LABEL */ + hTabAddItem (&labelRef, (IC_FALSE (loop))->key, loop); + /* next remove the goto */ + hTabDeleteItem (&labelRef, + (IC_LABEL (loop->next))->key, loop->next, DELETE_ITEM, NULL); + loop->next = loop->next->next; + loop->next->prev = loop; + change++; + continue; + } + + /* now do the same with the false labels */ + if (IC_FALSE (loop) && hTabItemWithKey (labelRef, (IC_FALSE (loop))->key)) + { + hTabDeleteItem (&labelRef, (IC_FALSE (loop))->key, loop, DELETE_ITEM, NULL); + IC_FALSE (loop) = NULL; + IC_TRUE (loop) = IC_LABEL (loop->next); + hTabAddItem (&labelRef, (IC_TRUE (loop))->key, loop); + hTabDeleteItem (&labelRef, (IC_LABEL (loop->next))->key, loop->next, DELETE_ITEM, NULL); + loop->next = loop->next->next; + loop->next->prev = loop; + change++; + continue; + } + } + + /* Optimize hidden jump-to-jump: + Simplify + v = 1; stat->prev + goto l1; stat + l0: + v = 0; loop->prev + l1: loop + if (v) goto l3; loop->next + Into + v = 1; + goto l3; + l0: + v = 0; + l1: */ + if (loop->op == LABEL && + loop->next && loop->next->op == IFX && + (stat = hTabFirstItemWK (labelRef, (IC_LABEL (loop))->key)) && + !hTabNextItemWK (labelRef) && + stat && stat->op == GOTO && + stat->prev && stat->prev->op == '=' && IS_OP_LITERAL (IC_RIGHT (stat->prev)) && + loop->prev && loop->prev->op == '=' && IS_OP_LITERAL (IC_RIGHT (loop->prev)) && + IC_RESULT (stat->prev)->key == IC_COND (loop->next)->key && + IC_RESULT (loop->prev)->key == IC_COND (loop->next)->key && + !IS_OP_VOLATILE (IC_COND (loop->next)) && + (!operandLitValue (IC_RIGHT (stat->prev)) ^ !operandLitValue (IC_RIGHT (loop->prev)))) + { + if (IC_FALSE (loop->next) && !operandLitValue (IC_RIGHT (loop->prev)) || + IC_TRUE (loop->next) && operandLitValue (IC_RIGHT (loop->prev))) + /* Complicated case: Insert goto, remove conditional jump. */ + { + /* Change IFX to GOTO. */ + stat = loop->next; + IC_LABEL (stat) = IC_TRUE (stat) ? IC_TRUE (stat) : IC_FALSE (stat); + stat->op = GOTO; + + /* Move to desired location. */ + if (loop->next->next) + loop->next->next->prev = loop; + loop->next = loop->next->next; + stat->prev = loop->prev; + stat->prev->next = stat; + stat->next = loop; + loop->prev = stat; + change++; + continue; + } + else /* Simple case: Redirect goto, remove conditional jump. */ + { + hTabDeleteItem (&labelRef, (IC_LABEL (stat))->key, stat, DELETE_ITEM, NULL); + IC_LABEL (stat) = IC_TRUE (loop->next) ? IC_TRUE (loop->next) : IC_FALSE (loop->next); + hTabAddItem (&labelRef, (IC_LABEL (stat))->key, stat); + hTabDeleteItem (&labelRef, IC_LABEL (stat)->key, loop->next, DELETE_ITEM, NULL); + if (loop->next->next) + loop->next->next->prev = loop; + loop->next = loop->next->next; + change++; + continue; + } + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* replaceGotoGoto - find new target for jump */ +/* if we have a target statement then check if the next */ +/* one is a goto: this means target of goto is a goto */ +/*-----------------------------------------------------------------*/ +static symbol * +replaceGotoGoto (const iCode *ic, const symbol *sLabel, const iCode *target) +{ + if (!target || !target->next) + return 0; + + if (target->next->op != GOTO && target->next->op != LABEL || target->next == ic) + return 0; + + symbol *repLabel = target->next->label; + + if (repLabel == sLabel) + return 0; + + return repLabel; +} + + +/*-----------------------------------------------------------------*/ +/* labelGotoGoto - target of a goto is a goto */ +/*-----------------------------------------------------------------*/ +int +labelGotoGoto (iCode *ic) +{ + iCode *loop; + int change = 0; + + for (loop = ic; loop; loop = loop->next) + { + iCode *stat; + symbol *sLabel = NULL; + symbol *repLabel; + stat = NULL; + switch (loop->op) + { + case GOTO: /* for a goto statement */ + + stat = hTabItemWithKey (labelDef, (sLabel = IC_LABEL (loop))->key); + + if (repLabel = replaceGotoGoto (loop, sLabel, stat)) + { + hTabDeleteItem (&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + loop->label = repLabel; + hTabAddItem (&labelRef, repLabel->key, loop); + change++; + } + break; + + case IFX: /* for a conditional jump */ + + if (IC_TRUE (loop)) + stat = hTabItemWithKey (labelDef, (sLabel = IC_TRUE (loop))->key); + else + stat = hTabItemWithKey (labelDef, (sLabel = IC_FALSE (loop))->key); + + if (repLabel = replaceGotoGoto (loop, sLabel, stat)) + { + if (IC_TRUE (loop)) + { + hTabDeleteItem (&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + IC_TRUE (loop) = repLabel; + } + else + { + hTabDeleteItem (&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + IC_FALSE (loop) = repLabel; + } + hTabAddItem (&labelRef, repLabel->key, loop); + change++; + } + break; + case JUMPTABLE: + + for (sLabel = setFirstItem (IC_JTLABELS (loop)); sLabel; sLabel = setNextItem (IC_JTLABELS (loop))) + if (repLabel = replaceGotoGoto (loop, sLabel, hTabItemWithKey (labelDef, sLabel->key))) + { + hTabDeleteItem (&labelRef, sLabel->key, loop, DELETE_ITEM, NULL); + replaceSetItem (IC_JTLABELS (loop), sLabel, repLabel); + hTabAddItem (&labelRef, repLabel->key, loop); + change++; + } + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* labelUnrefLabel - remove unreferenced labels */ +/*-----------------------------------------------------------------*/ +int +labelUnrefLabel (iCode * ic) +{ + iCode *loop; + int change = 0; + + for (loop = ic; loop; loop = loop->next) + { + + /* if this is a label */ + if (loop->op == LABEL) + { + if (((IC_LABEL (loop))->key == returnLabel->key) || + ((IC_LABEL (loop))->key == entryLabel->key)) + continue; + + if (hTabItemWithKey (labelRef, (IC_LABEL (loop))->key)) + continue; + + /* else eliminitate this one */ + loop->prev->next = loop->next; /* get this out of the chain */ + loop->next->prev = loop->prev; + change++; + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* labelUnreach - remove unreachable code */ +/*-----------------------------------------------------------------*/ +int +labelUnreach (iCode * ic) +{ + iCode *loop; + iCode *tic; + int change = 0; + + /* if we hit a return statement or a goto statement */ + /* remove all statements till we hit the next label */ + for (loop = ic; loop; loop = loop->next) + { + iCode *loop2; + + /* found a goto || return && the next */ + /* statement is not a label */ + if (loop->op == GOTO || loop->op == RETURN || + loop->op == CALL && IFFUNC_ISNORETURN (operandType (IC_LEFT (loop)))) + { + if (loop->next && + (loop->next->op == LABEL || loop->next->op == ENDFUNCTION)) + continue; + + /* loop till we find a label */ + loop2 = loop->next; + while (loop2 && loop2->op != LABEL) + loop2 = loop2->next; + + /* throw away those in between */ + for (tic = loop->next; tic && tic != loop2; tic = tic->next) + { + /* remove label references if any */ + switch (tic->op) + { + case GOTO: + hTabDeleteItem (&labelRef, IC_LABEL (tic)->key, tic, DELETE_ITEM, NULL); + break; + case IFX: + werrorfl (tic->filename, tic->lineno, W_CODE_UNREACH); + if (IC_TRUE (tic)) + hTabDeleteItem (&labelRef, IC_TRUE (tic)->key, tic, DELETE_ITEM, NULL); + else + hTabDeleteItem (&labelRef, IC_FALSE (tic)->key, tic, DELETE_ITEM, NULL); + break; + default: + werrorfl (tic->filename, tic->lineno, W_CODE_UNREACH); + } + } + + /* now set up the pointers */ + loop->next = loop2; + if (loop2) + loop2->prev = loop; + change++; + } + } + return change; +} + +/*-----------------------------------------------------------------*/ +/* iCodeLabelOptimize - some obvious & general optimizations */ +/*-----------------------------------------------------------------*/ +iCode * +iCodeLabelOptimize (iCode * ic) +{ + if (!optimize.label1 && + !optimize.label2 && + !optimize.label3 && + !optimize.label4) + return ic; + + /* build labelreferences */ + buildLabelRefTable (ic); + + /* the following transformations need to ne done */ + /* repeatedly till a fixed point is reached */ + while (1) + { + int change; + change = 0; + + /* first eliminate any goto statement */ + /* that goes to the next statement */ + if (optimize.label1) + change += labelGotoNext (ic); + + if (optimize.label2) + change += labelIfx (ic); + + /* target of a goto is a goto then rename this goto */ + if (optimize.label3) + change += labelGotoGoto (ic); + + /* remove unreference labels */ + if (optimize.label4) + change += labelUnrefLabel (ic); + + /* remove unreachable code */ + change += labelUnreach (ic); + + if (!change) /* fixed point reached */ + break; + } + + return ic; +} diff --git a/src/SDCClabel.h b/src/SDCClabel.h new file mode 100644 index 0000000..175908f --- /dev/null +++ b/src/SDCClabel.h @@ -0,0 +1,39 @@ +/*----------------------------------------------------------------- + SDCClabel.h - label optimizations on iCode (intermediate code) + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "SDCCglobl.h" +#include "SDCCsymt.h" + +#ifndef SDCCLABEL_H +#define SDCCLABEL_H + +extern hTab *labelRef; +extern hTab *labelDef; +extern int labelKey; + +iCode *iCodeLabelOptimize (iCode *); + + +#endif + diff --git a/src/SDCCloop.c b/src/SDCCloop.c new file mode 100644 index 0000000..fce994a --- /dev/null +++ b/src/SDCCloop.c @@ -0,0 +1,1425 @@ +/*------------------------------------------------------------------------- + + SDCCloop.c - source file for loop detection & optimizations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "newalloc.h" + +DEFSETFUNC (isDefAlive); + +STACK_DCL (regionStack, eBBlock *, MAX_NEST_LEVEL * 10) + +/*-----------------------------------------------------------------*/ +/* newInduction - creates a new induction variable */ +/*-----------------------------------------------------------------*/ +static induction * +newInduction (operand * sym, unsigned int op, long constVal, iCode * ic, operand * asym) +{ + induction *ip; + + ip = Safe_alloc (sizeof (induction)); + + ip->sym = sym; + ip->asym = asym; + ip->op = op; + ip->cval = constVal; + ip->ic = ic; +//updateSpillLocation(ic,1); + return ip; +} + +/*-----------------------------------------------------------------*/ +/* newRegion - allocate & returns a loop structure */ +/*-----------------------------------------------------------------*/ +static region * +newRegion () +{ + region *lp; + + lp = Safe_alloc (sizeof (region)); + + return lp; +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/* pinduction - prints induction */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (pinduction) +{ + induction *ip = item; + iCodeTable *icTab; + + fprintf (stdout, "\t"); + printOperand (ip->sym, stdout); + icTab = getTableEntry (ip->ic->op); + icTab->iCodePrint (stdout, ip->ic, icTab->printName); + fprintf (stdout, " %04d\n", (int) ip->cval); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* pregion - prints loop information */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (pregion) +{ + region *lp = item; + + printf ("================\n"); + printf (" loop with entry -- > "); + printEntryLabel (lp->entry, ap); + printf ("\n"); + printf (" loop body --> "); + applyToSet (lp->regBlocks, printEntryLabel); + printf ("\n"); + printf (" loop exits --> "); + applyToSet (lp->exits, printEntryLabel); + printf ("\n"); + return 0; +} +#endif + +/*-----------------------------------------------------------------*/ +/* backEdges - returns a list of back edges */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (backEdges) +{ + edge *ep = item; + V_ARG (set **, bEdges); + + /* if this is a back edge ; to determine this we check */ + /* to see if the 'to' is in the dominator list of the */ + /* 'from' if yes then this is a back edge */ + if (bitVectBitValue (ep->from->domVect, ep->to->bbnum)) + { + addSetHead (bEdges, ep); + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* intersectLoopSucc - returns intersection of loop Successors */ +/*-----------------------------------------------------------------*/ +static bitVect * +intersectLoopSucc (set * lexits, eBBlock ** ebbs) +{ + bitVect *succVect = NULL; + eBBlock *exit = setFirstItem (lexits); + + if (!exit) + return NULL; + + succVect = bitVectCopy (exit->succVect); + + for (exit = setNextItem (lexits); exit; exit = setNextItem (lexits)) + { + succVect = bitVectIntersect (succVect, exit->succVect); + } + + return succVect; +} + +/*-----------------------------------------------------------------*/ +/* loopInsert will insert a block into the loop set */ +/*-----------------------------------------------------------------*/ +static void +loopInsert (set ** regionSet, eBBlock * block) +{ + if (!isinSet (*regionSet, block)) + { + addSetHead (regionSet, block); + STACK_PUSH (regionStack, block); + } +} + +/*-----------------------------------------------------------------*/ +/* insertIntoLoop - insert item into loop */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (insertIntoLoop) +{ + eBBlock *ebp = item; + V_ARG (set **, regionSet); + + loopInsert (regionSet, ebp); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isNotInBlocks - will return 1 if not is blocks */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isNotInBlocks) +{ + eBBlock *ebp = item; + V_ARG (set *, blocks); + + if (!isinSet (blocks, ebp)) + return 1; + + return 0; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* hasIncomingDefs - has definitions coming into the loop. i.e. */ +/* check to see if the preheaders outDefs has any definitions */ +/*-----------------------------------------------------------------*/ +static int +hasIncomingDefs (region * lreg, operand * op) +{ + eBBlock *preHdr = lreg->entry->preHeader; + + if (preHdr && bitVectBitsInCommon (preHdr->outDefs, OP_DEFS (op))) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findLoopEndSeq - will return the sequence number of the last */ +/* iCode with the maximum dfNumber in the region */ +/*-----------------------------------------------------------------*/ +static int +findLoopEndSeq (region * lreg) +{ + eBBlock *block; + eBBlock *lblock; + + for (block = lblock = setFirstItem (lreg->regBlocks); block; block = setNextItem (lreg->regBlocks)) + { + if (block != lblock && block->lSeq > lblock->lSeq) + lblock = block; + } + + return lblock->lSeq; +} +#endif + +/*-----------------------------------------------------------------*/ +/* addToExitsMarkDepth - will add the the exitSet all blocks that */ +/* have exits, will also update the depth field in the blocks */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (addToExitsMarkDepth) +{ + eBBlock *ebp = item; + V_ARG (set *, loopBlocks); + V_ARG (set **, exits); + V_ARG (int, depth); + V_ARG (region *, lr); + + /* mark the loop depth of this block */ + //if (!ebp->depth) + if (ebp->depth < depth) + ebp->depth = depth; + + /* NOTE: here we will update only the inner most loop + that it is a part of */ + if (!ebp->partOfLoop) + ebp->partOfLoop = lr; + + /* if any of the successors go out of the loop then */ + /* we add this one to the exits */ + if (applyToSet (ebp->succList, isNotInBlocks, loopBlocks)) + { + addSetHead (exits, ebp); + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createLoop - will create a set of region */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (createLoop) +{ + edge *ep = item; + V_ARG (set **, allRegion); + region *aloop = newRegion (); + eBBlock *block; + + /* make sure regionStack is empty */ + while (!STACK_EMPTY (regionStack)) + STACK_POP (regionStack); + + /* add the entryBlock */ + addSet (&aloop->regBlocks, ep->to); + loopInsert (&aloop->regBlocks, ep->from); + + while (!STACK_EMPTY (regionStack)) + { + block = STACK_POP (regionStack); + /* if block != entry */ + if (block != ep->to) + applyToSet (block->predList, insertIntoLoop, &aloop->regBlocks); + } + + aloop->entry = ep->to; + + /* now add it to the set */ + addSetHead (allRegion, aloop); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* dominatedBy - will return 1 if item is dominated by block */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (dominatedBy) +{ + eBBlock *ebp = item; + V_ARG (eBBlock *, block); + + return bitVectBitValue (ebp->domVect, block->bbnum); +} + +/*-----------------------------------------------------------------*/ +/* addDefInExprs - adds an expression into the inexpressions */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (addDefInExprs) +{ + eBBlock *ebp = item; + V_ARG (cseDef *, cdp); + V_ARG (ebbIndex *, ebbi); + + addSetHead (&ebp->inExprs, cdp); + cseBBlock (ebp, optimize.global_cse, ebbi); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* assignmentsToSym - for a set of blocks determine # time assigned */ +/*-----------------------------------------------------------------*/ +static int +assignmentsToSym (set * sset, operand * sym) +{ + eBBlock *ebp; + int assigns = 0; + set *blocks = setFromSet (sset); + + for (ebp = setFirstItem (blocks); ebp; ebp = setNextItem (blocks)) + { + /* get all the definitions for this symbol + in this block */ + bitVect *defs = bitVectIntersect (ebp->ldefs, OP_DEFS (sym)); + assigns += bitVectnBitsOn (defs); + setToNull ((void *) &defs); + } + + return assigns; +} + + +/*-----------------------------------------------------------------*/ +/* isOperandInvariant - determines if an operand is an invariant */ +/*-----------------------------------------------------------------*/ +static int +isOperandInvariant (operand * op, region * theLoop, set * lInvars) +{ + int opin = 0; + /* operand is an invariant if it is a */ + /* a. constants . */ + /* b. that have defintions reaching loop entry */ + /* c. that are already defined as invariant */ + /* d. has no assignments in the loop */ + if (op) + { + if (IS_OP_LITERAL (op)) + opin = 1; + else if (IS_SYMOP (op) && OP_SYMBOL (op)->addrtaken) + opin = 0; + else if (ifDefSymIs (theLoop->entry->inExprs, op)) + opin = 1; + else if (ifDefSymIs (lInvars, op)) + opin = 1; + else if (IS_SYMOP (op) && !IS_OP_GLOBAL (op) && !IS_OP_VOLATILE (op) && assignmentsToSym (theLoop->regBlocks, op) == 0) + opin = 1; + } + else + opin++; + + return opin; +} + +/*-----------------------------------------------------------------*/ +/* pointerAssigned - will return 1 if pointer set found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (pointerAssigned) +{ + eBBlock *ebp = item; + V_ARG (operand *, op); + + if (ebp->hasFcall) + return 1; + + if (bitVectBitValue (ebp->ptrsSet, op->key)) + return 1; + + /* Unfortunately, one of the other pointer set operations */ + /* may be using an alias of this operand, and the above */ + /* test would miss it. To be thorough, some aliasing */ + /* analysis should be done here. In the meantime, be */ + /* conservative and assume any other pointer set operation */ + /* is dangerous */ + if (!bitVectIsZero (ebp->ptrsSet)) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hasNonPtrUse - returns true if operand has non pointer usage */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (hasNonPtrUse) +{ + eBBlock *ebp = item; + V_ARG (operand *, op); + iCode *ic = usedInRemaining (op, ebp->sch); + + if (ic && !POINTER_SET (ic) && !POINTER_GET (ic)) + return 1; + + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* loopInvariants - takes loop invariants out of region */ +/*-----------------------------------------------------------------*/ +static int +loopInvariants (region *theLoop, ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->dfOrder; + int count = ebbi->count; + set *lInvars = NULL; + + int change = 0; + int fCallsInBlock; + + /* if the preHeader does not exist then do nothing */ + /* or no exits then do nothing ( have to think about this situation */ + if (theLoop->entry->preHeader == NULL || theLoop->exits == NULL) + return 0; + + /* we will do the elimination for those blocks */ + /* in the loop that dominate all exits from the loop */ + for (eBBlock *lBlock = setFirstItem (theLoop->regBlocks); lBlock; lBlock = setNextItem (theLoop->regBlocks)) + { + iCode *ic; + int domsAllExits; + + /* mark the dominates all exits flag */ + domsAllExits = (applyToSet (theLoop->exits, dominatedBy, lBlock) == elementsInSet (theLoop->exits)); + + /* find out if we have a function call in this block */ + for (ic = lBlock->sch, fCallsInBlock = 0; ic; ic = ic->next) + { + if (SKIP_IC (ic)) + { + fCallsInBlock++; + } + } + + /* now we go thru the instructions of this block and */ + /* collect those instructions with invariant operands */ + for (ic = lBlock->sch; ic; ic = ic->next) + { + int lin, rin; + cseDef *ivar; + + /* TODO this is only needed if the call is between + here and the definition, but I am too lazy to do that now */ + + /* if there are function calls in this block */ + if (fCallsInBlock) + { + /* if this is a pointer get */ + if (POINTER_GET (ic)) + { + continue; + } + + /* if this is an assignment from a global */ + if (ic->op == '=' && isOperandGlobal (IC_RIGHT (ic))) + { + continue; + } + + /* Bug 1717943, + * if this is an assignment to a global */ + if (ic->op == '=' && isOperandGlobal (IC_RESULT (ic))) + { + continue; + } + } + + if (SKIP_IC (ic) || POINTER_SET (ic) || ic->op == IFX) + continue; + + /* iTemp assignment from a literal may be invariant, but it + will needlessly increase register pressure if the + iCode(s) that use this iTemp are not also invariant */ + if (ic->op == '=' && IS_ITEMP (IC_RESULT (ic)) && IS_OP_LITERAL (IC_RIGHT (ic))) + continue; + + /* if result is volatile then skip */ + if (IC_RESULT (ic) && (isOperandVolatile (IC_RESULT (ic), TRUE) || IS_OP_PARM (IC_RESULT (ic)))) + continue; + + /* if result depends on a volatile then skip */ + if ((IC_LEFT (ic) && isOperandVolatile (IC_LEFT (ic), TRUE)) || + (IC_RIGHT (ic) && isOperandVolatile (IC_RIGHT (ic), TRUE))) + continue; + + if (POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT (ic))->next)) + continue; + + lin = rin = 0; + + /* special case */ + /* if address of then it is an invariant */ + if (ic->op == ADDRESS_OF && IS_SYMOP (IC_LEFT (ic)) && IS_AGGREGATE (operandType (IC_LEFT (ic)))) + { + lin++; + } + else + { + /* check if left operand is an invariant */ + if ((lin = isOperandInvariant (IC_LEFT (ic), theLoop, lInvars)) && + /* if this is a pointer get then make sure + that the pointer set does not exist in + any of the blocks */ + POINTER_GET (ic) && applyToSet (theLoop->regBlocks, pointerAssigned, IC_LEFT (ic))) + { + lin = 0; + } + } + + /* do the same for right */ + rin = isOperandInvariant (IC_RIGHT (ic), theLoop, lInvars); + + /* if this is a POINTER_GET then special case, make sure all + usages within the loop are POINTER_GET any other usage + would mean that this is not an invariant , since the pointer + could then be passed as a parameter */ + if (POINTER_GET (ic) && applyToSet (theLoop->regBlocks, hasNonPtrUse, IC_LEFT (ic))) + continue; + + + /* if both the left & right are invariants : then check that */ + /* this definition exists in the out definition of all the */ + /* blocks, this will ensure that this is not assigned any */ + /* other value in the loop, and not used in this block */ + /* prior to this definition which means only this definition */ + /* is used in this loop */ + if (lin && rin && IC_RESULT (ic)) + { + eBBlock *sBlock; + set *lSet = setFromSet (theLoop->regBlocks); + + /* if this block does not dominate all exits */ + /* make sure this defintion is not used anywhere else */ + if (!domsAllExits) + { + if (isOperandGlobal (IC_RESULT (ic))) + continue; + /* for successors for all exits */ + for (sBlock = setFirstItem (theLoop->exits); sBlock; sBlock = setNextItem (theLoop->exits)) + { + for (int i = 0; i < count; ebbs[i++]->visited = 0); + lBlock->visited = 1; + if (applyToSet (sBlock->succList, isDefAlive, ic)) + break; + } + + /* we have found usage */ + if (sBlock) + continue; + } + + /* now make sure this is the only definition */ + for (sBlock = setFirstItem (lSet); sBlock; sBlock = setNextItem (lSet)) + { + iCode *ic2; + int used; + int defDominates; + + /* if this is the block make sure the definition */ + /* reaches the end of the block */ + if (sBlock == lBlock) + { + if (!ifDiCodeIs (sBlock->outExprs, ic)) + break; + } + else if (bitVectBitsInCommon (sBlock->defSet, OP_DEFS (IC_RESULT (ic)))) + break; + + /* Check that this definition is not assigned */ + /* any other value in this block. Also check */ + /* that any usage in the block is dominated by */ + /* by this definition. */ + defDominates = bitVectBitValue (sBlock->domVect, lBlock->bbnum); + used = 0; + for (ic2 = sBlock->sch; ic2; ic2 = ic2->next) + { + if (ic2->op == IFX) + { + if (isOperandEqual (IC_RESULT (ic), IC_COND (ic2))) + used = 1; + } + else if (ic2->op == JUMPTABLE) + { + if (isOperandEqual (IC_RESULT (ic), IC_JTCOND (ic2))) + used = 1; + } + else + { + if (IC_LEFT (ic2) && isOperandEqual (IC_RESULT (ic), IC_LEFT (ic2))) + used = 1; + if (IC_RIGHT (ic2) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic2))) + used = 1; + if ((ic != ic2) && (isOperandEqual (IC_RESULT (ic), IC_RESULT (ic2)))) + break; + /* If used before this definition, might not be invariant */ + if ((ic == ic2) && used) + break; + } + if (used && !defDominates) + break; + } + if (ic2) /* found another definition or a usage before the definition */ + break; + } + + if (sBlock) + continue; /* another definition present in the block */ + + /* now check if it exists in the in of this block */ + /* if not then it was killed before this instruction */ + if (!bitVectBitValue (lBlock->inDefs, ic->key)) + continue; + + /* now we know it is a true invariant */ + /* remove it from the insts chain & put */ + /* in the invariant set */ + + OP_SYMBOL (IC_RESULT (ic))->isinvariant = 1; + SPIL_LOC (IC_RESULT (ic)) = NULL; + remiCodeFromeBBlock (lBlock, ic); + + /* maintain the data flow */ + /* this means removing from definition from the */ + /* defset of this block and adding it to the */ + /* inexpressions of all blocks within the loop */ + bitVectUnSetBit (lBlock->defSet, ic->key); + bitVectUnSetBit (lBlock->ldefs, ic->key); + ivar = newCseDef (IC_RESULT (ic), ic); + applyToSet (theLoop->regBlocks, addDefInExprs, ivar, ebbi); + addSet (&lInvars, ivar); + } + } + } /* for all loop blocks */ + + /* if we have some invariants then */ + if (lInvars) + { + eBBlock *preHdr = theLoop->entry->preHeader; + iCode *icFirst = NULL, *icLast = NULL; + + /* create an iCode chain from it */ + for (cseDef *cdp = setFirstItem (lInvars); cdp; cdp = setNextItem (lInvars)) + { + /* maintain data flow .. add it to the */ + /* ldefs defSet & outExprs of the preheader */ + preHdr->defSet = bitVectSetBit (preHdr->defSet, cdp->diCode->key); + preHdr->ldefs = bitVectSetBit (preHdr->ldefs, cdp->diCode->key); + cdp->diCode->filename = preHdr->ech->filename; + cdp->diCode->lineno = preHdr->ech->lineno; + addSetHead (&preHdr->outExprs, cdp); + + if (!icFirst) + icFirst = cdp->diCode; + if (icLast) + { + icLast->next = cdp->diCode; + cdp->diCode->prev = icLast; + icLast = cdp->diCode; + } + else + icLast = cdp->diCode; + change++; + } + + /* add the instruction chain to the end of the + preheader for this loop, preheaders will always + have atleast a label */ + preHdr->ech->next = icFirst; + icFirst->prev = preHdr->ech; + preHdr->ech = icLast; + icLast->next = NULL; + } + return change; +} + +/*-----------------------------------------------------------------*/ +/* addressTaken - returns true if the symbol is found in the addrof */ +/*-----------------------------------------------------------------*/ +static int +addressTaken (set * sset, operand * sym) +{ + set *loop; + eBBlock *ebp; + set *loop2; + + for (loop = sset; loop; loop = loop->next) + { + ebp = loop->item; + loop2 = ebp->addrOf; + while (loop2) + { + if (isOperandEqual ((operand *) loop2->item, sym)) + return 1; + loop2 = loop2->next; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* findInduction :- returns 1 & the item if the induction is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (findInduction) +{ + induction *ip = item; + V_ARG (operand *, sym); + V_ARG (induction **, ipp); + + if (isOperandEqual (ip->sym, sym)) + { + *ipp = ip; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findDefInRegion - finds the definition within the region */ +/*-----------------------------------------------------------------*/ +static iCode * +findDefInRegion (set * regBlocks, operand * defOp, eBBlock ** owner) +{ + eBBlock *lBlock; + + /* for all blocks in the region */ + for (lBlock = setFirstItem (regBlocks); lBlock; lBlock = setNextItem (regBlocks)) + { + + /* if a definition for this exists */ + if (bitVectBitsInCommon (lBlock->defSet, OP_DEFS (defOp))) + { + iCode *ic; + + /* go thru the instruction chain to find it */ + for (ic = lBlock->sch; ic; ic = ic->next) + if (bitVectBitValue (OP_DEFS (defOp), ic->key)) + { + if (owner) + *owner = lBlock; + return ic; + } + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* addPostLoopBlock - add a ebblock before the successors of the */ +/* loop exits */ +/*-----------------------------------------------------------------*/ +static void +addPostLoopBlock (region * loopReg, ebbIndex * ebbi, iCode * ic) +{ + bitVect *loopSuccs; + int i; + + /* if the number of exits is greater than one then + we use another trick: we will create an intersection + of succesors of the exits, then take those that are not + part of the loop and have dfNumber greater loop entry (eblock), + insert a new predecessor postLoopBlk before them and add + a copy of ic in the new block. The postLoopBlk in between + is necessary, because the old successors of the loop exits can + be successors of other blocks too: see bug-136564. */ + + /* loopSuccs now contains intersection + of all the loops successors */ + loopSuccs = intersectLoopSucc (loopReg->exits, ebbi->dfOrder); + if (!loopSuccs) + return; + + for (i = 0; i < loopSuccs->size; i++) + { + eBBlock *eblock = NULL; + eBBlock *postLoopBlk; + iCode *newic; + int j; + + if (!bitVectBitValue (loopSuccs, i)) + continue; + + /* Need to search for bbnum == i since ebbi->dfOrder is + sorted by dfnum; a direct index won't do. */ + for (j = 0; j < ebbi->count; j++) + if (ebbi->dfOrder[j]->bbnum == i) + { + eblock = ebbi->dfOrder[j]; + break; + } + assert (eblock); + + /* if the successor does not belong to the loop + and will be executed after the loop : then + add a definition to the block */ + if (isinSet (loopReg->regBlocks, eblock)) + continue; + if (eblock->dfnum <= loopReg->entry->dfnum) + continue; + + /* look for an existing loopExitBlock */ + if (strncmp (LOOPEXITLBL, eblock->entryLabel->name, sizeof (LOOPEXITLBL) - 1) == 0) + { + /* reuse the existing one */ + postLoopBlk = eblock; + } + else + { + /* create and insert a new eBBlock. + Damn, that's messy ... */ + int i; + set *oldPredList; + eBBlock *ebpi; + + postLoopBlk = neweBBlock (); + + /* create a loopExit-label */ + postLoopBlk->entryLabel = newiTempLoopHeaderLabel (0); + + /* increase bbnum for all blocks after + (and including) eblock */ + for (i = 0; i < ebbi->count; i++) + { + if (ebbi->bbOrder[i]->bbnum >= eblock->bbnum) + ++ebbi->bbOrder[i]->bbnum; + } + /* insert postLoopBlk before bbnum */ + postLoopBlk->bbnum = eblock->bbnum - 1; + + ++ebbi->count; + + /* these arrays need one more block, which ... */ + ebbi->bbOrder = Safe_realloc (ebbi->bbOrder, (ebbi->count + 1) * sizeof (eBBlock *)); + /* ... must be initialized with 0 */ + ebbi->bbOrder[ebbi->count] = NULL; + /* move the blocks up ... */ + memmove (&ebbi->bbOrder[postLoopBlk->bbnum + 1], + &ebbi->bbOrder[postLoopBlk->bbnum], (ebbi->count - postLoopBlk->bbnum - 1) * sizeof (eBBlock *)); + /* ... and insert postLoopBlk */ + ebbi->bbOrder[postLoopBlk->bbnum] = postLoopBlk; + + /* just add postLoopBlk at the end of dfOrder, + computeControlFlow() will compute the new dfnum */ + ebbi->dfOrder = Safe_realloc (ebbi->dfOrder, (ebbi->count + 1) * sizeof (eBBlock *)); + ebbi->dfOrder[ebbi->count] = NULL; + ebbi->dfOrder[ebbi->count - 1] = postLoopBlk; + + /* copy loop information from eblock to postLoopBlk */ + if (eblock->partOfLoop) + { + postLoopBlk->partOfLoop = eblock->partOfLoop; + /* add postLoopBlk to loop region */ + addSetHead (&postLoopBlk->partOfLoop->regBlocks, postLoopBlk); + } + + /* go through loop exits and replace the old exit block eblock + with the new postLoopBlk */ + for (ebpi = setFirstItem (loopReg->exits); ebpi; ebpi = setNextItem (loopReg->exits)) + { + /* replace old label with new label */ + replaceLabel (ebpi, eblock->entryLabel, postLoopBlk->entryLabel); + } + + /* now eblock is replaced by postLoopBlk. + It's possible, that eblock has an immediate predecessor + (with ebpi->bbnum + 1 == eblock->bbnum), which isn't part of the + loop. This predecessor must stay predecessor of eblock, not of + postLoopBlk. But now postLoopBlk is in between, therefore a GOTO + from this predecessor to eblock must be appended. + Example: bug-136564.c */ + + /* take all predecessors and subtract the loop exits */ + oldPredList = subtractFromSet (eblock->predList, loopReg->exits, THROW_NONE); + for (ebpi = setFirstItem (oldPredList); ebpi; ebpi = setNextItem (oldPredList)) + { + /* Is it an immediate predecessor (without GOTO)? + All other predecessors end with a + GOTO, IF, IFX or JUMPTABLE: nothing to to do */ + if (ebpi->bbnum + 1 == postLoopBlk->bbnum) + { + /* insert goto to old predecessor of eblock */ + newic = newiCodeLabelGoto (GOTO, eblock->entryLabel); + addiCodeToeBBlock (ebpi, newic, NULL); + /* Make sure the GOTO has a target */ + if (eblock->sch->op != LABEL) + { + newic = newiCodeLabelGoto (LABEL, eblock->entryLabel); + addiCodeToeBBlock (eblock, newic, eblock->sch); + } + break; /* got it, only one is possible */ + } + } + + /* set the label */ + postLoopBlk->sch = postLoopBlk->ech = newiCodeLabelGoto (LABEL, postLoopBlk->entryLabel); + } + + /* create the definition in postLoopBlk */ + newic = newiCode ('=', NULL, operandFromOperand (IC_RIGHT (ic))); + IC_RESULT (newic) = operandFromOperand (IC_RESULT (ic)); + /* maintain data flow */ + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + OP_USES (IC_RIGHT (newic)) = bitVectSetBit (OP_USES (IC_RIGHT (newic)), newic->key); + + /* and add it */ + addiCodeToeBBlock (postLoopBlk, newic, NULL); + postLoopBlk->sch->filename = postLoopBlk->ech->filename = eblock->sch->filename; + postLoopBlk->sch->lineno = postLoopBlk->ech->lineno = eblock->sch->lineno; + + /* outDefs is needed by computeControlFlow(), anything + else will be set up by computeControlFlow() */ + postLoopBlk->outDefs = bitVectSetBit (postLoopBlk->outDefs, newic->key); + } /* for (i = 0; i < loopSuccs->size; i++) */ + + /* the postLoopBlk and the induction significantly + changed the control flow, recompute it */ + computeControlFlow (ebbi); +} + +/*-----------------------------------------------------------------*/ +/* basicInduction - finds the basic induction variables in a loop */ +/*-----------------------------------------------------------------*/ +static set * +basicInduction (region * loopReg, ebbIndex * ebbi) +{ + eBBlock *lBlock; + set *indVars = NULL; + + /* i.e. all assignments of the form a := a +/- const */ + /* for all blocks within the loop do */ + for (lBlock = setFirstItem (loopReg->regBlocks); lBlock; lBlock = setNextItem (loopReg->regBlocks)) + { + + iCode *ic, *dic; + + /* for all instructions in the blocks do */ + for (ic = lBlock->sch; ic; ic = ic->next) + { + operand *aSym; + long litValue; + induction *ip; + iCode *indIc; + eBBlock *owner = NULL; + int nexits; + sym_link *optype; + + /* To find an induction variable, we need to */ + /* find within the loop three iCodes in this */ + /* general form: */ + /* */ + /* ddic: iTempB := symbolVar */ + /* dic: iTempA := iTempB + lit */ + /* or iTempA := iTempB - lit */ + /* or iTempA := lit + iTempB */ + /* ic: symbolVar := iTempA */ + /* */ + /* (symbolVar may also be an iTemp if it is */ + /* register equivalent) */ + + /* look for assignments of the form */ + /* symbolVar := iTempNN */ + if (ic->op != '=') + continue; + + if (!IS_SYMOP (IC_RESULT (ic))) + continue; + + if (!IS_TRUE_SYMOP (IC_RESULT (ic)) && !OP_SYMBOL (IC_RESULT (ic))->isreqv) + continue; + + if (isOperandGlobal (IC_RESULT (ic))) + continue; + + if (!IS_ITEMP (IC_RIGHT (ic))) + continue; + + /* if it has multiple assignments within the loop then skip */ + if (assignmentsToSym (loopReg->regBlocks, IC_RESULT (ic)) > 1) + continue; + + /* if the address of this was taken inside the loop then continue */ + if (addressTaken (loopReg->regBlocks, IC_RESULT (ic))) + continue; + + /* Only consider variables with integral type. */ + /* (2004/12/06 - EEP - ds390 fails regression tests unless */ + /* pointers are also considered for induction (due to some */ + /* register alloctaion bugs). Remove !IS_PTR clause when */ + /* that gets fixed) */ + optype = operandType (IC_RIGHT (ic)); + if (!IS_INTEGRAL (optype) && !IS_PTR (optype)) + continue; + + /* find the definition for the result in the block */ + if (!(dic = findDefInRegion (setFromSet (loopReg->regBlocks), IC_RIGHT (ic), &owner))) + continue; + + /* if not +/- continue */ + if (dic->op != '+' && dic->op != '-') + continue; + + /* make sure definition is of the form a +/- c */ + if (!IS_OP_LITERAL (IC_LEFT (dic)) && !IS_OP_LITERAL (IC_RIGHT (dic))) + continue; + + /* make sure the definition found is the only one */ + if (assignmentsToSym (loopReg->regBlocks, IC_RIGHT (ic)) > 1) + continue; + + if (IS_OP_LITERAL (IC_RIGHT (dic))) + { + aSym = IC_LEFT (dic); + litValue = (long) operandLitValue (IC_RIGHT (dic)); + } + else + { + /* For minus, the literal must not be on the left side. */ + /* (Actually, this case could be handled, but it's probably */ + /* not worth the extra code) */ + if (dic->op == '-') + continue; + aSym = IC_RIGHT (dic); + litValue = (long) operandLitValue (IC_LEFT (dic)); + } + + if (!isOperandEqual (IC_RESULT (ic), aSym) && !isOperandEqual (IC_RIGHT (ic), aSym)) + { + iCode *ddic; + /* find the definition for this and check */ + if (!(ddic = findDefInRegion (setFromSet (loopReg->regBlocks), aSym, &owner))) + continue; + + if (ddic->op != '=') + continue; + + if (!isOperandEqual (IC_RESULT (ddic), aSym) || !isOperandEqual (IC_RIGHT (ddic), IC_RESULT (ic))) + continue; + } + + /* if the right hand side has more than one usage then + don't make it an induction (will have to think some more) */ + if (bitVectnBitsOn (OP_USES (IC_RIGHT (ic))) > 1) + continue; + + /* if the definition is volatile then it cannot be + an induction object */ + if (isOperandVolatile (IC_RIGHT (ic), FALSE) || isOperandVolatile (IC_RESULT (ic), FALSE)) + continue; + + /* whew !! that was a lot of work to find the definition */ + /* create an induction object */ + indIc = newiCode ('=', NULL, IC_RESULT (ic)); + indIc->filename = ic->filename; + indIc->lineno = ic->lineno; + IC_RESULT (indIc) = operandFromOperand (IC_RIGHT (ic)); + IC_RESULT (indIc)->isaddr = 0; + OP_SYMBOL (IC_RESULT (indIc))->isind = 1; + ip = newInduction (IC_RIGHT (ic), dic->op, litValue, indIc, NULL); + + /* replace the inducted variable by the iTemp */ + replaceSymBySym (loopReg->regBlocks, IC_RESULT (ic), IC_RIGHT (ic)); + /* ic should now be moved to the exit block(s) */ + + nexits = elementsInSet (loopReg->exits); + if (nexits == 0) + { + /* this is a endless loop without exits: there's + no need to restore the inducted variable */ + iCode *saveic = ic->prev; + + /* ic will be removed by killDeadCode() too, + but it's a nice to see a clean dumploop now. */ + remiCodeFromeBBlock (lBlock, ic); + /* clear the definition */ + bitVectUnSetBit (lBlock->defSet, ic->key); + ic = saveic; + } + else + addPostLoopBlock (loopReg, ebbi, ic); + addSet (&indVars, ip); + } /* for ic */ + } /* end of all blocks for basic induction variables */ + + return indVars; +} + +/*-----------------------------------------------------------------*/ +/* loopInduction - remove induction variables from a loop */ +/*-----------------------------------------------------------------*/ +static int +loopInduction (region * loopReg, ebbIndex * ebbi) +{ + int change = 0; + eBBlock *lBlock, *owner, *lastBlock = NULL; + set *indVars = NULL; + set *basicInd = NULL; + + if (loopReg->entry->preHeader == NULL) + return 0; + + /* we first determine the basic Induction variables */ + basicInd = setFromSet (indVars = basicInduction (loopReg, ebbi)); + + /* find other induction variables : by other we mean definitions of */ + /* the form x := y (* | / ) .. we will move this one to */ + /* beginning of the loop and reduce strength i.e. replace with +/- */ + /* these expensive expressions: OH! and y must be induction too */ + for (lBlock = setFirstItem (loopReg->regBlocks), lastBlock = lBlock; + lBlock && indVars; lBlock = setNextItem (loopReg->regBlocks)) + { + iCode *ic, *indIc, *dic; + induction *ip; + + /* last block is the one with the highest block + number */ + if (lastBlock->bbnum < lBlock->bbnum) + lastBlock = lBlock; + + for (ic = lBlock->sch; ic; ic = ic->next) + { + operand *aSym; + operand *litSym; + long litVal; + + /* consider only * & / */ + if (ic->op != '*' && ic->op != '/') + continue; + + /* only consider variables with integral type */ + if (!IS_INTEGRAL (operandType (IC_RESULT (ic)))) + continue; + + /* if the result has more definitions then */ + if (assignmentsToSym (loopReg->regBlocks, IC_RESULT (ic)) > 1) + continue; + + /* check if the operands are what we want */ + /* i.e. one of them an symbol the other a literal */ + if (!((IS_SYMOP (IC_LEFT (ic)) && IS_OP_LITERAL (IC_RIGHT (ic))) || + (IS_OP_LITERAL (IC_LEFT (ic)) && IS_SYMOP (IC_RIGHT (ic))))) + continue; + + if (IS_SYMOP (IC_LEFT (ic))) + { + aSym = IC_LEFT (ic); + litSym = IC_RIGHT (ic); + } + else + { + /* For division, the literal must not be on the left side */ + if (ic->op == '/') + continue; + aSym = IC_RIGHT (ic); + litSym = IC_LEFT (ic); + } + litVal = (long) operandLitValue (litSym); + + ip = NULL; + /* check if this is an induction variable */ + if (!applyToSetFTrue (basicInd, findInduction, aSym, &ip)) + continue; + + /* ask port for size not worth if native instruction + exist for multiply & divide */ + if (port->hasNativeMulFor (ic, operandType (IC_LEFT (ic)), operandType (IC_RIGHT (ic)))) + continue; + + /* if this is a division then the remainder should be zero + for it to be inducted */ + if (ic->op == '/' && (ip->cval % litVal)) + continue; + + /* create the iCode to be placed in the loop header */ + /* and create the induction object */ + + /* create an instruction */ + /* this will be put on the loop header */ + indIc = newiCode (ic->op, operandFromOperand (aSym), operandFromOperand (litSym)); + indIc->filename = ic->filename; + indIc->lineno = ic->lineno; + IC_RESULT (indIc) = operandFromOperand (IC_RESULT (ic)); + OP_SYMBOL (IC_RESULT (indIc))->isind = 1; + + /* keep track of the inductions */ + litVal = (ic->op == '*' ? (litVal * ip->cval) : (ip->cval / litVal)); + + addSet (&indVars, newInduction (IC_RESULT (ic), ip->op, litVal, indIc, NULL)); + + /* Replace mul/div with assignment to self; killDeadCode() will */ + /* clean this up since we can't use remiCodeFromeBBlock() here. */ + ic->op = '='; + IC_LEFT (ic) = NULL; + IC_RIGHT (ic) = IC_RESULT (ic); + + /* Insert an update of the induction variable just before */ + /* the update of the basic induction variable. */ + indIc = newiCode (ip->op, operandFromOperand (IC_RESULT (ic)), operandFromLit (litVal)); + IC_RESULT (indIc) = operandFromOperand (IC_RESULT (ic)); + owner = NULL; + dic = findDefInRegion (setFromSet (loopReg->regBlocks), aSym, &owner); + assert (dic); + addiCodeToeBBlock (owner, indIc, dic); + } + } + + /* if we have some induction variables then */ + if (indVars) + { + eBBlock *preHdr = loopReg->entry->preHeader; + iCode *icFirst = NULL, *icLast = NULL; + induction *ip; + bitVect *indVect = NULL; + + /* create an iCode chain from it */ + for (ip = setFirstItem (indVars); ip; ip = setNextItem (indVars)) + { + indVect = bitVectSetBit (indVect, ip->ic->key); + ip->ic->filename = preHdr->ech->filename; + ip->ic->lineno = preHdr->ech->lineno; + if (!icFirst) + icFirst = ip->ic; + if (icLast) + { + icLast->next = ip->ic; + ip->ic->prev = icLast; + icLast = ip->ic; + } + else + icLast = ip->ic; + change++; + } + + /* add the instruction chain to the end of the */ + /* preheader for this loop */ + preHdr->ech->next = icFirst; + icFirst->prev = preHdr->ech; + preHdr->ech = icLast; + icLast->next = NULL; + + /* add the induction variable vector to the last + block in the loop */ + lastBlock->isLastInLoop = 1; + lastBlock->linds = bitVectUnion (lastBlock->linds, indVect); + } + + setToNull ((void *) &indVars); + return change; +} + +/*-----------------------------------------------------------------*/ +/* mergeRegions - will merge region with same entry point */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (mergeRegions) +{ + region *theLoop = item; + V_ARG (set *, allRegion); + region *lp; + + /* if this has already been merged then do nothing */ + if (theLoop->merged) + return 0; + + /* go thru all the region and check if any of them have the */ + /* entryPoint as the Loop */ + for (lp = setFirstItem (allRegion); lp; lp = setNextItem (allRegion)) + { + + if (lp == theLoop) + continue; + + if (lp->entry == theLoop->entry) + { + theLoop->regBlocks = unionSets (theLoop->regBlocks, lp->regBlocks, THROW_DEST); + lp->merged = 1; + } + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* ifMerged - return 1 if the merge flag is 1 */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (ifMerged) +{ + region *lp = item; + + return lp->merged; +} + +/*-----------------------------------------------------------------*/ +/* mergeInnerLoops - will merge into body when entry is present */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (mergeInnerLoops) +{ + region *theLoop = item; + V_ARG (set *, allRegion); + V_ARG (int *, maxDepth); + region *lp; + + /* check if the entry point is present in the body of any */ + /* loop then put the body of this loop into the outer loop */ + for (lp = setFirstItem (allRegion); lp; lp = setNextItem (allRegion)) + { + + if (lp == theLoop) + continue; + + if (isinSet (lp->regBlocks, theLoop->entry)) + { + lp->containsLoops += theLoop->containsLoops + 1; + if (lp->containsLoops > (*maxDepth)) + *maxDepth = lp->containsLoops; + + lp->regBlocks = unionSets (lp->regBlocks, theLoop->regBlocks, THROW_DEST); + } + } + + return 1; +} + + +/*-----------------------------------------------------------------*/ +/* createLoopRegions - will detect and create a set of natural loops */ +/*-----------------------------------------------------------------*/ +hTab * +createLoopRegions (ebbIndex * ebbi) +{ + set *allRegion = NULL; /* set of all loops */ + hTab *orderedLoops = NULL; + set *bEdges = NULL; + int maxDepth = 0; + region *lp; + + /* get all the back edges in the graph */ + if (!applyToSet (graphEdges, backEdges, &bEdges)) + return 0; /* found no loops */ + + /* for each of these back edges get the blocks that */ + /* constitute the loops */ + applyToSet (bEdges, createLoop, &allRegion); + + /* now we will create regions from these loops */ + /* loops with the same entry points are considered to be the */ + /* same loop & they are merged. If the entry point of a loop */ + /* is found in the body of another loop then , all the blocks */ + /* in that loop are added to the loops containing the header */ + applyToSet (allRegion, mergeRegions, allRegion); + + /* delete those already merged */ + deleteItemIf (&allRegion, ifMerged); + + applyToSet (allRegion, mergeInnerLoops, allRegion, &maxDepth); + maxDepth++; + + /* now create all the exits .. also */ + /* create an ordered set of loops */ + /* i.e. we process loops in the inner to outer order */ + for (lp = setFirstItem (allRegion); lp; lp = setNextItem (allRegion)) + { + applyToSet (lp->regBlocks, addToExitsMarkDepth, lp->regBlocks, &lp->exits, (maxDepth - lp->containsLoops), lp); + + hTabAddItem (&orderedLoops, lp->containsLoops, lp); + + } + return orderedLoops; +} + +/*-----------------------------------------------------------------*/ +/* loopOptimizations - identify region & remove invariants & ind */ +/*-----------------------------------------------------------------*/ +int +loopOptimizations (hTab * orderedLoops, ebbIndex * ebbi) +{ + region *lp; + int change = 0; + int k; + + /* if no loop optimizations requested */ + if (!optimize.loopInvariant && !optimize.loopInduction) + return 0; + + /* now we process the loops inner to outer order */ + /* this is essential to maintain data flow information */ + /* the other choice is an ugly iteration for the depth */ + /* of the loops would hate that */ + for (lp = hTabFirstItem (orderedLoops, &k); lp; lp = hTabNextItem (orderedLoops, &k)) + { + + if (optimize.loopInvariant) + change += loopInvariants (lp, ebbi); + + if (optimize.loopInduction) + change += loopInduction (lp, ebbi); + } + + return change; +} diff --git a/src/SDCCloop.h b/src/SDCCloop.h new file mode 100644 index 0000000..34566f2 --- /dev/null +++ b/src/SDCCloop.h @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------- + + SDCCloop.h - header file for loop detection & optimizations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCBBlock.h" +#include "SDCCcse.h" + +#ifndef SDCCLOOP_H +#define SDCCLOOP_H 1 + +typedef struct region + { + + unsigned int merged:1; + unsigned int visited:1; + eBBlock *entry; /* entry Block */ + int containsLoops; /* contains other loops */ + set *regBlocks; /* set of all blocks */ + set *exits; /* set of exits */ + } +region; + +typedef struct induction + { + + operand *sym; + operand *asym; + unsigned int op; + long cval; + iCode *ic; + } +induction; + +int loopOptimizations (hTab *, ebbIndex *); +hTab *createLoopRegions (ebbIndex *); + +#endif diff --git a/src/SDCClospre.cc b/src/SDCClospre.cc new file mode 100644 index 0000000..18541d8 --- /dev/null +++ b/src/SDCClospre.cc @@ -0,0 +1,351 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2012 +// +// (c) 2012 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// +// Lifetime-optimal speculative partial redundancy elimination. + +// #define DEBUG_LOSPRE // Uncomment to get debug messages while doing lospre. +// #define DEBUG_LOSPRE_ASS // Uncomment to get debug messages on considered assignmentd while doing lospre. + +#include "SDCClospre.hpp" + +// A quick-and-dirty function to get the CFG from sdcc (a simplified version of the function from SDCCralloc.hpp). +void +create_cfg_lospre (cfg_lospre_t &cfg, iCode *start_ic, ebbIndex *ebbi) +{ + iCode *ic; + + std::map key_to_index; + { + int i; + + for (ic = start_ic, i = 0; ic; ic = ic->next, i++) + { + boost::add_vertex(cfg); + key_to_index[ic->key] = i; + cfg[i].ic = ic; + } + } + + // Get control flow graph from sdcc. + for (ic = start_ic; ic; ic = ic->next) + { + if((ic->op == '>' || ic->op == '<' || ic->op == LE_OP || ic->op == GE_OP || ic->op == EQ_OP || ic->op == NE_OP || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) && ifxForOp (IC_RESULT (ic), ic)) + boost::add_edge(key_to_index[ic->key], key_to_index[ic->next->key], 4.0f, cfg); // Try not to separate op from ifx. + else if (ic->op != GOTO && ic->op != RETURN && ic->op != JUMPTABLE && ic->next) + boost::add_edge(key_to_index[ic->key], key_to_index[ic->next->key], 3.0f, cfg); + + if (ic->op == GOTO) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, ic->label)->sch->key], 6.0f, cfg); + else if (ic->op == RETURN) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, returnLabel)->sch->key], 6.0f, cfg); + else if (ic->op == IFX) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->sch->key], 6.0f, cfg); + else if (ic->op == JUMPTABLE) + for (symbol *lbl = (symbol *)(setFirstItem (IC_JTLABELS (ic))); lbl; lbl = (symbol *)(setNextItem (IC_JTLABELS (ic)))) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, lbl)->sch->key], 6.0f, cfg); + } +} + +static bool +candidate_expression (const iCode *const ic, int lkey) +{ + wassert(ic); + + if ( + ic->op != '!' && + ic->op != '~' && + ic->op != UNARYMINUS && + ic->op != '+' && + ic->op != '-' && + ic->op != '*' && + ic->op != '/' && + ic->op != '%' && + ic->op != '>' && + ic->op != '<' && + ic->op != LE_OP && + ic->op != GE_OP && + ic->op != NE_OP && + ic->op != EQ_OP && + ic->op != AND_OP && + ic->op != OR_OP && + ic->op != '^' && + ic->op != '|' && + ic->op != BITWISEAND && + ic->op != RRC && + ic->op != RLC && + ic->op != GETABIT && + ic->op != GETHBIT && + ic->op != LEFT_OP && + ic->op != RIGHT_OP && + !(ic->op == '=' && !POINTER_SET(ic) && !(IS_ITEMP(IC_RIGHT(ic)) /*&& IC_RIGHT(ic)->key > lkey*/)) && + ic->op != GET_VALUE_AT_ADDRESS && + ic->op != CAST) + return (false); + + const operand *const left = IC_LEFT (ic); + const operand *const right = IC_RIGHT (ic); + const operand *const result = IC_RESULT (ic); + + // Todo: Allow literal right operand once backends can rematerialize literals! + if(ic->op == '=' && IS_OP_LITERAL (right)) + return (false); + + // Todo: Allow more operands! + if (ic->op != CAST && left && !(IS_SYMOP (left) || IS_OP_LITERAL (left)) || + right && !(IS_SYMOP (right) || IS_OP_LITERAL (right)) || + result && !(IS_SYMOP (result) || IS_OP_LITERAL (result))) + return (false); + + return (true); +} + +static bool +same_expression (const iCode *const lic, const iCode *const ric) +{ + wassert(lic); + wassert(ric); + + if (lic->op != ric->op) + return (false); + + const operand *lleft = IC_LEFT (lic); + const operand *lright = IC_RIGHT (lic); + const operand *lresult = IC_RESULT (lic); + const operand *rleft = IC_LEFT (ric); + const operand *rright = IC_RIGHT (ric); + const operand *rresult = IC_RESULT (ric); + + if ((isOperandEqual (lleft, rleft) && isOperandEqual (lright, rright) || + IS_COMMUTATIVE (lic) && isOperandEqual (lleft, rright) && isOperandEqual (lright, rleft)) && + (lresult && rresult && compareTypeInexact (operandType (lresult), operandType (rresult)) > 0) && + IS_FLOAT (operandType (lresult)) == IS_FLOAT (operandType (rresult))) + return (true); + + return (false); +} + +static void +get_candidate_set(std::set *c, const iCode *const sic, int lkey) +{ + // TODO: For loop invariant code motion allow expression that only occurs once, too - will be needed when optimizing for speed. + for (const iCode *ic = sic; ic; ic = ic->next) + { + if (!candidate_expression (ic, lkey)) + continue; + for (const iCode *pic = sic; pic != ic; pic = pic->next) + if (candidate_expression (pic, lkey) && same_expression (ic, pic) && c->find (pic->key) == c->end ()) + { + // Found expression that occurs at least twice. + c->insert (pic->key); + break; + } + } +} + +static bool +invalidates_expression(const iCode *const eic, const iCode *const iic) +{ + const operand *const eleft = IC_LEFT (eic); + const operand *const eright = IC_RIGHT (eic); + const bool uses_global = (eic->op == GET_VALUE_AT_ADDRESS || isOperandGlobal (eleft) || isOperandGlobal (eright) || IS_SYMOP (eleft) && OP_SYMBOL_CONST (eleft)->addrtaken || IS_SYMOP (eright) && OP_SYMBOL_CONST (eright)->addrtaken); + const bool uses_volatile = POINTER_GET (eic) && IS_VOLATILE (operandType (eleft)->next) || IS_OP_VOLATILE (eleft) || IS_OP_VOLATILE (eright); + + const operand *const left = IC_LEFT (iic); + const operand *const right = IC_RIGHT (iic); + const operand *const result = IC_RESULT (iic); + + if (IC_RESULT (iic) && !IS_OP_LITERAL (result) && !POINTER_SET(iic) && + (eleft && isOperandEqual (eleft, result) || eright && isOperandEqual (eright, result))) + return(true); + if (iic->op == FUNCTION || iic->op == ENDFUNCTION || iic->op == RECEIVE) + return(true); + if ((uses_global || uses_volatile) && (iic->op == CALL || iic->op == PCALL)) + return(true); + if (uses_volatile && (POINTER_GET (iic) && IS_VOLATILE (operandType (left)->next)) || IS_OP_VOLATILE (left) || IS_OP_VOLATILE (right)) + return(true); + if (uses_global && POINTER_SET (iic)) // TODO: More accuracy here! + return(true); + + return(false); +} + +static bool +setup_cfg_for_expression (cfg_lospre_t *const cfg, const iCode *const eic) +{ + typedef boost::graph_traits::vertex_descriptor vertex_t; + bool safety_required = false; + + // In redundancy elimination, safety means not doing a computation on any path were it was not done before. + // This is important, if the compuation can have side-effects, which depends on the target architecure. + // E.g. On some systems division requires safety, since division by zero might result in an interrupt. + // When there are memory-mapped devices or there is memory management, reading from a pointer requires + // safety, since reading from an unknown location could result in making the device do something or in a SIGSEGV. + // On the other hand, addition is something that typically does not require safety, since adding two undefined + // operands gives just another undefined (the C standard allows trap representations, which, could result + // in addition requiring safety though; AFAIK none of the targets currently supported by SDCC have trap representations). + // Philipp, 2012-07-06. + // + // For now we just always require safety for "dangerous" operations. + // + // TODO: Replace the current one by a more exact mechanism, that takes into account information from + // (not yet implemented) generalized constant propagation, pointer analysis, etc. + + // Function calls can have any side effects. + if (eic->op == CALL || eic->op == PCALL) + safety_required = true; + + // volatile requires safety. + if (POINTER_GET (eic) && IS_VOLATILE (operandType (IC_LEFT (eic))->next) || IS_OP_VOLATILE (IC_LEFT (eic)) || IS_OP_VOLATILE (IC_RIGHT (eic))) + safety_required = true; + + // Reading from an invalid address might be dangerous, since there could be memory-mapped I/O. + if (eic->op == GET_VALUE_AT_ADDRESS && !optimize.allow_unsafe_read) + safety_required = true; + + // The division routines for z80-like ports and the hc08/s08's and stm8's hardware division just give an undefined result + // for division by zero, but there are no harmful side effects. I don't know about the other ports. + if ((eic->op == '/' || eic->op == '%') && !TARGET_Z80_LIKE && !TARGET_HC08_LIKE && !TARGET_IS_STM8) + safety_required = true; + + // TODO: Relax this! There are cases where allowing unsafe optimizations will improve speed. + // This probably needs implementation of profile-guided optimization though. + if (optimize.codeSpeed) + safety_required = true; + +#ifdef DEBUG_LOSPRE + std::cout << "Invalidation set I: "; +#endif + for (vertex_t i = 0; i < boost::num_vertices (*cfg); i++) + { + const iCode *const ic = (*cfg)[i].ic; + + (*cfg)[i].uses = same_expression (eic, ic); + (*cfg)[i].invalidates = invalidates_expression (eic, ic); + + (*cfg)[i].forward = std::pair(-1, -1); + +#ifdef DEBUG_LOSPRE + if ((*cfg)[i].invalidates) + std::cout << i << ", "; +#endif + } +#ifdef DEBUG_LOSPRE + std::cout << "\n"; +#endif + + return (safety_required); +} + +// Dump cfg, with numbered nodes. +void dump_cfg_lospre (const cfg_lospre_t &cfg) +{ + if (!currFunc) + return; + + std::ofstream dump_file((std::string(dstFileName) + ".dumplosprecfg" + currFunc->rname + ".dot").c_str()); + + std::string *name = new std::string[num_vertices(cfg)]; + for (unsigned int i = 0; i < boost::num_vertices(cfg); i++) + { + const char *iLine = printILine (cfg[i].ic); + std::ostringstream os; + os << i << ", " << cfg[i].ic->key << " : " << iLine; + dbuf_free (iLine); + name[i] = os.str(); + } + boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name), boost::default_writer(), cfg_titlewriter(currFunc->rname, "lospre")); + delete[] name; +} + +// Dump tree decomposition. +static void dump_dec_lospre(const tree_dec_t &tree_dec) +{ + wassert (currFunc); + + std::ofstream dump_file((std::string(dstFileName) + ".dumplospredec" + currFunc->rname + ".dot").c_str()); + + unsigned int w = 0; + + std::string *name = new std::string[num_vertices(tree_dec)]; + for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) + { + if (tree_dec[i].bag.size() > w) + w = tree_dec[i].bag.size(); + std::ostringstream os; + typename decltype(tree_dec[0].bag)::const_iterator v1; + os << i << " | "; + for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) + os << *v1 << " "; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name)); + delete[] name; +} + +void +lospre (iCode *sic, ebbIndex *ebbi) +{ + cfg_lospre_t control_flow_graph; + tree_dec_t tree_decomposition; + + wassert (sic); + +#ifdef DEBUG_LOSPRE + if (currFunc) + std::cout << "lospre for " << currFunc->rname << "()\n"; +#endif + + create_cfg_lospre (control_flow_graph, sic, ebbi); + + if(options.dump_graphs) + dump_cfg_lospre(control_flow_graph); + + get_nice_tree_decomposition (tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_dec_lospre(tree_decomposition); + + int lkey = operandKey; + + for (bool change = true; change;) + { + change = false; + + std::set candidate_set; + get_candidate_set (&candidate_set, sic, lkey); + + std::set::iterator ci, ci_end; + for (ci = candidate_set.begin(), ci_end = candidate_set.end(); ci != ci_end; ++ci) + { + const iCode *ic; + for (ic = sic; ic && ic->key != *ci; ic = ic->next); + + if (!ic || !candidate_expression (ic, lkey)) + continue; + + bool safety = setup_cfg_for_expression (&control_flow_graph, ic); + + if (safety && tree_dec_safety (tree_decomposition, control_flow_graph, ic) < 0) + continue; + + change |= (tree_dec_lospre (tree_decomposition, control_flow_graph, ic) > 0); + } + } +} + diff --git a/src/SDCClospre.hpp b/src/SDCClospre.hpp new file mode 100644 index 0000000..50df1cf --- /dev/null +++ b/src/SDCClospre.hpp @@ -0,0 +1,808 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2012 +// +// (c) 2012 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// +// Lifetime-optimal speculative partial redundancy elimination. + +// Workaround for boost bug #11880 +#include +#if BOOST_VERSION == 106000 + #include +#endif + +#include +#include +#include + +extern "C" +{ +#include "SDCCsymt.h" +#include "SDCCicode.h" +#include "SDCCgen.h" +#include "SDCCBBlock.h" +#include "SDCCopt.h" +#include "SDCCy.h" +#include "SDCCasm.h" +#include "port.h" +} + +typedef std::set lospreset_t; + +struct assignment_lospre +{ + boost::tuple s; // First entry: Calculation costs, second entry: Lifetime costs. + lospreset_t local; + std::vector global; + + bool operator<(const assignment_lospre& a) const + { + lospreset_t::const_iterator i, ai, i_end, ai_end; + + i_end = local.end(); + ai_end = a.local.end(); + + for (i = local.begin(), ai = a.local.begin();; ++i, ++ai) + { + if (i == i_end && ai == ai_end) + return(false); + if (i == i_end) + return(true); + if (ai == ai_end) + return(false); + + if (*i < *ai) + return(true); + if (*i > *ai) + return(false); + + if (global[*i] < a.global[*ai]) + return(true); + if (global[*i] > a.global[*ai]) + return(false); + } + } +}; + +bool assignments_lospre_locally_same(const assignment_lospre &a1, const assignment_lospre &a2) +{ + if (a1.local != a2.local) + return(false); + + lospreset_t::const_iterator i, i_end; + for (i = a1.local.begin(), i_end = a1.local.end(); i != i_end; ++i) + if (a1.global[*i] != a2.global[*i]) + return(false); + + return(true); +} + +struct cfg_lospre_node +{ + iCode *ic; + + bool uses; + bool invalidates; + + std::pair forward; +}; + +typedef std::list assignment_list_lospre_t; + +struct tree_dec_lospre_node +{ + lospreset_t bag; + + assignment_list_lospre_t assignments; + unsigned weight; // The weight is the number of nodes at which intermediate results need to be remembered. In general, to minimize memory consumption, at join nodes the child with maximum weight should be processed first. +}; + +typedef boost::adjacency_list cfg_lospre_t; // The edge property is the cost of subdividing the edge and inserting an instruction (for now we always use 1, optimizing for code size, but relative execution frequency could be used when optimizing for speed or total energy consumption; aggregates thereof can be a good idea as well). +typedef boost::adjacency_list tree_dec_t; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif + +#include "SDCCtree_dec.hpp" + +#ifdef DEBUG_LOSPRE +void print_assignment(const assignment_lospre &a, cfg_lospre_t G) +{ + wassert(a.global.size() == boost::num_vertices (G)); + for(unsigned int i = 0; i < boost::num_vertices (G); i++) + std::cout << "(" << i << ", " << a.global[i] << "),"; + std::cout << "\n"; + std::cout << "Cost: " << a.s << "\nLocal:"; + for(lospreset_t::const_iterator i = a.local.begin(); i != a.local.end(); ++i) + std::cout << *i << " "; + std::cout << "\n"; + std::cout.flush(); +} +#endif + +// Handle Leaf nodes in the nice tree decomposition +template +void tree_dec_lospre_leaf(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + assignment_lospre a; + assignment_list_lospre_t &alist = T[t].assignments; + + a.s.get<0>() = 0; + a.s.get<1>() = 0; + a.global.resize(boost::num_vertices(G)); + alist.push_back(a); +} + +// Handle introduce nodes in the nice tree decomposition +template +int tree_dec_lospre_introduce(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + assignment_list_lospre_t::iterator ai; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + assignment_list_lospre_t &alist = T[t].assignments; + std::swap(alist, T[*c].assignments); + + if(alist.size() > size_t(options.max_allocs_per_node) / 2) + { + alist.clear(); + return(-1); + } + + lospreset_t new_inst; + std::set_difference(T[t].bag.begin(), T[t].bag.end(), T[*c].bag.begin(), T[*c].bag.end(), std::inserter(new_inst, new_inst.end())); + unsigned int i = *(new_inst.begin()); + + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + ai->local.insert(i); + ai->global[i] = false; + ai = alist.insert(ai, *ai); + ++ai; + ai->global[i] = true; + } + + return(0); +} + +// Handle forget nodes in the nice tree decomposition +template +void tree_dec_lospre_forget(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + boost::tie(c, c_end) = adjacent_vertices(t, T); + +#ifdef DEBUG_LOSPRE_ASS + std::cout << "Forget (" << t << "):\n"; std::cout.flush(); +#endif + + assignment_list_lospre_t &alist = T[t].assignments; + + std::swap(alist, T[*c].assignments); + + lospreset_t old_inst; + std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); + unsigned int i = *(old_inst.begin()); + + assignment_list_lospre_t::iterator ai, aif; + + for (ai = alist.begin(); ai != alist.end(); ++ai) + { + ai->local.erase(i); + ai->s.get<1>() += ai->global[i]; // Add lifetime cost. + { + typedef typename boost::graph_traits::out_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::out_edges(i, G); n != n_end; ++n) + { + if (ai->local.find(boost::target(*n, G)) == ai->local.end() || (ai->global[i] && !G[i].invalidates) >= (ai->global[boost::target(*n, G)] || G[boost::target(*n, G)].uses)) + continue; + + ai->s.get<0>() += G[*n]; // Add calculation cost. + } + } + { + typedef typename boost::graph_traits::in_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::in_edges(i, G); n != n_end; ++n) + { + if (ai->local.find(boost::source(*n, G)) == ai->local.end() || (ai->global[boost::source(*n, G)] && !G[boost::source(*n, G)].invalidates) >= (ai->global[i] || G[i].uses)) + continue; + + ai->s.get<0>() += G[*n]; // Add calculation cost. + } + } + } + + alist.sort(); + +#ifdef DEBUG_LOSPRE_ASS + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai, G); + std::cout << "\n"; + } +#endif + + // Collapse (locally) identical assignments. + for (ai = alist.begin(); ai != alist.end();) + { + aif = ai; + + for (++ai; ai != alist.end() && assignments_lospre_locally_same(*aif, *ai);) + { + if (aif->s > ai->s) + { + alist.erase(aif); + aif = ai; + ++ai; + } + else + ai = alist.erase(ai); + } + } + + if(!alist.size()) + std::cerr << "No surviving assignments at forget node (lospre).\n"; + +#ifdef DEBUG_LOSPRE + std::cout << "Remaining assignments: " << alist.size() << "\n"; std::cout.flush(); +#endif + +#ifdef DEBUG_LOSPRE_ASS + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai, G); + std::cout << "\n"; + } +#endif +} + +// Handle join nodes in the nice tree decomposition +template +void tree_dec_lospre_join(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end, c2, c3; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + c2 = c; + ++c; + c3 = c; + + assignment_list_lospre_t &alist = T[t].assignments; + assignment_list_lospre_t &alist2 = T[*c2].assignments; + std::swap(alist, T[*c3].assignments); + + alist.sort(); + alist2.sort(); + + assignment_list_lospre_t::iterator ai, ai2; + for (ai = alist.begin(), ai2 = alist2.begin(); ai != alist.end() && ai2 != alist2.end();) + { + if (assignments_lospre_locally_same(*ai, *ai2)) + { + ai->s.get<0>() += ai2->s.get<0>(); + ai->s.get<1>() += ai2->s.get<1>(); + for (size_t i = 0; i < ai->global.size(); i++) + ai->global[i] = (ai->global[i] || ai2->global[i]); + + ++ai; + ++ai2; + } + else if (*ai < *ai2) + ai = alist.erase(ai); + else if (*ai2 < *ai) + ++ai2; + } + while(ai != alist.end()) + ai = alist.erase(ai); + + alist2.clear(); +} + +template +int tree_dec_lospre_nodes(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + tree_dec_lospre_leaf(T, t, G); + break; + case 1: + c0 = *c; + if(tree_dec_lospre_nodes(T, c0, G) < 0) + return(-1); + if (T[c0].bag.size() < T[t].bag.size()) + { + if (tree_dec_lospre_introduce(T, t, G)) + return(-1); + } + else + tree_dec_lospre_forget(T, t, G); + break; + case 2: + c0 = *c++; + c1 = *c; + if(tree_dec_lospre_nodes(T, c0, G) < 0) + return(-1); + if(tree_dec_lospre_nodes(T, c1, G) < 0) + { + T[c0].assignments.clear(); + return(-1); + } + tree_dec_lospre_join(T, t, G); + break; + default: + std::cerr << "Not nice.\n"; + break; + } + return(0); +} + +template +void tree_dec_safety_forget(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + assignment_list_lospre_t &alist = T[t].assignments; + + std::swap(alist, T[*c].assignments); + + lospreset_t old_inst; + std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); + unsigned int i = *(old_inst.begin()); + + assignment_list_lospre_t::iterator ai, aif; + + for(ai = alist.begin(); ai != alist.end();) + { + ai->local.erase(i); + + if (!ai->global[i]) + { + ++ai; + continue; + } + + // Since we want the union of all paths between invalidating nodes without uses, + // by definition there may not be a use in it. + if (G[i].uses) + { + ai = alist.erase(ai); + continue; + } + + ai->s.get<1>() -= 1; // Maximize the subsets: Find all paths + + // At least one successor needs to be in the path or invalid. + { + typedef typename boost::graph_traits::out_edge_iterator n_iter_t; + n_iter_t n, n_end; + bool ok; + + for (ok = false, boost::tie(n, n_end) = boost::out_edges(i, G); !ok && n != n_end; ++n) + if (ai->global[boost::target(*n, G)] || G[boost::target(*n, G)].invalidates && !G[boost::target(*n, G)].uses) + ok = true; + + if(!ok) + { + ai = alist.erase(ai); + continue; + } + } + // At least one predecessor needs to be in the path or invalid. + { + typedef typename boost::graph_traits::in_edge_iterator n_iter_t; + n_iter_t n, n_end; + bool ok; + + for (ok = false, boost::tie(n, n_end) = boost::in_edges(i, G); !ok && n != n_end; ++n) + if (ai->global[boost::source(*n, G)] || G[boost::source(*n, G)].invalidates) + ok = true; + + if(!ok) + { + ai = alist.erase(ai); + continue; + } + } + + ++ai; + } + + alist.sort(); + + // Collapse (locally) identical assignments. + for(ai = alist.begin(); ai != alist.end();) + { + aif = ai; + + for(++ai; ai != alist.end() && assignments_lospre_locally_same(*aif, *ai);) + { + if(aif->s > ai->s) + { + alist.erase(aif); + aif = ai; + ++ai; + } + else + { + alist.erase(ai); + ai = aif; + ++ai; + } + } + } + + if(!alist.size()) + std::cerr << "No surviving assignments at forget node.\n"; +} + +template +int tree_dec_safety_nodes(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + tree_dec_lospre_leaf(T, t, G); + break; + case 1: + c0 = *c; + if(tree_dec_safety_nodes(T, c0, G) < 0) + return(-1); + if (T[c0].bag.size() < T[t].bag.size()) + { + if (tree_dec_lospre_introduce(T, t, G)) + return(-1); + } + else + tree_dec_safety_forget(T, t, G); + break; + case 2: + c0 = *c++; + c1 = *c; + + if (T[c0].weight < T[c1].weight) // Minimize memory consumption. + std::swap (c0, c1); + + if(tree_dec_safety_nodes(T, c0, G) < 0) + return(-1); + if(tree_dec_safety_nodes(T, c1, G) < 0) + { + T[c0].assignments.clear(); + return(-1); + } + tree_dec_lospre_join(T, t, G); + break; + default: + std::cerr << "Not nice.\n"; + break; + } + return(0); +} + +template +typename boost::graph_traits::vertex_descriptor split_edge(T_t &T, G_t &G, typename boost::graph_traits::edge_descriptor e, const iCode *ic, operand *tmpop) +{ + // Insert new iCode into chain. + iCode *newic = newiCode (ic->op, IC_LEFT (ic), IC_RIGHT (ic)); + IC_RESULT(newic) = tmpop; + newic->filename = ic->filename; + newic->lineno = ic->lineno; + newic->prev = G[boost::source(e, G)].ic; + newic->next = G[boost::target(e, G)].ic; + newic->count = G[boost::source(e, G)].ic->count; + G[boost::source(e, G)].ic->next = newic; + G[boost::target(e, G)].ic->prev = newic; + + //if (ic->op != ADDRESS_OF && IC_LEFT (ic) && IS_ITEMP (IC_LEFT (ic))) + // bitVectSetBit (OP_SYMBOL (IC_LEFT (ic))->uses, ic->key); + //if (IC_RIGHT (ic) && IS_ITEMP (IC_RIGHT (ic))) + // bitVectSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + //bitVectSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + + // Insert node into cfg. + typename boost::graph_traits::vertex_descriptor n = boost::add_vertex(G); + + G[n].ic = newic; + G[n].uses = false; + G[n].invalidates = false; + boost::add_edge(boost::source(e, G), n, G[e], G); + boost::add_edge(n, boost::target(e, G), G[e], G); + +#ifdef DEBUG_LOSPRE + std::cout << "Calculating " << OP_SYMBOL_CONST(tmpop)->name << " at ic " << newic->key << "\n"; +#endif + + // Update tree-decomposition. + // TODO: More efficiently. + for(typename boost::graph_traits::vertex_descriptor n1 = 0; n1 < boost::num_vertices(T); ++n1) + { + if(T[n1].bag.find(boost::source(e, G)) == T[n1].bag.end()) + continue; + if(T[n1].bag.find(boost::target(e, G)) == T[n1].bag.end()) + continue; + // Found bag that contains both endpoints of original edge. + + // Add new tree node with bag there. Let nicify() sort things out later. + typename boost::graph_traits::vertex_descriptor n2 = boost::add_vertex(T); + T[n2].bag.insert(boost::source(e, G)); + T[n2].bag.insert(boost::target(e, G)); + T[n2].bag.insert(n); + boost::add_edge(n1, n2, T); + break; + } + + // Remove old edge from cfg. + boost::remove_edge(e, G); + + return(n); +} + + +template +static void forward_lospre_assignment(G_t &G, typename boost::graph_traits::vertex_descriptor i, const iCode *ic, const assignment_lospre& a) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + + operand *tmpop = IC_RIGHT(ic); + const std::pair forward(IC_RESULT(ic)->key, IC_RIGHT(ic)->key); + + for(;;) + { + if (G[i].forward == forward) + break; // Was here before. + + iCode *nic = G[i].ic; + + if (isOperandEqual(IC_RESULT(ic), IC_LEFT(nic)) && IS_UNSIGNED (operandType (tmpop)) == IS_UNSIGNED (operandType (IC_LEFT(nic))) && nic->op != ADDRESS_OF && nic->op != PCALL && (!POINTER_GET(nic) || !IS_PTR(operandType(IC_LEFT(nic))) || !IS_BITFIELD(operandType(IC_LEFT(nic))->next) || compareType(operandType(IC_LEFT(nic)), operandType(tmpop)) == 1)) + { + bool isaddr = IC_LEFT (nic)->isaddr; +#ifdef DEBUG_LOSPRE + std::cout << "Forward substituted left operand " << OP_SYMBOL_CONST(IC_LEFT(nic))->name << " at " << nic->key << "\n"; +#endif + bitVectUnSetBit (OP_SYMBOL (IC_LEFT (nic))->uses, nic->key); + IC_LEFT(nic) = operandFromOperand (tmpop); + //bitVectSetBit (OP_SYMBOL (IC_LEFT (nic))->uses, nic->key); + IC_LEFT (nic)->isaddr = isaddr; + } + if (isOperandEqual(IC_RESULT(ic), IC_RIGHT(nic))) + { +#ifdef DEBUG_LOSPRE + std::cout << "Forward substituted right operand " << OP_SYMBOL_CONST(IC_RIGHT(nic))->name << " at " << nic->key << "\n"; +#endif + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (nic))->uses, nic->key); + IC_RIGHT(nic) = operandFromOperand (tmpop); + //bitVectSetBit (OP_SYMBOL (IC_RIGHT (nic))->uses, nic->key); + } + if (POINTER_SET(nic) && isOperandEqual(IC_RESULT(ic), IC_RESULT(nic)) && (!IS_PTR(operandType(IC_RESULT(nic))) || !IS_BITFIELD(operandType(IC_RESULT(nic))->next) || compareType(operandType(IC_RESULT(nic)), operandType(tmpop)) == 1)) + { +#ifdef DEBUG_LOSPRE + std::cout << "Forward substituted result operand " << OP_SYMBOL_CONST(IC_RESULT(nic))->name << " at " << nic->key << "\n"; +#endif + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (nic))->uses, nic->key); + IC_RESULT(nic) = operandFromOperand (tmpop); + IC_RESULT(nic)->isaddr = true; + //bitVectSetBit (OP_SYMBOL (IC_RESULT (nic))->uses, nic->key); + } + + if (nic->op == LABEL) // Reached label. Continue only if all edges goining here are safe. + { + typedef typename boost::graph_traits::in_edge_iterator in_edge_iter_t; + in_edge_iter_t e, e_end; + for (boost::tie(e, e_end) = boost::in_edges(i, G); e != e_end; ++e) + if (G[boost::source(*e, G)].forward != forward) + break; + if(e != e_end) + break; + } + if (isOperandEqual(IC_RESULT (ic), IC_RESULT(nic)) && !POINTER_SET(nic) /*|| G[i].uses*/) + break; + if ((nic->op == CALL || nic->op == PCALL || POINTER_SET(nic)) && IS_TRUE_SYMOP(IC_RESULT(ic))) + break; + + G[i].forward = forward; + + if (nic->op == GOTO || nic->op == IFX || nic->op == JUMPTABLE) + { + adjacency_iter_t c, c_end; + for(boost::tie(c, c_end) = boost::adjacent_vertices(i, G); c != c_end; ++c) + { + if(!(a.global[i] && !G[i].invalidates) && a.global[*c]) // Calculation edge + continue; + forward_lospre_assignment(G, *c, ic, a); + } + break; + } + + boost::tie(c, c_end) = adjacent_vertices(i, G); + if(c == c_end) + break; + if(!(a.global[i] && !G[i].invalidates) && a.global[*c]) // Calculation edge + break; + i = *c; + } +} + +template +static int implement_lospre_assignment(assignment_lospre a, T_t &T, G_t &G, const iCode *ic) // Assignment has to be passed as a copy (not reference), since the transformations on the tree-decomposition will invalidate it otherwise. +{ + operand *tmpop; + unsigned substituted = 0, split = 0; + + typedef typename boost::graph_traits::edge_iterator edge_iter_t; + typedef typename boost::graph_traits::edge_descriptor edge_desc_t; + std::set calculation_edges; // Use descriptor, not iterator due to possible invalidation of iterators when inserting vertices or edges. + edge_iter_t e, e_end; + for(boost::tie(e, e_end) = boost::edges(G); e != e_end; ++e) + if(!((a.global[boost::source(*e, G)] & true) && !G[boost::source(*e, G)].invalidates) && (a.global[boost::target(*e, G)] & true)) + calculation_edges.insert(*e); + + if(!calculation_edges.size()) + return(0); + +#ifdef DEBUG_LOSPRE + std::cout << "Optimizing at " << ic->key << "\n"; std::cout.flush(); +#endif + + tmpop = newiTempOperand (operandType (IC_RESULT (ic)), TRUE); + tmpop->isvolatile = false; +#ifdef DEBUG_LOSPRE + std::cout << "New tmpop: " << OP_SYMBOL_CONST(tmpop)->name << " "; printTypeChain(operandType (IC_RESULT(ic)), stdout); std::cout << "\n"; +#endif + + for(typename std::set::iterator i = calculation_edges.begin(); i != calculation_edges.end(); ++i) + { + typename boost::graph_traits::vertex_descriptor n = split_edge(T, G, *i, ic, tmpop); + a.global.resize(boost::num_vertices(G)); + a.global[n] = true; + split++; + } + + typedef typename boost::graph_traits::vertex_iterator vertex_iter_t; + vertex_iter_t v, v_end; + + for(boost::tie(v, v_end) = boost::vertices(G); v != v_end; ++v) + { + if(!G[*v].uses) + continue; + typename boost::graph_traits::in_edge_iterator e = in_edges(*v, G).first; + if (a.global.size() <= *v) + continue; + if(!((a.global[*v] & true) && !G[*v].invalidates || boost::source(*e, G) < a.global.size() && (a.global[boost::source(*e, G)] & true))) + continue; +#ifdef DEBUG_LOSPRE + std::cerr << "Substituting ic " << G[*v].ic->key << "\n"; +#endif + substituted++; + + iCode *ic = G[*v].ic; + + if (IS_SYMOP (IC_LEFT (ic))) + bitVectUnSetBit (OP_SYMBOL (IC_LEFT (ic))->uses, ic->key); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + IC_RIGHT(ic) = tmpop; + //bitVectSetBit (OP_SYMBOL (IC_RIGHT(ic))->uses, ic->key); + if (!POINTER_SET (ic)) + { + IC_LEFT(ic) = 0; + ic->op = '='; + IC_RESULT(ic) = operandFromOperand (IC_RESULT (ic)); + IC_RESULT(ic)->isaddr = 0; + } + if(IS_OP_VOLATILE(IC_RESULT (ic))) + continue; + + { + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + boost::tie(c, c_end) = adjacent_vertices(*v, G); + if (c != c_end) + forward_lospre_assignment(G, *c, ic, a); + } + } + + if(substituted <= 0) + { + std::cerr << "Introduced " << OP_SYMBOL_CONST(tmpop)->name << ", but did not substitute any calculations.\n"; + return (-1); + } + + if(substituted < split) // Todo: Remove this warning when optimization for speed instead of code size is implemented! + std::cout << "Introduced " << OP_SYMBOL_CONST(tmpop)->name << ", but did substitute only " << substituted << " calculations, while introducing "<< split << ".\n"; std::cout.flush(); + + return(1); +} + +/* Using a template here confuses debugging tools such as valgrind. */ +/*template */ +static int tree_dec_lospre (tree_dec_t/*T_t*/ &T, cfg_lospre_t/*G_t*/ &G, const iCode *ic) +{ + if(tree_dec_lospre_nodes(T, find_root(T), G)) + return(-1); + + wassert(T[find_root(T)].assignments.begin() != T[find_root(T)].assignments.end()); + const assignment_lospre &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_LOSPRE + std::cout << "Winner (lospre): "; + print_assignment(winner, G); +#endif + + int change; + if (change = implement_lospre_assignment(winner, T, G, ic)) + nicify (T); + T[find_root(T)].assignments.clear(); + return(change); +} + +template +static void implement_safety(const assignment_lospre &a, G_t &G) +{ + typedef typename boost::graph_traits::vertex_iterator vertex_iter_t; + vertex_iter_t v, v_end; + + for(boost::tie(v, v_end) = boost::vertices(G); v != v_end; ++v) + G[*v].invalidates |= a.global[*v]; +} + +/* Using a template here confuses debugging tools such as valgrind. */ +/*template */ +static int tree_dec_safety (tree_dec_t/*T_t*/ &T, cfg_lospre_t/*G_t*/ &G, const iCode *ic) +{ + if(tree_dec_safety_nodes(T, find_root(T), G)) + return(-1); + + wassert(T[find_root(T)].assignments.begin() != T[find_root(T)].assignments.end()); + const assignment_lospre &winner = *(T[find_root(T)].assignments.begin()); + + implement_safety(winner, G); + +#ifdef DEBUG_LOSPRE + std::cout << "Winner (safety) (I' \\ I): "; + print_assignment(winner, G); +#endif + + T[find_root(T)].assignments.clear(); + return (0); +} + diff --git a/src/SDCClrange.c b/src/SDCClrange.c new file mode 100644 index 0000000..13b20ee --- /dev/null +++ b/src/SDCClrange.c @@ -0,0 +1,1248 @@ +/*------------------------------------------------------------------------- + + SDCClrange.c - source file for live range computations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "limits.h" + +int iCodeSeq = 0; +hTab *liveRanges = NULL; +hTab *iCodehTab = NULL; +hTab *iCodeSeqhTab = NULL; + +/* all symbols, for which the previous definition is searched + and warning is emitted if there's none. */ +#define IS_AUTOSYM(op) (IS_ITEMP(op) || \ + (IS_SYMOP(op) && IS_AUTO(OP_SYMBOL (op)) && !IS_PARM(op))) + +/*-----------------------------------------------------------------*/ +/* hashiCodeKeys - add all iCodes to the hash table */ +/*-----------------------------------------------------------------*/ +void +hashiCodeKeys (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + iCode *ic; + for (ic = ebbs[i]->sch; ic; ic = ic->next) + hTabAddItem (&iCodehTab, ic->key, ic); + } +} + +/*-----------------------------------------------------------------*/ +/* sequenceiCode - creates a sequence number for the iCode & add */ +/*-----------------------------------------------------------------*/ +static void +sequenceiCode (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + + iCode *ic; + ebbs[i]->fSeq = iCodeSeq + 1; + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + ic->seq = ++iCodeSeq; + ic->depth = ebbs[i]->depth; + //hTabAddItem (&iCodehTab, ic->key, ic); + hTabAddItem (&iCodeSeqhTab, ic->seq, ic); + } + ebbs[i]->lSeq = iCodeSeq; + } +} + +/*-----------------------------------------------------------------*/ +/* setFromRange - sets the from range of a given operand */ +/*-----------------------------------------------------------------*/ +#if 0 +static void +setFromRange (operand * op, int from) +{ + /* only for compiler defined temporaries */ + if (!IS_ITEMP (op)) + return; + + hTabAddItemIfNotP (&liveRanges, op->key, OP_SYMBOL (op)); + + if (op->isaddr) + OP_SYMBOL (op)->isptr = 1; + + if (!OP_LIVEFROM (op) || + OP_LIVEFROM (op) > from) + OP_LIVEFROM (op) = from; +} +#endif + +/*-----------------------------------------------------------------*/ +/* setToRange - set the range to for an operand */ +/*-----------------------------------------------------------------*/ +void +setToRange (operand * op, int to, bool check) +{ + /* only for compiler defined temps */ + if (!IS_ITEMP (op)) + return; + + OP_SYMBOL (op)->key = op->key; + hTabAddItemIfNotP (&liveRanges, op->key, OP_SYMBOL (op)); + + if (op->isaddr) + OP_SYMBOL (op)->isptr = 1; + + if (check) + if (!OP_LIVETO (op)) + OP_LIVETO (op) = to; + else; + else + OP_LIVETO (op) = to; +} + +/*-----------------------------------------------------------------*/ +/* setFromRange - sets the from range of a given operand */ +/*-----------------------------------------------------------------*/ +static void +setLiveFrom (symbol * sym, int from) +{ + if (!sym->liveFrom || sym->liveFrom > from) + sym->liveFrom = from; +} + +/*-----------------------------------------------------------------*/ +/* setToRange - set the range to for an operand */ +/*-----------------------------------------------------------------*/ +static void +setLiveTo (symbol *sym, int to) +{ + if (!sym->liveTo || sym->liveTo < to) + sym->liveTo = to; +} + +/*-----------------------------------------------------------------*/ +/* markLiveRanges - for each operand mark the liveFrom & liveTo */ +/*-----------------------------------------------------------------*/ +static void +markLiveRanges (eBBlock **ebbs, int count) +{ + int i, key; + symbol *sym; + + for (i = 0; i < count; i++) + { + iCode *ic; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == CALL || ic->op == PCALL) + if (bitVectIsZero (OP_SYMBOL (IC_RESULT (ic))->uses)) + bitVectUnSetBit (ebbs[i]->defSet, ic->key); + + /* for all iTemps alive at this iCode */ + for (key = 1; key < ic->rlive->size; key++) + { + if (!bitVectBitValue(ic->rlive, key)) + continue; + + sym = hTabItemWithKey(liveRanges, key); + setLiveTo(sym, ic->seq); + setLiveFrom(sym, ic->seq); + } + + } + } +} + +/*-----------------------------------------------------------------*/ +/* markAlive - marks the operand as alive between sic and eic */ +/*-----------------------------------------------------------------*/ +static void +markAlive (iCode * sic, iCode * eic, int key) +{ + iCode *dic; + + for (dic = sic; dic != eic->next; dic = dic->next) + { + dic->rlive = bitVectSetBit (dic->rlive, key); + } +} + +/*-----------------------------------------------------------------*/ +/* findNextUseSym - finds the next use of the symbol and marks it */ +/* alive in between */ +/* Return 0 iff there is no next use. */ +/*-----------------------------------------------------------------*/ +static int +findNextUseSym (eBBlock *ebp, iCode *ic, symbol * sym) +{ + int retval = 0; + iCode *uic; + eBBlock *succ; + + hTabAddItemIfNotP (&liveRanges, sym->key, sym); + + if (!ic) + goto check_successors; + + /* if we check a complete block and the symbol */ + /* is alive at the beginning of the block */ + /* and not defined in the first instructions */ + /* then a next use exists (return 1) */ + if ((ic == ebp->sch) && bitVectBitValue(ic->rlive, sym->key)) + { + /* check if the first instruction is a def of this op */ + if (ic->op == JUMPTABLE || ic->op == IFX || SKIP_IC2(ic)) + return 1; + + if (IS_ITEMP(IC_RESULT(ic))) + if (IC_RESULT(ic)->key == sym->key) + return 0; + + return 1; + } + + if (ebp->visited) + return 0; + + if (ic == ebp->sch) + ebp->visited = 1; + + /* for all remaining instructions in current block */ + for (uic = ic; uic; uic = uic->next) + { + + if (SKIP_IC2(uic)) + continue; + + if (uic->op == JUMPTABLE) + { + if (IS_ITEMP(IC_JTCOND(uic)) && IC_JTCOND(uic)->key == sym->key) + { + markAlive(ic, uic, sym->key); + return 1; + } + continue; + } + + if (uic->op == IFX) + { + if (IS_ITEMP(IC_COND(uic)) && IC_COND(uic)->key == sym->key) + { + markAlive(ic, uic, sym->key); + return 1; + } + continue; + } + + if (IS_ITEMP (IC_LEFT (uic))) + if (IC_LEFT (uic)->key == sym->key) + { + markAlive(ic, uic, sym->key); + return 1; + } + + if (IS_ITEMP (IC_RIGHT (uic))) + if (IC_RIGHT (uic)->key == sym->key) + { + markAlive (ic, uic, sym->key); + return 1; + } + + if (IS_ITEMP (IC_RESULT (uic))) + if (IC_RESULT (uic)->key == sym->key) + { + if (POINTER_SET (uic)) + { + markAlive (ic, uic, sym->key); + return 1; + } + else + return 0; + } + + } + + /* check all successors */ +check_successors: + + succ = setFirstItem (ebp->succList); + for (; succ; succ = setNextItem (ebp->succList)) + { + retval += findNextUseSym (succ, succ->sch, sym); + } + + if (retval) + { + if (ic) markAlive (ic, ebp->ech, sym->key); + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findNextUse - finds the next use of the operand and marks it */ +/* alive in between */ +/* Return 0 iff there is no next use. */ +/*-----------------------------------------------------------------*/ +static int +findNextUse (eBBlock *ebp, iCode *ic, operand *op) +{ + if (op->isaddr) + OP_SYMBOL (op)->isptr = 1; + + OP_SYMBOL (op)->key = op->key; + + return findNextUseSym (ebp, ic, OP_SYMBOL(op)); +} + +/*-----------------------------------------------------------------*/ +/* unvisitBlocks - clears visited in all blocks */ +/*-----------------------------------------------------------------*/ +static void +unvisitBlocks (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + ebbs[i]->visited = 0; +} + +/*------------------------------------------------------------------*/ +/* markWholeLoop - mark the symbol 'key' alive in all blocks */ +/* included by the outermost loop */ +/*------------------------------------------------------------------*/ +static void +markWholeLoop (eBBlock *ebp, int key) +{ + eBBlock *ebpi; + + /* avoid endless loops */ + ebp->visited = 1; + + /* recurse through all predecessors */ + for (ebpi = setFirstItem (ebp->predList); + ebpi; + ebpi = setNextItem (ebp->predList)) + { + if (ebpi->visited) + continue; + /* is the predecessor still in the loop? */ + if (ebpi->depth == 0) + continue; + markWholeLoop (ebpi, key); + } + + /* recurse through all successors */ + for (ebpi = setFirstItem (ebp->succList); + ebpi; + ebpi = setNextItem (ebp->succList)) + { + if (ebpi->visited) + continue; + if (ebpi->depth == 0) + continue; + markWholeLoop (ebpi, key); + } + + markAlive (ebp->sch, ebp->ech, key); +} + +/*------------------------------------------------------------------*/ +/* findPrevUseSym - search for a previous definition of a symbol in */ +/* - the previous icodes */ +/* - all branches of predecessors */ +/*------------------------------------------------------------------*/ +static bool +findPrevUseSym (eBBlock *ebp, iCode *ic, symbol * sym) +{ + eBBlock * pred; + iCode * uic; + + if (ebp->visited) + { + /* already visited: this branch must have been succesfull, */ + /* because otherwise the search would have been aborted. */ + return TRUE; + } + ebp->visited = 1; + + /* search backward in the current block */ + for (uic = ic; uic; uic = uic->prev) + { + if (!POINTER_SET (uic) && IS_AUTOSYM (IC_RESULT (uic))) + { + if (IC_RESULT (uic)->key == sym->key) + { + /* Ok, found a definition */ + return TRUE; + } + } + /* address taken from symbol? */ + if (uic->op == ADDRESS_OF && IS_AUTOSYM (IC_LEFT (uic))) + { + if (IC_LEFT (uic)->key == sym->key) + { + /* Ok, found a definition */ + return TRUE; + } + } + } + + /* There's no definition in this bblock, */ + /* let's have a look at all predecessors. */ + pred = setFirstItem (ebp->predList); + if (!pred) + { + /* no more predecessors and nothing found yet :-( */ + return FALSE; + } + for (; pred; pred = setNextItem (ebp->predList)) + { + /* recurse into all predecessors */ + if (!findPrevUseSym (pred, pred->ech, sym)) + { + /* found nothing: abort */ + return FALSE; + } + } + + /* Success! Went through all branches with no abort: */ + /* all branches end with a definition */ + return TRUE; +} + +/*------------------------------------------------------------------*/ +/* findPrevUse - search for a previous definition of an operand */ +/* If there's no definition let's: */ +/* - emit a warning */ +/* - fix the life range, if the symbol is used in */ +/* a loop */ +/*------------------------------------------------------------------*/ +static void +findPrevUse (eBBlock *ebp, iCode *ic, operand *op, + eBBlock ** ebbs, int count, + bool emitWarnings) +{ + unvisitBlocks (ebbs, count); + + if (op->isaddr) + OP_SYMBOL (op)->isptr = 1; + OP_SYMBOL (op)->key = op->key; + + /* There must be a definition in each branch of predecessors */ + if (!findPrevUseSym (ebp, ic->prev, OP_SYMBOL(op))) + { + /* computeLiveRanges() is called at least twice */ + if (emitWarnings) + { + if (IS_ITEMP (op)) + { + if (OP_SYMBOL (op)->prereqv) + { + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, + OP_SYMBOL (op)->prereqv->name); + OP_SYMBOL (op)->prereqv->reqv = NULL; + OP_SYMBOL (op)->prereqv->allocreq = 1; + } + } + else + { + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, + OP_SYMBOL (op)->name); + } + } + /* is this block part of a loop? */ + if (IS_ITEMP (op) && ebp->depth != 0) + { + /* extend the life range to the outermost loop */ + unvisitBlocks(ebbs, count); + markWholeLoop (ebp, op->key); + } + } +} + +/*-----------------------------------------------------------------*/ +/* incUsed - increment a symbol's usage count */ +/*-----------------------------------------------------------------*/ +static void +incUsed (iCode *ic, operand *op) +{ + if (ic->depth) + OP_SYMBOL (op)->used += (((unsigned int) 1 << ic->depth) + 1); + else + OP_SYMBOL (op)->used += 1; +} + +/*-----------------------------------------------------------------*/ +/* rliveClear - clears the rlive bitVectors */ +/*-----------------------------------------------------------------*/ +static void +rliveClear (eBBlock **ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in this block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + freeBitVect (ic->rlive); + ic->rlive = NULL; + } + } +} + +/*-----------------------------------------------------------------*/ +/* rlivePoint - for each point compute the ranges that are alive */ +/* The live range is only stored for ITEMPs; the same code is used */ +/* to find use of unitialized AUTOSYMs (an ITEMP is an AUTOSYM). */ +/* also, update funcUsesVolatile flag for current function */ +/*-----------------------------------------------------------------*/ +static void +rlivePoint (eBBlock ** ebbs, int count, bool emitWarnings) +{ + int i, key; + eBBlock *succ; + bitVect *alive; + + bool uses_volatile = false; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in this block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + uses_volatile |= POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT(ic))->next) || IS_OP_VOLATILE (IC_LEFT(ic)) || IS_OP_VOLATILE (IC_RIGHT(ic)); + uses_volatile |= POINTER_SET (ic) && IS_VOLATILE (operandType (IC_RESULT(ic))->next) || IS_OP_VOLATILE (IC_RESULT(ic)); + + if (!ic->rlive) + ic->rlive = newBitVect (operandKey); + + if (SKIP_IC2(ic)) + continue; + + if (ic->op == JUMPTABLE && IS_SYMOP(IC_JTCOND(ic))) + { + incUsed (ic, IC_JTCOND(ic)); + + if (!IS_AUTOSYM(IC_JTCOND(ic))) + continue; + + findPrevUse (ebbs[i], ic, IC_JTCOND(ic), ebbs, count, emitWarnings); + if (IS_ITEMP(IC_JTCOND(ic))) + { + unvisitBlocks(ebbs, count); + ic->rlive = bitVectSetBit (ic->rlive, IC_JTCOND(ic)->key); + findNextUse (ebbs[i], ic->next, IC_JTCOND(ic)); + } + + continue; + } + + if (ic->op == IFX && IS_SYMOP(IC_COND(ic))) + { + incUsed (ic, IC_COND(ic)); + + if (!IS_AUTOSYM(IC_COND(ic))) + continue; + + findPrevUse (ebbs[i], ic, IC_COND(ic), ebbs, count, emitWarnings); + if (IS_ITEMP(IC_COND(ic))) + { + unvisitBlocks (ebbs, count); + ic->rlive = bitVectSetBit (ic->rlive, IC_COND(ic)->key); + findNextUse (ebbs[i], ic->next, IC_COND(ic)); + } + + continue; + } + + if (IS_SYMOP(IC_LEFT(ic))) + { + incUsed (ic, IC_LEFT(ic)); + if (IS_AUTOSYM(IC_LEFT(ic)) && + ic->op != ADDRESS_OF) + { + findPrevUse (ebbs[i], ic, IC_LEFT(ic), ebbs, count, emitWarnings); + if (IS_ITEMP(IC_LEFT(ic))) + { + unvisitBlocks(ebbs, count); + ic->rlive = bitVectSetBit (ic->rlive, IC_LEFT(ic)->key); + findNextUse (ebbs[i], ic->next, IC_LEFT(ic)); + + /* if this is a send extend the LR to the call */ + if (ic->op == SEND) + { + iCode *lic; + for (lic = ic; lic; lic = lic->next) + { + if (lic->op == CALL || lic->op == PCALL) + { + markAlive (ic, lic->prev, IC_LEFT (ic)->key); + break; + } + } + } + } + } + } + + if (IS_SYMOP(IC_RIGHT(ic))) + { + incUsed (ic, IC_RIGHT(ic)); + if (IS_AUTOSYM(IC_RIGHT(ic))) + { + findPrevUse (ebbs[i], ic, IC_RIGHT(ic), ebbs, count, emitWarnings); + if (IS_ITEMP(IC_RIGHT(ic))) + { + unvisitBlocks(ebbs, count); + ic->rlive = bitVectSetBit (ic->rlive, IC_RIGHT(ic)->key); + findNextUse (ebbs[i], ic->next, IC_RIGHT(ic)); + } + } + } + + if (POINTER_SET(ic) && IS_SYMOP(IC_RESULT(ic))) + incUsed (ic, IC_RESULT(ic)); + + if (IS_AUTOSYM(IC_RESULT(ic))) + { + if (POINTER_SET(ic)) + { + findPrevUse (ebbs[i], ic, IC_RESULT(ic), ebbs, count, emitWarnings); + } + if (IS_ITEMP(IC_RESULT(ic))) + { + unvisitBlocks(ebbs, count); + ic->rlive = bitVectSetBit (ic->rlive, IC_RESULT(ic)->key); + findNextUse (ebbs[i], ic->next, IC_RESULT(ic)); + /* findNextUse sometimes returns 0 here, which means that ic is + dead code. Something should be done about this dead code since + e.g. register allocation suffers. */ + } + } + + if (!POINTER_SET(ic) && IC_RESULT(ic)) + ic->defKey = IC_RESULT(ic)->key; + + } + + /* check all symbols that are alive in the last instruction */ + /* but are not alive in all successors */ + + succ = setFirstItem (ebbs[i]->succList); + if (!succ) + continue; + + alive = succ->sch->rlive; + while ((succ = setNextItem (ebbs[i]->succList))) + { + if (succ->sch) + alive = bitVectIntersect (alive, succ->sch->rlive); + } + + if (ebbs[i]->ech) + alive = bitVectCplAnd ( bitVectCopy (ebbs[i]->ech->rlive), alive); + + if(!alive) + continue; + for (key = 1; key < alive->size; key++) + { + if (!bitVectBitValue (alive, key)) + continue; + + unvisitBlocks(ebbs, count); + findNextUseSym (ebbs[i], NULL, hTabItemWithKey (liveRanges, key)); + } + + } + + if(currFunc) + currFunc->funcUsesVolatile = uses_volatile; +} + +/*-----------------------------------------------------------------*/ +/* computeClash - find out which live ranges collide with others */ +/*-----------------------------------------------------------------*/ +static void +computeClash (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for every iCode do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + symbol *sym1, *sym2; + int key1, key2; + + /* for all iTemps alive at this iCode */ + for (key1 = 1; key1 < ic->rlive->size; key1++) + { + if (!bitVectBitValue(ic->rlive, key1)) + continue; + + sym1 = hTabItemWithKey(liveRanges, key1); + + if (!sym1->isitmp) + continue; + + /* for all other iTemps alive at this iCode */ + for (key2 = key1+1; key2 < ic->rlive->size; key2++) + { + if (!bitVectBitValue(ic->rlive, key2)) + continue; + + sym2 = hTabItemWithKey(liveRanges, key2); + + if (!sym2->isitmp) + continue; + + /* if the result and left or right is an iTemp */ + /* than possibly the iTemps do not clash */ + if ((ic->op != JUMPTABLE) && (ic->op != IFX) && + IS_ITEMP(IC_RESULT(ic)) && + (IS_ITEMP(IC_LEFT(ic)) || IS_ITEMP(IC_RIGHT(ic)))) + { + if (OP_SYMBOL(IC_RESULT(ic))->key == key1 + && sym1->liveFrom == ic->seq + && sym2->liveTo == ic->seq) + { + if (IS_SYMOP(IC_LEFT(ic))) + if (OP_SYMBOL(IC_LEFT(ic))->key == key2) + continue; + if (IS_SYMOP(IC_RIGHT(ic))) + if (OP_SYMBOL(IC_RIGHT(ic))->key == key2) + continue; + } + + if (OP_SYMBOL(IC_RESULT(ic))->key == key2 + && sym2->liveFrom == ic->seq + && sym1->liveTo == ic->seq) + { + if (IS_SYMOP(IC_LEFT(ic))) + if (OP_SYMBOL(IC_LEFT(ic))->key == key1) + continue; + if (IS_SYMOP(IC_RIGHT(ic))) + if (OP_SYMBOL(IC_RIGHT(ic))->key == key1) + continue; + } + } + + /* the iTemps do clash. set the bits in clashes */ + sym1->clashes = bitVectSetBit (sym1->clashes, key2); + sym2->clashes = bitVectSetBit (sym2->clashes, key1); + + /* check if they share the same spill location */ + /* what is this good for? */ + if (SYM_SPIL_LOC(sym1) && SYM_SPIL_LOC(sym2) && + SYM_SPIL_LOC(sym1) == SYM_SPIL_LOC(sym2)) + { + if (sym1->reqv && !sym2->reqv) SYM_SPIL_LOC(sym2)=NULL; + else if (sym2->reqv && !sym1->reqv) SYM_SPIL_LOC(sym1)=NULL; + else if (sym1->used > sym2->used) SYM_SPIL_LOC(sym2)=NULL; + else SYM_SPIL_LOC(sym1)=NULL; + } + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* allDefsOutOfRange - all definitions are out of a range */ +/*-----------------------------------------------------------------*/ +bool +allDefsOutOfRange (bitVect * defs, int fseq, int toseq) +{ + int i; + + if (!defs) + return TRUE; + + for (i = 0; i < defs->size; i++) + { + iCode *ic; + + if (bitVectBitValue (defs, i) && + (ic = hTabItemWithKey (iCodehTab, i)) && + (ic->seq >= fseq && ic->seq <= toseq)) + return FALSE; + + } + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInBlock - not used in this block */ +/*-----------------------------------------------------------------*/ +int +notUsedInBlock (symbol * sym, eBBlock * ebp, iCode *ic) +{ + return (!bitVectBitsInCommon (sym->defs, ebp->usesDefs) && + allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq) && + allDefsOutOfRange (sym->uses, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* adjustIChain - correct the sch and ech pointers */ +/*-----------------------------------------------------------------*/ +void +adjustIChain (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + ic = ebbs[i]->sch; + + /* is there any code for this eBBlock? (e.g. ROM assignment) */ + if(!ic)continue; + + while (ic->prev) + ic = ic->prev; + ebbs[i]->sch = ic; + + ic = ebbs[i]->ech; + while (ic->next) + ic = ic->next; + ebbs[i]->ech = ic; + } +} + +/*-----------------------------------------------------------------*/ +/* computeLiveRanges - computes the live ranges for variables */ +/*-----------------------------------------------------------------*/ +void +computeLiveRanges (eBBlock **ebbs, int count, bool emitWarnings) +{ + /* first look through all blocks and adjust the + sch and ech pointers */ + adjustIChain (ebbs, count); + + /* sequence the code the live ranges are computed + in terms of this sequence additionally the + routine will also create a hashtable of instructions */ + iCodeSeq = 0; + setToNull ((void *) &iCodehTab); + iCodehTab = newHashTable (iCodeKey); + hashiCodeKeys (ebbs, count); + setToNull ((void *) &iCodeSeqhTab); + iCodeSeqhTab = newHashTable (iCodeKey); + sequenceiCode (ebbs, count); + + /* mark the ranges live for each point */ + setToNull ((void *) &liveRanges); + rlivePoint (ebbs, count, emitWarnings); + + /* mark the from & to live ranges for variables used */ + markLiveRanges (ebbs, count); + + /* compute which overlaps with what */ + computeClash(ebbs, count); +} + +/*-----------------------------------------------------------------*/ +/* recomputeLiveRanges - recomputes the live ranges for variables */ +/*-----------------------------------------------------------------*/ +void +recomputeLiveRanges (eBBlock **ebbs, int count, bool emitWarnings) +{ + symbol * sym; + int key; + + /* clear all rlive bitVectors */ + rliveClear (ebbs, count); + + sym = hTabFirstItem (liveRanges, &key); + if (sym) + { + do { + sym->used = 0; + sym->liveFrom = 0; + sym->liveTo = 0; + freeBitVect (sym->clashes); + sym->clashes = NULL; + } while ( (sym = hTabNextItem (liveRanges, &key))); + } + + /* do the LR computation again */ + computeLiveRanges (ebbs, count, emitWarnings); +} + +/*-----------------------------------------------------------------*/ +/* dump icode->rlive in all blocks */ +/*-----------------------------------------------------------------*/ +#if 0 +void +dumpIcRlive (eBBlock ** ebbs, int count) +{ + int i, j; + iCode *ic; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + printf ("bb %d %s alive symbols:\n", i, ebbs[i]->entryLabel->name); + /* for all instructions in this block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + printf ("\tic->key %d\n", ic->key); + + if (!ic->rlive) + continue; + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if ((sym = hTabItemWithKey (liveRanges, j))) + printf ("\t\tsym->key %2d: %s\n", sym->key, sym->rname[0] ? sym->rname : sym->name); + } + } + } +} +#endif + +/*-----------------------------------------------------------------*/ +/* Visit all iCodes reachable from ic */ +/*-----------------------------------------------------------------*/ +static void visit (set **visited, iCode *ic, const int key) +{ + symbol *lbl; + + while (ic && !isinSet (*visited, ic) && bitVectBitValue (ic->rlive, key)) + { + addSet (visited, ic); + + switch (ic->op) + { + case GOTO: + ic = hTabItemWithKey (labelDef, (IC_LABEL (ic))->key); + break; + case RETURN: + ic = hTabItemWithKey (labelDef, returnLabel->key); + break; + case JUMPTABLE: + for (lbl = setFirstItem (IC_JTLABELS (ic)); lbl; lbl = setNextItem (IC_JTLABELS (ic))) + visit (visited, hTabItemWithKey (labelDef, lbl->key), key); + break; + case IFX: + visit (visited, hTabItemWithKey (labelDef, (IC_TRUE(ic) ? IC_TRUE (ic) : IC_FALSE (ic))->key), key); + ic = ic->next; + break; + default: + ic = ic->next; + if (!POINTER_SET (ic) && IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL_CONST (IC_RESULT (ic))->key == key) + { + addSet (visited, ic); + return; + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* Split temporaries that have non-connected live ranges */ +/* Such temporaries can result from GCSE and losrpe, */ +/* And can confuse register allocation and rematerialization. */ +/*-----------------------------------------------------------------*/ +int +separateLiveRanges (iCode *sic, ebbIndex *ebbi) +{ + set *candidates = 0; + symbol *sym; + int num_separated = 0; + + // printf("separateLiveRanges()\n"); + + for (iCode *ic = sic; ic; ic = ic->next) + { + if (ic->op == IFX || ic->op == GOTO || ic->op == JUMPTABLE || !IC_RESULT (ic) || !IS_ITEMP (IC_RESULT (ic)) || bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) <= 1 || isinSet (candidates, OP_SYMBOL (IC_RESULT (ic)))) + continue; + + addSet (&candidates, OP_SYMBOL (IC_RESULT (ic))); + } + + if (!candidates) + return (0); + + for(sym = setFirstItem (candidates); sym; sym = setNextItem (candidates)) + { + // printf("Looking at %s, %d definitions\n", sym->name, bitVectnBitsOn (sym->defs)); + + set *defs = 0; + set *uses = 0; + bool skip_uses = false; + + for (int i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + iCode *dic; + if(dic = hTabItemWithKey (iCodehTab, i)) + addSet (&defs, dic); + else + { + werror (W_INTERNAL_ERROR, __FILE__, __LINE__, "Definition not found"); + return (num_separated); + } + } + if (bitVectBitValue (sym->uses, i)) + { + iCode *uic; + if(uic = hTabItemWithKey (iCodehTab, i)) + addSet (&uses, uic); + else + skip_uses = true; // werror (W_INTERNAL_ERROR, __FILE__, __LINE__, "Use not found"); // return (num_separated); seems too harsh. + } + } + do + { + set *visited = 0; + set *newdefs = 0; + int oldsize; + + wassert (defs); + wassert (setFirstItem (defs)); + + // printf("Looking at def at %d now\n", ((iCode *)(setFirstItem (defs)))->key); + + if (!bitVectBitValue (((iCode *)(setFirstItem (defs)))->rlive, sym->key)) + { + werror (W_INTERNAL_ERROR, __FILE__, __LINE__, "Variable is not alive at one of its definitions"); + break; + } + + visit (&visited, setFirstItem (defs), sym->key); + addSet (&newdefs, setFirstItem (defs)); + + do + { + oldsize = elementsInSet(visited); + setFirstItem (defs); + for(iCode *ic = setNextItem (defs); ic; ic = setNextItem (defs)) + { + // printf("Looking at other def at %d now\n", ic->key); + set *visited2 = 0; + set *intersection = 0; + visit (&visited2, ic, sym->key); + intersection = intersectSets (visited, visited2, THROW_NONE); + intersection = subtractFromSet (intersection, defs, THROW_DEST); + if (intersection) + { + visited = unionSets (visited, visited2, THROW_DEST); + addSet (&newdefs, ic); + } + deleteSet (&intersection); + deleteSet (&visited2); + } + } + while (oldsize < elementsInSet(visited)); + + defs = subtractFromSet (defs, newdefs, THROW_DEST); + + if (newdefs && defs) + { + operand *tmpop = newiTempOperand (operandType (IC_RESULT ((iCode *)(setFirstItem (newdefs)))), TRUE); + + // printf("Splitting %s from %s, using def at %d, op %d\n", OP_SYMBOL_CONST(tmpop)->name, sym->name, ((iCode *)(setFirstItem (newdefs)))->key, ((iCode *)(setFirstItem (newdefs)))->op); + + for (iCode *ic = setFirstItem (visited); ic; ic = setNextItem (visited)) + { + if (IC_LEFT (ic) && IS_ITEMP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic)) == sym) + IC_LEFT (ic) = operandFromOperand (tmpop); + if (IC_RIGHT (ic) && IS_ITEMP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic)) == sym) + IC_RIGHT (ic) = operandFromOperand (tmpop); + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic)) == sym && !POINTER_SET(ic) && ic->next && !isinSet (visited, ic->next)) + continue; + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic)) == sym) + { + bool pset = POINTER_SET(ic); + IC_RESULT (ic) = operandFromOperand (tmpop); + if (pset) + IC_RESULT(ic)->isaddr = TRUE; + else + bitVectUnSetBit (sym->defs, ic->key); + } + bitVectUnSetBit (sym->uses, ic->key); + + skip_uses = true; + num_separated++; + } + } + else if (!skip_uses) + { + set *undefined_uses = 0; + undefined_uses = subtractFromSet (uses, visited, THROW_NONE); + + // Eliminate uses of undefined variables. + for (iCode *ic = setFirstItem (undefined_uses); ic; ic = setNextItem (undefined_uses)) + { + iCode *prev = ic->prev; + iCode *next = ic->next; + if (prev && next) + { + prev->next = next; + next->prev = prev; + } + + bitVectUnSetBit (sym->uses, ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + } + + deleteSet (&undefined_uses); + } + + deleteSet (&newdefs); + deleteSet (&visited); + } + while (elementsInSet(defs) > 1); + + deleteSet (&defs); + deleteSet (&uses); + } + + deleteSet (&candidates); + + return (num_separated); +} + +/*-----------------------------------------------------------------*/ +/* Shorten live ranges by swapping order of operations */ +/*-----------------------------------------------------------------*/ +int +shortenLiveRanges (iCode *sic, ebbIndex *ebbi) +{ + int change = 0; + + for (iCode *ic = sic; ic; ic = ic->next) + { + iCode *ifx = 0; + + iCode *pic = ic->prev; + iCode *nic = ic->next; + + if (!pic || !nic) + continue; + + if (ic->op == IFX || nic->op == IFX) + continue; + + if (nic->op == IPUSH || nic->op == SEND) + continue; + + if (pic->op != '=' || !IS_ITEMP (IC_RESULT (pic)) || bitVectnBitsOn (OP_DEFS (IC_RESULT (pic))) != 1) + continue; + + if (IC_LEFT (nic) != IC_RESULT (pic) && IC_RIGHT (nic) != IC_RESULT (pic) || bitVectnBitsOn (OP_USES (IC_RESULT (pic))) != 1) + continue; + + if (IS_OP_VOLATILE (IC_RIGHT (pic)) || IS_OP_VOLATILE (IC_LEFT (nic)) || IS_OP_VOLATILE (IC_RIGHT (nic)) || IS_OP_VOLATILE (IC_RESULT (nic))) + continue; + + if (isOperandEqual (IC_RESULT (pic), IC_LEFT (ic)) || isOperandEqual (IC_RESULT (pic), IC_RIGHT (ic))) + continue; + + if (isOperandEqual (IC_RESULT (ic), IC_LEFT (nic)) || isOperandEqual (IC_RESULT (ic), IC_RIGHT (nic))) + continue; + + if ((POINTER_SET (nic) || isOperandGlobal (IC_RESULT (nic))) && (POINTER_GET (ic) || isOperandGlobal (IC_LEFT (ic)) || isOperandGlobal (IC_RIGHT (ic))) || + (POINTER_GET (nic) || isOperandGlobal (IC_LEFT (nic)) || isOperandGlobal (IC_RIGHT (nic))) && (POINTER_SET (ic) || POINTER_SET (nic) && isOperandGlobal (IC_RESULT (ic)))) + continue; + + if (isOperandGlobal (IC_RIGHT (pic))) // Might result in too many global operands per op for backend. + continue; + + if (ifx = ifxForOp (IC_RESULT (nic), nic)) + { + const symbol *starget = IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx); + const iCode *itarget = eBBWithEntryLabel(ebbi, starget)->sch; + + if (nic->next != ifx || bitVectBitValue(itarget->rlive, IC_RESULT (ic)->key)) + continue; + } + + if (IC_LEFT (nic) == IC_RESULT (pic)) + IC_LEFT (nic) = IC_RIGHT (pic); + if (IC_RIGHT (nic) == IC_RESULT (pic)) + IC_RIGHT (nic) = IC_RIGHT (pic); + bitVectUnSetBit (OP_USES (IC_RESULT (pic)), nic->key); + if (IS_SYMOP (IC_RIGHT (pic))) + bitVectSetBit (OP_USES (IC_RIGHT (pic)), nic->key); + + // Assignment to self will get optimized out later + IC_LEFT (pic) = IC_RESULT (pic); + bitVectSetBit (OP_USES (IC_RESULT (pic)), pic->key); + + pic->next = nic; + nic->prev = pic; + ic->prev = nic; + ic->next = nic->next; + nic->next = ic; + if (ic->next) + ic->next->prev = ic; + + if (ifx) // Move calculation beyond ifx. + { + ifx->prev = ic->prev; + ic->next = ifx->next; + ifx->next = ic; + ic->prev = ifx; + + ifx->prev->next = ifx; + if (ic->next) + ic->next->prev = ic; + } + + change++; + } + + return (change); +} + diff --git a/src/SDCClrange.h b/src/SDCClrange.h new file mode 100644 index 0000000..275473c --- /dev/null +++ b/src/SDCClrange.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + + SDCClrange.h - header file for live range computations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCLRANGE_H +#define SDCCLRANGE_H 1 + +extern hTab *liveRanges; +extern hTab *iCodehTab; +extern hTab *iCodeSeqhTab; + +int notUsedInBlock (symbol *, eBBlock *, iCode *); +bool allDefsOutOfRange (bitVect *, int, int); +void computeLiveRanges (eBBlock **ebbs, int count, bool emitWarnings); +void recomputeLiveRanges (eBBlock **ebbs, int count, bool emitWarnings); + +void setToRange (operand *, int, bool); +void hashiCodeKeys (eBBlock **, int); + +void adjustIChain (eBBlock ** ebbs, int count); + +int separateLiveRanges (iCode *sic, ebbIndex *ebbi); /* Split iTemps that have non-connected live-ranges. */ + +int shortenLiveRanges (iCode *sic, ebbIndex *ebbi); /* Do some optimizations that shorten live ranges. */ + +#endif + diff --git a/src/SDCCmacro.c b/src/SDCCmacro.c new file mode 100644 index 0000000..e71a7f7 --- /dev/null +++ b/src/SDCCmacro.c @@ -0,0 +1,186 @@ +/*------------------------------------------------------------------------- + SDCCmain.c - Macro support code. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +char * +eval_macros (hTab * pvals, const char *pfrom) +{ + bool fdidsomething = FALSE; + char quote = '\0'; + struct dbuf_s dbuf; + + assert (pvals); + assert (pfrom); + + dbuf_init (&dbuf, 256); + while (*pfrom) + { + switch (*pfrom) + { + case '"': + case '\'': + if (quote != '\0') + { + /* write previous quote */ + dbuf_append_char (&dbuf, quote); + } + quote = *pfrom++; + break; + + case '{': + { + const char *pend = ++pfrom; + const char *pval; + char *name; + + /* Find the end of macro */ + while (*pend && '}' != *pend) + { + pend++; + } + if ('}' != *pend) + { + wassertl (0, "Unterminated macro expansion"); + } + + name = Safe_strndup (pfrom, pend - pfrom); + + /* Look up the value in the hash table */ + pval = shash_find (pvals, name); + Safe_free (name); + + if (NULL == pval) + { + /* Empty macro value */ + if ('\0' != quote) + { + /* It was a quote */ + if (pend[1] == quote) + { + /* Start quote equals end quote: skip both */ + ++pend; + } + else + { + /* Start quote not equals end quote: add both */ + dbuf_append_char (&dbuf, quote); + } + } + } + else + { + if ('\0' != quote) + { + dbuf_append_char (&dbuf, quote); + } + dbuf_append_str (&dbuf, pval); + fdidsomething = TRUE; + } + + quote = '\0'; + pfrom = pend + 1; + } + break; + + default: + if ('\0' != quote) + { + dbuf_append_char (&dbuf, quote); + quote = '\0'; + } + + dbuf_append_char (&dbuf, *pfrom++); + } + } + + if ('\0' != quote) + { + dbuf_append_char (&dbuf, quote); + } + + + /* If we did something then recursivly expand any expanded macros */ + if (fdidsomething) + { + char *ret = eval_macros (pvals, dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + return ret; + } + + return dbuf_detach_c_str (&dbuf); +} + +char * +mvsprintf (hTab * pvals, const char *pformat, va_list ap) +{ + char *p; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 256); + + /* Recursivly evaluate all the macros in the string */ + p = eval_macros (pvals, pformat); + + /* Evaluate all the arguments */ + dbuf_vprintf (&dbuf, p, ap); + Safe_free (p); + + /* Recursivly evaluate any macros that were used as arguments */ + p = eval_macros (pvals, dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + return p; +} + +char * +msprintf (hTab * pvals, const char *pformat, ...) +{ + va_list ap; + char *pret; + + va_start (ap, pformat); + + pret = mvsprintf (pvals, pformat, ap); + + va_end (ap); + + return pret; +} + +void +mfprintf (FILE * fp, hTab * pvals, const char *pformat, ...) +{ + va_list ap; + char *p; + + va_start (ap, pformat); + + p = mvsprintf (pvals, pformat, ap); + + va_end (ap); + + fputs (p, fp); + Safe_free (p); +} diff --git a/src/SDCCmacro.h b/src/SDCCmacro.h new file mode 100644 index 0000000..5374bc0 --- /dev/null +++ b/src/SDCCmacro.h @@ -0,0 +1,38 @@ +/*----------------------------------------------------------------- + SDCChast.h - contains support routines for hashtables/sets . + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + + +#ifndef SDCCMACRO_H +#define SDCCMACRO_H + +#include "SDCChasht.h" +#include +#include + +char *eval_macros (hTab * pvals, const char *pfrom); +char *mvsprintf (hTab * pvals, const char *pformat, va_list ap); +char *msprintf (hTab * pvals, const char *pformat, ...); +void mfprintf (FILE * fp, hTab * pvals, const char *pformat, ...); + +#endif diff --git a/src/SDCCmain.c b/src/SDCCmain.c new file mode 100644 index 0000000..b20ef66 --- /dev/null +++ b/src/SDCCmain.c @@ -0,0 +1,2726 @@ +/*------------------------------------------------------------------------- + SDCCmain.c - main file + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include "common.h" +#include +#include "dbuf_string.h" +#include "SDCCerr.h" +#include "SDCCmacro.h" +#include "SDCCargs.h" +#include "SDCCglue.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +/* REMOVE ME!!! */ +extern int yyparse (void); + +FILE *srcFile; /* source file */ +const char *fullSrcFileName; /* full name for the source file; */ + /* can be NULL while c1mode or linking without compiling */ +const char *fullDstFileName; /* full name for the output file; */ + /* only given by -o, otherwise NULL */ +const char *dstFileName; /* destination file name without extension */ +const char *moduleName; /* module name is same as module name base, but with all */ + /* non-alphanumeric characters replaced with underscore */ +int currRegBank = 0; +int RegBankUsed[4] = { 1, 0, 0, 0 }; /*JCF: Reg Bank 0 used by default */ + +int BitBankUsed; /* MB: overlayable bit bank */ +struct optimize optimize; +struct options options; +int preProcOnly = 0; +int noAssemble = 0; +int gasOutput; +set *preArgvSet = NULL; /* pre-processor arguments */ +set *asmOptionsSet = NULL; /* set of assembler options */ +set *linkOptionsSet = NULL; /* set of linker options */ +set *libFilesSet = NULL; +set *libPathsSet = NULL; +set *relFilesSet = NULL; +set *dataDirsSet = NULL; /* list of data search directories */ +set *includeDirsSet = NULL; /* list of include search directories */ +set *userIncDirsSet = NULL; /* list of user include directories */ +set *libDirsSet = NULL; /* list of lib search directories */ +bool regalloc_dry_run = FALSE; + +static const char *dstPath = ""; /* path for the output files; */ + /* "" is equivalent with cwd */ +static const char *moduleNameBase = NULL; /* module name base is source file without path and extension */ + /* can be NULL while linking without compiling */ + +/* uncomment JAMIN_DS390 to always override and use ds390 port + for mcs51 work. This is temporary, for compatibility testing. */ +/* #define JAMIN_DS390 */ +#ifdef JAMIN_DS390 +static int ds390_jammed = 0; +#endif + +/* Globally accessible scratch buffer for file names. + TODO: replace them with local buffers */ +char buffer[PATH_MAX * 2]; + +#define LENGTH(_a) (sizeof (_a) / sizeof (*(_a))) + +#define OPTION_HELP "--help" +#define OPTION_OUT_FMT_IHX "--out-fmt-ihx" +#define OPTION_OUT_FMT_S19 "--out-fmt-s19" +#define OPTION_PEEP_FILE "--peep-file" +#define OPTION_LIB_PATH "--lib-path" +#define OPTION_CALLEE_SAVES "--callee-saves" +#define OPTION_STACK_LOC "--stack-loc" +#define OPTION_XSTACK_LOC "--xstack-loc" +#define OPTION_DATA_LOC "--data-loc" +#define OPTION_IDATA_LOC "--idata-loc" +#define OPTION_XRAM_LOC "--xram-loc" +#define OPTION_CODE_LOC "--code-loc" +#define OPTION_IRAM_SIZE "--iram-size" +#define OPTION_XRAM_SIZE "--xram-size" +#define OPTION_CODE_SIZE "--code-size" +#define OPTION_VERSION "--version" +#define OPTION_NO_LABEL_OPT "--nolabelopt" +#define OPTION_NO_LOOP_INV "--noinvariant" +#define OPTION_NO_LOOP_IND "--noinduction" +#define OPTION_LESS_PEDANTIC "--less-pedantic" +#define OPTION_DISABLE_WARNING "--disable-warning" +#define OPTION_WERROR "--Werror" +#define OPTION_DEBUG "--debug" +#define OPTION_NO_GCSE "--nogcse" +#define OPTION_NO_XINIT_OPT "--no-xinit-opt" +#define OPTION_NO_CCODE_IN_ASM "--no-c-code-in-asm" +#define OPTION_ICODE_IN_ASM "--i-code-in-asm" +#define OPTION_PRINT_SEARCH_DIRS "--print-search-dirs" +#define OPTION_MSVC_ERROR_STYLE "--vc" +#define OPTION_USE_STDOUT "--use-stdout" +#define OPTION_NO_PEEP_COMMENTS "--no-peep-comments" +#define OPTION_VERBOSE_ASM "--fverbose-asm" +#define OPTION_OPT_CODE_SPEED "--opt-code-speed" +#define OPTION_OPT_CODE_SIZE "--opt-code-size" +#define OPTION_STD_C89 "--std-c89" +#define OPTION_STD_C95 "--std-c95" +#define OPTION_STD_C99 "--std-c99" +#define OPTION_STD_C11 "--std-c11" +#define OPTION_STD_C2X "--std-c2x" +#define OPTION_STD_SDCC89 "--std-sdcc89" +#define OPTION_STD_SDCC99 "--std-sdcc99" +#define OPTION_STD_SDCC11 "--std-sdcc11" +#define OPTION_STD_SDCC2X "--std-sdcc2x" +#define OPTION_CODE_SEG "--codeseg" +#define OPTION_CONST_SEG "--constseg" +#define OPTION_DATA_SEG "--dataseg" +#define OPTION_DOLLARS_IN_IDENT "--fdollars-in-identifiers" +#define OPTION_SIGNED_CHAR "--fsigned-char" +#define OPTION_USE_NON_FREE "--use-non-free" +#define OPTION_PEEP_RETURN "--peep-return" +#define OPTION_NO_PEEP_RETURN "--no-peep-return" +#define OPTION_NO_OPTSDCC_IN_ASM "--no-optsdcc-in-asm" +#define OPTION_MAX_ALLOCS_PER_NODE "--max-allocs-per-node" +#define OPTION_NO_LOSPRE "--nolospre" +#define OPTION_ALLOW_UNSAFE_READ "--allow-unsafe-read" +#define OPTION_DUMP_AST "--dump-ast" +#define OPTION_DUMP_I_CODE "--dump-i-code" +#define OPTION_DUMP_GRAPHS "--dump-graphs" + +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_MEDIUM_MODEL "--model-medium" +#define OPTION_LARGE_MODEL "--model-large" +#define OPTION_HUGE_MODEL "--model-huge" + +static const OPTION optionsTable[] = { + {0, NULL, NULL, "General options"}, + {0, OPTION_HELP, NULL, "Display this help"}, + {'v', OPTION_VERSION, NULL, "Display sdcc's version"}, + {0, "--verbose", &options.verbose, "Trace calls to the preprocessor, assembler, and linker"}, + {'V', NULL, &options.verboseExec, "Execute verbosely. Show sub commands as they are run"}, + {'d', NULL, NULL, "Output list of mcaro definitions in effect. Use with -E"}, + {'D', NULL, NULL, "Define macro as in -Dmacro"}, + {'I', NULL, NULL, "Add to the include (*.h) path, as in -Ipath"}, + {'A', NULL, NULL, NULL}, + {'U', NULL, NULL, "Undefine macro as in -Umacro"}, + {'M', NULL, NULL, "Preprocessor option"}, + {'W', NULL, NULL, "Pass through options to the pre-processor (p), assembler (a) or linker (l)"}, + {'S', NULL, &noAssemble, "Compile only; do not assemble or link"}, + {0 , "--gas", &gasOutput, "Compile only in GAS (GNU Assembler) format. Incompatible with other assembly or compilation options."}, + {'c', "--compile-only", &options.cc_only, "Compile and assemble, but do not link"}, + {'E', "--preprocessonly", &preProcOnly, "Preprocess only, do not compile"}, + {0, "--c1mode", &options.c1mode, "Act in c1 mode. The standard input is preprocessed code, the output is assembly code."}, + {'o', NULL, NULL, "Place the output into the given path resp. file"}, + {0, OPTION_PRINT_SEARCH_DIRS, &options.printSearchDirs, "display the directories in the compiler's search path"}, + {0, OPTION_MSVC_ERROR_STYLE, &options.vc_err_style, "messages are compatible with Micro$oft visual studio"}, + {0, OPTION_USE_STDOUT, NULL, "send errors to stdout instead of stderr"}, + {0, "--nostdlib", &options.nostdlib, "Do not include the standard library directory in the search path"}, + {0, "--nostdinc", &options.nostdinc, "Do not include the standard include directory in the search path"}, + {0, OPTION_LESS_PEDANTIC, NULL, "Disable some of the more pedantic warnings"}, + {0, OPTION_DISABLE_WARNING, NULL, " Disable specific warning"}, + {0, OPTION_WERROR, NULL, "Treat the warnings as errors"}, + {0, OPTION_DEBUG, NULL, "Enable debugging symbol output"}, + {0, "--cyclomatic", &options.cyclomatic, "Display complexity of compiled functions"}, + {0, OPTION_STD_C89, NULL, "Use ISO C90 (aka ANSI C89) standard (slightly incomplete)"}, + {0, OPTION_STD_SDCC89, NULL, "Use ISO C90 (aka ANSI C89) standard with SDCC extensions"}, + {0, OPTION_STD_C95, NULL, "Use ISO C95 (aka ISO C94) standard (slightly incomplete)"}, + {0, OPTION_STD_C99, NULL, "Use ISO C99 standard (incomplete)"}, + {0, OPTION_STD_SDCC99, NULL, "Use ISO C99 standard with SDCC extensions"}, + {0, OPTION_STD_C11, NULL, "Use ISO C11 standard (incomplete)"}, + {0, OPTION_STD_SDCC11, NULL, "Use ISO C11 standard with SDCC extensions (default)"}, + {0, OPTION_STD_C2X, NULL, "Use ISO C2X standard (incomplete)"}, + {0, OPTION_STD_SDCC2X, NULL, "Use ISO C2X standard with SDCC extensions"}, + {0, OPTION_DOLLARS_IN_IDENT, &options.dollars_in_ident, "Permit '$' as an identifier character"}, + {0, OPTION_SIGNED_CHAR, &options.signed_char, "Make \"char\" signed by default"}, + {0, OPTION_USE_NON_FREE, &options.use_non_free, "Search / include non-free licensed libraries and header files"}, + + {0, NULL, NULL, "Code generation options"}, + {'m', NULL, NULL, "Set the port to use e.g. -mz80."}, + {'p', NULL, NULL, "Select port specific processor e.g. -mpic14 -p16f84"}, + {0, "--stack-auto", &options.stackAuto, "Stack automatic variables"}, + {0, "--xstack", &options.useXstack, "Use external stack"}, + {0, "--int-long-reent", &options.intlong_rent, "Use reentrant calls on the int and long support functions"}, + {0, "--float-reent", &options.float_rent, "Use reentrant calls on the float support functions"}, + {0, "--xram-movc", &options.xram_movc, "Use movc instead of movx to read xram (xdata)"}, + {0, OPTION_CALLEE_SAVES, &options.calleeSavesSet, " Cause the called function to save registers instead of the caller", CLAT_SET}, + {0, "--profile", &options.profile, "On supported ports, generate extra profiling information"}, + {0, "--fomit-frame-pointer", &options.omitFramePtr, "Leave out the frame pointer."}, + {0, "--all-callee-saves", &options.all_callee_saves, "callee will always save registers used"}, + {0, "--stack-probe", &options.stack_probe, "insert call to function __stack_probe at each function prologue"}, + {0, OPTION_NO_XINIT_OPT, &options.noXinitOpt, "don't memcpy initialized xram from code"}, + {0, OPTION_NO_CCODE_IN_ASM, &options.noCcodeInAsm, "don't include c-code as comments in the asm file"}, + {0, OPTION_NO_PEEP_COMMENTS, &options.noPeepComments, "don't include peephole optimizer comments"}, + {0, OPTION_CODE_SEG, NULL, " use this name for the code segment"}, + {0, OPTION_CONST_SEG, NULL, " use this name for the const segment"}, + {0, OPTION_DATA_SEG, NULL, " use this name for the data segment"}, + + {0, NULL, NULL, "Optimization options"}, + {0, "--nooverlay", &options.noOverlay, "Disable overlaying leaf function auto variables"}, + {0, OPTION_NO_GCSE, NULL, "Disable the GCSE optimisation"}, + {0, OPTION_NO_LABEL_OPT, NULL, "Disable label optimisation"}, + {0, OPTION_NO_LOOP_INV, NULL, "Disable optimisation of invariants"}, + {0, OPTION_NO_LOOP_IND, NULL, "Disable loop variable induction"}, + {0, "--noloopreverse", &optimize.noLoopReverse, "Disable the loop reverse optimisation"}, + {0, "--no-peep", &options.nopeep, "Disable the peephole assembly file optimisation"}, + {0, "--no-reg-params", &options.noRegParams, "On some ports, disable passing some parameters in registers"}, + {0, "--peep-asm", &options.asmpeep, "Enable peephole optimization on inline assembly"}, + {0, OPTION_PEEP_RETURN, NULL, "Enable peephole optimization for return instructions"}, + {0, OPTION_NO_PEEP_RETURN, NULL, "Disable peephole optimization for return instructions"}, + {0, OPTION_PEEP_FILE, &options.peep_file, " use this extra peephole file", CLAT_STRING}, + {0, OPTION_OPT_CODE_SPEED, NULL, "Optimize for code speed rather than size"}, + {0, OPTION_OPT_CODE_SIZE, NULL, "Optimize for code size rather than speed"}, + {0, OPTION_MAX_ALLOCS_PER_NODE, &options.max_allocs_per_node, "Maximum number of register assignments considered at each node of the tree decomposition", CLAT_INTEGER}, + {0, OPTION_NO_LOSPRE, NULL, "Disable lospre"}, + {0, OPTION_ALLOW_UNSAFE_READ, NULL, "Allow optimizations to read any memory location anytime"}, + {0, "--nostdlibcall", &optimize.noStdLibCall, "Disable optimization of calls to standard library"}, + + {0, NULL, NULL, "Internal debugging options"}, + {0, OPTION_DUMP_AST, &options.dump_ast, "Dump front-end AST before generating i-code"}, + {0, OPTION_DUMP_I_CODE, &options.dump_i_code, "Dump the i-code structure at all stages"}, + {0, OPTION_DUMP_GRAPHS, &options.dump_graphs, "Dump graphs (control-flow, conflict, etc)"}, + {0, OPTION_ICODE_IN_ASM, &options.iCodeInAsm, "Include i-code as comments in the asm file"}, + {0, OPTION_VERBOSE_ASM, &options.verboseAsm, "Include code generator comments in the asm output"}, + + {0, NULL, NULL, "Linker options"}, + {'l', NULL, NULL, "Include the given library in the link"}, + {'L', NULL, NULL, "Add the next field to the library search path"}, + {0, OPTION_LIB_PATH, &libPathsSet, " use this path to search for libraries", CLAT_ADD_SET}, + {0, OPTION_OUT_FMT_IHX, NULL, "Output in Intel hex format"}, + {0, OPTION_OUT_FMT_S19, NULL, "Output in S19 hex format"}, + {0, OPTION_XRAM_LOC, NULL, " External Ram start location", CLAT_INTEGER}, + {0, OPTION_XRAM_SIZE, NULL, " External Ram size"}, + {0, OPTION_IRAM_SIZE, &options.iram_size, " Internal Ram size", CLAT_INTEGER}, + {0, OPTION_XSTACK_LOC, &options.xstack_loc, " External Stack start location", CLAT_INTEGER}, + {0, OPTION_CODE_LOC, &options.code_loc, " Code Segment Location", CLAT_INTEGER}, + {0, OPTION_CODE_SIZE, &options.code_size, " Code Segment size", CLAT_INTEGER}, + {0, OPTION_STACK_LOC, &options.stack_loc, " Stack pointer initial value", CLAT_INTEGER}, + {0, OPTION_DATA_LOC, &options.data_loc, " Direct data start location", CLAT_INTEGER}, + {0, OPTION_IDATA_LOC, &options.idata_loc, NULL, CLAT_INTEGER}, + + {0, OPTION_NO_OPTSDCC_IN_ASM, &options.noOptsdccInAsm, "Do not emit .optsdcc in asm"}, + /* End of options */ + {0, NULL} +}; + +/** Table of all unsupported options and help text to display when one + is used. +*/ +typedef struct +{ + /** shortOpt as in OPTIONS. */ + char shortOpt; + /** longOpt as in OPTIONS. */ + const char *longOpt; + /** Message to display inside W_UNSUPPORTED_OPT when this option + is used. */ + const char *message; +} UNSUPPORTEDOPT; + +static const UNSUPPORTEDOPT unsupportedOptTable[] = { + {'X', NULL, "use --xstack-loc instead"}, + {'x', NULL, "use --xstack instead"}, + {'i', NULL, "use --idata-loc instead"}, + {'r', NULL, "use --xdata-loc instead"}, + {'s', NULL, "use --code-loc instead"}, + {'Y', NULL, "use -I instead"}, + {0, "--fommit-frame-pointer", "use --fomit-frame-pointer instead"}, +}; + +/** List of all default constant macros. + */ +static const char *_baseValues[] = { + "cpp", "sdcpp", + "cppextraopts", "", + /* Path seperator character */ + "sep", DIR_SEPARATOR_STRING, + NULL +}; + +static const char *_preCmd = "{cpp} -nostdinc -Wall {cppstd}{cppextraopts} {fullsrcfilename} {cppoutfilename}"; + +PORT *port; + +static PORT *_ports[] = { +#if !OPT_DISABLE_MCS51 + &mcs51_port, +#endif +#if !OPT_DISABLE_Z80 + &z80_port, +#endif +#if !OPT_DISABLE_Z180 + &z180_port, +#endif +#if !OPT_DISABLE_R2K + &r2k_port, +#endif +#if !OPT_DISABLE_R3KA + &r3ka_port, +#endif +#if !OPT_DISABLE_GBZ80 + &gbz80_port, +#endif +#if !OPT_DISABLE_TLCS90 + &tlcs90_port, +#endif +#if !OPT_DISABLE_EZ80_Z80 + &ez80_z80_port, +#endif +#if !OPT_DISABLE_AVR + &avr_port, +#endif +#if !OPT_DISABLE_DS390 + &ds390_port, +#endif +#if !OPT_DISABLE_PIC16 + &pic16_port, +#endif +#if !OPT_DISABLE_PIC14 + &pic_port, +#endif +#if !OPT_DISABLE_TININative + &tininative_port, +#endif +#if !OPT_DISABLE_DS400 + &ds400_port, +#endif +#if !OPT_DISABLE_HC08 + &hc08_port, +#endif +#if !OPT_DISABLE_S08 + &s08_port, +#endif +#if !OPT_DISABLE_STM8 + &stm8_port, +#endif +#if !OPT_DISABLE_PDK13 + &pdk13_port, +#endif +#if !OPT_DISABLE_PDK14 + &pdk14_port, +#endif +#if !OPT_DISABLE_PDK15 + &pdk15_port, +#endif +}; + +#define NUM_PORTS (sizeof(_ports)/sizeof(_ports[0])) + +/** Sets the port to the one given by the command line option. + @param The name minus the option (eg 'mcs51') + @return 0 on success. +*/ +static void +_setPort (const char *name) +{ + int i; + for (i = 0; i < NUM_PORTS; i++) + { + if (!strcmp (_ports[i]->target, name)) + { + port = _ports[i]; + return; + } + } + /* Error - didnt find */ + werror (E_UNKNOWN_TARGET, name); + exit (EXIT_FAILURE); +} + +/* Override the default processor with the one specified + * on the command line */ +static void +_setProcessor (char *_processor) +{ + port->processor = _processor; +} + +static void +_validatePorts (void) +{ + int i; + for (i = 0; i < NUM_PORTS; i++) + { + if (_ports[i]->magic != PORT_MAGIC) + { + /* Uncomment this line to debug which port is causing the problem + * (the target name is close to the beginning of the port struct + * and probably can be accessed just fine). */ + fprintf (stderr, "%s :", _ports[i]->target); + wassertl (0, "Port definition structure is incomplete"); + } + } +} + + +static char * +program_name (const char *path) +{ +#ifdef _WIN32 + char fname[_MAX_FNAME]; + + _splitpath (path, NULL, NULL, fname, NULL); + return Safe_strdup (fname); +#else + char *tmpPath = Safe_strdup (path); + char *res = Safe_strdup (basename (tmpPath)); + + Safe_free (tmpPath); + return res; +#endif +} + +/* search through the command line for the port */ +static void +_findPort (int argc, char **argv) +{ + char *programName = program_name (*argv); + int found = 0; + int i; + + _validatePorts (); + + /* try to assign port by command line option */ + while (argc-- && !found) + { + if (!strncmp (*argv, "-m", 2)) + { + _setPort (*argv + 2); + found = 1; + } + argv++; + } + + /* try to assign port by the name of the executable */ + for (i = 0; i < NUM_PORTS && !found; i++) + { + if (strstr (programName, _ports[i]->target)) + { + _setPort (_ports[i]->target); + found = 1; + } + } + + if (!found) + { + /* Use the first in the list as default */ + port = _ports[0]; + } + + Safe_free (programName); +} + +/* search through the command line options for the processor */ +static void +_findProcessor (int argc, char **argv) +{ + while (argc--) + { + if (!strncmp (*argv, "-p", 2)) + { + _setProcessor (*argv + 2); + return; + } + argv++; + } + + /* no error if processor was not specified. */ +} + +/*-----------------------------------------------------------------*/ +/* printVersionInfo - prints the version info */ +/*-----------------------------------------------------------------*/ +void +printVersionInfo (FILE * stream) +{ + int i; + + fprintf (stream, "SDCC : "); + for (i = 0; i < NUM_PORTS; i++) + fprintf (stream, "%s%s", i == 0 ? "" : "/", _ports[i]->target); + + fprintf (stream, " " SDCC_VERSION_STR +#ifdef SDCC_SUB_VERSION_STR + "/" SDCC_SUB_VERSION_STR +#endif + " #%s (%s)\n", getBuildNumber (), getBuildEnvironment ()); + fprintf (stream, "published under GNU General Public License (GPL)\n"); +} + +static void +printOptions (const OPTION * optionsTable, FILE * stream) +{ + int i; + for (i = 0; optionsTable[i].shortOpt != 0 || optionsTable[i].longOpt != NULL || optionsTable[i].help != NULL; i++) + { + if (!optionsTable[i].shortOpt && !optionsTable[i].longOpt && optionsTable[i].help) + { + fprintf (stream, "\n%s:\n", optionsTable[i].help); + } + else + { + fprintf (stream, " %c%c %-20s %s\n", + optionsTable[i].shortOpt != 0 ? '-' : ' ', + optionsTable[i].shortOpt != 0 ? optionsTable[i].shortOpt : ' ', + optionsTable[i].longOpt != NULL ? optionsTable[i].longOpt : "", + optionsTable[i].help != NULL ? optionsTable[i].help : ""); + } + } +} + +/*-----------------------------------------------------------------*/ +/* printUsage - prints command line syntax */ +/*-----------------------------------------------------------------*/ +static void +printUsage (bool err) +{ + int i; + FILE *stream = err ? stderr : stdout; + + printVersionInfo (stream); + fprintf (stream, + "Usage : sdcc [options] filename\n" + "Options :-\n"); + + printOptions (optionsTable, stream); + + for (i = 0; i < NUM_PORTS; i++) + { + if (_ports[i]->poptions != NULL) + { + fprintf (stream, "\nSpecial options for the %s port:\n", _ports[i]->target); + printOptions (_ports[i]->poptions, stream); + } + } +} + +/*-----------------------------------------------------------------*/ +/* setParseWithComma - separates string with comma to a set */ +/*-----------------------------------------------------------------*/ +void +setParseWithComma (set ** dest, const char *src) +{ + const char *p, *end; + struct dbuf_s dbuf; + + /* skip the initial white spaces */ + while (isspace ((unsigned char) *src)) + ++src; + + /* skip the trailing white spaces */ + end = &src[strlen (src) - 1]; + while (end >= src && isspace ((unsigned char) *end)) + --end; + ++end; + + p = src; + while (src < end) + { + dbuf_init (&dbuf, 16); + + while (p < end && ',' != *p) + ++p; + dbuf_append (&dbuf, src, p - src); + addSet (dest, dbuf_detach_c_str (&dbuf)); + + src = ++p; + } +} + +/*-------------------------------------------------------------*/ +/* setStackSize - set the stack size of a running sdcc process */ +/*-------------------------------------------------------------*/ +static void +setStackSize (void) +{ +#if defined (HAVE_SETRLIMIT) && defined (RLIMIT_STACK) + struct rlimit rl = {4 * 1024 * 1024, 4 * 1024 * 1024}; + setrlimit (RLIMIT_STACK, &rl); +#endif +} + +/*-----------------------------------------------------------------*/ +/* setDefaultOptions - sets the default options */ +/*-----------------------------------------------------------------*/ +static void +setDefaultOptions (void) +{ + /* first the options part */ + options.stack_loc = 0; /* stack pointer initialised to 0 */ + options.xstack_loc = 1; /* xternal stack starts at 1 */ + options.code_loc = 0; /* code starts at 0 */ + options.data_loc = 0; /* JCF: By default let the linker locate data */ + options.xdata_loc = 1; /* MB: Do not use address 0 by default as it equals NULL */ + options.idata_loc = 0; /* MB: No need to limit idata to 0x80-0xFF */ + options.nopeep = 0; + options.model = port->general.default_model; + options.nostdlib = 0; + options.nostdinc = 0; + options.verbose = 0; + options.std_sdcc = 1; /* enable SDCC language extensions */ + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; /* default to C11 (we want inline by default, so we need at least C99, and support for C11 is more complete than C99) */ + options.std_c2x = 0; + options.code_seg = CODE_NAME ? Safe_strdup (CODE_NAME) : NULL; /* default to CSEG for generated code */ + options.const_seg = CONST_NAME ? Safe_strdup (CONST_NAME) : NULL; /* default to CONST for generated code */ + options.data_seg = DATA_NAME ? Safe_strdup (DATA_NAME) : NULL; /* default to DATA for non-initialized data */ + options.stack10bit = 0; + options.out_fmt = 0; + options.dump_graphs = 0; + options.dependencyFileOpt = 0; + + /* now for the optimizations */ + /* turn on the everything */ + optimize.global_cse = 1; + optimize.label1 = 1; + optimize.label2 = 1; + optimize.label3 = 1; + optimize.label4 = 1; + optimize.loopInvariant = 1; + optimize.loopInduction = 1; + options.max_allocs_per_node = 3000; + optimize.lospre = 1; + optimize.allow_unsafe_read = 0; + + /* now for the ports */ + port->setDefaultOptions (); +} + +/*-----------------------------------------------------------------*/ +/* processFile - determines the type of file from the extension */ +/*-----------------------------------------------------------------*/ +static void +processFile (char *s) +{ + const char *extp; + struct dbuf_s ext; + struct dbuf_s path; + + dbuf_init (&ext, 128); + dbuf_init (&path, PATH_MAX); + + /* get the file extension. + If no '.' then we don't know what the file type is + so give an error and return */ + if (!dbuf_splitFile (s, &path, &ext)) + { + werror (E_UNKNOWN_FEXT, s); + + dbuf_destroy (&ext); + dbuf_destroy (&path); + + return; + } + + /* otherwise depending on the file type */ + extp = dbuf_c_str (&ext); + if (STRCASECMP (extp, ".c") == 0 || STRCASECMP (extp, ".h") == 0) + { + char *p, *m; + + dbuf_destroy (&ext); + + /* source file name : not if we already have a + source file */ + if (fullSrcFileName) + { + werror (W_TOO_MANY_SRC, s); + + dbuf_destroy (&path); + + return; + } + + /* the only source file */ + fullSrcFileName = s; + if (!(srcFile = fopen (fullSrcFileName, "r"))) + { + werror (E_FILE_OPEN_ERR, s); + + dbuf_destroy (&path); + + exit (EXIT_FAILURE); + } + + /* get rid of any path information + for the module name; */ + dbuf_init (&ext, 128); + + dbuf_splitPath (dbuf_c_str (&path), NULL, &ext); + dbuf_destroy (&path); + + moduleNameBase = Safe_strdup (dbuf_c_str (&ext)); + m = dbuf_detach (&ext); + + for (p = m; *p; ++p) + if (!isalnum ((unsigned char) *p)) + *p = '_'; + moduleName = m; + return; + } + + /* if the extension is type rel_ext + additional object file will be passed to the linker */ + if (STRCASECMP (extp, port->linker.rel_ext) == 0) + { + dbuf_destroy (&ext); + dbuf_destroy (&path); + + addSet (&relFilesSet, Safe_strdup (s)); + return; + } + + /* if .lib or .LIB */ + if (STRCASECMP (extp, ".lib") == 0) + { + dbuf_destroy (&ext); + dbuf_destroy (&path); + + addSet (&libFilesSet, Safe_strdup (s)); + return; + } + + dbuf_destroy (&ext); + dbuf_destroy (&path); + + werror (E_UNKNOWN_FEXT, s); +} + +static void +_setModel (int model, const char *sz) +{ + if (port->general.supported_models & model) + options.model = model; + else + werror (W_UNSUPPORTED_MODEL, sz, port->target); +} + +/** Gets the string argument to this option. If the option is '--opt' + then for input of '--optxyz' or '--opt xyz' returns xyz. +*/ +char * +getStringArg (const char *szStart, char **argv, int *pi, int argc) +{ + if (argv[*pi][strlen (szStart)]) + { + return &argv[*pi][strlen (szStart)]; + } + else + { + ++(*pi); + if (*pi >= argc) + { + werror (E_ARGUMENT_MISSING, szStart); + /* Die here rather than checking for errors later. */ + exit (EXIT_FAILURE); + } + else + { + return argv[*pi]; + } + } +} + +/** Gets the integer argument to this option using the same rules as + getStringArg. +*/ +long +getIntArg (const char *szStart, char **argv, int *pi, int argc) +{ + char *p; + int val; + char *str = getStringArg (szStart, argv, pi, argc); + + val = strtol (str, &p, 0); + if (p == str || *p != '\0') + { + werror (E_BAD_INT_ARGUMENT, szStart); + /* Die here rather than checking for errors later. */ + exit (EXIT_FAILURE); + } + return val; +} + +static void +verifyShortOption (const char *opt) +{ + if (strlen (opt) != 2) + { + werror (W_EXCESS_SHORT_OPTIONS, opt); + } +} + +static bool +tryHandleUnsupportedOpt (char **argv, int *pi) +{ + if (argv[*pi][0] == '-') + { + const char *longOpt = ""; + char shortOpt = -1; + int i; + + if (argv[*pi][1] == '-') + { + /* Long option. */ + longOpt = argv[*pi]; + } + else + { + shortOpt = argv[*pi][1]; + } + for (i = 0; i < LENGTH (unsupportedOptTable); i++) + { + if (unsupportedOptTable[i].shortOpt == shortOpt || + (longOpt && unsupportedOptTable[i].longOpt && !strcmp (unsupportedOptTable[i].longOpt, longOpt))) + { + /* Found an unsupported opt. */ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 100); + dbuf_printf (&dbuf, "%s%c%c", longOpt ? longOpt : "", shortOpt ? '-' : ' ', shortOpt ? shortOpt : ' '); + werror (W_UNSUPP_OPTION, dbuf_detach_c_str (&dbuf), unsupportedOptTable[i].message); + return 1; + } + } + /* Didn't find in the table */ + return 0; + } + else + { + /* Not an option, so can't be unsupported :) */ + return 0; + } +} + +static bool +scanOptionsTable (const OPTION * optionsTable, char shortOpt, const char *longOpt, char **argv, int *pi, int argc) +{ + int i; + + for (i = 0; optionsTable[i].shortOpt != 0 || optionsTable[i].longOpt != NULL || optionsTable[i].help != NULL; i++) + { + if (optionsTable[i].shortOpt == shortOpt) + { + if (optionsTable[i].pparameter != NULL) + { + verifyShortOption (argv[*pi]); + + (*(int *) optionsTable[i].pparameter)++; + + return TRUE; + } + } + else + { + size_t len = optionsTable[i].longOpt ? strlen (optionsTable[i].longOpt) : 0; + + if (longOpt && + (optionsTable[i].arg_type != CLAT_BOOLEAN || + (optionsTable[i].arg_type == CLAT_BOOLEAN && len == strlen (longOpt) && optionsTable[i].longOpt)) && + strncmp (optionsTable[i].longOpt, longOpt, len) == 0) + { + /* If it is a flag then we can handle it here */ + if (optionsTable[i].pparameter != NULL) + { + switch (optionsTable[i].arg_type) + { + case CLAT_BOOLEAN: + (*(int *) optionsTable[i].pparameter)++; + break; + + case CLAT_INTEGER: + *(int *) optionsTable[i].pparameter = getIntArg (optionsTable[i].longOpt, argv, pi, argc); + break; + + case CLAT_STRING: + if (*(char **) optionsTable[i].pparameter) + Safe_free (*(char **) optionsTable[i].pparameter); + *(char **) optionsTable[i].pparameter = + Safe_strdup (getStringArg (optionsTable[i].longOpt, argv, pi, argc)); + break; + + case CLAT_SET: + if (*(set **) optionsTable[i].pparameter) + { + deleteSet ((set **) optionsTable[i].pparameter); + } + setParseWithComma ((set **) optionsTable[i].pparameter, + getStringArg (optionsTable[i].longOpt, argv, pi, argc)); + break; + + case CLAT_ADD_SET: + addSet ((set **) optionsTable[i].pparameter, + Safe_strdup (getStringArg (optionsTable[i].longOpt, argv, pi, argc))); + break; + } + return TRUE; + } + else + { + /* Not a flag. Handled manually later. */ + return FALSE; + } + } + } + } + /* Didn't find in the table */ + return FALSE; +} + +static bool +tryHandleSimpleOpt (char **argv, int *pi, int argc) +{ + if (argv[*pi][0] == '-') + { + const char *longOpt = ""; + char shortOpt = -1; + + if (argv[*pi][1] == '-') + { + /* Long option. */ + longOpt = argv[*pi]; + } + else + { + shortOpt = argv[*pi][1]; + } + + if (scanOptionsTable (optionsTable, shortOpt, longOpt, argv, pi, argc)) + { + return TRUE; + } + else if (port && port->poptions && scanOptionsTable (port->poptions, shortOpt, longOpt, argv, pi, argc)) + { + return TRUE; + } + else + { + return FALSE; + } + } + else + { + /* Not an option, so can't be handled. */ + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* parseCmdLine - parses the command line and sets the options */ +/*-----------------------------------------------------------------*/ +static int +parseCmdLine (int argc, char **argv) +{ + int i; + + /* go thru all whole command line */ + for (i = 1; i < argc; i++) + { + if (i >= argc) + break; + + /* check port specific options before general ones */ + if (port->parseOption (&argc, argv, &i) == TRUE) + { + continue; + } + + if (tryHandleUnsupportedOpt (argv, &i) == TRUE) + { + continue; + } + + if (tryHandleSimpleOpt (argv, &i, argc) == TRUE) + { + continue; + } + + /* options */ + if (argv[i][0] == '-' && argv[i][1] == '-') + { + if (strcmp (argv[i], OPTION_USE_STDOUT) == 0) + { + if (options.use_stdout == 0) + { + options.use_stdout = 1; + dup2 (STDOUT_FILENO, STDERR_FILENO); + } + continue; + } + if (strcmp (argv[i], OPTION_HELP) == 0) + { + printUsage (FALSE); + exit (EXIT_SUCCESS); + } + + if (strcmp (argv[i], OPTION_OUT_FMT_IHX) == 0) + { + options.out_fmt = 'i'; + continue; + } + + if (strcmp (argv[i], OPTION_OUT_FMT_S19) == 0) + { + options.out_fmt = 's'; + continue; + } + + if (strcmp (argv[i], OPTION_SMALL_MODEL) == 0) + { + _setModel (MODEL_SMALL, argv[i]); + continue; + } + + if (strcmp (argv[i], OPTION_MEDIUM_MODEL) == 0) + { + _setModel (MODEL_MEDIUM, argv[i]); + continue; + } + + if (strcmp (argv[i], OPTION_LARGE_MODEL) == 0) + { + _setModel (MODEL_LARGE, argv[i]); + continue; + } + + if (strcmp (argv[i], OPTION_HUGE_MODEL) == 0) + { + _setModel (MODEL_HUGE, argv[i]); + continue; + } + + if (strcmp (argv[i], OPTION_VERSION) == 0) + { + printVersionInfo (stdout); + exit (EXIT_SUCCESS); + continue; + } + + if (strcmp (argv[i], OPTION_XRAM_LOC) == 0) + { + int val = getIntArg (OPTION_XRAM_LOC, argv, &i, argc); + if (options.xdata_loc == options.xstack_loc) + options.xstack_loc = val; + options.xdata_loc = val; + continue; + } + + if (strcmp (argv[i], OPTION_XRAM_SIZE) == 0) + { + options.xram_size = getIntArg (OPTION_XRAM_SIZE, argv, &i, argc); + options.xram_size_set = TRUE; + continue; + } + + if (strcmp (argv[i], OPTION_NO_GCSE) == 0) + { + optimize.global_cse = 0; + continue; + } + + if (strcmp (argv[i], OPTION_NO_LOOP_INV) == 0) + { + optimize.loopInvariant = 0; + continue; + } + + if (strcmp (argv[i], OPTION_NO_LABEL_OPT) == 0) + { + optimize.label2 = 0; + optimize.label4 = 0; + continue; + } + + if (strcmp (argv[i], OPTION_NO_LOOP_IND) == 0) + { + optimize.loopInduction = 0; + continue; + } + + if (strcmp (argv[i], OPTION_OPT_CODE_SPEED) == 0) + { + optimize.codeSpeed = 1; + optimize.codeSize = 0; + continue; + } + + if (strcmp (argv[i], OPTION_OPT_CODE_SIZE) == 0) + { + optimize.codeSpeed = 0; + optimize.codeSize = 1; + continue; + } + + if (strcmp (argv[i], OPTION_NO_LOSPRE) == 0) + { + optimize.lospre = 0; + continue; + } + + if (strcmp (argv[i], OPTION_ALLOW_UNSAFE_READ) == 0) + { + optimize.allow_unsafe_read = 1; + continue; + } + + if (strcmp (argv[i], OPTION_LESS_PEDANTIC) == 0) + { + options.lessPedantic = 1; + setErrorLogLevel (ERROR_LEVEL_WARNING); + continue; + } + + if (strcmp (argv[i], OPTION_DISABLE_WARNING) == 0) + { + int w = getIntArg (OPTION_DISABLE_WARNING, argv, &i, argc); + setWarningDisabled (w); + continue; + } + + if (strcmp (argv[i], OPTION_WERROR) == 0) + { + setWError (1); + addSet (&preArgvSet, Safe_strdup ("-Werror")); + continue; + } + + if (strcmp (argv[i], OPTION_STD_C89) == 0) + { + options.std_c95 = 0; + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + continue; + } + + if (strcmp (argv[i], OPTION_STD_C95) == 0) + { + options.std_c95 = 1; + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + continue; + } + + if (strcmp (argv[i], OPTION_STD_C99) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 0; + continue; + } + + if (strcmp (argv[i], OPTION_STD_C11) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 0; + options.std_sdcc = 0; + continue; + } + + if (strcmp (argv[i], OPTION_STD_C2X) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 1; + options.std_sdcc = 0; + continue; + } + + if (strcmp (argv[i], OPTION_STD_SDCC89) == 0) + { + options.std_c95 = 0; + options.std_c99 = 0; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 1; + continue; + } + + if (strcmp (argv[i], OPTION_STD_SDCC99) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 0; + options.std_c2x = 0; + options.std_sdcc = 1; + continue; + } + + if (strcmp (argv[i], OPTION_STD_SDCC11) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 0; + options.std_sdcc = 1; + continue; + } + + if (strcmp (argv[i], OPTION_STD_SDCC2X) == 0) + { + options.std_c95 = 1; + options.std_c99 = 1; + options.std_c11 = 1; + options.std_c2x = 1; + options.std_sdcc = 1; + continue; + } + + if (strcmp (argv[i], OPTION_CODE_SEG) == 0) + { + struct dbuf_s segname; + + dbuf_init (&segname, 16); + dbuf_printf (&segname, "%-8s(CODE)", getStringArg (OPTION_CODE_SEG, argv, &i, argc)); + if (options.code_seg) + Safe_free (options.code_seg); + options.code_seg = dbuf_detach (&segname); + continue; + } + + if (strcmp (argv[i], OPTION_CONST_SEG) == 0) + { + struct dbuf_s segname; + + dbuf_init (&segname, 16); + dbuf_printf (&segname, "%-8s(CODE)", getStringArg (OPTION_CONST_SEG, argv, &i, argc)); + if (options.const_seg) + Safe_free (options.const_seg); + options.const_seg = dbuf_detach (&segname); + continue; + } + + if (strcmp (argv[i], OPTION_DATA_SEG) == 0) + { + struct dbuf_s segname; + + dbuf_init (&segname, 16); + dbuf_printf (&segname, "%-8s(DATA)", getStringArg (OPTION_DATA_SEG, argv, &i, argc)); + if (options.data_seg) + Safe_free (options.data_seg); + options.data_seg = dbuf_detach (&segname); + continue; + } + + if (strcmp (argv[i], OPTION_PEEP_RETURN) == 0) + { + options.peepReturn = 1; + continue; + } + + if (strcmp (argv[i], OPTION_NO_PEEP_RETURN) == 0) + { + options.peepReturn = -1; + continue; + } + + if (strcmp (argv[i], OPTION_DEBUG) == 0) + { + if (options.peepReturn == 0) + options.peepReturn = -1; + options.debug = 1; + continue; + } + + werror (W_UNKNOWN_OPTION, argv[i]); + continue; + } + + /* if preceded by '-' then option */ + if (*argv[i] == '-') + { + switch (argv[i][1]) + { + case 'h': + verifyShortOption (argv[i]); + + printUsage (FALSE); + exit (EXIT_SUCCESS); + break; + + case 'm': + /* Used to select the port. But this has already been done. */ + break; + + case 'p': + /* Used to select the processor in port. But this has + * already been done. */ + break; + + case 'c': + verifyShortOption (argv[i]); + + options.cc_only = 1; + break; + + case 'L': + addSet (&libPathsSet, Safe_strdup (getStringArg ("-L", argv, &i, argc))); + break; + + case 'l': + addSet (&libFilesSet, Safe_strdup (getStringArg ("-l", argv, &i, argc))); + break; + + case 'o': + { + char *outName = getStringArg ("-o", argv, &i, argc); + size_t len = strlen (outName); + + /* point to last character */ + if (IS_DIR_SEPARATOR (outName[len - 1])) + { + /* only output path specified */ + dstPath = Safe_strndup (outName, len - 1); + fullDstFileName = NULL; + } + else + { + struct dbuf_s path; + + dbuf_init (&path, PATH_MAX); + fullDstFileName = Safe_strdup (outName); + + /* get rid of the "."-extension */ + dbuf_splitFile (outName, &path, NULL); + + dstFileName = dbuf_detach_c_str (&path); + + dbuf_init (&path, PATH_MAX); + /* strip module name to get path */ + if (dbuf_splitPath (dstFileName, &path, NULL)) + dstPath = dbuf_detach_c_str (&path); + else + dbuf_destroy (&path); + } + break; + } + + case 'W': + /* pre-processer options */ + if (argv[i][2] == 'p') + { + setParseWithComma (&preArgvSet, getStringArg ("-Wp", argv, &i, argc)); + } + /* linker options */ + else if (argv[i][2] == 'l') + { + setParseWithComma (&linkOptionsSet, getStringArg ("-Wl", argv, &i, argc)); + } + /* assembler options */ + else if (argv[i][2] == 'a') + { + setParseWithComma (&asmOptionsSet, getStringArg ("-Wa", argv, &i, argc)); + } + else + { + werror (W_UNKNOWN_OPTION, argv[i]); + } + break; + + case 'v': + verifyShortOption (argv[i]); + + printVersionInfo (stdout); + exit (EXIT_SUCCESS); + break; + + /* preprocessor options */ + case 'M': + { + if (argv[i][2] == 'M') + { + if (argv[i][3] == 'D') + { + options.dependencyFileOpt = USER_DEPENDENCY_FILE_OPT; + } + else + { + addSet (&preArgvSet, Safe_strdup ("-MM")); + preProcOnly = 1; + } + } + else + { + if (argv[i][2] == 'D') + { + options.dependencyFileOpt = SYSTEM_DEPENDENCY_FILE_OPT; + } + else + { + addSet (&preArgvSet, Safe_strdup ("-M")); + preProcOnly = 1; + } + } + break; + } + + case 'd': + case 'D': + case 'I': + case 'A': + case 'U': + { + char sOpt = argv[i][1]; + char *rest, *s; + struct dbuf_s dbuf; + + if (argv[i][2] == ' ' || argv[i][2] == '\0') + { + i++; + if (i >= argc) + { + /* No argument. */ + werror (E_ARGUMENT_MISSING, argv[i - 1]); + break; + } + else + { + rest = argv[i]; + } + } + else + rest = &argv[i][2]; + + if (sOpt == 'Y') + sOpt = 'I'; + + s = shell_escape (rest); + dbuf_init (&dbuf, 256); + dbuf_printf (&dbuf, "-%c%s", sOpt, s); + Safe_free (s); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + if (sOpt == 'I') + { + addSet (&includeDirsSet, Safe_strdup (rest)); + addSet (&userIncDirsSet, Safe_strdup (rest)); + } + } + break; + + default: + werror (W_UNKNOWN_OPTION, argv[i]); + } + continue; + } + + /* no option must be a filename */ + if (options.c1mode) + { + werror (W_NO_FILE_ARG_IN_C1, argv[i]); + } + else + { + processFile (argv[i]); + } + } + + if (gasOutput && noAssemble) + { + /* Incompatible assembly output formats. */ + werror(W_ILLEGAL_OPT_COMBINATION); + } + + /* some sanity checks in c1 mode */ + if (options.c1mode) + { + const char *s; + + if (fullSrcFileName) + { + fclose (srcFile); + werror (W_NO_FILE_ARG_IN_C1, fullSrcFileName); + } + fullSrcFileName = NULL; + for (s = setFirstItem (relFilesSet); s != NULL; s = setNextItem (relFilesSet)) + { + werror (W_NO_FILE_ARG_IN_C1, s); + } + for (s = setFirstItem (libFilesSet); s != NULL; s = setNextItem (libFilesSet)) + { + werror (W_NO_FILE_ARG_IN_C1, s); + } + deleteSet (&relFilesSet); + deleteSet (&libFilesSet); + + if (options.cc_only || noAssemble || preProcOnly || gasOutput) + { + werror (W_ILLEGAL_OPT_COMBINATION); + } + options.cc_only = noAssemble = preProcOnly = 0; + if (!dstFileName) + { + werror (E_NEED_OPT_O_IN_C1); + exit (EXIT_FAILURE); + } + else + { + char *p, *m; + + m = Safe_strdup (dstFileName); + for (p = m; *p; ++p) + if (!isalnum ((unsigned char) *p)) + *p = '_'; + moduleName = m; + } + } + /* if no dstFileName given with -o, we've to find one: */ + if (!dstFileName) + { + const char *s; + + /* use the modulename from the C-source */ + if (fullSrcFileName) + { + struct dbuf_s path; + + if (*dstPath != '\0') + { + dbuf_init (&path, 128); + dbuf_makePath (&path, dstPath, moduleNameBase); + dstFileName = dbuf_detach_c_str (&path); + } + else + dstFileName = Safe_strdup (moduleNameBase); + } + /* use the modulename from the first object file */ + else if ((s = peekSet (relFilesSet)) != NULL) + { + struct dbuf_s file; + + dbuf_init (&file, 128); + + /* get rid of the "."-extension */ + dbuf_splitFile (s, &file, NULL); + + s = dbuf_detach_c_str (&file); + + dbuf_init (&file, PATH_MAX); + + dbuf_splitPath (s, NULL, &file); + + if (*dstPath != '\0') + { + struct dbuf_s path; + + dbuf_init (&path, PATH_MAX); + dbuf_makePath (&path, dstPath, dbuf_detach_c_str (&file)); + dstFileName = dbuf_detach_c_str (&path); + } + else + { + dstFileName = dbuf_detach_c_str (&file); + } + } + /* else no module given: help text is displayed */ + } + + /* set int, long and float reentrancy based on stack-auto */ + if (options.stackAuto) + { + options.intlong_rent++; + options.float_rent++; + } + + /* if debug option is set then open the cdbFile */ + if (options.debug && fullSrcFileName) + { + struct dbuf_s adbFile; + + dbuf_init (&adbFile, PATH_MAX); + dbuf_append_str (&adbFile, dstFileName); + dbuf_append_str (&adbFile, ".adb"); + + if (debugFile->openFile (dbuf_c_str (&adbFile))) + debugFile->writeModule (moduleName); + else + werror (E_FILE_OPEN_ERR, dbuf_c_str (&adbFile)); + + dbuf_destroy (&adbFile); + } + MSVC_style (options.vc_err_style); + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* finalizeOptions - finalize (post-process) options */ +/*-----------------------------------------------------------------*/ +static void +finalizeOptions (void) +{ + /* no peephole comments if not verbose asm */ + if (!options.verboseAsm) + options.noPeepComments = 1; +} + +static const char * +getOutFmtExt (void) +{ + switch (options.out_fmt) + { + default: + case 'i': + return ".ihx"; + + case 's': + return ".s19"; + + case 'E': + return ".elf"; + } +} + +/*-----------------------------------------------------------------*/ +/* linkEdit : - calls the linkage editor with options */ +/*-----------------------------------------------------------------*/ +static void +linkEdit (char **envp) +{ + FILE *lnkfile; + int system_ret; + const char *s; + struct dbuf_s linkerScriptFileName; + struct dbuf_s binFileName; + char *buf, *tb; + + dbuf_init (&linkerScriptFileName, PATH_MAX); + dbuf_init (&binFileName, PATH_MAX); + + if (port->linker.needLinkerScript) + { + char out_fmt = (options.out_fmt == 0) ? 'i' : options.out_fmt; + + if (NULL != fullDstFileName) + { + dbuf_append_str (&binFileName, fullDstFileName); + } + else + { + dbuf_append_str (&binFileName, dstFileName); + dbuf_append_str (&binFileName, getOutFmtExt ()); + } + + /* first we need to create the .lk file */ + dbuf_printf (&linkerScriptFileName, "%s.lk", dstFileName); + if (!(lnkfile = fopen (dbuf_c_str (&linkerScriptFileName), "w"))) + { + werror (E_FILE_OPEN_ERR, dbuf_c_str (&linkerScriptFileName)); + exit (EXIT_FAILURE); + } + + if (TARGET_Z80_LIKE) + { + fprintf (lnkfile, "-mjwx\n-%c %s\n", out_fmt, dbuf_c_str (&binFileName)); + } + else /* For all the other ports which need linker script */ + { + fprintf (lnkfile, "-muwx\n-%c %s\n", out_fmt, dbuf_c_str (&binFileName)); + if (TARGET_MCS51_LIKE) + fprintf (lnkfile, "-M\n"); + if (!options.no_pack_iram) + fprintf (lnkfile, "-Y\n"); + else + werror (W_DEPRECATED_OPTION, "--no-pack-iram"); + } + + if (!TARGET_Z80_LIKE) /* Not for the z80, gbz80 */ + { + /* if iram size specified */ + if (options.iram_size) + fprintf (lnkfile, "-I 0x%04x\n", options.iram_size); + + /* if stack size specified */ + if (options.stack_size) + fprintf (lnkfile, "-S 0x%02x\n", options.stack_size); + + /* if xram size specified */ + if (options.xram_size_set) + fprintf (lnkfile, "-X 0x%04x\n", options.xram_size); + + /* if code size specified */ + if (options.code_size) + fprintf (lnkfile, "-C 0x%04x\n", options.code_size); + } + + if (options.debug) + fprintf (lnkfile, "-y\n"); + +#define WRITE_SEG_LOC(N, L) \ + if (N) \ + { \ + char *c, *segName; \ + segName = Safe_strdup (N); \ + c = strtok (segName, " \t"); \ + fprintf (lnkfile,"-b %s = 0x%04x\n", c, L); \ + if (segName) { Safe_free (segName); } \ + } + + if (!TARGET_Z80_LIKE) /* Not for the z80, z180, gbz80 */ + { + + /* code segment start */ + WRITE_SEG_LOC (HOME_NAME, options.code_loc); + + /* data segment start. If zero, the linker chooses + the best place for data */ + if (options.data_loc) + { + WRITE_SEG_LOC (DATA_NAME, options.data_loc); + } + + /* xdata segment start. If zero, the linker chooses + the best place for xdata */ + if (options.xdata_loc) + { + WRITE_SEG_LOC (XDATA_NAME, options.xdata_loc); + } + + /* pdata/xstack segment start. If zero, the linker + chooses the best place for them */ + if (options.xstack_loc) + { + WRITE_SEG_LOC (PDATA_NAME, options.xstack_loc); + } + + /* indirect data */ + if (IDATA_NAME) + { + WRITE_SEG_LOC (IDATA_NAME, options.idata_loc); + } + + /* bit segment start */ + WRITE_SEG_LOC (BIT_NAME, 0); + + /* stack start */ + if ((options.stack_loc) && (options.stack_loc < 0x100) && !TARGET_HC08_LIKE) + { + WRITE_SEG_LOC ("SSEG", options.stack_loc); + } + } + else /* For the z80, z180, gbz80 */ + { + WRITE_SEG_LOC ("_CODE", options.code_loc); + WRITE_SEG_LOC ("_DATA", options.data_loc); + } + + /* If the port has any special linker area declarations, get 'em */ + if (port->extraAreas.genExtraAreaLinkOptions) + { + port->extraAreas.genExtraAreaLinkOptions (lnkfile); + } + + /* add the extra linker options */ + fputStrSet (lnkfile, linkOptionsSet); + + /* command line defined library paths if specified */ + for (s = setFirstItem (libPathsSet); s != NULL; s = setNextItem (libPathsSet)) + fprintf (lnkfile, "-k %s\n", s); + + /* standard library path */ + if (!options.nostdlib) + { + for (s = setFirstItem (libDirsSet); s != NULL; s = setNextItem (libDirsSet)) + fprintf (lnkfile, "-k %s\n", s); + } + + /* command line defined library files if specified */ + for (s = setFirstItem (libFilesSet); s != NULL; s = setNextItem (libFilesSet)) + fprintf (lnkfile, "-l %s\n", s); + + /* standard library files */ + if (!options.nostdlib) + { + if (NULL != port->linker.libs) + { + const char *const *p; + + for (p = port->linker.libs; NULL != *p; ++p) + { + fprintf (lnkfile, "-l %s\n", *p); + } + } + } + + /* put in the object file, generated from the C cource */ + if (fullSrcFileName) + { + struct dbuf_s path; + + dbuf_init (&path, PATH_MAX); + dbuf_printf (&path, "%s%s", dstFileName, port->linker.rel_ext); + addSetHead (&relFilesSet, dbuf_detach (&path)); + } + + if (!options.no_std_crt0) + { + const char *const *p; + set *crtSet = NULL; + + if (NULL != port->linker.crt) + { + struct dbuf_s crtpath; + + dbuf_init (&crtpath, PATH_MAX); + + for (p = port->linker.crt; NULL != *p; ++p) + { + /* Try to find where C runtime files are ... + It is very important for this file to be first on the linking proccess + so the areas are set in the correct order, expecially _GSINIT */ + for (s = setFirstItem (libDirsSet); s != NULL; s = setNextItem (libDirsSet)) + { + dbuf_set_length (&crtpath, 0); + dbuf_printf (&crtpath, "%s%c%s", s, DIR_SEPARATOR_CHAR, *p); + + if (!access (dbuf_c_str (&crtpath), 0)) /* Found it! */ + { + /* append C runtime file to the crt list */ + addSet (&crtSet, Safe_strdup (dbuf_c_str (&crtpath))); + break; + } + } + if (NULL == s) + { + /* not found in standard library directories, search in user defined library paths */ + /* TODO: should crt be searched here at all? */ + for (s = setFirstItem (libPathsSet); s != NULL; s = setNextItem (libPathsSet)) + { + dbuf_set_length (&crtpath, 0); + dbuf_printf (&crtpath, "%s%c%s", s, DIR_SEPARATOR_CHAR, *p); + + if (!access (dbuf_c_str (&crtpath), 0)) /* Found it! */ + { + /* append C runtime file to the crt list */ + addSet (&crtSet, Safe_strdup (dbuf_c_str (&crtpath))); + break; + } + } + } + if (NULL == s) + fprintf (stderr, "Warning: couldn't find %s\n", *p); + } + dbuf_destroy (&crtpath); + } + + /* Merge crtSet and relFilesSet */ + mergeSets (&crtSet, relFilesSet); + relFilesSet = crtSet; + } + + /* put in all object files */ + fputStrSet (lnkfile, relFilesSet); + + fprintf (lnkfile, "\n-e\n"); + fclose (lnkfile); + } /* if(port->linker.needLinkerScript) */ + + if (port->linker.cmd) + { + /* shell_escape file names */ + char *b3 = shell_escape (dbuf_c_str (&linkerScriptFileName)); + char *bfn = shell_escape (dbuf_c_str (&binFileName)); + + buf = buildCmdLine (port->linker.cmd, b3, bfn, NULL, linkOptionsSet); + Safe_free (b3); + Safe_free (bfn); + } + else + { + buf = buildMacros (port->linker.mcmd); + } + + dbuf_destroy (&linkerScriptFileName); + + tb = setPrefixSuffix (buf); + if (options.verbose) + printf ("sdcc: %s\n", tb); + system_ret = sdcc_system (tb); + + /* if the binary file name is defined, + rename the linker output file name to binary file name */ + if (fullDstFileName) + { + struct dbuf_s lkrFileName; + + dbuf_init (&lkrFileName, PATH_MAX); + dbuf_append_str (&lkrFileName, dstFileName); + dbuf_append_str (&lkrFileName, getOutFmtExt ()); + + if (FILENAME_CMP (dbuf_c_str (&binFileName), dbuf_c_str (&lkrFileName))) + remove (dbuf_c_str (&binFileName)); + rename (dbuf_c_str (&lkrFileName), dbuf_c_str (&binFileName)); + + dbuf_destroy (&lkrFileName); + } + + dbuf_destroy (&binFileName); + + if (system_ret) + exit (EXIT_FAILURE); +} + +/*-----------------------------------------------------------------*/ +/* assemble - spawns the assembler with arguments */ +/*-----------------------------------------------------------------*/ +static void +assemble (char **envp) +{ + struct dbuf_s asmName; + + if (port->assembler.do_assemble) + { + port->assembler.do_assemble (asmOptionsSet); + } + else + { + char *buf, *tb; + + /* build assembler output filename */ + dbuf_init (&asmName, PATH_MAX); + + /* -o option overrides default name? */ + if (options.cc_only && fullDstFileName) + { + dbuf_append_str (&asmName, fullDstFileName); + } + else + { + /* the assembled file gets the name of the first module */ + dbuf_printf (&asmName, "%s%s", dstFileName, port->linker.rel_ext); + } + + if (port->assembler.cmd) + { + /* shell_escape file names */ + char *dfn = shell_escape (dstFileName); + char *asmn = shell_escape (dbuf_c_str (&asmName)); + + buf = buildCmdLine (port->assembler.cmd, dfn, asmn, + options.debug ? port->assembler.debug_opts : port->assembler.plain_opts, asmOptionsSet); + Safe_free (dfn); + Safe_free (asmn); + } + else + { + buf = buildMacros (port->assembler.mcmd); + } + + dbuf_destroy (&asmName); + tb = setPrefixSuffix (buf); + + if (options.verbose) + printf ("sdcc: %s\n", tb); + if (sdcc_system (tb)) + { + Safe_free (buf); + /* either system() or the assembler itself has reported an error */ + exit (EXIT_FAILURE); + } + Safe_free (buf); + + if (options.cc_only && fullDstFileName && TARGET_PIC_LIKE) + { + /* gpasm assembler doesn't properly handle the -o option: + the file extension is replaced with .o, + so sdcc have to rename the object file manually. + This has been fixed in gpasm svn: + http://sourceforge.net/tracker/?func=detail&aid=3018645&group_id=41924&atid=431665 + TODO: This code should be removed when the next gputils version + after gpasm-0.13.7 beta will be released */ + struct dbuf_s outName; + dbuf_init (&outName, PATH_MAX); + dbuf_printf (&outName, "%s%s", dstFileName, port->linker.rel_ext); + + if (strcmp (dbuf_c_str (&outName), fullDstFileName)) + { + /* file names are different: rename the genatated object file */ + remove (fullDstFileName); + rename (dbuf_c_str (&outName), fullDstFileName); + } + dbuf_destroy (&outName); + } + } +} + +/*-----------------------------------------------------------------*/ +/* preProcess - spawns the preprocessor with arguments */ +/*-----------------------------------------------------------------*/ +static int +preProcess (char **envp) +{ + if (options.c1mode) + { + yyin = stdin; + } + else + { + const char *s; + set *inclList = NULL; + char *buf; + + if (NULL != port->linker.rel_ext) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 256); + dbuf_printf (&dbuf, "-obj-ext=%s", port->linker.rel_ext); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + if (options.dependencyFileOpt) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + if (options.dependencyFileOpt == SYSTEM_DEPENDENCY_FILE_OPT) + dbuf_append_str (&dbuf, "-MD "); + else + dbuf_append_str (&dbuf, "-MMD "); + if (fullDstFileName) + dbuf_splitFile (fullDstFileName, &dbuf, NULL); + else + dbuf_append_str (&dbuf, dstFileName); + dbuf_append_str (&dbuf, ".d"); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + + dbuf_init (&dbuf, PATH_MAX); + if (fullDstFileName) + dbuf_printf (&dbuf, "-MT %s", fullDstFileName); + else + dbuf_printf (&dbuf, "-MT %s%s", dstFileName, port->linker.rel_ext); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + /* if using dollar signs in identifiers */ + if (options.dollars_in_ident) + addSet (&preArgvSet, Safe_strdup ("-fdollars-in-identifiers")); + + /* if using external stack define the macro */ + if (options.useXstack) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_USE_XSTACK")); + + /* set the macro for stack autos */ + if (options.stackAuto) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_STACK_AUTO")); + + /* set the macro for stack autos */ + if (options.stack10bit) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_STACK_TENBIT")); + + /* set the macro for no overlay */ + if (options.noOverlay) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_NOOVERLAY")); + + /* set the macro for unsigned char */ + if (options.signed_char) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_CHAR_SIGNED")); + else + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_CHAR_UNSIGNED")); + + /* set the macro for non-free */ + if (options.use_non_free) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_USE_NON_FREE")); + + /* set the macro for large model */ + switch (options.model) + { + case MODEL_LARGE: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_LARGE")); + break; + + case MODEL_SMALL: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_SMALL")); + break; + + case MODEL_COMPACT: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_COMPACT")); + break; + + case MODEL_MEDIUM: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_MEDIUM")); + break; + + case MODEL_HUGE: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_HUGE")); + break; + + case MODEL_FLAT24: + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_MODEL_FLAT24")); + break; + + case NO_MODEL: + break; + + default: + werror (W_UNKNOWN_MODEL, __FILE__, __LINE__); + break; + } + + /* set macro corresponding to compiler option */ + if (options.intlong_rent) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_INT_LONG_REENT")); + + /* set macro corresponding to compiler option */ + if (options.float_rent) + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_FLOAT_REENT")); + + if (options.all_callee_saves) + addSet(&preArgvSet, Safe_strdup("-D__SDCC_ALL_CALLEE_SAVES")); + + /* add SDCC version number */ + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 32); + dbuf_printf (&dbuf, "-D__SDCC=%d_%d_%d", SDCC_VERSION_HI, SDCC_VERSION_LO, SDCC_VERSION_P); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 32); + dbuf_printf (&dbuf, "-D__SDCC_VERSION_MAJOR=%d", SDCC_VERSION_HI); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 32); + dbuf_printf (&dbuf, "-D__SDCC_VERSION_MINOR=%d", SDCC_VERSION_LO); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 32); + dbuf_printf (&dbuf, "-D__SDCC_VERSION_PATCH=%d", SDCC_VERSION_P); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + /* A macro that has been deprecated since 3.2.0, + since its name makes it non-compliant. + It got removed a few times, but keeps coming back. + This time it got added back for the 3.7.0 release + to support the old SiLabs IDE */ + if (TARGET_IS_MCS51 && options.std_sdcc) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 32); + dbuf_printf (&dbuf, "-DSDCC=%d%d%d", SDCC_VERSION_HI, SDCC_VERSION_LO, SDCC_VERSION_P); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + /* add SDCC revision number */ + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 20); + dbuf_printf (&dbuf, "-D__SDCC_REVISION=%s", getBuildNumber ()); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + /* add port (processor information to processor */ + addSet (&preArgvSet, Safe_strdup ("-D__SDCC_{port}")); + + /* Optional C features not (yet) supported by SDCC */ + addSet (&preArgvSet, Safe_strdup ("-D__STDC_NO_COMPLEX__=1")); + addSet (&preArgvSet, Safe_strdup ("-D__STDC_NO_THREADS__=1")); + addSet (&preArgvSet, Safe_strdup ("-D__STDC_NO_ATOMICS__=1")); + addSet (&preArgvSet, Safe_strdup ("-D__STDC_NO_VLA__=1")); + + /* Character encoding - these need to be set in device/lib/Makefile.in for $CPP, too */ + addSet (&preArgvSet, Safe_strdup ("-D__STDC_ISO_10646__=201409L")); // wchar_t is UTF-32 + addSet (&preArgvSet, Safe_strdup ("-D__STDC_UTF_16__=1")); // char16_t is UTF-16 + addSet (&preArgvSet, Safe_strdup ("-D__STDC_UTF_32__=1")); // char32_t is UTF-32 + + /* standard include path */ + if (!options.nostdinc) + { + inclList = processStrSet (includeDirsSet, "-isystem ", NULL, shell_escape); + mergeSets (&preArgvSet, inclList); + } + + setMainValue ("cppextraopts", (s = joinStrSet (preArgvSet))); + Safe_free ((void *) s); + if (inclList != NULL) + deleteSet (&inclList); + + if (preProcOnly && fullDstFileName) + { + /* -E and -o given */ + char *s = shell_escape (fullDstFileName); + + setMainValue ("cppoutfilename", s); + Safe_free (s); + } + else + { + /* Piping: set cppoutfilename to NULL, to avoid empty quotes */ + setMainValue ("cppoutfilename", NULL); + } + + if (options.verbose) + printf ("sdcc: Calling preprocessor...\n"); + buf = buildMacros (_preCmd); + buf = setPrefixSuffix (buf); + if (options.verbose) + printf ("sdcc: %s\n", buf); + + if (preProcOnly) + { + if (sdcc_system (buf)) + { + exit (EXIT_FAILURE); + } + + exit (EXIT_SUCCESS); + } + + yyin = sdcc_popen (buf); + if (yyin == NULL) + { + perror ("Preproc file not found"); + exit (EXIT_FAILURE); + } + } + + return 0; +} + +/* Set bin paths */ +static void +setBinPaths (const char *argv0) +{ + const char *p; + + /* + * Search logic: + * + * 1. - $SDCCDIR/PREFIX2BIN_DIR + * 2. - path(argv[0]) + * 3. - $PATH + */ + + /* do it in reverse mode, so that addSetHead() can be used + instead of slower addSet() */ + + if ((p = getBinPath (argv0)) != NULL) + addSetHead (&binPathSet, (void *) p); + + if ((p = getenv (SDCC_DIR_NAME)) != NULL) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + dbuf_append_str (&dbuf, p); + dbuf_append_str (&dbuf, PREFIX2BIN_DIR); + addSetHead (&binPathSet, dbuf_detach_c_str (&dbuf)); + } +} + +/* Set system include path */ +static void +setIncludePath (void) +{ + /* + * Search logic: + * + * 1. - $SDCC_INCLUDE/target + * 2. - $SDCC_INCLUDE + * 3. - $SDCC_HOME/PREFIX2DATA_DIR/INCLUDE_DIR_SUFFIX/target + * 4. - path(argv[0])/BIN2DATA_DIR/INCLUDE_DIR_SUFFIX/target + * 5. - DATADIR/INCLUDE_DIR_SUFFIX/target (only on *nix) + * 6. - $SDCC_HOME/PREFIX2DATA_DIR/INCLUDE_DIR_SUFFIX + * 7. - path(argv[0])/BIN2DATA_DIR/INCLUDE_DIR_SUFFIX + * 8. - DATADIR/INCLUDE_DIR_SUFFIX (only on *nix) + * 9. - $SDCC_HOME/PREFIX2DATA_DIR/NON_FREE_INCLUDE_DIR_SUFFIX/target + * 10. - path(argv[0])/BIN2DATA_DIR/NON_FREE_INCLUDE_DIR_SUFFIX/target + * 11. - DATADIR/NON_FREE_INCLUDE_DIR_SUFFIX/target (only on *nix) + * 12. - $SDCC_HOME/PREFIX2DATA_DIR/NON_FREE_INCLUDE_DIR_SUFFIX + * 13. - path(argv[0])/BIN2DATA_DIR/NON_FREE_INCLUDE_DIR_SUFFIX + * 14. - DATADIR/NON_FREE_INCLUDE_DIR_SUFFIX (only on *nix) + */ + + if (!options.nostdinc) + { + char *p; + set *tempSet; + + tempSet = processStrSet (dataDirsSet, NULL, INCLUDE_DIR_SUFFIX, NULL); + includeDirsSet = processStrSet (tempSet, NULL, DIR_SEPARATOR_STRING, NULL); + includeDirsSet = processStrSet (includeDirsSet, NULL, port->target, NULL); + mergeSets (&includeDirsSet, tempSet); + + if (options.use_non_free) + { + set *tempSet1; + + tempSet = processStrSet (dataDirsSet, NULL, NON_FREE_INCLUDE_DIR_SUFFIX, NULL); + tempSet1 = processStrSet (tempSet, NULL, DIR_SEPARATOR_STRING, NULL); + tempSet1 = processStrSet (tempSet1, NULL, port->target, NULL); + mergeSets (&tempSet1, tempSet); + mergeSets (&includeDirsSet, tempSet1); + } + + if ((p = getenv (SDCC_INCLUDE_NAME)) != NULL) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + addSetHead (&includeDirsSet, p); + dbuf_makePath (&dbuf, p, port->target); + addSetHead (&includeDirsSet, dbuf_detach (&dbuf)); + } + } +} + +/* Set system lib path */ +static void +setLibPath (void) +{ + /* + * Search logic: + * + * 1. - $SDCC_LIB/ + * 2. - $SDCC_LIB + * 3. - $SDCC_HOME/PREFIX2DATA_DIR/LIB_DIR_SUFFIX/ + * 4. - path(argv[0])/BIN2DATA_DIR/LIB_DIR_SUFFIX/ + * 5. - DATADIR/LIB_DIR_SUFFIX/ (only on *nix) + * 6. - $SDCC_HOME/PREFIX2DATA_DIR/NON_FREE_LIB_DIR_SUFFIX/ + * 7. - path(argv[0])/BIN2DATA_DIR/NON_FREE_LIB_DIR_SUFFIX/ + * 8. - DATADIR/NON_FREE_LIB_DIR_SUFFIX/ (only on *nix) + */ + + if (!options.nostdlib) + { + char *p; + const char *targetname; + + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + + targetname = port->target; + + dbuf_makePath (&dbuf, LIB_DIR_SUFFIX, port->general.get_model ? port->general.get_model () : targetname); + libDirsSet = processStrSet (dataDirsSet, NULL, dbuf_c_str (&dbuf), NULL); + + if (options.use_non_free) + { + dbuf_set_length (&dbuf, 0); + dbuf_makePath (&dbuf, NON_FREE_LIB_DIR_SUFFIX, port->general.get_model ? port->general.get_model () : targetname); + mergeSets (&libDirsSet, processStrSet (dataDirsSet, NULL, dbuf_c_str (&dbuf), NULL)); + } + + if ((p = getenv (SDCC_LIB_NAME)) != NULL) + { + addSetHead (&libDirsSet, Safe_strdup (p)); + + dbuf_set_length (&dbuf, 0); + dbuf_makePath (&dbuf, p, port->general.get_model ? port->general.get_model () : targetname); + addSetHead (&libDirsSet, dbuf_detach (&dbuf)); + } + else + dbuf_destroy (&dbuf); + } +} + +/* Set data path */ +static void +setDataPaths (const char *argv0) +{ + const char *p; + + /* + * Search logic: + * + * 1. - $SDCC_HOME/PREFIX2DATA_DIR + * 2. - path(argv[0])/BIN2DATA_DIR + * 3. - DATADIR (only on *nix) + */ + + if ((p = getenv (SDCC_DIR_NAME)) != NULL) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + dbuf_append_str (&dbuf, p); + dbuf_append_str (&dbuf, PREFIX2DATA_DIR); + addSetHead (&dataDirsSet, dbuf_detach_c_str (&dbuf)); + } + + if ((p = getBinPath (argv0)) != NULL) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + dbuf_append_str (&dbuf, p); + dbuf_append_str (&dbuf, BIN2DATA_DIR); + addSetHead (&dataDirsSet, dbuf_detach_c_str (&dbuf)); + } + +#ifdef _WIN32 + if (peekSet (dataDirsSet) == NULL) + { + /* this should never happen... */ + wassertl (0, "Can't get binary path"); + } +#else + addSet (&dataDirsSet, Safe_strdup (DATADIR)); +#endif + + setIncludePath (); + setLibPath (); +} + +static void +initValues (void) +{ + char *s; + + populateMainValues (_baseValues); + setMainValue ("port", port->target); + setMainValue ("objext", port->linker.rel_ext); + setMainValue ("asmext", port->assembler.file_ext); + + setMainValue ("dstfilename", dstFileName); + + s = fullSrcFileName ? shell_escape (fullSrcFileName) : "fullsrcfilename"; + setMainValue ("fullsrcfilename", s); + if (fullSrcFileName) + Safe_free (s); + + if (options.cc_only && fullDstFileName) + /* compile + assemble and -o given: -o specifies name of object file */ + { + setMainValue ("objdstfilename", fullDstFileName); + } + else + { + setMainValue ("objdstfilename", "{stdobjdstfilename}"); + } + if (fullDstFileName) + /* if we're linking, -o gives the final file name */ + { + setMainValue ("linkdstfilename", fullDstFileName); + } + else + { + setMainValue ("linkdstfilename", "{stdlinkdstfilename}"); + } + + /* + * Make sure the preprocessor is called with the "-std" option + * corresponding to the --std used to start sdcc + */ + setMainValue ("cppstd", + options.std_c11 ? "-std=c11 " : + (options.std_c99 ? "-std=c99 " : + (options.std_c95 ? "-std=iso9899:199409 " : + "-std=c89 "))); +} + +static void +doPrintSearchDirs (void) +{ + const char *s; + + printf ("programs:\n"); + fputStrSet (stdout, binPathSet); + + printf ("datadir:\n"); + fputStrSet (stdout, dataDirsSet); + + printf ("includedir:\n"); + fputStrSet (stdout, includeDirsSet); + + printf ("libdir:\n"); + for (s = setFirstItem (libDirsSet); s != NULL; s = setNextItem (libDirsSet)) + fprintf (stdout, "%s\n", s); + + printf ("libpath:\n"); + fputStrSet (stdout, libPathsSet); +} + + +static void +sig_handler (int signal) +{ + char *sig_string; + + switch (signal) + { + case SIGABRT: + sig_string = "SIGABRT"; + break; + + case SIGTERM: + sig_string = "SIGTERM"; + break; + + case SIGINT: + sig_string = "SIGINT"; + break; + + case SIGSEGV: + sig_string = "SIGSEGV"; + break; + + default: + sig_string = "Unknown?"; + break; + } + fprintf (stderr, "Caught signal %d: %s\n", signal, sig_string); + exit (EXIT_FAILURE); +} + +/* + * main routine + * initialises and calls the parser + */ + +int +main (int argc, char **argv, char **envp) +{ + /* get the prefix and the suffix of the sdcc command */ + getPrefixSuffix (argv[0]); + + /* set a larger stack size of a running sdcc process to 4MB */ + setStackSize (); + + /* turn all optimizations off by default */ + memset (&optimize, 0, sizeof (struct optimize)); + + if (NUM_PORTS == 0) + { + fprintf (stderr, "Build error: no ports are enabled.\n"); + exit (EXIT_FAILURE); + } + + /* install signal handler; + it's only purpose is to call exit() to remove temp files */ + if (!getenv ("SDCC_LEAVE_SIGNALS")) + { + signal (SIGABRT, sig_handler); + signal (SIGTERM, sig_handler); + signal (SIGINT, sig_handler); + signal (SIGSEGV, sig_handler); + } + + /* Before parsing the command line options, do a + * search for the port and processor and initialize + * them if they're found. (We can't gurantee that these + * will be the first options specified). + */ + + _findPort (argc, argv); + +#ifdef JAMIN_DS390 + if (strcmp (port->target, "mcs51") == 0) + { + printf ("DS390 jammed in A\n"); + _setPort ("ds390"); + ds390_jammed = 1; + } +#endif + + _findProcessor (argc, argv); + + /* Initalise the port. */ + if (port->init) + port->init (); + + setDefaultOptions (); +#ifdef JAMIN_DS390 + if (ds390_jammed) + { + options.model = MODEL_SMALL; + options.stack10bit = 0; + } +#endif + + parseCmdLine (argc, argv); + + if (options.verbose && NULL != port->processor) + printf ("Processor: %s\n", port->processor); + + initValues (); + + setBinPaths (argv[0]); + setDataPaths (argv[0]); + + if (port->initPaths) + port->initPaths (); + + if (options.printSearchDirs) + doPrintSearchDirs (); + + /* if no input then printUsage & exit */ + if (!options.c1mode && !fullSrcFileName && peekSet (relFilesSet) == NULL) + { + if (options.printSearchDirs) + exit (EXIT_SUCCESS); + printUsage (TRUE); + exit (EXIT_FAILURE); + } + + /* initMem() is expensive, but + initMem() must called before port->finaliseOptions (). + And the z80 port needs port->finaliseOptions(), + even if we're only linking. */ + initMem (); + + /* finalize target specific options */ + port->finaliseOptions (); + + /* finalize common options */ + finalizeOptions (); + + if (fullSrcFileName || options.c1mode) + { + preProcess (envp); + + initSymt (); + initiCode (); + initCSupport (); + initBuiltIns (); + initPeepHole (); + + if (options.verbose) + printf ("sdcc: Generating code...\n"); + + yyparse (); + + if (!options.c1mode) + if (sdcc_pclose (yyin)) + fatalError = 1; + + if (fatalError) + exit (EXIT_FAILURE); + + if (gasOutput) + { + if (port->general.gas_glue) + port->general.gas_glue(); + else + { + werror(W_ILLEGAL_OPT_COMBINATION, __FILE__, __LINE__, "selected target does not support GNU assembler output"); + exit (1); + } + } + else if (port->general.do_glue != NULL) + (*port->general.do_glue) (); + else + { + /* this shouldn't happen */ + assert (FALSE); + /* in case of NDEBUG */ + glue (); + } + + if (fatalError) + exit (EXIT_FAILURE); + + if (!options.c1mode && !noAssemble) + { + if (options.verbose) + printf ("sdcc: Calling assembler...\n"); + assemble (envp); + } + } + closeDumpFiles (); + + if (options.debug && debugFile) + debugFile->closeFile (); + + if (!options.cc_only && !fatalError && !noAssemble && !options.c1mode && (fullSrcFileName || peekSet (relFilesSet) != NULL)) + { + if (options.verbose) + printf ("sdcc: Calling linker...\n"); + + if (port->linker.do_link) + port->linker.do_link (); + else + linkEdit (envp); + } + + return 0; +} + diff --git a/src/SDCCmem.c b/src/SDCCmem.c new file mode 100644 index 0000000..550ee38 --- /dev/null +++ b/src/SDCCmem.c @@ -0,0 +1,1353 @@ +/*------------------------------------------------------------------------- + SDCCmem.c - 8051 memory management routines + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + + +#include "common.h" +#include "dbuf_string.h" +#include "SDCCbtree.h" + +/* memory segments */ +memmap *xstack = NULL; /* xternal stack data */ +memmap *istack = NULL; /* internal stack */ +memmap *code = NULL; /* code segment */ +memmap *data = NULL; /* internal data upto 128 */ +memmap *initialized = NULL; /* initialized data, such as initialized, nonzero globals or local statics. */ +memmap *initializer = NULL; /* a copz of the values for the initialized data from initialized in code space */ +memmap *pdata = NULL; /* paged external data */ +memmap *xdata = NULL; /* external data */ +memmap *xidata = NULL; /* the initialized xdata */ +memmap *xinit = NULL; /* the initializers for xidata */ +memmap *idata = NULL; /* internal data upto 256 */ +memmap *bit = NULL; /* bit addressable space */ +memmap *statsg = NULL; /* the constant data segment */ +memmap *c_abs = NULL; /* constant absolute data */ +memmap *x_abs = NULL; /* absolute xdata/pdata */ +memmap *i_abs = NULL; /* absolute idata upto 256 */ +memmap *d_abs = NULL; /* absolute data upto 128 */ +memmap *sfr = NULL; /* register space */ +memmap *reg = NULL; /* register space */ +memmap *sfrbit = NULL; /* sfr bit space */ +memmap *generic = NULL; /* is a generic pointer */ +memmap *overlay = NULL; /* overlay segment */ +memmap *eeprom = NULL; /* eeprom location */ +memmap *home = NULL; /* Unswitchable code bank */ +namedspacemap *namedspacemaps = 0; /* memory segments for named address spaces */ + +/* this is a set of sets each set containing + symbols in a single overlay */ +set *ovrSetSets = NULL; + +int fatalError = 0; /* fatal error flag */ + +/*-----------------------------------------------------------------*/ +/* allocMap - allocates a memory map */ +/*-----------------------------------------------------------------*/ +memmap * +allocMap (char rspace, /* sfr space */ + char farmap, /* far or near segment */ + char paged, /* can this segment be paged */ + char direct, /* directly addressable */ + char bitaddr, /* bit addressable space */ + char codemap, /* this is code space */ + unsigned sloc, /* starting location */ + const char *name, /* 8 character name */ + char dbName, /* debug name */ + int ptrType /* pointer type for this space */ +) +{ + memmap *map; + + if (!name) + return NULL; + + if (!(map = Safe_alloc (sizeof (memmap)))) + { + werror (E_OUT_OF_MEM, __FILE__, sizeof (memmap)); + exit (1); + } + + memset (map, 0, sizeof (memmap)); + map->regsp = rspace; + map->fmap = farmap; + map->paged = paged; + map->direct = direct; + map->bitsp = bitaddr; + map->codesp = codemap; + map->sloc = sloc; + map->sname = name; + map->dbName = dbName; + map->ptrType = ptrType; + map->syms = NULL; + + dbuf_init(&map->oBuf, 4096); + + return map; +} + +/*-----------------------------------------------------------------*/ +/* initMem - allocates and initializes all the segments */ +/*-----------------------------------------------------------------*/ +void +initMem () +{ + /* allocate all the segments */ + /* xternal stack segment ; + SFRSPACE - NO + FAR-SPACE - YES + PAGED - YES + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'A' + POINTER-TYPE - FPOINTER + */ + xstack = allocMap (0, 1, 1, 0, 0, 0, options.xstack_loc, XSTACK_NAME, 'A', PPOINTER); + + /* internal stack segment ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'B' + POINTER-TYPE - POINTER + */ + istack = allocMap (0, 0, 0, 0, 0, 0, options.stack_loc, ISTACK_NAME, 'B', POINTER); + + /* code segment ; + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - YES + DEBUG-NAME - 'C' + POINTER-TYPE - CPOINTER + */ + code = allocMap (0, 1, 0, 0, 0, 1, options.code_loc, CODE_NAME, 'C', CPOINTER); + + /* home segment ; + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - YES + DEBUG-NAME - 'C' + POINTER-TYPE - CPOINTER + */ + home = allocMap (0, 1, 0, 0, 0, 1, options.code_loc, HOME_NAME, 'C', CPOINTER); + + /* Static segment (code for variables ); + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - YES + DEBUG-NAME - 'D' + POINTER-TYPE - CPOINTER + */ + statsg = allocMap (0, 1, 0, 0, 0, 1, 0, STATIC_NAME, 'D', CPOINTER); + + /* Constant Absolute Data segment (for variables ); + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - YES + DEBUG-NAME - 'D' + POINTER-TYPE - CPOINTER + */ + c_abs = allocMap (0, 1, 0, 0, 0, 1, 0, CABS_NAME, 'D', CPOINTER); + + /* Data segment - internal storage segment ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'E' + POINTER-TYPE - POINTER + */ + data = allocMap (0, 0, 0, 1, 0, 0, options.data_loc, DATA_NAME, 'E', POINTER); + + initialized = allocMap (0, 0, 0, 1, 0, 0, options.data_loc, INITIALIZED_NAME, 'E', POINTER); + initializer = allocMap (0, 0, 0, 1, 0, 1, options.code_loc, INITIALIZER_NAME, 'C', CPOINTER); + + /* Absolute internal storage segment ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'E' + POINTER-TYPE - POINTER + */ + d_abs = allocMap (0, 0, 0, 1, 0, 0, options.data_loc, IABS_NAME, 'E', POINTER); + + /* overlay segment - same as internal storage segment ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'E' + POINTER-TYPE - POINTER + */ + if (OVERLAY_NAME) + overlay = allocMap (0, 0, 0, 1, 0, 0, options.data_loc, DATA_NAME, 'E', POINTER); + + /* Xternal paged segment ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - YES + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'P' + POINTER-TYPE - PPOINTER + */ + pdata = allocMap (0, 0, 1, 0, 0, 0, options.xstack_loc, PDATA_NAME, 'P', PPOINTER); + + /* Xternal Data segment - + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'F' + POINTER-TYPE - FPOINTER + */ + xdata = allocMap (0, 1, 0, 0, 0, 0, options.xdata_loc, XDATA_NAME, 'F', FPOINTER); + xidata = allocMap (0, 1, 0, 0, 0, 0, 0, XIDATA_NAME, 'F', FPOINTER); + xinit = allocMap (0, 1, 0, 0, 0, 1, 0, XINIT_NAME, 'C', CPOINTER); + + /* Absolute external storage segment ; + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'F' + POINTER-TYPE - FPOINTER + */ + x_abs = allocMap (0, 1, 0, 0, 0, 0, options.xdata_loc, XABS_NAME, 'F', FPOINTER); + + /* Indirectly addressed internal data segment + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'G' + POINTER-TYPE - IPOINTER + */ + idata = allocMap (0, 0, 0, 0, 0, 0, options.idata_loc, IDATA_NAME, 'G', IPOINTER); + + /* Indirectly addressed absolute internal segment + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'E' + POINTER-TYPE - IPOINTER + */ + i_abs = allocMap (0, 0, 0, 0, 0, 0, options.data_loc, IABS_NAME, 'E', IPOINTER); + + /* Bit space ; + SFRSPACE - NO + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - YES + CODE-ACCESS - NO + DEBUG-NAME - 'H' + POINTER-TYPE - _NONE_ + */ + bit = allocMap (0, 0, 0, 1, 1, 0, 0, BIT_NAME, 'H', 0); + + /* Special function register space :- + SFRSPACE - YES + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'I' + POINTER-TYPE - _NONE_ + */ + sfr = allocMap (1, 0, 0, 1, 0, 0, 0, REG_NAME, 'I', 0); + + /* Register space ; + SFRSPACE - YES + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - ' ' + POINTER-TYPE - _NONE_ + */ + reg = allocMap (1, 0, 0, 0, 0, 0, 0, REG_NAME, ' ', 0); + + /* SFR bit space + SFRSPACE - YES + FAR-SPACE - NO + PAGED - NO + DIRECT-ACCESS - YES + BIT-ACCESS - YES + CODE-ACCESS - NO + DEBUG-NAME - 'J' + POINTER-TYPE - _NONE_ + */ + sfrbit = allocMap (1, 0, 0, 1, 1, 0, 0, REG_NAME, 'J', 0); + + /* EEPROM space + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACCESS - NO + DEBUG-NAME - 'K' + POINTER-TYPE - EEPPOINTER + */ + eeprom = allocMap (0, 1, 0, 0, 0, 0, 0, REG_NAME, 'K', EEPPOINTER); + + /* the unknown map */ + generic = allocMap (0, 0, 0, 0, 0, 0, 0, DATA_NAME, ' ', GPOINTER); + +} + +/*-----------------------------------------------------------------*/ +/* allocIntoSeg - puts a symbol into a memory segment */ +/*-----------------------------------------------------------------*/ +void +allocIntoSeg (symbol *sym) +{ + memmap *segment; + + if (SPEC_ADDRSPACE (sym->etype)) + { + namedspacemap *nm; + for (nm = namedspacemaps; nm; nm = nm->next) + if (!strcmp (nm->name, SPEC_ADDRSPACE (sym->etype)->name)) + break; + + if (!nm) + { + nm = Safe_alloc (sizeof (namedspacemap)); + nm->name = Safe_alloc (strlen(SPEC_ADDRSPACE (sym->etype)->name) + 1); + strcpy (nm->name, SPEC_ADDRSPACE (sym->etype)->name); + nm->is_const = (SPEC_ADDRSPACE (sym->etype)->type && SPEC_CONST (SPEC_ADDRSPACE (sym->etype)->type)); + nm->map = nm->is_const ? + allocMap (0, 1, 0, 0, 0, 1, options.code_loc, SPEC_ADDRSPACE (sym->etype)->name, 'C', CPOINTER) : + allocMap (0, 0, 0, 1, 0, 0, options.data_loc, SPEC_ADDRSPACE (sym->etype)->name, 'E', POINTER); + nm->next = namedspacemaps; + namedspacemaps = nm; + } + + addSet (&nm->map->syms, sym); + + return; + } + if (!(segment = SPEC_OCLS (sym->etype))) + { + fprintf (stderr, "Symbol %s:\n", sym->name); + wassertl (0, "Failed to allocate symbol to memory segment due to missing output storage class"); + return; + } + addSet (&segment->syms, sym); + if (segment == pdata) + sym->iaccess = 1; +} + +/*-----------------------------------------------------------------*/ +/* deleteFromSeg - deletes a symbol from segment used when a var */ +/* first declared as "extern" then no extern */ +/*-----------------------------------------------------------------*/ +void deleteFromSeg(symbol *sym) +{ + if (SPEC_OCLS(sym->etype)) + { + memmap *segment = SPEC_OCLS (sym->etype); + deleteSetItem(&segment->syms, sym); + } +} + +/*-----------------------------------------------------------------*/ +/* defaultOClass - set the output segment based on SCLASS */ +/*-----------------------------------------------------------------*/ +bool +defaultOClass (symbol *sym) +{ + switch (SPEC_SCLS (sym->etype)) + { + case S_SFR: + SPEC_OCLS (sym->etype) = sfr; + break; + case S_SBIT: + SPEC_OCLS (sym->etype) = sfrbit; + break; + case S_CODE: + if (sym->_isparm) + return FALSE; + /* if code change to constant */ + if (sym->ival && SPEC_ABSA (sym->etype)) + { + SPEC_OCLS(sym->etype) = c_abs; + } + else + { + SPEC_OCLS (sym->etype) = statsg; + } + break; + case S_XDATA: + /* absolute initialized global */ + if (sym->ival && SPEC_ABSA (sym->etype)) + { + SPEC_OCLS(sym->etype) = x_abs; + } + /* or should we move this to the initialized data segment? */ + else if (port->genXINIT && sym->ival && (sym->level==0)) + { + SPEC_OCLS(sym->etype) = xidata; + } + else + { + SPEC_OCLS (sym->etype) = xdata; + } + break; + case S_DATA: + /* Absolute initialized global */ + if (sym->ival && SPEC_ABSA (sym->etype)) + { + SPEC_OCLS (sym->etype) = d_abs; + } + /* Other initialized global */ + else if (sym->ival && port->mem.initialized_name && sym->level == 0) + { + SPEC_OCLS (sym->etype) = initialized; + } + else + { + SPEC_OCLS (sym->etype) = data; + } + break; + case S_IDATA: + /* absolute initialized global */ + if (sym->ival && SPEC_ABSA (sym->etype)) + { + SPEC_OCLS(sym->etype) = i_abs; + } + else + { + SPEC_OCLS (sym->etype) = idata; + } + sym->iaccess = 1; + break; + case S_PDATA: + SPEC_OCLS (sym->etype) = pdata; + sym->iaccess = 1; + break; + case S_BIT: + SPEC_OCLS (sym->etype) = bit; + break; + case S_EEPROM: + SPEC_OCLS (sym->etype) = eeprom; + break; + default: + return FALSE; + } + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* allocDefault - assigns the output segment based on SCLASS */ +/*-----------------------------------------------------------------*/ +bool +allocDefault (struct symbol * sym) +{ + if (defaultOClass (sym)) + { + allocIntoSeg (sym); + return TRUE; + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* allocGlobal - assigns the output segment to a global var */ +/*-----------------------------------------------------------------*/ +void +allocGlobal (symbol * sym) +{ + /* symbol name is internal name */ + if (!sym->level) /* local statics can come here */ + SNPRINTF (sym->rname, sizeof(sym->rname), + "%s%s", port->fun_prefix, sym->name); + + /* add it to the operandKey reset */ + if (!isinSet (operKeyReset, sym)) + { + addSet(&operKeyReset, sym); + } + + /* if this is a literal e.g. enumerated type */ + /* put it in the data segment & do nothing */ + if (IS_LITERAL (sym->etype)) + { + SPEC_OCLS (sym->etype) = data; + return; + } + + /* if this is a function then assign code space */ + if (IS_FUNC (sym->type)) + { + SPEC_OCLS (sym->etype) = code; + /* if this is an interrupt service routine + then put it in the interrupt service array */ + if (FUNC_ISISR (sym->type) && !options.noiv && + (FUNC_INTNO (sym->type) != INTNO_UNSPEC)) + { + if (interrupts[FUNC_INTNO (sym->type)]) + werror (E_INT_DEFINED, + FUNC_INTNO (sym->type), + interrupts[FUNC_INTNO (sym->type)]->name); + else + interrupts[FUNC_INTNO (sym->type)] = sym; + + /* automagically extend the maximum interrupts */ + if (FUNC_INTNO (sym->type) >= maxInterrupts && FUNC_INTNO (sym->type)!=INTNO_TRAP) + maxInterrupts = FUNC_INTNO (sym->type) + 1; + } + /* if it is not compiler defined */ + if (!sym->cdef) + allocIntoSeg (sym); + + return; + } + + /* if this is a bit variable and no storage class */ + if (bit && IS_SPEC(sym->type) && SPEC_NOUN (sym->type) == V_BIT) + { + SPEC_OCLS (sym->type) = bit; + allocIntoSeg (sym); + return; + } + + if (!TARGET_IS_PIC16 || sym->level) + /* register storage class ignored changed to FIXED */ + if (SPEC_SCLS (sym->etype) == S_REGISTER) + SPEC_SCLS (sym->etype) = S_FIXED; + + /* if it is fixed, then allocate depending on the */ + /* current memory model, same for automatics */ + if (SPEC_SCLS (sym->etype) == S_FIXED || + (TARGET_IS_PIC16 && (SPEC_SCLS (sym->etype) == S_REGISTER) && (sym->level == 0)) || + SPEC_SCLS (sym->etype) == S_AUTO) + { + if (port->mem.default_globl_map != xdata) + { + if (sym->ival && SPEC_ABSA (sym->etype)) + { + /* absolute initialized global */ + SPEC_OCLS (sym->etype) = x_abs; + } + else if (sym->ival && sym->level == 0 && port->mem.initialized_name) + { + SPEC_OCLS (sym->etype) = initialized; + } + else + { + /* set the output class */ + SPEC_OCLS (sym->etype) = port->mem.default_globl_map; + } + /* generate the symbol */ + allocIntoSeg (sym); + return; + } + else + { + SPEC_SCLS (sym->etype) = S_XDATA; + } + } + + allocDefault (sym); + return; +} + +/*-----------------------------------------------------------------*/ +/* allocParms - parameters are always passed on stack */ +/*-----------------------------------------------------------------*/ +void +allocParms (value *val, bool smallc) +{ + value *lval; + int pNum = 1; + int stackParamSizeAdjust = 0; + + if (smallc) + { + for (lval = val; lval; lval = lval->next) + { + if (IS_REGPARM (lval->etype)) + continue; + stackParamSizeAdjust += getSize (lval->type) + (getSize (lval->type) == 1); + } + } + stackPtr += stackParamSizeAdjust; + + for (lval = val; lval; lval = lval->next, pNum++) + { + /* check the declaration */ + checkDecl (lval->sym, 0); + + /* if this a register parm then allocate + it as a local variable by adding it + to the first block we see in the body */ + if (IS_REGPARM (lval->etype)) + continue; + + /* mark it as my parameter */ + lval->sym->ismyparm = 1; + lval->sym->localof = currFunc; + + /* if automatic variables r 2b stacked */ + if (options.stackAuto || IFFUNC_ISREENT (currFunc->type)) + { + int paramsize = getSize (lval->type) + (getSize (lval->type) == 1 && (smallc || TARGET_PDK_LIKE)); + + if (lval->sym) + lval->sym->onStack = 1; + + /* choose which stack 2 use */ + if (options.useXstack) /* use external stack */ + { + /* PENDING: stack direction support */ + wassertl (!smallc, "SmallC calling convention not yet supported for xstack callee"); + SPEC_OCLS (lval->etype) = SPEC_OCLS (lval->sym->etype) = xstack; + SPEC_STAK (lval->etype) = SPEC_STAK (lval->sym->etype) = lval->sym->stack = + xstackPtr - paramsize; + xstackPtr -= paramsize; + } + else /* use internal stack */ + { + + SPEC_OCLS (lval->etype) = SPEC_OCLS (lval->sym->etype) = istack; + if ((port->stack.direction > 0) != smallc) + { + SPEC_STAK (lval->etype) = SPEC_STAK (lval->sym->etype) = lval->sym->stack = + stackPtr - (FUNC_REGBANK (currFunc->type) ? port->stack.bank_overhead : 0) - + paramsize - + (FUNC_ISISR (currFunc->type) ? port->stack.isr_overhead : 0); + stackPtr -= paramsize; + } + else + { + /* This looks like the wrong order but it turns out OK... */ + /* PENDING: isr, bank overhead, ... */ + SPEC_STAK (lval->etype) = SPEC_STAK (lval->sym->etype) = lval->sym->stack = + stackPtr + + (FUNC_ISISR (currFunc->type) ? port->stack.isr_overhead : 0) + + 0; + stackPtr += paramsize; + } + } + allocIntoSeg (lval->sym); + } + else + { + /* Do not allocate for inline functions to avoid multiple definitions - see bug report #2591. */ + if(IFFUNC_ISINLINE (currFunc->type) && !IS_STATIC (currFunc->etype) && !IS_EXTERN (currFunc->etype)) + continue; + + /* allocate them in the automatic space */ + /* generate a unique name */ + SNPRINTF (lval->sym->rname, sizeof(lval->sym->rname), + "%s%s_PARM_%d", port->fun_prefix, currFunc->name, pNum); + strncpyz (lval->name, lval->sym->rname, sizeof(lval->name)); + + /* if declared in specific storage */ + if (allocDefault (lval->sym)) + { + SPEC_OCLS (lval->etype) = SPEC_OCLS (lval->sym->etype); + continue; + } + + /* otherwise depending on the memory model */ + SPEC_OCLS (lval->etype) = SPEC_OCLS (lval->sym->etype) = + port->mem.default_local_map; + if (options.model == MODEL_SMALL) + { + /* note here that we put it into the overlay segment + first, we will remove it from the overlay segment + after the overlay determination has been done */ + if (!options.noOverlay) + { + SPEC_OCLS (lval->etype) = SPEC_OCLS (lval->sym->etype) = + overlay; + } + } + else if (options.model == MODEL_MEDIUM) + { + SPEC_SCLS (lval->etype) = S_PDATA; + } + else + { + SPEC_SCLS (lval->etype) = S_XDATA; + } + allocIntoSeg (lval->sym); + } + } + + stackPtr -= stackParamSizeAdjust; + + return; +} + +/*-----------------------------------------------------------------*/ +/* deallocParms - parameters are always passed on stack */ +/*-----------------------------------------------------------------*/ +void +deallocParms (value * val) +{ + value *lval; + + for (lval = val; lval; lval = lval->next) + { + /* unmark is myparm */ + lval->sym->ismyparm = 0; + + /* delete it from the symbol table */ + deleteSym (SymbolTab, lval->sym, lval->sym->name); + + if (!lval->sym->isref) + { + lval->sym->allocreq = 0; + werror (W_NO_REFERENCE, + currFunc ? currFunc->name : "(unknown)", + "function argument", lval->sym->name); + } + + /* move the rname if any to the name for both val & sym */ + /* and leave a copy of it in the symbol table */ + if (lval->sym->rname[0]) + { + char buffer[SDCC_NAME_MAX]; + symbol * argsym = lval->sym; + + strncpyz (buffer, lval->sym->rname, sizeof(buffer)); + lval->sym = copySymbol (lval->sym); + strncpyz (lval->sym->rname, buffer, sizeof(lval->sym->rname)); + + strncpyz (lval->sym->name, buffer, sizeof(lval->sym->name)); + /* need to keep the original name for inlining to work */ + /*strncpyz (lval->name, buffer, sizeof(lval->name)); */ + + addSym (SymbolTab, lval->sym, lval->sym->name, + lval->sym->level, lval->sym->block, 1); + lval->sym->_isparm = 1; + if (!isinSet (operKeyReset, lval->sym)) + { + addSet(&operKeyReset, lval->sym); + } + + /* restore the original symbol */ + lval->sym = argsym; + } + } + return; +} + +/*-----------------------------------------------------------------*/ +/* allocLocal - allocate local variables */ +/*-----------------------------------------------------------------*/ +void +allocLocal (symbol * sym) +{ + /* generate an unique name */ + SNPRINTF (sym->rname, sizeof(sym->rname), + "%s%s_%s_%d_%d", + port->fun_prefix, + currFunc->name, sym->name, sym->level, sym->block); + + sym->islocal = 1; + sym->localof = currFunc; + + /* if this is a static variable */ + if (IS_STATIC (sym->etype)) + { + allocGlobal (sym); + sym->allocreq = 1; + return; + } + + /* if volatile then */ + if (IS_VOLATILE (sym->etype)) + sym->allocreq = 1; + + /* this is automatic */ + + /* if it's to be placed on the stack */ + if (options.stackAuto || reentrant) + { + sym->onStack = 1; + if (options.useXstack) + { + /* PENDING: stack direction for xstack */ + SPEC_OCLS (sym->etype) = xstack; + SPEC_STAK (sym->etype) = sym->stack = (xstackPtr + 1); + xstackPtr += getSize (sym->type); + } + else + { + SPEC_OCLS (sym->etype) = istack; + if (port->stack.direction > 0) + { + SPEC_STAK (sym->etype) = sym->stack = (stackPtr + 1); + stackPtr += getSize (sym->type); + } + else + { + stackPtr -= getSize (sym->type); + SPEC_STAK (sym->etype) = sym->stack = stackPtr; + } + } + allocIntoSeg (sym); + return; + } + + /* else depending on the storage class specified */ + + /* if this is a function then assign code space */ + if (IS_FUNC (sym->type)) + { + SPEC_OCLS (sym->etype) = code; + return; + } + + /* if this is a bit variable and no storage class */ + if (bit && IS_SPEC(sym->type) && SPEC_NOUN (sym->type) == V_BIT) + { + SPEC_SCLS (sym->type) = S_BIT; + SPEC_OCLS (sym->type) = bit; + allocIntoSeg (sym); + return; + } + + if ((SPEC_SCLS (sym->etype) == S_DATA) || (SPEC_SCLS (sym->etype) == S_REGISTER)) + { + SPEC_OCLS (sym->etype) = (options.noOverlay ? data : overlay); + allocIntoSeg (sym); + return; + } + + if (allocDefault (sym)) + { + return; + } + + /* again note that we have put it into the overlay segment + will remove and put into the 'data' segment if required after + overlay analysis has been done */ + if (options.model == MODEL_SMALL) + { + SPEC_OCLS (sym->etype) = + (options.noOverlay ? port->mem.default_local_map : overlay); + } + else + { + SPEC_OCLS (sym->etype) = port->mem.default_local_map; + } + allocIntoSeg (sym); +} + +/*-----------------------------------------------------------------*/ +/* deallocLocal - deallocates the local variables */ +/*-----------------------------------------------------------------*/ +void +deallocLocal (symbol * csym) +{ + symbol *sym; + + for (sym = csym; sym; sym = sym->next) + { + if (sym->_isparm) + continue; + + /* if it is on the stack */ + if (sym->onStack) + { + if (options.useXstack) + xstackPtr -= getSize (sym->type); + else + stackPtr -= getSize (sym->type); + } + /* if not used give a warning */ + if (!sym->isref && !IS_STATIC (sym->etype)) + werror (W_NO_REFERENCE, + currFunc ? currFunc->name : "(unknown)", + "local variable", sym->name); + /* now delete it from the symbol table */ + deleteSym (SymbolTab, sym, sym->name); + } +} + +/*-----------------------------------------------------------------*/ +/* overlay2data - moves declarations from the overlay seg to data */ +/*-----------------------------------------------------------------*/ +void +overlay2data () +{ + symbol *sym; + + for (sym = setFirstItem (overlay->syms); sym; + sym = setNextItem (overlay->syms)) + { + + SPEC_OCLS (sym->etype) = data; + allocIntoSeg (sym); + } + + setToNull ((void *) &overlay->syms); +} + +/*-----------------------------------------------------------------*/ +/* overlay2Set - will add all symbols from the overlay segment to */ +/* the set of sets containing the overlable symbols */ +/*-----------------------------------------------------------------*/ +void +overlay2Set () +{ + symbol *sym; + set *oset = NULL; + + for (sym = setFirstItem (overlay->syms); sym; + sym = setNextItem (overlay->syms)) + { + + addSet (&oset, sym); + } + + setToNull ((void *) &overlay->syms); + addSet (&ovrSetSets, oset); +} + +/*-----------------------------------------------------------------*/ +/* allocVariables - creates decl & assign storage class for a v */ +/*-----------------------------------------------------------------*/ +int +allocVariables (symbol * symChain) +{ + symbol *sym; + symbol *csym; + int stack = 0; + long saveLevel = 0; + + /* go thru the symbol chain */ + for (sym = symChain; sym; sym = sym->next) + { + /* if this is a typedef then add it */ + /* to the typedef table */ + if (IS_TYPEDEF (sym->etype)) + { + /* check if the typedef already exists */ + csym = findSym (TypedefTab, NULL, sym->name); + if (csym && csym->level == sym->level && + !(options.std_c11 && compareTypeExact (sym->type, csym->type, -1))) /* typedef to same type not allowed before ISO C11 */ + werror (E_DUPLICATE_TYPEDEF, sym->name); + + SPEC_EXTR (sym->etype) = 0; + addSym (TypedefTab, sym, sym->name, sym->level, sym->block, 0); + continue; /* go to the next one */ + } + /* make sure it already exists */ + csym = findSymWithLevel (SymbolTab, sym); + if (!csym || (csym && csym->level != sym->level)) + csym = sym; + + /* check the declaration */ + checkDecl (csym, 0); + + /* if this is a function or a pointer to a */ + /* function then do args processing */ + if (funcInChain (csym->type)) + { + processFuncArgs (csym); + } + + /* if this is an extern variable then change */ + /* the level to zero temporarily */ + if (IS_EXTERN (csym->etype) || IS_FUNC (csym->type)) + { + saveLevel = csym->level; + csym->level = 0; + } + + /* if this is a literal then it is an enumerated */ + /* type so need not allocate it space for it */ + if (IS_LITERAL (sym->etype)) + continue; + + /* generate the actual declaration */ + if (csym->level) + { + allocLocal (csym); + if (csym->onStack) + stack += getSize (csym->type); + } + else + allocGlobal (csym); + + /* restore the level */ + if (IS_EXTERN (csym->etype) || IS_FUNC (csym->type)) + csym->level = saveLevel; + } + + return stack; +} + +void +clearStackOffsets (void) +{ + const symbol *sym; + + for (sym = setFirstItem (istack->syms); sym; + sym = setNextItem (istack->syms)) + { + const int size = getSize (sym->type); + + /* nothing to do with parameters so continue */ + if ((sym->_isparm && !IS_REGPARM (sym->etype))) + continue; + + currFunc->stack -= size; + SPEC_STAK (currFunc->etype) -= size; + } + + if (currFunc) + { + //wassert(!(currFunc->stack)); // Sometimes some local variable was included in istack->sams. + currFunc->stack = 0; + } +} + +#define BTREE_STACK 1 + +/*-----------------------------------------------------------------*/ +/* redoStackOffsets :- will reassign the values for stack offsets */ +/*-----------------------------------------------------------------*/ +void +redoStackOffsets (void) +{ + symbol *sym; + int sPtr = 0; + int xsPtr = -1; + + /* after register allocation is complete we know + which variables will need to be assigned space + on the stack. We will eliminate those variables + which do not have the allocReq flag thus reducing + the stack space */ + for (sym = setFirstItem (istack->syms); sym; sym = setNextItem (istack->syms)) + { + int size = getSize (sym->type); + /* nothing to do with parameters so continue */ + if ((sym->_isparm && !IS_REGPARM (sym->etype))) + continue; + + if (BTREE_STACK) + { + /* Remove them all, and let btree_alloc() below put them back in more efficiently. */ + currFunc->stack -= size; + SPEC_STAK (currFunc->etype) -= size; + + if(IS_AGGREGATE (sym->type) || sym->allocreq) + btree_add_symbol (sym); + } + /* Do it the old way - compared to the btree approach we waste space when allocating + variables that had their address taken, unions and aggregates. */ + else + { + /* if allocation not required then subtract + size from overall stack size & continue */ + if (!IS_AGGREGATE (sym->type) && !sym->allocreq) + { + currFunc->stack -= size; + SPEC_STAK (currFunc->etype) -= size; + continue; + } + + if (port->stack.direction > 0) + { + SPEC_STAK (sym->etype) = sym->stack = (sPtr + 1); + sPtr += size; + } + else + { + sPtr -= size; + SPEC_STAK (sym->etype) = sym->stack = sPtr; + } + } + } + + if (BTREE_STACK && elementsInSet (istack->syms)) + { + btree_alloc (); + btree_clear (); + } + + /* do the same for the external stack */ + + if (!xstack) + return; + + for (sym = setFirstItem (xstack->syms); sym; sym = setNextItem (xstack->syms)) + { + int size = getSize (sym->type); + /* nothing to do with parameters so continue */ + if ((sym->_isparm && !IS_REGPARM (sym->etype))) + continue; + + if (IS_AGGREGATE (sym->type)) + { + SPEC_STAK (sym->etype) = sym->stack = (xsPtr + 1); + xsPtr += size; + continue; + } + + /* if allocation not required then subtract + size from overall stack size & continue */ + if (!sym->allocreq) + { + currFunc->xstack -= size; + SPEC_STAK (currFunc->etype) -= size; + continue; + } + + SPEC_STAK (sym->etype) = sym->stack = (xsPtr + 1); + xsPtr += size; + } +} + +#define SP_BP(sp, bp) (options.omitFramePtr ? sp : bp) +#define SYM_BP(sym) (SPEC_OCLS (sym->etype)->paged ? SP_BP("_spx", "_bpx") : SP_BP("sp", "_bp")) + +/*-----------------------------------------------------------------*/ +/* printAllocInfoSeg- print the allocation for a given section */ +/*-----------------------------------------------------------------*/ +static int +printAllocInfoSeg (memmap * map, symbol * func, struct dbuf_s *oBuf) +{ + symbol *sym; + int flg = FALSE; + + if (!map || !map->syms) + return 0; + + for (sym = setFirstItem (map->syms); sym; + sym = setNextItem (map->syms)) + { + if (sym->level == 0) + continue; + if (sym->localof != func) + continue; + + dbuf_printf (oBuf, ";%-25s Allocated ", sym->name); + flg = TRUE; + + /* if assigned to registers */ + if (!sym->allocreq && sym->reqv) + { + int i; + + sym = OP_SYMBOL (sym->reqv); + if (!sym->isspilt || sym->remat) + { + dbuf_append_str (oBuf, "to registers "); + for (i = 0; i < 4 && sym->regs[i]; i++) + dbuf_printf (oBuf, "%s ", port->getRegName (sym->regs[i])); + dbuf_append_char (oBuf, '\n'); + continue; + } + else + { + sym = sym->usl.spillLoc; + } + } + + /* if on stack */ + if (sym->onStack) + { + int stack_offset = 0; + + if (options.omitFramePtr) + { + if (SPEC_OCLS (sym->etype)->paged) + stack_offset = func->xstack; + else + stack_offset = func->stack; + } + + stack_offset += port->stack.offset; /* in case sp/bp points to the next location instead of last */ + + if (port->stack.direction < 0) + stack_offset = -stack_offset; + + dbuf_printf (oBuf, "to stack - %s %+d\n", SYM_BP (sym), sym->stack - stack_offset); + continue; + } + + /* otherwise give rname */ + dbuf_printf (oBuf, "with name '%s'\n", sym->rname); + } + + return flg; +} + +/*-----------------------------------------------------------------*/ +/* canOverlayLocals - returns true if the local variables can overlayed */ +/*-----------------------------------------------------------------*/ +static bool +canOverlayLocals (eBBlock ** ebbs, int count) +{ + int i; + /* if staticAuto is in effect or the current function + being compiled is reentrant or the overlay segment + is empty or no overlay option is in effect then */ + if (options.noOverlay || + options.stackAuto || + (currFunc && + (IFFUNC_ISREENT (currFunc->type) || + FUNC_ISISR (currFunc->type))) || + elementsInSet (overlay->syms) == 0) + { + return FALSE; + } + /* if this is a forces overlay */ + if (IFFUNC_ISOVERLAY(currFunc->type)) return TRUE; + + /* otherwise do thru the blocks and see if there + any function calls if found then return false */ + for (i = 0; i < count; i++) + { + iCode *ic; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (ic) + { + if (ic->op == CALL) + { + sym_link *ftype = operandType(IC_LEFT(ic)); + /* builtins only can use overlays */ + if (!IFFUNC_ISBUILTIN(ftype)) return FALSE; + } + else if (ic->op == PCALL) + { + return FALSE; + } + } + } + + /* no function calls found return TRUE */ + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* doOverlays - move the overlay segment to appropriate location */ +/*-----------------------------------------------------------------*/ +void +doOverlays (eBBlock ** ebbs, int count) +{ + if (!overlay) + return; + + /* check if the parameters and local variables + of this function can be put in the overlay segment + This check is essentially to see if the function + calls any other functions if yes then we cannot + overlay */ + if (canOverlayLocals (ebbs, count)) + /* if we can then put the parameters & + local variables in the overlay set */ + overlay2Set (); + else + /* otherwise put them into data where + they belong */ + overlay2data (); +} + +/*-----------------------------------------------------------------*/ +/* printAllocInfo - prints allocation information for a function */ +/*-----------------------------------------------------------------*/ +void +printAllocInfo (symbol * func, struct dbuf_s * oBuf) +{ +#define BREAKLINE ";------------------------------------------------------------\n" + int cnt = 0; + set *ovrset; + set *tempOverlaySyms; + + if (!func) + return; + + /* must be called after register allocation is complete */ + dbuf_append_str (oBuf, BREAKLINE); + dbuf_printf (oBuf, ";Allocation info for local variables in function '%s'\n", func->name); + dbuf_append_str (oBuf, BREAKLINE); + + cnt += printAllocInfoSeg (xstack, func, oBuf); + cnt += printAllocInfoSeg (istack, func, oBuf); + cnt += printAllocInfoSeg (code, func, oBuf); + cnt += printAllocInfoSeg (data, func, oBuf); + cnt += printAllocInfoSeg (xdata, func, oBuf); + cnt += printAllocInfoSeg (idata, func, oBuf); + cnt += printAllocInfoSeg (sfr, func, oBuf); + cnt += printAllocInfoSeg (sfrbit, func, oBuf); + + tempOverlaySyms = overlay->syms; + + /* search the set of overlay sets for local variables/parameters */ + for (ovrset = setFirstItem (ovrSetSets); ovrset; + ovrset = setNextItem (ovrSetSets)) + { + overlay->syms = ovrset; + cnt += printAllocInfoSeg (overlay, func, oBuf); + } + overlay->syms = tempOverlaySyms; + + if (cnt) + dbuf_append_str (oBuf, BREAKLINE); +} diff --git a/src/SDCCmem.h b/src/SDCCmem.h new file mode 100644 index 0000000..ddfea0b --- /dev/null +++ b/src/SDCCmem.h @@ -0,0 +1,131 @@ +/*-----------------------------------------------------------------*/ +/* SDCCmem.h - header file for memory management */ +/*-----------------------------------------------------------------*/ + +#ifndef SDCCMEM_H +#define SDCCMEM_H + +#include +#include +#include "dbuf.h" +#include "SDCCsymt.h" + +struct set; +struct value; +struct eBBlock; + +typedef struct memmap + { + unsigned char pageno; /* page no for this variable */ + const char *sname; /* character prefix for map */ + char dbName; /* debugger address space name */ + int ptrType; /* pointer Type for this space */ + int slbl; /* label counter for space */ + unsigned sloc; /* starting location */ + unsigned fmap:1; /* 1 = 16bit addressing reqd */ + unsigned paged:1; /* this is a paged mem space */ + unsigned direct:1; /* 1 = indirect access only */ + unsigned bitsp:1; /* 1 = bit addressable space */ + unsigned codesp:1; /* 1 = code space */ + unsigned regsp:1; /* 1 = sfr space */ + struct dbuf_s oBuf; /* object buffer associated */ + struct set *syms; /* symbols defined in this segment */ + } +memmap; + +/* For performance we might want to use a hash map instead of the linked list */ +typedef struct namedspacemap + { + char *name; + bool is_const; + memmap *map; + struct namedspacemap *next; + } +namedspacemap; + +extern FILE *junkFile; + +/* memory map prefixes MOF added the DATA,CODE,XDATA,BIT */ +#define XSTACK_NAME port->mem.xstack_name +#define ISTACK_NAME port->mem.istack_name +#define CODE_NAME port->mem.code_name +#define DATA_NAME port->mem.data_name +#define INITIALIZED_NAME port->mem.initialized_name +#define INITIALIZER_NAME port->mem.initializer_name +#define IDATA_NAME port->mem.idata_name +#define PDATA_NAME port->mem.pdata_name +#define XDATA_NAME port->mem.xdata_name +#define XIDATA_NAME port->mem.xidata_name +#define XINIT_NAME port->mem.xinit_name +#define BIT_NAME port->mem.bit_name +#define REG_NAME port->mem.reg_name +#define STATIC_NAME port->mem.static_name +#define HOME_NAME port->mem.home_name +#define OVERLAY_NAME port->mem.overlay_name +#define CONST_NAME port->mem.const_name +#define CABS_NAME port->mem.cabs_name +#define XABS_NAME port->mem.xabs_name +#define IABS_NAME port->mem.iabs_name + +/* forward definition for variables */ +extern memmap *xstack; /* xternal stack data */ +extern memmap *istack; /* internal stack */ +extern memmap *code; /* code segment */ +extern memmap *data; /* internal data upto 128 */ +extern memmap *initialized; /* initialized data, such as initalized, nonzero globals or local statics. */ +extern memmap *initializer; /* a copy of the values for the initalized data from initialized in code space */ +extern memmap *pdata; /* paged external data upto 256 */ +extern memmap *xdata; /* external data */ +extern memmap *xidata; /* the initialized xdata */ +extern memmap *xinit; /* the initializers for xidata */ +extern memmap *idata; /* internal data upto 256 */ +extern memmap *bit; /* bit addressable space */ +extern memmap *statsg; /* static code segment */ +extern memmap *c_abs; /* constant absolute data */ +extern memmap *x_abs; /* absolute xdata/pdata */ +extern memmap *i_abs; /* absolute idata upto 256 */ +extern memmap *d_abs; /* absolute data upto 128 */ +extern memmap *sfr; /* register space */ +extern memmap *sfrbit; /* sfr bit space */ +extern memmap *reg; /* register space */ +extern memmap *generic; /* unknown */ +extern memmap *overlay; /* the overlay segment */ +extern memmap *eeprom; /* eeprom space */ +extern memmap *home; /* Non-banked home space */ + +extern namedspacemap *namedspacemaps; + +extern int fatalError; + +extern struct set *ovrSetSets; + +/* easy access macros */ +#define IN_BITSPACE(map) (map && map->bitsp) +#define IN_STACK(x) (IS_SPEC(x) && (SPEC_OCLS(x) == xstack || SPEC_OCLS(x) == istack )) +#define IN_FARSPACE(map) (map && map->fmap) +#define IN_DIRSPACE(map) (map && map->direct) +#define IN_PAGEDSPACE(map) (map && map->paged ) +#define IN_CODESPACE(map) (map && map->codesp) +#define IN_REGSP(map) (map && map->regsp) +#define PTR_TYPE(map) (map ? (map->ptrType ? map->ptrType : POINTER)\ + : port->unqualified_pointer) + +/* forward decls for functions */ +memmap *allocMap (char, char, char, char, char, char, unsigned, const char *, char, int); +void initMem (); +bool defaultOClass (struct symbol *); +void allocGlobal (struct symbol *); +void allocLocal (struct symbol *); +void allocParms (struct value *, bool smallc); +void deallocParms (struct value *); +void deallocLocal (struct symbol *); +int allocVariables (struct symbol *); +void overlay2Set (); +void overlay2data (); +void clearStackOffsets (void); +void redoStackOffsets (void); +void printAllocInfo (struct symbol *, struct dbuf_s *); +void doOverlays (struct eBBlock **, int count); +void deleteFromSeg(struct symbol *); +#endif + diff --git a/src/SDCCnaddr.cc b/src/SDCCnaddr.cc new file mode 100644 index 0000000..d867f6c --- /dev/null +++ b/src/SDCCnaddr.cc @@ -0,0 +1,80 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011 +// +// (c) 2011 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// +// Optimal placement of bank switching instructions for named address spaces. + +#include "SDCCnaddr.hpp" + +// A quick-and-dirty function to get the CFG from sdcc (a simplified version of the function from SDCCralloc.hpp). +void +create_cfg_naddr(cfg_t &cfg, iCode *start_ic, ebbIndex *ebbi) +{ + iCode *ic; + + std::map key_to_index; + { + int i; + + for (ic = start_ic, i = 0; ic; ic = ic->next, i++) + { + boost::add_vertex(cfg); + key_to_index[ic->key] = i; + cfg[i].ic = ic; + } + } + + // Get control flow graph from sdcc. + for (ic = start_ic; ic; ic = ic->next) + { + if (ic->op != GOTO && ic->op != RETURN && ic->op != JUMPTABLE && ic->next) + boost::add_edge(key_to_index[ic->key], key_to_index[ic->next->key], 3.0f, cfg); + + if (ic->op == GOTO) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, ic->label)->sch->key], 6.0f, cfg); + else if (ic->op == RETURN) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, returnLabel)->sch->key], 6.0f, cfg); + else if (ic->op == IFX) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->sch->key], 6.0f, cfg); + else if (ic->op == JUMPTABLE) + for (symbol *lbl = (symbol *)(setFirstItem (IC_JTLABELS (ic))); lbl; lbl = (symbol *)(setNextItem (IC_JTLABELS (ic)))) + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, lbl)->sch->key], 6.0f, cfg); + } +} + +int +switchAddressSpacesOptimally (iCode *ic, ebbIndex *ebbi) +{ + cfg_t control_flow_graph; + tree_dec_t tree_decomposition; + std::map addrspaces; + + create_cfg_naddr(control_flow_graph, ic, ebbi); + annotate_cfg_naddr(control_flow_graph, addrspaces); + + if(options.dump_graphs) + dump_cfg_naddr(control_flow_graph); + + get_nice_tree_decomposition (tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition_naddr(tree_decomposition); + + return(tree_dec_address_switch(tree_decomposition, control_flow_graph, addrspaces)); +} + diff --git a/src/SDCCnaddr.hpp b/src/SDCCnaddr.hpp new file mode 100644 index 0000000..e2da759 --- /dev/null +++ b/src/SDCCnaddr.hpp @@ -0,0 +1,511 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011 +// +// (c) 2011 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// +// Optimal placement of bank switching instructions for named address spaces. +// +// For details, see: +// +// Philipp Klaus Krause, +// "Optimal Placement of Bank Selection Instructions in Polynomial Time", +// Proceedings of the 16th International Workshop on Software and Compilers for Embedded Systems, +// M-SCOPES '13, pp. 23-30. +// Association for Computing Machinery, +// 2013. + +#ifndef SDCCNADDR_HH +#define SDCCNADDR_HH 1 + + +#include +#include +#include +#include + +// Workaround for boost bug #11880 +#include +#if BOOST_VERSION == 106000 + #include +#endif + +#include + +extern "C" +{ +#include "SDCCsymt.h" +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#include "SDCCopt.h" +#include "SDCCy.h" +} + +typedef short int naddrspace_t; // Named address spaces. -1: Undefined, Others: see map. + +typedef std::set naddrspaceset_t; + +struct assignment_naddr +{ + float s; + naddrspaceset_t local; + std::vector global; + + bool operator<(const assignment_naddr& a) const + { + naddrspaceset_t::const_iterator i, ai, i_end, ai_end; + + i_end = local.end(); + ai_end = a.local.end(); + + for (i = local.begin(), ai = a.local.begin();; ++i, ++ai) + { + if (i == i_end) + return(true); + if (ai == ai_end) + return(false); + + if (*i < *ai) + return(true); + if (*i > *ai) + return(false); + + if (global[*i] < a.global[*ai]) + return(true); + if (global[*i] > a.global[*ai]) + return(false); + } + } +}; + +bool assignments_naddr_locally_same(const assignment_naddr &a1, const assignment_naddr &a2) +{ + if (a1.local != a2.local) + return(false); + + naddrspaceset_t::const_iterator i, i_end; + for (i = a1.local.begin(), i_end = a1.local.end(); i != i_end; ++i) + if (a1.global[*i] != a2.global[*i]) + return(false); + + return(true); +} + +struct cfg_naddr_node +{ + iCode *ic; + naddrspaceset_t possible_naddrspaces; +}; + +typedef std::list assignment_list_naddr_t; + +struct tree_dec_naddr_node +{ + std::set bag; + assignment_list_naddr_t assignments; + unsigned weight; // The weight is the number of nodes at which intermediate results need to be remembered. In general, to minimize memory consumption, at join nodes the child with maximum weight should be processed first. +}; + +typedef boost::adjacency_list cfg_t; // The edge property is the cost of subdividing the edge and inserting a bank switching instruction. +typedef boost::adjacency_list tree_dec_t; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif + +#include "SDCCtree_dec.hpp" + +// Annotate nodes of the control flow graph with the set of possible named address spaces active there. +void annotate_cfg_naddr(cfg_t &cfg, std::map &addrspaces) +{ + /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */ + typedef /*typename*/ boost::graph_traits::vertex_descriptor vertex_t; + + std::map sym_to_index; + naddrspace_t na_max = -1; + + std::vector predetermined(boost::num_vertices (cfg), false); + + // Initialize the cfg vertices where there is information on the desired named address space. + for (vertex_t i = 0; i < boost::num_vertices (cfg); i++) + { + const iCode *ic = cfg[i].ic; + const symbol *addrspace; + + // We do not know the current named address space when entering a function or after calling one. + if (ic->op == CALL || ic->op == PCALL || ic->op == FUNCTION) + predetermined[i] = true; + + // Set the required named address spaces + if (addrspace = getAddrspaceiCode (ic)) + { + naddrspace_t na; + + if (sym_to_index.find (addrspace) == sym_to_index.end ()) + sym_to_index[addrspace] = ++na_max; + na = sym_to_index[addrspace]; + addrspaces[na] = addrspace; + + cfg[i].possible_naddrspaces.insert (na); + predetermined[i] = true; + } + else + cfg[i].possible_naddrspaces.insert(-1); + } + + // Extend. + bool change; + do + { + change = false; + for (vertex_t i = 0; i < boost::num_vertices (cfg); i++) + { + if (predetermined[i]) + continue; + + size_t oldsize = cfg[i].possible_naddrspaces.size(); + { + /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */ + typedef /*typename*/ boost::graph_traits::out_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::out_edges(i, cfg); n != n_end; ++n) + { + vertex_t v = boost::target(*n, cfg); + cfg[i].possible_naddrspaces.insert(cfg[v].possible_naddrspaces.begin(), cfg[v].possible_naddrspaces.end()); + } + } + { + /* MSVC 2010 doesn't like the typename here, though it accepts it elsewhere */ + typedef /*typename*/ boost::graph_traits::in_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::in_edges(i, cfg); n != n_end; ++n) + { + vertex_t v = boost::source(*n, cfg); + cfg[i].possible_naddrspaces.insert(cfg[v].possible_naddrspaces.begin(), cfg[v].possible_naddrspaces.end()); + } + } + + if (oldsize != cfg[i].possible_naddrspaces.size()) + change = true; + } + } + while(change); +} + +// Handle Leaf nodes in the nice tree decomposition +template +void tree_dec_naddrswitch_leaf(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + assignment_naddr a; + assignment_list_naddr_t &alist = T[t].assignments; + + a.s = 0; + a.global.resize(boost::num_vertices(G), -2); + alist.push_back(a); +} + +// Handle introduce nodes in the nice tree decomposition +template +int tree_dec_naddrswitch_introduce(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + assignment_list_naddr_t::iterator ai; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + assignment_list_naddr_t &alist2 = T[t].assignments; + assignment_list_naddr_t &alist = T[*c].assignments; + + std::set new_inst; + std::set_difference(T[t].bag.begin(), T[t].bag.end(), T[*c].bag.begin(), T[*c].bag.end(), std::inserter(new_inst, new_inst.end())); + unsigned short int i = *(new_inst.begin()); + + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + ai->local.insert(i); + + naddrspaceset_t::const_iterator ni, ni_end; + for(ni = G[i].possible_naddrspaces.begin(), ni_end = G[i].possible_naddrspaces.end(); ni != ni_end; ++ni) + { + ai->global[i] = *ni; + alist2.push_back(*ai); + } + } + + alist.clear(); + + return((int)alist2.size() <= options.max_allocs_per_node ? 0 : -1); +} + +// Handle forget nodes in the nice tree decomposition +template +void tree_dec_naddrswitch_forget(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + assignment_list_naddr_t &alist = T[t].assignments; + + std::swap(alist, T[*c].assignments); + + std::set old_inst; + std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); + unsigned short int i = *(old_inst.begin()); + + assignment_list_naddr_t::iterator ai, aif; + + // Restrict assignments (locally) to current variables. + for (ai = alist.begin(); ai != alist.end(); ++ai) + { + ai->local.erase(i); + { + typedef typename boost::graph_traits::out_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::out_edges(i, G); n != n_end; ++n) + { + if (ai->local.find(boost::target(*n, G)) == ai->local.end() || ai->global[boost::target(*n, G)] == -1) + continue; + if (ai->global[boost::source(*n, G)] == ai->global[boost::target(*n, G)]) + continue; + ai->s += G[*n]; + } + } + { + typedef typename boost::graph_traits::in_edge_iterator n_iter_t; + n_iter_t n, n_end; + for (boost::tie(n, n_end) = boost::in_edges(i, G); n != n_end; ++n) + { + if (ai->local.find(boost::source(*n, G)) == ai->local.end() || ai->global[boost::target(*n, G)] == -1) + continue; + if (ai->global[boost::source(*n, G)] == ai->global[boost::target(*n, G)]) + continue; + ai->s += G[*n]; + } + } + } + + alist.sort(); + + // Collapse (locally) identical assignments. + for (ai = alist.begin(); ai != alist.end();) + { + aif = ai; + + for (++ai; ai != alist.end() && assignments_naddr_locally_same(*aif, *ai);) + { + if (aif->s > ai->s) + { + alist.erase(aif); + aif = ai; + ++ai; + } + else + { + alist.erase(ai); + ai = aif; + ++ai; + } + } + } +} + +// Handle join nodes in the nice tree decomposition +template +void tree_dec_naddrswitch_join(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end, c2, c3; + boost::tie(c, c_end) = adjacent_vertices(t, T); + + c2 = c; + ++c; + c3 = c; + + assignment_list_naddr_t &alist1 = T[t].assignments; + assignment_list_naddr_t &alist2 = T[*c2].assignments; + assignment_list_naddr_t &alist3 = T[*c3].assignments; + + alist2.sort(); + alist3.sort(); + + assignment_list_naddr_t::iterator ai2, ai3; + for (ai2 = alist2.begin(), ai3 = alist3.begin(); ai2 != alist2.end() && ai3 != alist3.end();) + { + if (assignments_naddr_locally_same(*ai2, *ai3)) + { + ai2->s += ai3->s; + for (size_t i = 0; i < ai2->global.size(); i++) + ai2->global[i] = ((ai2->global[i] != -2) ? ai2->global[i] : ai3->global[i]); + alist1.push_back(*ai2); + ++ai2; + ++ai3; + } + else if (*ai2 < *ai3) + { + ++ai2; + continue; + } + else if (*ai3 < *ai2) + { + ++ai3; + continue; + } + } + + alist2.clear(); + alist3.clear(); +} + +template +int tree_dec_naddrswitch_nodes(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + tree_dec_naddrswitch_leaf(T, t, G); + break; + case 1: + c0 = *c; + tree_dec_naddrswitch_nodes(T, c0, G); + if (T[c0].bag.size() < T[t].bag.size()) + { + if (tree_dec_naddrswitch_introduce(T, t, G)) + return(-1); + } + else + tree_dec_naddrswitch_forget(T, t, G); + break; + case 2: + c0 = *c++; + c1 = *c; + + if (T[c0].weight < T[c1].weight) // Minimize memory consumption. + std::swap (c0, c1); + + tree_dec_naddrswitch_nodes(T, c0, G); + tree_dec_naddrswitch_nodes(T, c1, G); + tree_dec_naddrswitch_join(T, t, G); + break; + default: + std::cerr << "Not nice.\n"; + break; + } + return(0); +} + +template +static void implement_naddr_assignment(const assignment_naddr &a, const G_t &G, const std::map addrspaces) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_t; + typedef typename boost::graph_traits::edge_iterator ei_t; + ei_t e, e_end; + + for(boost::tie(e, e_end) = boost::edges(G); e != e_end; ++e) + { + const vertex_t source = boost::source(*e, G); + const vertex_t target = boost::target(*e, G); + const naddrspace_t sourcespace = a.global[source]; + const naddrspace_t targetspace = a.global[target]; + + // Nothing to do if the space doesn't change, or we just forget it. + if(targetspace == -1 || sourcespace == targetspace) + continue; + + // This shouldn't happen with the CFGs sdcc generates and a cost function based on code size. + if(G[source].ic->next != G[target].ic) + std::cerr << "Trying to switch address space at weird edge in CFG."; + + switchAddressSpaceAt(G[target].ic, addrspaces.find(targetspace)->second); + } +} + +template +int tree_dec_address_switch(T_t &T, const G_t &G, const std::map addrspaces) +{ + if(tree_dec_naddrswitch_nodes(T, find_root(T), G)) + return(-1); + + const assignment_naddr &winner = *(T[find_root(T)].assignments.begin()); + +#if 0 + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + implement_naddr_assignment(winner, G, addrspaces); + + return(0); +} + +// Dump cfg, with numbered nodes, show possible address spaces at each node. +void dump_cfg_naddr(const cfg_t &cfg) +{ + std::ofstream dump_file((std::string(dstFileName) + ".dumpnaddrcfg" + (currFunc ? currFunc->rname : "__global") + ".dot").c_str()); + + std::string *name = new std::string[num_vertices(cfg)]; + for (unsigned int i = 0; i < boost::num_vertices(cfg); i++) + { + std::ostringstream os; + os << i << ", " << cfg[i].ic->key << ": "; + naddrspaceset_t::const_iterator n; + for (n = cfg[i].possible_naddrspaces.begin(); n != cfg[i].possible_naddrspaces.end(); ++n) + os << *n << " "; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name), boost::default_writer(), cfg_titlewriter(currFunc->rname, " bank selection instr. placement")); + delete[] name; +} + +// Dump tree decomposition, show bag and live variables at each node. +static void dump_tree_decomposition_naddr(const tree_dec_t &tree_dec) +{ + std::ofstream dump_file((std::string(dstFileName) + ".dumpnaddrdec" + currFunc->rname + ".dot").c_str()); + + unsigned int w = 0; + + std::string *name = new std::string[num_vertices(tree_dec)]; + for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) + { + if (tree_dec[i].bag.size() > w) + w = tree_dec[i].bag.size(); + std::ostringstream os; + std::set::const_iterator v1; + os << i << " | "; + for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) + os << *v1 << " "; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name), boost::default_writer(), dec_titlewriter((w - 1), currFunc->rname, " bank selection instr. placement")); + delete[] name; +} + +#endif + diff --git a/src/SDCCopt.c b/src/SDCCopt.c new file mode 100644 index 0000000..35681e4 --- /dev/null +++ b/src/SDCCopt.c @@ -0,0 +1,3328 @@ +/*------------------------------------------------------------------------- + SDCCopt.c - calls all the optimizations routines and does some of the + hackier transformations, these include translating iCodes + to function calls and replacing local variables with their + register equivalents etc. Also contains the driver routine + for dead code elimination + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include "common.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* global variables */ +int cseDefNum = 0; + +char flowChanged = 0; + + +/*-----------------------------------------------------------------*/ +/* printSymName - prints the symbol names */ +/*-----------------------------------------------------------------*/ +int +printSymName (void *vsym) +{ + symbol *sym = vsym; + fprintf (stdout, " %s ", sym->name); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* cnvToFcall - does the actual conversion to function call */ +/*-----------------------------------------------------------------*/ +static void +cnvToFcall (iCode * ic, eBBlock * ebp) +{ + iCode *ip; + iCode *newic; + operand *left; + operand *right; + symbol *func = NULL; + char *filename = ic->filename; + int lineno = ic->lineno; + int bytesPushed=0; + + ip = ic->next; /* insertion point */ + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SYMOP (left)) + bitVectUnSetBit (OP_USES (left), ic->key); + if (IS_SYMOP (right)) + bitVectUnSetBit (OP_USES (right), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + if (IS_FLOAT (operandType (right))) + { + switch (ic->op) + { + case '+': + func = fsadd; + break; + case '-': + func = fssub; + break; + case '/': + func = fsdiv; + break; + case '*': + func = fsmul; + break; + case EQ_OP: + func = fseq; + break; + case NE_OP: + func = fsneq; + break; + case '<': + func = fslt; + break; + case '>': + { + operand *tmp = right; + right = left; + left = tmp; + func = fslt; + break; + } + } + } + else if (IS_FIXED16X16 (operandType (right))) + { + switch (ic->op) + { + case '+': + func = fps16x16_add; + break; + case '-': + func = fps16x16_sub; + break; + case '/': + func = fps16x16_div; + break; + case '*': + func = fps16x16_mul; + break; + case EQ_OP: + func = fps16x16_eq; + break; + case NE_OP: + func = fps16x16_neq; + break; + case '<': + func = fps16x16_lt; + break; + case '>': + func = fps16x16_gt; + break; + case LE_OP: + func = fps16x16_lteq; + break; + case GE_OP: + func = fps16x16_gteq; + break; + } + } + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, left, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, left); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + + /* second one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, right, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode ('=', NULL, right); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)->next); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + } + else + { + /* push right */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, right, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode (IPUSH, right, NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(right)); + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + + /* insert push left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, left, NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, left, NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(left)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + } + /* insert the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + newic->filename = filename; + newic->lineno = lineno; + newic->parmBytes += bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to mark as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); +} + +/*-----------------------------------------------------------------*/ +/* cnvToFloatCast - converts casts to floats to function calls */ +/*-----------------------------------------------------------------*/ +static void +cnvToFloatCast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_RIGHT (ic)); + int linenno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = conv[0][bwd][su]; + goto found; + } + } + } + + if (compareType (type, fixed16x16Type) == 1) + { + func = fp16x16conv[0][4][0]; + goto found; + } + + if (IS_BOOLEAN (type)) + { + wassert(multypes[0][1] == UCHARTYPE); + func = conv[0][0][1]; + goto found; + } + + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*----------------------------------------------------------------------*/ +/* cnvToFixed16x16Cast - converts casts to fixed16x16 to function calls */ +/*----------------------------------------------------------------------*/ +static void +cnvToFixed16x16Cast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_RIGHT (ic)); + int linenno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = fp16x16conv[0][bwd][su]; + goto found; + } + } + } + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = linenno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*-----------------------------------------------------------------*/ +/* cnvFromFloatCast - converts casts From floats to function calls */ +/*-----------------------------------------------------------------*/ +static void +cnvFromFloatCast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_LEFT (ic)); + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = conv[1][bwd][su]; + goto found; + } + } + } + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +/*--------------------------------------------------------------------------*/ +/* cnvFromFixed16x16Cast - converts casts from fixed16x16 to function calls */ +/*--------------------------------------------------------------------------*/ +static void +cnvFromFixed16x16Cast (iCode * ic, eBBlock * ebp) +{ + iCode *ip, *newic; + symbol *func = NULL; + sym_link *type = operandType (IC_LEFT (ic)); + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd, su; + int bytesPushed=0; + + ip = ic->next; + /* remove it from the iCode */ + remiCodeFromeBBlock (ebp, ic); + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + if (IS_SYMOP (IC_RESULT (ic))) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (type, multypes[bwd][su]) == 1) + { + func = fp16x16conv[1][bwd][su]; + goto found; + } + } + } + + if (compareType (type, floatType) == 1) + { + func = fp16x16conv[1][4][0]; + goto found; + } + + assert (0); +found: + + /* if float support routines NOT compiled as reentrant */ + if (!options.float_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + else + { + /* push the left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RIGHT (ic))) + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + } + + /* make the call */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + newic->parmBytes+=bytesPushed; + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (IC_RESULT (ic))) + OP_DEFS (IC_RESULT (ic)) = bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); +} + +extern operand *geniCodeRValue (operand *, bool); + +/*-----------------------------------------------------------------*/ +/* convilong - converts int or long mults or divs to fcalls */ +/*-----------------------------------------------------------------*/ +static void +convilong (iCode * ic, eBBlock * ebp) +{ + int op = ic->op; + symbol *func = NULL; + iCode *ip = ic->next; + iCode *newic; + char *filename = ic->filename; + int lineno = ic->lineno; + int bwd; + int su; + int bytesPushed=0; + operand *left; + operand *right; + sym_link *leftType = operandType (IC_LEFT (ic)); + sym_link *rightType = operandType (IC_RIGHT (ic)); + + remiCodeFromeBBlock (ebp, ic); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SYMOP (left)) + bitVectUnSetBit (OP_USES (left), ic->key); + if (IS_SYMOP (right)) + bitVectUnSetBit (OP_USES (right), ic->key); + + if (op == '*' && (muls16tos32[0] || muls16tos32[1]) && + (IS_SYMOP (left) && bitVectnBitsOn (OP_DEFS (left)) == 1 && bitVectnBitsOn (OP_USES (left)) == 0 || IS_OP_LITERAL (left) && operandLitValue (left) < 32768 && operandLitValue (left) >= -32768) && + (IS_SYMOP (right) && bitVectnBitsOn (OP_DEFS (right)) == 1 && bitVectnBitsOn (OP_USES (right)) == 0 || IS_OP_LITERAL (right) && operandLitValue (right) < 32768 && operandLitValue (right) >= -32768) && + getSize (leftType) == 4 && getSize (rightType) == 4) + { + iCode *lic = IS_SYMOP (left) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (left))) : 0; + iCode *ric = IS_SYMOP (right) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (right))) : 0; + + if ((!lic || lic->op == CAST && getSize (operandType (IC_RIGHT (lic))) == 2 && SPEC_USIGN (operandType (IC_RIGHT (lic))) == SPEC_USIGN (operandType (left))) && + (!ric || ric->op == CAST && getSize (operandType (IC_RIGHT (ric))) == 2 && SPEC_USIGN (operandType (IC_RIGHT (ric))) == SPEC_USIGN (operandType (right)))) + { + func = muls16tos32[SPEC_USIGN (operandType (left))]; + + if (lic) + { + lic->op = '='; + OP_SYMBOL (left)->type = newIntLink(); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newIntLink(), operandLitValue (left), operandLitValue (left))); + + if (ric) + { + ric->op = '='; + OP_SYMBOL (right)->type = newIntLink(); + } + else + IC_RIGHT (ic) = operandFromValue (valCastLiteral (newIntLink(), operandLitValue (right), operandLitValue (right))); + + if (func) + goto found; + } + } + + if (getSize (leftType) == 1 && getSize (rightType) == 1) + { + int muldivmod; + + if (op == '*') + muldivmod = 0; + else if (op == '/') + muldivmod = 1; + else if (op == '%') + muldivmod = 2; + else + muldivmod = -1; + + for (su = 0; su < 4 && muldivmod >= 0; su++) + { + if ((compareType (leftType, multypes[0][su%2]) == 1) && + (compareType (rightType, multypes[0][su/2]) == 1)) + { + func = muldiv[muldivmod][0][su]; + goto found; + } + } + } + + /* depending on the type */ + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + if (compareType (leftType, multypes[bwd][su]) == 1) + { + if ((op=='*' || op=='/' || op=='%')) + { + int ret = compareType (rightType, multypes[bwd][su]); + if (ret != 1) + { + assert(0); + } + } + if (op == '*') + func = muldiv[0][bwd][su]; + else if (op == '/') + func = muldiv[1][bwd][su]; + else if (op == '%') + func = muldiv[2][bwd][su]; + else if (op == RRC) + func = rlrr[1][bwd][su]; + else if (op == RLC) + func = rlrr[0][bwd][su]; + else if (op == RIGHT_OP) + func = rlrr[1][bwd][su]; + else if (op == LEFT_OP) + func = rlrr[0][bwd][su]; + else + assert (0); + goto found; + } + } + } + werrorfl (filename, lineno, E_INVALID_OP, ""); + return; +found: + /* if int & long support routines NOT compiled as reentrant */ + if (!options.intlong_rent) + { + /* first one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_LEFT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode ('=', NULL, IC_LEFT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + + /* second one */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode ('=', NULL, IC_RIGHT (ic)); + IC_RESULT (newic) = operandFromValue (FUNC_ARGS(func->type)->next); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + } + else + { + /* compiled as reentrant then push */ + /* push right */ + if (IS_REGPARM (FUNC_ARGS(func->type)->next->etype)) + { + newic = newiCode (SEND, IC_RIGHT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->next->etype); + } + else + { + newic = newiCode (IPUSH, IC_RIGHT (ic), NULL); + newic->parmPush = 1; + + bytesPushed += getSize(operandType(IC_RIGHT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (right)) + OP_USES (right) = bitVectSetBit (OP_USES (right), newic->key); + + /* insert push left */ + if (IS_REGPARM (FUNC_ARGS(func->type)->etype)) + { + newic = newiCode (SEND, IC_LEFT (ic), NULL); + newic->argreg = SPEC_ARGREG(FUNC_ARGS(func->type)->etype); + } + else + { + newic = newiCode (IPUSH, IC_LEFT (ic), NULL); + newic->parmPush = 1; + + bytesPushed += getSize(operandType(IC_LEFT(ic))); + } + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); + newic->filename = filename; + newic->lineno = lineno; + if (IS_SYMOP (left)) + OP_USES (left) = bitVectSetBit (OP_USES (left), newic->key); + } + + /* for the result */ + newic = newiCode (CALL, operandFromSymbol (func), NULL); + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + newic->filename = filename; + newic->lineno = lineno; + newic->parmBytes+=bytesPushed; // to clear the stack after the call + ebp->hasFcall = 1; + if (currFunc) + FUNC_HASFCALL (currFunc->type) = 1; + + if (TARGET_PIC_LIKE) + { + /* normally these functions aren't marked external, so we can use their + * _extern field to marked as already added to symbol table */ + + if (!SPEC_EXTR(func->etype)) + { + memmap *seg = SPEC_OCLS(OP_SYMBOL(IC_LEFT(newic))->etype); + + SPEC_EXTR(func->etype) = 1; + seg = SPEC_OCLS( func->etype ); + addSet(&seg->syms, func); + } + } + + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebp, newic, ip); +} + +/*-----------------------------------------------------------------*/ +/* convertbuiltin - maybe convert some builtins back */ +/*-----------------------------------------------------------------*/ +static void +convbuiltin (iCode *const ic, eBBlock *ebp) +{ + sym_link *ftype; + symbol *bif; + int stack; + + iCode *icc = ic, *icp = ic->prev, *ico = NULL; + iCode *lastparam = ic; + while (icc->op != CALL) + { + if (icc->op != SEND || !icc->builtinSEND) + return; + lastparam = icc; + icc = icc->next; + } + + if (!IS_SYMOP (IC_LEFT(icc))) + return; + + ftype = operandType (IC_LEFT(icc)); + if (!IFFUNC_ISBUILTIN (ftype)) + return; + + bif = OP_SYMBOL (IC_LEFT (icc)); + + /* Now we can be sure to have found a builtin function. */ + + if ((!strcmp (bif->name, "__builtin_memcpy") || !strcmp (bif->name, "__builtin_strncpy") || !strcmp (bif->name, "__builtin_memset")) && + IS_OP_LITERAL (IC_LEFT (lastparam)) && !operandLitValue (IC_LEFT (lastparam))) + { + /* We have a builtin that does nothing. */ + /* TODO: Eliminate it, convert any SEND of volatile into DUMMY_READ_VOLATILE. */ + /* For now just convert back to call to make sure any volatiles are read. */ + + strcpy(OP_SYMBOL (IC_LEFT (icc))->rname, !strcmp (bif->name, "__builtin_memcpy") ? "___memcpy" : (!strcmp (bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); + goto convert; + } + + if ((TARGET_IS_Z80 || TARGET_IS_Z180 || TARGET_IS_RABBIT || TARGET_IS_EZ80_Z80) && (!strcmp (bif->name, "__builtin_memcpy") || !strcmp (bif->name, "__builtin_strncpy") || !strcmp (bif->name, "__builtin_memset"))) + { + /* Replace iff return value is used or last parameter is not an integer constant. */ + if (bitVectIsZero (OP_USES (IC_RESULT (icc))) && IS_OP_LITERAL (IC_LEFT (lastparam))) + return; + + strcpy(OP_SYMBOL (IC_LEFT (icc))->rname, !strcmp (bif->name, "__builtin_memcpy") ? "___memcpy" : (!strcmp (bif->name, "__builtin_strncpy") ? "_strncpy" : "_memset")); + goto convert; + } + + return; + +convert: + /* Convert parameter passings from SEND to PUSH. */ + stack = 0; + for (icc = ic; icc->op != CALL; icc = icc->next) + { + icc->builtinSEND = 0; + icc->op = IPUSH; + icc->parmPush = 1; + stack += getSize (operandType (IC_LEFT (icc))); + } + icc->parmBytes = stack; + + /* Reverse parameters. */ + for (icc = ic; icc->op != CALL; icc = icc->next) + { + if(icc->next->op != CALL) + icc->prev = icc->next; + else + icc->prev = icp; + } + if(icc != ic) + { + if(icp) + icp->next = icc->prev; + icc->prev = ic; + } + for(; icc != icp; ico = icc, icc = icc->prev) + { + if(icc->op != CALL) + icc->next = ico; + } +} + +static void +convsmallc (iCode *ic, eBBlock *ebp) +{ + iCode *icc, *icp, *ico = NULL; + + assert (ic->op == CALL || ic->op == PCALL); + + for (icc = ic->prev; icc && icc->op == IPUSH; icc = icc->prev) + ic = icc; + icp = icc; + + /* Reverse parameters. */ + for (icc = ic; icc->op != CALL && icc->op != PCALL; icc = icc->next) + { + if (icc->next->op != CALL && icc->next->op != PCALL) + icc->prev = icc->next; + else + icc->prev = icp; + } + if (icc != ic) + { + if (icp) + icp->next = icc->prev; + icc->prev = ic; + } + for (; icc != icp; ico = icc, icc = icc->prev) + { + if (icc->op != CALL && icc->op != PCALL) + icc->next = ico; + } +} + +/*-----------------------------------------------------------------*/ +/* convertToFcall - converts some operations to fcalls */ +/*-----------------------------------------------------------------*/ +static void +convertToFcall (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* floating point operations are + converted to function calls */ + if ((IS_CONDITIONAL (ic) || IS_ARITHMETIC_OP (ic)) && + (IS_FLOAT (operandType (IC_RIGHT (ic))) || IS_FIXED( operandType (IC_RIGHT (ic))))) + { + cnvToFcall (ic, ebbs[i]); + } + + /* casting is a little different */ + if (ic->op == CAST) + { + if (IS_FLOAT (operandType (IC_RIGHT (ic)))) + cnvFromFloatCast (ic, ebbs[i]); + else if (IS_FLOAT (operandType (IC_LEFT (ic)))) + cnvToFloatCast (ic, ebbs[i]); + if (IS_FIXED16X16 (operandType (IC_RIGHT (ic)))) + cnvFromFixed16x16Cast (ic, ebbs[i]); + else if (IS_FIXED16X16 (operandType (IC_LEFT (ic)))) + cnvToFixed16x16Cast (ic, ebbs[i]); + } + + // Easy special case which avoids function call: modulo by a literal power + // of two can be replaced by a bitwise AND. + if (ic->op == '%' && isOperandLiteral (IC_RIGHT(ic))) + { + bool us = IS_UNSIGNED (operandType (IC_LEFT(ic))); + + // Chek if left really is just an upcasted unsigned value. + if (!us && IS_SYMOP (IC_LEFT(ic)) && bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) == 1) + { + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))); + + if (dic && dic->op == CAST && IS_UNSIGNED (operandType (IC_RIGHT (dic))) && getSize (operandType (IC_RIGHT (dic))) < getSize (operandType (IC_RESULT (dic)))) + us = true; + } + + if (us) + { + unsigned long litVal = double2ul (operandLitValue (IC_RIGHT (ic))); + + /* modulo by 1: no remainder */ + if (litVal == 1) + { + ic->op = '='; + IC_RIGHT (ic) = operandFromLit (0); + IC_LEFT (ic) = NULL; + continue; + } + // See if literal value is a power of 2. + while (litVal && !(litVal & 1)) + { + litVal >>= 1; + } + if (litVal) + { + // discard lowest set bit. + litVal >>= 1; + } + + if (!litVal) + { + ic->op = BITWISEAND; + IC_RIGHT(ic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) - 1); + continue; + } + } + } + + /* if long / int mult or divide or mod */ + if (ic->op == '*' || ic->op == '/' || ic->op == '%') + { + sym_link *leftType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (leftType)) + { + sym_link *rightType = operandType (IC_RIGHT (ic)); + + if (port->hasNativeMulFor != NULL && + port->hasNativeMulFor (ic, leftType, rightType)) + { + /* Leave as native */ + } + else + { + convilong (ic, ebbs[i]); + } + } + } + + if (ic->op == RRC || ic->op == RLC || ic->op == LEFT_OP || ic->op == RIGHT_OP) + { + sym_link *type = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (type) && getSize (type) > (unsigned)port->support.shift && port->support.shift >= 0) + { + convilong (ic, ebbs[i]); + } + } + if (ic->op == SEND && ic->builtinSEND) + { + convbuiltin (ic, ebbs[i]); + } + if ((ic->op == CALL && IFFUNC_ISSMALLC (operandType (IC_LEFT (ic)))) || + (ic->op == PCALL && IFFUNC_ISSMALLC (operandType (IC_LEFT (ic))->next))) + { + convsmallc (ic, ebbs[i]); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* isPowerOf2 - test if val is power of 2 */ +/*-----------------------------------------------------------------*/ +bool +isPowerOf2 (unsigned long val) +{ + while (val && !(val & 1)) + { + val >>= 1; + } + return val == 1; +} + +/*-----------------------------------------------------------------*/ +/* miscOpt - miscellaneous optimizations */ +/*-----------------------------------------------------------------*/ +static void +miscOpt (eBBlock ** ebbs, int count) +{ +/* Borut: disabled optimization of comparision unsigned with 2^n literal + * since it is broken; see bug #2165 Broken comparison */ +#if 0 + int i; + + /* for all blocks do */ + for (i = 0; i < count; ++i) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* patch ID: 2702889 - Summary of all uncommitted changes I applied on "my" SDCC */ + /* MB: This seems rather incomplete. + MB: What if using <= or >= ? + Borut: Currently <= and >= are transformed to > and < on all targets. + Transformation depends on lt_nge, gt_nle, bool le_ngt, + ge_nlt, ne_neq and eq_nne members of PORT structure. + MB: Why do we need IFX in the first case and not in the second ? + Borutr: Because the result of comparision is logically negated, + so in case of IFX the jump logic is inverted for '<' and '<='. + TODO: The logical negation of the result should be implemeted + for '<' and '<=' in case when the following instruction is not IFX. + Philipp: Added the test for ifx in the second case, too: + We want 0 or 1 as a result, the bitwise and won't do, unless we add a cast to bool. + */ + switch (ic->op) + { + case '<': + case LE_OP: + case '>': + case GE_OP: + /* Only if the the right operand is literal and left operand is unsigned */ + if (isOperandLiteral (IC_RIGHT (ic)) && IS_UNSIGNED (operandType (IC_LEFT (ic)))) + { + unsigned litVal = ulFromVal (OP_VALUE (IC_RIGHT (ic))); + + /* Only if the literal value is greater than 255 and a power of 2 */ + if (litVal >= 255 && + (isPowerOf2 (litVal) && (ic->op == '<' || ic->op == GE_OP) || + isPowerOf2 (litVal + 1) && (ic->op == '>' || ic->op == LE_OP))) + { + iCode *ic_nxt = ic->next; + + switch (ic->op) + { + case LE_OP: + ++litVal; + /* fall through */ + case '<': + /* Only if the next instruction is IFX */ + if (ic_nxt && (ic_nxt->op == IFX) && (ic->eBBlockNum == ic_nxt->eBBlockNum)) + { + int AndMaskVal = 0 - litVal; + symbol *TrueLabel; + + /* set op to bitwise and */ + ic->op = BITWISEAND; + IC_RIGHT (ic) = operandFromLit (AndMaskVal); + + /* invert jump logic */ + TrueLabel = IC_TRUE (ic_nxt); + IC_TRUE (ic_nxt) = IC_FALSE (ic_nxt); + IC_FALSE (ic_nxt) = TrueLabel; + } + break; + + case '>': + ++litVal; + /* fall through */ + case GE_OP: + if (ic_nxt && (ic_nxt->op == IFX) && (ic->eBBlockNum == ic_nxt->eBBlockNum)) + { + int AndMaskVal = 0 - litVal; + + ic->op = BITWISEAND; + IC_RIGHT (ic) = operandFromLit (AndMaskVal); + } + break; + } /* switch */ + } /* if */ + } /* if */ + } /* switch */ + } /* for */ + } /* for */ +#endif +} + +/*-----------------------------------------------------------------*/ +/* separateAddressSpaces - enforce restrictions on bank switching */ +/* Operands of a single iCode must be in at most one */ +/* named address space. Use temporaries and additional assignments */ +/* to enforce the rule. */ +/*-----------------------------------------------------------------*/ +static void +separateAddressSpaces (eBBlock **ebbs, int count) +{ + int i; + + /* for all blocks do */ + for (i = 0; i < count; ++i) + { + iCode *ic; + symbol *source; + + /* Skip this block if not reachable; other routines may have */ + /* also skipped it, so these iCodes may be undercooked. */ + if (ebbs[i]->noPath) + continue; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + iCode *iic = 0, *newic = 0; + operand *left, *right, *result; + const symbol *leftaddrspace = 0, *rightaddrspace = 0, *resultaddrspace = 0; + + /* JUMPTABLE and IFX do not have left/right/result operands. */ + /* However, they only have a single operand so they cannot */ + /* have more than one address space to worry about. */ + if (ic->op == JUMPTABLE || ic->op == IFX) + continue; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /*printf ("Looking at ic %d, op %d\n", ic->key, (int)(ic->op));*/ + + if (left && IS_SYMOP (left)) + { + if (POINTER_GET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (left)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (left)->type))); + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type->next); + } + else + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type); + } + if (right && IS_SYMOP (right)) + rightaddrspace = getAddrspace (OP_SYMBOL (right)->type); + if (result && IS_SYMOP (result)) + { + if (POINTER_SET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (result)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (result)->type))); + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type->next); + } + else + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type); + } + +#if 0 + if (leftaddrspace) + printf("ic %d (dcl? %d) leftaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (left)->type)), leftaddrspace->name); + if (rightaddrspace) + printf("ic %d (dcl? %d) rightaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (right)->type)), rightaddrspace->name); + if (resultaddrspace) + printf("ic %d (dcl? %d) resultaddrspace %s\n", ic->key, (int)(IS_DECL (OP_SYMBOL (result)->type)), resultaddrspace->name); +#endif + + if (ic->op == IPUSH && leftaddrspace) + { + operand *newop; + + source = OP_SYMBOL (left); + newic = newiCode ('=', 0, left); + IC_RESULT (newic) = newop = newiTempOperand (source->type, 0); + IC_LEFT (ic) = newop; + leftaddrspace = 0; + for (iic = ic; iic->prev && iic->prev->op == IPUSH; iic = iic->prev); + } + else if (leftaddrspace && rightaddrspace && leftaddrspace != rightaddrspace || + resultaddrspace && rightaddrspace && resultaddrspace != rightaddrspace || + resultaddrspace && leftaddrspace && resultaddrspace != leftaddrspace) + { + operand *newop; + + if (rightaddrspace == resultaddrspace) + source = OP_SYMBOL (left); + else + source = OP_SYMBOL (right); + newic = newiCode ('=', 0, rightaddrspace == resultaddrspace ? left : right); + IC_RESULT (newic) = newop = newiTempOperand (source->type, 0); + if (rightaddrspace == resultaddrspace) + { + IC_LEFT (ic) = newop; + leftaddrspace = 0; + } + else + { + IC_RIGHT (ic) = newop; + rightaddrspace = 0; + } + iic = ic; + } + + if (newic) + { + newic->filename = ic->filename; + newic->lineno = ic->lineno; + hTabAddItem (&iCodehTab, newic->key, newic); + addiCodeToeBBlock (ebbs[i], newic, iic); + } + + assert (!leftaddrspace || !resultaddrspace || leftaddrspace == resultaddrspace); + assert (!rightaddrspace || !resultaddrspace || rightaddrspace == resultaddrspace); + } + } +} + +const symbol * +getAddrspaceiCode (const iCode *ic) +{ + operand *left, *right, *result; + const symbol *leftaddrspace = 0, *rightaddrspace = 0, *resultaddrspace = 0; + const symbol *addrspace; + + /* Not safe to use IC_LEFT, IC_RIGHT, or IC_RESULT macros on */ + /* IFX or JUMPTABLE iCodes. Handle these as a special case. */ + if (ic->op == IFX || ic->op == JUMPTABLE) + { + operand *cond; + if (ic->op == IFX) + cond = IC_COND (ic); + else + cond = IC_JTCOND (ic); + if (IS_SYMOP (cond)) + return getAddrspace (OP_SYMBOL (cond)->type); + else + return NULL; + } + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* Previous transformations in separateAddressSpaces() should + ensure that at most one addressspace occours in each iCode. */ + if (left && IS_SYMOP (left)) + { + if (POINTER_GET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (left)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (left)->type))); + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type->next); + } + else + leftaddrspace = getAddrspace (OP_SYMBOL (left)->type); + } + if (right && IS_SYMOP (right)) + rightaddrspace = getAddrspace (OP_SYMBOL (right)->type); + if (result && IS_SYMOP (result)) + { + if (POINTER_SET (ic)) + { + assert (!(IS_DECL (OP_SYMBOL (result)->type) && DCL_PTR_ADDRSPACE (OP_SYMBOL (result)->type))); + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type->next); + } + else + resultaddrspace = getAddrspace (OP_SYMBOL (result)->type); + } + + addrspace = leftaddrspace; + if (rightaddrspace) + { + wassertl (!addrspace || addrspace == rightaddrspace, "Multiple named address spaces in icode."); + addrspace = rightaddrspace; + } + if (resultaddrspace) + { + wassertl (!addrspace || addrspace == resultaddrspace, "Multiple named address spaces in icode."); + addrspace = resultaddrspace; + } + + return (addrspace); +} + +/*-----------------------------------------------------------------*/ +/* switchAddressSpaceAt - insert a bank selection instruction */ +/*-----------------------------------------------------------------*/ +void +switchAddressSpaceAt (iCode *ic, const symbol *const addrspace) +{ + iCode *newic; + const symbol *const laddrspace = getAddrspaceiCode (ic); + wassertl(!laddrspace || laddrspace == addrspace, "Switching to invalid address space."); + + newic = newiCode (CALL, operandFromSymbol (addrspace->addressmod[0]), 0); + + IC_RESULT (newic) = newiTempOperand (newVoidLink (), 1); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + + newic->next = ic; + newic->prev = ic->prev; + if (ic->prev) + ic->prev->next = newic; + ic->prev = newic; +} + +/*-----------------------------------------------------------------*/ +/* switchAddressSpaces - insert instructions for bank switching */ +/* This is just a fallback, in case the optimal approach fails - */ +/* improbable, but possible depending on sdcc options and code. */ +/*-----------------------------------------------------------------*/ +static void +switchAddressSpaces (iCode *ic) +{ + const symbol *oldaddrspace = 0; + + for (; ic; ic = ic->next) + { + const symbol *const addrspace = getAddrspaceiCode (ic); + + if (addrspace && addrspace != oldaddrspace) + { + switchAddressSpaceAt (ic, addrspace); + + oldaddrspace = addrspace; + } + + /* Address space might not be preserved over these. */ + if (ic->op == LABEL || ic->op == CALL || ic->op == PCALL) + oldaddrspace = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* isLocalWithoutDef - return 1 if sym might be used without a */ +/* defining iCode */ +/*-----------------------------------------------------------------*/ +static int +isLocalWithoutDef (symbol * sym) +{ + if (!IS_AUTO (sym)) + return 0; + + if (IS_VOLATILE (sym->type)) + return 0; + + if (sym->_isparm) + return 0; + + if (IS_AGGREGATE (sym->type)) + return 0; + + if (sym->addrtaken) + return 0; + + return !sym->defs; +} + +static void +replaceRegEqvOperand (iCode * ic, operand ** opp, int force_isaddr, int new_isaddr) +{ + operand * op = *opp; + symbol * sym = OP_SYMBOL (op); + + if (isLocalWithoutDef (sym)) + { + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->name); + OP_REQV (op) = NULL; + sym->allocreq = 1; + } + else if (OP_REQV (op)) + { + operand * nop; + + nop = operandFromOperand (OP_REQV (op)); + + /* Copy def/use info from true symbol to register equivalent */ + /* but only if this hasn't been done already. */ + if (!OP_DEFS (nop)) + OP_DEFS (nop) = bitVectCopy (OP_DEFS (op)); + if (!OP_USES (nop)) + OP_USES (nop) = bitVectCopy (OP_USES (op)); + + if (force_isaddr) + nop->isaddr = new_isaddr; + + *opp = nop; /* Replace true sym operand with reg equiv */ + } +} + +/*-----------------------------------------------------------------*/ +/* replaceRegEqv - replace all local variables with their reqv */ +/*-----------------------------------------------------------------*/ +static void +replaceRegEqv (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + int i; + + /* Reset all the def/use info (Otherwise there may be stale def/use */ + /* info if a variable is also used in a previous functions) */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + if (IS_TRUE_SYMOP (IC_COND (ic))) + { + OP_DEFS (IC_COND (ic)) = NULL; + OP_USES (IC_COND (ic)) = NULL; + } + continue; + } + + if (ic->op == JUMPTABLE) + { + if (IS_TRUE_SYMOP (IC_JTCOND (ic))) + { + OP_DEFS (IC_JTCOND (ic)) = NULL; + OP_USES (IC_JTCOND (ic)) = NULL; + } + continue; + } + + if (IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_DEFS (IC_RESULT (ic)) = NULL; + OP_USES (IC_RESULT (ic)) = NULL; + } + + if (IS_TRUE_SYMOP (IC_RIGHT (ic))) + { + OP_DEFS (IC_RIGHT (ic)) = NULL; + OP_USES (IC_RIGHT (ic)) = NULL; + } + + if (IS_TRUE_SYMOP (IC_LEFT (ic))) + { + OP_DEFS (IC_LEFT (ic)) = NULL; + OP_USES (IC_LEFT (ic)) = NULL; + } + } + } + + /* Update the symbols' def bitvector so we know if there is */ + /* a defining iCode or not. Only replace a local variable */ + /* with its register equivalent if there is a defining iCode; */ + /* otherwise, the port's register allocater may choke. */ + cseAllBlocks (ebbi, TRUE); + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + if (IS_TRUE_SYMOP (IC_COND (ic))) + replaceRegEqvOperand (ic, &IC_COND (ic), 0, 0); + continue; + } + + if (ic->op == JUMPTABLE) + { + if (IS_TRUE_SYMOP (IC_JTCOND (ic))) + replaceRegEqvOperand (ic, &IC_JTCOND (ic), 0, 0); + continue; + } + + if (ic->op == RECEIVE) + { + if (OP_SYMBOL (IC_RESULT (ic))->addrtaken) + OP_SYMBOL (IC_RESULT (ic))->isspilt = 1; + } + + /* general case */ + if (IS_TRUE_SYMOP (IC_RESULT (ic))) + { + if (POINTER_SET (ic)) + replaceRegEqvOperand (ic, &IC_RESULT (ic), 1, 1); + else + replaceRegEqvOperand (ic, &IC_RESULT (ic), 0, 0); + } + if (IS_TRUE_SYMOP (IC_RIGHT (ic))) + replaceRegEqvOperand (ic, &IC_RIGHT (ic), 1, 0); + if (IS_TRUE_SYMOP (IC_LEFT (ic))) + replaceRegEqvOperand (ic, &IC_LEFT (ic), 1, 0); + } + } +} + +/*-----------------------------------------------------------------*/ +/* findReqv - search for a register equivalent */ +/*-----------------------------------------------------------------*/ +operand * +findReqv (symbol * prereqv, eBBlock ** ebbs, int count) +{ + int i; + iCode * ic; + + /* for all blocks do */ + for (i=0; isch; ic; ic = ic->next) + { + if (ic->op == IFX) + { + if (IS_ITEMP (IC_COND (ic)) + && OP_SYMBOL (IC_COND (ic))->prereqv == prereqv) + return IC_COND (ic); + } + else if (ic->op == JUMPTABLE) + { + if (IS_ITEMP (IC_JTCOND (ic)) + && OP_SYMBOL (IC_JTCOND (ic))->prereqv == prereqv) + return IC_JTCOND (ic); + } + else + { + if (IS_ITEMP (IC_LEFT (ic)) + && OP_SYMBOL (IC_LEFT (ic))->prereqv == prereqv) + return IC_LEFT (ic); + if (IS_ITEMP (IC_RIGHT (ic)) + && OP_SYMBOL (IC_RIGHT (ic))->prereqv == prereqv) + return IC_RIGHT (ic); + if (IS_ITEMP (IC_RESULT (ic)) + && OP_SYMBOL (IC_RESULT (ic))->prereqv == prereqv) + return IC_RESULT (ic); + } + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* killDeadCode - eliminates dead assignments */ +/*-----------------------------------------------------------------*/ +int +killDeadCode (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->dfOrder; + int count = ebbi->count; + int change = 1; + int gchange = 0; + int i = 0; + + /* basic algorithm :- */ + /* first the exclusion rules :- */ + /* 1. if result is a global or volatile then skip */ + /* 2. if assignment and result is a temp & isaddr then skip */ + /* since this means array & pointer access, will be taken */ + /* care of by alias analysis. */ + /* 3. if the result is used in the remainder of the block skip */ + /* 4. if this definition does not reach the end of the block */ + /* i.e. the result is not present in the outExprs then KILL */ + /* 5. if it reaches the end of block & is used by some success */ + /* or then skip */ + /* else KILL */ + /* this whole process is carried on iteratively till no change */ + do + { + change = 0; + /* for all blocks do */ + for (i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions in the block do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int kill, j; + kill = 0; + + if (SKIP_IC (ic) || + ic->op == IFX || + ic->op == RETURN || + ic->op == DUMMY_READ_VOLATILE || + ic->op == CRITICAL || + ic->op == ENDCRITICAL) + continue; + + /* Since both IFX & JUMPTABLE (in SKIP_IC) have been tested for */ + /* it is now safe to assume IC_LEFT, IC_RIGHT, & IC_RESULT are */ + /* valid. */ + + /* if the result is volatile then continue */ + if (IC_RESULT (ic) && isOperandVolatile (IC_RESULT (ic), FALSE)) + continue; + + /* if the result is a temp & isaddr then skip */ + if (IC_RESULT (ic) && POINTER_SET (ic)) + continue; + + /* if the results address has been taken then skip */ + if (IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->addrtaken) + continue; + + if (POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT (ic))->next) + && !SPIL_LOC (IC_RESULT (ic))) + continue; + + /* if the result is used in the remainder of the */ + /* block then skip */ + if (usedInRemaining (IC_RESULT (ic), ic->next)) + continue; + + /* does this definition reach the end of the block + or the usage is zero then we can kill */ + if (!bitVectBitValue (ebbs[i]->outDefs, ic->key)) + kill = 1; /* if not we can kill it */ + else + { + /* if this is a global variable or function parameter */ + /* we cannot kill anyway */ + if (isOperandGlobal (IC_RESULT (ic)) || + (OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->ismyparm)) + continue; + + /* if we are sure there are no usages */ + if (bitVectIsZero (OP_USES (IC_RESULT (ic)))) + { + kill = 1; + goto kill; + } + + /* reset visited flag */ + for (j = 0; j < count; ebbs[j++]->visited = 0); + + /* find out if this definition is alive */ + if (applyToSet (ebbs[i]->succList, isDefAlive, ic)) + continue; + + kill = 1; + } + + kill: + /* kill this one if required */ + if (kill) + { + bool volLeft = IS_SYMOP (IC_LEFT (ic)) + && isOperandVolatile (IC_LEFT (ic), FALSE); + bool volRight = IS_SYMOP (IC_RIGHT (ic)) + && isOperandVolatile (IC_RIGHT (ic), FALSE); + + /* a dead address-of operation should die, even if volatile */ + if (ic->op == ADDRESS_OF) + volLeft = FALSE; + + if (ic->next && ic->seqPoint == ic->next->seqPoint + && (ic->next->op == '+' || ic->next->op == '-')) + { + if (isOperandEqual (IC_LEFT(ic), IC_LEFT(ic->next)) + || isOperandEqual (IC_LEFT(ic), IC_RIGHT(ic->next))) + volLeft = FALSE; + if (isOperandEqual (IC_RIGHT(ic), IC_LEFT(ic->next)) + || isOperandEqual (IC_RIGHT(ic), IC_RIGHT(ic->next))) + volRight = FALSE; + } + + if (POINTER_GET (ic) && IS_VOLATILE (operandType (IC_LEFT (ic))->next)) + { + if (SPIL_LOC (IC_RESULT (ic))) + { + IC_RESULT (ic) = newiTempFromOp (IC_RESULT (ic)); + SPIL_LOC (IC_RESULT (ic)) = NULL; + } + continue; + } + + change = 1; + gchange++; + + /* now delete from defUseSet */ + deleteItemIf (&ebbs[i]->outExprs, ifDiCodeIsX, ic); + bitVectUnSetBit (ebbs[i]->outDefs, ic->key); + + /* and defset of the block */ + bitVectUnSetBit (ebbs[i]->defSet, ic->key); + + /* If this is the last of a register equivalent, */ + /* look for a successor register equivalent. */ + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + if (IS_ITEMP (IC_RESULT (ic)) + && OP_SYMBOL (IC_RESULT (ic))->isreqv + && bitVectIsZero (OP_DEFS (IC_RESULT (ic)))) + { + symbol * resultsym = OP_SYMBOL (IC_RESULT (ic)); + symbol * prereqv = resultsym->prereqv; + + if (prereqv && prereqv->reqv && (OP_SYMBOL (prereqv->reqv) == resultsym)) + { + operand * newreqv; + + IC_RESULT (ic) = NULL; + newreqv = findReqv (prereqv, ebbs, count); + if (newreqv) + { + prereqv->reqv = newreqv; + } + } + } + + /* delete the result */ + if (IC_RESULT (ic)) + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + IC_RESULT (ic) = NULL; + + if (volLeft || volRight) + { + /* something is volatile, so keep the iCode */ + /* and change the operator instead */ + ic->op = DUMMY_READ_VOLATILE; + + /* keep only the volatile operands */ + if (!volLeft) + IC_LEFT (ic) = NULL; + if (!volRight) + IC_RIGHT (ic) = NULL; + } + else + { + /* nothing is volatile, eliminate the iCode */ + remiCodeFromeBBlock (ebbs[i], ic); + + /* for the left & right remove the usage */ + if (IS_SYMOP (IC_LEFT (ic))) + { + if (OP_SYMBOL (IC_LEFT (ic))->isstrlit) + freeStringSymbol (OP_SYMBOL (IC_LEFT (ic))); + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + } + if (IS_SYMOP (IC_RIGHT (ic))) + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + } + } + } /* end of all instructions */ + + if (!ebbs[i]->sch && !ebbs[i]->noPath) + disconBBlock (ebbs[i], ebbi); + } /* end of for all blocks */ + } /* end of do */ + while (change); + + return gchange; +} + +/*-----------------------------------------------------------------*/ +/* printCyclomatic - prints the cyclomatic information */ +/*-----------------------------------------------------------------*/ +static void +printCyclomatic (eBBlock ** ebbs, int count) +{ + int nEdges = elementsInSet (graphEdges); + int i, nNodes = 0; + + for (i = 0; i < count; i++) + nNodes += (!ebbs[i]->noPath); + + /* print the information */ + werror (I_CYCLOMATIC, currFunc->name, nEdges, nNodes, nEdges - nNodes + 2); +} + +/*-----------------------------------------------------------------*/ +/* discardDeadParamReceives - remove any RECEIVE opcodes which */ +/* refer to dead variables. */ +/*-----------------------------------------------------------------*/ +static void +discardDeadParamReceives (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + iCode dummyIcode; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == RECEIVE) + { + if (IC_RESULT (ic) && OP_SYMBOL (IC_RESULT (ic)) + && !OP_SYMBOL (IC_RESULT (ic))->used) + { +#if 0 + fprintf (stderr, "discarding dead receive for %s\n", + OP_SYMBOL (IC_RESULT (ic))->name); +#endif + dummyIcode.next = ic->next; + remiCodeFromeBBlock (ebbs[i], ic); + ic = &dummyIcode; + } + } + } + } +} + +/* Insert a cast of operand op of ic to type type */ +static void prependCast (iCode *ic, operand *op, sym_link *type, eBBlock *ebb) +{ + iCode *newic = newiCode (CAST, operandFromLink (type), op); + hTabAddItem (&iCodehTab, newic->key, newic); + + IC_RESULT (newic) = newiTempOperand (type, 0); + bitVectSetBit (OP_USES (op), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (op), ic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + + addiCodeToeBBlock (ebb, newic, ic); + + if (isOperandEqual (op, IC_LEFT (ic))) + IC_LEFT (ic) = IC_RESULT (newic); + + if (isOperandEqual (op, IC_RIGHT (ic))) + IC_RIGHT (ic) = IC_RESULT (newic); +} + +/* Insert a cast of result of ic from type type */ +static void appendCast (iCode *ic, sym_link *type, eBBlock *ebb) +{ + iCode *newic = newiCode (CAST, operandFromLink (operandType (IC_RESULT (ic))), 0); + hTabAddItem (&iCodehTab, newic->key, newic); + + IC_RESULT (newic) = IC_RESULT (ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + bitVectSetBit (OP_DEFS (IC_RESULT (ic)), newic->key); + IC_RESULT (ic) = newiTempOperand (type, 0); + IC_RIGHT (newic) = operandFromOperand (IC_RESULT (ic)); + bitVectSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + bitVectSetBit (OP_USES (IC_RESULT (ic)), newic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebb, newic, ic->next); +} + + + +/*-----------------------------------------------------------------*/ +/* optimizeOpWidth - reduce operation width. */ +/* Wide arithmetic operations where the result is cast to narrow */ +/* type can be optimized by doing the casts on the operands */ +/*-----------------------------------------------------------------*/ +static int +optimizeOpWidth (eBBlock ** ebbs, int count) +{ + int i; + int change = 0; + iCode *ic, *newic; + iCode *uic, *skipuic; + sym_link *nextresulttype; + symbol *sym; + int resultsize, nextresultsize; + + // Wide loop counter + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + sym_link *newcountertype, *oldcountertype; + const symbol *label; + const iCode *ifx, *inc = 0, *obstacle = 0; + iCode *mul = 0; + bool found = false; + + if (ic->op != LABEL || !ic->next) + continue; + + label = IC_LABEL (ic); + ic = ic->next; + + if (ic->op != '<' || !IS_ITEMP (IC_LEFT (ic)) || bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 2) + continue; + + oldcountertype = operandType (IC_LEFT (ic)); + if (IS_VOLATILE (oldcountertype)) + continue; + + // Only try to narrow wide counters. + if (!IS_INTEGRAL(oldcountertype) || bitsForType (oldcountertype) <= 8 || TARGET_IS_DS390 || TARGET_IS_DS400 || (!SPEC_USIGN (oldcountertype))) // TODO: Handle signed types as well, maybe even transform int to unsigned int? + continue; + + ifx = ifxForOp (IC_RESULT (ic), ic); + + if (!ifx || IC_TRUE (ifx) || i + 1 >= count) + continue; + + /* For now we handle only loops that have no complex control flow inside them and where + the loop is entered and left through ifx only */ + for(uic = ebbs[i + 1]->sch; uic; uic = uic->next) + { + if(uic->op == GOTO && IC_LABEL (uic) == label) + break; + + if(!obstacle && + (uic->op == CALL || uic->op == PCALL || uic->op == IFX || uic->op == LABEL || + uic->op == GOTO && IC_LABEL (uic) != label || uic->op == INLINEASM)) + { + obstacle = uic; + break; + } + } + + // TODO: Proceed despite obstacle, but only consider array accesses before obstacle. + if(obstacle || !uic || uic->op != GOTO || IC_LABEL (uic) != label) + continue; + + const bitVect *uses; + int bit; + + uses = bitVectCopy (OP_USES (IC_LEFT (ic))); + for (bit = bitVectFirstBit (uses); bitVectnBitsOn (uses); bitVectUnSetBit (uses, bit), bit = bitVectFirstBit (uses)) + { + operand *prevresult = IC_LEFT(ic); + operand *mulotherop = 0; + iCode *mul_candidate = 0; + uic = hTabItemWithKey (iCodehTab, bit); + + if(uic->op == '+' && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) == 1 && isOperandEqual (IC_LEFT (uic), IC_LEFT (ic))) + { + inc = uic; + continue; + } + + if (uic->op != CAST && uic->op != '=' && uic->op != '+' && uic->op != '*' && uic->op != '-' && uic->op != LEFT_OP && uic->op != RIGHT_OP && uic->op != '<') + { + found = false; + break; + } + + if (uic && uic->op == '*') + { + mulotherop = isOperandEqual (IC_LEFT (ic), IC_LEFT (uic)) ? IC_RIGHT (uic) : IC_LEFT (uic); + if (isOperandEqual (IC_RIGHT (ic), mulotherop)) + { + mul_candidate = uic; + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + } + + for (int i = 0; i < 8 && uic && + (uic->op == CAST && bitsForType (operandType (IC_RESULT (uic))) >= 16 || + uic->op == '=' || uic->op == '+' || uic->op == LEFT_OP || + uic->op == '*' && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) >= 1); i++) + { + prevresult = IC_RESULT (uic); + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + + if(!uic) + continue; + + // Use as array index? + if (uic->op == GET_VALUE_AT_ADDRESS || POINTER_SET(uic) && isOperandEqual (IC_RESULT (uic), prevresult)) + { + found = true; + if (mul_candidate) + mul = mul_candidate; + } + } + + if (!found || !inc) + continue; + + /* All backends (except ds390 / ds400) have an array size limit smaller than 2^16. Thus if the loop counter ever goes outside + the range of a 16-bit type, the array access would result in undefined behaviour. We can thus replace the loop + counter by a 16-bit type. If we found a squaring multiplication, we can even use an 8-bit type*/ + if (bitsForType (oldcountertype) <= 16 && !mul) + continue; + + newcountertype = mul ? newCharLink () : newIntLink (); + SPEC_USIGN (newcountertype) = 1; + OP_SYMBOL (IC_LEFT (ic))->type = newcountertype; + OP_SYMBOL (IC_RESULT (inc))->type = newcountertype; + + uses = bitVectCopy (OP_USES (IC_LEFT (ic))); + for (bit = bitVectFirstBit (uses); bitVectnBitsOn (uses); bitVectUnSetBit (uses, bit), bit = bitVectFirstBit (uses)) + { + uic = hTabItemWithKey (iCodehTab, bit); + + if (uic == inc || uic == mul) + continue; + if (uic->op == CAST) + continue; + if (uic->key == ic->key) + continue; + if (uic->op == '=') + { + uic->op = CAST; + continue; + } + + // Need to insert cast. + prependCast (uic, IC_LEFT (ic), oldcountertype, ebbs[i + 1]); + } + + // Insert cast for comparison. + if (IS_OP_LITERAL (IC_RIGHT (ic))) + IC_RIGHT (ic) = operandFromValue (valCastLiteral (newcountertype, operandLitValue (IC_RIGHT (ic)), operandLitValue (IC_RIGHT (ic)))); + else + prependCast (ic, IC_RIGHT (ic), newcountertype, ebbs[i]); + + // Bonus: Can narrow a multiplication in the loop. + if (mul) + { + prependCast (mul, IC_LEFT (mul), newcountertype, ebbs[i + 1]); + prependCast (mul, IC_RIGHT (mul), newcountertype, ebbs[i + 1]); + nextresulttype = newIntLink(); + SPEC_USIGN (nextresulttype) = 1; + appendCast(mul, nextresulttype, ebbs[i + 1]); + } + } + } + + /* long and long long multiplications where operands are unsigned char due to bitwise and */ + for (i = 0; i < count; i++) + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (ic->op == '*' && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + sym_link *resulttype = operandType (IC_RESULT (ic)); + + if (!IS_INTEGRAL (resulttype) || bitsForType (resulttype) <= 16 || + !(IS_ITEMP (left) || IS_OP_LITERAL (left)) || + !(IS_ITEMP (right) || IS_OP_LITERAL (right))) + continue; + + if (IS_ITEMP (left) && bitVectnBitsOn (OP_DEFS (left)) != 1 || + IS_ITEMP (right) && bitVectnBitsOn (OP_DEFS (right)) != 1) + continue; + + iCode *lic = IS_ITEMP (left) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (left))) : 0; + iCode *ric = IS_ITEMP (right) ? hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (right))) : 0; + + if (lic) + { + if (lic->op != BITWISEAND || !IS_OP_LITERAL (IC_LEFT (lic)) && !IS_OP_LITERAL (IC_RIGHT (lic))) + continue; + + unsigned long litval = operandLitValue (IS_OP_LITERAL (IC_LEFT (lic)) ? IC_LEFT (lic) : IC_RIGHT (lic)); + + if (litval > 0x7f) + continue; + } + else if (operandLitValue (left) > 0x7f) + continue; + + if (ric) + { + if (ric->op != BITWISEAND || !IS_OP_LITERAL (IC_LEFT (ric)) && !IS_OP_LITERAL (IC_RIGHT (ric))) + continue; + + unsigned long litval = operandLitValue (IS_OP_LITERAL (IC_LEFT (ric)) ? IC_LEFT (ric) : IC_RIGHT (ric)); + + if (litval > 0x7f) + continue; + } + else if (operandLitValue (right) > 0x7f) + continue; + + // Now replace the wide multiplication by 8x8->16 multiplication and insert casts. + + if (lic) + { + newic = newiCode (CAST, operandFromLink (newCharLink()), left); + hTabAddItem (&iCodehTab, newic->key, newic); + IC_RESULT (newic) = newiTempOperand (newCharLink(), 0); + IC_LEFT (ic) = operandFromOperand (IC_RESULT (newic)); + bitVectUnSetBit (OP_USES (left), ic->key); + bitVectSetBit (OP_USES (left), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newCharLink(), operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + + if (ric) + { + newic = newiCode (CAST, operandFromLink (newCharLink()), right); + hTabAddItem (&iCodehTab, newic->key, newic); + IC_RESULT (newic) = newiTempOperand (newCharLink(), 0); + IC_RIGHT (ic) = operandFromOperand (IC_RESULT (newic)); + bitVectUnSetBit (OP_USES (right), ic->key); + bitVectSetBit (OP_USES (right), newic->key); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + OP_USES (IC_RESULT (newic)) = bitVectSetBit (OP_USES (IC_RESULT (newic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + IC_LEFT (ic) = operandFromValue (valCastLiteral (newCharLink(), operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + + // Insert cast on result + nextresulttype = newIntLink(); + SPEC_USIGN (nextresulttype) = 1; + appendCast(ic, nextresulttype, ebbs[i]); + } + + // Operation followed by cast + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS || ic->op == '*' || ic->op == LEFT_OP || ic->op == RIGHT_OP || ic->op == BITWISEAND || ic->op == '|' || ic->op == CAST) && + IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + sym_link *resulttype = operandType (IC_RESULT (ic)); + + if (!IS_INTEGRAL (resulttype) || + ic->op != CAST && !(IS_SYMOP (IC_LEFT (ic)) || IS_OP_LITERAL (IC_LEFT (ic))) || + !(IS_SYMOP (IC_RIGHT (ic)) || IS_OP_LITERAL (IC_RIGHT (ic)) || ic->op == UNARYMINUS)) + continue; + + resultsize = bitsForType (resulttype); + + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) != 1 || bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if(!uic) + continue; + + /* Skip over assignment */ + skipuic = NULL; + if(uic->op == '=' && IS_ITEMP (IC_RESULT (uic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (uic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (uic))) == 1 && + compareType (operandType (IC_RESULT (ic)), operandType (IC_RESULT (uic))) == 1) + { + skipuic = uic; + uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (uic)))); + } + + /* Try to handle a few cases where the result has multiple uses */ + else if(ic->op == '*' && bitsForType (operandType (IC_RESULT (ic))) > 16 && uic->op == '=' && + bitVectnBitsOn (OP_DEFS (IC_RESULT (uic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && bitVectnBitsOn (OP_USES (IC_RESULT (uic))) > 1 && + compareType (operandType (IC_RESULT (ic)), operandType (IC_RESULT (uic))) == 1) + { + bool ok = true; + const bitVect *uses; + int bit; + + uses = bitVectCopy (OP_USES (IC_RESULT (uic))); + for (bit = bitVectFirstBit(uses); bitVectnBitsOn (uses); bitVectUnSetBit(uses, bit), bit = bitVectFirstBit(uses)) + { + iCode *uuic = hTabItemWithKey (iCodehTab, bit); + if (uuic->op != CAST || bitsForType (operandType (IC_RESULT (uuic))) > 16 || IS_BOOLEAN (operandType (IC_RESULT (uuic)))) + { + ok = false; + break; + } + } + + if (!ok) + continue; + + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + sym = OP_SYMBOL (IC_RESULT (uic)); + sym->type = nextresulttype; + + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + goto optimize; + } + + if (uic->op != CAST && uic->op != '+' && uic->op != LEFT_OP && uic->op != RIGHT_OP) + continue; + + /* Special handling since we might need more bits in the operand than in the result */ + if (ic->op == RIGHT_OP) + { + int shiftbits, resultbits; + + if (!IS_OP_LITERAL (IC_RIGHT (ic))) + continue; + + shiftbits = (int) operandLitValue (IC_RIGHT (ic)); + resultbits = bitsForType (operandType (IC_RESULT (uic))); + + if (resultbits + shiftbits > 16) + continue; + else if (resultbits + shiftbits > 8) + nextresulttype = newIntLink (); + else + nextresulttype = newCharLink (); + SPEC_USIGN (nextresulttype) = 1; + } + /* It must be a cast to another integer type that */ + /* has fewer bits */ + else if (uic->op == LEFT_OP || uic->op == RIGHT_OP) + { + /* Since shifting by the width of an operand or more is undefined behaviour, and no type is wider than 256 bits, + we can optimize when the result is used as right operand to a shift. */ + if(!isOperandEqual (IC_RESULT (ic), IC_RIGHT (uic)) || isOperandEqual (IC_RESULT (ic), IC_LEFT (uic))) + continue; + + nextresulttype = newCharLink (); + } + else + { + nextresulttype = operandType (IC_RESULT (uic)); + if (!IS_INTEGRAL (nextresulttype) && !(IS_PTR (nextresulttype) && NEARPTRSIZE == 2)) + continue; + + if (IS_PTR (nextresulttype)) + { + nextresulttype = newIntLink (); + SPEC_USIGN (nextresulttype) = 1; + } + else + nextresulttype = copyLinkChain (nextresulttype); + } + + nextresultsize = bitsForType (nextresulttype); + if (nextresultsize >= resultsize) + continue; + /* Cast to bool and bool-like types must be preserved to ensure that all nonzero values are correctly cast to true */ + if (uic->op == CAST && IS_BOOLEAN (nextresulttype)) + continue; + +optimize: + /* Make op result narrower */ + sym = OP_SYMBOL (IC_RESULT (ic)); + sym->type = nextresulttype; + + /* Insert casts on operands */ + if (ic->op != CAST) + { + if (IS_SYMOP (IC_LEFT (ic))) + { + newic = newiCode (CAST, operandFromLink (nextresulttype), IC_LEFT (ic)); + hTabAddItem (&iCodehTab, newic->key, newic); + bitVectSetBit (OP_USES (IC_LEFT (ic)), newic->key); + IC_RESULT (newic) = newiTempOperand (nextresulttype, 0); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (IC_LEFT (ic)), ic->key); + IC_LEFT (ic) = operandFromOperand (IC_RESULT (newic)); + OP_USES (IC_LEFT (ic)) = bitVectSetBit (OP_USES (IC_LEFT (ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else + { + wassert (IS_OP_LITERAL (IC_LEFT (ic))); + IC_LEFT (ic) = operandFromValue (valCastLiteral (nextresulttype, operandLitValue (IC_LEFT (ic)), operandLitValue (IC_LEFT (ic)))); + } + if (ic->op != LEFT_OP && IS_SYMOP (IC_RIGHT (ic))) + { + newic = newiCode (CAST, operandFromLink (nextresulttype), IC_RIGHT (ic)); + hTabAddItem (&iCodehTab, newic->key, newic); + bitVectSetBit (OP_USES (IC_RIGHT (ic)), newic->key); + IC_RESULT (newic) = newiTempOperand (nextresulttype, 0); + OP_DEFS (IC_RESULT (newic)) = bitVectSetBit (OP_DEFS (IC_RESULT (newic)), newic->key); + bitVectUnSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + IC_RIGHT (ic) = operandFromOperand (IC_RESULT (newic)); + OP_USES (IC_RIGHT (ic)) = bitVectSetBit (OP_USES (IC_RIGHT (ic)), ic->key); + newic->filename = ic->filename; + newic->lineno = ic->lineno; + addiCodeToeBBlock (ebbs[i], newic, ic); + } + else if (ic->op != LEFT_OP && ic->op != UNARYMINUS) + { + wassert (IS_OP_LITERAL (IC_RIGHT (ic))); + IC_RIGHT (ic) = operandFromValue (valCastLiteral (nextresulttype, operandLitValue (IC_RIGHT (ic)), operandLitValue (IC_RIGHT (ic)))); + } + } + if (uic->op == CAST && ic->op != RIGHT_OP) + { + uic->op = '='; + if (skipuic) + { + bitVectUnSetBit (OP_USES (IC_RIGHT (uic)), uic->key); + IC_RIGHT (uic) = IC_RIGHT (skipuic); + OP_USES (IC_RIGHT (uic)) = bitVectSetBit (OP_USES (IC_RIGHT (uic)), uic->key); + } + } + change++; + } + } + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* Go back a chain of assigments / casts to try to find a string */ +/* literal symbol that op really is. */ +/*-----------------------------------------------------------------*/ +static symbol *findStrLitDef (operand *op, iCode **def) +{ + for(;;) + { + if (!IS_ITEMP (op)) + return (0); + + if (bitVectnBitsOn (OP_DEFS (op)) != 1) + return (0); + + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))); + + wassert (dic); + + if (dic->op == ADDRESS_OF) + { + if (def) + *def = dic; + symbol *sym = OP_SYMBOL (IC_LEFT (dic)); + return (sym->isstrlit ? sym : 0); + } + + if (dic->op != '=' && dic->op != CAST) + return (0); + + op = IC_RIGHT (dic); + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeStdLibCall - optimize calls to standard library. */ +/* for now we just merge adjacent calls to puts() */ +/*-----------------------------------------------------------------*/ +static void +optimizeStdLibCall (eBBlock ** ebbs, int count) +{ + iCode *ic, *nic, *ndic; + symbol *strsym, *nstrsym, *cstrsym; + sym_link *strlink, *nstrlink; + size_t replacecost; + + for (int i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + // Look for call to puts(). + if (ic->op != CALL || !ic->prev || ic->prev->op != IPUSH && ic->prev->op != SEND) + continue; + if (!IS_SYMOP (IC_LEFT (ic)) || !OP_SYMBOL (IC_LEFT (ic))->rname || strcmp (OP_SYMBOL (IC_LEFT (ic))->rname, "_puts")) + continue; + + // Look for following call to puts(). + for (nic = ic->next; nic; nic = nic->next) + { + if (nic->op == '=' && !POINTER_SET (ic) || nic->op == CAST) + { + if (!IS_ITEMP (IC_RESULT (nic))) + break; + if (IS_OP_VOLATILE (IC_RIGHT (nic))) + break; + } + else if (nic->op == ADDRESS_OF) + { + if (!IS_ITEMP (IC_RESULT (nic))) + break; + } + else if (nic->op == IPUSH || nic->op == SEND) + { + if (IS_OP_VOLATILE (IC_LEFT (nic))) + break; + } + else // Todo: Handle more to make the optimization more general. + break; + } + if (!nic || nic->op != CALL || nic->prev->op != IPUSH && nic->prev->op != SEND) + continue; + if (!IS_SYMOP (IC_LEFT (nic)) || !OP_SYMBOL (IC_LEFT (nic))->rname || strcmp (OP_SYMBOL (IC_LEFT (nic))->rname, "_puts")) + continue; + + // Check that the return values are unused + if (IC_RESULT (ic) && (!IS_ITEMP (IC_RESULT (ic)) || bitVectnBitsOn (OP_USES (IC_RESULT (ic))))) + continue; + if (IC_RESULT (nic) && (!IS_ITEMP (IC_RESULT (nic)) || bitVectnBitsOn (OP_USES (IC_RESULT (nic))))) + continue; + + // Chek that their parameters are string literals + strsym = findStrLitDef (IC_LEFT (ic->prev), 0); + nstrsym = findStrLitDef (IC_LEFT (nic->prev), &ndic); + if (!strsym || !nstrsym) + continue; + strlink = strsym->etype; + nstrlink = nstrsym->etype; + + // Calculate the cost of doing the replacement in bytes of string literal + replacecost = 1; // For '\n' + if (strsym->isstrlit > 1) + replacecost += strlen (SPEC_CVAL (strlink).v_char); + if (nstrsym->isstrlit > 1) + replacecost += strlen (SPEC_CVAL (nstrlink).v_char); + + // Doing the replacement saves at least 6 bytes of call overhead (assuming pointers are 16 bits). + if (replacecost > 7 - optimize.codeSize + 4 * optimize.codeSpeed) + continue; + + // Combine strings + struct dbuf_s dbuf; + dbuf_init (&dbuf, 3); + dbuf_append_str(&dbuf, SPEC_CVAL (strlink).v_char); + dbuf_append_str(&dbuf, "\n"); + dbuf_append_str(&dbuf, SPEC_CVAL (nstrlink).v_char); + cstrsym = stringToSymbol (rawStrVal (dbuf_c_str (&dbuf), dbuf_get_length (&dbuf) + 1))->sym; + freeStringSymbol (nstrsym); + dbuf_destroy (&dbuf); + + // Make second call print the combined string (which allows further optimization with subsequent calls) + IC_LEFT (ndic)->key = cstrsym->key; + IC_LEFT (ndic)->svt.symOperand = cstrsym; + + // Change unused call to assignments to self to mark it for dead-code elimination. + bitVectSetBit (OP_USES (IC_LEFT (ic->prev)), ic->key); + bitVectSetBit (OP_DEFS (IC_LEFT (ic->prev)), ic->prev->key); + ic->op = '='; + IC_RESULT (ic) = IC_LEFT (ic->prev); + IC_RIGHT (ic) = IC_LEFT (ic->prev); + IC_LEFT (ic) = 0; + ic->prev->op = '='; + IC_RESULT (ic->prev) = IC_LEFT (ic->prev); + IC_RIGHT (ic->prev) = IC_LEFT (ic->prev); + IC_LEFT (ic->prev) = 0; + } + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeCastCast - remove unneeded intermediate casts. */ +/* Integer promotion may cast (un)signed char to int and then */ +/* recast the int to (un)signed long. If the signedness of the */ +/* char and long are the same, the cast can be safely performed in */ +/* a single step. */ +/*-----------------------------------------------------------------*/ +static void +optimizeCastCast (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + sym_link *type1; + sym_link *type2; + sym_link *type3; + symbol *sym; + int size1, size2, size3; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == CAST && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + type1 = operandType (IC_RIGHT (ic)); + type2 = operandType (IC_RESULT (ic)); + + /* Look only for a cast from an integer type to an */ + /* integer type that has no loss of bits */ + if (!IS_INTEGRAL (type1) || !IS_INTEGRAL (type2)) + continue; + size1 = bitsForType (type1); + size2 = bitsForType (type2); + if (size2 < size1) + continue; + /* If they are the same size, they must have the same signedness */ + if (size2 == size1 && SPEC_USIGN (type2) != SPEC_USIGN (type1)) + continue; + + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1 || + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) != 1) + continue; + + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + if(!uic || (uic->op != CAST && uic->op != BITWISEAND)) + continue; + + type3 = operandType (IC_RESULT (uic)); + + /* Cast to bool must be preserved to ensure that all nonzero values are correctly cast to true */ + if (SPEC_NOUN (type2) == V_BOOL && SPEC_NOUN(type3) != V_BOOL) + continue; + + /* Special case: Second use is a bit test */ + if (uic->op == BITWISEAND && IS_OP_LITERAL (IC_RIGHT (uic)) && ifxForOp (IC_RESULT (uic), uic)) + { + unsigned long long mask = operandLitValue (IC_RIGHT (uic)); + + /* Signed cast might set bits above the width of type1 */ + if (!SPEC_USIGN (type1) && (mask >> (bitsForType (type1)))) + continue; + + IC_RIGHT (uic) = operandFromValue (valCastLiteral (type1, operandLitValue (IC_RIGHT (uic)), operandLitValue (IC_RIGHT (uic)))); + } + else if (uic->op == CAST) /* Otherwise this use must be a second cast */ + { + /* It must be a cast to another integer type that */ + /* has no loss of bits */ + type3 = operandType (IC_RESULT (uic)); + if (!IS_INTEGRAL (type3)) + continue; + size3 = bitsForType (type3); + if (size3 < size1) + continue; + /* If they are the same size, they must have the same signedness */ + if (size3 == size2 && SPEC_USIGN (type3) != SPEC_USIGN (type2)) + continue; + + /* The signedness between the first and last types must match */ + if (SPEC_USIGN (type3) != SPEC_USIGN (type1)) + continue; + } + else + continue; + + + /* Change the first cast to a simple assignment and */ + /* let the second cast do all the work */ + ic->op = '='; + IC_LEFT (ic) = NULL; + + sym = OP_SYMBOL (IC_RESULT (ic)); + sym->type = copyLinkChain (type1); + sym->etype = getSpec (sym->type); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* optimizeNegation - remove unneeded intermediate negation */ +/*-----------------------------------------------------------------*/ +static void +optimizeNegation (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == '!' && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + /* There must be only one use of this first result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be an ifx */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + if (!uic) + continue; + /* Todo: Optimize case where use is another negation */ + else if(uic->op == IFX) /* Remove negation by inverting jump targets */ + { + IC_LEFT (uic) = IC_LEFT (ic); + IC_LEFT (ic) = 0; + IC_RIGHT (ic) = IC_RESULT (ic); + ic->op = '='; + + if (IC_TRUE (uic)) + { + IC_FALSE (uic) = IC_TRUE (uic); + IC_TRUE (uic) = 0; + } + else + { + IC_TRUE (uic) = IC_FALSE (uic); + IC_FALSE (uic) = 0; + } + } + } + } + } +} + +/* Fold pointer addition into offset of ADDRESS_OF. */ +static void +offsetFoldGet (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + if (!TARGET_Z80_LIKE && !TARGET_IS_STM8) + return; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == ADDRESS_OF && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + /* There must be only one use of the result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be an addition / subtraction */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if (uic->op != '+' && uic->op != '-' || !IS_OP_LITERAL (IC_RIGHT (uic))) + continue; + + /* Historically ADDRESS_OF didn't have a right operand */ + wassertl (IC_RIGHT (ic), "ADDRESS_OF without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (ic)), "ADDRESS_OF with non-literal right operand"); + + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->uses, uic->key); + + if (uic->op == '+') + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) + operandLitValue (IC_RIGHT (uic))); + else + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (ic)) - operandLitValue (IC_RIGHT (uic))); + IC_LEFT (uic) = operandFromOperand (IC_LEFT(ic)); + uic->op = ADDRESS_OF; + IC_LEFT (uic)->isaddr = 1; + + ic->op = '='; + IC_RIGHT (ic) = IC_RESULT (ic); + IC_LEFT (ic) = 0; + SET_ISADDR (IC_RESULT (ic), 0); + } + } + } +} + +/* Fold pointer addition into offset of GET_VALUE_AT_ADDRESS. */ +/* The hc08-related ports do a similar thing in hc08/ralloc.c, packPointerOp() */ +static void +offsetFoldUse (eBBlock **ebbs, int count) +{ + int i; + iCode *ic; + iCode *uic; + + if (!TARGET_IS_Z80 && !TARGET_IS_Z180 && !TARGET_IS_RABBIT && !TARGET_IS_STM8) + return; + + for (i = 0; i < count; i++) + { + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if ((ic->op == '+' || ic->op == '-') && IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + if (!IS_OP_LITERAL (IC_RIGHT (ic))) + continue; + + /* There must be only one use of the result */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) != 1) + continue; + + /* This use must be a GET_VALUE_AT_ADDRESS */ + uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))); + + if (!POINTER_GET (uic)) + continue; + + /* Historically GET_VALUE_AT_ADDRESS didn't have a right operand */ + wassertl (IC_RIGHT (uic), "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (uic)), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + + if (ic->op == '+') + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (uic)) + operandLitValue (IC_RIGHT (ic))); + else + IC_RIGHT (uic) = operandFromLit (operandLitValue (IC_RIGHT (uic)) - operandLitValue (IC_RIGHT (ic))); + + ic->op = '='; + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = 0; + SET_ISADDR (IC_RESULT (ic), 0); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* guessCounts - Guess execution counts for iCodes */ +/* Needs ic->seq assigned (typically done by computeLiveRanges() */ +/*-----------------------------------------------------------------*/ +void guessCounts (iCode *start_ic, ebbIndex *ebbi) +{ + iCode *ic; + int i; + bool needprop; + + for (ic = start_ic; ic; ic = ic->next) + ic->count = 0; + start_ic->pcount = 1.0f; + needprop = TRUE; + + for(i = 0; needprop && i < 24; i++) // 24 is an arbitrary limit to reduce runtime at the cost of accuracy. + { + needprop = FALSE; + for (ic = start_ic; ic; ic = ic->next) + { + if(ic->pcount <= 0.01) // 0.01 is an arbitrary limit to reduce runtime at the cost of accuracy. + continue; + + ic->count += ic->pcount; + + if (ic->op == GOTO) + { + iCode *target = hTabItemWithKey (labelDef, IC_LABEL (ic)->key); + target->pcount += ic->pcount; + needprop = TRUE; + } + else if(ic->op == IFX) // Use a classic, simple branch prediction. Works well for typical loops. + { + iCode *target = hTabItemWithKey (labelDef, (IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic))->key); + if(target->seq >= ic->seq) + { + target->pcount += ic->pcount / 4; + if(ic->next) + ic->next->pcount += ic->pcount * 3 / 4; + } + else + { + target->pcount += ic->pcount * 3 / 4; + if(ic->next) + ic->next->pcount += ic->pcount / 4; + } + needprop = TRUE; + } + else if(ic->op == JUMPTABLE) + { + symbol *label; + int n = elementsInSet (IC_JTLABELS (ic)); + + for (label = setFirstItem (IC_JTLABELS (ic)); label; label = setNextItem (IC_JTLABELS (ic))) + { + iCode *target = hTabItemWithKey (labelDef, label->key); + target->pcount += ic->pcount / n; + } + needprop = TRUE; + } + else if(ic->op == CALL && IS_SYMOP (IC_LEFT (ic)) && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)) + ; + else if (ic->next) + ic->next->pcount += ic->pcount; + ic->pcount = 0.0f; + } + } +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating a downcast. */ +/*-----------------------------------------------------------------*/ +static int +narrowRead (iCode *ic, operand **opp, eBBlock *ebp) +{ + iCode *dic; + operand *op = *opp; + + if (ic->op != CAST || !IS_ITEMP (op)) + return 0; + + if (bitVectnBitsOn (OP_USES (op)) != 1 || bitVectnBitsOn (OP_DEFS (op)) != 1) + return 0; + + // get the definition + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return 0; + + // found the definition now check if it is local + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; + + // for now handle pointer reads only + if (dic->op != GET_VALUE_AT_ADDRESS || IS_VOLATILE (operandType (IC_LEFT (dic))->next)) + return 0; + + sym_link *resulttype = operandType (IC_RESULT (ic)); + sym_link *righttype = operandType (IC_RIGHT (ic)); + + if (IS_BOOL (resulttype) || getSize (resulttype) >= getSize (righttype)) + return 0; + + // Narrow read + if (!port->little_endian) + { + int offset = getSize (righttype) - getSize (resulttype); + IC_RIGHT (dic) = operandFromLit (operandLitValue (IC_RIGHT (dic)) + offset); + } + OP_SYMBOL (IC_RESULT (dic))->type = resulttype; + ic->op = '='; + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* narrowRead() - Will read fewer bytes by eliminating downcasts. */ +/*-----------------------------------------------------------------*/ +static void +narrowReads(ebbIndex *ebbi) +{ + for (int i = 0; i < ebbi->count; i++) + { + eBBlock **ebbs = ebbi->bbOrder; + eBBlock *ebp = ebbs[i]; + + for (iCode *ic = ebp->sch; ic; ic = ic->next) + if (ic->op == CAST) + narrowRead (ic, &(IC_RIGHT (ic)), ebp); + } +} + +/*-----------------------------------------------------------------*/ +/* eBBlockFromiCode - creates extended basic blocks from iCode */ +/* will return an array of eBBlock pointers */ +/*-----------------------------------------------------------------*/ +eBBlock ** +eBBlockFromiCode (iCode *ic) +{ + ebbIndex *ebbi = NULL; + int change = 1; + int lchange = 0; + int kchange = 0; + hTab *loops; + + /* if nothing passed then return nothing */ + if (!ic) + return NULL; + + eBBNum = 0; + + /* optimize the chain for labels & gotos + this will eliminate redundant labels and + will change jump to jumps by jumps */ + ic = iCodeLabelOptimize (ic); + + /* break it down into basic blocks */ + ebbi = iCodeBreakDown (ic); + /* hash the iCode keys so that we can quickly index */ + /* them in the rest of the optimization steps */ + setToNull ((void *) &iCodehTab); + iCodehTab = newHashTable (iCodeKey); + hashiCodeKeys (ebbi->bbOrder, ebbi->count); + + /* compute the control flow */ + computeControlFlow (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RAW0, ebbi); + + /* replace the local variables with their + register equivalents : the liveRange computation + along with the register allocation will determine + if it finally stays in the registers */ + replaceRegEqv (ebbi); + + /* create loop regions */ + loops = createLoopRegions (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RAW1, ebbi); + + if (!optimize.noStdLibCall) + optimizeStdLibCall (ebbi->bbOrder, ebbi->count); + + optimizeCastCast (ebbi->bbOrder, ebbi->count); + while (optimizeOpWidth (ebbi->bbOrder, ebbi->count)) + optimizeCastCast (ebbi->bbOrder, ebbi->count); + optimizeNegation (ebbi->bbOrder, ebbi->count); + + /* Burn the corpses, so the dead may rest in peace, + safe from cse necromancy */ + computeDataFlow (ebbi); + killDeadCode (ebbi); + + /* do common subexpression elimination for each block */ + change = cseAllBlocks (ebbi, FALSE); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_CSE, ebbi); + + /* compute the data flow */ + computeDataFlow (ebbi); + + /* dumpraw if asked for */ + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_DFLOW, ebbi); + + /* global common subexpression elimination */ + if (optimize.global_cse) + { + change += cseAllBlocks (ebbi, FALSE); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_GCSE, ebbi); + } + else + { + // compute the dataflow only + assert(cseAllBlocks (ebbi, TRUE)==0); + } + + /* kill dead code */ + kchange = killDeadCode (ebbi); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_DEADCODE, ebbi); + + /* do loop optimizations */ + change += (lchange = loopOptimizations (loops, ebbi)); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOP, ebbi); + + /* recompute the data flow and apply global cse again + if loops optimizations or dead code caused a change: + loops will brings out of the loop which then may be + available for use in the later blocks: dead code + elimination could potentially disconnect some blocks + conditional flow may be efected so we need to apply + subexpression once more */ + if (lchange || kchange) + { + computeDataFlow (ebbi); + change += cseAllBlocks (ebbi, FALSE); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOPG, ebbi); + + /* if loop optimizations caused a change then do + dead code elimination once more : this will + get rid of the extra assignments to the induction + variables created during loop optimizations */ + killDeadCode (ebbi); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOOPD, ebbi); + } + + offsetFoldGet (ebbi->bbOrder, ebbi->count); + + /* lospre */ + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + computeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + while (optimizeOpWidth (ebbi->bbOrder, ebbi->count)) + optimizeCastCast (ebbi->bbOrder, ebbi->count); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + shortenLiveRanges (ic, ebbi); + guessCounts (ic, ebbi); + if (optimize.lospre && (TARGET_Z80_LIKE || TARGET_HC08_LIKE || TARGET_IS_STM8)) /* For mcs51, we get a code size regression with lospre enabled, since the backend can't deal well with the added temporaries */ + { + lospre (ic, ebbi); + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_LOSPRE, ebbi); + + /* GCSE, lospre and maybe other optimizations sometimes create temporaries that have non-connected live ranges, which is bad. Split them. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + separateLiveRanges (ic, ebbi); + } + + /* Break down again and redo some steps to not confuse live range analysis later. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + + killDeadCode (ebbi); + + offsetFoldUse (ebbi->bbOrder, ebbi->count); + killDeadCode (ebbi); + + /* sort it back by block number */ + //qsort (ebbs, saveCount, sizeof (eBBlock *), bbNumCompare); + + /* enforce restrictions on acesses to named address spaces */ + separateAddressSpaces (ebbi->bbOrder, ebbi->count); + + /* insert bank switching instructions. Do it here, before the + other support routines, since we can assume that there is no + bank switching happening in those other support routines + (but assume that it can happen in other functions) */ + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + if (!currFunc || switchAddressSpacesOptimally (ic, ebbi)) + switchAddressSpaces (ic); /* Fallback. Very unlikely to be triggered, unless --max-allocs-per-node is set to very small values or very weird control-flow graphs */ + + /* Break down again and redo some steps to not confuse live range analysis. */ + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + + if (!options.lessPedantic) + { + // this is a good place to check missing return values + if (currFunc) + { + // the user is on his own with naked functions... + if (!IS_VOID(currFunc->etype) && !FUNC_ISNAKED(currFunc->type)) + { + eBBlock *bp; + // make sure all predecessors of the last block end in a return + for (bp=setFirstItem(ebbi->bbOrder[ebbi->count-1]->predList); + bp; + bp=setNextItem(ebbi->bbOrder[ebbi->count-1]->predList)) + { + if (bp->ech->op != RETURN) + { + werrorfl (bp->ech->filename, bp->ech->lineno, W_VOID_FUNC, currFunc->name); + } + } + } + } + } + + /* if cyclomatic info requested then print it */ + if (options.cyclomatic) + printCyclomatic (ebbi->bbOrder, ebbi->count); + + /* convert operations with support routines + written in C to function calls : I am doing + this at this point since I want all the + operations to be as they are for optimizations */ + convertToFcall (ebbi->bbOrder, ebbi->count); + + /* miscelaneous optimizations */ + miscOpt (ebbi->bbOrder, ebbi->count); + + /* Split any live-ranges that became non-connected in dead code elimination. */ + change = 0; + do + { + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, FALSE); + adjustIChain (ebbi->bbOrder, ebbi->count); + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbi->bbOrder, ebbi->count)); + change = separateLiveRanges (ic, ebbi); + freeeBBlockData (ebbi); + ebbi = iCodeBreakDown (ic); + computeControlFlow (ebbi); + loops = createLoopRegions (ebbi); + computeDataFlow (ebbi); + } + while (change); + killDeadCode (ebbi); /* iCodeLabelOptimize() above might result in dead code, when both branches of an ifx go to the same destination. */ + + /* compute the live ranges */ + recomputeLiveRanges (ebbi->bbOrder, ebbi->count, TRUE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RANGE, ebbi); + + /* Now that we have the live ranges, discard parameter + * receives for unused parameters. + */ + discardDeadParamReceives (ebbi->bbOrder, ebbi->count); + + narrowReads(ebbi); + + /* allocate registers & generate code */ + port->assignRegisters (ebbi); + + /* throw away blocks */ + setToNull ((void *) &graphEdges); + freeeBBlockData (ebbi); + + return NULL; +} + diff --git a/src/SDCCopt.h b/src/SDCCopt.h new file mode 100644 index 0000000..d2962da --- /dev/null +++ b/src/SDCCopt.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + + SDCCopt.h - header file for Optimizations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCBBlock.h" + +#ifndef SDCCOPT_H +#define SDCCOPT_H 1 + + +/*----------------------------------------------------------------------------*/ +eBBlock **eBBlockFromiCode (iCode *); +void printEbbs (eBBlock **); +iCode *iCodeLabelOptimize (iCode *); +eBBlock *iCode2eBBlock (iCode * ic); +void lospre (iCode *ic, ebbIndex *ebbi); +int switchAddressSpacesOptimally (iCode *ic, ebbIndex *ebbi); +const symbol *getAddrspaceiCode (const iCode *ic); +void switchAddressSpaceAt (iCode *ic, const symbol *const addrspace); +bool isPowerOf2 (unsigned long val); +void guessCounts (iCode *start_ic, ebbIndex *ebbi); + +#endif diff --git a/src/SDCCpeeph.awk b/src/SDCCpeeph.awk new file mode 100644 index 0000000..18999d7 --- /dev/null +++ b/src/SDCCpeeph.awk @@ -0,0 +1,14 @@ +BEGIN { + print "/* Generated file, DO NOT Edit! */" + print "/* To Make changes to rules edit */" + print "/* /peeph.def instead. */" +} + +/^\/\// { next} + +{ printf "\"" ; + printf "%s",$0; + print "\\n\""; +} + + diff --git a/src/SDCCpeeph.c b/src/SDCCpeeph.c new file mode 100644 index 0000000..d607675 --- /dev/null +++ b/src/SDCCpeeph.c @@ -0,0 +1,2893 @@ +/*------------------------------------------------------------------------- + SDCCpeeph.c - The peep hole optimizer: for interpreting the + peep hole rules + + Copyright (C) 1999, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +#define ISCHARDIGIT(c) isdigit((unsigned char)c) +#define ISCHARSPACE(c) isspace((unsigned char)c) +#define ISCHARALNUM(c) isalnum((unsigned char)c) + +static peepRule *rootRules = NULL; +static peepRule *currRule = NULL; + +#define HTAB_SIZE 53 + +hTab *labelHash = NULL; + +static struct +{ + allocTrace values; + allocTrace labels; +} _G; + +static int hashSymbolName (const char *name); +static void buildLabelRefCountHash (lineNode * head); +static void bindVar (int key, char **s, hTab ** vtab); + +static bool matchLine (char *, const char *, hTab **); + +#define FBYNAME(x) static int x (hTab *vars, lineNode *currPl, lineNode *endPl, \ + lineNode *head, char *cmdLine) + +#if !OPT_DISABLE_PIC14 +void peepRules2pCode(peepRule *); +#endif + +#if !OPT_DISABLE_PIC16 +void pic16_peepRules2pCode(peepRule *); +#endif + +/*-----------------------------------------------------------------*/ +/* getPatternVar - finds a pattern variable */ +/*-----------------------------------------------------------------*/ + +static char* +getPatternVar (hTab *vars, char **cmdLine) +{ + int varNumber; + char *digitend; + + if (!cmdLine || !*cmdLine || !**cmdLine) + return NULL; /* no parameters given */ + + while (**cmdLine && ISCHARSPACE(**cmdLine)) + (*cmdLine)++; /* skip whitespace */ + + if (**cmdLine != '%') + goto error; + (*cmdLine)++; + if (!ISCHARDIGIT (**cmdLine)) + goto error; + varNumber = strtol (*cmdLine, &digitend, 10); + *cmdLine = digitend; + return hTabItemWithKey (vars, varNumber); + +error: + fprintf (stderr, + "*** internal error: peephole restriction malformed: %s\n", *cmdLine); + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pcDistance - finds a label backward or forward */ +/*-----------------------------------------------------------------*/ + +static int +pcDistance (lineNode *cpos, char *lbl, bool back) +{ + lineNode *pl = cpos; + char buff[MAX_PATTERN_LEN]; + int dist = 0; + + SNPRINTF (buff, sizeof(buff) - 1, "%s:", lbl); + while (pl) + { + if (pl->line && + !pl->isComment && + !pl->isLabel && + !pl->isDebug) + { + if (port->peep.getSize) + { + dist += port->peep.getSize(pl); +#if 0 + fprintf(stderr, "Line: %s, dist: %i, total: %i\n", pl->line, port->peep.getSize(pl), dist); +#endif + } + else + { + dist += 4; // maximum instruction size + } + } + + if (strncmp (pl->line, buff, strlen (buff)) == 0) + return dist; + + if (back) + pl = pl->prev; + else + pl = pl->next; + + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* portIsDS390 - return true if port is DS390 */ +/*-----------------------------------------------------------------*/ +FBYNAME (portIsDS390) +{ + return ((strcmp(port->target,"ds390") == 0) || + (strcmp(port->target,"ds400") == 0)); +} + +/*-----------------------------------------------------------------*/ +/* flat24bitMode - will check to see if we are in flat24 mode */ +/*-----------------------------------------------------------------*/ +FBYNAME (flat24bitMode) +{ + return (options.model == MODEL_FLAT24); +} + +/*-----------------------------------------------------------------*/ +/* xramMovcOption - check if using movc to read xram */ +/*-----------------------------------------------------------------*/ +FBYNAME (xramMovcOption) +{ + return (options.xram_movc && (strcmp(port->target,"mcs51") == 0)); +} + +/*-----------------------------------------------------------------*/ +/* useAcallAjmp - Enable replacement of lcall/ljmp with acall/ajmp */ +/*-----------------------------------------------------------------*/ +FBYNAME (useAcallAjmp) +{ + return (options.acall_ajmp && (strcmp(port->target,"mcs51") == 0)); +} + +/*-----------------------------------------------------------------*/ +/* labelInRange - will check to see if label is within range */ +/*-----------------------------------------------------------------*/ +FBYNAME (labelInRange) +{ + int dist = 0; + char *lbl = getPatternVar (vars, &cmdLine); + + if (!lbl) + { + fprintf (stderr, + "*** internal error: labelInRange peephole restriction" + " malformed: %s\n", cmdLine); + + /* If no parameters given, assume that %5 pattern variable + has the label name for backward compatibility */ + lbl = hTabItemWithKey (vars, 5); + } + + if (!lbl) + return FALSE; + + do + { + /* Don't optimize jumps in a jump table; a more generic test */ + if (currPl->ic && currPl->ic->op == JUMPTABLE) + return FALSE; + + /* if the previous two instructions are "ljmp"s then don't + do it since it can be part of a jump table */ + if (currPl->prev && currPl->prev->prev && + strstr (currPl->prev->line, "ljmp") && + strstr (currPl->prev->prev->line, "ljmp")) + return FALSE; + + /* Calculate the label distance. For mcs51 the jump can be + -127 to + 127 bytes, for Z80 -126 to +129 bytes.*/ + dist = (pcDistance (currPl, lbl, TRUE) + + pcDistance (currPl, lbl, FALSE)); + + /* Use 125 for now. Could be made more exact using port and + exact jump location instead of currPl. */ + if (!dist || dist > 127) + return FALSE; + + lbl = getPatternVar (vars, &cmdLine); + } + while (lbl); + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* labelJTInRange - will check to see if label %5 and up are */ +/* within range. */ +/* Specifically meant to optimize long (3-byte) jumps to short */ +/* (2-byte) jumps in jumptables */ +/*-----------------------------------------------------------------*/ +FBYNAME (labelJTInRange) +{ + char *lbl; + int dist, count, i; + + /* Only optimize within a jump table */ + if (currPl->ic && currPl->ic->op != JUMPTABLE) + return FALSE; + + count = elementsInSet( IC_JTLABELS (currPl->ic) ); + + /* check all labels (this is needed if the case statements are unsorted) */ + for (i=0; i 127+ /* range of sjmp */ + (3+3*i)+ /* offset between this jump and currPl, + should use pcDistance instead? */ + (count-i-1) /* if peephole applies distance is shortened */ + ) + return FALSE; + } + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* optimizeReturn - is it allowed to optimize RET instructions */ +/*-----------------------------------------------------------------*/ +FBYNAME (optimizeReturn) +{ + return (options.peepReturn >= 0); +} + +/*-----------------------------------------------------------------*/ +/* labelIsReturnOnly - Check if label is followed by ret */ +/*-----------------------------------------------------------------*/ +FBYNAME (labelIsReturnOnly) +{ + /* assumes that %5 pattern variable has the label name */ + const char *label, *p; + const lineNode *pl; + int len; + char * retInst; + + /* Don't optimize jumps in a jump table; a more generic test */ + if (currPl->ic && currPl->ic->op == JUMPTABLE) + return FALSE; + + if (!(label = getPatternVar (vars, &cmdLine))) + { + fprintf (stderr, + "*** internal error: labelIsReturnOnly peephole restriction" + " malformed: %s\n", cmdLine); + + /* If no parameters given, assume that %5 pattern variable + has the label name for backward compatibility */ + label = hTabItemWithKey (vars, 5); + } + + if (!label) + return FALSE; + len = strlen(label); + + for(pl = currPl; pl; pl = pl->next) + { + if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel) + { + if (strncmp(pl->line, label, len) == 0) + break; /* Found Label */ + if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) || + !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) || + !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) || + *(pl->line+5) != '$') + { + return FALSE; /* non-local label encountered */ + } + } + } + if (!pl) + return FALSE; /* did not find the label */ + pl = pl->next; + while (pl && (pl->isDebug || pl->isComment || pl->isLabel)) + pl = pl->next; + if (!pl || !pl->line || pl->isDebug) + return FALSE; /* next line not valid */ + p = pl->line; + for (p = pl->line; *p && ISCHARSPACE(*p); p++) + ; + + retInst = "ret"; + if (TARGET_HC08_LIKE) + retInst = "rts"; + if (strcmp(p, retInst) == 0) + return TRUE; + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* labelIsUncondJump - Check if label %5 is followed by an */ +/* unconditional jump and put the destination of that jump in %6 */ +/*-----------------------------------------------------------------*/ +FBYNAME (labelIsUncondJump) +{ + /* assumes that %5 pattern variable has the label name */ + const char *label; + char *p, *q; + const lineNode *pl; + bool found = FALSE; + int len; + char * jpInst = NULL; + char * jpInst2 = NULL; + + label = hTabItemWithKey (vars, 5); + if (!label) + return FALSE; + len = strlen(label); + + for (pl = currPl; pl; pl = pl->prev) + { + if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel) + { + if (strncmp(pl->line, label, len) == 0) + { + found = TRUE; + break; /* Found Label */ + } + if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) || + !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) || + !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) || + *(pl->line+5) != '$') + { + break; /* non-local label encountered */ + } + } + } + + if (!found) + { + for (pl = currPl; pl; pl = pl->next) + { + if (pl->line && !pl->isDebug && !pl->isComment && pl->isLabel) + { + if (strncmp(pl->line, label, len) == 0) + { + found = TRUE; + break; /* Found Label */ + } + if (strlen(pl->line) != 7 || !ISCHARDIGIT(*(pl->line)) || + !ISCHARDIGIT(*(pl->line+1)) || !ISCHARDIGIT(*(pl->line+2)) || + !ISCHARDIGIT(*(pl->line+3)) || !ISCHARDIGIT(*(pl->line+4)) || + *(pl->line+5) != '$') + { + return FALSE; /* non-local label encountered */ + } + } + } + } + + if (!pl || !found) + return FALSE; /* did not find the label */ + pl = pl->next; + while (pl && (pl->isDebug || pl->isComment)) + pl = pl->next; + if (!pl || !pl->line) + return FALSE; /* next line not valid */ + p = pl->line; + while (*p && ISCHARSPACE(*p)) + p++; + + if (TARGET_MCS51_LIKE) + { + jpInst = "ljmp"; + jpInst2 = "sjmp"; + } + else if (TARGET_HC08_LIKE) + { + jpInst = "jmp"; + jpInst2 = "bra"; + } + else if (TARGET_Z80_LIKE) + { + jpInst = "jp"; + jpInst2 = "jr"; + } + else if (TARGET_IS_STM8) + { + jpInst = (options.model == MODEL_LARGE ? "jpf" : "jp"); + jpInst2 = "jra"; + } + else if (TARGET_PDK_LIKE) + { + jpInst = "goto"; + } + len = strlen(jpInst); + if (strncmp(p, jpInst, len)) + { + len = jpInst2 ? strlen(jpInst2) : 0; + if(!jpInst2 || strncmp(p, jpInst2, len)) + return FALSE; /* next line is no jump */ + } + + p += len; + while (*p && ISCHARSPACE(*p)) + p++; + + q = p; + while (*q && *q!=';') + q++; + while (q>p && ISCHARSPACE(*q)) + q--; + len = q-p; + if (len == 0) + return FALSE; /* no destination? */ + + if (TARGET_Z80_LIKE) + { + while (q>p && *q!=',') + q--; + if (*q==',') + return FALSE; /* conditional jump */ + } + + /* now put the destination in %6 */ + bindVar (6, &p, &vars); + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* okToRemoveSLOC - Check if label %1 is a SLOC and not other */ +/* usage of it in the code depends on a value from this section */ +/*-----------------------------------------------------------------*/ +FBYNAME (okToRemoveSLOC) +{ + const lineNode *pl; + const char *sloc, *p; + int dummy1, dummy2, dummy3; + + /* assumes that %1 as the SLOC name */ + sloc = hTabItemWithKey (vars, 1); + if (sloc == NULL) return FALSE; + p = strstr(sloc, "sloc"); + if (p == NULL) return FALSE; + p += 4; + if (sscanf(p, "%d_%d_%d", &dummy1, &dummy2, &dummy3) != 3) return FALSE; + /*TODO: ultra-paranoid: get funtion name from "head" and check that */ + /* the sloc name begins with that. Probably not really necessary */ + + /* Look for any occurance of this SLOC before the peephole match */ + for (pl = currPl->prev; pl; pl = pl->prev) { + if (pl->line && !pl->isDebug && !pl->isComment + && *pl->line != ';' && strstr(pl->line, sloc)) + return FALSE; + } + /* Look for any occurance of this SLOC after the peephole match */ + for (pl = endPl->next; pl; pl = pl->next) { + if (pl->line && !pl->isDebug && !pl->isComment + && *pl->line != ';' && strstr(pl->line, sloc)) + return FALSE; + } + return TRUE; /* safe for a peephole to remove it :) */ +} + +/*-----------------------------------------------------------------*/ +/* deadMove - Check, if a pop/push pair can be removed */ +/*-----------------------------------------------------------------*/ +FBYNAME (deadMove) +{ + const char *reg = hTabItemWithKey (vars, 1); + + if (port->peep.deadMove) + return port->peep.deadMove (reg, currPl, head); + + fprintf (stderr, "Function deadMove not initialized in port structure\n"); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* labelHashEntry- searches for a label in the list labelHash */ +/* Builds labelHash, if it does not yet exist. */ +/* Returns the labelHashEntry or NULL */ +/*-----------------------------------------------------------------*/ +labelHashEntry * +getLabelRef (const char *label, lineNode *head) +{ + labelHashEntry *entry; + + /* If we don't have the label hash table yet, build it. */ + if (!labelHash) + { + buildLabelRefCountHash (head); + } + + entry = hTabFirstItemWK (labelHash, hashSymbolName (label)); + + while (entry) + { + if (!strcmp (label, entry->name)) + { + break; + } + entry = hTabNextItemWK (labelHash); + } + return entry; +} + +/* labelRefCount: + + * takes two parameters: a variable (bound to a label name) + * and an expected reference count. + * + * Returns TRUE if that label is defined and referenced exactly + * the given number of times. + */ +FBYNAME (labelRefCount) +{ + int varNumber, expectedRefCount; + bool rc = FALSE; + + if (sscanf (cmdLine, "%*[ \t%]%d %d", &varNumber, &expectedRefCount) == 2) + { + char *label = hTabItemWithKey (vars, varNumber); + + if (label) + { + labelHashEntry *entry = getLabelRef (label, head); + + if (entry) + { +#if 0 + /* debug spew. */ + fprintf (stderr, "labelRefCount: %s has refCount %d, want %d\n", + label, entry->refCount, expectedRefCount); +#endif + + rc = (expectedRefCount == entry->refCount); + } + else + { + // Not a local label. We do not know how often it might be referenced. + rc = FALSE; + } + } + else + { + fprintf (stderr, "*** internal error: var %d not bound" + " in peephole labelRefCount rule.\n", + varNumber); + } + } + else + { + fprintf (stderr, + "*** internal error: labelRefCount peephole restriction" + " malformed: %s\n", cmdLine); + } + return rc; +} + +/* labelRefCountChange: + * takes two parameters: a variable (bound to a label name) + * and a signed int for changing the reference count. + * + * Please note, this function is not a conditional. It unconditionally + * changes the label. It should be passed as the 'last' function + * so it only is applied if all other conditions have been met. + * + * should always return TRUE + */ +FBYNAME (labelRefCountChange) +{ + int varNumber, RefCountDelta; + bool rc = FALSE; + + /* If we don't have the label hash table yet, build it. */ + if (!labelHash) + { + buildLabelRefCountHash (head); + } + + if (sscanf (cmdLine, "%*[ \t%]%d %i", &varNumber, &RefCountDelta) == 2) + { + char *label = hTabItemWithKey (vars, varNumber); + + if (label) + { + labelHashEntry *entry; + + entry = hTabFirstItemWK (labelHash, hashSymbolName (label)); + + while (entry) + { + if (!strcmp (label, entry->name)) + { + break; + } + entry = hTabNextItemWK (labelHash); + } + if (entry) + { + if (0 <= entry->refCount + RefCountDelta) + { + entry->refCount += RefCountDelta; + rc = TRUE; + } + else + { + fprintf (stderr, "*** internal error: label %s may not get" + " negative refCount in %s peephole.\n", + label, __func__); + } + } + else + { + // Not a local label. We do not know how often it might be referenced. + return TRUE; + } + } + else + { + fprintf (stderr, "*** internal error: var %d not bound" + " in peephole %s rule.\n", + varNumber, __func__); + } + } + else + { + fprintf (stderr, + "*** internal error: labelRefCountChange peephole restriction" + " malformed: %s\n", cmdLine); + } + return rc; +} + +/* Within the context of the lines currPl through endPl, determine +** if the variable var contains a symbol that is volatile. Returns +** TRUE only if it is certain that this was not volatile (the symbol +** was found and not volatile, or var was a constant or CPU register). +** Returns FALSE if the symbol was found and volatile, the symbol was +** not found, or var was a indirect/pointer addressing mode. +*/ +static bool +notVolatileVariable(const char *var, lineNode *currPl, lineNode *endPl) +{ + char symname[SDCC_NAME_MAX + 1]; + char *p = symname; + const char *vp = var; + lineNode *cl; + operand *op; + iCode *last_ic; + + const bool global_not_volatile = currFunc ? !currFunc->funcUsesVolatile : false; + + /* Can't tell if indirect accesses are volatile or not, so + ** assume they are (if there is a volatile access in the function at all), just to be safe. + */ + if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400) + { + if (*var=='@') + return global_not_volatile; + } + if (TARGET_Z80_LIKE) + { + if (var[0] == '#') + return true; + if (var[0] == '(') + return global_not_volatile; + if (strstr (var, "(bc)")) + return global_not_volatile; + if (strstr (var, "(de)")) + return global_not_volatile; + if (strstr (var, "(hl)")) + return global_not_volatile; + if (strstr (var, "(ix")) + return global_not_volatile; + if (strstr (var, "(iy")) + return global_not_volatile; + } + + if (TARGET_IS_STM8) + { + if (var[0] == '#') + return true; + if (var[0] == '(') + return global_not_volatile; + if (strstr (var, "(x)")) + return global_not_volatile; + if (strstr (var, "(y)")) + return global_not_volatile; + if (strstr (var, ", x)")) + return global_not_volatile; + if (strstr (var, ", y)")) + return global_not_volatile; + if (strstr (var, ", sp)")) + return global_not_volatile; + if (strchr (var, '[') && strchr (var, ']')) + return global_not_volatile; + if (strstr(var, "0x") || strstr(var, "0X") || isdigit(var[0])) + return global_not_volatile; + } + + if (TARGET_PDK_LIKE) + { + if (var[0] == '#') + return true; + if (!strcmp (var, "p")) + return true; + } + + /* Extract a symbol name from the variable */ + while (*vp && (*vp!='_')) + vp++; + while (*vp && (ISCHARALNUM(*vp) || *vp=='_')) + *p++ = *vp++; + *p='\0'; + + if (!symname[0]) + { + /* Nothing resembling a symbol name was found, so it can't + be volatile + */ + return true; + } + + last_ic = NULL; + for (cl = currPl; cl!=endPl->next; cl = cl->next) + { + if (cl->ic && (cl->ic!=last_ic)) + { + last_ic = cl->ic; + switch (cl->ic->op) + { + case IFX: + op = IC_COND (cl->ic); + if (IS_SYMOP (op) && + ( !strcmp(OP_SYMBOL (op)->rname, symname) || + (OP_SYMBOL (op)->isspilt && + SPIL_LOC (op) && + !strcmp(SPIL_LOC (op)->rname, symname)) )) + { + return !op->isvolatile; + } + case JUMPTABLE: + op = IC_JTCOND (cl->ic); + if (IS_SYMOP (op) && + ( !strcmp(OP_SYMBOL (op)->rname, symname) || + (OP_SYMBOL (op)->isspilt && + SPIL_LOC (op) && + !strcmp(SPIL_LOC (op)->rname, symname)) )) + { + return !op->isvolatile; + } + default: + op = IC_LEFT (cl->ic); + if (IS_SYMOP (op) && + ( !strcmp(OP_SYMBOL (op)->rname, symname) || + (OP_SYMBOL (op)->isspilt && + SPIL_LOC (op) && + !strcmp(SPIL_LOC (op)->rname, symname)) )) + { + return !op->isvolatile; + } + op = IC_RIGHT (cl->ic); + if (IS_SYMOP (op) && + ( !strcmp(OP_SYMBOL (op)->rname, symname) || + (OP_SYMBOL (op)->isspilt && + SPIL_LOC (op) && + !strcmp(SPIL_LOC (op)->rname, symname)) )) + { + return !op->isvolatile; + } + op = IC_RESULT (cl->ic); + if (IS_SYMOP (op) && + ( !strcmp(OP_SYMBOL (op)->rname, symname) || + (OP_SYMBOL (op)->isspilt && + SPIL_LOC (op) && + !strcmp(SPIL_LOC (op)->rname, symname)) )) + { + return !op->isvolatile; + } + } + } + } + + /* Couldn't find the symbol for some reason. Assume volatile if the current function touches anything volatile. */ + return global_not_volatile; +} + +/* notVolatile: + * + * This rule restriction has two different behaviours depending on + * the number of parameters given. + * + * if notVolatile (no parameters given) + * The rule is applied only if none of the iCodes originating + * the matched pattern reference a volatile operand. + * + * if notVolatile %1 ... (one or more parameters given) + * The rule is applied if the parameters are not expressions + * containing volatile symbols and are not pointer accesses. + * + */ +FBYNAME (notVolatile) +{ + int varNumber; + char *var; + char *digitend; + lineNode *cl; + operand *op; + + if (!cmdLine) + { + /* If no parameters given, just scan the iCodes for volatile operands */ + for (cl = currPl; cl!=endPl->next; cl = cl->next) + { + if (cl->ic) + { + switch (cl->ic->op) + { + case IFX: + op = IC_COND (cl->ic); + if (IS_SYMOP (op) && op->isvolatile) + return FALSE; + case JUMPTABLE: + op = IC_JTCOND (cl->ic); + if (IS_SYMOP (op) && op->isvolatile) + return FALSE; + default: + op = IC_LEFT (cl->ic); + if (IS_SYMOP (op) && op->isvolatile) + return FALSE; + op = IC_RIGHT (cl->ic); + if (IS_SYMOP (op) && op->isvolatile) + return FALSE; + op = IC_RESULT (cl->ic); + if (IS_SYMOP (op) && op->isvolatile) + return FALSE; + } + } + } + return TRUE; + } + + /* There were parameters; check the volatility of each */ + while (*cmdLine && ISCHARSPACE(*cmdLine)) + cmdLine++; + while (*cmdLine) + { + if (*cmdLine!='%') + goto error; + cmdLine++; + if (!ISCHARDIGIT(*cmdLine)) + goto error; + varNumber = strtol(cmdLine, &digitend, 10); + cmdLine = digitend; + while (*cmdLine && ISCHARSPACE(*cmdLine)) + cmdLine++; + + var = hTabItemWithKey (vars, varNumber); + + if (var) + { + if (!notVolatileVariable (var, currPl, endPl)) + return false; + } + else + { + fprintf (stderr, "*** internal error: var %d not bound" + " in peephole notVolatile rule.\n", + varNumber); + return FALSE; + } + } + + return TRUE; + +error: + fprintf (stderr, + "*** internal error: notVolatile peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; +} + +/*------------------------------------------------------------------*/ +/* setFromConditionArgs - parse a peephole condition's arguments */ +/* to produce a set of strings, one per argument. Variables %x will */ +/* be replaced with their values. String literals (in single quotes)*/ +/* are accepted and return in unquoted form. */ +/*------------------------------------------------------------------*/ +static set * +setFromConditionArgs (char *cmdLine, hTab * vars) +{ + int varNumber; + char *var; + char *digitend; + set *operands = NULL; + + if (!cmdLine) + return NULL; + + while (*cmdLine && ISCHARSPACE(*cmdLine)) + cmdLine++; + + while (*cmdLine) + { + if (*cmdLine == '%') + { + cmdLine++; + if (!ISCHARDIGIT(*cmdLine)) + goto error; + varNumber = strtol(cmdLine, &digitend, 10); + cmdLine = digitend; + + var = hTabItemWithKey (vars, varNumber); + + if (var) + { + addSetHead (&operands, var); + } + else + goto error; + } + else if (*cmdLine == '\'' ) + { + char quote = *cmdLine; + + var = ++cmdLine; + while (*cmdLine && *cmdLine != quote) + cmdLine++; + if (*cmdLine == quote) + *cmdLine++ = '\0'; + else + goto error; + addSetHead (&operands, var); + } + else + goto error; + + while (*cmdLine && ISCHARSPACE(*cmdLine)) + cmdLine++; + } + + return operands; + +error: + deleteSet (&operands); + return NULL; +} + +static const char * +operandBaseName (const char *op) +{ + if (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400) + { + if (!strcmp (op, "acc") || !strncmp (op, "acc.", 4)) + return "a"; + if (!strncmp (op, "ar", 2) && ISCHARDIGIT(*(op+2)) && !*(op+3)) + return op+1; + // bug 1739475, temp fix + if (op[0] == '@') + return operandBaseName(op+1); + } + if (TARGET_Z80_LIKE) + { + if (!strcmp (op, "d") || !strcmp (op, "e") || !strcmp (op, "(de)")) + return "de"; + if (!strcmp (op, "b") || !strcmp (op, "c") || !strcmp (op, "(bc)")) + return "bc"; + if (!strcmp (op, "h") || !strcmp (op, "l") || !strcmp (op, "(hl)") || !strcmp (op, "(hl+)") || !strcmp (op, "(hl-)")) + return "hl"; + if (!strcmp (op, "iyh") || !strcmp (op, "iyl") || strstr (op, "iy")) + return "iy"; + if (!strcmp (op, "ixh") || !strcmp (op, "ixl") || strstr (op, "ix")) + return "ix"; + if (!strcmp (op, "a")) + return "af"; + } + + return op; +} + +/*-----------------------------------------------------------------*/ +/* notUsed - Check, if value in register is not read again */ +/*-----------------------------------------------------------------*/ +FBYNAME (notUsed) +{ + const char *what; + bool ret; + + set *operands = setFromConditionArgs (cmdLine, vars); + + if (!operands || elementsInSet(operands) != 1) + { + fprintf (stderr, + "*** internal error: notUsed peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + what = setFirstItem (operands); + + if (!port->peep.notUsed) + { + fprintf (stderr, "Function notUsed not initialized in port structure\n"); + return FALSE; + } + + ret = port->peep.notUsed (what, endPl, head); + + deleteSet(&operands); + + return (ret); +} + +/*-----------------------------------------------------------------*/ +/* notUsed - Check, if value in register is not read again */ +/* starting from label */ +/*-----------------------------------------------------------------*/ +FBYNAME (notUsedFrom) +{ + const char *what, *label; + set *operands = setFromConditionArgs (cmdLine, vars); + + if (!operands || elementsInSet(operands) != 2) + { + fprintf (stderr, + "*** internal error: notUsedFrom peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + what = setFirstItem (operands); + label = setNextItem (operands); + + if (port->peep.notUsedFrom) + return port->peep.notUsedFrom (what, label, head); + + fprintf (stderr, "Function notUsed not initialized in port structure\n"); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* canAssign - Check, if we can do ld dst, src. */ +/*-----------------------------------------------------------------*/ +FBYNAME (canAssign) +{ + set *operands; + const char *dst, *src, *exotic; + + operands = setFromConditionArgs (cmdLine, vars); + + if (!operands || elementsInSet(operands) < 2 || elementsInSet(operands) > 3) + { + fprintf (stderr, + "*** internal error: canAssign peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + if(elementsInSet(operands) == 3) + { + exotic = setFirstItem (operands); + src = setNextItem (operands); + dst = setNextItem (operands); + } + else + { + exotic = 0; + src = setFirstItem (operands); + dst = setNextItem (operands); + } + + if (port->peep.canAssign) + { + bool ret = port->peep.canAssign (dst, src, exotic); + deleteSet (&operands); + return (ret); + } + + deleteSet (&operands); + + fprintf (stderr, "Function canAssign not initialized in port structure\n"); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsNotRelated - returns true if the condition's operands */ +/* are not related (taking into account register name aliases). */ +/* N-way comparison performed between all operands. */ +/*-----------------------------------------------------------------*/ +FBYNAME (operandsNotRelated) +{ + set *operands; + const char *op1, *op2; + + operands = setFromConditionArgs (cmdLine, vars); + + if (!operands) + { + fprintf (stderr, + "*** internal error: operandsNotRelated peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + while ((op1 = setFirstItem (operands))) + { + deleteSetItem (&operands, (void*)op1); + op1 = operandBaseName (op1); + + for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands)) + { + op2 = operandBaseName (op2); + if (strcmp (op1, op2) == 0) + { + deleteSet (&operands); + return FALSE; + } + } + } + + deleteSet (&operands); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* notSimilar - Check, if one is another's substring */ +/*-----------------------------------------------------------------*/ +FBYNAME (notSimilar) +{ + set *operands; + const char *op1, *op2; + + operands = setFromConditionArgs (cmdLine, vars); + + if (!operands) + { + fprintf (stderr, + "*** internal error: notSimilar peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + while ((op1 = setFirstItem (operands))) + { + deleteSetItem (&operands, (void*)op1); + + for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands)) + { + if ((strstr (op1, op2) || strstr (op2, op1)) && strcmp (op1, op2) == 0) + { + deleteSet (&operands); + return FALSE; + } + } + } + + deleteSet (&operands); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* symmParmStack - Caller readjusts stack by the number of bytes + that were pushed in all calls to this function */ +/*-----------------------------------------------------------------*/ +FBYNAME (symmParmStack) +{ + if (port->peep.symmParmStack) + return port->peep.symmParmStack(); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* notSame - Check, that arguments are pairwise not the same */ +/*-----------------------------------------------------------------*/ +FBYNAME (notSame) +{ + set *operands; + const char *op1, *op2; + + operands = setFromConditionArgs (cmdLine, vars); + + if (!operands) + { + fprintf (stderr, + "*** internal error: notSame peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + while ((op1 = setFirstItem (operands))) + { + deleteSetItem (&operands, (void*)op1); + + for (op2 = setFirstItem (operands); op2; op2 = setNextItem (operands)) + { + if (strcmp (op1, op2) == 0) + { + deleteSet (&operands); + return FALSE; + } + } + } + + deleteSet (&operands); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* same - Check if first operand matches any of the remaining */ +/*-----------------------------------------------------------------*/ +FBYNAME (same) +{ + set *operands; + const char *match, *op; + + operands = setFromConditionArgs(cmdLine, vars); + + if (!operands) + { + fprintf(stderr, + "*** internal error: same peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + operands = reverseSet(operands); + + match = setFirstItem(operands); + for (op = setNextItem(operands); op; op = setNextItem(operands)) + { + if (strcmp(match, op) == 0) + { + deleteSet(&operands); + return TRUE; + } + } + + deleteSet(&operands); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsLiteral - returns true if the condition's operands are */ +/* literals. */ +/*-----------------------------------------------------------------*/ +FBYNAME (operandsLiteral) +{ + set *operands; + const char *op; + + operands = setFromConditionArgs (cmdLine, vars); + + if (!operands) + { + fprintf (stderr, + "*** internal error: operandsLiteral peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + for (op = setFirstItem (operands); op; op = setNextItem (operands)) + { + if (!isdigit( (unsigned char)(*op) )) + { + deleteSet (&operands); + return FALSE; + } + } + + deleteSet (&operands); + return TRUE; +} + +static long * +immdGet (const char *pc, long *pl) +{ + long s = 1; + + if (!pc || !pl) + return NULL; + + // omit space + for (; ISCHARSPACE (*pc); pc++); + // parse sign + for (; !ISCHARDIGIT (*pc); pc++) + if (*pc == '-') + s *= -1; + else if (*pc == '+') + s *= +1; + else + return NULL; + + if (pc[0] == '0' && (pc[1] == 'x' || pc[1] == 'X')) + { + if (sscanf (pc + 2, "%lx", pl) != 1) + return NULL; + } + else + { + if (sscanf (pc, "%ld", pl) != 1) + return NULL; + } + + *pl *= s; + return pl; +} + +static bool +immdError (const char *info, const char *param, const char *cmd) +{ + fprintf (stderr, "*** internal error: immdInRange gets " + "%s: \"%s\" in \"%s\"\n", info, param, cmd); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* immdInRange - returns true if the sum or difference of two */ +/* immediates is in a give range. */ +/*-----------------------------------------------------------------*/ +FBYNAME (immdInRange) +{ + char r[64], operator[8]; + const char *op; + long i, j, k, h, low, high, left_l, right_l, order; + const char *padd[] = {"+", "'+'", "\"+\"", "add", "'add'", "\"add\""}; + const char *psub[] = {"-", "'-'", "\"-\"", "sub", "'sub'", "\"sub\""}; + + for (i = order = 0; order < 6;) + { + // pick up one parameter in the temp buffer r[64] + for (; ISCHARSPACE (cmdLine[i]) && cmdLine[i]; i++); + for (j = i; !ISCHARSPACE (cmdLine[j]) && cmdLine[j]; j++); + if (!cmdLine[i]) // unexpected end + return immdError ("no enough input", "", cmdLine); + else + { + for (k = i; k < j; k++) + r[k - i] = cmdLine[k]; + r[j - i] = 0; + } + // parse the string by order + switch (order) + { + case 0: // lower bound + if (!immdGet (r, &low)) + return immdError ("bad lower bound", r, cmdLine); + break; + case 1: // upper bound + if (!immdGet (r, &high)) + return immdError ("bad upper bound", r, cmdLine); + break; + case 2: // operator + if (sscanf (r, "%s", operator) != 1) + return immdError ("bad operator", r, cmdLine); + break; + case 3: // left operand + if (immdGet (r, &left_l)) // the left operand is given directly + { + } + else if (r[0] == '%') // the left operand is passed via pattern match + { + if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k))) + return immdError ("bad left operand", r, cmdLine); + else if (!immdGet (op, &left_l)) + return immdError ("bad left operand", op, r); + } + else + return immdError ("bad left operand", r, cmdLine); + break; + case 4: // right operand + if (immdGet (r, &right_l)) // the right operand is given directly + { + } + else if (r[0] == '%') // the right operand is passed via pattern match + { + if (!immdGet (r + 1, &k) || !(op = hTabItemWithKey (vars, (int) k))) + return immdError ("bad right operand", r, cmdLine); + else if (!immdGet (op, &right_l)) + return immdError ("bad right operand", op, r); + } + else + return immdError ("bad right operand", r, cmdLine); + break; + case 5: // result + if (r[0] != '%' || !immdGet (r + 1, &h)) + return immdError ("bad result container", r, cmdLine); + break; + default: // should not reach + return immdError ("unexpected input", "", cmdLine); + break; + } + order++; + i = j; + } + + // calculate + for (j = k = 0; k < sizeof (padd) / sizeof (padd[0]); k++) // add + if (strcmp (operator, padd[k]) == 0) + { + i = left_l + right_l; + j = 1; + break; + } + if (!j) + for (k = 0; k < sizeof (psub) / sizeof (psub[0]); k++) // sub + if (strcmp (operator, psub[k]) == 0) + { + i = left_l - right_l; + j = 1; + break; + } + if (!j) + return immdError ("bad operator", operator, cmdLine); + + // bind the result + if ((low <= i && i <= high) || (high <= i && i <= low)) + { + char *p[] = {r, NULL}; + sprintf (r, "%ld", i); + bindVar ((int) h, p, &vars); + return TRUE; + } + else + { + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* inSequence - Check that numerical constants are in sequence */ +/*-----------------------------------------------------------------*/ +FBYNAME (inSequence) +{ + set *operands; + const char *op; + long seq, val, stride; + + if ((operands = setFromConditionArgs(cmdLine, vars)) == NULL) + { + fprintf (stderr, + "*** internal error: inSequence peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + operands = reverseSet(operands); + + op = setFirstItem(operands); + if ((immdGet(op, &stride) == NULL) || ((op = setNextItem(operands)) == NULL)) + { + fprintf (stderr, + "*** internal error: inSequence peephole restriction" + " malformed: %s\n", cmdLine); + return FALSE; + } + + for (seq = LONG_MIN; op; op = setNextItem(operands)) + { + if ((immdGet(op, &val) == NULL) || ((seq != LONG_MIN) && (val != seq+stride))) + { + deleteSet(&operands); + return FALSE; + } + seq = val; + } + + deleteSet(&operands); + return TRUE; +} + +static const struct ftab +{ + char *fname; + int (*func) (hTab *, lineNode *, lineNode *, lineNode *, char *); +} +ftab[] = // sorted on the number of times used +{ // in the peephole rules on 2010-06-12 + { + "labelRefCount", labelRefCount // 161 + }, + { + "notVolatile", notVolatile // 118 + }, + { + "notUsed", notUsed // 96 + }, + { + "labelRefCountChange", labelRefCountChange // 86 + }, + { + "labelInRange", labelInRange // 51 + }, + { + "notSame", notSame // 28 + }, + { + "operandsNotRelated", operandsNotRelated // 28 + }, + { + "same", same // z88dk z80 + }, + { + "labelJTInRange", labelJTInRange // 13 + }, + { + "24bitMode", flat24bitMode // 9 + }, + { + "canAssign", canAssign // 8 + }, + { + "inSequence", inSequence // z88dk z80 + }, + { + "optimizeReturn", optimizeReturn // ? just a guess + }, + { + "notUsedFrom", notUsedFrom // ? just a guess + }, + { + "labelIsReturnOnly", labelIsReturnOnly // 6 + }, + { + "operandsLiteral", operandsLiteral // 6 + }, + { + "portIsDS390", portIsDS390 // 5 + }, + { + "labelIsUncondJump", labelIsUncondJump // 4 + }, + { + "deadMove", deadMove // 2 + }, + { + "useAcallAjmp", useAcallAjmp // 2 + }, + { + "xramMovcOption", xramMovcOption // 2 + }, + { + "okToRemoveSLOC", okToRemoveSLOC // 0 + }, + { + "immdInRange", immdInRange + }, + { + "notSimilar", notSimilar + }, + { + "symmParmStack", symmParmStack + }, +}; + +/*-----------------------------------------------------------------*/ +/* callFuncByName - calls a function as defined in the table */ +/*-----------------------------------------------------------------*/ +static int +callFuncByName (char *fname, + hTab *vars, + lineNode *currPl, /* first source line matched */ + lineNode *endPl, /* last source line matched */ + lineNode *head) +{ + int i; + char *cmdCopy, *funcName, *funcArgs, *cmdTerm; + char c; + int rc; + + /* Isolate the function name part (we are passed the full condition + * string including arguments) + */ + cmdTerm = cmdCopy = Safe_strdup(fname); + + do + { + funcArgs = funcName = cmdTerm; + while ((c = *funcArgs) && c != ' ' && c != '\t' && c != '(') + funcArgs++; + *funcArgs = '\0'; /* terminate the function name */ + if (c) + funcArgs++; + + /* Find the start of the arguments */ + if (c == ' ' || c == '\t') + while ((c = *funcArgs) && (c == ' ' || c == '\t')) + funcArgs++; + + /* If the arguments started with an opening parenthesis, */ + /* use the closing parenthesis for the end of the */ + /* arguments and look for the start of another condition */ + /* that can optionally follow. If there was no opening */ + /* parethesis, then everything that follows are arguments */ + /* and there can be no additional conditions. */ + if (c == '(') + { + + int num_parenthesis = 0; + cmdTerm = funcArgs; + + while ((c = *cmdTerm) && (c != ')' || num_parenthesis)) + { + if (c == '(') + num_parenthesis++; + else if (c == ')') + num_parenthesis--; + cmdTerm++; + } + *cmdTerm = '\0'; /* terminate the arguments */ + if (c == ')') + { + cmdTerm++; + while ((c = *cmdTerm) && (c == ' ' || c == '\t' || c == ',')) + cmdTerm++; + if (!*cmdTerm) + cmdTerm = NULL; + } + else + cmdTerm = NULL; /* closing parenthesis missing */ + } + else + cmdTerm = NULL; + + if (!*funcArgs) + funcArgs = NULL; + + rc = -1; + for (i = 0; i < ((sizeof (ftab)) / (sizeof (struct ftab))); i++) + { + if (strcmp (ftab[i].fname, funcName) == 0) + { + rc = (*ftab[i].func) (vars, currPl, endPl, head, funcArgs); + break; + } + } + + if (rc == -1) + { + fprintf (stderr, + "could not find named function \"%s\" in " + "peephole function table\n", + funcName); + // If the function couldn't be found, let's assume it's + // a bad rule and refuse it. + rc = FALSE; + break; + } + } + while (rc && cmdTerm); + + Safe_free(cmdCopy); + + return rc; +} + +/*-----------------------------------------------------------------*/ +/* newPeepRule - creates a new peeprule and attach it to the root */ +/*-----------------------------------------------------------------*/ +static peepRule * +newPeepRule (lineNode * match, + lineNode * replace, + char *cond, + int restart, + int barrier) +{ + peepRule *pr; + + pr = Safe_alloc ( sizeof (peepRule)); + pr->match = match; + pr->replace = replace; + pr->restart = restart; + pr->barrier = barrier; + + if (cond && *cond) + { + pr->cond = Safe_strdup (cond); + } + else + pr->cond = NULL; + + pr->vars = newHashTable (16); + + /* if root is empty */ + if (!rootRules) + rootRules = currRule = pr; + else + currRule = currRule->next = pr; + + return pr; +} + +#define SKIP_SPACE(x,y) { while (*x && (ISCHARSPACE(*x) || *x == '\n')) x++; \ + if (!*x) { fprintf(stderr,y); return ; } } + +#define EXPECT_STR(x,y,z) { while (*x && strncmp(x,y,strlen(y))) x++ ; \ + if (!*x) { fprintf(stderr,z); return ; } } +#define EXPECT_CHR(x,y,z) { while (*x && *x != y) x++ ; \ + if (!*x) { fprintf(stderr,z); return ; } } + +/*-----------------------------------------------------------------*/ +/* getPeepLine - parses the peep lines */ +/*-----------------------------------------------------------------*/ +static void +getPeepLine (lineNode ** head, const char **bpp) +{ + char lines[MAX_PATTERN_LEN]; + char *lp; + int isComment; + + lineNode *currL = NULL; + const char *bp = *bpp; + while (1) + { + + if (!*bp) + { + fprintf (stderr, "unexpected end of match pattern\n"); + return; + } + + if (*bp == '\n') + { + bp++; + while (ISCHARSPACE (*bp) || + *bp == '\n') + bp++; + } + + if (*bp == '}') + { + bp++; + break; + } + + /* read till end of line */ + lp = lines; + while ((*bp != '\n' && *bp != '}') && *bp) + *lp++ = *bp++; + *lp = '\0'; + + lp = lines; + while (*lp && ISCHARSPACE(*lp)) + lp++; + isComment = (*lp == ';'); + + if (!isComment || (isComment && !options.noPeepComments)) + { + const char *dummy1; + int dummy2; + + if (!currL) + *head = currL = newLineNode (lines); + else + currL = connectLine (currL, newLineNode (lines)); + currL->isComment = isComment; + currL->isLabel = isLabelDefinition (currL->line, &dummy1, &dummy2, + TRUE); + } + + } + + *bpp = bp; +} + +/*-----------------------------------------------------------------*/ +/* readRules - reads the rules from a string buffer */ +/*-----------------------------------------------------------------*/ +static void +readRules (const char *bp) +{ + char restart = 0, barrier = 0; + char lines[MAX_PATTERN_LEN]; + size_t safetycounter; + char *lp; + const char *rp; + lineNode *match; + lineNode *replace; + lineNode *currL = NULL; + + if (!bp) + return; +top: + restart = 0; + barrier = 0; + + /* look for the token "replace" that is the + start of a rule */ + while (*bp && strncmp (bp, "replace", 7)) + { + if (!strncmp (bp, "barrier", 7)) + barrier = 1; + bp++; + } + + /* if not found */ + if (!*bp) + return; + + /* then look for either "restart" or '{' */ + while (strncmp (bp, "restart", 7) && + *bp != '{' && bp) + bp++; + + /* not found */ + if (!*bp) + { + fprintf (stderr, "expected 'restart' or '{'\n"); + return; + } + + /* if brace */ + if (*bp == '{') + bp++; + else + { /* must be restart */ + restart++; + bp += strlen ("restart"); + /* look for '{' */ + EXPECT_CHR (bp, '{', "expected '{'\n"); + bp++; + } + + /* skip thru all the blank space */ + SKIP_SPACE (bp, "unexpected end of rule\n"); + + match = replace = currL = NULL; + /* we are the start of a rule */ + getPeepLine (&match, &bp); + + /* now look for by */ + EXPECT_STR (bp, "by", "expected 'by'\n"); + + /* then look for a '{' */ + EXPECT_CHR (bp, '{', "expected '{'\n"); + bp++; + + /* save char position (needed for generating error msg) */ + rp = bp; + + SKIP_SPACE (bp, "unexpected end of rule\n"); + getPeepLine (&replace, &bp); + + /* look for a 'if' */ + while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp) + { + ++bp; + if (*bp == '/') while (*bp && *bp != '\n') ++bp; + } + + if (strncmp (bp, "if", 2) == 0) + { + bp += 2; + while ((ISCHARSPACE (*bp) || *bp == '\n' || (*bp == '/' && *(bp+1) == '/')) && *bp) + { + bp++; + if (*bp == '/') + while (*bp && *bp != '\n') + bp++; + } + if (!*bp) + { + fprintf (stderr, "expected condition name\n"); + return; + } + + /* look for the condition */ + lp = lines; + for (safetycounter = 0; *bp && (*bp != '\n'); safetycounter++) + { + wassertl(safetycounter < MAX_PATTERN_LEN, "Peephole line too long.\n"); + *lp++ = *bp++; + } + *lp = '\0'; + + newPeepRule (match, replace, lines, restart, barrier); + } + else + { + if (*bp && strncmp (bp, "replace", 7) && strncmp (bp, "barrier", 7)) + { + /* not the start of a new peeprule, so "if" should be here */ + + char strbuff[1000]; + char *cp; + + /* go to the start of the line following "{" of the "by" token */ + while (*rp && (*rp == '\n')) + rp++; + + /* copy text of rule starting with line after "by {" */ + cp = strbuff; + while (*rp && (rp < bp) && ((cp - strbuff) < sizeof(strbuff))) + *cp++ = *rp++; + + /* and now the rest of the line */ + while (*rp && (*rp != '\n') && ((cp - strbuff) < sizeof(strbuff))) + *cp++ = *rp++; + + *cp = '\0'; + fprintf (stderr, "%s\nexpected '} if ...'\n", strbuff); + return; + } + newPeepRule (match, replace, NULL, restart, barrier); + } + goto top; + +} + +/*-----------------------------------------------------------------*/ +/* keyForVar - returns the numeric key for a var */ +/*-----------------------------------------------------------------*/ +static int +keyForVar (const char *d) +{ + int i = 0; + + while (ISCHARDIGIT (*d)) + { + i *= 10; + i += (*d++ - '0'); + } + + return i; +} + +/*-----------------------------------------------------------------*/ +/* bindVar - binds a value to a variable in the given hashtable */ +/*-----------------------------------------------------------------*/ +static void +bindVar (int key, char **s, hTab ** vtab) +{ + char vval[MAX_PATTERN_LEN]; + char *vvx; + char *vv = vval; + + /* first get the value of the variable */ + vvx = *s; + /* the value is ended by a ',' or space or newline or null or ) */ + while (*vvx && + *vvx != ',' && + !ISCHARSPACE (*vvx) && + *vvx != '\n' && + *vvx != ':' && + *vvx != ')') + { + char ubb = 0; + /* if we find a '(' then we need to balance it */ + if (*vvx == '(') + { + ubb++; + while (ubb) + { + *vv++ = *vvx++; + if (*vvx == '(') + ubb++; + if (*vvx == ')') + ubb--; + } + // include the trailing ')' + *vv++ = *vvx++; + } + else + *vv++ = *vvx++; + } + *s = vvx; + *vv = '\0'; + /* got value */ + vvx = traceAlloc (&_G.values, Safe_strdup(vval)); + + hTabAddItem (vtab, key, vvx); +} + +/*-----------------------------------------------------------------*/ +/* matchLine - matches one line */ +/*-----------------------------------------------------------------*/ +static bool +matchLine (char *s, const char *d, hTab ** vars) +{ + if (!s || !(*s)) + return FALSE; + + /* skip leading white spaces */ + while (ISCHARSPACE (*s)) + s++; + while (ISCHARSPACE (*d)) + d++; + + while (*s && *d) + { + /* skip white space in both */ + while (ISCHARSPACE(*s)) + s++; + while (ISCHARSPACE(*d)) + d++; + + /* if the destination is a var */ + if (*d == '%' && ISCHARDIGIT (*(d + 1)) && vars) + { + const char *v = hTabItemWithKey (*vars, keyForVar (d + 1)); + /* if the variable is already bound + then it MUST match with dest */ + if (v) + { + while (*v) + if (*v++ != *s++) + return FALSE; + } + else + /* variable not bound we need to bind it */ + bindVar (keyForVar (d + 1), &s, vars); + + /* in either case go past the variable */ + d++; + while (ISCHARDIGIT (*d)) + d++; + } + else if (ISCHARSPACE (*s) && ISCHARSPACE (*d)) /* whitespace sequences match any whitespace sequences */ + { + while (ISCHARSPACE (*s)) + s++; + while (ISCHARSPACE (*d)) + d++; + } + else if (*s == ',' && *d == ',') /* Allow comman to match comma followed by whitespace */ + { + s++, d++; + while (ISCHARSPACE (*s)) + s++; + while (ISCHARSPACE (*d)) + d++; + } + else if (*s && *d) /* they should be an exact match otherwise */ + { + if (*s++ != *d++) + return FALSE; + } + } + + /* skip trailing whitespaces */ + if (*s) + while (ISCHARSPACE (*s)) + s++; + + if (*d) + while (ISCHARSPACE (*d)) + d++; + + /* after all this if only one of them + has something left over then no match */ + if (*s || *d) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* matchRule - matches a all the rule lines */ +/*-----------------------------------------------------------------*/ +static bool +matchRule (lineNode * pl, + lineNode ** mtail, + peepRule * pr, + lineNode * head) +{ + lineNode *spl; /* source pl */ + lineNode *rpl; /* rule peep line */ + +/* setToNull((void *) &pr->vars); */ +/* pr->vars = newHashTable(100); */ + + /* for all the lines defined in the rule */ + rpl = pr->match; + spl = pl; + while (spl && rpl) + { + + /* if the source line starts with a ';' then + comment line don't process or the source line + contains == . debugger information skip it */ + if (spl->line && + (*spl->line == ';' || spl->isDebug)) + { + spl = spl->next; + continue; + } + + if (!matchLine (spl->line, rpl->line, &pr->vars)) + return FALSE; + + rpl = rpl->next; + if (rpl) + spl = spl->next; + } + + /* if rules ended */ + if (!rpl) + { + /* if this rule has additional conditions */ + if (pr->cond) + { + if (callFuncByName (pr->cond, pr->vars, pl, spl, head)) + { + *mtail = spl; + return TRUE; + } + else + return FALSE; + } + else + { + *mtail = spl; + return TRUE; + } + } + else + return FALSE; +} + +static void +reassociate_ic_down (lineNode *shead, lineNode *stail, + lineNode *rhead, lineNode *rtail) +{ + lineNode *csl; /* current source line */ + lineNode *crl; /* current replacement line */ + + csl = shead; + crl = rhead; + while (1) + { + /* skip over any comments */ + while (csl!=stail->next && csl->isComment) + csl = csl->next; + while (crl!=rtail->next && crl->isComment) + crl = crl->next; + + /* quit if we reach the end */ + if ((csl==stail->next) || (crl==rtail->next) || crl->ic) + break; + + if (matchLine(csl->line,crl->line,NULL)) + { + crl->ic = csl->ic; + csl = csl->next; + crl = crl->next; + } + else + break; + } +} + +static void +reassociate_ic_up (lineNode *shead, lineNode *stail, + lineNode *rhead, lineNode *rtail) +{ + lineNode *csl; /* current source line */ + lineNode *crl; /* current replacement line */ + + csl = stail; + crl = rtail; + while (1) + { + /* skip over any comments */ + while (csl!=shead->prev && csl->isComment) + csl = csl->prev; + while (crl!=rhead->prev && crl->isComment) + crl = crl->prev; + + /* quit if we reach the end */ + if ((csl==shead->prev) || (crl==rhead->prev) || crl->ic) + break; + + if (matchLine(csl->line,crl->line,NULL)) + { + crl->ic = csl->ic; + csl = csl->prev; + crl = crl->prev; + } + else + break; + } +} + +/*------------------------------------------------------------------*/ +/* reassociate_ic - reassociate replacement lines with origin iCode */ +/*------------------------------------------------------------------*/ +static void +reassociate_ic (lineNode *shead, lineNode *stail, + lineNode *rhead, lineNode *rtail) +{ + lineNode *csl; /* current source line */ + lineNode *crl; /* current replacement line */ + bool single_iCode; + iCode *ic; + + /* Check to see if all the source lines (excluding comments) came + ** for the same iCode + */ + ic = NULL; + for (csl=shead;csl!=stail->next;csl=csl->next) + if (csl->ic && !csl->isComment) + { + ic = csl->ic; + break; + } + single_iCode = (ic!=NULL); + for (csl=shead;csl!=stail->next;csl=csl->next) + if ((csl->ic != ic) && !csl->isComment) + { + /* More than one iCode was found. However, if it's just the + ** last line with the different iCode and it was not changed + ** in the replacement, everything else must be the first iCode. + */ + if ((csl==stail) && matchLine (stail->line, rtail->line, NULL)) + { + rtail->ic = stail->ic; + for (crl=rhead;crl!=rtail;crl=crl->next) + crl->ic = ic; + return; + } + + single_iCode = FALSE; + break; + } + + /* If all of the source lines came from the same iCode, then so have + ** all of the replacement lines too. + */ + if (single_iCode) + { + for (crl=rhead;crl!=rtail->next;crl=crl->next) + crl->ic = ic; + return; + } + + /* The source lines span iCodes, so we may end up with replacement + ** lines that we don't know which iCode(s) to associate with. Do the + ** best we can by using the following strategies: + ** 1) Start at the top and scan down. As long as the source line + ** matches the replacement line, they have the same iCode. + ** 2) Start at the bottom and scan up. As long as the source line + ** matches the replacement line, they have the same iCode. + ** 3) For any label in the source, look for a matching label in + ** the replacment. If found, they have the same iCode. From + ** these matching labels, scan down for additional matching + ** lines; if found, they also have the same iCode. + */ + + /* Strategy #1: Start at the top and scan down for matches + */ + reassociate_ic_down(shead,stail,rhead,rtail); + + /* Strategy #2: Start at the bottom and scan up for matches + */ + reassociate_ic_up(shead,stail,rhead,rtail); + + /* Strategy #3: Try to match labels + */ + csl = shead; + while (1) + { + /* skip over any comments */ + while (csl!=stail->next && csl->isComment) + csl = csl->next; + if (csl==stail->next) + break; + + if (csl->isLabel) + { + /* found a source line label; look for it in the replacment lines */ + crl = rhead; + while (1) + { + while (crl!=rtail->next && crl->isComment) + crl = crl->next; + if (crl==rtail->next) + break; + if (matchLine(csl->line, crl->line, NULL)) + { + reassociate_ic_down(csl,stail,crl,rtail); + break; + } + else + crl = crl->next; + } + } + csl = csl->next; + } + + /* Try to assign a meaningful iCode to any comment that is missing + one. Since they are comments, it's ok to make mistakes; we are just + trying to improve continuity to simplify other tests. + */ + ic = NULL; + for (crl=rtail;crl!=rhead->prev;crl=crl->prev) + { + if (!crl->ic && ic && crl->isComment) + crl->ic = ic; + ic = crl->ic; + } +} + + +/*-----------------------------------------------------------------*/ +/* replaceRule - does replacement of a matching pattern */ +/*-----------------------------------------------------------------*/ +static void +replaceRule (lineNode ** shead, lineNode * stail, peepRule * pr) +{ + lineNode *cl = NULL; + lineNode *pl = NULL, *lhead = NULL; + /* a long function name and long variable name can evaluate to + 4x max pattern length e.g. "mov dptr,((fie_var>>8)<<8)+fie_var" */ + char lb[MAX_PATTERN_LEN*4]; + char *lbp; + lineNode *comment = NULL; + + /* collect all the comment lines in the source */ + for (cl = *shead; cl != stail; cl = cl->next) + { + if (cl->line && (*cl->line == ';' || cl->isDebug)) + { + pl = (pl ? connectLine (pl, newLineNode (cl->line)) : + (comment = newLineNode (cl->line))); + pl->isDebug = cl->isDebug; + pl->isComment = cl->isComment || (*cl->line == ';'); + } + } + cl = NULL; + + /* for all the lines in the replacement pattern do */ + for (pl = pr->replace; pl; pl = pl->next) + { + char *v; + char *l; + lbp = lb; + + l = pl->line; + + while (*l) + { + /* if the line contains a variable */ + if (*l == '%' && ISCHARDIGIT (*(l + 1))) + { + v = hTabItemWithKey (pr->vars, keyForVar (l + 1)); + if (!v) + { + fprintf (stderr, "used unbound variable in replacement\n"); + l++; + continue; + } + while (*v) { + *lbp++ = *v++; + } + l++; + while (ISCHARDIGIT (*l)) { + l++; + } + continue; + } + *lbp++ = *l++; + } + + *lbp = '\0'; + if (cl) + cl = connectLine (cl, newLineNode (lb)); + else + lhead = cl = newLineNode (lb); + cl->isComment = pl->isComment; + cl->isLabel = pl->isLabel; + } + + /* add the comments if any to the head of list */ + if (comment) + { + lineNode *lc = comment; + while (lc->next) + lc = lc->next; + lc->next = lhead; + if (lhead) + lhead->prev = lc; + lhead = comment; + } + + if (lhead && cl) + { + /* determine which iCodes the replacment lines relate to */ + reassociate_ic(*shead,stail,lhead,cl); + + /* now we need to connect / replace the original chain */ + /* if there is a prev then change it */ + if ((*shead)->prev) + { + (*shead)->prev->next = lhead; + lhead->prev = (*shead)->prev; + } + *shead = lhead; + /* now for the tail */ + if (stail && stail->next) + { + stail->next->prev = cl; + if (cl) + cl->next = stail->next; + } + } + else + { + /* the replacement is empty - delete the source lines */ + if ((*shead)->prev) + (*shead)->prev->next = stail->next; + if (stail->next) + stail->next->prev = (*shead)->prev; + *shead = stail->next; + } +} + +/* Returns TRUE if this line is a label definition. + + * If so, start will point to the start of the label name, + * and len will be it's length. + */ +bool +isLabelDefinition (const char *line, const char **start, int *len, bool isPeepRule) +{ + const char *cp = line; + + /* This line is a label if it consists of: + * [optional whitespace] followed by identifier chars + * (alnum | $ | _ ) followed by a colon. + */ + + while (*cp && ISCHARSPACE (*cp)) + { + cp++; + } + + if (!*cp) + { + return FALSE; + } + + *start = cp; + + while (ISCHARALNUM (*cp) || (*cp == '$') || (*cp == '_') || + (isPeepRule && (*cp == '%'))) + { + cp++; + } + + if ((cp == *start) || (*cp != ':')) + { + return FALSE; + } + + *len = (cp - (*start)); + return TRUE; +} + +/* Not perfect, will not find all references yet. + Will however find references in call on Z80, which is sufficient to fix #2970351. */ +bool +isLabelReference (const char *line, const char **start, int *len) +{ + const char *s, *e; + if (!TARGET_Z80_LIKE && !TARGET_IS_STM8 && !TARGET_PDK_LIKE) + return FALSE; + + s = line; + while (ISCHARSPACE (*s)) + ++s; + + if(strncmp(s, "call", 4)) + return FALSE; + s += 4; + + while (ISCHARSPACE (*s)) + ++s; + + /* Skip condition in conditional call */ + if (strchr(s, ',')) + s = strchr(s, ',') + 1; + + e = s, *len = 0; + while(*e && !ISCHARSPACE (*e) && *e != ';') + ++e, ++(*len); + + *start = s; + + return TRUE; +} + +/* Quick & dirty string hash function. */ +static int +hashSymbolName (const char *name) +{ + int hash = 0; + + while (*name) + { + hash = (hash << 6) ^ *name; + name++; + } + + if (hash < 0) + { + hash = -hash; + } + + return hash % HTAB_SIZE; +} + +/* Build a hash of all labels in the passed set of lines + * and how many times they are referenced. + */ +static void +buildLabelRefCountHash (lineNode *head) +{ + lineNode *line; + const char *label; + int labelLen; + int i; + + assert (labelHash == NULL); + labelHash = newHashTable (HTAB_SIZE); + + /* First pass: locate all the labels. */ + for (line = head; line; line = line->next) + { + bool ref = FALSE; + /* run isLabelDefinition to: + - look for labels in inline assembler + - calculate labelLen + */ + if ((line->isLabel || line->isInline) && isLabelDefinition (line->line, &label, &labelLen, FALSE) || + (ref = TRUE) && isLabelReference (line->line, &label, &labelLen)) + { + labelHashEntry *entry, *e; + + assert (labelLen <= SDCC_NAME_MAX); + + entry = traceAlloc (&_G.labels, Safe_alloc(sizeof (labelHashEntry))); + + memcpy (entry->name, label, labelLen); + entry->name[labelLen] = 0; + entry->refCount = -1; + + for (e = hTabFirstItemWK (labelHash, hashSymbolName (entry->name)); e; e = hTabNextItemWK (labelHash)) + if (!strcmp (entry->name, e->name)) + goto c; + + /* Assume function entry points are referenced somewhere, */ + /* even if we can't find a reference (might be from outside */ + /* the function) */ + if (line->ic && (line->ic->op == FUNCTION) || ref) + entry->refCount++; + + hTabAddItem (&labelHash, hashSymbolName (entry->name), entry); + } + c:; + } + + + /* Second pass: for each line, note all the referenced labels. */ + /* This is ugly, O(N^2) stuff. Optimizations welcome... */ + for (line = head; line; line = line->next) + { + if (line->isComment) + continue; + + /* Padauk skip instructions */ + if (TARGET_PDK_LIKE && + (!strncmp(line->line, "ceqsn", 5) || !strncmp(line->line, "cneqsn", 6) || + !strncmp(line->line, "t0sn", 4) || !strncmp(line->line, "t1sn", 4) || + !strncmp(line->line, "izsn", 4) || !strncmp(line->line, "dzsn", 4))) + { + const lineNode *const l = line->next->next; + wassert (l); + if (l->isLabel && isLabelDefinition (l->line, &label, &labelLen, false)) + { + char name[SDCC_NAME_MAX]; + strcpy(name, label); + name[labelLen] = 0; + + labelHashEntry *e = hTabFirstItemWK (labelHash, hashSymbolName (name)); + if (e) + e->refCount++; + } + } + + + for (i = 0; i < HTAB_SIZE; i++) + { + labelHashEntry *thisEntry; + + thisEntry = hTabFirstItemWK (labelHash, i); + + while (thisEntry) + { + const char *s; + if ((s = strstr (line->line, thisEntry->name)) && !ISCHARALNUM (*(s + strlen (thisEntry->name))) && (s == line->line || !ISCHARALNUM (*(s - 1)))) + thisEntry->refCount++; + + thisEntry = hTabNextItemWK (labelHash); + } + } + } + +#if 0 + /* Spew the contents of the table. Debugging fun only. */ + for (i = 0; i < HTAB_SIZE; i++) + { + labelHashEntry *thisEntry; + + thisEntry = hTabFirstItemWK (labelHash, i); + + while (thisEntry) + { + fprintf (stderr, "label: %s ref %d\n", + thisEntry->name, thisEntry->refCount); + thisEntry = hTabNextItemWK (labelHash); + } + } +#endif +} + +/* How does this work? + peepHole + For each rule, + For each line, + Try to match + If it matches, + replace and restart. + + matchRule + matchLine + + Where is stuff allocated? + +*/ + +/*-----------------------------------------------------------------*/ +/* peepHole - matches & substitutes rules */ +/*-----------------------------------------------------------------*/ +void +peepHole (lineNode ** pls) +{ + lineNode *spl; + peepRule *pr; + lineNode *mtail = NULL; + bool restart, replaced; + +#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16 + /* The PIC port uses a different peep hole optimizer based on "pCode" */ + if (TARGET_PIC_LIKE) + return; +#endif + + assert(labelHash == NULL); + + do + { + restart = FALSE; + + /* for all rules */ + for (pr = rootRules; pr; pr = pr->next) + { + if (restart && pr->barrier) + break; + + for (spl = *pls; spl; spl = replaced ? spl : spl->next) + { + replaced = FALSE; + + /* if inline assembler then no peep hole */ + if (spl->isInline) + continue; + + /* don't waste time starting a match on debug symbol + ** or comment */ + if (spl->isDebug || spl->isComment || *(spl->line)==';') + continue; + + mtail = NULL; + + /* Tidy up any data stored in the hTab */ + + /* if it matches */ + if (matchRule (spl, &mtail, pr, *pls)) + { + /* restart at the replaced line */ + replaced = TRUE; + + /* then replace */ + if (spl == *pls) + { + replaceRule (pls, mtail, pr); + spl = *pls; + } + else + replaceRule (&spl, mtail, pr); + + /* if restart rule type then + start at the top again */ + if (pr->restart) + { + restart = TRUE; + } + } + + if (pr->vars) + { + hTabDeleteAll (pr->vars); + Safe_free (pr->vars); + pr->vars = NULL; + } + + freeTrace (&_G.values); + } + } + } while (restart == TRUE); + + if (labelHash) + { + hTabDeleteAll (labelHash); + freeTrace (&_G.labels); + } + labelHash = NULL; +} + + +/*-----------------------------------------------------------------*/ +/* readFileIntoBuffer - reads a file into a string buffer */ +/*-----------------------------------------------------------------*/ +static char * +readFileIntoBuffer (char *fname) +{ + FILE *f; + char *rs = NULL; + int nch = 0; + int ch; + char lb[MAX_PATTERN_LEN]; + + if (!(f = fopen (fname, "r"))) + { + fprintf (stderr, "cannot open peep rule file\n"); + return NULL; + } + + while ((ch = fgetc (f)) != EOF) + { + lb[nch++] = ch; + + /* if we maxed out our local buffer */ + if (nch >= (MAX_PATTERN_LEN - 2)) + { + lb[nch] = '\0'; + /* copy it into allocated buffer */ + if (rs) + { + rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1); + strncatz (rs, lb, strlen (rs) + strlen (lb) + 1); + } + else + { + rs = Safe_strdup (lb); + } + nch = 0; + } + } + fclose (f); + + /* if some characters left over */ + if (nch) + { + lb[nch] = '\0'; + /* copy it into allocated buffer */ + if (rs) + { + rs = Safe_realloc (rs, strlen (rs) + strlen (lb) + 1); + strncatz (rs, lb, strlen (rs) + strlen (lb) + 1); + } + else + { + rs = Safe_strdup (lb); + } + } + return rs; +} + +/*-----------------------------------------------------------------*/ +/* initPeepHole - initialises the peep hole optimizer stuff */ +/*-----------------------------------------------------------------*/ +void +initPeepHole (void) +{ + char *s; + + /* read in the default rules */ + if (!options.nopeep) + { + readRules (port->peep.default_rules); + } + + /* if we have any additional file read it too */ + if (options.peep_file) + { + readRules (s = readFileIntoBuffer (options.peep_file)); + setToNull ((void *) &s); + /* override nopeep setting, default rules have not been read */ + options.nopeep = 0; + } + +#if !OPT_DISABLE_PIC14 + /* Convert the peep rules into pcode. + NOTE: this is only support in the PIC port (at the moment) + */ + if (TARGET_IS_PIC14) + peepRules2pCode (rootRules); +#endif + +#if !OPT_DISABLE_PIC16 + /* Convert the peep rules into pcode. + NOTE: this is only support in the PIC port (at the moment) + and the PIC16 port (VR 030601) + */ + if (TARGET_IS_PIC16) + pic16_peepRules2pCode (rootRules); + +#endif +} + +/*-----------------------------------------------------------------*/ +/* StrStr - case-insensitive strstr implementation */ +/*-----------------------------------------------------------------*/ +const char * StrStr (const char * str1, const char * str2) +{ + const char * cp = str1; + const char * s1; + const char * s2; + + if ( !*str2 ) + return str1; + + while (*cp) + { + s1 = cp; + s2 = str2; + + while ( *s1 && *s2 && !(tolower(*s1)-tolower(*s2)) ) + s1++, s2++; + + if (!*s2) + return( cp ); + + cp++; + } + + return (NULL) ; +} diff --git a/src/SDCCpeeph.h b/src/SDCCpeeph.h new file mode 100644 index 0000000..aa291cd --- /dev/null +++ b/src/SDCCpeeph.h @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- + SDCCpeeph.h - Header file for The peep hole optimizer: for interpreting + the peep hole rules + + Copyright (C) 1999, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCPEEPH_H +#define SDCCPEEPH_H 1 + +#include "SDCCgen.h" + +#define MAX_PATTERN_LEN 256 + +typedef struct peepRule + { + lineNode *match; + lineNode *replace; + unsigned int restart:1; + unsigned int barrier:1; + char *cond; + hTab *vars; + struct peepRule *next; + } +peepRule; + +typedef struct + { + char name[SDCC_NAME_MAX + 1]; + int refCount; + /* needed for deadMove: */ + bool passedLabel; + int jmpToCount; + } +labelHashEntry; + +bool isLabelDefinition (const char *line, const char **start, int *len, + bool isPeepRule); + +extern hTab *labelHash; +labelHashEntry *getLabelRef (const char *label, lineNode *head); + +void initPeepHole (void); +void peepHole (lineNode **); + +const char * StrStr (const char * str1, const char * str2); + +#endif diff --git a/src/SDCCptropt.c b/src/SDCCptropt.c new file mode 100644 index 0000000..8f706dc --- /dev/null +++ b/src/SDCCptropt.c @@ -0,0 +1,420 @@ +/*------------------------------------------------------------------------- + + SDCCptropt.c - source file for pointer arithmetic Optimizations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +/*-----------------------------------------------------------------------*/ +/* findPointerGetSet - find the pointer get or set for a operand */ +/*-----------------------------------------------------------------------*/ +static iCode * +findPointerGetSet (iCode * sic, operand * op) +{ + iCode *ic = sic; + + for (; ic; ic = ic->next) + { + if ((POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) || (POINTER_GET (ic) && isOperandEqual (op, IC_LEFT (ic)))) + return ic; + + /* if we find any other usage or definition of op null */ + if (IC_RESULT (ic) && isOperandEqual (IC_RESULT (ic), op)) + return NULL; + + if (IC_RIGHT (ic) && isOperandEqual (IC_RIGHT (ic), op)) + return NULL; + + if (IC_LEFT (ic) && isOperandEqual (IC_LEFT (ic), op)) + return NULL; + + } + + return NULL; +} + +static int +pattern1 (iCode * sic, eBBlock * ebb) +{ + /* this is what we do. look for sequences like + + iTempX := _SOME_POINTER_; + iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) sic->next + _SOME_POINTER_ := iTempY; sic->next->next + either + iTempZ := @[iTempX]; sic->next->next->next + or + *(iTempX) := ..something.. sic->next->next->next + if we find this then transform this to + iTempX := _SOME_POINTER_; + either + iTempZ := @[iTempX]; + or + *(iTempX) := ..something.. + iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) + _SOME_POINTER_ := iTempY; */ + + /* sounds simple enough so let's start , here I use negative + tests all the way to return if any test fails */ + iCode *pgs, *sh, *st; + + if (!(sic->next && sic->next->next && sic->next->next->next)) + return 0; + if (sic->next->op != '+' && sic->next->op != '-') + return 0; + if (!(sic->next->next->op == '=' && !POINTER_SET (sic->next->next))) + return 0; + if (!isOperandEqual (IC_LEFT (sic->next), IC_RIGHT (sic)) || !IS_OP_LITERAL (IC_RIGHT (sic->next))) + return 0; + if (operandLitValue (IC_RIGHT (sic->next)) != getSize (operandType (IC_RIGHT (sic))->next)) + return 0; + if (!isOperandEqual (IC_RESULT (sic->next->next), IC_RIGHT (sic))) + return 0; + if (!isOperandEqual (IC_RESULT (sic->next), IC_RIGHT (sic->next->next))) + return 0; + if (!(pgs = findPointerGetSet (sic->next->next, IC_RESULT (sic)))) + return 0; + + /* found the pattern .. now do the transformation */ + sh = sic->next; + st = sic->next->next; + + /* take the two out of the chain */ + sic->next = st->next; + st->next->prev = sic; + + /* and put them after the pointer get/set icode */ + if ((st->next = pgs->next)) + st->next->prev = st; + if (!st->next) + ebb->ech = st; + pgs->next = sh; + sh->prev = pgs; + return 1; +} + +static int +pattern2 (iCode * sic, eBBlock * ebb) +{ + /* this is what we do. look for sequences like + + iTempX := _SOME_POINTER_; + iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) sic->next + iTempK := iTempY; sic->next->next + _SOME_POINTER_ := iTempK; sic->next->next->next + either + iTempZ := @[iTempX]; sic->next->next->next->next + or + *(iTempX) := ..something.. sic->next->next->next->next + if we find this then transform this to + iTempX := _SOME_POINTER_; + either + iTempZ := @[iTempX]; + or + *(iTempX) := ..something.. + iTempY := _SOME_POINTER_ + nn ; nn = sizeof (pointed to object) + iTempK := iTempY; + _SOME_POINTER_ := iTempK; */ + + /* sounds simple enough so let's start , here I use negative + tests all the way to return if any test fails */ + iCode *pgs, *sh, *st; + + if (!(sic->next && sic->next->next && sic->next->next->next && sic->next->next->next->next)) + return 0; + + /* yes I can OR them together and make one large if... but I have + simple mind and like to keep things simple & readable */ + if (!(sic->next->op == '+' || sic->next->op == '-')) + return 0; + if (!isOperandEqual (IC_RIGHT (sic), IC_LEFT (sic->next))) + return 0; + if (!IS_OP_LITERAL (IC_RIGHT (sic->next))) + return 0; + if (operandLitValue (IC_RIGHT (sic->next)) != getSize (operandType (IC_RIGHT (sic))->next)) + return 0; + if (!IS_ASSIGN_ICODE (sic->next->next)) + return 0; + if (!isOperandEqual (IC_RIGHT (sic->next->next), IC_RESULT (sic->next))) + return 0; + if (!IS_ASSIGN_ICODE (sic->next->next->next)) + return 0; + if (!isOperandEqual (IC_RIGHT (sic->next->next->next), IC_RESULT (sic->next->next))) + return 0; + if (!isOperandEqual (IC_RESULT (sic->next->next->next), IC_LEFT (sic->next))) + return 0; + if (!(pgs = findPointerGetSet (sic->next->next->next, IC_RESULT (sic)))) + return 0; + + /* found the pattern .. now do the transformation */ + sh = sic->next; + st = sic->next->next->next; + + /* take the three out of the chain */ + sic->next = st->next; + st->next->prev = sic; + + /* and put them after the pointer get/set icode */ + if ((st->next = pgs->next)) + st->next->prev = st; + if (!st->next) + ebb->ech = st; + pgs->next = sh; + sh->prev = pgs; + return 1; +} + +/*-----------------------------------------------------------------------*/ +/* ptrPostIncDecOpts - will do some pointer post increment optimizations */ +/* this will help register allocation amongst others */ +/*-----------------------------------------------------------------------*/ +void +ptrPostIncDecOpt (iCode * sic, eBBlock * ebb) +{ + if (pattern1 (sic, ebb)) + return; + pattern2 (sic, ebb); +} + +/*-----------------------------------------------------------------------*/ +/* addPattern1 - transform addition to pointer of variables */ +/*-----------------------------------------------------------------------*/ +static int +addPattern1 (iCode * ic) +{ + iCode *dic; + operand *tmp; + /* transform : + iTempAA = iTempBB + iTempCC + iTempDD = iTempAA + CONST + to + iTempAA = iTempBB + CONST + iTempDD = iTempAA + iTempCC + */ + if (!isOperandLiteral (IC_RIGHT (ic))) + return 0; + if ((dic = findBackwardDef (IC_LEFT (ic), ic->prev)) == NULL) + return 0; + if (bitVectnBitsOn (OP_SYMBOL (IC_RESULT (dic))->uses) > 1) + return 0; + if (dic->op != '+') + return 0; + tmp = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_RIGHT (dic); + IC_RIGHT (dic) = tmp; + return 1; +} + +/*-----------------------------------------------------------------------*/ +/* ptrAddition - optimize pointer additions */ +/*-----------------------------------------------------------------------*/ +int +ptrAddition (iCode * sic) +{ + if (addPattern1 (sic)) + return 1; + return 0; +} + +/*--------------------------------------------------------------------*/ +/* ptrBaseRematSym - find the base symbol of a remat. pointer */ +/*--------------------------------------------------------------------*/ +symbol * +ptrBaseRematSym (symbol * ptrsym) +{ + iCode *ric; + + if (!ptrsym->remat) + return NULL; + + ric = ptrsym->rematiCode; + while (ric) + { + if (ric->op == '+' || ric->op == '-') + ric = OP_SYMBOL (IC_LEFT (ric))->rematiCode; + else if (IS_CAST_ICODE (ric)) + ric = OP_SYMBOL (IC_RIGHT (ric))->rematiCode; + else + break; + } + + if (ric && IS_SYMOP (IC_LEFT (ric))) + return OP_SYMBOL (IC_LEFT (ric)); + else + return NULL; +} + + +/*--------------------------------------------------------------------*/ +/* ptrPseudoSymSafe - check to see if the conversion of the result of */ +/* a pointerGet of a rematerializable pointer to a pseudo symbol is */ +/* safe. Returns true if safe, or false if hazards were detected. */ +/*--------------------------------------------------------------------*/ +int +ptrPseudoSymSafe (symbol * sym, iCode * dic) +{ + symbol *ptrsym; + symbol *basesym; + iCode *ric; + iCode *ic; + int ptrsymDclType; + //int isGlobal; + + assert (POINTER_GET (dic)); + + /* Can't if spills to this symbol are prohibited */ + if (sym->noSpilLoc) + return 0; + + /* Get the pointer */ + if (!IS_SYMOP (IC_LEFT (dic))) + return 0; + ptrsym = OP_SYMBOL (IC_LEFT (dic)); + + /* Must be a rematerializable pointer */ + if (!ptrsym->remat) + return 0; + + /* The pointer type must be uncasted */ + if (IS_CAST_ICODE (ptrsym->rematiCode)) + return 0; + + /* The symbol's live range must not preceed its definition */ + if (dic->seq > sym->liveFrom) + return 0; + + /* Ok, this is a good candidate for a pseudo symbol. */ + /* However, we must check for two hazards: */ + /* 1) The symbol's live range must not include a CALL */ + /* or PCALL iCode. */ + /* 2) The symbol's live range must not include any */ + /* writes to the variable the pointer rematerializes */ + /* within (to avoid aliasing problems) */ + + /* Find the base symbol the rematerialization is based on */ + ric = ptrsym->rematiCode; + while (ric->op == '+' || ric->op == '-') + ric = OP_SYMBOL (IC_LEFT (ric))->rematiCode; + if (IS_CAST_ICODE (ric)) + return 0; + basesym = OP_SYMBOL (IC_LEFT (ric)); + + //isGlobal = !basesym->islocal && !basesym->ismyparm; + ptrsymDclType = aggrToPtrDclType (ptrsym->type, FALSE); + + ic = dic->next; + while (ic && ic->seq <= sym->liveTo) + { + if (!(SKIP_IC3 (ic) || ic->op == IFX)) + { + /* Check for hazard #1 */ + if ((ic->op == CALL || ic->op == PCALL) /* && isGlobal */ ) + { + if (ic->seq <= sym->liveTo) + return 0; + } + /* Check for hazard #2 */ + else if (POINTER_SET (ic)) + { + symbol *ptrsym2; + + if (!IS_SYMOP (IC_RESULT (ic))) + return 0; + + ptrsym2 = OP_SYMBOL (IC_RESULT (ic)); + + if (ptrsym2->remat) + { + /* Must not be the same base symbol */ + if (basesym == ptrBaseRematSym (ptrsym2)) + return 0; + } + else + { + int ptrsym2DclType = aggrToPtrDclType (ptrsym2->type, FALSE); + + /* Pointer must have no memory space in common */ + if (ptrsym2DclType == ptrsymDclType || ptrsym2DclType == GPOINTER || ptrsymDclType == GPOINTER) + return 0; + } + } + else if (IC_RESULT (ic)) + { + symbol *rsym; + + if (!IS_SYMOP (IC_RESULT (ic))) + return 0; + + rsym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure there is no conflict with another pseudo symbol */ + if (rsym->psbase == basesym) + return 0; + if (rsym->isspilt && rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + if (rsym->psbase == basesym) + return 0; + } + } + + if (ic->seq == sym->liveTo) + break; + ic = ic->next; + } + + /* If the live range went past the end of the defining basic */ + /* block, then a full analysis is too complicated to attempt */ + /* here. To be safe, we must assume the worst. */ + if (!ic) + return 0; + + /* Ok, looks safe */ + return 1; +} + +/*--------------------------------------------------------------------*/ +/* ptrPseudoSymConvert - convert the result of a pointerGet to a */ +/* pseudo symbol. The pointer must be rematerializable. */ +/*--------------------------------------------------------------------*/ +void +ptrPseudoSymConvert (symbol * sym, iCode * dic, const char *name) +{ + symbol *psym = newSymbol (name, 1); + psym->psbase = ptrBaseRematSym (OP_SYMBOL (IC_LEFT (dic))); + psym->type = sym->type; + psym->etype = psym->psbase->etype; + + strcpy (psym->rname, psym->name); + sym->isspilt = 1; + sym->usl.spillLoc = psym; +#if 0 // an alternative fix for bug #480076 + /* now this is a useless assignment to itself */ + remiCodeFromeBBlock (ebbs, dic); +#else + /* now this really is an assignment to itself, make it so; + it will be optimized out later */ + dic->op = '='; + ReplaceOpWithCheaperOp (&IC_RIGHT (dic), IC_RESULT (dic)); + IC_LEFT (dic) = NULL; +#endif +} diff --git a/src/SDCCptropt.h b/src/SDCCptropt.h new file mode 100644 index 0000000..942d477 --- /dev/null +++ b/src/SDCCptropt.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + + SDCCptropt.h - header file for pointer arithmetic Optimizations + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCPTROPT_H +#define SDCCPTROPT_H 1 + +void ptrPostIncDecOpt (iCode *, eBBlock *); +int ptrAddition (iCode *); +symbol *ptrBaseRematSym (symbol *); +int ptrPseudoSymSafe (symbol *, iCode *); +void ptrPseudoSymConvert (symbol *, iCode *, const char *); + +#endif diff --git a/src/SDCCralloc.hpp b/src/SDCCralloc.hpp new file mode 100644 index 0000000..616aab6 --- /dev/null +++ b/src/SDCCralloc.hpp @@ -0,0 +1,1337 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2013 +// +// (c) 2010-2012 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// An optimal, polynomial-time register allocator. +// +// For details, see: +// +// Philipp Klaus Krause, +// "Optimal Register Allocation in Polynomial Time", +// Compiler Construction - 22nd International Conference, CC 2013, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2013. Proceedings, +// Lecture Notes in Computer Science, volume 7791, pp. 1-20. +// Springer, +// 2013. +// +// To use this from a port do the following: +// +// 1) Supply a cost function +// template +// float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I); +// Which can range from +// simple, e.g. cost 1 for each byte accessed in a register, cost 4 for each byte accessed in memory +// to +// quite involved, e.g. the number of bytes of code the code generator would generate. +// +// 2) Call +// create_cfg(), thorup_tree_decomposition(), nicify(), alive_tree_dec(), tree_dec_ralloc_nodes(). +// +// The Z80 port can serve as an example, see z80_ralloc2_cc() in z80/ralloc2.cc. + +#ifndef SDCCRALLOC_HH +#define SDCCRALLOC_HH 1 + +#include +#include +#include +#include +#include + +// Workaround for boost bug #11880 +#include +#if BOOST_VERSION == 106000 + #include +#endif + +#include +#include +#include +#include +#include + +#include "common.h" + +extern "C" +{ +#include "SDCCbtree.h" +} + +typedef short int var_t; +typedef signed char reg_t; + +// Integer constant upper bound on port->num_regs +#define MAX_NUM_REGS 9 + +// Assignment at an instruction +struct i_assignment_t +{ + var_t registers[MAX_NUM_REGS][2]; + + i_assignment_t(void) + { + for (reg_t r = 0; r < MAX_NUM_REGS; r++) + for (unsigned int i = 0; i < 2; i++) + registers[r][i] = -1; + } + +#if 0 + bool operator<(const i_assignment_t &i_a) const + { + for (reg_t r = 0; r < port->num_regs; r++) + for (unsigned int i = 0; i < 2; i++) + { + if (registers[r][i] < i_a.registers[r][i]) + return(true); + else if (registers[r][i] > i_a.registers[r][i]) + return(false); + } + return(false); + } +#endif + + void add_var(var_t v, reg_t r) + { + if (registers[r][1] < v) + { + registers[r][0] = registers[r][1]; + registers[r][1] = v; + } + else + registers[r][0] = v; + } + + void remove_var(var_t v) + { + for (reg_t r = 0; r < port->num_regs; r++) + { + if (registers[r][1] == v) + { + registers[r][1] = registers[r][0]; + registers[r][0] = -1; + } + else if (registers[r][0] == v) + registers[r][0] = -1; + } + } +}; + +typedef std::vector varset_t; // Faster than std::set, std::tr1::unordered_set and stx::btree_set here. + +typedef boost::container::flat_map icosts_t; // Faster than std::map and stx::btree_map here. + +typedef std::vector cfg_alive_t; // Faster than stx::btree_set here . +typedef boost::container::flat_set cfg_dying_t; // Faster than stx::btree_set and std::set here. + +struct assignment +{ + float s; + + varset_t local; // Entries: var + std::vector global; // Entries: global[var] = reg (-1 if no reg assigned) + icosts_t i_costs; // Costs for all instructions in bag (needed to avoid double counting costs at join nodes) + i_assignment_t i_assignment; // Assignment at the instruction currently being added in an introduce node; + + bool marked; + + bool operator<(const assignment& a) const + { + varset_t::const_iterator i, ai, i_end, ai_end; + + i_end = local.end(); + ai_end = a.local.end(); + + for (i = local.begin(), ai = a.local.begin();; ++i, ++ai) + { + if (i == i_end && ai == ai_end) + return(false); + if (i == i_end) + return(true); + if (ai == ai_end) + return(false); + + if (*i < *ai) + return(true); + if (*i > *ai) + return(false); + + if (global[*i] < a.global[*ai]) + return(true); + if (global[*i] > a.global[*ai]) + return(false); + } + } +}; + +typedef std::list assignment_list_t; +//typedef std::vector assignment_list_t; // Probably faster, but would require some code reorganization. + +struct tree_dec_node +{ + std::set bag; + std::set alive; + assignment_list_t assignments; + unsigned weight; // The weight is the number of nodes at which intermediate results need to be remembered. In general, to minimize memory consumption, at join nodes the child with maximum weight should be processed first. +}; + +typedef boost::container::flat_multimap operand_map_t; // Faster than std::multimap and stx::btree_multimap here. + +struct cfg_node +{ + iCode *ic; + operand_map_t operands; + cfg_alive_t alive; + cfg_dying_t dying; + + std::set stack_alive; + +#ifdef DEBUG_SEGV + cfg_node(void); +#endif +}; + +#ifdef DEBUG_SEGV +// This only exists to track down #3506333 and #3475617. +bool default_constructor_of_cfg_node_called; +cfg_node::cfg_node(void) +{ + default_constructor_of_cfg_node_called = true; +} +#endif + +struct con_node +{ + int v; + int byte; + int size; + char *name; +}; + +typedef boost::adjacency_list tree_dec_t; +typedef boost::adjacency_list con_t; +typedef boost::adjacency_matrix con2_t; +typedef boost::adjacency_list cfg_t; +typedef boost::adjacency_list cfg_sym_t; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +TREEDEC_TREEDEC_BAG_TRAITS(tree_dec_t, bag); +#endif + +#include "SDCCtree_dec.hpp" + +// Cost function. Port-specific. +template +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I); + +// For early removel of assignments that cannot be extended to valid assignments. Port-specific. +template +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar); + +// Rough cost estimate. Port-specific. +template +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I); + +// Avoid overwriting operands that are still needed by the result. Port-specific. +template +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I); + +// Port-specific +template +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T); + +// Code for another ic is generated when generating this one. Mark the other as generated. Port-specific. +static void extra_ic_generated(iCode *ic); + +inline void +add_operand_to_cfg_node(cfg_node &n, operand *o, std::map, var_t> &sym_to_index) +{ + reg_t k; + if (o && IS_SYMOP(o) && sym_to_index.find(std::pair(OP_SYMBOL_CONST(o)->key, 0)) != sym_to_index.end()) + { + if (n.operands.find(OP_SYMBOL_CONST(o)->key) == n.operands.end()) + for (k = 0; k < OP_SYMBOL_CONST(o)->nRegs; k++) + n.operands.insert(std::pair(OP_SYMBOL_CONST(o)->key, sym_to_index[std::pair(OP_SYMBOL_CONST(o)->key, k)])); + } +} + +// Check if the live-range of variable i is connected +#if 0 +// This check was too expensive - Profiling shows that compiling the Dhrystone benchmark for stm8 with default options, we spent about a quarter of compiler runtime in here! +// Profiling shows that we spent a significant amount of time on the first call to copy_graph() +// Todo: Improve efficiency, e.g. using subgraph or filtered_graph to avoid the costly first call to copy_graph() +// Issues to solve: cfg2 is undirected, cfg is bidirectional; this makes use of subgraph or filtered_graph harder. +static bool liverange_connected(cfg_t &cfg, var_t i) +{ + cfg_sym_t cfg2; + boost::copy_graph(cfg, cfg2, boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); // This call to copy_graph is expensive! + for (int j = boost::num_vertices(cfg) - 1; j >= 0; j--) + { + if (std::find(cfg[j].alive.begin(), cfg[j].alive.end(), i) == cfg[j].alive.end()) + { + boost::clear_vertex(j, cfg2); + boost::remove_vertex(j, cfg2); + } + } + + std::vector::vertices_size_type> component(num_vertices(cfg2)); + + return(boost::connected_components(cfg2, &component[0]) <= 1); +} +#else +// A not very elegant, but faster check +static inline int component_size_impl(const cfg_t &cfg, const std::vector &life, var_t v, int i, std::vector& visited) +{ + typename boost::graph_traits::in_edge_iterator in, in_end; + typename boost::graph_traits::out_edge_iterator out, out_end; + + int size = 1; + visited[i] = true; + + for(boost::tie(in, in_end) = boost::in_edges(i, cfg); in != in_end; ++in) + if(life[boost::source(*in, cfg)] && !visited[boost::source(*in, cfg)]) + size += component_size_impl(cfg, life, v, boost::source(*in, cfg), visited); + + for(boost::tie(out, out_end) = boost::out_edges(i, cfg); out != out_end; ++out) + if(life[boost::target(*out, cfg)] && !visited[boost::target(*out, cfg)]) + size += component_size_impl(cfg, life, v, boost::target(*out, cfg), visited); + + return(size); +} + +static inline int component_size(const cfg_t &cfg, const std::vector &life, var_t v, int i) +{ + std::vector visited(boost::num_vertices(cfg)); + + return(component_size_impl(cfg, life, v, i, visited)); +} + +static bool liverange_connected(const cfg_t &cfg, var_t v) +{ + std::vector life(boost::num_vertices(cfg)); + int num_life = 0; + int last_life; + + for(int i = 0; i < boost::num_vertices (cfg); i++) + if(std::find(cfg[i].alive.begin(), cfg[i].alive.end(), v) != cfg[i].alive.end()) + { + life[i] = true; + num_life++; + last_life = i; + } + + if(!num_life) + return(true); + + return(component_size(cfg, life, v, last_life) >= num_life); +} +#endif + +// A quick-and-dirty function to get the CFG from sdcc. +static iCode * +create_cfg(cfg_t &cfg, con_t &con, ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + iCode *start_ic; + iCode *ic; + + std::map key_to_index; + std::map, var_t> sym_to_index; + + if(currFunc) + currFunc->funcDivFlagSafe = 1; + + start_ic = iCodeLabelOptimize(iCodeFromeBBlock (ebbs, ebbi->count)); + { + int i; + var_t j; + wassertl (!boost::num_vertices(cfg), "CFG non-empty before creation."); + for (ic = start_ic, i = 0, j = 0; ic; ic = ic->next, i++) + { + if (currFunc) + currFunc->funcDivFlagSafe &= !(ic->op == INLINEASM || ic->op == '/' || ic->op == '%' || ic->op == PCALL || + ic->op == CALL && (IS_OP_LITERAL (IC_LEFT (ic)) || !OP_SYMBOL(IC_LEFT (ic))->funcDivFlagSafe) || + ic->op == RIGHT_OP && IS_OP_LITERAL (IC_RIGHT (ic))); // Right shift might be implemented using division. + +#ifdef DEBUG_SEGV + default_constructor_of_cfg_node_called = false; +#endif + boost::add_vertex(cfg); + +#ifdef DEBUG_SEGV + wassertl (default_constructor_of_cfg_node_called, "add_vertex failed to call default constructor of cfg_node!"); +#endif + wassertl (cfg[i].alive.empty(), "Alive set non-empty upon creation."); + key_to_index[ic->key] = i; + + if(ic->op == SEND && ic->builtinSEND) // Ensure that only the very first send iCode is active. + { + operand *bi_parms[MAX_BUILTIN_ARGS]; + int nbi_parms; + getBuiltinParms(ic, &nbi_parms, bi_parms); + } + + extra_ic_generated(ic); + + cfg[i].ic = ic; + ic->rSurv = newBitVect(port->num_regs); // Never freed. Memory leak? + ic->rMask = newBitVect(port->num_regs); // Never freed. Memory leak? + + if (ic->generated) + continue; + + for (int j2 = 0; j2 <= operandKey; j2++) + { + if (bitVectBitValue(ic->rlive, j2)) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, j2)); + + if (!sym->for_newralloc) + continue; + + // Add node to conflict graph: + if (sym_to_index.find(std::pair(j2, 0)) != sym_to_index.end()) + continue; + + // Other parts of the allocator may rely on the variables corresponding to bytes from the same sdcc variable to have subsequent numbers. + for (reg_t k = 0; k < sym->nRegs; k++) + { + boost::add_vertex(con); + con[j].v = j2; + con[j].byte = k; + con[j].size = sym->nRegs; + con[j].name = sym->name; + sym_to_index[std::pair(j2, k)] = j; + for (reg_t l = 0; l < k; l++) + boost::add_edge(j - l - 1, j, con); + j++; + } + } + } + } + } + + // Get control flow graph from sdcc. + for (ic = start_ic; ic; ic = ic->next) + { + wassertl (key_to_index[ic->key] < boost::num_vertices(cfg), "Node not in CFG."); + + if (ic->op != GOTO && ic->op != RETURN && ic->op != JUMPTABLE && ic->next) + { + wassertl (key_to_index[ic->next->key] < boost::num_vertices(cfg), "Next node not in CFG."); + boost::add_edge(key_to_index[ic->key], key_to_index[ic->next->key], cfg); + } + + if (ic->op == GOTO) + { + wassertl (key_to_index[eBBWithEntryLabel(ebbi, ic->label)->sch->key] < boost::num_vertices(cfg), "GOTO target not in CFG."); + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, ic->label)->sch->key], cfg); + } + else if (ic->op == RETURN) + { + wassertl (key_to_index[eBBWithEntryLabel(ebbi, returnLabel)->sch->key] < boost::num_vertices(cfg), "RETURN target not in CFG."); + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, returnLabel)->sch->key], cfg); + } + else if (ic->op == IFX) + { + wassertl (key_to_index[eBBWithEntryLabel(ebbi, IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->sch->key] < boost::num_vertices(cfg), "IFX target not in CFG."); + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, IC_TRUE(ic) ? IC_TRUE(ic) : IC_FALSE(ic))->sch->key], cfg); + } + else if (ic->op == JUMPTABLE) + for (symbol *lbl = (symbol *)(setFirstItem (IC_JTLABELS (ic))); lbl; lbl = (symbol *)(setNextItem (IC_JTLABELS (ic)))) + { + wassertl (key_to_index[eBBWithEntryLabel(ebbi, lbl)->sch->key] < boost::num_vertices(cfg), "GOTO target not in CFG."); + boost::add_edge(key_to_index[ic->key], key_to_index[eBBWithEntryLabel(ebbi, lbl)->sch->key], cfg); + } + + for (int i = 0; i <= operandKey; i++) + { + if (sym_to_index.find(std::pair(i, 0)) == sym_to_index.end()) + continue; + + if (bitVectBitValue(ic->rlive, i)) + { + symbol *isym = (symbol *)(hTabItemWithKey(liveRanges, i)); + for (reg_t k = 0; k < isym->nRegs; k++) + { + wassert (key_to_index.find(ic->key) != key_to_index.end()); + wassert (sym_to_index.find(std::pair(i, k)) != sym_to_index.end()); + wassertl (key_to_index[ic->key] < boost::num_vertices(cfg), "Node not in CFG."); + cfg[key_to_index[ic->key]].alive.push_back(sym_to_index[std::pair(i, k)]); + } + + // TODO: Move this to a place where it also works when using the old allocator! + isym->block = btree_lowest_common_ancestor(isym->block, ic->block); + // If this symbol has a spill location, ensure the spill location is also allocated in a compatible block + if (SYM_SPIL_LOC(isym)) + SYM_SPIL_LOC(isym)->block = btree_lowest_common_ancestor(SYM_SPIL_LOC(isym)->block, isym->block); + } + } + + if (ic->op == IFX) + add_operand_to_cfg_node(cfg[key_to_index[ic->key]], IC_COND(ic), sym_to_index); + else if (ic->op == JUMPTABLE) + add_operand_to_cfg_node(cfg[key_to_index[ic->key]], IC_JTCOND(ic), sym_to_index); + else + { + add_operand_to_cfg_node(cfg[key_to_index[ic->key]], IC_RESULT(ic), sym_to_index); + add_operand_to_cfg_node(cfg[key_to_index[ic->key]], IC_LEFT(ic), sym_to_index); + add_operand_to_cfg_node(cfg[key_to_index[ic->key]], IC_RIGHT(ic), sym_to_index); + } + + // TODO: Extend live-ranges of returns of built-in function calls back to first SEND. + + add_operand_conflicts_in_node(cfg[key_to_index[ic->key]], con); + } + +#if 0 + // Get conflict graph from sdcc + for (var_t i = 0; static_cast::vertices_size_type>(i) < num_vertices(con); i++) + { + symbol *isym = (symbol *)(hTabItemWithKey(liveRanges, con[i].v)); + for (int j = 0; j <= operandKey; j++) + if (bitVectBitValue(isym->clashes, j)) + { + symbol *jsym = (symbol *)(hTabItemWithKey(liveRanges, j)); + if (sym_to_index.find(std::pair(j, 0)) == sym_to_index.end()) + continue; + for (reg_t k = 0; k < jsym->nRegs; k++) + boost::add_edge(i, sym_to_index[std::pair(j, k)], con); + } + } +#endif + + // Check for unconnected live ranges, some might have survived earlier stages. + for (var_t i = (var_t)boost::num_vertices(con) - 1; i >= 0; i--) + if (!liverange_connected(cfg, i)) + { + // Non-connected CFGs are created by at least GCSE and lospre. We now have a live-range splitter that fixes them, so this should no longer be necessary, but we leave this code here for now, so in case one gets through, we can still generate correct code. + std::cerr << "Warning: Non-connected liverange found and extended to connected component of the CFG:" << con[i].name << ". Please contact sdcc authors with source code to reproduce.\n"; + + cfg_sym_t cfg2; + boost::copy_graph(cfg, cfg2, boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); + std::vector::vertices_size_type> component(num_vertices(cfg2)); + boost::connected_components(cfg2, &component[0]); + + for (boost::graph_traits::vertices_size_type j = 0; j < boost::num_vertices(cfg) - 1; j++) + { + if (std::find(cfg[j].alive.begin(), cfg[j].alive.end(), i) == cfg[j].alive.end()) + continue; + + for (boost::graph_traits::vertices_size_type k = 0; k < boost::num_vertices(cfg) - 1; k++) + if (component[j] == component[k] && std::find(cfg[k].alive.begin(), cfg[k].alive.end(), i) == cfg[k].alive.end()) + cfg[k].alive.push_back(i); + } + } + + // Sort alive and setup dying. + for (boost::graph_traits::vertices_size_type i = 0; i < num_vertices(cfg); i++) + { + std::sort(cfg[i].alive.begin(), cfg[i].alive.end()); + cfg[i].dying = cfg_dying_t(cfg[i].alive.begin(), cfg[i].alive.end());; + typedef boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t j, j_end; + for (boost::tie(j, j_end) = adjacent_vertices(i, cfg); j != j_end; ++j) + { + cfg_alive_t::const_iterator v, v_end; + for (v = cfg[*j].alive.begin(), v_end = cfg[*j].alive.end(); v != v_end; ++v) + { + const symbol *const vsym = (symbol *)(hTabItemWithKey(liveRanges, con[*v].v)); + + const operand *const left = IC_LEFT(cfg[*j].ic); + const operand *const right = IC_RIGHT(cfg[*j].ic); + const operand *const result = IC_RESULT(cfg[*j].ic); + + if (!(POINTER_SET(cfg[*j].ic) || cfg[*j].ic->op == SET_VALUE_AT_ADDRESS) && + (!left || !IS_SYMOP(left) || OP_SYMBOL_CONST(left)->key != vsym->key) && + (!right || !IS_SYMOP(right) || OP_SYMBOL_CONST(right)->key != vsym->key) && + result && IS_SYMOP(result) && OP_SYMBOL_CONST(result)->key == vsym->key) + continue; + + cfg[i].dying.erase(*v); + } + } + } + + // Construct conflict graph + for (boost::graph_traits::vertices_size_type i = 0; i < num_vertices(cfg); i++) + { + cfg_alive_t::const_iterator v, v_end; + const iCode *ic = cfg[i].ic; + + for (v = cfg[i].alive.begin(), v_end = cfg[i].alive.end(); v != v_end; ++v) + { + cfg_alive_t::const_iterator v2, v2_end; + + // Conflict between operands are handled by add_operand_conflicts_in_node(). + if (cfg[i].dying.find (*v) != cfg[i].dying.end()) + continue; + if (ic->op != IFX && ic->op != JUMPTABLE && IC_RESULT(ic) && IS_SYMOP(IC_RESULT(ic))) + { + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = cfg[i].operands.equal_range(OP_SYMBOL_CONST(IC_RESULT(ic))->key); oi != oi_end; ++oi) + if(oi->second == *v) + goto next_var; + } + + // Here, v is a variable that survives cfg[i]. + // TODO: Check if we can use v, ++v2 instead of cfg[i].alive.begin() to speed things up. + for (v2 = cfg[i].alive.begin(), v2_end = cfg[i].alive.end(); v2 != v2_end; ++v2) + { + if(*v == *v2) + continue; + if (cfg[i].dying.find (*v2) != cfg[i].dying.end()) + continue; + + boost::add_edge(*v, *v2, con); + } + + next_var: + ; + } + } + + return(start_ic); +} + +// Computes live ranges for tree decomposition from live ranges from cfg. +inline void alive_tree_dec(tree_dec_t &tree_dec, const cfg_t &cfg) +{ + for (unsigned int i = 0; i < num_vertices(tree_dec); i++) + { + std::set::const_iterator v; + for (v = tree_dec[i].bag.begin(); v != tree_dec[i].bag.end(); ++v) + tree_dec[i].alive.insert(cfg[*v].alive.begin(), cfg[*v].alive.end()); + } +} + +#if defined(DEBUG_RALLOC_DEC) || defined (DEBUG_RALLOC_DEC_ASS) +static void print_assignment(const assignment &a) +{ + varset_t::const_iterator i; + std::cout << "["; + for (i = a.local.begin(); i != a.local.end(); ++i) + std::cout << "(" << int(*i) << ", " << int(a.global[*i]) << "), "; + std::cout << "c: " << a.s << "]"; +} +#endif + +template +bool assignment_conflict(const assignment &a, const I_t &I, var_t v, reg_t r) +{ + varset_t::const_iterator i, i_end; + + for (i = a.local.begin(), i_end = a.local.end(); i != i_end; ++i) + { + if (a.global[*i] != r) + continue; + if (boost::edge(*i, v, I).second) + return(true); + } + + return(false); +} + +template +void assignments_introduce_instruction(assignment_list_t &alist, unsigned short int i, const G_t &G) +{ + assignment_list_t::iterator ai, ai_end; + +#if !defined(_MSC_VER) // Efficient code - reduces total SDCC runtime by about 5.5% vs. code below, but doesn't work with MSVC++ (at least up to MSVC++ 2015) + struct inserter_t + { + explicit inserter_t(const std::vector& g, i_assignment_t& a) : global(g), ia(a) + { + } + inserter_t& operator=(var_t v) + { + if (global[v] >= 0) + ia.add_var(v, global[v]); + return(*this); + } + inserter_t& operator*() + { + return(*this); + } + inserter_t& operator++() + { + return(*this); + } + inserter_t& operator++(int i) + { + i; + return(*this); + } + private: + const std::vector& global; + i_assignment_t& ia; + }; + + for (ai = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) + { + i_assignment_t ia; + + std::set_intersection(ai->local.begin(), ai->local.end(), G[i].alive.begin(), G[i].alive.end(), inserter_t(ai->global, ia)); + + ai->i_assignment = ia; + } +#else // Human-readable code + for (ai = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) + { + varset_t i_variables; + + std::set_intersection(ai->local.begin(), ai->local.end(), G[i].alive.begin(), G[i].alive.end(), std::inserter(i_variables, i_variables.end())); + + i_assignment_t ia; + + varset_t::const_iterator v, v_end; + + for (v = i_variables.begin(), v_end = i_variables.end(); v != v_end; ++v) + if (ai->global[*v] >= 0) + ia.add_var(*v, ai->global[*v]); + + ai->i_assignment = ia; + } +#endif +} + +template +static void assignments_introduce_variable(assignment_list_t &alist, unsigned short int i, short int v, const G_t &G, const I_t &I) +{ + assignment_list_t::iterator ai; + bool a_initialized; + assignment a; + size_t c, c_end; + + for (ai = alist.begin(), c = 0, c_end = alist.size(); c < c_end; c++, ai++) + { + a_initialized = false; + + for (reg_t r = 0; r < port->num_regs; r++) + { + if (!assignment_conflict(*ai, I, v, r)) + { + if(!a_initialized) + { + a = *ai; + ai->marked = true; + a.marked = false; + varset_t::iterator i = std::lower_bound(a.local.begin(), a.local.end(), v); + if (i == a.local.end() || *i != v) + a.local.insert(i, v); + } + a.global[v] = r; + a.i_assignment.add_var(v, r); + if(!assignment_hopeless(a, i, G, I, v)) + alist.push_back(a); + a.i_assignment.remove_var(v); + } + } + } +} + +struct assignment_rep +{ + assignment_list_t::iterator i; + float s; + + bool operator<(const assignment_rep& a) const + { + return(s < a.s); + } +}; + +template +float compability_cost(const assignment& a, const assignment& ac, const I_t &I) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + float c = 0.0f; + + varset_t::const_iterator vi, vi_end; + + for(vi = ac.local.begin(), vi_end = ac.local.end(); vi != vi_end; ++vi) + { + const var_t v = *vi; + if(a.global[v] != ac.global[v]) + { + c += 1000.0f; + continue; + } +#if 0 // This improves the quality of assignments, but it has a big runtime overhead for some cases. + adjacency_iter_t j, j_end; + for (boost::tie(j, j_end) = adjacent_vertices(v, I); j != j_end; ++j) + if(ac.global[v] != -1 && a.global[*j] == ac.global[v]) + { + c += 1000.0f; + break; + } +#endif + } + + return(c); +} + +// Ensure that we never get more than options.max_allocs_per_node assignments at a single node of the tree decomposition. +// Tries to drop the worst ones first (but never drop the empty assignment, as it's the only one guaranteed to be always valid). +template +static void drop_worst_assignments(assignment_list_t &alist, unsigned short int i, const G_t &G, const I_t &I, const assignment& ac, bool *const assignment_optimal) +{ + unsigned int n; + size_t alist_size; + assignment_list_t::iterator ai, an; + + if ((alist_size = alist.size()) * port->num_regs <= static_cast(options.max_allocs_per_node) || alist_size <= 1) + return; + + *assignment_optimal = false; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Too many assignments here (" << i << "):" << alist_size << " > " << options.max_allocs_per_node / port->num_regs << ". Dropping some.\n"; std::cout.flush(); +#endif + +#if 0 + assignment_rep *arep = new assignment_rep[alist_size]; + + for (n = 0, ai = alist.begin(); n < alist_size; ++ai, n++) + { + arep[n].i = ai; + arep[n].s = ai->s + rough_cost_estimate(*ai, i, G, I) + compability_cost(*ai, ac, I); + } + + std::nth_element(arep + 1, arep + options.max_allocs_per_node / port->num_regs, arep + alist_size); + + //std::cout << "nth elem. est. cost: " << arep[options.max_allocs_per_node / port->num_regs].s << "\n"; std::cout.flush(); + + for (n = options.max_allocs_per_node / port->num_regs + 1; n < alist_size; n++) + alist.erase(arep[n].i); +#else // More efficient, reduces total SDCC runtime by about 1%. + + size_t endsize = options.max_allocs_per_node / port->num_regs + 1; + size_t arep_maxsize = std::min(alist_size, endsize * 2) + 1; + size_t m, k; + float bound = std::numeric_limits::infinity(); + + assignment_rep *arep = new assignment_rep[arep_maxsize]; + + for(m = 0, n = 1, ai = alist.begin(), ++ai; n < alist_size; n++) + { + float s = ai->s; + + if(s > bound) + { + alist.erase(ai++); + continue; + } + s += compability_cost(*ai, ac, I); + if(s > bound) + { + alist.erase(ai++); + continue; + } + s += rough_cost_estimate(*ai, i, G, I); + if(s > bound) + { + alist.erase(ai++); + continue; + } + + if(m >= arep_maxsize - 1) + { + std::nth_element(arep, arep + (endsize - 1), arep + m); + for(k = endsize; k < m; k++) + alist.erase(arep[k].i); + bound = arep[endsize - 1].s; + + m = endsize; + } + + arep[m].i = ai; + arep[m].s = s; + + m++; + + ++ai; + } + + std::nth_element(arep, arep + (endsize - 1), arep + m); + + for (n = endsize; n < m; n++) + alist.erase(arep[n].i); +#endif + + delete[] arep; +} + +// Handle Leaf nodes in the nice tree decomposition +template +static void tree_dec_ralloc_leaf(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G, const I_t &I) +{ +#ifdef DEBUG_RALLOC_DEC + std::cout << "Leaf (" << t << "):\n"; std::cout.flush(); +#endif + + assignment a; + assignment_list_t &alist = T[t].assignments; + + a.s = 0; + a.global.resize(boost::num_vertices(I), -1); + alist.push_back(a); + +#ifdef DEBUG_RALLOC_DEC_ASS + assignment_list_t::iterator ai; + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai); + std::cout << "\n"; + } + assignment best; + get_best_local_assignment(best, t, T); + std::cout << "Best: "; print_assignment(best); std::cout << "\n"; +#endif +} + +// Handle introduce nodes in the nice tree decomposition +template +static void tree_dec_ralloc_introduce(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G, const I_t &I, const assignment& ac, bool *const assignment_optimal) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + assignment_list_t::iterator ai; + boost::tie(c, c_end) = adjacent_vertices(t, T); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Introduce (" << t << "):\n"; std::cout.flush(); + std::cout << "ac: "; print_assignment(ac); std::cout << "\n"; +#endif + + assignment_list_t &alist = T[t].assignments; + + std::swap(alist, T[*c].assignments); + + std::set new_vars; + std::set_difference(T[t].alive.begin(), T[t].alive.end(), T[*c].alive.begin(), T[*c].alive.end(), std::inserter(new_vars, new_vars.end())); + + std::set new_inst; + std::set_difference(T[t].bag.begin(), T[t].bag.end(), T[*c].bag.begin(), T[*c].bag.end(), std::inserter(new_inst, new_inst.end())); + unsigned short int i = *(new_inst.begin()); + + // Extend to new instruction. + assignments_introduce_instruction(alist, i, G); + + std::set::const_iterator v; + for (v = new_vars.begin(); v != new_vars.end(); ++v) + { + drop_worst_assignments(alist, i, G, I, ac, assignment_optimal); + assignments_introduce_variable(alist, i, *v, G, I); + } + + // Summation of costs and early removal of assignments. + for (ai = alist.begin(); ai != alist.end();) + { + if ((ai->s += (ai->i_costs[i] = instruction_cost(*ai, i, G, I))) == std::numeric_limits::infinity()) + ai = alist.erase(ai); + else + ++ai; + } + + // Free memory in the std::set > that live in the assignments in the list. + //boost::singleton_pool::release_memory(); + +#ifdef DEBUG_RALLOC_DEC_ASS + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai); + std::cout << "\n"; + } + + assignment best; + get_best_local_assignment(best, t, T); + std::cout << "Best: "; print_assignment(best); std::cout << "\n"; +#endif +} + +static bool assignments_locally_same(const assignment &a1, const assignment &a2) +{ + if (a1.local != a2.local) + return(false); + + varset_t::const_iterator i, i_end; + for (i = a1.local.begin(), i_end = a1.local.end(); i != i_end; ++i) + if (a1.global[*i] != a2.global[*i]) + return(false); + + return(true); +} + +// Handle forget nodes in the nice tree decomposition +template +static void tree_dec_ralloc_forget(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G, const I_t &I) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + boost::tie(c, c_end) = adjacent_vertices(t, T); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Forget (" << t << "):\n"; std::cout.flush(); +#endif + + assignment_list_t &alist = T[t].assignments; + + std::swap(alist, T[*c].assignments); + + std::set old_inst; + std::set_difference(T[*c].bag.begin(), T[*c].bag.end(), T[t].bag.begin(), T[t].bag.end(), std::inserter(old_inst, old_inst.end())); + wassert(old_inst.size() == 1); + unsigned short int i = *(old_inst.begin()); + + varset_t old_vars; + std::set_difference(T[*c].alive.begin(), T[*c].alive.end(), T[t].alive.begin(), T[t].alive.end(), std::inserter(old_vars, old_vars.end())); + + assignment_list_t::iterator ai, aif; + + // Restrict assignments (locally) to current variables. + varset_t newlocal; + for (ai = alist.begin(); ai != alist.end(); ++ai) + { + newlocal.clear(); + std::set_difference(ai->local.begin(), ai->local.end(), old_vars.begin(), old_vars.end(), std::inserter(newlocal, newlocal.end())); + std::swap(ai->local, newlocal); + + ai->i_costs.erase(i); + } + + alist.sort(); + + // Collapse (locally) identical assignments. + for (ai = alist.begin(); ai != alist.end();) + { + aif = ai; + + for (++ai; ai != alist.end() && assignments_locally_same(*aif, *ai);) + { + if (aif->s > ai->s) + { + alist.erase(aif); + aif = ai; + ++ai; + } + else + ai = alist.erase(ai); + } + } + + // Free memory in the std::set > that live in the assignments in the list. + //boost::singleton_pool::release_memory(); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Remaining assignments: " << alist.size() << "\n"; std::cout.flush(); +#endif + +#ifdef DEBUG_RALLOC_DEC_ASS + for(ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai); + std::cout << "\n"; + } + + assignment best; + get_best_local_assignment(best, t, T); + std::cout << "Best: "; print_assignment(best); std::cout << "\n"; +#endif +} + +// Handle join nodes in the nice tree decomposition +template +static void tree_dec_ralloc_join(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G, const I_t &I) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end, c2, c3; + boost::tie(c, c_end) = adjacent_vertices(t, T); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Join (" << t << "):\n"; std::cout.flush(); +#endif + + c2 = c; + ++c; + c3 = c; + + assignment_list_t &alist = T[t].assignments; + assignment_list_t &alist2 = T[*c2].assignments; + std::swap(alist, T[*c3].assignments); + + alist.sort(); + alist2.sort(); + + assignment_list_t::iterator ai, ai2; + for (ai = alist.begin(), ai2 = alist2.begin(); ai != alist.end() && ai2 != alist2.end();) + { + if (assignments_locally_same(*ai, *ai2)) + { + ai->s += ai2->s; + // Avoid double-counting instruction costs. + std::set::iterator bi; + for (bi = T[t].bag.begin(); bi != T[t].bag.end(); ++bi) + ai->s -= ai->i_costs[*bi]; + for (size_t i = 0; i < ai->global.size(); i++) + ai->global[i] = ((ai->global[i] != -1) ? ai->global[i] : ai2->global[i]); + ++ai; + ++ai2; + } + else if (*ai < *ai2) + ai = alist.erase(ai); + else if (*ai2 < *ai) + ++ai2; + } + while(ai != alist.end()) + ai = alist.erase(ai); + + alist2.clear(); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Remaining assignments: " << alist.size() << "\n"; std::cout.flush(); +#endif + +#ifdef DEBUG_RALLOC_DEC_ASS + for(std::list::iterator ai = alist.begin(); ai != alist.end(); ++ai) + { + print_assignment(*ai); + std::cout << "\n"; + } +#endif +} + +template +void get_best_local_assignment(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T) +{ + const assignment_list_t &alist = T[t].assignments; + + assignment_list_t::const_iterator ai, ai_end, ai_best; + for(ai = ai_best = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) + if(ai->s < ai_best->s) + ai_best = ai; + + a = *ai_best; +} + +// Handle nodes in the tree decomposition, by detecting their type and calling the appropriate function. Recurses. +template +static void tree_dec_ralloc_nodes(T_t &T, typename boost::graph_traits::vertex_descriptor t, const G_t &G, const I_t &I, const assignment& ac, bool *const assignment_optimal) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + tree_dec_ralloc_leaf(T, t, G, I); + break; + case 1: + c0 = *c; + tree_dec_ralloc_nodes(T, c0, G, I, ac, assignment_optimal); + T[c0].bag.size() < T[t].bag.size() ? tree_dec_ralloc_introduce(T, t, G, I, ac, assignment_optimal) : tree_dec_ralloc_forget(T, t, G, I); + break; + case 2: + c0 = *c++; + c1 = *c; + + if (T[c0].weight < T[c1].weight) // Minimize memory consumption needed for keeping intermediate results. As a side effect, this also helps the ac mechanism in the heuristic. + std::swap (c0, c1); + + tree_dec_ralloc_nodes(T, c0, G, I, ac, assignment_optimal); + { + assignment *ac2 = new assignment; + get_best_local_assignment_biased(*ac2, c0, T); + tree_dec_ralloc_nodes(T, c1, G, I, *ac2, assignment_optimal); + delete ac2; + } + tree_dec_ralloc_join(T, t, G, I); + break; + default: + std::cerr << "Not nice.\n"; + break; + } +} + +// Find the best root selecting from t_old and the leafs under t. +template +static std::pair::vertex_descriptor, size_t> find_best_root(const T_t &T, typename boost::graph_traits::vertex_descriptor t, size_t t_s, typename boost::graph_traits::vertex_descriptor t_old, size_t t_old_s) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1, t0; + size_t t0_s; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + return(t_s > t_old_s ? std::pair::vertex_descriptor, size_t>(t, t_s) : std::pair::vertex_descriptor, size_t>(t_old, t_old_s)); + case 1: + return(find_best_root(T, *c, T[*c].alive.size() ? T[*c].alive.size() : t_s, t_old, t_old_s)); + case 2: + c0 = *c++; + c1 = *c; + boost::tie(t0, t0_s) = find_best_root(T, c0, T[c0].alive.size() ? T[c0].alive.size() : t_s, t_old, t_old_s); + return(find_best_root(T, c1, T[c1].alive.size() ? T[c1].alive.size() : t_s, t0_s > t_old_s ? t0 : t_old, t0_s > t_old_s ? t0_s : t_old_s)); + break; + default: + std::cerr << "Not nice.\n"; + break; + } + + return(std::pair::vertex_descriptor, size_t>(t_old, t_old_s)); +} + +// Change the root to t. +template +static void re_root(T_t &T, typename boost::graph_traits::vertex_descriptor t) +{ + typename boost::graph_traits::vertex_descriptor s0, s1, s2; + typename boost::graph_traits::in_edge_iterator e, e_end; + + boost::tie(e, e_end) = boost::in_edges(t, T); + if (e == e_end) + return; + + s0 = t; + s1 = boost::source(*e, T); + + for (boost::tie(e, e_end) = boost::in_edges(s1, T); e != e_end; boost::tie(e, e_end) = boost::in_edges(s1, T)) + { + s2 = boost::source(*e, T); + boost::remove_edge(s1, s0, T); + boost::add_edge(s0, s1, T); + s0 = s1; + s1 = s2; + } + boost::remove_edge(s1, s0, T); + boost::add_edge(s0, s1, T); +} + +// Change the root to improve the assignment removal heuristic. +template +static void good_re_root(T_t &T) +{ + typename boost::graph_traits::vertex_descriptor t; + + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + adjacency_iter_t c, c_end; + + t = find_root(T); + + for (boost::tie(c, c_end) = boost::adjacent_vertices(t, T); c != c_end && !T[*c].alive.size();) + boost::tie(c, c_end) = boost::adjacent_vertices(*c, T); + + size_t t_s = (c != c_end ? T[*c].alive.size() : 0); + t = find_best_root(T, t, t_s, t, t_s).first; + + if (T[t].alive.size()) + { + std::cerr << "Error: Invalid root.\n"; + return; + } + + re_root(T, t); +} + +// Dump conflict graph, with numbered nodes, show live variables at each node. +static void dump_con(const con_t &con) +{ + if (!currFunc) + return; + + std::ofstream dump_file((std::string(dstFileName) + ".dumpralloccon" + currFunc->rname + ".dot").c_str()); + + std::string *name = new std::string[num_vertices(con)]; + for (var_t i = 0; static_cast::vertices_size_type>(i) < boost::num_vertices(con); i++) + { + std::ostringstream os; + os << i; + if (con[i].name) + os << " : " << con[i].name << ":" << con[i].byte; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, con, boost::make_label_writer(name)); + delete[] name; +} + +// Dump cfg, with numbered nodes, show live variables at each node. +static void dump_cfg(const cfg_t &cfg) +{ + if (!currFunc) + return; + + std::ofstream dump_file((std::string(dstFileName) + ".dumpralloccfg" + currFunc->rname + ".dot").c_str()); + + std::string *name = new std::string[num_vertices(cfg)]; + for (unsigned int i = 0; i < boost::num_vertices(cfg); i++) + { + std::ostringstream os; + os << i << ", " << cfg[i].ic->key << ": "; + cfg_alive_t::const_iterator v; + for (v = cfg[i].alive.begin(); v != cfg[i].alive.end(); ++v) + os << *v << " "; + name[i] = os.str(); + } + + boost::write_graphviz(dump_file, cfg, boost::make_label_writer(name), boost::default_writer(), cfg_titlewriter(currFunc->rname, "register allocator")); + delete[] name; +} + +// Dump tree decomposition, show bag and live variables at each node. +static void dump_tree_decomposition(const tree_dec_t &tree_dec) +{ + if (!currFunc) + return; + + std::ofstream dump_file((std::string(dstFileName) + ".dumprallocdec" + currFunc->rname + ".dot").c_str()); + + unsigned int w = 0; + + std::string *name = new std::string[num_vertices(tree_dec)]; + for (unsigned int i = 0; i < boost::num_vertices(tree_dec); i++) + { + if (tree_dec[i].bag.size() > w) + w = tree_dec[i].bag.size(); + std::ostringstream os; + std::set::const_iterator v1; + os << i << " | "; + for (v1 = tree_dec[i].bag.begin(); v1 != tree_dec[i].bag.end(); ++v1) + os << *v1 << " "; + os << ": "; + std::set::const_iterator v2; + for (v2 = tree_dec[i].alive.begin(); v2 != tree_dec[i].alive.end(); ++v2) + os << *v2 << " "; + name[i] = os.str(); + } + + boost::write_graphviz(dump_file, tree_dec, boost::make_label_writer(name), boost::default_writer(), dec_titlewriter(w - 1, currFunc->rname, "register allocator")); + delete[] name; + +#ifdef D_RALLOC_DEC + std::cout << "Width: " << (w - 1) << "(" << currFunc->name << ")\n"; +#endif +} + +#endif + diff --git a/src/SDCCsalloc.hpp b/src/SDCCsalloc.hpp new file mode 100644 index 0000000..d1b6345 --- /dev/null +++ b/src/SDCCsalloc.hpp @@ -0,0 +1,445 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2011-2018 +// +// (c) 2011-2012 Goethe-Universität Frankfurt +// (c) 2018 Albert-Ludwigs-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// A Chaitin-style stack allocator. + +#ifndef SDCCSALLOC_HH +#define SDCCSALLOC_HH 1 + +#include + +#include +#include + +extern "C" +{ +#include "SDCCmem.h" +#include "SDCCglobl.h" +} + +// #define DEBUG_SALLOC + +struct scon_node_t +{ + symbol *sym; + int color; + boost::icl::interval_set free_stack; + std::set > alignment_conflicts; +}; + +struct scon_edge_t +{ + bool alignment_conflict_only; +}; + +typedef boost::adjacency_list scon_t; // Conflict graph for on-stack variables + +static bool clash (const symbol *s1, const symbol *s2) +{ + wassert(s1); + wassert(s2); + + if(!s1->isspilt && !(IS_AGGREGATE(s1->type) || s1->allocreq && (s1->addrtaken || isVolatile(s1->type)))) // Spill location + { + for(const symbol *s = (const symbol *)setFirstItem (s1->usl.itmpStack); s; s = (const symbol *)setNextItem (s1->usl.itmpStack)) + if(clash(s, s2)) + return(true); + return(false); + } + if(!s2->isspilt && !(IS_AGGREGATE(s2->type) || s2->allocreq && (s2->addrtaken || isVolatile(s2->type)))) // Spill location + { + for(const symbol *s = (const symbol *)setFirstItem (s2->usl.itmpStack); s; s = (const symbol *)setNextItem (s2->usl.itmpStack)) + if(clash(s1, s)) + return(true); + return(false); + } + + return(bitVectBitValue (s1->clashes, s2->key)); +} + +static var_t var_from_operand(const std::map& symbol_to_sindex, const operand *const op) +{ + if(!op || !IS_SYMOP(op)) + return(-1); + std::map::const_iterator si = symbol_to_sindex.find(OP_SYMBOL_CONST(op)); + if (si == symbol_to_sindex.end()) + return(-1); + + return(si->second); +} + +template +static void set_spilt(G_t &G, const I_t &I, SI_t &scon) +{ + std::map symbol_to_sindex; + std::map iindex_to_sindex; + symbol *sym; + var_t j, j_mark; + + // Add variables that need to be on the stack due to having had their address taken (or for a few other reasons, such as being too large or too many to behandled by the register allocator). + for(sym = static_cast(setFirstItem(istack->syms)), j = 0; sym; sym = static_cast(setNextItem(istack->syms))) + { + if(sym->_isparm) + continue; + + // std::cout << "set_spilt() 1: Considering " << sym->name << "\n"; + + if(/*!(IS_AGGREGATE(sym->type) || sym->allocreq && (sym->addrtaken || isVolatile(sym->type)))*/sym->for_newralloc) + continue; + + if(!sym->isspilt && !(IS_AGGREGATE(sym->type) || sym->allocreq && (sym->addrtaken || isVolatile(sym->type)))) // Looks like a spill location - check if it is already covered by live ranges below. + { + bool covered = true; + for (const symbol *s = (const symbol *)setFirstItem (sym->usl.itmpStack); s; s = (const symbol *)setNextItem (sym->usl.itmpStack)) + if (!s->for_newralloc) + { +#ifdef DEBUG_SALLOC + std::cout << "Adding " << sym->name << " for " << s->name << "(" << s << ") to be allocated to stack. (" << s->for_newralloc << ")\n"; + std::cout.flush(); +#endif + covered = false; + symbol_to_sindex[s] = j; + break; + } + if(covered) + continue; + } + + boost::add_vertex(scon); + symbol_to_sindex[sym] = j; + scon[j].sym = sym; + scon[j].color = -1; + j++; + } + j_mark = j; + + // Add edges due to scope (see C99 standard, verse 1233, which requires things to have different addresses, not allowing us to allocate them to the same location, even if we otherwise could). + for(unsigned int i = 0; i < boost::num_vertices(scon); i++) + for(unsigned int j = i + 1; j < boost::num_vertices(scon); j++) + { + if (!(scon[i].sym->addrtaken) || !(scon[i].sym->addrtaken)) + continue; + short p = btree_lowest_common_ancestor(scon[i].sym->block, scon[j].sym->block); + if(p == scon[i].sym->block || p == scon[j].sym->block) + boost::add_edge(i, j, scon); + } + + // Set stack live ranges + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + { + for(unsigned int j = 0; j < boost::num_vertices(scon); j++) + { + short p = btree_lowest_common_ancestor(G[i].ic->block, scon[j].sym->block); + if(p == G[i].ic->block || p == scon[j].sym->block) + G[i].stack_alive.insert(j); + } + } + + // Add variables that have been spilt in register allocation. + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + { + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + var_t vs; + + symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + + if ((sym->regs[0] && !sym->isspilt) || sym->accuse || sym->remat || !sym->nRegs || sym->usl.spillLoc && sym->usl.spillLoc->_isparm) + continue; + + if (iindex_to_sindex.find(I[*v].v) == iindex_to_sindex.end()) + { + wassert(boost::add_vertex(scon) == j); + scon[j].sym = sym; + scon[j].color = -1; + iindex_to_sindex[I[*v].v] = j; + symbol_to_sindex[sym] = j; + j++; + } + + vs = iindex_to_sindex[I[*v].v]; + + G[i].stack_alive.insert(vs); // Needs to be allocated on the stack. + } + } + + // Add edges to conflict graph. + typename boost::graph_traits::edge_iterator e, e_end; + for (boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + { + if (I[boost::source(*e, I)].v == I[boost::target(*e, I)].v || iindex_to_sindex.find(I[boost::source(*e, I)].v) == iindex_to_sindex.end() || iindex_to_sindex.find(I[boost::target(*e, I)].v) == iindex_to_sindex.end()) + continue; + + boost::add_edge(iindex_to_sindex[I[boost::source(*e, I)].v], iindex_to_sindex[I[boost::target(*e, I)].v], scon); + } + + // Add conflicts between variables that had their address taken and those that have been spilt by register allocation. + // TODO: More exact live range analysis for variables that had their address taken (to reduce stack space consumption further, by reducing the number of conflicts here). + for(unsigned int i = 0; i < j_mark; i++) + for(unsigned int j = 0; j < boost::num_vertices(scon); j++) + { + if (i == j) + continue; + if(!scon[i].sym->isspilt && !(IS_AGGREGATE(scon[i].sym->type) || scon[i].sym->allocreq && (scon[i].sym->addrtaken || isVolatile(scon[i].sym->type)))) // Spill location + { + if (clash (scon[i].sym, scon[j].sym)) + boost::add_edge(i, j, scon); + continue; + } + short p = btree_lowest_common_ancestor(scon[i].sym->block, scon[j].sym->block); + if(p == scon[i].sym->block || p == scon[j].sym->block) + boost::add_edge(i, j, scon); + } + + // Ugly hack: Regparms. + for(sym = static_cast(setFirstItem(istack->syms)), j = boost::num_vertices(scon); sym; sym = static_cast(setNextItem(istack->syms))) + { + if(!sym->_isparm || !IS_REGPARM(sym->etype) || !sym->onStack || !sym->allocreq) + continue; + + boost::add_vertex(scon); + scon[j].sym = sym; + scon[j].color = -1; + + // Extend liverange to cover everything. + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + G[i].stack_alive.insert(j); + + // Conflict with everything. + for(unsigned int i = 0; i < j; i++) + boost::add_edge(i, j, scon); + + j++; + } + + // Edges for aligment conflict + typename SI_t::edge_iterator ei, ei_end; + for(boost::tie(ei, ei_end) = boost::edges(scon); ei != ei_end; ++ei) + scon[*ei].alignment_conflict_only = false; + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + { + const var_t result = var_from_operand (symbol_to_sindex, IC_RESULT(G[i].ic)); + + if(result < 0) + continue; + + const var_t left = var_from_operand (symbol_to_sindex, IC_LEFT(G[i].ic)); + const var_t right = var_from_operand (symbol_to_sindex, IC_RIGHT(G[i].ic)); + + if(left >= 0 && !boost::edge (result, left, scon).second) + scon[(boost::add_edge(result, left, scon)).first].alignment_conflict_only = true; + if(right >= 0 && !boost::edge (result, right, scon).second) + scon[(boost::add_edge(result, right, scon)).first].alignment_conflict_only = true; + } +} + +template +void color_stack_var(const var_t v, SI_t &SI, int start, int *ssize) +{ + symbol *const sym = SI[v].sym; + const int size = getSize(sym->type); + + SI[v].color = start; + + const int sloc = (port->stack.direction > 0) ? start + 1 : -start - size ; + symbol *const ssym = (sym->isspilt && sym->usl.spillLoc) ? sym->usl.spillLoc : sym; + + SPEC_STAK(ssym->etype) = ssym->stack = sloc; + + if(ssize) + *ssize = (start + size > *ssize) ? start + size : *ssize; + +#ifdef DEBUG_SALLOC + std::cout << "Placing " << sym->name << " (really " << ssym->name << ") at [" << start << ", " << (start + size - 1) << "]\n"; + std::cout.flush(); +#endif + + // Mark stack location as used for all conflicting variables. + typename boost::graph_traits::adjacency_iterator n, n_end; + for(boost::tie(n, n_end) = boost::adjacent_vertices(v, SI); n != n_end; ++n) + if (!SI[boost::edge(v, *n, SI).first].alignment_conflict_only) + SI[*n].free_stack -= boost::icl::discrete_interval::type(start, start + size); + else + SI[*n].alignment_conflicts.insert(boost::icl::discrete_interval::type(start, start + size)); +} + +// Place a single variable on the stack greedily. +template +void color_stack_var_greedily(const var_t v, SI_t &SI, int alignment, int *ssize) +{ + int start; + symbol *const sym = SI[v].sym; + const int size = getSize(sym->type); + + // Find a suitable free stack location. + boost::icl::interval_set::iterator si; + for(si = SI[v].free_stack.begin();; ++si) + { + start = boost::icl::first(*si); + + bool alignment_issue; + do + { + // Adjust start address for alignment conflict + std::set >::const_iterator ai, ai_end; + for(ai = SI[v].alignment_conflicts.begin(), ai_end = SI[v].alignment_conflicts.end(); ai != ai_end; ++ai) + { + if(ai->upper() < start || ai->lower() > start + size - 1) + continue; + if(ai->lower() == start) + continue; + +#ifdef DEBUG_SALLOC + std::cerr << "Resolving alignment conflict at " << SI[v].sym->name << "\n"; +#endif + + start = ai->upper() + 1; // Resolve conflict. + } + + // Adjust start address for alignment + alignment_issue = start % alignment; + if(start % alignment) + start = start + alignment - start % alignment; + } + while (alignment_issue); + + if(boost::icl::last(*si) >= start + size - 1) + break; // Found one. + } + + color_stack_var(v, SI, start, ssize); +} + +static +int get_alignment(sym_link *type) +{ +#if 1 + return(1); +#else + for(; IS_ARRAY (type); type = type->next); + + switch(getSize(type)) + { + case 0: // ? + case 1: + return(1); + case 2: + return(2); + case 3: + case 4: + return(4); + default: + return(8); + } +#endif +} + +template +void chaitin_ordering(const SI_t &SI, std::list &ordering) +{ + std::vector marked(boost::num_vertices(SI)); + unsigned int num_marked, i, d, mind, minn; + std::stack stack; + + for(num_marked = 0; num_marked < boost::num_vertices(SI); num_marked++) + { + mind = UINT_MAX; + minn = -1; + for(i = 0; i < boost::num_vertices(SI); i++) + { + if(marked[i]) + continue; + + typename boost::graph_traits::adjacency_iterator n, n_end; + for(boost::tie(n, n_end) = boost::adjacent_vertices(i, SI), d = 0; n != n_end; ++n) + d += !marked[*n]; + + if(d < mind || d == mind && get_alignment(SI[i].sym->type) < get_alignment(SI[minn].sym->type)) // Coloring aligned variables first tends to keep gaps from alignment small. + { + mind = d; + minn = i; + } + } + + stack.push(minn); + marked[minn] = true; + } + + while(!stack.empty()) + { + ordering.push_back(stack.top()); + stack.pop(); + } +} + +template +void chaitin_salloc(SI_t &SI) +{ + std::list ordering; + + chaitin_ordering(SI, ordering); + + for(unsigned int i = 0; i < boost::num_vertices(SI); i++) + SI[i].free_stack.insert(boost::icl::discrete_interval::type(0, 1 << 15)); + + int ssize = 0; + + clearStackOffsets(); + + std::list::const_iterator i, i_end; + for(i = ordering.begin(), i_end = ordering.end(); i != i_end; ++i) + { + // Alignment, even when not required by the hardware helps avoid artially overlapping stack operands (which are not supported by code generation in some backends). + color_stack_var_greedily(*i, SI, get_alignment (SI[*i].sym->type), &ssize); + } + + if(currFunc) + { +#ifdef DEBUG_SALLOC + std::cout << "currFunc->stack: old " << currFunc->stack << ", new " << (currFunc->stack + ssize) << "\n"; +#endif + currFunc->stack += ssize; + SPEC_STAK (currFunc->etype) += ssize; + } +} + +static +void dump_scon(const scon_t &scon) +{ + if(!currFunc) + return; + + std::ofstream dump_file((std::string(dstFileName) + ".dumpsalloccon" + currFunc->rname + ".dot").c_str()); + + std::string *name = new std::string[boost::num_vertices(scon)]; + for(var_t i = 0; static_cast::vertices_size_type>(i) < boost::num_vertices(scon); i++) + { + int start = scon[i].color; + std::ostringstream os; + os << i; + if (scon[i].sym->name) + os << " : " << scon[i].sym->name << " : " << getSize(scon[i].sym->type) << " [" << start << "," << (start + getSize(scon[i].sym->type) - 1) << "]"; + name[i] = os.str(); + } + boost::write_graphviz(dump_file, scon, boost::make_label_writer(name)); + delete[] name; +} +#endif + diff --git a/src/SDCCset.c b/src/SDCCset.c new file mode 100644 index 0000000..e59dfca --- /dev/null +++ b/src/SDCCset.c @@ -0,0 +1,728 @@ +/*----------------------------------------------------------------- + SDCCset.c - contains support routines for doubly linked lists. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include "newalloc.h" +#include "SDCCerr.h" +#include "SDCCset.h" + +/*-----------------------------------------------------------------*/ +/* newSet - will allocate & return a new set entry */ +/*-----------------------------------------------------------------*/ +set * +newSet (void) +{ + set *lp; + + lp = Safe_alloc (sizeof (set)); + lp->item = lp->curr = lp->next = NULL; + return lp; +} + + +/*-----------------------------------------------------------------*/ +/* setFromSet - creates a list from another list; the order of */ +/* elements in new list is reverted */ +/*-----------------------------------------------------------------*/ +set * +setFromSet (const set *lp) +{ + set *lfl = NULL; + + while (lp) + { + addSetHead (&lfl, lp->item); + lp = lp->next; + } + + return lfl; +} + +/*-----------------------------------------------------------------*/ +/* setFromSet - creates a list from another list; the order of */ +/* elements in retained */ +/*-----------------------------------------------------------------*/ +set * +setFromSetNonRev (const set *lp) +{ + set *lfl = NULL; + + while (lp) + { + addSet (&lfl, lp->item); + lp = lp->next; + } + + return lfl; +} + +/*-----------------------------------------------------------------*/ +/* isSetsEqual - are the lists equal, they are equal if they have */ +/* the same objects & the same number of objects */ +/*-----------------------------------------------------------------*/ +int +isSetsEqual (const set *dest, const set *src) +{ + const set *src1 = src; + + for (; dest && src; dest = dest->next, src = src->next) + { + if (!isinSet (src1, dest->item)) + return 0; + } + if (!dest && !src) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isSetsEqualWith - are the lists equal, they are equal if they */ +/* have the same objects & the same number of */ +/* objects , compare function */ +/*-----------------------------------------------------------------*/ +int +isSetsEqualWith (set * dest, set * src, int (*cFunc) (void *, void *)) +{ + set *src1 = src; + + for (; dest && src; dest = dest->next, src = src->next) + { + if (!isinSetWith (src1, dest->item, cFunc)) + return 0; + } + if (!dest && !src) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* addSetIfnotP - adds to a linked list if not already present */ +/*-----------------------------------------------------------------*/ +void * +addSetIfnotP (set ** list, void *item) +{ + if (isinSet (*list, item)) + return item; + + addSetHead (list, item); + + return item; +} + +/*-----------------------------------------------------------------*/ +/* addSetHead - add item to head of linked list */ +/*-----------------------------------------------------------------*/ +void * +addSetHead (set ** list, void *item) +{ + set *lp = newSet (); + + lp->item = item; + lp->next = *list; + + assert (lp != lp->item); + *list = lp; + return item; +} + +/*-----------------------------------------------------------------*/ +/* addSet - add an item to a linear linked list */ +/*-----------------------------------------------------------------*/ +void * +addSet (set ** list, void *item) +{ + set *lp; + + if (!list) + werror (E_INTERNAL_ERROR,__FILE__,__LINE__, "Invalid set."); + + /* item added to the tail of the list */ + + /* if the list is empty */ + if (*list == NULL) + { + lp = *list = newSet (); + } + else + { + /* go to the end of the list */ + for (lp = *list; lp->next; lp = lp->next); + lp = lp->next = newSet (); + } + if (!list) + werror (E_OUT_OF_MEM,__FILE__,__LINE__, "Can't add to set."); + + /* lp now all set */ + lp->item = item; + + return item; +} + +/*-----------------------------------------------------------------*/ +/* deleteItemIf - will delete from set if cond function returns 1 */ +/*-----------------------------------------------------------------*/ +void +deleteItemIf (set ** sset, int (*cond) (void *, va_list),...) +{ + set *sp = *sset; + va_list ap; + + while (sp) + { + /* + * On the x86 va_list is just a pointer, so due to pass by value + * ap is not mofified by the called function. On the PPC va_list + * is a pointer to a structure, so ap is modified. Re-init each time. + */ + va_start (ap, cond); + + if ((*cond) (sp->item, ap)) + { + deleteSetItem (sset, sp->item); + sp = *sset; + continue; + } + + va_end(ap); + sp = sp->next; + } +} + +/*-------------------------------------------------------------------*/ +/* destructItemIf - will delete from set if cond function returns 1, */ +/* upon deletion, item's destructor is also called */ +/*-------------------------------------------------------------------*/ +void +destructItemIf (set ** sset, void (*destructor)(void * item), int (*cond) (void *, va_list),...) +{ + set *sp = *sset; + va_list ap; + + while (sp) + { + /* + * On the x86 va_list is just a pointer, so due to pass by value + * ap is not mofified by the called function. On the PPC va_list + * is a pointer to a structure, so ap is modified. Re-init each time. + */ + va_start (ap, cond); + + if ((*cond) (sp->item, ap)) + { + destructor (sp->item); + deleteSetItem (sset, sp->item); + sp = *sset; + continue; + } + + va_end(ap); + sp = sp->next; + } +} + + +/*-----------------------------------------------------------------*/ +/* deleteSetItem - will delete a given item from the list */ +/*-----------------------------------------------------------------*/ +void +deleteSetItem (set **list, void *item) +{ + set *lp, *lp1; + + /* if list is empty */ + if (*list == NULL) + return; + + /* if this item is at the head of the list */ + if ((*list)->item == item) + { + lp = *list; + *list = (*list)->next; + Safe_free (lp); + return; + } + + /* find the item in the list */ + for (lp = *list; lp->next; lp = lp->next) + { + if (lp->next->item == item) /* the next one is it */ + { + lp1 = lp->next; /* this one will need to be freed */ + lp->next = lp->next->next; /* take out of list */ + Safe_free (lp1); + return; + } + } + + /* could not find it */ +} + +/*-----------------------------------------------------------------*/ +/* replaceSetItem - will replace a given item in the list */ +/*-----------------------------------------------------------------*/ +void +replaceSetItem (set *list, void *olditem, void *newitem) +{ + /* find the item in the list */ + for (; list; list = list->next) + if (list->item == olditem) + { + list->item = newitem; + return; + } + + + /* could not find it */ +} + +/*-----------------------------------------------------------------*/ +/* isinSet - the item is present in the linked list */ +/*-----------------------------------------------------------------*/ +int +isinSet (const set *list, const void *item) +{ + const set *lp; + + for (lp = list; lp; lp = lp->next) + if (lp->item == item) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* isinSetWith - the item is present in the linked list */ +/*-----------------------------------------------------------------*/ +int +isinSetWith (set * list, void *item, int (*cFunc) (void *, void *)) +{ + set *lp; + + for (lp = list; lp; lp = lp->next) + if ((*cFunc) (lp->item, item)) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* mergeSets - append list to sset */ +/*-----------------------------------------------------------------*/ +void +mergeSets (set **sset, set *list) +{ + if (*sset == NULL) + { + *sset = list; + } + else + { + set *lp; + + for (lp = *sset; lp->next; lp = lp->next) + ; + lp->next = list; + } +} + +/*-----------------------------------------------------------------*/ +/* unionSets - will return the union of the two lists */ +/*-----------------------------------------------------------------*/ +set * +unionSets (set * list1, set * list2, int throw) +{ + set *un = NULL; + set *lp; + + /* If we were going to throw away the destination list */ + /* anyway, save memory and time by using it as the */ + /* starting point for the new list. */ + if (throw == THROW_DEST || throw == THROW_BOTH) + { + un = list1; + if (throw == THROW_BOTH) + throw = THROW_SRC; + else + throw = THROW_NONE; + } + else + { + /* add all elements in the first list */ + for (lp = list1; lp; lp = lp->next) + addSet (&un, lp->item); + } + + /* now for all those in list2 which does not */ + /* already exist in the list add */ + for (lp = list2; lp; lp = lp->next) + if (!isinSet (un, lp->item)) + addSet (&un, lp->item); + + switch (throw) + { + case THROW_SRC: + setToNull ((void *) &list2); + break; + case THROW_DEST: + setToNull ((void *) &list1); + break; + case THROW_BOTH: + setToNull ((void *) &list1); + setToNull ((void *) &list2); + } + + return un; +} + +/*-----------------------------------------------------------------*/ +/* unionSetsWith - will return the union of the two lists */ +/*-----------------------------------------------------------------*/ +set * +unionSetsWith (set * list1, set * list2, int (*cFunc) (), int throw) +{ + set *un = NULL; + set *lp; + + /* add all elements in the first list */ + for (lp = list1; lp; lp = lp->next) + addSet (&un, lp->item); + + /* now for all those in list2 which does not */ + /* already exist in the list add */ + for (lp = list2; lp; lp = lp->next) + if (!isinSetWith (un, lp->item, (int (*)(void *, void *)) cFunc)) + addSet (&un, lp->item); + + switch (throw) + { + case THROW_SRC: + setToNull ((void *) &list2); + break; + case THROW_DEST: + setToNull ((void *) &list1); + break; + case THROW_BOTH: + setToNull ((void *) &list1); + setToNull ((void *) &list2); + } + + return un; +} + +/*-----------------------------------------------------------------*/ +/* intersectSets - returns list of items in common to two lists */ +/*-----------------------------------------------------------------*/ +set * +intersectSets (set * list1, set * list2, int throw) +{ + set *in = NULL; + set *lp; + + /* we can take any one of the lists and iterate over it */ + for (lp = list1; lp; lp = lp->next) + if (isinSet (list2, lp->item)) + addSetHead (&in, lp->item); + + switch (throw) + { + case THROW_SRC: + setToNull ((void *) &list2); + break; + case THROW_DEST: + setToNull ((void *) &list1); + break; + case THROW_BOTH: + setToNull ((void *) &list1); + setToNull ((void *) &list2); + } + + return in; +} + +/*-----------------------------------------------------------------*/ +/* intersectSetsWith - returns list of items in common to two */ +/* lists */ +/*-----------------------------------------------------------------*/ +set * +intersectSetsWith (set * list1, set * list2, + int (*cFunc) (void *, void *), int throw) +{ + set *in = NULL; + set *lp; + + /* we can take any one of the lists and iterate over it */ + for (lp = list1; lp; lp = lp->next) + if (isinSetWith (list2, lp->item, cFunc)) + addSetHead (&in, lp->item); + + switch (throw) + { + case THROW_SRC: + setToNull ((void *) &list2); + break; + case THROW_DEST: + setToNull ((void *) &list1); + break; + case THROW_BOTH: + setToNull ((void *) &list1); + setToNull ((void *) &list2); + } + + return in; +} + +/*-----------------------------------------------------------------*/ +/* elementsInSet - elements count of a set */ +/*-----------------------------------------------------------------*/ +int +elementsInSet (const set * s) +{ + const set *loop = s; + int count = 0; + + while (loop) + { + count++; + loop = loop->next; + } + + return count; +} + +/*-----------------------------------------------------------------*/ +/* indexSet - returns the i'th item in set */ +/*-----------------------------------------------------------------*/ +void * +indexSet (set * s, int index) +{ + set *loop = s; + + while (loop && index--) + { + loop = loop->next; + } + + return loop ? loop->item : NULL; +} + + +/*-----------------------------------------------------------------*/ +/* reverseSet - reverse the order of the items of a set */ +/*-----------------------------------------------------------------*/ +set * +reverseSet (set * s) +{ + set *t = NULL; + set *u = NULL; + + while(s->next) + { + t = s->next; + s->next = u; + u = s; + s = t; + } + s->next = u; + return s; +} + +/*-----------------------------------------------------------------*/ +/* subtractFromSet - take away from set1 elements of set2 */ +/*-----------------------------------------------------------------*/ +set * +subtractFromSet (set * left, set * right, int throw) +{ + set *result = setFromSet (left); + set *loop; + + if (right) + { + for (loop = right; loop; loop = loop->next) + if (isinSet (result, loop->item)) + deleteSetItem (&result, loop->item); + } + + switch (throw) + { + case THROW_SRC: + setToNull ((void *) &right); + break; + case THROW_DEST: + setToNull ((void *) &left); + break; + case THROW_BOTH: + setToNull ((void *) &left); + setToNull ((void *) &right); + break; + } + + return result; +} + +/*-----------------------------------------------------------------*/ +/* applyToSet - will call the supplied function with each item */ +/*-----------------------------------------------------------------*/ +int +applyToSet (set * list, int (*somefunc) (void *, va_list),...) +{ + set *lp; + va_list ap; + int rvalue = 0; + + for (lp = list; lp; lp = lp->next) + { + va_start (ap, somefunc); + rvalue += (*somefunc) (lp->item, ap); + va_end (ap); + } + return rvalue; +} + +/*-----------------------------------------------------------------*/ +/* applyToSetFTrue - will call the supplied function with each */ +/* item until list is exhausted or a true is */ +/* returned */ +/*-----------------------------------------------------------------*/ +int +applyToSetFTrue (set * list, int (*somefunc) (void *, va_list),...) +{ + set *lp; + va_list ap; + int rvalue = 0; + + for (lp = list; lp; lp = lp->next) + { + va_start (ap, somefunc); + rvalue += (*somefunc) (lp->item, ap); + va_end (ap); + if (rvalue) + break; + } + return rvalue; +} + +/*-----------------------------------------------------------------*/ +/* peekSet - will return the first element of the set */ +/*-----------------------------------------------------------------*/ +void * +peekSet (const set *sp) +{ + if (!sp) + return NULL; + + return sp->item; +} + +/*-----------------------------------------------------------------*/ +/* setFirstItem - gets the first item in the set, begins iteration */ +/*-----------------------------------------------------------------*/ +void * +setFirstItem (set *sset) +{ + if (sset) + { + sset->curr = sset; + return sset->item; + } + + return NULL; +} +/*-----------------------------------------------------------------*/ +/* setNextItem - gets the next item, changes the iteration */ +/*-----------------------------------------------------------------*/ +void * +setNextItem (set * sset) +{ + if (sset && sset->curr) + { + sset->curr = sset->curr->next; + if (sset->curr) + return sset->curr->item; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* getSet - will delete & return the first item from the set */ +/*-----------------------------------------------------------------*/ +void * +getSet (set ** list) +{ + set *lp; + void *item; + + /* if list is empty then we cannot delete */ + if (*list == NULL) + return (void *) NULL; + + /* find the item in the list */ + lp = *list; + item = lp->item; /* save the item */ + + *list = lp->next; + return item; +} + +/*-----------------------------------------------------------------*/ +/* setToNull - will throw away the entire list */ +/*-----------------------------------------------------------------*/ +void +setToNull (void **item) +{ + if (!item) + return; + + if (!*item) + return; + Safe_free (*item); + *item = NULL; +} + +/*-----------------------------------------------------------------*/ +/* deleteSet - will throw away the entire list */ +/* note - setToNull doesn't actually throw away the whole list. */ +/* Instead it only throws away the first item. */ +/*-----------------------------------------------------------------*/ +void +deleteSet (set **s) +{ + set *curr; + set *next; + + if(!s || !*s) + return; + + curr = *s; + next = curr->next; + while (next) + { + Safe_free (curr); + curr = next; + next = next->next; + } + + Safe_free (curr); + + *s = NULL; +} diff --git a/src/SDCCset.h b/src/SDCCset.h new file mode 100644 index 0000000..5df8858 --- /dev/null +++ b/src/SDCCset.h @@ -0,0 +1,84 @@ +/*----------------------------------------------------------------- + SDCCset.h - contains support routines for doubly linked lists. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCSET_H +#define SDCCSET_H +#include + + +#ifndef THROWS +#define THROWS +#define THROW_NONE 0 +#define THROW_SRC 1 +#define THROW_DEST 2 +#define THROW_BOTH 3 +#endif + +/* linear linked list generic */ +typedef struct set + { + void *item; + struct set *curr; + struct set *next; + } +set; + +#define DEFSETFUNC(fname) int fname ( void *item, va_list ap) +#define V_ARG(type,var) type var = va_arg(ap,type) + +/* set related functions */ +set *newSet (void); +void *addSet (set **, void *); +void *addSetHead (set **, void *); +void *getSet (set **); +void deleteSetItem (set **, void *); +void replaceSetItem (set *, void *olditem, void *newitem); +void deleteItemIf (set **, int (*cond) (void *, va_list),...); +void destructItemIf (set **, void (*destructor)(void *), int (*cond) (void *, va_list),...); +int isinSet (const set *, const void *); +typedef int (* insetwithFunc) (void *, void *); +int isinSetWith (set *, void *, insetwithFunc cfunc); +int applyToSet (set * list, int (*somefunc) (void *, va_list),...); +int applyToSetFTrue (set * list, int (*somefunc) (void *, va_list),...); +void mergeSets (set **sset, set *list); +set *unionSets (set *, set *, int); +set *unionSetsWith (set *, set *, int (*cFunc) (), int); +set *intersectSets (set *, set *, int); +void *addSetIfnotP (set **, void *); +set *setFromSet (const set *); +set *setFromSetNonRev (const set *); +int isSetsEqual (const set *, const set *); +set *subtractFromSet (set *, set *, int); +int elementsInSet (const set *); +void *indexSet(set *, int); +set *intersectSetsWith (set *, set *, int (*cFunc) (void *, void *), int); +int isSetsEqualWith (set *, set *, int (*cFunc) (void *, void *)); +void *peekSet (const set *); +void *setFirstItem (set *); +void *setNextItem (set *); +void setToNull (void **); +set *reverseSet (set *); +void deleteSet (set **s); + +#endif diff --git a/src/SDCCsymt.c b/src/SDCCsymt.c new file mode 100644 index 0000000..ec45d1a --- /dev/null +++ b/src/SDCCsymt.c @@ -0,0 +1,4587 @@ +/*------------------------------------------------------------------------- + SDCCsymt.c - Code file for Symbols table related structures and MACRO's. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "newalloc.h" +#include "dbuf_string.h" + +#include "SDCCsymt.h" + +value *aggregateToPointer (value * val); +void printTypeChainRaw (sym_link * start, FILE * of); + +void +printFromToType (sym_link * from, sym_link * to) +{ + struct dbuf_s dbuf; + dbuf_init (&dbuf, 1024); + dbuf_append_str (&dbuf, "from type '"); + dbuf_printTypeChain (from, &dbuf); + dbuf_append_str (&dbuf, "'\n to type '"); + dbuf_printTypeChain (to, &dbuf); + dbuf_append_str (&dbuf, "'\n"); + dbuf_write_and_destroy (&dbuf, stderr); +} + +/* noun strings */ +char * +nounName (sym_link * sl) +{ + switch (SPEC_NOUN (sl)) + { + case V_INT: + { + if (SPEC_LONGLONG (sl)) + return "long long"; + if (SPEC_LONG (sl)) + return "long"; + if (SPEC_SHORT (sl)) + return "short"; + return "int"; + } + case V_FLOAT: + return "float"; + case V_FIXED16X16: + return "fixed16x16"; + case V_BOOL: + return "_Bool"; + case V_CHAR: + return "char"; + case V_VOID: + return "void"; + case V_STRUCT: + return "struct"; + case V_LABEL: + return "label"; + case V_BITFIELD: + return "bitfield"; + case V_BBITFIELD: + return "_Boolbitfield"; + case V_BIT: + return "bit"; + case V_SBIT: + return "sbit"; + case V_DOUBLE: + return "double"; + } + return "unknown"; +} + +bucket *SymbolTab[256]; /* the symbol table */ +bucket *StructTab[256]; /* the structure table */ +bucket *TypedefTab[256]; /* the typedef table */ +bucket *LabelTab[256]; /* the Label table */ +bucket *enumTab[256]; /* enumerated table */ +bucket *AddrspaceTab[256]; /* the named address space table */ + +/*------------------------------------------------------------------*/ +/* initSymt () - initialises symbol table related stuff */ +/*------------------------------------------------------------------*/ +void +initSymt (void) +{ + int i = 0; + + for (i = 0; i < 256; i++) + SymbolTab[i] = StructTab[i] = (void *) NULL; +} + +/*-----------------------------------------------------------------*/ +/* newBucket - allocates & returns a new bucket */ +/*-----------------------------------------------------------------*/ +bucket * +newBucket (void) +{ + bucket *bp; + + bp = Safe_alloc (sizeof (bucket)); + + return bp; +} + +/*-----------------------------------------------------------------*/ +/* hashKey - computes the hashkey given a symbol name */ +/*-----------------------------------------------------------------*/ +int +hashKey (const char *s) +{ + unsigned long key = 0; + + while (*s) + key += *s++; + return key % 256; +} + +/*-----------------------------------------------------------------*/ +/* addSym - adds a symbol to the hash Table */ +/*-----------------------------------------------------------------*/ +void +addSym (bucket ** stab, void *sym, char *sname, long level, int block, int checkType) +{ + int i; /* index into the hash Table */ + bucket *bp; /* temp bucket * */ + + if (checkType) + { + symbol *csym = (symbol *) sym; + + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "addSym: %s ", sname); + } + /* make sure the type is complete and sane */ + checkTypeSanity (csym->etype, csym->name); + } + + /* prevent overflow of the (r)name buffers */ + if (strlen (sname) > SDCC_SYMNAME_MAX) + { + werror (W_SYMBOL_NAME_TOO_LONG, SDCC_SYMNAME_MAX); + sname[SDCC_SYMNAME_MAX] = '\0'; + } + + /* the symbols are always added at the head of the list */ + i = hashKey (sname); + /* get a free entry */ + bp = Safe_alloc (sizeof (bucket)); + + bp->sym = sym; /* update the symbol pointer */ + bp->level = level; /* update the nest level */ + bp->block = block; + strncpyz (bp->name, sname, sizeof (bp->name)); /* copy the name into place */ + + /* if this is the first entry */ + if (stab[i] == NULL) + { + bp->prev = bp->next = (void *) NULL; /* point to nothing */ + stab[i] = bp; + } + /* not first entry then add @ head of list */ + else + { + bp->prev = NULL; + stab[i]->prev = bp; + bp->next = stab[i]; + stab[i] = bp; + } +} + +/*-----------------------------------------------------------------*/ +/* deleteSym - deletes a symbol from the hash Table entry */ +/*-----------------------------------------------------------------*/ +void +deleteSym (bucket ** stab, void *sym, const char *sname) +{ + int i = 0; + bucket *bp; + + i = hashKey (sname); + + bp = stab[i]; + /* find the symbol */ + while (bp) + { + if (bp->sym == sym) /* found it then break out */ + break; /* of the loop */ + bp = bp->next; + } + + if (!bp) /* did not find it */ + return; + + /* if this is the first one in the chain */ + if (!bp->prev) + { + stab[i] = bp->next; + if (stab[i]) /* if chain ! empty */ + stab[i]->prev = (void *) NULL; + } + /* middle || end of chain */ + else + { + if (bp->next) /* if not end of chain */ + bp->next->prev = bp->prev; + + bp->prev->next = bp->next; + } +} + +/*-----------------------------------------------------------------*/ +/* findSym - finds a symbol in a table */ +/*-----------------------------------------------------------------*/ +void * +findSym (bucket ** stab, void *sym, const char *sname) +{ + bucket *bp; + + bp = stab[hashKey (sname)]; + while (bp) + { + if (bp->sym == sym || strcmp (bp->name, sname) == 0) + break; + bp = bp->next; + } + + return (bp ? bp->sym : (void *) NULL); +} + +/*-----------------------------------------------------------------*/ +/* findSymWithLevel - finds a symbol with a name & level */ +/*-----------------------------------------------------------------*/ +void * +findSymWithLevel (bucket ** stab, symbol * sym) +{ + bucket *bp; + + if (!sym) + return sym; + + bp = stab[hashKey (sym->name)]; + + /** + ** do the search from the head of the list since the + ** elements are added at the head it is ensured that + ** we will find the deeper definitions before we find + ** the global ones. we need to check for symbols with + ** level <= to the level given, if levels match then block + ** numbers need to match as well + **/ + while (bp) + { + if (strcmp (bp->name, sym->name) == 0 && bp->level <= sym->level) + { + /* if this is parameter then nothing else need to be checked */ + if (((symbol *) (bp->sym))->_isparm) + return (bp->sym); + /* if levels match then block numbers should also match */ + if (bp->level && bp->level == sym->level && bp->block == sym->block + && ((symbol *)(bp->sym))->seqPoint <= sym->seqPoint) + return (bp->sym); + /* if levels don't match then we are okay if the symbol is in scope */ + if (bp->level && bp->level != sym->level && bp->block <= sym->block + && ((symbol *) (bp->sym))->isinscope + && (stab == LabelTab || ((symbol *)(bp->sym))->seqPoint <= sym->seqPoint)) + return (bp->sym); + /* if this is a global variable then we are ok too */ + if (bp->level == 0) + return (bp->sym); + } + + bp = bp->next; + } + + return (void *) NULL; +} + +/*-----------------------------------------------------------------*/ +/* findSymWithBlock - finds a symbol with name in a block */ +/*-----------------------------------------------------------------*/ +void * +findSymWithBlock (bucket ** stab, symbol * sym, int block, long level) +{ + bucket *bp; + + if (!sym) + return sym; + + bp = stab[hashKey (sym->name)]; + while (bp) + { + if (strcmp (bp->name, sym->name) == 0 && (bp->block == block || (bp->block < block && bp->level < level))) + break; + bp = bp->next; + } + + return (bp ? bp->sym : (void *) NULL); +} + +/*------------------------------------------------------------------*/ +/* newSymbol () - returns a new pointer to a symbol */ +/*------------------------------------------------------------------*/ +symbol * +newSymbol (const char *name, long scope) +{ + symbol *sym; + + sym = Safe_alloc (sizeof (symbol)); + + strncpyz (sym->name, name, sizeof (sym->name)); /* copy the name */ + sym->level = scope; /* set the level */ + sym->block = currBlockno; + sym->seqPoint = seqPointNo; + sym->lineDef = lexLineno; /* set the line number */ + sym->fileDef = lexFilename; + sym->for_newralloc = 0; + sym->isinscope = 1; + sym->usl.spillLoc = 0; + sym->funcDivFlagSafe = 0; + sym->funcUsesVolatile = 1; + + return sym; +} + +/*------------------------------------------------------------------*/ +/* newLink - creates a new link (declarator,specifier) */ +/*------------------------------------------------------------------*/ +sym_link * +newLink (SYM_LINK_CLASS select) +{ + sym_link *p; + + p = Safe_alloc (sizeof (sym_link)); + p->xclass = select; + p->funcAttrs.z88dk_params_offset = 0; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newStruct - creats a new structdef from the free list */ +/*------------------------------------------------------------------*/ +structdef * +newStruct (const char *tag) +{ + structdef *s; + + s = Safe_alloc (sizeof (structdef)); + + strncpyz (s->tag, tag, sizeof (s->tag)); /* copy the tag */ + return s; +} + +/*------------------------------------------------------------------*/ +/* sclsFromPtr - Return the storage class a pointer points into. */ +/* S_FIXED is returned for generic pointers or other */ +/* unexpected cases */ +/*------------------------------------------------------------------*/ +STORAGE_CLASS +sclsFromPtr (sym_link * ptr) +{ + switch (DCL_TYPE (ptr)) + { + case POINTER: + return S_DATA; + case GPOINTER: + return S_FIXED; + case FPOINTER: + return S_XDATA; + case CPOINTER: + return S_CODE; + case IPOINTER: + return S_IDATA; + case PPOINTER: + return S_PDATA; + case EEPPOINTER: + return S_EEPROM; + case FUNCTION: + return S_CODE; + default: + return S_FIXED; + } +} + +/*------------------------------------------------------------------*/ +/* pointerTypes - do the computation for the pointer types */ +/*------------------------------------------------------------------*/ +void +pointerTypes (sym_link * ptr, sym_link * type) +{ + sym_link *p; + sym_link *etype; + + if (IS_SPEC (ptr)) + return; + + /* find the last unknown pointer type */ + p = ptr; + while (p) + { + if (IS_PTR (p) && DCL_TYPE (p) == UPOINTER) + ptr = p; + p = p->next; + } + + /* could not find it */ + if (!ptr || IS_SPEC (ptr) || !IS_PTR (ptr)) + return; + + if (IS_PTR (ptr) && DCL_TYPE (ptr) != UPOINTER) + { + pointerTypes (ptr->next, type); + return; + } + + /* change the pointer type depending on the + storage class of the etype */ + etype = getSpec (type); + if (IS_SPEC (etype)) + { + switch (SPEC_SCLS (etype)) + { + case S_XDATA: + DCL_TYPE (ptr) = FPOINTER; + break; + case S_IDATA: + DCL_TYPE (ptr) = IPOINTER; + break; + case S_PDATA: + DCL_TYPE (ptr) = PPOINTER; + break; + case S_DATA: + DCL_TYPE (ptr) = POINTER; + break; + case S_CODE: + DCL_TYPE (ptr) = CPOINTER; + break; + case S_EEPROM: + DCL_TYPE (ptr) = EEPPOINTER; + break; + default: + DCL_TYPE (ptr) = port->unqualified_pointer; + break; + } + /* the storage class of etype ends here */ + SPEC_SCLS (etype) = 0; + } + + /* now change all the remaining unknown pointers + to generic pointers */ + while (ptr) + { + if (!IS_SPEC (ptr) && DCL_TYPE (ptr) == UPOINTER) + DCL_TYPE (ptr) = port->unqualified_pointer; + ptr = ptr->next; + } + + /* same for the type although it is highly unlikely that + type will have a pointer */ + while (type) + { + if (!IS_SPEC (type) && DCL_TYPE (type) == UPOINTER) + DCL_TYPE (type) = port->unqualified_pointer; + type = type->next; + } +} + +/*------------------------------------------------------------------*/ +/* addDecl - adds a declarator @ the end of a chain */ +/*------------------------------------------------------------------*/ +void +addDecl (symbol * sym, int type, sym_link * p) +{ + static sym_link *empty = NULL; + sym_link *head; + sym_link *tail; + sym_link *t; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "SDCCsymt.c:addDecl(%s,%d,%p)\n", sym->name, type, (void *)p); + + if (empty == NULL) + empty = newLink (SPECIFIER); + + /* if we are passed a link then set head & tail */ + if (p) + { + tail = head = p; + while (tail->next) + tail = tail->next; + } + else + { + head = tail = newLink (DECLARATOR); + DCL_TYPE (head) = type; + } + + /* if this is the first entry */ + if (!sym->type) + { + sym->type = head; + sym->etype = tail; + } + else if (IS_SPEC (sym->etype) && IS_SPEC (head) && head == tail) + { + sym->etype = mergeSpec (sym->etype, head, sym->name); + } + else if (IS_SPEC (sym->etype) && !IS_SPEC (head) && head == tail) + { + t = sym->type; + while (t->next != sym->etype) + t = t->next; + t->next = head; + tail->next = sym->etype; + } + else if (IS_FUNC (sym->type) && IS_SPEC (sym->type->next) && !memcmp (sym->type->next, empty, sizeof (sym_link))) + { + sym->type->next = head; + sym->etype = tail; + } + else + { + sym->etype->next = head; + sym->etype = tail; + } + + /* if the type is an unknown pointer and has + a tspec then take the storage class const & volatile + attribute from the tspec & make it those of this + symbol */ + if (p && !IS_SPEC (p) && + //DCL_TYPE (p) == UPOINTER && + DCL_TSPEC (p)) + { + if (!IS_SPEC (sym->etype)) + { + sym->etype = sym->etype->next = newLink (SPECIFIER); + } + SPEC_SCLS (sym->etype) = SPEC_SCLS (DCL_TSPEC (p)); + SPEC_ABSA (sym->etype) |= SPEC_ABSA (DCL_TSPEC (p)); + SPEC_ADDR (sym->etype) |= SPEC_ADDR (DCL_TSPEC (p)); + DCL_TSPEC (p) = NULL; + } + + // if there is a function in this type chain + if (p && funcInChain (sym->type)) + { + processFuncArgs (sym); + } + + return; +} + +/*------------------------------------------------------------------ + checkTypeSanity: prevent the user from doing e.g.: + unsigned float uf; + ------------------------------------------------------------------*/ +void +checkTypeSanity (sym_link *etype, const char *name) +{ + char *noun; + + if (!etype) + { + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "sanity check skipped for %s (etype==0)\n", name); + } + return; + } + + if (!IS_SPEC (etype)) + { + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "sanity check skipped for %s (!IS_SPEC)\n", name); + } + return; + } + + noun = nounName (etype); + + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "checking sanity for %s %p\n", name, (void *)etype); + } + + if ((SPEC_NOUN (etype) == V_BOOL || + SPEC_NOUN (etype) == V_CHAR || + SPEC_NOUN (etype) == V_FLOAT || + SPEC_NOUN (etype) == V_FIXED16X16 || + SPEC_NOUN (etype) == V_DOUBLE || SPEC_NOUN (etype) == V_VOID) && (SPEC_SHORT (etype) || SPEC_LONG (etype) || SPEC_LONGLONG (etype))) + { // long or short for char float double or void + werror (E_LONG_OR_SHORT_INVALID, noun, name); + } + if ((SPEC_NOUN (etype) == V_BOOL || + SPEC_NOUN (etype) == V_FLOAT || + SPEC_NOUN (etype) == V_FIXED16X16 || + SPEC_NOUN (etype) == V_DOUBLE || SPEC_NOUN (etype) == V_VOID) && (etype->select.s.b_signed || SPEC_USIGN (etype))) + { // signed or unsigned for float double or void + werror (E_SIGNED_OR_UNSIGNED_INVALID, noun, name); + } + + // special case for "short" + if (SPEC_SHORT (etype)) + { + SPEC_NOUN (etype) = V_INT; + SPEC_SHORT (etype) = 0; + } + + /* if no noun e.g. + "const a;" or "data b;" or "signed s" or "long l" + assume an int */ + if (!SPEC_NOUN (etype)) + { + SPEC_NOUN (etype) = V_INT; + if (!(SPEC_SHORT (etype) || SPEC_LONG (etype) || SPEC_LONGLONG (etype) || SPEC_SIGN (etype) || SPEC_USIGN (etype))) + werror (options.std_c99 ? E_NO_TYPE_SPECIFIER : W_NO_TYPE_SPECIFIER, name); + } + + /* ISO/IEC 9899 J.3.9 implementation defined behaviour: */ + /* a "plain" int bitfield is unsigned */ + if (SPEC_NOUN (etype) == V_BIT || SPEC_NOUN (etype) == V_SBIT) + { + if (!etype->select.s.b_signed) + SPEC_USIGN (etype) = 1; + } + + if (etype->select.s.b_signed && SPEC_USIGN (etype)) + { // signed AND unsigned + werror (E_SIGNED_AND_UNSIGNED_INVALID, noun, name); + } + if (SPEC_SHORT (etype) && SPEC_LONG (etype)) + { // short AND long + werror (E_LONG_AND_SHORT_INVALID, noun, name); + } +} + +/*------------------------------------------------------------------*/ +/* finalizeSpec */ +/* currently just a V_CHAR is forced to be unsigned */ +/* when it's neither signed nor unsigned */ +/* unless the --fsigned-char command line switch is active */ +/*------------------------------------------------------------------*/ +sym_link * +finalizeSpec (sym_link * lnk) +{ + sym_link *p = lnk; + while (p && !IS_SPEC (p)) + p = p->next; + if (SPEC_NOUN (p) == V_CHAR && !SPEC_USIGN (p) && !p->select.s.b_signed) + { + SPEC_USIGN (p) = !options.signed_char; + p->select.s.b_implicit_sign = true; + } + return lnk; +} + +/*------------------------------------------------------------------*/ +/* mergeSpec - merges two specifiers and returns the new one */ +/*------------------------------------------------------------------*/ +sym_link * +mergeSpec (sym_link * dest, sym_link * src, const char *name) +{ + unsigned int i; + + if (!IS_SPEC (dest) || !IS_SPEC (src)) + { +#if 0 + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "cannot merge declarator"); + exit (1); +#else + werror (E_SYNTAX_ERROR, yytext); + // the show must go on + return newIntLink (); +#endif + } + + if (!options.std_c11 && !options.std_c99) + { + if (SPEC_SIGN (dest) && SPEC_SIGN (src)) + werror (W_REPEAT_QUALIFIER, "signed"); + if (SPEC_USIGN (dest) && SPEC_USIGN (src)) + werror (W_REPEAT_QUALIFIER, "unsigned"); + if (SPEC_CONST (dest) && SPEC_CONST (src)) + werror (W_REPEAT_QUALIFIER, "const"); + if (SPEC_VOLATILE (dest) && SPEC_VOLATILE (src)) + werror (W_REPEAT_QUALIFIER, "volatile"); + if (SPEC_STAT (dest) && SPEC_STAT (src)) + werror (W_REPEAT_QUALIFIER, "static"); + if (SPEC_EXTR (dest) && SPEC_EXTR (src)) + werror (W_REPEAT_QUALIFIER, "extern"); + if (SPEC_TYPEDEF (dest) && SPEC_TYPEDEF (src)) + werror (W_REPEAT_QUALIFIER, "typedef"); + if (SPEC_SCLS (dest) == S_REGISTER && SPEC_SCLS (src) == S_REGISTER) + werror (W_REPEAT_QUALIFIER, "register"); + if (SPEC_SCLS (dest) == S_AUTO && SPEC_SCLS (src) == S_AUTO) + werror (W_REPEAT_QUALIFIER, "auto"); + } + + if (SPEC_NOUN (src)) + { + if (!SPEC_NOUN (dest)) + { + SPEC_NOUN (dest) = SPEC_NOUN (src); + } + else + { + /* we shouldn't redeclare the type */ + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "mergeSpec: "); + } + werror (E_TWO_OR_MORE_DATA_TYPES, name); + } + } + + if ((SPEC_SHORT (src) || SPEC_LONG (src) || SPEC_LONGLONG (src)) && + (SPEC_SHORT (dest) || SPEC_LONG (dest) || SPEC_LONGLONG (dest))) + { + if (!(options.std_c99 && SPEC_LONG (src) && SPEC_LONG (dest) && !TARGET_PIC_LIKE)) /* C99 has long long */ + werror (E_SHORTLONG, name); + } + + if (SPEC_SCLS (src)) + { + /* if destination has no storage class */ + if (!SPEC_SCLS (dest)) + { + SPEC_SCLS (dest) = SPEC_SCLS (src); + } + else if (SPEC_SCLS (dest) == S_REGISTER && SPEC_SCLS (src) != S_AUTO) + { + SPEC_SCLS (dest) = SPEC_SCLS (src); + } + else + { + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "mergeSpec: "); + } + werror (E_TWO_OR_MORE_STORAGE_CLASSES, name); + } + } + + /* copy all the specifications */ + + // we really should do: +#if 0 + if (SPEC_what (src)) + { + if (SPEC_what (dest)) + { + werror (W_DUPLICATE_SPEC, "what"); + } + SPEC_what (dst) |= SPEC_what (src); + } +#endif + // but there are more important thing right now + + if (options.std_c99 && SPEC_LONG (src) && SPEC_LONG (dest)) + { + SPEC_LONG (dest) = 0; + SPEC_LONGLONG (dest) = 1; + } + else + SPEC_LONG (dest) |= SPEC_LONG (src); + SPEC_LONGLONG (dest) |= SPEC_LONGLONG (src); + SPEC_SHORT (dest) |= SPEC_SHORT (src); + SPEC_USIGN (dest) |= SPEC_USIGN (src); + dest->select.s.b_signed |= src->select.s.b_signed; + SPEC_STAT (dest) |= SPEC_STAT (src); + SPEC_EXTR (dest) |= SPEC_EXTR (src); + SPEC_INLINE (dest) |= SPEC_INLINE (src); + SPEC_NORETURN (dest) |= SPEC_NORETURN(src); + SPEC_CONST (dest) |= SPEC_CONST (src); + SPEC_ABSA (dest) |= SPEC_ABSA (src); + SPEC_VOLATILE (dest) |= SPEC_VOLATILE (src); + SPEC_RESTRICT (dest) |= SPEC_RESTRICT (src); + SPEC_ADDR (dest) |= SPEC_ADDR (src); + SPEC_OCLS (dest) = SPEC_OCLS (src); + SPEC_BLEN (dest) |= SPEC_BLEN (src); + SPEC_BSTR (dest) |= SPEC_BSTR (src); + SPEC_TYPEDEF (dest) |= SPEC_TYPEDEF (src); + SPEC_ENUM (dest) |= SPEC_ENUM (src); + if (SPEC_ARGREG (src) && !SPEC_ARGREG (dest)) + SPEC_ARGREG (dest) = SPEC_ARGREG (src); + + if (SPEC_STAT (dest) && SPEC_EXTR (dest)) + werror (E_TWO_OR_MORE_STORAGE_CLASSES, name); + + if (IS_STRUCT (dest) && SPEC_STRUCT (dest) == NULL) + SPEC_STRUCT (dest) = SPEC_STRUCT (src); + + if (FUNC_ISISR (dest) && FUNC_ISISR (src)) + werror (E_INT_MULTIPLE, name); + + /* these are the only function attributes that will be set + in a specifier while parsing */ + FUNC_NONBANKED (dest) |= FUNC_NONBANKED (src); + FUNC_BANKED (dest) |= FUNC_BANKED (src); + FUNC_ISCRITICAL (dest) |= FUNC_ISCRITICAL (src); + FUNC_ISREENT (dest) |= FUNC_ISREENT (src); + FUNC_ISNAKED (dest) |= FUNC_ISNAKED (src); + FUNC_ISISR (dest) |= FUNC_ISISR (src); + FUNC_ISJAVANATIVE (dest) |= FUNC_ISJAVANATIVE (src); + FUNC_ISBUILTIN (dest) |= FUNC_ISBUILTIN (src); + FUNC_ISOVERLAY (dest) |= FUNC_ISOVERLAY (src); + FUNC_INTNO (dest) |= FUNC_INTNO (src); + FUNC_REGBANK (dest) |= FUNC_REGBANK (src); + FUNC_ISINLINE (dest) |= FUNC_ISINLINE (src); + FUNC_ISNORETURN (dest) |= FUNC_ISNORETURN (src); + FUNC_ISSMALLC (dest) |= FUNC_ISSMALLC (src); + FUNC_ISZ88DK_FASTCALL (dest) |= FUNC_ISZ88DK_FASTCALL (src); + FUNC_ISZ88DK_CALLEE (dest) |= FUNC_ISZ88DK_CALLEE (src); + for (i = 0; i < 9; i++) + dest->funcAttrs.preserved_regs[i] |= src->funcAttrs.preserved_regs[i]; + + if (SPEC_ADDRSPACE (src) && SPEC_ADDRSPACE (dest)) + werror (E_TWO_OR_MORE_STORAGE_CLASSES, name); + if (SPEC_ADDRSPACE (src)) + SPEC_ADDRSPACE (dest) = SPEC_ADDRSPACE (src); + + if (SPEC_ALIGNAS (src) > SPEC_ALIGNAS (dest)) + SPEC_ALIGNAS (dest) = SPEC_ALIGNAS (src); + if (SPEC_SCLS (dest) == S_REGISTER && SPEC_ALIGNAS (dest)) + werror (E_ALIGNAS, SPEC_ALIGNAS (dest)); + + return dest; +} + +/*------------------------------------------------------------------*/ +/* mergeDeclSpec - merges a specifier and a declarator */ +/*------------------------------------------------------------------*/ +sym_link * +mergeDeclSpec (sym_link * dest, sym_link * src, const char *name) +{ + sym_link *decl, *spec, *lnk; + + if (IS_SPEC (src)) + { + if (IS_SPEC (dest)) + { + return mergeSpec (dest, src, name); + } + else + { + decl = dest; + spec = src; + } + } + else + { + if (IS_SPEC (dest)) + { + decl = src; + spec = dest; + } + else + { + werror (E_SYNTAX_ERROR, yytext); + // the show must go on + return newIntLink (); + } + } + + DCL_PTR_CONST (decl) |= SPEC_CONST (spec); + DCL_PTR_VOLATILE (decl) |= SPEC_VOLATILE (spec); + DCL_PTR_RESTRICT (decl) |= SPEC_RESTRICT (spec); + if (DCL_PTR_ADDRSPACE (decl) && SPEC_ADDRSPACE (spec) && + strcmp (DCL_PTR_ADDRSPACE (decl)->name, SPEC_ADDRSPACE (spec)->name)) + werror (E_SYNTAX_ERROR, yytext); + if (SPEC_ADDRSPACE (spec)) + DCL_PTR_ADDRSPACE (decl) = SPEC_ADDRSPACE (spec); + + lnk = decl; + while (lnk && !IS_SPEC (lnk->next)) + lnk = lnk->next; + lnk->next = mergeSpec (spec, lnk->next, name); + return decl; +} + +/*-------------------------------------------------------------------*/ +/* genSymName - generates and returns a name used for anonymous vars */ +/*-------------------------------------------------------------------*/ +char * +genSymName (long level) +{ + static int gCount = 0; + static char gname[SDCC_NAME_MAX + 1]; + + SNPRINTF (gname, sizeof (gname), "__%04d%04d", level, gCount++); + return gname; +} + +/*------------------------------------------------------------------*/ +/* getSpec - returns the specifier part from a declaration chain */ +/*------------------------------------------------------------------*/ +sym_link * +getSpec (sym_link * p) +{ + while (p && !(IS_SPEC (p))) + p = p->next; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newCharLink() - creates an char type */ +/*------------------------------------------------------------------*/ +sym_link * +newCharLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_CHAR; + SPEC_USIGN (p) = 1; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newFloatLink - a new Float type */ +/*------------------------------------------------------------------*/ +sym_link * +newFloatLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_FLOAT; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newFixed16x16Link - a new Float type */ +/*------------------------------------------------------------------*/ +sym_link * +newFixed16x16Link () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_FIXED16X16; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newLongLink() - new long type */ +/*------------------------------------------------------------------*/ +sym_link * +newLongLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_INT; + SPEC_LONG (p) = 1; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newLongLongLink() - new long long type */ +/*------------------------------------------------------------------*/ +sym_link * +newLongLongLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_INT; + SPEC_LONGLONG (p) = 1; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newIntLink() - creates an int type */ +/*------------------------------------------------------------------*/ +sym_link * +newIntLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_INT; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newBoolLink() - creates an bool type */ +/*------------------------------------------------------------------*/ +sym_link * +newBoolLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + if (bit) + SPEC_NOUN (p) = V_BIT; + else + SPEC_NOUN (p) = V_BOOL; + + return p; +} + +/*------------------------------------------------------------------*/ +/* newVoidLink() - creates an void type */ +/*------------------------------------------------------------------*/ +sym_link * +newVoidLink () +{ + sym_link *p; + + p = newLink (SPECIFIER); + SPEC_NOUN (p) = V_VOID; + + return p; +} + +/*------------------------------------------------------------------*/ +/* getSize - returns size of a type chain in bytes */ +/*------------------------------------------------------------------*/ +unsigned int +getSize (sym_link * p) +{ + /* if nothing return 0 */ + if (!p) + return 0; + if (IS_SPEC (p)) + { /* if this is the specifier then */ + switch (SPEC_NOUN (p)) + { /* depending on the specifier type */ + case V_INT: + return (IS_LONGLONG (p) ? LONGLONGSIZE : (IS_LONG (p) ? LONGSIZE : INTSIZE)); + case V_FLOAT: + return FLOATSIZE; + case V_FIXED16X16: + return (4); + case V_BOOL: + return BOOLSIZE; + case V_CHAR: + return CHARSIZE; + case V_VOID: + return 0; + case V_STRUCT: + return SPEC_STRUCT (p)->size; + case V_LABEL: + return 0; + case V_SBIT: + case V_BIT: + return BITSIZE; + case V_BITFIELD: + case V_BBITFIELD: + return ((SPEC_BLEN (p) / 8) + (SPEC_BLEN (p) % 8 ? 1 : 0)); + default: + return 0; + } + } + + /* this is a declarator */ + switch (DCL_TYPE (p)) + { + case ARRAY: + if (DCL_ELEM (p)) + { + return DCL_ELEM (p) * getSize (p->next); + } + else + { + return 0; + } + case IPOINTER: + case PPOINTER: + case POINTER: + return (NEARPTRSIZE); + case EEPPOINTER: + case FPOINTER: + case CPOINTER: + case FUNCTION: + return (IFFUNC_ISBANKEDCALL (p) ? BFUNCPTRSIZE : FUNCPTRSIZE); + case GPOINTER: + return (GPTRSIZE); + + default: + return 0; + } +} + +#define FLEXARRAY 1 +#define INCOMPLETE 2 + +/*------------------------------------------------------------------*/ +/* checkStructFlexArray - check tree behind a struct */ +/*------------------------------------------------------------------*/ +static int +checkStructFlexArray (symbol * sym, sym_link * p) +{ + /* if nothing return FALSE */ + if (!p) + return 0; + + if (IS_SPEC (p)) + { + /* (nested) struct with flexible array member? */ + if (IS_STRUCT (p) && SPEC_STRUCT (p)->b_flexArrayMember) + { + werror (W_INVALID_FLEXARRAY); + return INCOMPLETE; + } + /* or otherwise incomplete (nested) struct? */ + if (IS_STRUCT (p) && ((SPEC_STRUCT (p)->size == 0) || !SPEC_STRUCT (p)->fields)) + { + return INCOMPLETE; + } + return 0; + } + + /* this is a declarator */ + if (IS_ARRAY (p)) + { + /* flexible array member? */ + if (!DCL_ELEM (p)) + { + if (!options.std_c99) + werror (W_C89_NO_FLEXARRAY); + if (checkStructFlexArray (sym, p->next) == INCOMPLETE) + werror (E_INCOMPLETE_FIELD, sym->name); + return FLEXARRAY; + } + /* walk tree */ + return checkStructFlexArray (sym, p->next); + } + return 0; +} + +/*------------------------------------------------------------------*/ +/* bitsForType - returns # of bits required to store this type */ +/*------------------------------------------------------------------*/ +unsigned int +bitsForType (sym_link * p) +{ + /* if nothing return 0 */ + if (!p) + return 0; + + if (IS_SPEC (p)) + { /* if this is the specifier then */ + switch (SPEC_NOUN (p)) + { /* depending on the specifier type */ + case V_INT: + if (IS_LONGLONG (p)) + return LONGLONGSIZE * 8; + if (IS_LONG (p)) + return LONGSIZE * 8; + return INTSIZE * 8; + case V_FLOAT: + return FLOATSIZE * 8; + case V_FIXED16X16: + return (32); + case V_BOOL: + return BOOLSIZE * 8; + case V_CHAR: + return CHARSIZE * 8; + case V_VOID: + return 0; + case V_STRUCT: + return SPEC_STRUCT (p)->size * 8; + case V_LABEL: + return 0; + case V_SBIT: + case V_BIT: + return 1; + case V_BITFIELD: + case V_BBITFIELD: + return SPEC_BLEN (p); + default: + return 0; + } + } + + /* this is a specifier */ + switch (DCL_TYPE (p)) + { + case ARRAY: + return DCL_ELEM (p) * getSize (p->next) * 8; + case IPOINTER: + case PPOINTER: + case POINTER: + return (NEARPTRSIZE * 8); + case EEPPOINTER: + case FPOINTER: + case CPOINTER: + case FUNCTION: + return (FARPTRSIZE * 8); + case GPOINTER: + return (GPTRSIZE * 8); + default: + return 0; + } +} + +/*------------------------------------------------------------------*/ +/* copySymbolChain - copies a symbol chain */ +/*------------------------------------------------------------------*/ +symbol * +copySymbolChain (const symbol * src) +{ + symbol *dest; + + if (!src) + return NULL; + + dest = copySymbol (src); + dest->next = copySymbolChain (src->next); + return dest; +} + +/*------------------------------------------------------------------*/ +/* copySymbol - makes a copy of a symbol */ +/*------------------------------------------------------------------*/ +symbol * +copySymbol (const symbol * src) +{ + symbol *dest; + + if (!src) + return NULL; + + dest = newSymbol (src->name, src->level); + memcpy (dest, src, sizeof (symbol)); + dest->level = src->level; + dest->block = src->block; + dest->ival = copyIlist (src->ival); + dest->type = copyLinkChain (src->type); + dest->etype = getSpec (dest->type); + dest->next = NULL; + dest->key = src->key; + dest->allocreq = src->allocreq; + return dest; +} + +/*------------------------------------------------------------------*/ +/* reverseSyms - reverses the links for a symbol chain */ +/*------------------------------------------------------------------*/ +symbol * +reverseSyms (symbol * sym) +{ + symbol *prev, *curr, *next; + + if (!sym) + return NULL; + + prev = sym; + curr = sym->next; + + while (curr) + { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + sym->next = (void *) NULL; + return prev; +} + +/*------------------------------------------------------------------*/ +/* reverseLink - reverses the links for a type chain */ +/*------------------------------------------------------------------*/ +sym_link * +reverseLink (sym_link * type) +{ + sym_link *prev, *curr, *next; + + if (!type) + return NULL; + + prev = type; + curr = type->next; + + while (curr) + { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + type->next = (void *) NULL; + return prev; +} + +/*------------------------------------------------------------------*/ +/* addSymChain - adds a symbol chain to the symboltable */ +/*------------------------------------------------------------------*/ +void +addSymChain (symbol ** symHead) +{ + symbol *sym; + symbol *csym = NULL; + symbol **symPtrPtr; + int error = 0; + int elemsFromIval = 0; + + for (sym = *symHead; sym != NULL; sym = sym->next) + { + changePointer (sym->type); + checkTypeSanity (sym->etype, sym->name); + + if (IS_NORETURN (sym->etype)) + { + SPEC_NORETURN (sym->etype) = 0; + FUNC_ISNORETURN (sym->type) = 1; + } + + if (!sym->level && !(IS_SPEC (sym->etype) && IS_TYPEDEF (sym->etype))) + elemsFromIval = checkDecl (sym, 0); + else + { + /* if this is an array without any dimension + then update the dimension from the initial value */ + if (IS_ARRAY (sym->type) && !DCL_ELEM (sym->type)) + elemsFromIval = DCL_ELEM (sym->type) = getNelements (sym->type, sym->ival); + } + + /* if already exists in the symbol table on the same level, ignoring sublevels */ + if ((csym = findSymWithLevel (SymbolTab, sym)) && csym->level / LEVEL_UNIT == sym->level / LEVEL_UNIT) + { + /* if not formal parameter and not in file scope + then show symbol redefined error + else check if symbols have compatible types */ + if (!sym->_isparm && sym->level > 0) + error = 1; + else + { + /* If the previous definition was for an array with incomplete + type, and the new definition has completed the type, update + the original type to match */ + if (IS_ARRAY (csym->type) && IS_ARRAY (sym->type)) + { + if (!DCL_ELEM (csym->type) && DCL_ELEM (sym->type)) + DCL_ELEM (csym->type) = DCL_ELEM (sym->type); + if ((DCL_ELEM (csym->type) > DCL_ELEM (sym->type)) && elemsFromIval) + DCL_ELEM (sym->type) = DCL_ELEM (csym->type); + } + +#if 0 + /* If only one of the definitions used the "at" keyword, copy */ + /* the address to the other. */ + if (IS_SPEC (csym->etype) && SPEC_ABSA (csym->etype) && IS_SPEC (sym->etype) && !SPEC_ABSA (sym->etype)) + { + SPEC_ABSA (sym->etype) = 1; + SPEC_ADDR (sym->etype) = SPEC_ADDR (csym->etype); + } + if (IS_SPEC (csym->etype) && !SPEC_ABSA (csym->etype) && IS_SPEC (sym->etype) && SPEC_ABSA (sym->etype)) + { + SPEC_ABSA (csym->etype) = 1; + SPEC_ADDR (csym->etype) = SPEC_ADDR (sym->etype); + } +#endif + + error = 0; + if (csym->ival && sym->ival) + error = 1; + if (compareTypeExact (csym->type, sym->type, sym->level) != 1) + error = 1; + } + + if (error) + { + /* one definition extern ? */ + if (IS_EXTERN (csym->etype) || IS_EXTERN (sym->etype)) + werror (E_EXTERN_MISMATCH, sym->name); + else + werror (E_DUPLICATE, sym->name); + werrorfl (csym->fileDef, csym->lineDef, E_PREVIOUS_DEF); +#if 0 + fprintf (stderr, "from type '"); + printTypeChain (csym->type, stderr); + if (IS_SPEC (csym->etype) && SPEC_ABSA (csym->etype)) + fprintf (stderr, " at 0x%x", SPEC_ADDR (csym->etype)); + fprintf (stderr, "'\nto type '"); + printTypeChain (sym->type, stderr); + if (IS_SPEC (sym->etype) && SPEC_ABSA (sym->etype)) + fprintf (stderr, " at 0x%x", SPEC_ADDR (sym->etype)); + fprintf (stderr, "'\n"); +#endif + continue; + } + + if (FUNC_BANKED (csym->type) || FUNC_BANKED (sym->type)) + { + if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type)) + { + werror (W_BANKED_WITH_NONBANKED); + FUNC_BANKED (sym->type) = 0; + FUNC_NONBANKED (sym->type) = 1; + } + else + { + FUNC_BANKED (sym->type) = 1; + } + } + else + { + if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type)) + { + FUNC_NONBANKED (sym->type) = 1; + } + } + + if (csym->ival && !sym->ival) + sym->ival = csym->ival; + + if (!csym->cdef && !sym->cdef && IS_EXTERN (sym->etype)) + { + /* if none of symbols is a compiler defined function + and at least one is not extern + then set the new symbol to non extern */ + SPEC_EXTR (sym->etype) = SPEC_EXTR (csym->etype); + } + + /* delete current entry */ + deleteSym (SymbolTab, csym, csym->name); + deleteFromSeg (csym); + + symPtrPtr = symHead; + while (*symPtrPtr && *symPtrPtr != csym) + symPtrPtr = &(*symPtrPtr)->next; + if (*symPtrPtr == csym) + *symPtrPtr = csym->next; + } + + /* add new entry */ + addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 1); + } +} + + +/*------------------------------------------------------------------*/ +/* funcInChain - DCL Type 'FUNCTION' found in type chain */ +/*------------------------------------------------------------------*/ +int +funcInChain (sym_link * lnk) +{ + while (lnk) + { + if (IS_FUNC (lnk)) + return 1; + lnk = lnk->next; + } + return 0; +} + +/*------------------------------------------------------------------*/ +/* structElemType - returns the type info of a struct member */ +/*------------------------------------------------------------------*/ +sym_link * +structElemType (sym_link * stype, value * id) +{ + symbol *fields = (SPEC_STRUCT (stype) ? SPEC_STRUCT (stype)->fields : NULL); + sym_link *type, *etype; + sym_link *petype = getSpec (stype); + + if (fields && id) + { + /* look for the id */ + while (fields) + { + if (strcmp (fields->rname, id->name) == 0) + { + sym_link *t; + type = copyLinkChain (fields->type); + etype = getSpec (type); + SPEC_SCLS (etype) = (SPEC_SCLS (petype) == S_REGISTER ? SPEC_SCLS (etype) : SPEC_SCLS (petype)); + SPEC_OCLS (etype) = (SPEC_SCLS (petype) == S_REGISTER ? SPEC_OCLS (etype) : SPEC_OCLS (petype)); + /* find the first non-array link */ + t = type; + while (IS_ARRAY (t)) + t = t->next; + if (IS_SPEC (t)) + SPEC_CONST (t) |= SPEC_CONST (stype); + else + DCL_PTR_CONST (t) |= SPEC_CONST (stype); + return type; + } + fields = fields->next; + } + } + + werror (E_NOT_MEMBER, id->name); + + // the show must go on + return newIntLink (); +} + +/*------------------------------------------------------------------*/ +/* getStructElement - returns element of a tructure definition */ +/*------------------------------------------------------------------*/ +symbol * +getStructElement (structdef * sdef, symbol * sym) +{ + symbol *field; + + for (field = sdef->fields; field; field = field->next) + if (strcmp (field->name, sym->name) == 0) + return field; + + werror (E_NOT_MEMBER, sym->name); + + return sdef->fields; +} + +/*------------------------------------------------------------------*/ +/* compStructSize - computes the size of a structure */ +/*------------------------------------------------------------------*/ +int +compStructSize (int su, structdef * sdef) +{ + int sum = 0, usum = 0; + int bitOffset = 0; + symbol *loop; + const int oldlineno = lineno; + + if (!sdef->fields) + { + werror (E_UNKNOWN_SIZE, sdef->tag); + } + + /* for the identifiers */ + loop = sdef->fields; + while (loop) + { + lineno = loop->lineDef; + + /* create the internal name for this variable */ + SNPRINTF (loop->rname, sizeof (loop->rname), "_%s", loop->name); + if (su == UNION) + { + sum = 0; + bitOffset = 0; + } + SPEC_VOLATILE (loop->etype) |= (su == UNION ? 1 : 0); + + /* if this is a bit field */ + if (loop->bitVar) + { + SPEC_BUNNAMED (loop->etype) = loop->bitUnnamed; + + /* change it to a unsigned bit */ + switch (SPEC_NOUN (loop->etype)) + { + case V_BOOL: + SPEC_NOUN( loop->etype) = V_BBITFIELD; + if (loop->bitVar > 1) + werror (E_BITFLD_SIZE, 1); + break; + case V_CHAR: + SPEC_NOUN (loop->etype) = V_BITFIELD; + if (loop->bitVar > 8) + werror (E_BITFLD_SIZE , 8); + break; + case V_INT: + SPEC_NOUN (loop->etype) = V_BITFIELD; + if (loop->bitVar > port->s.int_size * 8) + werror (E_BITFLD_SIZE , port->s.int_size * 8); + break; + default: + werror (E_BITFLD_TYPE); + } + + /* ISO/IEC 9899 J.3.9 implementation defined behaviour: */ + /* a "plain" int bitfield is unsigned */ + if (!loop->etype->select.s.b_signed) + SPEC_USIGN (loop->etype) = 1; + + if (loop->bitVar == BITVAR_PAD) + { + /* A zero length bitfield forces padding */ + SPEC_BLEN (loop->etype) = 0; + SPEC_BSTR (loop->etype) = bitOffset; + if (bitOffset > 0) + bitOffset = 8; /* padding is not needed when at bit 0 */ + loop->offset = sum; + } + else + { + SPEC_BLEN (loop->etype) = loop->bitVar; + + if (bitOffset == 8) + { + bitOffset = 0; + sum++; + } + /* check if this fit into the remaining */ + /* bits of this byte else align it to the */ + /* next byte boundary */ + if (loop->bitVar <= (8 - bitOffset)) + { + /* fits into current byte */ + loop->offset = sum; + SPEC_BSTR (loop->etype) = bitOffset; + bitOffset += loop->bitVar; + } + else if (!bitOffset) + { + /* does not fit, but is already byte aligned */ + loop->offset = sum; + SPEC_BSTR (loop->etype) = bitOffset; + bitOffset += loop->bitVar; + } + else + { + if (TARGET_IS_PIC16 && getenv ("PIC16_PACKED_BITFIELDS")) + { + /* if PIC16 && enviroment variable is set, then + * tightly pack bitfields, this means that when a + * bitfield goes beyond byte alignment, do not + * automatically start allocatint from next byte, + * but also use the available bits first */ + fprintf (stderr, ": packing bitfields in structures\n"); + SPEC_BSTR (loop->etype) = bitOffset; + bitOffset += loop->bitVar; + loop->offset = (su == UNION ? sum = 0 : sum); + } + else + { + /* does not fit; need to realign first */ + sum++; + loop->offset = (su == UNION ? sum = 0 : sum); + bitOffset = 0; + SPEC_BSTR (loop->etype) = bitOffset; + bitOffset += loop->bitVar; + } + } + while (bitOffset > 8) + { + bitOffset -= 8; + sum++; + } + } + } + else + { + /* This is a non-bit field. Make sure we are */ + /* byte aligned first */ + if (bitOffset) + { + sum++; + loop->offset = (su == UNION ? sum = 0 : sum); + bitOffset = 0; + } + loop->offset = sum; + checkDecl (loop, 1); + sum += getSize (loop->type); + + /* search for "flexible array members" */ + /* and do some syntax checks */ + if (su == STRUCT) + { + int ret = checkStructFlexArray (loop, loop->type); + if (ret == FLEXARRAY) + { + /* found a "flexible array member" */ + sdef->b_flexArrayMember = TRUE; + /* is another struct-member following? */ + if (loop->next) + werror (E_FLEXARRAY_NOTATEND, loop->name); + /* is it the first struct-member? */ + else if (loop == sdef->fields) + werror (E_FLEXARRAY_INEMPTYSTRCT, loop->name); + } + else if (ret == INCOMPLETE) + { + werror (E_INCOMPLETE_FIELD, loop->name); + } + } + } + + loop = loop->next; + + /* if union then size = sizeof largest field */ + if (su == UNION) + { + /* For UNION, round up after each field */ + sum += ((bitOffset + 7) / 8); + usum = max (usum, sum); + } + } + + /* For STRUCT, round up after all fields processed */ + if (su != UNION) + sum += ((bitOffset + 7) / 8); + + lineno = oldlineno; + + return (su == UNION ? usum : sum); +} + +/*-------------------------------------------------------------------*/ +/* promoteAnonStructs - promote anonymous struct/union's fields into */ +/* an enclosing struct/union */ +/*-------------------------------------------------------------------*/ +void +promoteAnonStructs (int su, structdef * sdef) +{ + symbol *field; + symbol *subfield; + symbol **tofield; + symbol *nextfield; + symbol *dupfield; + int base; + + tofield = &sdef->fields; + field = sdef->fields; + while (field) + { + nextfield = field->next; + if (!*field->name && IS_STRUCT (field->type)) + { + /* Found an anonymous struct/union. Replace it */ + /* with the fields it contains and adjust all */ + /* the offsets */ + + /* tagged anonymous struct/union is rejected here, though gcc allow it */ + if (SPEC_STRUCT (field->type)->tagsym != NULL) + werrorfl (field->fileDef, field->lineDef, E_ANONYMOUS_STRUCT_TAG, SPEC_STRUCT (field->type)->tag); + + base = field->offset; + subfield = copySymbolChain (SPEC_STRUCT (field->type)->fields); + if (!subfield) + continue; /* just in case it's empty */ + + *tofield = subfield; + for (;;) + { + /* check for field name conflicts resulting from promotion */ + dupfield = sdef->fields; + while (dupfield && dupfield != subfield) + { + if (*subfield->name && !strcmp (dupfield->name, subfield->name)) + { + werrorfl (subfield->fileDef, subfield->lineDef, + E_DUPLICATE_MEMBER, su == STRUCT ? "struct" : "union", subfield->name); + werrorfl (dupfield->fileDef, dupfield->lineDef, E_PREVIOUS_DEF); + } + dupfield = dupfield->next; + } + + subfield->offset += base; + if (subfield->next) + subfield = subfield->next; + else + break; + } + subfield->next = nextfield; + tofield = &subfield->next; + } + else + tofield = &field->next; + field = nextfield; + } +} + + +/*------------------------------------------------------------------*/ +/* checkSClass - check the storage class specification */ +/*------------------------------------------------------------------*/ +static void +checkSClass (symbol *sym, int isProto) +{ + sym_link *t; + + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "checkSClass: %s \n", sym->name); + } + + /* type is literal can happen for enums change to auto */ + if (SPEC_SCLS (sym->etype) == S_LITERAL && !SPEC_ENUM (sym->etype)) + SPEC_SCLS (sym->etype) = S_AUTO; + + /* if sfr or sbit then must also be volatile */ + if (SPEC_SCLS (sym->etype) == S_SBIT || SPEC_SCLS (sym->etype) == S_SFR) + { + SPEC_VOLATILE (sym->etype) = 1; + } + + if (SPEC_NEEDSPAR (sym->etype)) + { + werrorfl (sym->fileDef, sym->lineDef, E_QUALIFIED_ARRAY_NOPARAM); + SPEC_NEEDSPAR (sym->etype) = 0; + } + + /* make sure restrict is only used with pointers */ + if (SPEC_RESTRICT (sym->etype)) + { + werrorfl (sym->fileDef, sym->lineDef, E_BAD_RESTRICT); + SPEC_RESTRICT (sym->etype) = 0; + } + + t = sym->type; + while (t) + { + if (IS_DECL (t) && DCL_PTR_RESTRICT (t) && !(IS_PTR (t) && !IS_FUNCPTR(t))) + { + werrorfl (sym->fileDef, sym->lineDef, E_BAD_RESTRICT); + DCL_PTR_RESTRICT (t) = 0; + break; + } + t = t->next; + } + + /* if absolute address given then it mark it as + volatile -- except in the PIC port */ + +#if !OPT_DISABLE_PIC14 || !OPT_DISABLE_PIC16 + /* The PIC port uses a different peep hole optimizer based on "pCode" */ + if (!TARGET_PIC_LIKE) +#endif + + if (IS_ABSOLUTE (sym->etype)) + SPEC_VOLATILE (sym->etype) = 1; + + if (TARGET_IS_MCS51 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + int n, size; + unsigned addr; + + if (SPEC_NOUN (sym->etype) == V_CHAR) + size = 8; + else if (SPEC_LONG (sym->etype) == 0) + size = 16; + else if (SPEC_LONGLONG (sym->etype) == 0) + size = 32; + else + size = 64; + + addr = SPEC_ADDR (sym->etype); + for (n = 0; n < size; n += 8) + if (((addr >> n) & 0xFF) < 0x80) + werror (W_SFR_ABSRANGE, sym->name); + } + else if (TARGET_Z80_LIKE && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + if (SPEC_ADDR (sym->etype) > (FUNC_REGBANK (sym->type) ? 0xffff : 0xff)) + werror (W_SFR_ABSRANGE, sym->name); + } + else if (TARGET_IS_PDK13 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + if (SPEC_ADDR (sym->etype) > 0x1f) + werror (W_SFR_ABSRANGE, sym->name); + } + else if (TARGET_IS_PDK14 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + if (SPEC_ADDR (sym->etype) > 0x3f) + werror (W_SFR_ABSRANGE, sym->name); + } + else if (TARGET_IS_PDK15 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + if (SPEC_ADDR (sym->etype) > 0x7f) + werror (W_SFR_ABSRANGE, sym->name); + } + else if (TARGET_IS_PDK16 && IS_ABSOLUTE (sym->etype) && SPEC_SCLS (sym->etype) == S_SFR) + { + if (SPEC_ADDR (sym->etype) > 0x1f) + werror (W_SFR_ABSRANGE, sym->name); + } + + /* If code memory is read only, then pointers to code memory */ + /* implicitly point to constants -- make this explicit */ + CodePtrPointsToConst (sym->type); + + /* global variables declared const put into code */ + /* if no other storage class specified */ + if ((sym->level == 0 || SPEC_STAT(sym->etype)) && SPEC_SCLS (sym->etype) == S_FIXED && !IS_FUNC (sym->type)) + { + /* find the first non-array link */ + t = sym->type; + while (IS_ARRAY (t)) + t = t->next; + if (IS_CONSTANT (t)) + SPEC_SCLS (sym->etype) = S_CODE; + } + + /* global variable in code space is a constant */ + if ((sym->level == 0 || SPEC_STAT(sym->etype)) && SPEC_SCLS (sym->etype) == S_CODE && port->mem.code_ro) + { + /* find the first non-array link */ + t = sym->type; + while (IS_ARRAY (t)) + t = t->next; + if (IS_SPEC (t)) + SPEC_CONST (t) = 1; + else + DCL_PTR_CONST (t) = 1; + } + + /* if bit variable then no storage class can be */ + /* specified since bit is already a storage */ + if (IS_BITVAR (sym->etype) && + (SPEC_SCLS (sym->etype) != S_FIXED && SPEC_SCLS (sym->etype) != S_SBIT && SPEC_SCLS (sym->etype) != S_BIT)) + { + /* find out if this is the return type of a function */ + t = sym->type; + while (t && t->next != sym->etype) + t = t->next; + if (!t || t->next != sym->etype || !IS_FUNC (t)) + { + werror (E_BITVAR_STORAGE, sym->name); + SPEC_SCLS (sym->etype) = S_FIXED; + } + } + + /* if this is an automatic symbol */ + if (sym->level && (options.stackAuto || reentrant)) + { + if (SPEC_SCLS (sym->etype) != S_BIT && + SPEC_SCLS (sym->etype) != S_REGISTER) + { + if ((SPEC_SCLS (sym->etype) == S_AUTO || + SPEC_SCLS (sym->etype) == S_FIXED || + SPEC_SCLS (sym->etype) == S_STACK || + SPEC_SCLS (sym->etype) == S_XSTACK)) + { + SPEC_SCLS (sym->etype) = S_AUTO; + } + else + { + /* storage class may only be specified for statics */ + if (!IS_STATIC (sym->etype)) + { + werror (E_AUTO_ASSUMED, sym->name); + } + } + } + } + + /* automatic symbols cannot be given */ + /* an absolute address ignore it */ + if (sym->level && !IS_STATIC (sym->etype) && SPEC_ABSA (sym->etype) && (options.stackAuto || reentrant)) + { + werror (E_AUTO_ABSA, sym->name); + SPEC_ABSA (sym->etype) = 0; + } + + if (sym->level && !IS_STATIC (sym->etype) && (IS_DECL (sym->type) ? DCL_PTR_ADDRSPACE (sym->type) : SPEC_ADDRSPACE (sym->type)) && (options.stackAuto || reentrant)) + { + werror (E_AUTO_ADDRSPACE, sym->name); + if (IS_DECL (sym->type)) + DCL_PTR_ADDRSPACE (sym->type) = 0; + else + SPEC_ADDRSPACE (sym->type) = 0; + } + + /* arrays & pointers cannot be defined for bits */ + /* SBITS or SFRs or BIT */ + if ((IS_ARRAY (sym->type) || IS_PTR (sym->type)) && + (SPEC_NOUN (sym->etype) == V_BIT || SPEC_NOUN (sym->etype) == V_SBIT || + SPEC_NOUN (sym->etype) == V_BITFIELD || SPEC_NOUN (sym->etype) == V_BBITFIELD || + SPEC_SCLS (sym->etype) == S_SFR)) + { + /* find out if this is the return type of a function */ + t = sym->type; + while (t && t->next != sym->etype) + t = t->next; + if (t->next != sym->etype || !IS_FUNC (t)) + { + werror (E_BIT_ARRAY, sym->name); + } + } + + /* if this is a bit|sbit then set length & start */ + if (SPEC_NOUN (sym->etype) == V_BIT || SPEC_NOUN (sym->etype) == V_SBIT) + { + SPEC_BLEN (sym->etype) = 1; + SPEC_BSTR (sym->etype) = 0; + } + + if (!isProto) + { + /* variables declared in CODE space must have */ + /* initializers if not an extern, a global or a static */ + if (SPEC_SCLS (sym->etype) == S_CODE && sym->ival == NULL && !sym->_isparm && + IS_AUTO(sym) && + port->mem.code_ro && !SPEC_ABSA (sym->etype) && !funcInChain (sym->type)) + werror (E_CODE_NO_INIT, sym->name); + } + + /* if parameter or local variable then change */ + /* the storage class to reflect where the var will go */ + if (sym->level && SPEC_SCLS (sym->etype) == S_FIXED && !IS_STATIC (sym->etype)) + { + if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type))) + { + SPEC_SCLS (sym->etype) = (options.useXstack ? S_XSTACK : S_STACK); + } + } +} + +/*------------------------------------------------------------------*/ +/* changePointer - change pointer to functions */ +/*------------------------------------------------------------------*/ +void +changePointer (sym_link * p) +{ + /* go thru the chain of declarations */ + /* if we find a pointer to a function */ + /* change it to a ptr to code area */ + /* unless the function is banked. */ + for (; p; p = p->next) + { + if (IS_DECL (p) && DCL_TYPE (p) == UPOINTER) + DCL_TYPE (p) = port->unqualified_pointer; + if (IS_PTR (p) && IS_FUNC (p->next)) + if (!IFFUNC_ISBANKEDCALL (p->next)) + DCL_TYPE (p) = CPOINTER; + } +} + +/*------------------------------------------------------------------*/ +/* checkDecl - does semantic validation of a declaration */ +/*------------------------------------------------------------------*/ +int +checkDecl (symbol * sym, int isProto) +{ + checkSClass (sym, isProto); /* check the storage class */ + changePointer (sym->type); /* change pointers if required */ + + /* if this is an array without any dimension + then update the dimension from the initial value */ + if (IS_ARRAY (sym->type) && !DCL_ELEM (sym->type)) + return DCL_ELEM (sym->type) = getNelements (sym->type, sym->ival); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* copyLinkChain - makes a copy of the link chain & rets ptr 2 head */ +/*------------------------------------------------------------------*/ +sym_link * +copyLinkChain (const sym_link *p) +{ + sym_link *head, *loop; + const sym_link *curr; + + /* note: v_struct and v_struct->fields are not copied! */ + curr = p; + head = loop = (curr ? newLink (p->xclass) : (void *) NULL); + while (curr) + { + memcpy (loop, curr, sizeof (sym_link)); /* copy it */ + loop->next = (curr->next ? newLink (curr->next->xclass) : (void *) NULL); + loop = loop->next; + curr = curr->next; + } + + return head; +} + +/*------------------------------------------------------------------*/ +/* cleanUpBlock - cleansup the symbol table specified for all the */ +/* symbols in the given block */ +/*------------------------------------------------------------------*/ +void +cleanUpBlock (bucket ** table, int block) +{ + int i; + bucket *chain; + + /* go thru the entire table */ + for (i = 0; i < 256; i++) + { + for (chain = table[i]; chain; chain = chain->next) + { + if (chain->block >= block) + { + deleteSym (table, chain->sym, chain->name); + } + } + } +} + +/*------------------------------------------------------------------*/ +/* cleanUpLevel - cleans up the symbol table specified for all the */ +/* symbols in the given level */ +/*------------------------------------------------------------------*/ +void +cleanUpLevel (bucket ** table, long level) +{ + int i; + bucket *chain; + + /* go thru the entire table */ + for (i = 0; i < 256; i++) + { + for (chain = table[i]; chain; chain = chain->next) + { + if (chain->level >= level) + { + deleteSym (table, chain->sym, chain->name); + } + } + } +} + +symbol * +getAddrspace (sym_link *type) +{ + if (IS_DECL (type)) + return (DCL_PTR_ADDRSPACE (type)); + return (SPEC_ADDRSPACE (type)); +} + +/*------------------------------------------------------------------*/ +/* computeTypeOr - computes the resultant type from two types */ +/*------------------------------------------------------------------*/ +static sym_link * +computeTypeOr (sym_link * etype1, sym_link * etype2, sym_link * reType) +{ + /* sanity check */ + assert ((IS_CHAR (etype1) || IS_BOOLEAN (etype1)) && + (IS_CHAR (etype2) || IS_BOOLEAN (etype2))); + + if (SPEC_USIGN (etype1) == SPEC_USIGN (etype2)) + { + SPEC_USIGN (reType) = SPEC_USIGN (etype1); + return reType; + } + + if (SPEC_USIGN (etype1)) + { + if (IS_LITERAL (etype2) && floatFromVal (valFromType (etype2)) >= 0) + SPEC_USIGN (reType) = 1; + else + { + /* promote to int */ + SPEC_USIGN (reType) = 0; + SPEC_NOUN (reType) = V_INT; + } + } + else /* etype1 signed */ + { + if (IS_LITERAL (etype2) && floatFromVal (valFromType (etype2)) <= 127) + SPEC_USIGN (reType) = 0; + else + { + /* promote to int */ + SPEC_USIGN (reType) = 0; + SPEC_NOUN (reType) = V_INT; + } + } + + if (SPEC_USIGN (etype2)) + { + if (IS_LITERAL (etype1) && floatFromVal (valFromType (etype1)) >= 0) + SPEC_USIGN (reType) = 1; + else + { + /* promote to int */ + SPEC_USIGN (reType) = 0; + SPEC_NOUN (reType) = V_INT; + } + } + else /* etype2 signed */ + { + if (IS_LITERAL (etype1) && floatFromVal (valFromType (etype1)) <= 127) + SPEC_USIGN (reType) = 0; + else + { + /* promote to int */ + SPEC_USIGN (reType) = 0; + SPEC_NOUN (reType) = V_INT; + } + } + return reType; +} + +/*------------------------------------------------------------------*/ +/* computeType - computes the resultant type from two types */ +/*------------------------------------------------------------------*/ +sym_link * +computeType (sym_link * type1, sym_link * type2, RESULT_TYPE resultType, int op) +{ + sym_link *rType; + sym_link *reType; + sym_link *etype1 = getSpec (type1); + sym_link *etype2; + + etype2 = type2 ? getSpec (type2) : type1; + + /* Conditional operator has some special type conversion rules */ + if (op == ':') + { + /* If either type is an array, convert to pointer */ + if (IS_ARRAY(type1)) + { + value * val = aggregateToPointer (valFromType (type1)); + type1 = val->type; + Safe_free (val); + etype1 = getSpec (type1); + } + if (IS_ARRAY(type2)) + { + value * val = aggregateToPointer (valFromType (type2)); + type2 = val->type; + Safe_free (val); + etype2 = getSpec (type2); + } + + /* If NULL and another pointer, use the non-NULL pointer type. */ + /* Note that NULL can be defined as either 0 or (void *)0. */ + if (IS_LITERAL (etype1) && + ((IS_PTR (type1) && IS_VOID (type1->next)) || IS_INTEGRAL (type1)) && + (floatFromVal (valFromType (etype1)) == 0) && + IS_PTR (type2)) + return copyLinkChain (type2); + else if (IS_LITERAL (etype2) && + ((IS_PTR (type2) && IS_VOID (type2->next)) || IS_INTEGRAL (type2)) && + (floatFromVal (valFromType (etype2)) == 0) && + IS_PTR (type1)) + return copyLinkChain (type1); + + /* If a void pointer, use the void pointer type */ + else if (IS_PTR(type1) && IS_VOID(type1->next)) + return copyLinkChain (type1); + else if (IS_PTR(type2) && IS_VOID(type2->next)) + return copyLinkChain (type2); + + /* Otherwise fall through to the general case */ + } + + /* shift operators have the important type in the left operand */ + if (op == LEFT_OP || op == RIGHT_OP) + rType = copyLinkChain(type1); + + /* if one of them is a pointer or array then that prevails */ + else if (IS_PTR (type1) || IS_ARRAY (type1)) + rType = copyLinkChain (type1); + else if (IS_PTR (type2) || IS_ARRAY (type2)) + rType = copyLinkChain (type2); + + /* if one of them is a float then result is a float */ + /* here we assume that the types passed are okay */ + /* and can be cast to one another */ + /* which ever is greater in size */ + else if (IS_FLOAT (etype1) || IS_FLOAT (etype2)) + rType = newFloatLink (); + /* if both are fixed16x16 then result is float */ + else if (IS_FIXED16X16 (etype1) && IS_FIXED16X16 (etype2)) + rType = newFixed16x16Link (); + else if (IS_FIXED16X16 (etype1) && IS_FLOAT (etype2)) + rType = newFloatLink (); + else if (IS_FLOAT (etype1) && IS_FIXED16X16 (etype2)) + rType = newFloatLink (); + + /* if only one of them is a bool variable then the other one prevails */ + else if (IS_BOOLEAN (etype1) && !IS_BOOLEAN (etype2)) + { + rType = copyLinkChain (type2); + } + else if (IS_BOOLEAN (etype2) && !IS_BOOLEAN (etype1)) + { + rType = copyLinkChain (type1); + } + + /* if both are bitvars choose the larger one */ + else if (IS_BITVAR (etype1) && IS_BITVAR (etype2)) + rType = SPEC_BLEN (etype1) >= SPEC_BLEN (etype2) ? copyLinkChain (type1) : copyLinkChain (type2); + + /* if only one of them is a bit variable then the other one prevails */ + else if (IS_BITVAR (etype1) && !IS_BITVAR (etype2)) + { + rType = copyLinkChain (type2); + /* bitfield can have up to 16 bits */ + if (getSize (etype1) > 1) + SPEC_NOUN (getSpec (rType)) = V_INT; + } + else if (IS_BITVAR (etype2) && !IS_BITVAR (etype1)) + { + rType = copyLinkChain (type1); + /* bitfield can have up to 16 bits */ + if (getSize (etype2) > 1) + SPEC_NOUN (getSpec (rType)) = V_INT; + } + + else if (bitsForType (type1) > bitsForType (type2)) + rType = copyLinkChain (type1); + else + rType = copyLinkChain (type2); + + reType = getSpec (rType); + + /* avoid conflicting types */ + reType->select.s.b_signed = 0; + + /* if result is a literal then make not so */ + if (IS_LITERAL (reType)) + { + SPEC_SCLS (reType) = S_REGISTER; + SPEC_CONST (reType) = 0; + } + + switch (resultType) + { + case RESULT_TYPE_IFX: + if (TARGET_HC08_LIKE) + break; + //fallthrough + case RESULT_TYPE_BOOL: + if (op == ':') + { + SPEC_NOUN (reType) = V_BIT; + return rType; + } + break; + case RESULT_TYPE_CHAR: + if (IS_BOOL (reType) || IS_BITVAR (reType)) + { + SPEC_NOUN (reType) = V_CHAR; + SPEC_SCLS (reType) = 0; + SPEC_USIGN (reType) = 0; + return rType; + } + break; + case RESULT_TYPE_INT: + case RESULT_TYPE_NONE: + case RESULT_TYPE_OTHER: + case RESULT_TYPE_GPTR: + if (!IS_SPEC (rType)) + { + return rType; + } + if (IS_BOOLEAN (reType)) + { + SPEC_NOUN (reType) = V_CHAR; + SPEC_SCLS (reType) = 0; + SPEC_USIGN (reType) = 0; + return rType; + } + else if (IS_BITFIELD (reType)) + { + /* could be smarter, but it depends on the op */ + /* this is for the worst case: a multiplication of 4 * 4 bit */ + SPEC_NOUN (reType) = SPEC_BLEN (reType) <= 4 ? V_CHAR : V_INT; + SPEC_SCLS (reType) = 0; + SPEC_USIGN (reType) = 0; + return rType; + } + else if (IS_CHAR (reType)) + { + /* promotion of some special cases */ + switch (op) + { + case '|': + case '^': + return computeTypeOr (etype1, etype2, reType); + case '&': + case BITWISEAND: + if (SPEC_USIGN (etype1) != SPEC_USIGN (etype2)) + { + SPEC_USIGN (reType) = 1; + return rType; + } + break; + case '*': + SPEC_NOUN (reType) = V_INT; + SPEC_USIGN (reType) = 0; + return rType; + case '/': + /* if both are unsigned char then no promotion required */ + if (!(SPEC_USIGN (etype1) && SPEC_USIGN (etype2))) + { + SPEC_NOUN (reType) = V_INT; + SPEC_USIGN (reType) = 0; + return rType; + } + break; + default: + break; + } + } + break; + default: + break; + } + + /* SDCC's sign promotion: + - if one or both operands are unsigned, the resultant type will be unsigned + (except char, see below) + - if an operand is promoted to a larger type (char -> int, int -> long), + the larger type will be signed + + SDCC tries hard to avoid promotion to int and does 8 bit calculation as + much as possible. We're leaving ISO IEC 9899 here and have to extrapolate + the standard. The standard demands, that the result has to be the same + "as if" the promotion would have been performed: + + - if the result of an operation with two char's is promoted to a + larger type, the result will be signed. + + More sophisticated are these: + - if the result of an operation with two char's is a char again, + the result will only then be unsigned, if both operands are + unsigned. In all other cases the result will be signed. + + This seems to be contradictionary to the first two rules, but it makes + real sense (all types are char's): + + A signed char can be negative; this must be preserved in the result + -1 * 100 = -100; + + Only if both operands are unsigned it's safe to make the result + unsigned; this helps to avoid overflow: + 2 * 100 = 200; + + - ToDo: document '|', '^' and '&' + + Homework: - why is (200 * 200 < 0) true? + - why is { char l = 200, r = 200; (r * l > 0) } true? + */ + + if (!IS_FLOAT (reType) && ((SPEC_USIGN (etype1) + /* if this operand is promoted to a larger type, + then it will be promoted to a signed type */ + && !(bitsForType (etype1) < bitsForType (reType)) + /* char require special handling */ + && !IS_CHAR (etype1)) || /* same for 2nd operand */ + (SPEC_USIGN (etype2) && !(bitsForType (etype2) < bitsForType (reType)) && !IS_CHAR (etype2)) || /* if both are 'unsigned char' and not promoted + let the result be unsigned too */ + (SPEC_USIGN (etype1) + && SPEC_USIGN (etype2) && IS_CHAR (etype1) && IS_CHAR (etype2) && IS_CHAR (reType)))) + SPEC_USIGN (reType) = 1; + else + SPEC_USIGN (reType) = 0; + + return rType; +} + +/*------------------------------------------------------------------*/ +/* compareFuncType - compare function prototypes */ +/*------------------------------------------------------------------*/ +int +compareFuncType (sym_link * dest, sym_link * src) +{ + value *exargs, *acargs; + value *checkValue; + int argCnt = 0; + int i; + + /* if not type then some kind of error */ + if (!dest || !src) + return 0; + + /* check the return value type */ + if (compareType (dest->next, src->next) <= 0) + return 0; + + /* Really, reentrant should match regardless of argCnt, but */ + /* this breaks some existing code (the fp lib functions). If */ + /* the first argument is always passed the same way, this */ + /* lax checking is ok (but may not be true for in future ports) */ + if (IFFUNC_ISREENT (dest) != IFFUNC_ISREENT (src) && argCnt > 1) + { + //printf("argCnt = %d\n",argCnt); + return 0; + } + + if (IFFUNC_ISWPARAM (dest) != IFFUNC_ISWPARAM (src)) + { + return 0; + } + + if (IFFUNC_ISSHADOWREGS (dest) != IFFUNC_ISSHADOWREGS (src)) + { + return 0; + } + + if (IFFUNC_ISZ88DK_FASTCALL (dest) != IFFUNC_ISZ88DK_FASTCALL (src) || + IFFUNC_ISZ88DK_CALLEE (dest) != IFFUNC_ISZ88DK_CALLEE (src)) + return 0; + + for (i = 0; i < 9; i++) + if (dest->funcAttrs.preserved_regs[i] > src->funcAttrs.preserved_regs[i]) + return 0; + + /* compare register bank */ + if (FUNC_REGBANK (dest) != FUNC_REGBANK (src)) + { /* except for ISR's whose prototype need not match + since they are the top of a call-tree and + the prototype is only necessary for its vector in main */ + if (!IFFUNC_ISISR (dest) || !IFFUNC_ISISR (src)) + { + return 0; + } + } + + /* compare expected args with actual args */ + exargs = FUNC_ARGS (dest); + acargs = FUNC_ARGS (src); + + /* for all the expected args do */ + for (argCnt = 1; exargs && acargs; exargs = exargs->next, acargs = acargs->next, argCnt++) + { + /* If the actual argument is an array, any prototype + * will have modified it to a pointer. Duplicate that + * change here. + */ + if (IS_AGGREGATE (acargs->type)) + { + checkValue = copyValue (acargs); + aggregateToPointer (checkValue); + } + else + { + checkValue = acargs; + } + if (IFFUNC_ISREENT (dest) && compareType (exargs->type, checkValue->type) <= 0) + { + return 0; + } + if (!IFFUNC_ISREENT (dest) && compareTypeExact (exargs->type, checkValue->type, 1) <= 0) + { + return 0; + } + } + + /* if one them ended we have a problem */ + if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type))) + { + return 0; + } + + return 1; +} + +int +comparePtrType (sym_link *dest, sym_link *src, bool bMustCast) +{ + int res; + + if (getAddrspace (src->next) != getAddrspace (dest->next)) + bMustCast = 1; + + if (IS_VOID (src->next) && IS_VOID (dest->next)) + return bMustCast ? -1 : 1; + if ((IS_VOID (src->next) && !IS_VOID (dest->next)) || (!IS_VOID (src->next) && IS_VOID (dest->next))) + return -1; + res = compareType (dest->next, src->next); + + /* All function pointers can be cast (6.6 in the ISO C11 standard) TODO: What about address spaces? */ + if (res == 0 && !bMustCast && IS_DECL (src) && IS_FUNC (src->next) && IS_DECL (dest) && IS_FUNC (dest->next)) + return -1; + else if (res == 1) + return bMustCast ? -1 : 1; + else if (res == -2) + return bMustCast ? -1 : -2; + else + return res; +} + +/*--------------------------------------------------------------------*/ +/* compareType - will do type check return 1 if match, 0 if no match, */ +/* -1 if castable, -2 if only signedness differs */ +/*--------------------------------------------------------------------*/ +int +compareType (sym_link *dest, sym_link *src) +{ + if (!dest && !src) + return 1; + + if (dest && !src) + return 0; + + if (src && !dest) + return 0; + + /* if dest is a declarator then */ + if (IS_DECL (dest)) + { + if (IS_DECL (src)) + { + if (IS_GENPTR (dest) && IS_GENPTR (src)) + { + /* banked function pointer */ + if (IS_FUNC (src->next) && IS_VOID (dest->next)) + return -1; + if (IS_FUNC (dest->next) && IS_VOID (src->next)) + return -1; + return comparePtrType (dest, src, FALSE); + } + + if (DCL_TYPE (src) == DCL_TYPE (dest)) + { + if (IS_FUNC (src)) + { + return compareFuncType (dest, src); + } + return comparePtrType (dest, src, FALSE); + } + if (IS_PTR (dest) && IS_GENPTR (src) && IS_VOID (src->next)) + { + return -1; + } + if (IS_PTR (src) && (IS_GENPTR (dest) || ((DCL_TYPE (src) == POINTER) && (DCL_TYPE (dest) == IPOINTER)))) + { + return comparePtrType (dest, src, TRUE); + } + if (IS_PTR (dest) && IS_ARRAY (src)) + { + value *val = aggregateToPointer (valFromType (src)); + int res = compareType (dest, val->type); + Safe_free (val->type); + Safe_free (val); + return res; + } + if (IS_PTR (dest) && IS_FUNC (dest->next) && IS_FUNC (src)) + { + return compareType (dest->next, src); + } + if (IS_PTR (dest) && IS_VOID (dest->next) && IS_FUNC (src)) + return -1; + + return 0; + } + else if (IS_PTR (dest) && IS_INTEGRAL (src)) + return -1; + else + return 0; + } + + if (IS_PTR (src) && IS_VOID (dest)) + return -1; + + /* if one is a specifier and the other is not */ + if ((IS_SPEC (src) && !IS_SPEC (dest)) || (IS_SPEC (dest) && !IS_SPEC (src))) + return 0; + + /* if one of them is a void then ok */ + if (SPEC_NOUN (dest) == V_VOID && SPEC_NOUN (src) != V_VOID) + return -1; + + if (SPEC_NOUN (dest) != V_VOID && SPEC_NOUN (src) == V_VOID) + return -1; + + if (SPEC_NOUN (src) == V_BBITFIELD && SPEC_NOUN (dest) != V_BBITFIELD || SPEC_NOUN (src) != V_BBITFIELD && SPEC_NOUN (dest) == V_BBITFIELD) + return -1; + + /* if they are both bitfields then if the lengths + and starts don't match */ + if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src))) + return -1; + + /* it is a specifier */ + if (SPEC_NOUN (dest) != SPEC_NOUN (src)) + { + if ((SPEC_USIGN (dest) == SPEC_USIGN (src)) && + IS_INTEGRAL (dest) && IS_INTEGRAL (src) && + /* I would prefer + bitsForType (dest) == bitsForType (src)) + instead of the next two lines, but the regression tests fail with + them; I guess it's a problem with replaceCheaperOp */ + (getSize (dest) == getSize (src)) && + (IS_BOOLEAN (dest) == IS_BOOLEAN (src))) + return 1; + else if (IS_ARITHMETIC (dest) && IS_ARITHMETIC (src)) + return -1; + else + return 0; + } + else if (IS_STRUCT (dest)) + { + if (SPEC_STRUCT (dest) != SPEC_STRUCT (src)) + return 0; + else + return 1; + } + if (SPEC_LONG (dest) != SPEC_LONG (src)) + return -1; + + if (SPEC_LONGLONG (dest) != SPEC_LONGLONG (src)) + return -1; + + if (SPEC_USIGN (dest) != SPEC_USIGN (src)) + return -2; + + return 1; +} + +/*--------------------------------------------------------------------*/ +/* compareTypeExact - will do type check return 1 if match exactly */ +/*--------------------------------------------------------------------*/ +int +compareTypeExact (sym_link * dest, sym_link * src, long level) +{ + STORAGE_CLASS srcScls, destScls; + + if (!dest && !src) + return 1; + + if (dest && !src) + return 0; + + if (src && !dest) + return 0; + + /* if dest is a declarator then */ + if (IS_DECL (dest)) + { + if (IS_DECL (src)) + { + if (DCL_TYPE (src) == DCL_TYPE (dest)) + { + if ((DCL_TYPE (src) == ARRAY) && (DCL_ELEM (src) != DCL_ELEM (dest))) + return 0; + if (DCL_PTR_CONST (src) != DCL_PTR_CONST (dest)) + return 0; + if (DCL_PTR_VOLATILE (src) != DCL_PTR_VOLATILE (dest)) + return 0; + if (IS_FUNC (src)) + { + value *exargs, *acargs, *checkValue; + + /* verify function return type */ + if (!compareTypeExact (dest->next, src->next, -1)) + return 0; + if (FUNC_ISISR (dest) != FUNC_ISISR (src)) + return 0; + if (FUNC_REGBANK (dest) != FUNC_REGBANK (src)) + return 0; + if (IFFUNC_ISNAKED (dest) != IFFUNC_ISNAKED (src)) + return 0; +#if 0 + if (IFFUNC_ISREENT (dest) != IFFUNC_ISREENT (src) && argCnt > 1) + return 0; +#endif + + /* compare expected args with actual args */ + exargs = FUNC_ARGS (dest); + acargs = FUNC_ARGS (src); + + /* for all the expected args do */ + for (; exargs && acargs; exargs = exargs->next, acargs = acargs->next) + { + //checkTypeSanity(acargs->etype, acargs->name); + + if (IS_AGGREGATE (acargs->type)) + { + checkValue = copyValue (acargs); + aggregateToPointer (checkValue); + } + else + checkValue = acargs; + +#if 0 + if (!compareTypeExact (exargs->type, checkValue->type, -1)) + return 0; +#endif + } + + /* if one them ended we have a problem */ + if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type))) + return 0; + return 1; + } + return compareTypeExact (dest->next, src->next, level); + } + return 0; + } + return 0; + } + + /* if one is a specifier and the other is not */ + if ((IS_SPEC (src) && !IS_SPEC (dest)) || (IS_SPEC (dest) && !IS_SPEC (src))) + return 0; + + /* if they have a different noun */ + if (SPEC_NOUN (dest) != SPEC_NOUN (src)) + return 0; + + /* if they are both bitfields then if the lengths + and starts don't match */ + if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src))) + return 0; + + if (IS_INTEGRAL (dest)) + { + /* signedness must match */ + if (SPEC_USIGN (dest) != SPEC_USIGN (src)) + return 0; + /* size must match */ + if (SPEC_SHORT (dest) != SPEC_SHORT (src)) + return 0; + if (SPEC_LONG (dest) != SPEC_LONG (src)) + return 0; + if (SPEC_LONGLONG (dest) != SPEC_LONGLONG (src)) + return 0; + } + + if (IS_STRUCT (dest)) + { + if (SPEC_STRUCT (dest) != SPEC_STRUCT (src)) + return 0; + } + + if (SPEC_CONST (dest) != SPEC_CONST (src)) + return 0; + if (SPEC_VOLATILE (dest) != SPEC_VOLATILE (src)) + return 0; + if (SPEC_STAT (dest) != SPEC_STAT (src)) + return 0; + if (SPEC_ABSA (dest) != SPEC_ABSA (src)) + return 0; + if (SPEC_ABSA (dest) && SPEC_ADDR (dest) != SPEC_ADDR (src)) + return 0; + + destScls = SPEC_SCLS (dest); + srcScls = SPEC_SCLS (src); + + /* Compensate for const to const code change in checkSClass() */ + if (((!level) & port->mem.code_ro) && SPEC_CONST (dest)) + { + if (srcScls == S_CODE && destScls == S_FIXED) + destScls = S_CODE; + if (destScls == S_CODE && srcScls == S_FIXED) + srcScls = S_CODE; + } + + /* compensate for allocGlobal() */ + if ((srcScls == S_FIXED || srcScls == S_AUTO) && + (port->mem.default_globl_map == xdata) && (destScls == S_XDATA) && (level <= 0)) + { + srcScls = S_XDATA; + } + + if ((level > 0) && !SPEC_STAT (dest)) + { + /* Compensate for hack-o-matic in checkSClass() */ + if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type))) + { + if (destScls == S_FIXED) + destScls = (options.useXstack ? S_XSTACK : S_STACK); + if (srcScls == S_FIXED) + srcScls = (options.useXstack ? S_XSTACK : S_STACK); + } + else if (TARGET_IS_DS390 || TARGET_IS_DS400 || options.useXstack || TARGET_IS_HC08 || TARGET_IS_S08) + { + if (destScls == S_FIXED) + destScls = S_XDATA; + if (srcScls == S_FIXED) + srcScls = S_XDATA; + } + } + + if (srcScls != destScls) + { +#if 0 + printf ("level = %ld:%ld\n", level / LEVEL_UNIT, level % LEVEL_UNIT); + printf ("SPEC_SCLS (src) = %d, SPEC_SCLS (dest) = %d\n", SPEC_SCLS (src), SPEC_SCLS (dest)); + printf ("srcScls = %d, destScls = %d\n", srcScls, destScls); +#endif + return 0; + } + + return 1; +} + +/*---------------------------------------------------------------------------*/ +/* compareTypeInexact - will do type check return 1 if representation is same. */ +/* Useful for redundancy elimination. */ +/*---------------------------------------------------------------------------*/ +int +compareTypeInexact (sym_link *dest, sym_link *src) +{ + if (!dest && !src) + return 1; + + if (dest && !src) + return 0; + + if (src && !dest) + return 0; + + if (IS_BITFIELD (dest) != IS_BITFIELD (src)) + return 0; + + if (IS_BITFIELD (dest) && IS_BITFIELD (src) && (SPEC_BLEN (dest) != SPEC_BLEN (src) || SPEC_BSTR (dest) != SPEC_BSTR (src))) + return 0; + + if (getSize (dest) != getSize (src)) + return 0; + + return 1; +} + +/*------------------------------------------------------------------*/ +/* inCalleeSaveList - return 1 if found in callee save list */ +/*------------------------------------------------------------------*/ +static int +calleeCmp (void *p1, void *p2) +{ + return (strcmp ((char *) p1, (char *) (p2)) == 0); +} + +bool +inCalleeSaveList (char *s) +{ + if (options.all_callee_saves) + return 1; + return isinSetWith (options.calleeSavesSet, s, calleeCmp); +} + +/*-----------------------------------------------------------------*/ +/* aggregateToPointer: change an aggregate type function */ +/* argument to a pointer to that type. */ +/*-----------------------------------------------------------------*/ +value * +aggregateToPointer (value * val) +{ + if (IS_AGGREGATE (val->type)) + { + /* if this is a structure */ + if (IS_STRUCT (val->type)) + { + werror (E_STRUCT_AS_ARG, val->name); + return NULL; + } + + /* change to a pointer depending on the */ + /* storage class specified */ + switch (SPEC_SCLS (val->etype)) + { + case S_IDATA: + DCL_TYPE (val->type) = IPOINTER; + break; + case S_PDATA: + DCL_TYPE (val->type) = PPOINTER; + break; + case S_FIXED: + if (SPEC_OCLS (val->etype)) + { + DCL_TYPE (val->type) = PTR_TYPE (SPEC_OCLS (val->etype)); + } + else + { // this happens for (external) function parameters + DCL_TYPE (val->type) = port->unqualified_pointer; + } + break; + case S_AUTO: + DCL_TYPE (val->type) = PTR_TYPE (SPEC_OCLS (val->etype)); + break; + case S_DATA: + case S_REGISTER: + DCL_TYPE (val->type) = POINTER; + break; + case S_CODE: + DCL_TYPE (val->type) = CPOINTER; + break; + case S_XDATA: + DCL_TYPE (val->type) = FPOINTER; + break; + case S_EEPROM: + DCL_TYPE (val->type) = EEPPOINTER; + break; + default: + DCL_TYPE (val->type) = port->unqualified_pointer; + } + + /* is there is a symbol associated then */ + /* change the type of the symbol as well */ + if (val->sym) + { + val->sym->type = copyLinkChain (val->type); + val->sym->etype = getSpec (val->sym->type); + } + } + return val; +} + +/*------------------------------------------------------------------*/ +/* checkFunction - does all kinds of check on a function */ +/*------------------------------------------------------------------*/ +int +checkFunction (symbol * sym, symbol * csym) +{ + value *exargs, *acargs; + value *checkValue; + int argCnt = 0; + + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "checkFunction: %s ", sym->name); + } + + if (!IS_FUNC (sym->type)) + { + werrorfl (sym->fileDef, sym->lineDef, E_SYNTAX_ERROR, sym->name); + return 0; + } + + /* move function specifier from return type to function attributes */ + if (IS_INLINE (sym->etype)) + { + SPEC_INLINE (sym->etype) = 0; + FUNC_ISINLINE (sym->type) = 1; + } + if (IS_NORETURN (sym->etype)) + { + SPEC_NORETURN (sym->etype) = 0; + FUNC_ISNORETURN (sym->type) = 1; + } + + /* make sure the type is complete and sane */ + checkTypeSanity (sym->etype, sym->name); + + /* if not type then some kind of error */ + if (!sym->type) + return 0; + + /* if the function has no type then make it return int */ + if (!sym->type->next) + sym->type->next = sym->etype = newIntLink (); + + /* function cannot return aggregate */ + if (IS_AGGREGATE (sym->type->next)) + { + werrorfl (sym->fileDef, sym->lineDef, E_FUNC_AGGR, sym->name); + return 0; + } + + /* check if this function is defined as calleeSaves + then mark it as such */ + FUNC_CALLEESAVES (sym->type) = inCalleeSaveList (sym->name); + + /* if interrupt service routine */ + /* then it cannot have arguments */ + if (IFFUNC_ARGS (sym->type) && FUNC_ISISR (sym->type)) + { + if (!IS_VOID (FUNC_ARGS (sym->type)->type)) + { + werrorfl (sym->fileDef, sym->lineDef, E_INT_ARGS, sym->name); + FUNC_ARGS (sym->type) = NULL; + } + } + + if (IFFUNC_ISSHADOWREGS (sym->type) && !FUNC_ISISR (sym->type)) + { + werrorfl (sym->fileDef, sym->lineDef, E_SHADOWREGS_NO_ISR, sym->name); + } + + for (argCnt = 1, acargs = FUNC_ARGS (sym->type); acargs; acargs = acargs->next, argCnt++) + { + if (!acargs->sym) + { + // this can happen for reentrant functions + werrorfl (sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, argCnt); + // the show must go on: synthesize a name and symbol + SNPRINTF (acargs->name, sizeof (acargs->name), "_%s_PARM_%d", sym->name, argCnt); + acargs->sym = newSymbol (acargs->name, 1); + SPEC_OCLS (acargs->etype) = istack; + acargs->sym->type = copyLinkChain (acargs->type); + acargs->sym->etype = getSpec (acargs->sym->type); + acargs->sym->_isparm = 1; + strncpyz (acargs->sym->rname, acargs->name, sizeof (acargs->sym->rname)); + } + else if (strcmp (acargs->sym->name, acargs->sym->rname) == 0) + { + // synthesized name + werrorfl (sym->fileDef, sym->lineDef, E_PARAM_NAME_OMITTED, sym->name, argCnt); + } + } + argCnt--; + + /*JCF: Mark the register bank as used */ + RegBankUsed[FUNC_REGBANK (sym->type)] = 1; + + if (!csym && !(csym = findSymWithLevel (SymbolTab, sym))) + return 1; /* not defined nothing more to check */ + + /* check if body already present */ + if (csym && IFFUNC_HASBODY (csym->type)) + { + werrorfl (sym->fileDef, sym->lineDef, E_FUNC_BODY, sym->name); + return 0; + } + + /* check the return value type */ + if (compareType (csym->type, sym->type) <= 0) + { + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "type", csym->fileDef, csym->lineDef); + printFromToType (csym->type, sym->type); + return 0; + } + + if (FUNC_ISISR (csym->type) != FUNC_ISISR (sym->type)) + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "interrupt", csym->fileDef, csym->lineDef); + + /* I don't think this is necessary for interrupts. An isr is a */ + /* root in the calling tree. */ + if ((FUNC_REGBANK (csym->type) != FUNC_REGBANK (sym->type)) && (!FUNC_ISISR (sym->type))) + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "using", csym->fileDef, csym->lineDef); + + if (IFFUNC_ISNAKED (csym->type) != IFFUNC_ISNAKED (sym->type)) + { +// disabled since __naked has no influence on the calling convention +// werror (E_PREV_DECL_CONFLICT, csym->name, "__naked", csym->fileDef, csym->lineDef); + FUNC_ISNAKED (sym->type) = 1; + } + + if (FUNC_BANKED (csym->type) || FUNC_BANKED (sym->type)) + { + if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type)) + { + werrorfl (sym->fileDef, sym->lineDef, W_BANKED_WITH_NONBANKED); + FUNC_BANKED (sym->type) = 0; + FUNC_NONBANKED (sym->type) = 1; + } + else + { + FUNC_BANKED (sym->type) = 1; + } + } + else + { + if (FUNC_NONBANKED (csym->type) || FUNC_NONBANKED (sym->type)) + { + FUNC_NONBANKED (sym->type) = 1; + } + } + + /* Really, reentrant should match regardless of argCnt, but */ + /* this breaks some existing code (the fp lib functions). If */ + /* the first argument is always passed the same way, this */ + /* lax checking is ok (but may not be true for in future backends) */ + if (IFFUNC_ISREENT (csym->type) != IFFUNC_ISREENT (sym->type) && argCnt > 1) + { + //printf("argCnt = %d\n",argCnt); + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "reentrant", csym->fileDef, csym->lineDef); + } + + if (IFFUNC_ISWPARAM (csym->type) != IFFUNC_ISWPARAM (sym->type)) + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "wparam", csym->fileDef, csym->lineDef); + + if (IFFUNC_ISSHADOWREGS (csym->type) != IFFUNC_ISSHADOWREGS (sym->type)) + werrorfl (sym->fileDef, sym->lineDef, E_PREV_DECL_CONFLICT, csym->name, "shadowregs", csym->fileDef, csym->lineDef); + + /* compare expected args with actual args */ + exargs = FUNC_ARGS (csym->type); + acargs = FUNC_ARGS (sym->type); + + /* for all the expected args do */ + for (argCnt = 1; exargs && acargs; exargs = exargs->next, acargs = acargs->next, argCnt++) + { + if (getenv ("DEBUG_SANITY")) + { + fprintf (stderr, "checkFunction: %s ", exargs->name); + } + /* make sure the type is complete and sane */ + checkTypeSanity (exargs->etype, exargs->name); + + /* If the actual argument is an array, any prototype + * will have modified it to a pointer. Duplicate that + * change here. + */ + if (IS_AGGREGATE (acargs->type)) + { + checkValue = copyValue (acargs); + aggregateToPointer (checkValue); + } + else + { + checkValue = acargs; + } + + if (compareType (exargs->type, checkValue->type) <= 0) + { + werror (E_ARG_TYPE, argCnt); + printFromToType (exargs->type, checkValue->type); + return 0; + } + } + + /* if one of them ended we have a problem */ + if ((exargs && !acargs && !IS_VOID (exargs->type)) || (!exargs && acargs && !IS_VOID (acargs->type))) + werror (E_ARG_COUNT); + + /* replace with this definition */ + sym->cdef = csym->cdef; + deleteSym (SymbolTab, csym, csym->name); + deleteFromSeg (csym); + addSym (SymbolTab, sym, sym->name, sym->level, sym->block, 1); + if (IS_EXTERN (csym->etype) && !IS_EXTERN (sym->etype)) + { + SPEC_EXTR (sym->etype) = 1; + addSet (&publics, sym); + } + + SPEC_STAT (sym->etype) |= SPEC_STAT (csym->etype); + if (SPEC_STAT (sym->etype) && SPEC_EXTR (sym->etype)) + { + werrorfl (sym->fileDef, sym->lineDef, E_TWO_OR_MORE_STORAGE_CLASSES, sym->name); + } + + return 1; +} + +/*------------------------------------------------------------------*/ +/* cdbStructBlock - calls struct printing for a blocks */ +/*------------------------------------------------------------------*/ +void +cdbStructBlock (int block) +{ + int i; + bucket **table = StructTab; + bucket *chain; + + /* go thru the entire table */ + for (i = 0; i < 256; i++) + { + for (chain = table[i]; chain; chain = chain->next) + { + if (chain->block >= block) + { + if (debugFile) + debugFile->writeType ((structdef *) chain->sym, chain->block, 0, NULL); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* processFuncPtrArgs - does some processing with args of func ptrs*/ +/*-----------------------------------------------------------------*/ +void +processFuncPtrArgs (sym_link * funcType) +{ + value *val = FUNC_ARGS (funcType); + + /* if it is void then remove parameters */ + if (val && IS_VOID (val->type)) + { + FUNC_ARGS (funcType) = NULL; + return; + } +} + +/*-----------------------------------------------------------------*/ +/* processFuncArgs - does some processing with function args */ +/*-----------------------------------------------------------------*/ +void +processFuncArgs (symbol *func) +{ + value *val; + int pNum = 1; + sym_link *funcType = func->type; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "SDCCsymt.c:processFuncArgs(%s)\n", func->name); + + /* find the function declaration within the type */ + while (funcType && !IS_FUNC (funcType)) + funcType = funcType->next; + + /* if this function has variable argument list */ + /* then make the function a reentrant one */ + if (IFFUNC_HASVARARGS (funcType) || (options.stackAuto && !func->cdef)) + FUNC_ISREENT (funcType) = 1; + + /* check if this function is defined as calleeSaves + then mark it as such */ + FUNC_CALLEESAVES (funcType) = inCalleeSaveList (func->name); + + /* loop thru all the arguments */ + val = FUNC_ARGS (funcType); + + /* if it is void then remove parameters */ + if (val && IS_VOID (val->type)) + { + FUNC_ARGS (funcType) = NULL; + return; + } + + /* reset regparm for the port */ + (*port->reset_regparms) (funcType); + + /* if any of the arguments is an aggregate */ + /* change it to pointer to the same type */ + while (val) + { + int argreg = 0; + struct dbuf_s dbuf; + + if (val->sym && val->sym->name) + for (value *val2 = val->next; val2; val2 = val2->next) + if (val2->sym && val2->sym->name && !strcmp (val->sym->name, val2->sym->name)) + werror (E_DUPLICATE_PARAMTER_NAME, val->sym->name, func->name); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%s parameter %d", func->name, pNum); + checkTypeSanity (val->etype, dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + if (IS_AGGREGATE (val->type)) + { + aggregateToPointer (val); + } + + /* mark it as a register parameter if + the function does not have VA_ARG + and as port dictates */ + if (!IFFUNC_HASVARARGS (funcType) && (argreg = (*port->reg_parm) (val->type, FUNC_ISREENT (funcType)))) + { + SPEC_REGPARM (val->etype) = 1; + SPEC_ARGREG (val->etype) = argreg; + + /* is there is a symbol associated then */ + /* change the type of the symbol as well */ + if (val->sym) + { + SPEC_REGPARM (val->sym->etype) = 1; + SPEC_ARGREG (val->sym->etype) = argreg; + } + } + else if (IFFUNC_ISREENT (funcType)) + { + FUNC_HASSTACKPARM (funcType) = 1; + } + + val = val->next; + pNum++; + } + + /* if this is an internal generated function call */ + if (func->cdef) + { + /* ignore --stack-auto for this one, we don't know how it is compiled */ + /* simply trust on --int-long-reent or --float-reent */ + if (IFFUNC_ISREENT (funcType)) + { + return; + } + } + else + { + /* if this function is reentrant or */ + /* automatics r 2b stacked then nothing */ + if (IFFUNC_ISREENT (funcType) || options.stackAuto) + return; + } + + val = FUNC_ARGS (funcType); + pNum = 1; + while (val) + { + /* if a symbolname is not given */ + /* synthesize a variable name */ + if (!val->sym) + { + SNPRINTF (val->name, sizeof (val->name), "_%s_PARM_%d", func->name, pNum++); + val->sym = newSymbol (val->name, 1); + val->sym->type = copyLinkChain (val->type); + val->sym->etype = getSpec (val->sym->type); + val->sym->_isparm = 1; + if (!defaultOClass (val->sym)) + SPEC_OCLS (val->sym->etype) = port->mem.default_local_map; + SPEC_OCLS (val->etype) = SPEC_OCLS (val->sym->etype); + strncpyz (val->sym->rname, val->name, sizeof (val->sym->rname)); + addSymChain (&val->sym); + } + else /* symbol name given create synth name */ + { + SNPRINTF (val->name, sizeof (val->name), "_%s_PARM_%d", func->name, pNum++); + strncpyz (val->sym->rname, val->name, sizeof (val->sym->rname)); + val->sym->_isparm = 1; + if (!defaultOClass (val->sym)) + SPEC_OCLS (val->sym->etype) = port->mem.default_local_map; + SPEC_OCLS (val->etype) = SPEC_OCLS (val->sym->etype); + } + if (SPEC_OCLS (val->sym->etype) == pdata) + val->sym->iaccess = 1; + if (!isinSet (operKeyReset, val->sym)) + { + addSet (&operKeyReset, val->sym); + applyToSet (operKeyReset, resetParmKey); + } + val = val->next; + } +} + +/*-----------------------------------------------------------------*/ +/* isSymbolEqual - compares two symbols return 1 if they match */ +/*-----------------------------------------------------------------*/ +int +isSymbolEqual (const symbol * dest, const symbol * src) +{ + /* if pointers match then equal */ + if (dest == src) + return 1; + + /* if one of them is null then don't match */ + if (!dest || !src) + return 0; + + /* if both of them have rname match on rname */ + if (dest->rname[0] && src->rname[0]) + return (!strcmp (dest->rname, src->rname)); + + /* otherwise match on name */ + return (!strcmp (dest->name, src->name)); +} + +void +PT (sym_link * type) +{ + printTypeChain (type, 0); +} + +/*-----------------------------------------------------------------*/ +/* printTypeChain - prints the type chain in human readable form */ +/*-----------------------------------------------------------------*/ +void +printTypeChain (sym_link * start, FILE * of) +{ + struct dbuf_s dbuf; + int nlr = 0; + + if (!of) + { + of = stdout; + nlr = 1; + } + + dbuf_init (&dbuf, 1024); + dbuf_printTypeChain (start, &dbuf); + dbuf_write_and_destroy (&dbuf, of); + + if (nlr) + putc ('\n', of); +} + +void +dbuf_printTypeChain (sym_link * start, struct dbuf_s *dbuf) +{ + value *args; + sym_link *type, *search; + STORAGE_CLASS scls; + static struct dbuf_s dbuf2; + + if (start == NULL) + { + dbuf_append_str (dbuf, "void"); + return; + } + + /* Print the chain as it is written in the source: */ + /* start with the last entry. */ + /* However, the storage class at the end of the */ + /* chain really applies to the first in the chain! */ + + for (type = start; type && type->next; type = type->next) + ; + if (IS_SPEC (type)) + scls = SPEC_SCLS (type); + else + scls = 0; + while (type) + { + if (IS_DECL (type)) + { + switch (DCL_TYPE (type)) + { + case FUNCTION: + dbuf_printf (dbuf, "function %s%s", + (IFFUNC_ISBUILTIN (type) ? "__builtin__ " : ""), + (IFFUNC_ISJAVANATIVE (type) ? "_JavaNative " : "")); + dbuf_append_str (dbuf, "( "); + for (args = FUNC_ARGS (type); args; args = args->next) + { + dbuf_printTypeChain (args->type, dbuf); + if (args->next) + dbuf_append_str (dbuf, ", "); + } + dbuf_append_str (dbuf, ")"); + if (IFFUNC_ISREENT (type) && isTargetKeyword("__reentrant")) + dbuf_append_str (dbuf, " __reentrant"); + if (FUNC_REGBANK (type)) + { + dbuf_set_length (&dbuf2, 0); + dbuf_printf (&dbuf2, " __using(%d)", FUNC_REGBANK (type)); + dbuf_append_str (dbuf, dbuf_c_str (&dbuf2)); + } + if (IFFUNC_ISBANKEDCALL (type)) + dbuf_append_str (dbuf, " __banked"); + if (IFFUNC_ISZ88DK_CALLEE (type)) + dbuf_append_str (dbuf, " __z88dk_callee"); + if (IFFUNC_ISZ88DK_FASTCALL (type)) + dbuf_append_str (dbuf, " __z88dk_fastcall"); + for (unsigned char i = 0; i < 9; i++) + if (type->funcAttrs.preserved_regs[i]) + { + dbuf_append_str (dbuf, " __preserves_regs("); + for (; i < 9; i++) + if (type->funcAttrs.preserved_regs[i]) + dbuf_printf (dbuf, " %d", i); + dbuf_append_str (dbuf, " )"); + break; + } + break; + case GPOINTER: + dbuf_append_str (dbuf, "generic*"); + break; + case CPOINTER: + dbuf_append_str (dbuf, "code*"); + break; + case FPOINTER: + dbuf_append_str (dbuf, "xdata*"); + break; + case EEPPOINTER: + dbuf_append_str (dbuf, "eeprom*"); + break; + case POINTER: + dbuf_append_str (dbuf, "near*"); + break; + case IPOINTER: + dbuf_append_str (dbuf, "idata*"); + break; + case PPOINTER: + dbuf_append_str (dbuf, "pdata*"); + break; + case UPOINTER: + dbuf_append_str (dbuf, "unknown*"); + break; + case ARRAY: + if (DCL_ELEM (type)) + { + dbuf_printf (dbuf, "[%u]", (unsigned int) DCL_ELEM (type)); + } + else + { + dbuf_append_str (dbuf, "[]"); + } + break; + default: + dbuf_append_str (dbuf, "unknown?"); + break; + } + if (!IS_FUNC (type)) + { + if (DCL_PTR_VOLATILE (type)) + { + dbuf_append_str (dbuf, " volatile"); + } + if (DCL_PTR_CONST (type)) + { + dbuf_append_str (dbuf, " const"); + } + if (DCL_PTR_RESTRICT (type)) + { + dbuf_append_str (dbuf, " restrict"); + } + } + } + else + { + if (SPEC_VOLATILE (type)) + dbuf_append_str (dbuf, "volatile-"); + if (SPEC_CONST (type)) + dbuf_append_str (dbuf, "const-"); + if (SPEC_USIGN (type)) + dbuf_append_str (dbuf, "unsigned-"); + else if (SPEC_NOUN (type) == V_CHAR) + dbuf_append_str (dbuf, "signed-"); + switch (SPEC_NOUN (type)) + { + case V_INT: + if (IS_LONGLONG (type)) + dbuf_append_str (dbuf, "longlong-"); + else if (IS_LONG (type)) + dbuf_append_str (dbuf, "long-"); + dbuf_append_str (dbuf, "int"); + break; + + case V_BOOL: + dbuf_append_str (dbuf, "_Bool"); + break; + + case V_CHAR: + dbuf_append_str (dbuf, "char"); + break; + + case V_VOID: + dbuf_append_str (dbuf, "void"); + break; + + case V_FLOAT: + dbuf_append_str (dbuf, "float"); + break; + + case V_FIXED16X16: + dbuf_append_str (dbuf, "fixed16x16"); + break; + + case V_STRUCT: + dbuf_printf (dbuf, "struct %s", SPEC_STRUCT (type)->tag); + break; + + case V_SBIT: + dbuf_append_str (dbuf, "sbit"); + break; + + case V_BIT: + dbuf_append_str (dbuf, "bit"); + break; + + case V_BITFIELD: + dbuf_printf (dbuf, "bitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type)); + break; + + case V_BBITFIELD: + dbuf_printf (dbuf, "_Boolbitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type)); + break; + + case V_DOUBLE: + dbuf_append_str (dbuf, "double"); + break; + + default: + dbuf_append_str (dbuf, "unknown type"); + break; + } + } + if (type == start) + { + switch (scls) + { + case S_FIXED: + dbuf_append_str (dbuf, " fixed"); + break; + case S_AUTO: + dbuf_append_str (dbuf, " auto"); + break; + case S_REGISTER: + dbuf_append_str (dbuf, " register"); + break; + case S_DATA: + dbuf_append_str (dbuf, " data"); + break; + case S_XDATA: + dbuf_append_str (dbuf, " xdata"); + break; + case S_SFR: + dbuf_append_str (dbuf, " sfr"); + break; + case S_SBIT: + dbuf_append_str (dbuf, " sbit"); + break; + case S_CODE: + dbuf_append_str (dbuf, " code"); + break; + case S_IDATA: + dbuf_append_str (dbuf, " idata"); + break; + case S_PDATA: + dbuf_append_str (dbuf, " pdata"); + break; + case S_LITERAL: + dbuf_append_str (dbuf, " literal"); + break; + case S_STACK: + dbuf_append_str (dbuf, " stack"); + break; + case S_XSTACK: + dbuf_append_str (dbuf, " xstack"); + break; + case S_BIT: + dbuf_append_str (dbuf, " bit"); + break; + case S_EEPROM: + dbuf_append_str (dbuf, " eeprom"); + break; + default: + break; + } + } + + /* search entry in list before "type" */ + for (search = start; search && search->next != type;) + search = search->next; + type = search; + if (type) + dbuf_append_char (dbuf, ' '); + } +} + +/*--------------------------------------------------------------------*/ +/* printTypeChainRaw - prints the type chain in human readable form */ +/* in the raw data structure ordering */ +/*--------------------------------------------------------------------*/ +void +printTypeChainRaw (sym_link * start, FILE * of) +{ + int nlr = 0; + value *args; + sym_link *type; + + if (!of) + { + of = stdout; + nlr = 1; + } + + if (start == NULL) + { + fprintf (of, "void"); + return; + } + + type = start; + + while (type) + { + if (IS_DECL (type)) + { + if (!IS_FUNC (type)) + { + if (DCL_PTR_VOLATILE (type)) + { + fprintf (of, "volatile-"); + } + if (DCL_PTR_CONST (type)) + { + fprintf (of, "const-"); + } + if (DCL_PTR_RESTRICT (type)) + { + fprintf (of, "restrict-"); + } + } + switch (DCL_TYPE (type)) + { + case FUNCTION: + if (IFFUNC_ISINLINE (type)) + { + fprintf (of, "inline-"); + } + if (IFFUNC_ISNORETURN (type)) + { + fprintf (of, "_Noreturn-"); + } + fprintf (of, "function %s %s", + (IFFUNC_ISBUILTIN (type) ? "__builtin__" : " "), (IFFUNC_ISJAVANATIVE (type) ? "_JavaNative" : " ")); + fprintf (of, "( "); + for (args = FUNC_ARGS (type); args; args = args->next) + { + printTypeChain (args->type, of); + if (args->next) + fprintf (of, ", "); + } + fprintf (of, ") "); + break; + case GPOINTER: + fprintf (of, "generic* "); + break; + case CPOINTER: + fprintf (of, "code* "); + break; + case FPOINTER: + fprintf (of, "xdata* "); + break; + case EEPPOINTER: + fprintf (of, "eeprom* "); + break; + case POINTER: + fprintf (of, "near* "); + break; + case IPOINTER: + fprintf (of, "idata* "); + break; + case PPOINTER: + fprintf (of, "pdata* "); + break; + case UPOINTER: + fprintf (of, "unknown* "); + break; + case ARRAY: + if (DCL_ELEM (type)) + { + fprintf (of, "[%ud] ", (unsigned int) DCL_ELEM (type)); + } + else + { + fprintf (of, "[] "); + } + break; + } + if (DCL_TSPEC (type)) + { + fprintf (of, "{"); + printTypeChainRaw (DCL_TSPEC (type), of); + fprintf (of, "}"); + } + } + else if (IS_SPEC (type)) + { + switch (SPEC_SCLS (type)) + { + case S_DATA: + fprintf (of, "data-"); + break; + case S_XDATA: + fprintf (of, "xdata-"); + break; + case S_SFR: + fprintf (of, "sfr-"); + break; + case S_SBIT: + fprintf (of, "sbit-"); + break; + case S_CODE: + fprintf (of, "code-"); + break; + case S_IDATA: + fprintf (of, "idata-"); + break; + case S_PDATA: + fprintf (of, "pdata-"); + break; + case S_LITERAL: + fprintf (of, "literal-"); + break; + case S_STACK: + fprintf (of, "stack-"); + break; + case S_XSTACK: + fprintf (of, "xstack-"); + break; + case S_BIT: + fprintf (of, "bit-"); + break; + case S_EEPROM: + fprintf (of, "eeprom-"); + break; + default: + break; + } + if (SPEC_VOLATILE (type)) + fprintf (of, "volatile-"); + if (SPEC_CONST (type)) + fprintf (of, "const-"); + if (SPEC_USIGN (type)) + fprintf (of, "unsigned-"); + switch (SPEC_NOUN (type)) + { + case V_INT: + if (IS_LONGLONG (type)) + fprintf (of, "longlong-"); + else if (IS_LONG (type)) + fprintf (of, "long-"); + fprintf (of, "int"); + break; + + case V_BOOL: + fprintf (of, "_Bool"); + break; + + case V_CHAR: + fprintf (of, "char"); + break; + + case V_VOID: + fprintf (of, "void"); + break; + + case V_FLOAT: + fprintf (of, "float"); + break; + + case V_FIXED16X16: + fprintf (of, "fixed16x16"); + break; + + case V_STRUCT: + fprintf (of, "struct %s", SPEC_STRUCT (type)->tag); + break; + + case V_SBIT: + fprintf (of, "sbit"); + break; + + case V_BIT: + fprintf (of, "bit"); + break; + + case V_BITFIELD: + fprintf (of, "bitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type)); + break; + + case V_BBITFIELD: + fprintf (of, "_Boolbitfield {%d,%d}", SPEC_BSTR (type), SPEC_BLEN (type)); + break; + + case V_DOUBLE: + fprintf (of, "double"); + break; + + default: + fprintf (of, "unknown type"); + break; + } + } + else + fprintf (of, "NOT_SPEC_OR_DECL"); + type = type->next; + if (type) + fputc (' ', of); + } + if (nlr) + fprintf (of, "\n"); +} + + +/*-----------------------------------------------------------------*/ +/* powof2 - returns power of two for the number if number is pow 2 */ +/*-----------------------------------------------------------------*/ +int +powof2 (TYPE_TARGET_ULONG num) +{ + int nshifts = 0; + int n1s = 0; + + while (num) + { + if (num & 1) + n1s++; + num >>= 1; + nshifts++; + } + + if (n1s > 1 || nshifts == 0) + return -1; + return nshifts - 1; +} + +symbol *fsadd; +symbol *fssub; +symbol *fsmul; +symbol *fsdiv; +symbol *fseq; +symbol *fsneq; +symbol *fslt; + +symbol *fps16x16_add; +symbol *fps16x16_sub; +symbol *fps16x16_mul; +symbol *fps16x16_div; +symbol *fps16x16_eq; +symbol *fps16x16_neq; +symbol *fps16x16_lt; +symbol *fps16x16_lteq; +symbol *fps16x16_gt; +symbol *fps16x16_gteq; + +/* Dims: mul/div/mod, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED/BOTH */ +symbol *muldiv[3][4][4]; +symbol *muls16tos32[2]; +/* Dims: BYTE/WORD/DWORD/QWORD SIGNED/UNSIGNED */ +sym_link *multypes[4][2]; +/* Dims: to/from float, BYTE/WORD/DWORD/QWORD, SIGNED/USIGNED */ +symbol *conv[2][4][2]; +/* Dims: to/from fixed16x16, BYTE/WORD/DWORD/QWORD/FLOAT, SIGNED/USIGNED */ +symbol *fp16x16conv[2][5][2]; +/* Dims: shift left/shift right, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED */ +symbol *rlrr[2][4][2]; + +sym_link *charType; +sym_link *floatType; +sym_link *fixed16x16Type; + +symbol *memcpy_builtin; + +static const char * +_mangleFunctionName (const char *in) +{ + if (port->getMangledFunctionName) + { + return port->getMangledFunctionName (in); + } + else + { + return in; + } +} + +/*-----------------------------------------------------------------*/ +/* typeFromStr - create a typechain from an encoded string */ +/* basic types - 'b' - bool */ +/* 'c' - char */ +/* 's' - short */ +/* 'i' - int */ +/* 'l' - long */ +/* 'L' - long long */ +/* 'f' - float */ +/* 'q' - fixed16x16 */ +/* 'v' - void */ +/* '*' - pointer - default (GPOINTER) */ +/* modifiers - 'S' - signed */ +/* 'U' - unsigned */ +/* 'C' - const */ +/* pointer modifiers - 'g' - generic */ +/* 'x' - xdata */ +/* 'p' - code */ +/* 'd' - data */ +/* 'F' - function */ +/* examples : "ig*" - generic int * */ +/* "cx*" - char xdata * */ +/* "Ui" - unsigned int */ +/* "Sc" - signed char */ +/*-----------------------------------------------------------------*/ +sym_link * +typeFromStr (const char *s) +{ + sym_link *r = newLink (DECLARATOR); + int sign = 0; + int usign = 0; + int constant = 0; + + do + { + sym_link *nr; + switch (*s) + { + case 'S': + sign = 1; + break; + case 'U': + usign = 1; + break; + case 'C': + constant = 1; + break; + case 'b': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_BOOL; + break; + case 'c': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_CHAR; + if (!sign && !usign) + r->select.s.b_implicit_sign = true; + if (usign) + { + SPEC_USIGN (r) = 1; + usign = 0; + } + else if (!sign && !options.signed_char) + SPEC_USIGN (r) = 1; + break; + case 's': + case 'i': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_INT; + break; + case 'l': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_INT; + SPEC_LONG (r) = 1; + break; + case 'L': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_INT; + SPEC_LONGLONG (r) = 1; + break; + case 'f': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_FLOAT; + break; + case 'q': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_FIXED16X16; + break; + case 'v': + r->xclass = SPECIFIER; + SPEC_NOUN (r) = V_VOID; + break; + case '*': + DCL_TYPE (r) = port->unqualified_pointer; + break; + case 'g': + case 'x': + case 'p': + case 'd': + case 'F': + assert (*(s + 1) == '*'); + nr = newLink (DECLARATOR); + nr->next = r; + r = nr; + switch (*s) + { + case 'g': + DCL_TYPE (r) = GPOINTER; + break; + case 'x': + DCL_TYPE (r) = FPOINTER; + break; + case 'p': + DCL_TYPE (r) = CPOINTER; + break; + case 'd': + DCL_TYPE (r) = POINTER; + break; + case 'F': + DCL_TYPE (r) = FUNCTION; + nr = newLink (DECLARATOR); + nr->next = r; + r = nr; + DCL_TYPE (r) = CPOINTER; + break; + } + s++; + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "typeFromStr: unknown type"); + fprintf(stderr, "unknown: %s\n", s); + break; + } + if (usign && sign) + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "typeFromStr: both signed and unsigned specified"); + if (IS_SPEC (r) && usign) + { + SPEC_USIGN (r) = 1; + usign = 0; + } + if (IS_SPEC (r) && constant) + { + SPEC_CONST (r) = 1; + constant = 0; + } + s++; + } + while (*s); + return r; +} + +/*-----------------------------------------------------------------*/ +/* initCSupport - create functions for C support routines */ +/*-----------------------------------------------------------------*/ +void +initCSupport (void) +{ + const char *smuldivmod[] = { + "mul", "div", "mod" + }; + const char *sbwd[] = { + "char", "int", "long", "longlong", "fixed16x16", + }; + const char *fp16x16sbwd[] = { + "char", "int", "long", "longlong", "float", + }; + const char *ssu[] = { + "s", "su", "us", "u" + }; + const char *srlrr[] = { + "rl", "rr" + }; + /* type as character codes for typeFromStr() */ + const char *sbwdCodes[] = { + "c", "i", "l", "L", + "Uc", "Ui", "Ul", "UL" + }; + + int bwd, su, muldivmod, tofrom, slsr; + + if (getenv ("SDCC_NO_C_SUPPORT")) + { + /* for debugging only */ + return; + } + + for (bwd = 0; bwd < 4; bwd++) + { + sym_link *l = NULL; + switch (bwd) + { + case 0: + l = newCharLink (); + break; + case 1: + l = newIntLink (); + break; + case 2: + l = newLongLink (); + break; + case 3: + l = newLongLongLink (); + break; + default: + assert (0); + } + multypes[bwd][0] = l; + multypes[bwd][1] = copyLinkChain (l); + SPEC_USIGN (multypes[bwd][0]) = 0; + SPEC_USIGN (multypes[bwd][1]) = 1; + } + + floatType = newFloatLink (); + fixed16x16Type = newFixed16x16Link (); + charType = (options.signed_char) ? SCHARTYPE : UCHARTYPE; + + fsadd = funcOfType ("__fsadd", floatType, floatType, 2, options.float_rent); + fssub = funcOfType ("__fssub", floatType, floatType, 2, options.float_rent); + fsmul = funcOfType ("__fsmul", floatType, floatType, 2, options.float_rent); + fsdiv = funcOfType ("__fsdiv", floatType, floatType, 2, options.float_rent); + fseq = funcOfType ("__fseq", charType, floatType, 2, options.float_rent); + fsneq = funcOfType ("__fsneq", charType, floatType, 2, options.float_rent); + fslt = funcOfType ("__fslt", charType, floatType, 2, options.float_rent); + + fps16x16_add = funcOfType ("__fps16x16_add", fixed16x16Type, fixed16x16Type, 2, options.float_rent); + fps16x16_sub = funcOfType ("__fps16x16_sub", fixed16x16Type, fixed16x16Type, 2, options.float_rent); + fps16x16_mul = funcOfType ("__fps16x16_mul", fixed16x16Type, fixed16x16Type, 2, options.float_rent); + fps16x16_div = funcOfType ("__fps16x16_div", fixed16x16Type, fixed16x16Type, 2, options.float_rent); + fps16x16_eq = funcOfType ("__fps16x16_eq", charType, fixed16x16Type, 2, options.float_rent); + fps16x16_neq = funcOfType ("__fps16x16_neq", charType, fixed16x16Type, 2, options.float_rent); + fps16x16_lt = funcOfType ("__fps16x16_lt", charType, fixed16x16Type, 2, options.float_rent); + fps16x16_lteq = funcOfType ("__fps16x16_lteq", charType, fixed16x16Type, 2, options.float_rent); + fps16x16_gt = funcOfType ("__fps16x16_gt", charType, fixed16x16Type, 2, options.float_rent); + fps16x16_gteq = funcOfType ("__fps16x16_gteq", charType, fixed16x16Type, 2, options.float_rent); + + for (tofrom = 0; tofrom < 2; tofrom++) + { + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (tofrom) + { + dbuf_printf (&dbuf, "__fs2%s%s", ssu[su * 3], sbwd[bwd]); + conv[tofrom][bwd][su] = funcOfType (dbuf_c_str (&dbuf), multypes[bwd][su], floatType, 1, options.float_rent); + } + else + { + dbuf_printf (&dbuf, "__%s%s2fs", ssu[su * 3], sbwd[bwd]); + conv[tofrom][bwd][su] = funcOfType (dbuf_c_str (&dbuf), floatType, multypes[bwd][su], 1, options.float_rent); + } + dbuf_destroy (&dbuf); + } + } + } + + for (tofrom = 0; tofrom < 2; tofrom++) + { + for (bwd = 0; bwd < 5; bwd++) + { + for (su = 0; su < 2; su++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (tofrom) + { + dbuf_printf (&dbuf, "__fps16x162%s%s", ssu[su * 3], fp16x16sbwd[bwd]); + if (bwd == 4) + fp16x16conv[tofrom][bwd][su] = + funcOfType (dbuf_c_str (&dbuf), floatType, fixed16x16Type, 1, options.float_rent); + else + fp16x16conv[tofrom][bwd][su] = + funcOfType (dbuf_c_str (&dbuf), multypes[bwd][su], fixed16x16Type, 1, options.float_rent); + } + else + { + dbuf_printf (&dbuf, "__%s%s2fps16x16", ssu[su * 3], fp16x16sbwd[bwd]); + if (bwd == 4) + fp16x16conv[tofrom][bwd][su] = + funcOfType (dbuf_c_str (&dbuf), fixed16x16Type, floatType, 1, options.float_rent); + else + fp16x16conv[tofrom][bwd][su] = + funcOfType (dbuf_c_str (&dbuf), fixed16x16Type, multypes[bwd][su], 1, options.float_rent); + } + dbuf_destroy (&dbuf); + } + } + } + +/* + for (muldivmod = 0; muldivmod < 3; muldivmod++) + { + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su*3], sbwd[bwd]); + muldiv[muldivmod][bwd][su] = funcOfType (_mangleFunctionName(dbuf_c_str (&dbuf)), multypes[bwd][su], multypes[bwd][su], 2, options.intlong_rent); + dbuf_destroy (&dbuf); + FUNC_NONBANKED (muldiv[muldivmod][bwd][su]->type) = 1; + } + } + } + + muluint() and mulsint() resp. mululong() and mulslong() return the same result. + Therefore they've been merged into mulint() and mullong(). +*/ + + /* byte */ + + /* _divschar/_modschar return int, so that both + * 100 / -4 = -25 and -128 / -1 = 128 can be handled correctly + * (first one would have to be sign extended, second one must not be). + * Similarly, modschar should be handled, but the iCode introduces cast + * here and forces '% : s8 x s8 -> s8' ... */ + bwd = 0; + for (su = 0; su < 4; su++) + { + for (muldivmod = 0; muldivmod < 3; muldivmod++) + { + /* muluchar, mulschar, mulsuchar and muluschar are separate functions, because e.g. the z80 + port is sign/zero-extending to int before calling mulint() */ + /* div and mod : s8_t x s8_t -> s8_t should be s8_t x s8_t -> s16_t, see below */ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su], sbwd[bwd]); + muldiv[muldivmod][bwd][su] = + funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[(TARGET_IS_PIC16 && muldivmod == 1 && bwd == 0 && su == 0 || (TARGET_IS_PIC14 || TARGET_IS_STM8 || TARGET_Z80_LIKE || TARGET_PDK_LIKE) && bwd == 0) ? 1 : bwd][su % 2], multypes[bwd][su / 2], 2, + options.intlong_rent); + dbuf_destroy (&dbuf); + } + } + + for (bwd = 1; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + for (muldivmod = 1; muldivmod < 3; muldivmod++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_%s%s%s", smuldivmod[muldivmod], ssu[su * 3], sbwd[bwd]); + muldiv[muldivmod][bwd][su] = + funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[(TARGET_IS_PIC16 && muldivmod == 1 && bwd == 0 && su == 0 || (TARGET_IS_STM8 || TARGET_Z80_LIKE || TARGET_PDK_LIKE) && bwd == 0) ? 1 : bwd][su], multypes[bwd][su], 2, + options.intlong_rent); + dbuf_destroy (&dbuf); + } + } + } + + /* mul only */ + muldivmod = 0; + /* signed only */ + su = 0; + /* word, doubleword, and quadword */ + for (bwd = 1; bwd < 4; bwd++) + { + /* mul, int/long */ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_%s%s", smuldivmod[muldivmod], sbwd[bwd]); + muldiv[muldivmod][bwd][0] = + funcOfType (_mangleFunctionName (dbuf_c_str (&dbuf)), multypes[bwd][su], multypes[bwd][su], 2, options.intlong_rent); + dbuf_destroy (&dbuf); + /* signed = unsigned */ + muldiv[muldivmod][bwd][1] = muldiv[muldivmod][bwd][0]; + } + + for (slsr = 0; slsr < 2; slsr++) + { + for (bwd = 0; bwd < 4; bwd++) + { + for (su = 0; su < 2; su++) + { + struct dbuf_s dbuf; + symbol *sym; + const char *params[2]; + + params[0] = sbwdCodes[bwd + 4*su]; + params[1] = sbwdCodes[0]; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "_%s%s%s", srlrr[slsr], ssu[su * 3], sbwd[bwd]); + rlrr[slsr][bwd][su] = sym = + funcOfTypeVarg (_mangleFunctionName (dbuf_c_str (&dbuf)), + sbwdCodes[bwd + 4*su], 2, ¶ms[0]); + FUNC_ISREENT (sym->type) = options.intlong_rent ? 1 : 0; + FUNC_NONBANKED (sym->type) = 1; + dbuf_destroy (&dbuf); + } + } + } + + { + const char *iparams[] = {"i", "i"}; + const char *uiparams[] = {"Ui", "Ui"}; + muls16tos32[0] = port->support.has_mulint2long ? funcOfTypeVarg ("__mulsint2slong", "l", 2, iparams) : 0; + muls16tos32[1] = port->support.has_mulint2long ? funcOfTypeVarg ("__muluint2ulong", "Ul", 2, uiparams) : 0; + } +} + +/*-----------------------------------------------------------------*/ +/* initBuiltIns - create prototypes for builtin functions */ +/*-----------------------------------------------------------------*/ +void +initBuiltIns () +{ + int i; + symbol *sym; + + if (port->builtintable) + { + for (i = 0; port->builtintable[i].name; i++) + { + sym = funcOfTypeVarg (port->builtintable[i].name, port->builtintable[i].rtype, + port->builtintable[i].nParms, (const char **)port->builtintable[i].parm_types); + FUNC_ISBUILTIN (sym->type) = 1; + FUNC_ISREENT (sym->type) = 0; /* can never be reentrant */ + } + } + + /* initialize memcpy symbol for struct assignment */ + memcpy_builtin = findSym (SymbolTab, NULL, "__builtin_memcpy"); + /* if there is no __builtin_memcpy, use __memcpy instead of an actual builtin */ + if (!memcpy_builtin) + { + const char *argTypeStrs[] = {"vg*", "Cvg*", "Ui"}; + memcpy_builtin = funcOfTypeVarg ("__memcpy", "vg*", 3, argTypeStrs); + FUNC_ISBUILTIN (memcpy_builtin->type) = 0; + FUNC_ISREENT (memcpy_builtin->type) = options.stackAuto; + } +} + +sym_link * +validateLink (sym_link * l, const char *macro, const char *args, const char select, const char *file, unsigned line) +{ + if (l && l->xclass == select) + { + return l; + } + fprintf (stderr, + "Internal error: validateLink failed in %s(%s) @ %s:%u:" + " expected %s, got %s\n", + macro, args, file, line, DECLSPEC2TXT (select), l ? DECLSPEC2TXT (l->xclass) : "null-link"); + exit (EXIT_FAILURE); + return l; // never reached, makes compiler happy. +} + +/*--------------------------------------------------------------------*/ +/* newEnumType - create an integer type compatible with enumerations */ +/*--------------------------------------------------------------------*/ +sym_link * +newEnumType (symbol * enumlist) +{ + int min, max, v; + symbol *sym; + sym_link *type; + + if (!enumlist) + { + type = newLink (SPECIFIER); + SPEC_NOUN (type) = V_INT; + return type; + } + + /* Determine the range of the enumerated values */ + sym = enumlist; + min = max = (int) ulFromVal (valFromType (sym->type)); + for (sym = sym->next; sym; sym = sym->next) + { + v = (int) ulFromVal (valFromType (sym->type)); + if (v < min) + min = v; + if (v > max) + max = v; + } + + /* Determine the smallest integer type that is compatible with this range */ + type = newLink (SPECIFIER); + if (min >= 0 && max <= 255) + { + SPEC_NOUN (type) = V_CHAR; + SPEC_USIGN (type) = 1; + } + else if (min >= -128 && max <= 127) + { + SPEC_NOUN (type) = V_CHAR; + SPEC_SIGN (type) = 1; + } + else if (min >= 0 && max <= 65535) + { + SPEC_NOUN (type) = V_INT; + SPEC_USIGN (type) = 1; + } + else if (min >= -32768 && max <= 32767) + { + SPEC_NOUN (type) = V_INT; + } + else + { + SPEC_NOUN (type) = V_INT; + SPEC_LONG (type) = 1; + if (min >= 0) + SPEC_USIGN (type) = 1; + } + + return type; +} + +/*-------------------------------------------------------------------*/ +/* isConstant - check if the type is constant */ +/*-------------------------------------------------------------------*/ +int +isConstant (sym_link * type) +{ + if (!type) + return 0; + + while (IS_ARRAY (type)) + type = type->next; + + if (IS_SPEC (type)) + return SPEC_CONST (type); + else + return DCL_PTR_CONST (type); +} + +/*-------------------------------------------------------------------*/ +/* isVolatile - check if the type is volatile */ +/*-------------------------------------------------------------------*/ +int +isVolatile (sym_link * type) +{ + if (!type) + return 0; + + while (IS_ARRAY (type)) + type = type->next; + + if (IS_SPEC (type)) + return SPEC_VOLATILE (type); + else + return DCL_PTR_VOLATILE (type); +} + +/*-------------------------------------------------------------------*/ +/* isRestrict - check if the type is restricted */ +/*-------------------------------------------------------------------*/ +int +isRestrict (sym_link * type) +{ + if (!type) + return 0; + + while (IS_ARRAY (type)) + type = type->next; + + if (IS_SPEC (type)) + return SPEC_RESTRICT (type); + else + return DCL_PTR_RESTRICT (type); +} diff --git a/src/SDCCsymt.h b/src/SDCCsymt.h new file mode 100644 index 0000000..4b5b74e --- /dev/null +++ b/src/SDCCsymt.h @@ -0,0 +1,734 @@ +/*------------------------------------------------------------------------- + SDCCsymt.h - Header file for Symbols table related structures and MACRO's. + + Copyright (C) 1998 Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCSYMT_H +#define SDCCSYMT_H + +#define MAX_NEST_LEVEL 256 +#define SDCC_SYMNAME_MAX 64 +#define SDCC_NAME_MAX 3*SDCC_SYMNAME_MAX // big enough for ___etc +#include "SDCChasht.h" +#include "SDCCglobl.h" +#include "dbuf.h" + +#define INTNO_MAX 255 /* maximum allowed interrupt number */ +#define INTNO_TRAP INTNO_MAX +#define INTNO_UNSPEC (INTNO_MAX+1) /* interrupt number unspecified */ + + +#define BITVAR_PAD -1 + +enum +{ + TYPEOF_INT = 1, + TYPEOF_SHORT, + TYPEOF_BOOL, + TYPEOF_CHAR, + TYPEOF_LONG, + TYPEOF_LONGLONG, + TYPEOF_FLOAT, + TYPEOF_FIXED16X16, + TYPEOF_BIT, + TYPEOF_BITFIELD, + TYPEOF_SBIT, + TYPEOF_SFR, + TYPEOF_VOID, + TYPEOF_STRUCT, + TYPEOF_ARRAY, + TYPEOF_FUNCTION, + TYPEOF_POINTER, + TYPEOF_FPOINTER, + TYPEOF_CPOINTER, + TYPEOF_GPOINTER, + TYPEOF_PPOINTER, + TYPEOF_IPOINTER, + TYPEOF_EEPPOINTER +}; + +// values for first byte (or 3 most significant bits) of generic pointer. +#if 0 +#define GPTYPE_FAR 0x00 +#define GPTYPE_NEAR 0x40 +#define GPTYPE_XSTACK 0x60 +#define GPTYPE_CODE 0x80 +#else +#define GPTYPE_FAR (port->gp_tags.tag_far) +#define GPTYPE_NEAR (port->gp_tags.tag_near) +#define GPTYPE_XSTACK (port->gp_tags.tag_xstack) +#define GPTYPE_CODE (port->gp_tags.tag_code) +#endif + +#define HASHTAB_SIZE 256 + +/* hash table bucket */ +typedef struct bucket +{ + void *sym; /* pointer to the object */ + char name[SDCC_NAME_MAX + 1]; /* name of this symbol */ + long level; /* nest level for this symbol */ + int block; /* belongs to which block */ + struct bucket *prev; /* ptr 2 previous bucket */ + struct bucket *next; /* ptr 2 next bucket */ +} +bucket; + +typedef struct structdef +{ + char tag[SDCC_NAME_MAX + 1]; /* tag part of structure */ + long level; /* Nesting level */ + int block; /* belongs to which block */ + struct symbol *fields; /* pointer to fields */ + unsigned size; /* sizeof the table in bytes */ + int type; /* STRUCT or UNION */ + bool b_flexArrayMember; /* has got a flexible array member, + only needed for syntax checks */ + struct symbol *tagsym; /* tag symbol (NULL if no tag) */ +} +structdef; + +/* noun definitions */ +typedef enum +{ + V_INT = 1, + V_FLOAT, + V_FIXED16X16, + V_BOOL, + V_CHAR, + V_VOID, + V_STRUCT, + V_LABEL, + V_BIT, + V_BITFIELD, + V_BBITFIELD, + V_SBIT, + V_DOUBLE +} +NOUN; + +/* storage class */ +typedef enum +{ + S_FIXED = 0, + S_AUTO, + S_REGISTER, + S_SFR, + S_SBIT, + S_CODE, + S_XDATA, + S_DATA, + S_IDATA, + S_PDATA, + S_LITERAL, + S_STACK, + S_XSTACK, + S_BIT, + S_EEPROM +} +STORAGE_CLASS; + +#define TYPE_TARGET_CHAR TYPE_BYTE +#define TYPE_TARGET_INT TYPE_WORD +#define TYPE_TARGET_LONG TYPE_DWORD +#define TYPE_TARGET_UCHAR TYPE_UBYTE +#define TYPE_TARGET_UINT TYPE_UWORD +#define TYPE_TARGET_ULONG TYPE_UDWORD +#define TYPE_TARGET_LONGLONG TYPE_QWORD +#define TYPE_TARGET_ULONGLONG TYPE_UQWORD + +/* specifier is the last in the type-chain */ +typedef struct specifier +{ + NOUN noun; /* CHAR INT STRUCTURE LABEL */ + STORAGE_CLASS sclass; /* REGISTER,AUTO,FIX,CONSTANT */ + struct memmap *oclass; /* output storage class */ + unsigned b_long:1; /* 1=long */ + unsigned b_longlong:1; /* 1=long long */ + unsigned b_short:1; /* 1=short int */ + unsigned b_unsigned:1; /* 1=unsigned, 0=signed */ + unsigned b_signed:1; /* just for sanity checks only*/ + bool b_implicit_sign:1; /* signedness not explicitly specified - needed to keep char a separate type from signed char and unsigned char. */ + unsigned b_static:1; /* 1=static keyword found */ + unsigned b_extern:1; /* 1=extern found */ + unsigned b_inline:1; /* inline function requested */ + unsigned b_noreturn:1; /* promised not to return */ + unsigned b_alignas:1; /* alignment */ + unsigned b_absadr:1; /* absolute address specfied */ + unsigned b_volatile:1; /* is marked as volatile */ + unsigned b_const:1; /* is a constant */ + unsigned b_restrict:1; /* is restricted */ + struct symbol *addrspace; /* is in named address space */ + unsigned b_typedef:1; /* is typedefed */ + unsigned b_isregparm:1; /* is the first parameter */ + unsigned b_isenum:1; /* is an enumerated type */ + unsigned b_bitUnnamed:1; /* is an unnamed bit-field */ + unsigned b_needspar:1; /* has to be a parameter */ + unsigned _bitStart; /* bit start position */ + unsigned _bitLength; /* bit length */ + unsigned _addr; /* address of symbol */ + unsigned _stack; /* stack offset for stacked v */ + int argreg; /* reg no for regparm */ + union + { /* Values if constant or enum */ + TYPE_TARGET_INT v_int; /* 2 bytes: int and char values */ + const char *v_char; /* char character string */ + const TYPE_TARGET_UINT *v_char16; /* char16_t character string */ + const TYPE_TARGET_ULONG *v_char32;/* char32_t character string */ + TYPE_TARGET_UINT v_uint; /* 2 bytes: unsigned int const value */ + TYPE_TARGET_LONG v_long; /* 4 bytes: long constant value */ + TYPE_TARGET_ULONG v_ulong; /* 4 bytes: unsigned long constant value */ + TYPE_TARGET_LONGLONG v_longlong; /* 8 bytes: long long constant value */ + TYPE_TARGET_ULONGLONG v_ulonglong;/* 8 bytes: unsigned long long const value */ + double v_float; /* floating point constant value */ + TYPE_TARGET_ULONG v_fixed16x16; /* 4 bytes: fixed point constant value */ + struct symbol *v_enum; /* ptr to enum_list if enum==1 */ + } + const_val; + struct structdef *v_struct; /* structure pointer */ +} +specifier; + +/* types of declarators */ +typedef enum +{ + UPOINTER = 0, /* unknown pointer used only when parsing */ + POINTER, /* pointer to near data */ + IPOINTER, /* pointer to upper 128 bytes */ + PPOINTER, /* paged area pointer */ + FPOINTER, /* pointer to far data */ + CPOINTER, /* pointer to code space */ + GPOINTER, /* generic pointer */ + EEPPOINTER, /* pointer to eeprom */ + ARRAY, + FUNCTION +} +DECLARATOR_TYPE; + +typedef struct declarator +{ + DECLARATOR_TYPE dcl_type; /* POINTER,ARRAY or FUNCTION */ + size_t num_elem; /* # of elems if type==array, */ + /* always 0 for flexible arrays */ + unsigned ptr_const:1; /* pointer is constant */ + unsigned ptr_volatile:1; /* pointer is volatile */ + unsigned ptr_restrict:1; /* pointer is resticted */ + struct symbol *ptr_addrspace; /* pointer is in named address space */ + + struct sym_link *tspec; /* pointer type specifier */ +} +declarator; + +typedef enum +{ + DECLARATOR = 1, + SPECIFIER +} SYM_LINK_CLASS; +#define DECLSPEC2TXT(select) (select==DECLARATOR?"DECLARATOR":select==SPECIFIER?"SPECIFIER":"UNKNOWN") + +typedef struct sym_link +{ + SYM_LINK_CLASS xclass; /* DECLARATOR or SPECIFIER */ + unsigned tdef:1; /* current link created by */ + /* typedef if this flag is set */ + union + { + specifier s; /* if CLASS == SPECIFIER */ + declarator d; /* if CLASS == DECLARATOR */ + } select; + + /* function attributes */ + struct + { + struct value *args; /* the defined arguments */ + unsigned hasVargs:1; /* functions has varargs */ + unsigned calleeSaves:1; /* functions uses callee save */ + unsigned hasbody:1; /* function body defined */ + unsigned hasFcall:1; /* does it call other functions */ + unsigned reent:1; /* function is reentrant */ + unsigned naked:1; /* naked function */ + + unsigned shadowregs:1; /* function uses shadow registers (pic16 port) */ + unsigned wparam:1; /* first byte of arguments is passed via WREG (pic16 port) */ + unsigned nonbanked:1; /* function has the nonbanked attribute */ + unsigned banked:1; /* function has the banked attribute */ + unsigned critical:1; /* critical function */ + unsigned intrtn:1; /* this is an interrupt routine */ + unsigned rbank:1; /* seperate register bank */ + unsigned inlinereq:1; /* inlining requested */ + unsigned noreturn:1; /* promised not to return */ + unsigned smallc:1; /* Parameters on stack are passed in reverse order */ + unsigned z88dk_fastcall:1; /* For the z80-related ports: Function has a single paramter of at most 32 bits that is passed in dehl */ + unsigned z88dk_callee:1; /* Stack pointer adjustment for parameters passed on the stack is done by the callee */ + unsigned z88dk_shortcall:1; /* Short call available via rst (see values later) (Z80 only) */ + unsigned z88dk_has_params_offset:1; /* Has a parameter offset (Z80 only) */ + unsigned intno; /* Number of interrupt for interrupt service routine */ + short regbank; /* register bank 2b used */ + unsigned builtin; /* is a builtin function */ + unsigned javaNative; /* is a JavaNative Function (TININative ONLY) */ + unsigned overlay; /* force parameters & locals into overlay segment */ + unsigned hasStackParms; /* function has parameters on stack */ + bool preserved_regs[9]; /* Registers preserved by the function - may be an underestimate */ + unsigned char z88dk_shortcall_rst; /* Rst for a short call */ + unsigned short z88dk_shortcall_val; /* Value for a short call */ + unsigned short z88dk_params_offset; /* Additional offset from for arguments */ + } funcAttrs; + + struct sym_link *next; /* next element on the chain */ +} +sym_link; + +typedef struct symbol +{ + char name[SDCC_SYMNAME_MAX + 1]; /* Input Variable Name */ + char rname[SDCC_NAME_MAX + 1]; /* internal name */ + + long level; /* declaration lev,fld offset */ + short block; /* sequential block # of definition */ + int seqPoint; /* sequence point defined or, if unbound, used */ + int key; + unsigned flexArrayLength; /* if the symbol specifies a struct + with a "flexible array member", then the additional length in bytes for + the "fam" is stored here. Because the length can be different from symbol + to symbol AND v_struct isn't copied in copyLinkChain(), it's located here + in the symbol and not in v_struct or the declarator */ + unsigned implicit:1; /* implicit flag */ + unsigned undefined:1; /* undefined variable */ + unsigned infertype:1; /* type should be inferred from first assign */ + unsigned _isparm:1; /* is a parameter */ + unsigned ismyparm:1; /* is parameter of the function being generated */ + unsigned isitmp:1; /* is an intermediate temp */ + unsigned islbl:1; /* is a temporary label */ + unsigned isref:1; /* has been referenced */ + unsigned isind:1; /* is an induction variable */ + unsigned isinvariant:1; /* is a loop invariant */ + unsigned cdef:1; /* compiler defined symbol */ + unsigned addrtaken:1; /* address of the symbol was taken */ + unsigned isreqv:1; /* is the register equivalent of a symbol */ + unsigned udChked:1; /* use def checking has been already done */ + unsigned generated:1; /* code generated (function symbols only) */ + unsigned isinscope:1; /* is in scope */ + + /* following flags are used by the backend + for code generation and can be changed + if a better scheme for backend is thought of */ + unsigned isLiveFcall:1; /* is live at or across a function call */ + unsigned isspilt:1; /* has to be spilt */ + unsigned spillA:1; /* spilt be register allocator */ + unsigned remat:1; /* can be remateriazed */ + unsigned isptr:1; /* is a pointer */ + unsigned uptr:1; /* used as a pointer */ + unsigned isFree:1; /* used by register allocator */ + unsigned islocal:1; /* is a local variable */ + unsigned blockSpil:1; /* spilt at block level */ + unsigned remainSpil:1; /* spilt because not used in remainder */ + unsigned stackSpil:1; /* has been spilt on temp stack location */ + unsigned onStack:1; /* this symbol allocated on the stack */ + unsigned iaccess:1; /* indirect access */ + unsigned ruonly:1; /* used in return statement only */ + unsigned spildir:1; /* spilt in direct space */ + unsigned ptrreg:1; /* this symbol assigned to a ptr reg */ + unsigned noSpilLoc:1; /* cannot be assigned a spil location */ + bool funcDivFlagSafe:1; /* we know this function is safe to call with undocumented stm8 flag bit 6 set*/ + bool funcUsesVolatile:1; /* The function accesses a volatile variable */ + unsigned isstrlit; /* is a string literal and it's usage count */ + unsigned accuse; /* can be left in the accumulator + On the Z80 accuse is divided into + ACCUSE_A and ACCUSE_HL as the idea + is quite similar. + */ + unsigned dptr; /* 8051 variants with multiple DPTRS + currently implemented in DS390 only + */ + int allocreq; /* allocation is required for this variable */ + int stack; /* offset on stack */ + int xstack; /* offset on xternal stack */ + short nRegs; /* number of registers required */ + short regType; /* type of register required */ + + struct reg_info *regs[8]; /* can have at the most 8 registers */ + struct asmop *aop; /* asmoperand for this symbol */ + struct iCode *fuse; /* furthest use */ + struct iCode *rematiCode; /* rematerialise with which instruction */ + struct operand *reqv; /* register equivalent of a local variable */ + struct symbol *prereqv; /* symbol before register equiv. substitution */ + struct symbol *psbase; /* if pseudo symbol, the symbol it is based on */ + union + { + struct symbol *spillLoc; /* register spil location */ + struct set *itmpStack; /* symbols spilt @ this stack location */ + } + usl; + int bitVar; /* if bitVar != 0: this is a bit variable, bitVar is the size in bits */ + unsigned bitUnnamed:1; /* unnamed bit variable */ + unsigned offset; /* offset from top if struct */ + + int lineDef; /* defined line number */ + char *fileDef; /* defined filename */ + int lastLine; /* for functions the last line */ + struct sym_link *type; /* 1st link to declarator chain */ + struct sym_link *etype; /* last link to declarator chain */ + struct symbol *next; /* crosslink to next symbol */ + struct symbol *localof; /* local variable of which function */ + struct initList *ival; /* ptr to initializer if any */ + struct bitVect *defs; /* bit vector for definitions */ + struct bitVect *uses; /* bit vector for uses */ + struct bitVect *regsUsed; /* for functions registers used */ + int liveFrom; /* live from iCode sequence number */ + int liveTo; /* live to sequence number */ + int used; /* no. of times this was used */ + int recvSize; /* size of first argument */ + struct bitVect *clashes; /* overlaps with what other symbols */ + struct ast *funcTree; /* function body ast if inlined */ + struct symbol *addressmod[2]; /* access functions for named address spaces */ + + bool for_newralloc; +} +symbol; + +extern sym_link *validateLink (sym_link * l, + const char *macro, const char *args, const char select, const char *file, unsigned line); +/* Easy Access Macros */ +#define IS_OP_RUONLY(x) (IS_SYMOP(x) && OP_SYMBOL(x) && OP_SYMBOL(x)->ruonly) +#define IS_OP_ACCUSE(x) (IS_SYMOP(x) && OP_SYMBOL(x) && OP_SYMBOL(x)->accuse) + +#define DCL_TYPE(l) validateLink(l, "DCL_TYPE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.dcl_type +#define DCL_ELEM(l) validateLink(l, "DCL_ELEM", #l, DECLARATOR, __FILE__, __LINE__)->select.d.num_elem +#define DCL_PTR_CONST(l) validateLink(l, "DCL_PTR_CONST", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_const +#define DCL_PTR_VOLATILE(l) validateLink(l, "DCL_PTR_VOLATILE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_volatile +#define DCL_PTR_RESTRICT(l) validateLink(l, "DCL_PTR_RESTRICT", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_restrict +#define DCL_PTR_ADDRSPACE(l) validateLink(l, "DCL_PTR_ADDRSPACE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_addrspace +#define DCL_TSPEC(l) validateLink(l, "DCL_TSPEC", #l, DECLARATOR, __FILE__, __LINE__)->select.d.tspec + +#define FUNC_DEBUG //assert(IS_FUNC(x)); +#define FUNC_HASVARARGS(x) (x->funcAttrs.hasVargs) +#define IFFUNC_HASVARARGS(x) (IS_FUNC(x) && FUNC_HASVARARGS(x)) +#define FUNC_ARGS(x) (x->funcAttrs.args) +#define IFFUNC_ARGS(x) (IS_FUNC(x) && FUNC_ARGS(x)) +#define FUNC_HASFCALL(x) (x->funcAttrs.hasFcall) +#define IFFUNC_HASFCALL(x) (IS_FUNC(x) && FUNC_HASFCALL(x)) +#define FUNC_HASBODY(x) (x->funcAttrs.hasbody) +#define IFFUNC_HASBODY(x) (IS_FUNC(x) && FUNC_HASBODY(x)) +#define FUNC_CALLEESAVES(x) (x->funcAttrs.calleeSaves) +#define IFFUNC_CALLEESAVES(x) (IS_FUNC(x) && FUNC_CALLEESAVES(x)) +#define FUNC_ISISR(x) (x->funcAttrs.intrtn) +#define IFFUNC_ISISR(x) (IS_FUNC(x) && FUNC_ISISR(x)) +#define FUNC_INTNO(x) (x->funcAttrs.intno) +#define FUNC_REGBANK(x) (x->funcAttrs.regbank) +#define FUNC_HASSTACKPARM(x) (x->funcAttrs.hasStackParms) +#define FUNC_ISINLINE(x) (x->funcAttrs.inlinereq) +#define IFFUNC_ISINLINE(x) (IS_FUNC(x) && FUNC_ISINLINE(x)) +#define FUNC_ISNORETURN(x) (x->funcAttrs.noreturn) +#define IFFUNC_ISNORETURN(x) (IS_FUNC(x) && FUNC_ISNORETURN(x)) + +#define FUNC_ISREENT(x) (x->funcAttrs.reent) +#define IFFUNC_ISREENT(x) (IS_FUNC(x) && FUNC_ISREENT(x)) +#define FUNC_ISSHADOWREGS(x) (x->funcAttrs.shadowregs) +#define IFFUNC_ISSHADOWREGS(x) (IS_FUNC(x) && FUNC_ISSHADOWREGS(x)) +#define FUNC_ISWPARAM(x) (x->funcAttrs.wparam) +#define IFFUNC_ISWPARAM(x) (IS_FUNC(x) && FUNC_ISWPARAM(x)) +#define FUNC_ISNAKED(x) (x->funcAttrs.naked) +#define IFFUNC_ISNAKED(x) (IS_FUNC(x) && FUNC_ISNAKED(x)) +#define FUNC_NONBANKED(x) (x->funcAttrs.nonbanked) +#define IFFUNC_NONBANKED(x) (IS_FUNC(x) && FUNC_NONBANKED(x)) +#define FUNC_BANKED(x) (x->funcAttrs.banked) +#define IFFUNC_BANKED(x) (IS_FUNC(x) && FUNC_BANKED(x)) +#define FUNC_ISCRITICAL(x) (x->funcAttrs.critical) +#define IFFUNC_ISCRITICAL(x) (IS_FUNC(x) && FUNC_ISCRITICAL(x)) +#define FUNC_ISBUILTIN(x) (x->funcAttrs.builtin) +#define IFFUNC_ISBUILTIN(x) (IS_FUNC(x) && FUNC_ISBUILTIN(x)) +#define FUNC_ISJAVANATIVE(x) (x->funcAttrs.javaNative) +#define IFFUNC_ISJAVANATIVE(x) (IS_FUNC(x) && FUNC_ISJAVANATIVE(x)) +#define FUNC_ISOVERLAY(x) (x->funcAttrs.overlay) +#define IFFUNC_ISOVERLAY(x) (IS_FUNC(x) && FUNC_ISOVERLAY(x)) +#define FUNC_ISSMALLC(x) (x->funcAttrs.smallc) +#define IFFUNC_ISSMALLC(x) (IS_FUNC(x) && FUNC_ISSMALLC(x)) +#define FUNC_ISZ88DK_FASTCALL(x) (x->funcAttrs.z88dk_fastcall) +#define IFFUNC_ISZ88DK_FASTCALL(x) (IS_FUNC(x) && FUNC_ISZ88DK_FASTCALL(x)) +#define FUNC_ISZ88DK_CALLEE(x) (x->funcAttrs.z88dk_callee) +#define IFFUNC_ISZ88DK_CALLEE(x) (IS_FUNC(x) && FUNC_ISZ88DK_CALLEE(x)) +#define FUNC_ISZ88DK_SHORTCALL(x) (x->funcAttrs.z88dk_shortcall) +#define IFFUNC_ISZ88DK_SHORTCALL(x) (IS_FUNC(x) && FUNC_ISZ88DK_SHORTCALL(x)) + +#define BANKED_FUNCTIONS ( options.model == MODEL_HUGE || \ + ( (options.model == MODEL_LARGE || options.model == MODEL_MEDIUM) && \ + TARGET_Z80_LIKE ) ) +#define IFFUNC_ISBANKEDCALL(x) ( IS_FUNC(x) && \ + ( FUNC_BANKED(x) || ( BANKED_FUNCTIONS && !FUNC_NONBANKED(x) ) ) ) + +#define SPEC_NOUN(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.noun +#define SPEC_LONG(x) validateLink(x, "SPEC_LONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_long +#define SPEC_LONGLONG(x) validateLink(x, "SPEC_LONGLONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_longlong +#define SPEC_SHORT(x) validateLink(x, "SPEC_LONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_short +#define SPEC_USIGN(x) validateLink(x, "SPEC_USIGN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_unsigned +#define SPEC_SIGN(x) validateLink(x, "SPEC_USIGN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_signed +#define SPEC_SCLS(x) validateLink(x, "SPEC_SCLS", #x, SPECIFIER, __FILE__, __LINE__)->select.s.sclass +#define SPEC_ENUM(x) validateLink(x, "SPEC_ENUM", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_isenum +#define SPEC_OCLS(x) validateLink(x, "SPEC_OCLS", #x, SPECIFIER, __FILE__, __LINE__)->select.s.oclass +#define SPEC_STAT(x) validateLink(x, "SPEC_STAT", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_static +#define SPEC_EXTR(x) validateLink(x, "SPEC_EXTR", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_extern +#define SPEC_CODE(x) validateLink(x, "SPEC_CODE", #x, SPECIFIER, __FILE__, __LINE__)->select.s._codesg +#define SPEC_ABSA(x) validateLink(x, "SPEC_ABSA", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_absadr +#define SPEC_BANK(x) validateLink(x, "SPEC_BANK", #x, SPECIFIER, __FILE__, __LINE__)->select.s._regbank +#define SPEC_ADDR(x) validateLink(x, "SPEC_ADDR", #x, SPECIFIER, __FILE__, __LINE__)->select.s._addr +#define SPEC_STAK(x) validateLink(x, "SPEC_STAK", #x, SPECIFIER, __FILE__, __LINE__)->select.s._stack +#define SPEC_CVAL(x) validateLink(x, "SPEC_CVAL", #x, SPECIFIER, __FILE__, __LINE__)->select.s.const_val +#define SPEC_BSTR(x) validateLink(x, "SPEC_BSTR", #x, SPECIFIER, __FILE__, __LINE__)->select.s._bitStart +#define SPEC_BLEN(x) validateLink(x, "SPEC_BLEN", #x, SPECIFIER, __FILE__, __LINE__)->select.s._bitLength +#define SPEC_BUNNAMED(x) validateLink(x, "SPEC_BUNNAMED", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_bitUnnamed +#define SPEC_NEEDSPAR(x) validateLink(x, "SPEC_NEEDSPAR", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_needspar + +/* Sleaze: SPEC_ISR_SAVED_BANKS is only used on + * function type symbols, which obviously cannot + * be of BIT type. Therefore, we recycle the + * _bitStart field instead of defining a new field. + */ +#define SPEC_ISR_SAVED_BANKS(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s._bitStart +#define SPEC_VOLATILE(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_volatile +#define SPEC_CONST(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_const +#define SPEC_RESTRICT(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_restrict +#define SPEC_ADDRSPACE(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.addrspace +#define SPEC_STRUCT(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.v_struct +#define SPEC_TYPEDEF(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_typedef +#define SPEC_REGPARM(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_isregparm +#define SPEC_ARGREG(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.argreg +#define SPEC_INLINE(x) validateLink(x, "SPEC_INLINE", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_inline +#define SPEC_NORETURN(x) validateLink(x, "SPEC_NORETURN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_noreturn +#define SPEC_ALIGNAS(x) validateLink(x, "SPEC_ALIGNAS", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_alignas + +/* type check macros */ +#define IS_DECL(x) ( x && x->xclass == DECLARATOR ) +#define IS_SPEC(x) ( x && x->xclass == SPECIFIER ) + +#define IS_ARRAY(x) (IS_DECL(x) && DCL_TYPE(x) == ARRAY) +#define IS_DATA_PTR(x) (IS_DECL(x) && DCL_TYPE(x) == POINTER) +#define IS_SMALL_PTR(x) (IS_DECL(x) && (DCL_TYPE(x) == POINTER || \ + DCL_TYPE(x) == IPOINTER || \ + DCL_TYPE(x) == PPOINTER )) +#define IS_PTR(x) (IS_DECL(x) && (DCL_TYPE(x) == POINTER || \ + DCL_TYPE(x) == FPOINTER || \ + DCL_TYPE(x) == GPOINTER || \ + DCL_TYPE(x) == IPOINTER || \ + DCL_TYPE(x) == PPOINTER || \ + DCL_TYPE(x) == EEPPOINTER || \ + DCL_TYPE(x) == CPOINTER || \ + DCL_TYPE(x) == UPOINTER )) +#define IS_PTR_CONST(x) (IS_PTR(x) && DCL_PTR_CONST(x)) +#define IS_PTR_RESTRICT(x) (IS_PTR(x) && DCL_PTR_RESTRICT(x)) +#define IS_FARPTR(x) (IS_DECL(x) && DCL_TYPE(x) == FPOINTER) +#define IS_CODEPTR(x) (IS_DECL(x) && DCL_TYPE(x) == CPOINTER) +#define IS_GENPTR(x) (IS_DECL(x) && DCL_TYPE(x) == GPOINTER) +#define IS_FUNCPTR(x) (IS_DECL(x) && (DCL_TYPE(x) == CPOINTER || DCL_TYPE(x) == GPOINTER) && IS_FUNC(x->next)) +#define IS_FUNC(x) (IS_DECL(x) && DCL_TYPE(x) == FUNCTION) +#define IS_LONG(x) (IS_SPEC(x) && x->select.s.b_long) +#define IS_LONGLONG(x) (IS_SPEC(x) && x->select.s.b_longlong) +#define IS_UNSIGNED(x) (IS_SPEC(x) && x->select.s.b_unsigned) +#define IS_TYPEDEF(x) (IS_SPEC(x) && x->select.s.b_typedef) +#define IS_CONSTANT(x) (isConstant (x)) +#define IS_RESTRICT(x) (isRestrict (x)) +#define IS_STRUCT(x) (IS_SPEC(x) && x->select.s.noun == V_STRUCT) +#define IS_ABSOLUTE(x) (IS_SPEC(x) && x->select.s.b_absadr ) +#define IS_REGISTER(x) (IS_SPEC(x) && SPEC_SCLS(x) == S_REGISTER) +#define IS_RENT(x) (IS_SPEC(x) && x->select.s._reent ) +#define IS_STATIC(x) (IS_SPEC(x) && SPEC_STAT(x)) +#define IS_INLINE(x) (IS_SPEC(x) && SPEC_INLINE(x)) +#define IS_NORETURN(x) (IS_SPEC(x) && SPEC_NORETURN(x)) +#define IS_INT(x) (IS_SPEC(x) && x->select.s.noun == V_INT) +#define IS_VOID(x) (IS_SPEC(x) && x->select.s.noun == V_VOID) +#define IS_BOOL(x) (IS_SPEC(x) && x->select.s.noun == V_BOOL) +#define IS_CHAR(x) (IS_SPEC(x) && x->select.s.noun == V_CHAR) +#define IS_EXTERN(x) (IS_SPEC(x) && x->select.s.b_extern) +#define IS_VOLATILE(x) (isVolatile (x)) +#define IS_INTEGRAL(x) (IS_SPEC(x) && (x->select.s.noun == V_INT || \ + x->select.s.noun == V_BOOL || \ + x->select.s.noun == V_CHAR || \ + x->select.s.noun == V_BITFIELD || \ + x->select.s.noun == V_BBITFIELD || \ + x->select.s.noun == V_BIT || \ + x->select.s.noun == V_SBIT )) +#define IS_BITFIELD(x) (IS_SPEC(x) && (x->select.s.noun == V_BITFIELD || \ + x->select.s.noun == V_BBITFIELD )) +#define IS_BITVAR(x) (IS_SPEC(x) && (x->select.s.noun == V_BITFIELD || \ + x->select.s.noun == V_BBITFIELD || \ + x->select.s.noun == V_BIT || \ + x->select.s.noun == V_SBIT )) +#define IS_BIT(x) (IS_SPEC(x) && (x->select.s.noun == V_BIT || \ + x->select.s.noun == V_SBIT )) +#define IS_BOOLEAN(x) (IS_SPEC(x) && (x->select.s.noun == V_BIT || \ + x->select.s.noun == V_SBIT || \ + x->select.s.noun == V_BBITFIELD || \ + x->select.s.noun == V_BOOL )) +#define IS_FLOAT(x) (IS_SPEC(x) && x->select.s.noun == V_FLOAT) +#define IS_FIXED16X16(x) (IS_SPEC(x) && x->select.s.noun == V_FIXED16X16) +#define IS_FIXED(x) (IS_FIXED16X16(x)) +#define IS_ARITHMETIC(x) (IS_INTEGRAL(x) || IS_FLOAT(x) || IS_FIXED(x)) +#define IS_AGGREGATE(x) (IS_ARRAY(x) || IS_STRUCT(x)) +#define IS_LITERAL(x) (IS_SPEC(x) && x->select.s.sclass == S_LITERAL) +#define IS_CODE(x) (IS_SPEC(x) && SPEC_SCLS(x) == S_CODE) +#define IS_REGPARM(x) (IS_SPEC(x) && SPEC_REGPARM(x)) + +#define IS_VALID_PARAMETER_STORAGE_CLASS_SPEC(x) (!SPEC_TYPEDEF(x) && !SPEC_EXTR(x) && !SPEC_STAT(x) && SPEC_SCLS(x) != S_AUTO) + +/* symbol check macros */ +#define IS_AUTO(x) (x->level && !IS_STATIC(x->etype) && !IS_EXTERN(x->etype)) + +/* forward declaration for the global vars */ +extern bucket *SymbolTab[]; +extern bucket *StructTab[]; +extern bucket *TypedefTab[]; +extern bucket *LabelTab[]; +extern bucket *enumTab[]; +extern bucket *AddrspaceTab[]; +extern symbol *fsadd; +extern symbol *fssub; +extern symbol *fsmul; +extern symbol *fsdiv; +extern symbol *fseq; +extern symbol *fsneq; +extern symbol *fslt; + +extern symbol *fps16x16_add; +extern symbol *fps16x16_sub; +extern symbol *fps16x16_mul; +extern symbol *fps16x16_div; +extern symbol *fps16x16_eq; +extern symbol *fps16x16_neq; +extern symbol *fps16x16_lt; +extern symbol *fps16x16_lteq; +extern symbol *fps16x16_gt; +extern symbol *fps16x16_gteq; + +/* Dims: mul/div/mod, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED/BOTH */ +extern symbol *muldiv[3][4][4]; +/* 16 x 16 -> 32 multiplication SIGNED/UNSIGNED */ +extern symbol *muls16tos32[2]; +/* Dims: BYTE/WORD/DWORD/QWORD SIGNED/UNSIGNED */ +extern sym_link *multypes[4][2]; +/* Dims: to/from float, BYTE/WORD/DWORD/QWORD, SIGNED/USIGNED */ +extern symbol *conv[2][4][2]; +/* Dims: to/from fixed16x16, BYTE/WORD/DWORD/QWORD/FLOAT, SIGNED/USIGNED */ +extern symbol *fp16x16conv[2][5][2]; +/* Dims: shift left/shift right, BYTE/WORD/DWORD/QWORD, SIGNED/UNSIGNED */ +extern symbol *rlrr[2][4][2]; + +extern symbol *memcpy_builtin; + +#define SCHARTYPE multypes[0][0] +#define UCHARTYPE multypes[0][1] +#define INTTYPE multypes[1][0] +#define UINTTYPE multypes[1][1] +#define LONGTYPE multypes[2][0] +#define ULONGTYPE multypes[2][1] +#define LONGLONGTYPE multypes[3][0] +#define ULONGLONGTYPE multypes[3][1] + +extern sym_link *floatType; +extern sym_link *fixed16x16Type; + +#include "SDCCval.h" + +typedef enum +{ + RESULT_TYPE_NONE = 0, /* operands will be promoted to int */ + RESULT_TYPE_BOOL, + RESULT_TYPE_CHAR, + RESULT_TYPE_INT, + RESULT_TYPE_OTHER, /* operands will be promoted to int */ + RESULT_TYPE_IFX, + RESULT_TYPE_GPTR /* operands will be promoted to generic ptr */ +} RESULT_TYPE; + +/* forward definitions for the symbol table related functions */ +void initSymt (); +symbol *newSymbol (const char *, long); +sym_link *newLink (SYM_LINK_CLASS); +sym_link *newFloatLink (); +structdef *newStruct (const char *); +void addDecl (symbol *, int, sym_link *); +sym_link *finalizeSpec (sym_link *); +sym_link *mergeSpec (sym_link *, sym_link *, const char *name); +sym_link *mergeDeclSpec (sym_link *, sym_link *, const char *name); +symbol *reverseSyms (symbol *); +sym_link *reverseLink (sym_link *); +symbol *copySymbol (const symbol *); +symbol *copySymbolChain (const symbol *); +void printSymChain (symbol *, int); +void printStruct (structdef *, int); +char *genSymName (long); +sym_link *getSpec (sym_link *); +int compStructSize (int, structdef *); +sym_link *copyLinkChain (const sym_link *); +int checkDecl (symbol *, int); +void checkBasic (sym_link *, sym_link *); +value *checkPointerIval (sym_link *, value *); +value *checkStructIval (symbol *, value *); +value *checkArrayIval (sym_link *, value *); +value *checkIval (sym_link *, value *); +unsigned int getSize (sym_link *); +unsigned int bitsForType (sym_link *); +sym_link *newIntLink (); +sym_link *newCharLink (); +sym_link *newLongLink (); +sym_link *newBoolLink (); +sym_link *newVoidLink (); +int compareType (sym_link *, sym_link *); +int compareTypeExact (sym_link *, sym_link *, long); +int compareTypeInexact (sym_link *, sym_link *); +int checkFunction (symbol *, symbol *); +void cleanUpLevel (bucket **, long); +void cleanUpBlock (bucket **, int); +symbol *getAddrspace (sym_link *type); +int funcInChain (sym_link *); +void addSymChain (symbol **); +sym_link *structElemType (sym_link *, value *); +symbol *getStructElement (structdef *, symbol *); +sym_link *computeType (sym_link *, sym_link *, RESULT_TYPE, int); +void processFuncPtrArgs (sym_link *); +void processFuncArgs (symbol *); +int isSymbolEqual (const symbol *, const symbol *); +int powof2 (TYPE_TARGET_ULONG); +void dbuf_printTypeChain (sym_link *, struct dbuf_s *); +void printTypeChain (sym_link *, FILE *); +void printTypeChainRaw (sym_link *, FILE *); +void initCSupport (); +void initBuiltIns (); +void pointerTypes (sym_link *, sym_link *); +void cdbStructBlock (int); +void initHashT (); +bucket *newBucket (); +void addSym (bucket **, void *, char *, long, int, int checkType); +void deleteSym (bucket **, void *, const char *); +void *findSym (bucket **, void *, const char *); +void *findSymWithLevel (bucket **, struct symbol *); +void *findSymWithBlock (bucket **, struct symbol *, int, long); +void changePointer (sym_link * p); +void checkTypeSanity (sym_link * etype, const char *name); +sym_link *typeFromStr (const char *); +STORAGE_CLASS sclsFromPtr (sym_link * ptr); +sym_link *newEnumType (symbol *); +void promoteAnonStructs (int, structdef *); +int isConstant (sym_link * type); +int isVolatile (sym_link * type); +int isRestrict (sym_link * type); +value *aggregateToPointer (value *); + + +extern char *nounName (sym_link *); /* noun strings */ +extern void printFromToType (sym_link *, sym_link *); + +#endif diff --git a/src/SDCCsystem.c b/src/SDCCsystem.c new file mode 100644 index 0000000..a340045 --- /dev/null +++ b/src/SDCCsystem.c @@ -0,0 +1,408 @@ +/*------------------------------------------------------------------------- + SDCCsystem - SDCC system & pipe functions + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifdef _WIN32 +/* avoid DATADIR definition clash :-( */ +#undef DATADIR +#include +#undef TRUE +#undef FALSE +#include +#include +#include +#else +#include +#endif +#include +#include +#include "SDCCglobl.h" +#include "SDCCutil.h" +#include "dbuf_string.h" +#include "SDCCsystem.h" +#include "newalloc.h" + + +set *binPathSet = NULL; /* set of binary paths */ + + +/*! + * get command and arguments from command line + */ + +static void +split_command (const char *cmd_line, char **command, char **params) +{ + const char *p, *cmd_start; + char delim; + char *str; + unsigned len; + + /* skip leading spaces */ + for (p = cmd_line; isspace (*p); p++) + ; + + /* get command */ + switch (*p) + { + case '\'': + case '"': + delim = *p; + cmd_start = ++p; + break; + + default: + delim = ' '; + cmd_start = p; + } + + if (delim == ' ') + { + while (*p != '\0' && !isspace (*p)) + p++; + } + else + { + while (*p != '\0' && *p != delim) + p++; + } + + if (command != NULL) + { + len = p - cmd_start; + str = Safe_alloc (len + 1); + strncpy (str, cmd_start, len); + str[len] = '\0'; + *command = str; + } + + p++; + + /* skip spaces before parameters */ + while (isspace (*p)) + p++; + + /* get parameters */ + if (params != NULL) + *params = Safe_strdup (p); +} + + +/*! + * find the command: + * 1) if the command is specified by path, try it + * 2) try to find the command in predefined path's + * 3) trust on $PATH + */ + +#ifdef _WIN32 +/* WIN32 version */ + +/* + * I don't like this solution, but unfortunately cmd.exe and command.com + * don't accept something like this: + * "program" "argument" + * Cmd.exe accepts the following: + * ""program" "argument"" + * but command.com doesn't. + * The following is accepted by both: + * program "argument" + * + * So the most portable WIN32 solution is to use GetShortPathName() for + * program to get rid of spaces, so that quotes are not needed :-( + * Using spawnvp() instead of system() is more portable cross platform approach, + * but then also a substitute for _popen() should be developed... + */ + +#define EXE_EXT ".exe" + +/*! + * merge command and parameters to command line + */ + +static const char * +merge_command (const char *command, const char *params) +{ + struct dbuf_s dbuf; + + /* allocate extra space for ' ' and '\0' */ + dbuf_init (&dbuf, strlen (command) + strlen (params) + 2); + + dbuf_append_str (&dbuf, command); + dbuf_append (&dbuf, " ", 1); + dbuf_append_str (&dbuf, params); + + return dbuf_detach_c_str (&dbuf); +} + + +/*! + * check if path/command exist by converting it to short file name + * if it exists, compose with args and return it + */ + +static const char * +compose_command_line (const char *path, const char *command, const char *args) +{ + unsigned len; + struct dbuf_s cmdPath; + char shortPath[PATH_MAX]; + + dbuf_init (&cmdPath, PATH_MAX); + + if (path != NULL) + dbuf_makePath (&cmdPath, path, command); + else + dbuf_append_str (&cmdPath, command); + + /* Try if cmdPath or cmdPath.exe exist by converting it to the short path name */ + len = GetShortPathName (dbuf_c_str (&cmdPath), shortPath, sizeof shortPath); + assert (len < sizeof shortPath); + if (0 == len) + { + dbuf_append_str (&cmdPath, EXE_EXT); + len = GetShortPathName (dbuf_c_str (&cmdPath), shortPath, sizeof shortPath); + assert (len < sizeof shortPath); + } + if (0 != len) + { + /* compose the command line */ + return merge_command (shortPath, args); + } + else + { + /* path/command not found */ + return NULL; + } +} + + +static const char * +get_path (const char *cmd) +{ + const char *cmdLine; + char *command; + char *args; + char *path; + + /* get the command */ + split_command (cmd, &command, &args); + + if (NULL == (cmdLine = compose_command_line(NULL, command, args))) + { + /* not an absolute path: try to find the command in predefined binary paths */ + if (NULL != (path = (char *)setFirstItem (binPathSet))) + { + while (NULL == (cmdLine = compose_command_line (path, command, args)) && + NULL != (path = (char *)setNextItem (binPathSet))) + ; + } + + if (NULL == cmdLine) + { + /* didn't found the command in predefined binary paths: try with PATH */ + char *envPath; + + if (NULL != (envPath = getenv("PATH"))) + { + /* make a local copy; strtok() will modify it */ + envPath = Safe_strdup (envPath); + + if (NULL != (path = strtok (envPath, ";"))) + { + while (NULL == (cmdLine = compose_command_line (path, command, args)) && + NULL != (path = strtok (NULL, ";"))) + ; + } + + Safe_free (envPath); + } + } + + /* didn't found it; probably this won't help neither :-( */ + if (NULL == cmdLine) + cmdLine = merge_command (command, args); + } + + Safe_free(command); + Safe_free(args); + + return cmdLine; +} + +#else +/* *nix version */ + +/*! + * merge command and parameters to command line + */ + +static const char * +merge_command (const char *command, const char *params) +{ + struct dbuf_s dbuf; + char *s = shell_escape (command); + + /* allocate extra space for ' ' and '\0' */ + dbuf_init (&dbuf, strlen (command) + strlen (params) + 2); + + dbuf_append_str (&dbuf, s); + dbuf_append (&dbuf, " ", 1); + dbuf_append_str (&dbuf, params); + + return dbuf_detach_c_str (&dbuf); +} + + +/*! + * check if the path is relative or absolute (if contains the dir separator) + */ + +static int +has_path (const char *path) +{ + return dbuf_splitPath (path, NULL, NULL); +} + + +static const char * +get_path (const char *cmd) +{ + const char *cmdLine = NULL; + char *command; + char *args; + char *path; + + /* get the command */ + split_command (cmd, &command, &args); + + if (!has_path (command)) + { + /* try to find the command in predefined binary paths */ + if (NULL != (path = (char *)setFirstItem (binPathSet))) + { + do + { + struct dbuf_s dbuf; + const char *cmdPath; + + dbuf_init (&dbuf, PATH_MAX); + dbuf_makePath (&dbuf, path, command); + cmdPath = dbuf_detach (&dbuf); + + /* Try if cmdPath */ + if (0 == access (cmdPath, X_OK)) + { + /* compose the command line */ + cmdLine = merge_command (cmdPath, args); + break; + } + } while (NULL != (path = (char *)setNextItem (binPathSet))); + } + if (NULL == cmdLine) + cmdLine = merge_command (command, args); + + Safe_free (command); + Safe_free (args); + + return cmdLine; + } + else + { + /* + * the command is defined with absolute path: + * just return it + */ + Safe_free (command); + Safe_free (args); + + return Safe_strdup (cmd); + } +} +#endif + + +/*! + * call an external program with arguements + */ + +int +sdcc_system (const char *cmd) +{ + int e; + const char *cmdLine = get_path (cmd); + + assert (NULL != cmdLine); + + if (options.verboseExec) + printf ("+ %s\n", cmdLine); + + e = system (cmdLine); + + if (options.verboseExec && e) + printf ("+ %s returned errorcode %d\n", cmdLine, e); + + dbuf_free (cmdLine); + + return e; +} + + +/*! + * pipe an external program with arguements + */ + +#ifdef _WIN32 +#define sdcc_popen_read(cmd) _popen ((cmd), "rt") +int +sdcc_pclose (FILE *fp) +{ + return _pclose (fp); +} +#else +#define sdcc_popen_read(cmd) popen ((cmd), "r") +int +sdcc_pclose (FILE *fp) +{ + return pclose (fp); +} +#endif + +FILE * +sdcc_popen (const char *cmd) +{ + FILE *fp; + const char *cmdLine = get_path (cmd); + + assert (NULL != cmdLine); + + if (options.verboseExec) + { + printf ("+ %s\n", cmdLine); + } + + fp = sdcc_popen_read (cmdLine); + dbuf_free (cmdLine); + + return fp; +} diff --git a/src/SDCCsystem.h b/src/SDCCsystem.h new file mode 100644 index 0000000..d70caca --- /dev/null +++ b/src/SDCCsystem.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + SDCCsystem - SDCC system & pipe functions + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCSYSTEM_H +#define SDCCSYSTEM_H + +#include +#include "SDCCset.h" + +extern set *binPathSet; /* set of binary paths */ + +int sdcc_system (const char *cmd); +FILE *sdcc_popen (const char *cmd); +int sdcc_pclose (FILE *fp); + +#endif diff --git a/src/SDCCtree_dec.hpp b/src/SDCCtree_dec.hpp new file mode 100644 index 0000000..7870396 --- /dev/null +++ b/src/SDCCtree_dec.hpp @@ -0,0 +1,648 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2011 +// +// (c) 2010-2011 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// +// Some routines for tree-decompositions. +// +// A tree decomposition is a graph that has a set of vertex indices as bundled property, e.g.: +// +// struct tree_dec_node +// { +// std::set bag; +// }; +// typedef boost::adjacency_list tree_dec_t; +// +// The following are the routines that are most likely to be interesting for outside use: +// +// void nicify(T_t &T) +// Transforms a tree decomposition T into a nice tree decomposition +// +// void thorup_tree_decomposition(T_t &tree_decomposition, const G_t &cfg) +// Creates a tree decomposition T from a graph cfg using Thorup's heuristic. +// +// void tree_decomposition_from_elimination_ordering(T_t &T, std::list& l, const G_t &G) +// Creates a tree decomposition T of a graph G from an elimination ordering l. +// +// void thorup_elimination_ordering(l_t &l, const J_t &J) +// Creates an elimination ordering l of a graph J using Thorup's heuristic. +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#undef RANGE +#undef BLOCK + +struct forget_properties +{ + template + void operator()(const T1&, const T2&) const + { + } +}; + +// Thorup algorithm D. +// The use of the multimap makes the complexity of this O(|I|log|I|), which could be reduced to O(|I|). +template +void thorup_D(l_t &l, const std::multimap &MJ, const std::multimap &MS, const unsigned int n) +{ + std::map m; + + l.clear(); + + unsigned int i = 0; + for (unsigned int j = n; j > 0;) + { + j--; + if (m.find(j) == m.end()) + m[j] = i++; + + std::multimap::const_iterator k, k_end; + + for (boost::tie(k, k_end) = MS.equal_range(j); k != k_end; ++k) + if (m.find(k->second) == m.end()) + m[k->second] = i++; + + for (boost::tie(k, k_end) = MJ.equal_range(j); k != k_end; ++k) + if (m.find(k->second) == m.end()) + m[k->second] = i++; + } + + std::vector v(n); + + std::map::iterator mi; + + for (mi = m.begin(); mi != m.end(); ++mi) + v[mi->second] = mi->first; + + for (i = 0; i < n; i++) + l.push_back(v[i]); +} + +// Thorup algorithm E. +// The use of the multimap makes the complexity of this O(|I|log|I|), which could be reduced to O(|I|). +template +void thorup_E(std::multimap &M, const I_t &I) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + typedef typename boost::property_map::type index_map; + index_map index = boost::get(boost::vertex_index, I); + + std::stack > s; + + M.clear(); + + s.push(std::pair(-1, boost::num_vertices(I))); + + for (unsigned int i = 0; i < boost::num_vertices(I); i++) + { + unsigned int j = i; + adjacency_iter_t j_curr, j_end; + + for (boost::tie(j_curr, j_end) = boost::adjacent_vertices(i, I); j_curr != j_end; ++j_curr) + if (index[*j_curr] > j) + j = index[*j_curr]; + + if (j == i) + continue; + + while (s.top().second <= i) + { + M.insert(std::pair(s.top().second, s.top().first)); + s.pop(); + } + + unsigned int i2 = i; + while (j >= s.top().second && s.top().second > i2) + { + i2 = s.top().first; + s.pop(); + } + + s.push(std::pair(i2, j)); + } + + // Thorup forgot this in his paper. Without it, some maximal chains are omitted. + while(s.size() > 1) + { + M.insert(std::pair(s.top().second, s.top().first)); + s.pop(); + } +} + +// Heuristically give an elimination ordering for a directed graph. +// For a description of this, including algorithms D and E, see +// Mikkel Thorup, "All Structured Programs have Small Tree-Width and Good Register Allocation", Appendix A. +// The use of the multimap makes the complexity of this O(|I|log|I|), could be reduced to O(|I|). +template +void thorup_elimination_ordering(l_t &l, const G_t &G) +{ + // Remove edges to immediately following instruction. By "each statement can have at most obne jump" in the last paragraph of Appendix A it is clear that Thorup does not consider the implicit next-instruction-edges as jumps. + boost::adjacency_list J; + boost::copy_graph(G, J, boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); + for (unsigned int i = 0; i < boost::num_vertices(J) - 1; i++) + remove_edge(i, i + 1, J); + + // Todo: Implement a graph adaptor for boost that allows to treat directed graphs as undirected graphs. + boost::adjacency_list S; + boost::copy_graph(J, S); + + std::multimap MJ, MS; + + thorup_E(MJ, J); + + thorup_E(MS, S); + + thorup_D(l, MJ, MS, num_vertices(J)); +} + +// Finds a (the newest) bag that contains all vertices in X in the tree decomposition T. +template +typename boost::graph_traits::vertex_iterator find_bag(const X_t &X, const T_t &T) +{ + typedef typename boost::graph_traits::vertex_iterator T_vertex_iter_t; + typedef typename X_t::const_iterator vertex_index_iter_t; + + T_vertex_iter_t t, t_end, t_found; + vertex_index_iter_t v; + + for (boost::tie(t, t_end) = vertices(T), t_found = t_end; t != t_end; ++t) + { + for (v = X.begin(); v != X.end(); ++v) + if (T[*t].bag.find(*v) == T[*t].bag.end()) + break; + + if (v == X.end()) + t_found = t; + } + + if (t_found == t_end) // Todo: Better error handling (throw exception?) + { + std::cerr << "find_bag() failed.\n"; + std::cerr.flush(); + } + + return(t_found); +} + +// Add edges to make the vertices in X a clique in G. +template +void make_clique(const X_t &X , G_t &G) +{ + typename X_t::const_iterator n1, n2; + for (n1 = X.begin(); n1 != X.end(); n1++) + for (n2 = n1, ++n2; n2 != X.end(); ++n2) + add_edge(*n1, *n2, G); +} + +template +void add_vertices_to_tree_decomposition(T_t &T, const v_t v, const v_t v_end, G_t &G, std::vector &active) +{ + // Base case: Empty graph. Create an empty bag. + if (v == v_end) + { + boost::add_vertex(T); + return; + } + + // Todo: A more elegant solution, e.g. using subgraphs or filtered graphs. + + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + typedef typename boost::property_map::type index_map; + index_map index = boost::get(boost::vertex_index, G); + + // Get the neigbours + adjacency_iter_t n, n_end; + decltype(T[0].bag) neighbours; + for (boost::tie(n, n_end) = boost::adjacent_vertices(*v, G); n != n_end; ++n) + if (active[index[*n]]) + neighbours.insert(index[*n]); + + // Recurse + active[*v] = false; + make_clique(neighbours, G); + v_t v_next = v; + add_vertices_to_tree_decomposition(T, ++v_next, v_end, G, active); + + // Add new bag + typename boost::graph_traits::vertex_iterator t; + typename boost::graph_traits::vertex_descriptor s; + t = find_bag(neighbours, T); + s = boost::add_vertex(T); + boost::add_edge(*t, s, T); + T[s].bag = neighbours; + T[s].bag.insert(*v); +} + +// Create a tree decomposition from an elimination ordering. +template +void tree_decomposition_from_elimination_ordering(T_t &T, const std::list& l, const G_t &G) +{ + std::list::const_reverse_iterator v, v_end; + v = l.rbegin(), v_end = l.rend(); + + // Todo: Implement a graph adaptor for boost that allows to treat directed graphs as undirected graphs. + boost::adjacency_list G_sym; + boost::copy_graph(G, G_sym, boost::vertex_copy(forget_properties()).edge_copy(forget_properties())); + + std::vector active(boost::num_vertices(G), true); + + add_vertices_to_tree_decomposition(T, v, v_end, G_sym, active); +} + +template +void thorup_tree_decomposition(T_t &tree_decomposition, const G_t &cfg) +{ + std::list elimination_ordering; + + thorup_elimination_ordering(elimination_ordering, cfg); + + tree_decomposition_from_elimination_ordering(tree_decomposition, elimination_ordering, cfg); +} + +// Ensure that all joins are at proper join nodes: Each node that has two children has the same bag as its children. +// Complexity: Linear in the number of vertices of T. +template +void nicify_joins(T_t &T, typename boost::graph_traits::vertex_descriptor t) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = boost::adjacent_vertices(t, T); + + switch (out_degree(t, T)) + { + case 0: + return; + case 1: + nicify_joins(T, *c); + return; + case 2: + break; + default: + c0 = *c++; + c1 = *c; + typename boost::graph_traits::vertex_descriptor d; + d = boost::add_vertex(T); + add_edge(d, c0, T); + add_edge(d, c1, T); + boost::remove_edge(t, c0, T); + boost::remove_edge(t, c1, T); + T[d].bag = T[t].bag; + boost::add_edge(t, d, T); + nicify_joins(T, t); + return; + } + + c0 = *c++; + c1 = *c; + nicify_joins(T, c0); + if (T[t].bag != T[c0].bag) + { + typename boost::graph_traits::vertex_descriptor d; + d = boost::add_vertex(T); + boost::add_edge(d, c0, T); + boost::remove_edge(t, c0, T); + T[d].bag = T[t].bag; + boost::add_edge(t, d, T); + } + nicify_joins(T, c1); + if (T[t].bag != T[c1].bag) + { + typename boost::graph_traits::vertex_descriptor d = boost::add_vertex(T); + boost::add_edge(d, c1, T); + boost::remove_edge(t, c1, T); + T[d].bag = T[t].bag; + boost::add_edge(t, d, T); + } +} + +// Ensure that all nodes' bags are either a subset or a superset of their successors'. +// Complexity: Linear in the number of vertices of T. +template +void nicify_diffs(T_t &T, typename boost::graph_traits::vertex_descriptor t) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (boost::out_degree(t, T)) + { + case 0: + if (T[t].bag.size()) + boost::add_edge(t, boost::add_vertex(T), T); + return; + case 1: + break; + case 2: + c0 = *c++; + c1 = *c; + nicify_diffs(T, c0); + nicify_diffs(T, c1); + return; + default: + std::cerr << "nicify_diffs error.\n"; + return; + } + + c0 = *c; + nicify_diffs(T, c0); + + // Redundant bags are isolated, and thus marked for later removal. + if (T[t].bag == T[c0].bag) + { + T[c0].bag.clear(); + boost::remove_edge(t, c0, T); + adjacency_iter_t c, c_end; + for(boost::tie(c, c_end) = adjacent_vertices(c0, T); c != c_end; ++c) + boost::add_edge(t, *c, T); + boost::clear_vertex(c0, T); + } + + if (std::includes(T[t].bag.begin(), T[t].bag.end(), T[c0].bag.begin(), T[c0].bag.end()) || std::includes(T[c0].bag.begin(), T[c0].bag.end(), T[t].bag.begin(), T[t].bag.end())) + return; + + typename boost::graph_traits::vertex_descriptor d = boost::add_vertex(T); + + boost::add_edge(d, c0, T); + boost::remove_edge(t, c0, T); + std::set_intersection(T[t].bag.begin(), T[t].bag.end(), T[c0].bag.begin(), T[c0].bag.end(), std::inserter(T[d].bag, T[d].bag.begin())); + boost::add_edge(t, d, T); +} + +// Ensure that all nodes' bags' sizes differ by at most one to their successors'. +template +void nicify_diffs_more(T_t &T, typename boost::graph_traits::vertex_descriptor t) +{ + typedef typename boost::graph_traits::adjacency_iterator adjacency_iter_t; + + adjacency_iter_t c, c_end; + typename boost::graph_traits::vertex_descriptor c0, c1; + + boost::tie(c, c_end) = adjacent_vertices(t, T); + + switch (boost::out_degree(t, T)) + { + case 0: + if (T[t].bag.size() > 1) + { + typename boost::graph_traits::vertex_descriptor d = boost::add_vertex(T); + T[d].bag = T[t].bag; + T[d].bag.erase(T[d].bag.begin()); + T[d].weight = 0; + boost::add_edge(t, d, T); + nicify_diffs_more(T, t); + } + else + T[t].weight = 0; + return; + case 1: + break; + case 2: + c0 = *c++; + c1 = *c; + nicify_diffs_more(T, c0); + nicify_diffs_more(T, c1); + { + const unsigned l = T[c0].weight; + const unsigned r = T[c1].weight; + T[t].weight = (l == r) ? l + 1 : std::max(l , r); + } + return; + default: + std::cerr << "nicify_diffs_more error.\n"; + return; + } + + c0 = *c; + + size_t c0_size, t_size; + t_size = T[t].bag.size(); + c0_size = T[c0].bag.size(); + + if (t_size <= c0_size + 1 && t_size + 1 >= c0_size) + { + nicify_diffs_more(T, c0); + T[t].weight = T[c0].weight; + return; + } + + typename boost::graph_traits::vertex_descriptor d = add_vertex(T); + boost::add_edge(d, c0, T); + boost::remove_edge(t, c0, T); + T[d].bag = T[t_size > c0_size ? t : c0].bag; + typename decltype(T[d].bag)::iterator i; + for (i = T[d].bag.begin(); T[t_size < c0_size ? t : c0].bag.find(*i) != T[t_size < c0_size ? t : c0].bag.end(); ++i); + T[d].bag.erase(i); + boost::add_edge(t, d, T); + + nicify_diffs_more(T, t); +} + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP +#include +#include +#include +#include +#include + +using treedec::find_root; +#else +// Find a root of an acyclic graph T +// Complexity: Linear in the number of vertices of T. +template +typename boost::graph_traits::vertex_descriptor find_root(T_t &T) +{ + typename boost::graph_traits::vertex_descriptor t; + typename boost::graph_traits::in_edge_iterator e, e_end; + + t = *(boost::vertices(T).first); + + for (boost::tie(e, e_end) = boost::in_edges(t, T); e != e_end; boost::tie(e, e_end) = boost::in_edges(t, T)) + t = boost::source(*e, T); + + return(t); +} +#endif + +// Remove isolated vertices possibly introduced by nicify_diffs(). Complicated, since boost does not support removing more than one vertex at a time. +template +void remove_isolated_vertices(T_t &T) +{ + bool change = true; + while(change) + { + change = false; + if(boost::num_vertices(T) <= 1) + return; + + for (unsigned int i = 0; i < boost::num_vertices(T); i++) + if(!boost::out_degree(i, T) && !boost::in_degree(i, T)) + { + remove_vertex(i, T); + change = true; + break; + } + } +} + +// Transform a tree decomposition into a nice tree decomposition. +template +void nicify(T_t &T) +{ + typename boost::graph_traits::vertex_descriptor t; + + t = find_root(T); + + // Ensure we have an empty bag at the root. + if(T[t].bag.size()) + { + typename boost::graph_traits::vertex_descriptor d = t; + t = add_vertex(T); + boost::add_edge(t, d, T); + } + + nicify_joins(T, t); + nicify_diffs(T, t); + nicify_diffs_more(T, t); + remove_isolated_vertices(T); +} + +class cfg_titlewriter +{ +public: + explicit cfg_titlewriter(const std::string& f, const std::string& p) : function(f), purpose(p) + { + } + void operator()(std::ostream& out) const + { + out << "graph [label=\"Control-flow-graph for " << purpose << " (function " << function << ")\"]\n"; + } +private: + std::string function; + std::string purpose; +}; + +class dec_titlewriter +{ +public: + explicit dec_titlewriter(unsigned int w, const std::string& f, const std::string& p) : function(f), purpose(p) + { + width = w; + } + void operator()(std::ostream& out) const + { + out << "graph [label=\"Tree-decomposition of width " << width << " for " << purpose << " (function " << function << ")\"]\n"; + } +private: + unsigned int width; + std::string function; + std::string purpose; +}; + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + +#include +#include +#include + +#include +#include +#include + +template +void copy_undir(Gd_t &dst, const Gs_t &src) +{ + for(unsigned i = 0; i < boost::num_vertices(src); i++) + boost::add_vertex(dst); + + typename boost::graph_traits::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(src); e != e_end; ++e) + { + wassert (boost::source(*e, src) != boost::target(*e, src)); + if (!boost::edge(boost::source(*e, src), boost::target(*e, src), dst).second) + boost::add_edge(boost::source(*e, src), boost::target(*e, src), dst); + } +} + +#endif + +#undef USE_THORUP // Thorup's classic algorithm (default in SDCC pre-3.7.0). Substantially worse width than the others. +#define USE_PP_FI_TM 1 // A good trade-off between width and runtime +#undef USE_EX17 // Slightly better width than PP_FI_TM, but no polynomial runtime bound. +#undef USE_PP_MD // Slightly worse width than PP_FI_TM. +#undef USE_PP_FI // Slightly worse width than PP_FI_TM. + +// Get a nice tree decomposition for a cfg. +template +void get_nice_tree_decomposition(T_t &tree_dec, const G_t &cfg) +{ + thorup_tree_decomposition(tree_dec, cfg); + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + +#ifdef USE_THORUP + treedec::thorup a2(cfg); +#else + typedef boost::adjacency_list cfg2_t; + cfg2_t cfg2; + copy_undir(cfg2, cfg); +#if USE_PP_FI_TM + treedec::comb::PP_FI_TM a2(cfg2); +#elif USE_EX17 + treedec::comb::ex17 a2(cfg2); +#elif USE_PP_MD + treedec::comb::PP_MD a2(cfg2); +#elif USE_PP_FI + treedec::comb::PP_FI a2(cfg2); +#else +#error No algorithm selected +#endif +#endif + + T_t tree_dec2; + a2.do_it(); + a2.get_tree_decomposition(tree_dec2); + wassert(treedec::is_valid_treedecomposition(cfg, tree_dec2)); + + if (treedec::get_width(tree_dec2) < treedec::get_width(tree_dec)) + std::swap(tree_dec, tree_dec2); +#endif + + nicify(tree_dec); + +#ifdef HAVE_TREEDEC_COMBINATIONS_HPP + wassert(treedec::is_valid_treedecomposition(cfg, tree_dec)); +#endif +} + diff --git a/src/SDCCutil.c b/src/SDCCutil.c new file mode 100644 index 0000000..59bd60c --- /dev/null +++ b/src/SDCCutil.c @@ -0,0 +1,1173 @@ +/*------------------------------------------------------------------------- + SDCCutil.c - Small utility functions. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include +#include + +#ifdef _WIN32 +#include +#undef TRUE +#undef FALSE +#endif +#include +#include "dbuf.h" +#include "SDCCglobl.h" +#include "SDCCmacro.h" +#include "SDCCutil.h" +#include "newalloc.h" +#include "dbuf_string.h" +#ifndef _WIN32 +#include "findme.h" +#endif + +#include "version.h" + +/** Given an array of name, value string pairs creates a new hash + containing all of the pairs. +*/ +hTab * +populateStringHash (const char **pin) +{ + hTab *pret = NULL; + + while (*pin) + { + shash_add (&pret, pin[0], pin[1]); + pin += 2; + } + + return pret; +} + + +/** shell escape string + returns dynamically allocated string, which should be free-ed +*/ +char * +shell_escape (const char *str) +{ +#ifdef _WIN32 + /* see http://www.autohotkey.net/~deleyd/parameters/parameters.htm */ + + struct dbuf_s dbuf; + int backshl = 0; + const char *begin = str; + char backslbuf[16]; + char *backslp; + + dbuf_init (&dbuf, 128); + + /* opening double quotes */ + dbuf_append_char(&dbuf, '"'); + while (*str) + { + if (NULL == begin) + begin = str; + + if ('\\' == *str) + ++backshl; + else + { + if ('"' == *str) + { + /* append the remaining characters */ + if (str > begin) + dbuf_append (&dbuf, begin, str - begin); + + /* append additional beckslash */ + ++backshl; + + /* special handling if last chars before double quote are backslashes */ + backslp = backslbuf; + while (backshl--) + { + *backslp++ = '\\'; + if (sizeof (backslbuf) == backslp - backslbuf) + { + dbuf_append (&dbuf, backslbuf, sizeof (backslbuf)); + backslp = backslbuf; + } + } + if (backslp > backslbuf) + dbuf_append (&dbuf, backslbuf, backslp - backslbuf); + + begin = str; + } + else if ('%' == *str) + { + /* diseble env. variable expansion */ + /* append the remaining characters */ + if (begin && str > begin) + dbuf_append (&dbuf, begin, str - begin); + + /* special handling if last chars before double quote are backslashes */ + backslp = backslbuf; + while (backshl--) + { + *backslp++ = '\\'; + if (sizeof (backslbuf) == backslp - backslbuf) + { + dbuf_append (&dbuf, backslbuf, sizeof (backslbuf)); + backslp = backslbuf; + } + } + if (backslp > backslbuf) + dbuf_append (&dbuf, backslbuf, backslp - backslbuf); + + /* closing doube quotes */ + backslbuf[0] = '"'; + backslbuf[1] = *str; + /* re opening double quotes */ + backslbuf[2] = '"'; + dbuf_append (&dbuf, backslbuf, 3); + begin = NULL; + } + backshl = 0; + } + ++str; + } + /* append the remaining characters */ + if (begin && str > begin) + dbuf_append (&dbuf, begin, str - begin); + + /* special handling if last chars before double quote are backslashes */ + backslp = backslbuf; + while (backshl--) + { + *backslp++ = '\\'; + if (sizeof (backslbuf) == backslp - backslbuf) + { + dbuf_append (&dbuf, backslbuf, sizeof (backslbuf)); + backslp = backslbuf; + } + } + if (backslp > backslbuf) + dbuf_append (&dbuf, backslbuf, backslp - backslbuf); + + /* closing doube quotes */ + dbuf_append_char (&dbuf, '"'); + + return dbuf_detach_c_str (&dbuf); +#else + struct dbuf_s dbuf; + const char *s = str; + const char *begin = str; + + dbuf_init (&dbuf, 128); + + while (*s) + { + switch (*s) + { + case ' ': case '\t': case '\n': /* IFS white space */ + case '\'': case '"': case '\\': /* quoting chars */ + case '|': case '&': case ';': /* shell metacharacters */ + case '(': case ')': case '<': case '>': + case '!': case '{': case '}': /* reserved words */ + case '*': case '[': case '?': case ']': /* globbing chars */ + case '^': + case '$': case '`': /* expansion chars */ + case ',': /* brace expansion */ + /* flush */ + if (s > begin) + dbuf_append (&dbuf, begin, s - begin); + + dbuf_append_char (&dbuf, '\\'); + begin = s; + break; + + case '~': /* tilde expansion */ + if (s == str || s[-1] == '=' || s[-1] == ':') + { + /* flush */ + if (s > begin) + dbuf_append (&dbuf, begin, s - begin); + + dbuf_append_char (&dbuf, '\\'); + begin = s; + } + break; + + case '#': /* comment char */ + { + /* flush */ + if (s > begin) + dbuf_append (&dbuf, begin, s - begin); + + dbuf_append_char (&dbuf, '\\'); + begin = s; + } + /* FALLTHROUGH */ + default: + break; + } + ++s; + } + /* flush */ + if (s > begin) + dbuf_append (&dbuf, begin, s - begin); + + return dbuf_detach_c_str (&dbuf); +#endif +} + +/** Prints elements of the set to the file, each element on new line +*/ +void +fputStrSet (FILE * fp, set * list) +{ + const char *s; + + for (s = setFirstItem (list); s != NULL; s = setNextItem (list)) + { + fputs (s, fp); + fputc ('\n', fp); + } +} + +/** Prepend / append / process given strings to each item of string set. + The result is in a new string set. +*/ +set * +processStrSet (set * list, const char *pre, const char *post, char *(*func)(const char *)) +{ + set *new_list = NULL; + const char *item; + struct dbuf_s dbuf; + + for (item = setFirstItem (list); item != NULL; item = setNextItem (list)) + { + dbuf_init (&dbuf, PATH_MAX); + + if (pre) + dbuf_append_str (&dbuf, pre); + + if (func) + { + char *s = func (item); + + dbuf_append_str (&dbuf, s); + Safe_free (s); + } + else + dbuf_append_str (&dbuf, item); + + if (post) + dbuf_append_str (&dbuf, post); + + addSet (&new_list, dbuf_detach_c_str (&dbuf)); + } + + return new_list; +} + +/** Given a set returns a string containing all of the strings seperated + by spaces. The returned string is on the heap. +*/ +const char * +joinStrSet (set * list) +{ + const char *s; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + + for (s = setFirstItem (list); s != NULL; s = setNextItem (list)) + { + dbuf_append_str (&dbuf, s); + dbuf_append_char (&dbuf, ' '); + } + + return dbuf_detach_c_str (&dbuf); +} + +/** Split the path string to the directory and file name (including extension) components. + The directory component doesn't contain trailing directory separator. + Returns true if the path contains the directory separator. */ +int +dbuf_splitPath (const char *path, struct dbuf_s *dir, struct dbuf_s *file) +{ + const char *p; + int ret; + const char *end = &path[strlen (path)]; + + for (p = end - 1; p >= path && !IS_DIR_SEPARATOR (*p); --p) + ; + + ret = p >= path; + + if (NULL != dir) + { + int len = p - path; + + if (0 < len) + dbuf_append (dir, path, len); + } + + if (NULL != file) + { + int len; + + ++p; + len = end - p; + + if (0 < len) + dbuf_append (file, p, len); + } + + return ret; +} + +/** Split the path string to the file name (including directory) and file extension components. + File extension component contains the extension separator. + Returns true if the path contains the extension separator. */ +int +dbuf_splitFile (const char *path, struct dbuf_s *file, struct dbuf_s *ext) +{ + const char *p; + const char *end = &path[strlen (path)]; + + for (p = end - 1; p >= path && !IS_DIR_SEPARATOR (*p) && '.' != *p; --p) + ; + + if (p < path || '.' != *p) + { + dbuf_append_str (file, path); + + return 0; + } + else + { + if (NULL != file) + { + int len = p - path; + + if (0 < len) + dbuf_append (file, path, len); + } + + if (NULL != ext) + { + int len = end - p; + + if (0 < len) + dbuf_append (ext, p, len); + } + + return 1; + } +} + +/** Combine directory and the file name to a path string using the DIR_SEPARATOR_CHAR. + */ +void +dbuf_makePath (struct dbuf_s *path, const char *dir, const char *file) +{ + if (dir != NULL) + dbuf_append_str (path, dir); + + dbuf_append_char (path, DIR_SEPARATOR_CHAR); + + if (file != NULL) + dbuf_append_str (path, file); +} + +/** Given a file with path information in the binary files directory, + returns the directory component. Used for discovery of bin + directory of SDCC installation. Returns NULL if the path is + impossible. +*/ +#ifdef _WIN32 +const char * +getBinPath (const char *prel) +{ + struct dbuf_s path; + const char *p; + + dbuf_init (&path, 128); + dbuf_splitPath (prel, &path, NULL); + + p = dbuf_c_str (&path); + if ('\0' != *p) + return p; + else + { + char module[PATH_MAX]; + + dbuf_destroy (&path); + + /* not enough info in prel; do it with module name */ + if (0 != GetModuleFileName (NULL, module, sizeof (module))) + { + dbuf_init (&path, 128); + + dbuf_splitPath (module, &path, NULL); + return dbuf_detach_c_str (&path); + } + else + return NULL; + } +} +#else +const char * +getBinPath (const char *prel) +{ + const char *ret_path; + + if (NULL != (ret_path = findProgramPath (prel))) + { + struct dbuf_s path; + + dbuf_init (&path, 128); + + dbuf_splitPath (ret_path, &path, NULL); + free ((void *) ret_path); + return dbuf_detach_c_str (&path); + } + else + return NULL; +} +#endif + +/** Returns true if the given path exists. + */ +bool +pathExists (const char *ppath) +{ + struct stat s; + + return stat (ppath, &s) == 0; +} + +static hTab *_mainValues; + +void +setMainValue (const char *pname, const char *pvalue) +{ + assert (pname); + + shash_add (&_mainValues, pname, pvalue); +} + +char * +buildMacros (const char *cmd) +{ + return eval_macros (_mainValues, cmd); +} + +char * +buildCmdLine (const char **cmds, const char *p1, const char *p2, const char *p3, set * list) +{ + int first = 1; + struct dbuf_s dbuf; + + assert (cmds != NULL); + + dbuf_init (&dbuf, 256); + + while (*cmds) + { + const char *p, *from, *par; + int sep = 1; + + from = *cmds; + cmds++; + + /* See if it has a '$' anywhere - if not, just copy */ + if ((p = strchr (from, '$'))) + { + /* include first part of cmd */ + if (p != from) + { + if (!first && sep) + dbuf_append_char (&dbuf, ' '); + dbuf_append (&dbuf, from, p - from); + sep = 0; + } + from = p + 2; + + /* include parameter */ + p++; + switch (*p) + { + case '1': + par = p1; + break; + + case '2': + par = p2; + break; + + case '3': + par = p3; + break; + + case 'l': + { + const char *tmp; + par = NULL; + + if (list != NULL && (tmp = (const char *) setFirstItem (list)) != NULL) + { + do + { + if (*tmp != '\0') + { + if (sep) + dbuf_append_char (&dbuf, ' '); /* seperate it */ + dbuf_append_str (&dbuf, tmp); + tmp++; + sep = 1; + } + } + while ((tmp = (const char *) setNextItem (list)) != NULL); + } + } + break; + + default: + par = NULL; + assert (0); + } + + if (par && *par != '\0') + { + if (!first && sep) + dbuf_append_char (&dbuf, ' '); /* seperate it */ + dbuf_append_str (&dbuf, par); + sep = 0; + } + } + + /* include the rest of cmd, e.g. ".asm" from "$1.asm" */ + if (*from != '\0') + { + if (!first && sep) + dbuf_append_char (&dbuf, ' '); /* seperate it */ + dbuf_append_str (&dbuf, from); + sep = 0; + } + + first = 0; + } + + return dbuf_detach_c_str (&dbuf); +} + +char * +buildCmdLine2 (const char *pcmd, ...) +{ + va_list ap; + char *poutcmd; + + assert (pcmd); + assert (_mainValues); + + va_start (ap, pcmd); + + poutcmd = mvsprintf (_mainValues, pcmd, ap); + + va_end (ap); + + return poutcmd; +} + +void +populateMainValues (const char **ppin) +{ + _mainValues = populateStringHash (ppin); +} + +/** Returns true if sz starts with the string given in key. + */ +bool +startsWith (const char *sz, const char *key) +{ + return !strncmp (sz, key, strlen (key)); +} + +/** Removes any newline characters from the string. Not strictly the + same as perl's chomp. +*/ +void +chomp (char *sz) +{ + char *nl; + while ((nl = strrchr (sz, '\n'))) + *nl = '\0'; +} + +hTab * +getRuntimeVariables (void) +{ + return _mainValues; +} + +/* strncpy() with guaranteed NULL termination. */ +char * +strncpyz (char *dest, const char *src, size_t n) +{ + assert (n > 0); + + --n; + /* paranoia... */ + if (strlen (src) > n) + { + fprintf (stderr, "strncpyz prevented buffer overrun!\n"); + } + + strncpy (dest, src, n); + dest[n] = 0; + return dest; +} + +/* like strncat() with guaranteed NULL termination + * The passed size should be the size of the dest buffer, not the number of + * bytes to copy. + */ +char * +strncatz (char *dest, const char *src, size_t n) +{ + size_t maxToCopy; + size_t destLen = strlen (dest); + + assert (n > 0); + assert (n > destLen); + + maxToCopy = n - destLen - 1; + + /* paranoia... */ + if (strlen (src) + destLen >= n) + { + fprintf (stderr, "strncatz prevented buffer overrun!\n"); + } + + strncat (dest, src, maxToCopy); + dest[n - 1] = 0; + return dest; +} + +/*-----------------------------------------------------------------*/ +/* getBuildNumber - return build number */ +/*-----------------------------------------------------------------*/ +const char * +getBuildNumber (void) +{ + return (SDCC_BUILD_NUMBER); +} + +/*-----------------------------------------------------------------*/ +/* getBuildEnvironment - return environment used to build SDCC */ +/*-----------------------------------------------------------------*/ +const char * +getBuildEnvironment (void) +{ +#ifdef __CYGWIN__ + return "CYGWIN"; +#elif defined __MINGW64__ + return "MINGW64"; +#elif defined __MINGW32__ + return "MINGW32"; +#elif defined __DJGPP__ + return "DJGPP"; +#elif defined(_MSC_VER) + return "MSVC"; +#elif defined(__BORLANDC__) + return "BORLANDC"; +#elif defined(__APPLE__) +#if defined(__i386__) + return "Mac OS X i386"; +#elif defined(__x86_64__) + return "Mac OS X x86_64"; +#else + return "Mac OS X ppc"; +#endif +#elif defined(__linux__) + return "Linux"; +#elif defined(__NetBSD__) + return "NetBSD"; +#elif defined(__FreeBSD__) + return "FreeBSD"; +#elif defined(__OpenBSD__) + return "OpenBSD"; +#elif defined(__sun) +#if defined(__i386) + return "Solaris i386"; +#elif defined(__amd64) + return "Solaris amd64"; +#else + return "Solaris SPARC"; +#endif +#else + return "UNIX"; +#endif +} + +size_t +SDCCsnprintf (char *dst, size_t n, const char *fmt, ...) +{ + va_list args; + int len; + + va_start (args, fmt); + + len = vsnprintf (dst, n, fmt, args); + + va_end (args); + + /* on some gnu systems, vsnprintf returns -1 if output is truncated. + * In the C99 spec, vsnprintf returns the number of characters that + * would have been written, were space available. + */ + if ((len < 0) || (size_t) len >= n) + { + fprintf (stderr, "internal error: sprintf truncated.\n"); + } + + return len; +} + +/** Pragma tokenizer + */ +void +init_pragma_token (struct pragma_token_s *token) +{ + dbuf_init (&token->dbuf, 16); + token->type = TOKEN_UNKNOWN; +} + +char * +get_pragma_token (const char *s, struct pragma_token_s *token) +{ + dbuf_set_length (&token->dbuf, 0); + + /* skip leading spaces */ + while ('\n' != *s && isspace (*s)) + ++s; + + if ('\0' == *s || '\n' == *s) + { + token->type = TOKEN_EOL; + } + else + { + char *end; + + long val = strtol (s, &end, 0); + + if (end != s && ('\0' == *end || isspace (*end))) + { + token->val.int_val = val; + token->type = TOKEN_INT; + dbuf_append (&token->dbuf, s, end - s); + s = end; + } + else + { + while ('\0' != *s && !isspace (*s)) + { + dbuf_append_char (&token->dbuf, *s); + ++s; + } + + token->type = TOKEN_STR; + } + } + + return (char *) s; +} + +const char * +get_pragma_string (struct pragma_token_s *token) +{ + return dbuf_c_str (&token->dbuf); +} + +void +free_pragma_token (struct pragma_token_s *token) +{ + dbuf_destroy (&token->dbuf); +} + +/*! /fn char hexEscape(char **src) + + /param src Pointer to 'x' from start of hex character value +*/ + +unsigned long int +hexEscape (const char **src) +{ + char *s; + unsigned long value; + + ++*src; /* Skip over the 'x' */ + + value = strtoul (*src, &s, 16); + + if (s == *src) + { + /* no valid hex found */ + werror (E_INVALID_HEX); + } + + *src = s; + + return value; +} + +/*------------------------------------------------------------------*/ +/* universalEscape - process an hex constant of exactly four digits */ +/* return the hex value, throw a warning for illegal octal */ +/* adjust src to point at the last proccesed char */ +/*------------------------------------------------------------------*/ + +unsigned long int +universalEscape (const char **str, unsigned int n) +{ + unsigned int digits; + unsigned long value = 0; + const char *s = *str; + + if (!options.std_c95) + { + werror (W_UNIVERSAL_C95); + } + + ++*str; /* Skip over the 'u' or 'U' */ + + for (digits = 0; digits < n; ++digits) + { + if (**str >= '0' && **str <= '7') + { + value = (value << 4) + (**str - '0'); + ++*str; + } + else if ((**str | 0x20) >= 'a' && (**str | 0x20) <= 'f') + { + value = (value << 4) + (**str - 'a' + 10); + ++*str; + } + else + break; + } + if (digits != n || value < 0x00a0 && value != 0x0024 && value != 0x0040 && value != 0x0060 || value >= 0xd800 && 0xdfff >= value) + { + werror (E_INVALID_UNIVERSAL, s); + } + + return value; +} + +/*------------------------------------------------------------------*/ +/* octalEscape - process an octal constant of max three digits */ +/* return the octal value, throw a warning for illegal octal */ +/* adjust src to point at the last proccesed char */ +/*------------------------------------------------------------------*/ + +unsigned long int +octalEscape (const char **str) +{ + int digits; + unsigned value = 0; + + for (digits = 0; digits < 3; ++digits) + { + if (**str >= '0' && **str <= '7') + { + value = value * 8 + (**str - '0'); + ++*str; + } + else + { + break; + } + } + + return value; +} + +/*! + /fn const char *copyStr (const char *src, size_t *size) + + Copies source string to a dynamically allocated buffer interpreting + escape sequences and special characters + + /param src Buffer containing the source string with escape sequecnes + /param size Pointer to loction where the resulting buffer length is written + /return Dynamically allocated resulting buffer +*/ + +const char * +copyStr (const char *src, size_t *size) +{ + const char *begin = NULL; + struct dbuf_s dbuf; + + dbuf_init(&dbuf, 128); + + while (*src) + { + if (*src == '\"') + { + if (begin) + { + /* copy what we have until now */ + dbuf_append (&dbuf, begin, src - begin); + begin = NULL; + } + ++src; + } + else if (*src == '\\') + { + unsigned long int c; + bool universal = FALSE; + + if (begin) + { + /* copy what we have until now */ + dbuf_append (&dbuf, begin, src - begin); + begin = NULL; + } + ++src; + switch (*src) + { + case 'n': + c = '\n'; + break; + + case 't': + c = '\t'; + break; + + case 'v': + c = '\v'; + break; + + case 'b': + c = '\b'; + break; + + case 'r': + c = '\r'; + break; + + case 'f': + c = '\f'; + break; + + case 'a': + c = '\a'; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = octalEscape (&src); + --src; + break; + + case 'x': + c = hexEscape (&src); + --src; + break; + + case 'u': + c = universalEscape (&src, 4); + universal = TRUE; + --src; + break; + + case 'U': + c = universalEscape (&src, 8); + universal = TRUE; + --src; + break; + + case '\\': + case '\?': + case '\'': + case '\"': + default: + c = *src; + break; + } + if (universal) // Encode one utf-32 character to utf-8 + { + char s[5] = "\0\0\0\0"; + if (c < 0x80) + s[0] = (char)c; + else if (c < 0x800) + { + s[0] = (c >> 6) & 0x1f | 0xc0; + s[1] = (c >> 0) & 0x3f | 0x80; + } + else if (c < 0x10000) + { + s[0] = (c >> 12) & 0x0f | 0xe0; + s[1] = (c >> 6) & 0x3f | 0x80; + s[2] = (c >> 0) & 0x3f | 0x80; + } + else if (c < 0x110000) + { + s[0] = (c >> 18) & 0x07 | 0xf0; + s[1] = (c >> 12) & 0x3f | 0x80; + s[2] = (c >> 6) & 0x3f | 0x80; + s[3] = (c >> 0) & 0x3f | 0x80; + } + else + wassert (0); + dbuf_append_str (&dbuf, s); + } + else + dbuf_append_char (&dbuf, (char)c); + + ++src; + } + else + { + if (!begin) + begin = src; + ++src; + } + } + + if (begin) + { + /* copy what we have until now */ + dbuf_append (&dbuf, begin, src - begin); + begin = NULL; + } + + if (size) + { + /* include null terminator character + appended by dbuf_detach_c_str() */ + *size = dbuf_get_length (&dbuf) + 1; + } + + return dbuf_detach_c_str (&dbuf); +} + +static char prefix[256] = ""; +static char suffix[256] = ""; +static char cmd[4096] = ""; + +void getPrefixSuffix(const char *arg) +{ + const char *p; + const char sdcc[] = "sdcc"; + + if (!arg) + return; + + p = arg + strlen (arg); + while (p != arg && *(p - 1) != '\\' && *(p - 1) != '/') + p--; + arg = p; + + /* found no "sdcc" in command line argv[0] */ + if ((p = strstr (arg, sdcc)) == NULL) + return; + + /* found more than one "sdcc" in command line argv[0] */ + if (strstr (p + strlen (sdcc), sdcc) != NULL) + return; + + /* copy prefix and suffix */ + strncpy (prefix, arg, p - arg); + strcpy (suffix, p + strlen (sdcc)); +} + +char *setPrefixSuffix(const char *arg) +{ + const char *p; + + if (!arg) + return NULL; + else + memset (cmd, 0x00, sizeof (cmd)); + + /* find the core name of command line */ + for (p = arg; (*p) && isblank (*p); p++); + arg = p; + assert (strstr (arg, ".exe") == NULL); + for (p = arg; (*p) && !isblank (*p); p++); + + /* compose new command line with prefix and suffix */ + strcpy (cmd, prefix); + strncat (cmd, arg, p - arg); + strcat (cmd, suffix); + strcat (cmd, p); + + return cmd; +} + +char *formatInlineAsm (char *asmStr) +{ + char *p, *q; + + if (!asmStr) + return NULL; + else + q = asmStr; + + for (;;) + { + // omit leading space or tab + while (*q == '\t' || *q == ' ') + q++; + // then record the head of current line + p = q; + // search for CL or reach the end + while (*q != '\n' && *q != '\r' && *q != 0) + q++; + // omit more CL characters + while (*q == '\n' || *q == '\r') + q++; + // replace the first with tab + while (p != q) + if (*p == '\t') // '\t' appears first then no need to do + { + break; + } + else if (*p == ' ') // find the first space then replace it with tab + { + *p = '\t'; + break; + } + else // go on to search + { + p++; + } + // check if end + if (*q == 0) + return asmStr; + } +} diff --git a/src/SDCCutil.h b/src/SDCCutil.h new file mode 100644 index 0000000..1e06675 --- /dev/null +++ b/src/SDCCutil.h @@ -0,0 +1,157 @@ +/*------------------------------------------------------------------------- + SDCCutil.c - Small utility functions. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCUTIL_H +#define SDCCUTIL_H + +#include "SDCChasht.h" +#include "dbuf.h" +#include + +/** Given an array of name, value string pairs creates a new hash + * containing all of the pairs. + */ +hTab *populateStringHash (const char **pin); + +/** Given an array of name, value string pairs creates a new hash + * containing all of the pairs. + */ +char *shell_escape (const char *str); + +/** Prints elements of the set to the file, each element on new line + */ +void fputStrSet (FILE * fp, set * list); + +/** Prepend / append given strings to each item of string set. The result is in a + * new string set. + */ +set *processStrSet (set * list, const char *pre, const char *post, char *(*file) (const char *)); + +/** Given a set returns a string containing all of the strings seperated + * by spaces. The returned string is on the heap. + */ +const char *joinStrSet (set * list); + +/** Split the path string to the directory and file name (including extension) components. + * The directory component doesn't contain trailing directory separator. + * Returns true if the path contains the directory separator. + */ +int dbuf_splitPath (const char *path, struct dbuf_s *dir, struct dbuf_s *file); + +/** Split the path string to the file name (including directory) and file extension components. + * The file name component doesn't contain trailing extension separator. + * Returns true if the path contains the extension separator. + */ +int dbuf_splitFile (const char *path, struct dbuf_s *file, struct dbuf_s *ext); + +/** Combile directory and the file name to a path string using the DIR_SEPARATOR_CHAR. + */ +void dbuf_makePath (struct dbuf_s *path, const char *dir, const char *file); + +/** Given a file with path information in the binary files directory, + * returns the directory component. Used for discovery of bin + * directory of SDCC installation. Returns NULL if the path is + * impossible. + */ +const char *getBinPath (const char *prel); + +/** Returns true if the given path exists. + */ +bool pathExists (const char *ppath); + +void setMainValue (const char *pname, const char *pvalue); + +char *buildMacros (const char *cmd); + +void populateMainValues (const char **ppin); + +char *buildCmdLine (const char **cmds, const char *p1, const char *p2, const char *p3, set *list); + +char *buildCmdLine2 (const char *pcmd, ...); + +/** Returns true if sz starts with the string given in key. + */ +bool startsWith (const char *sz, const char *key); + +/** Removes any newline characters from the string. + * Not strictly the same as perl's chomp. + */ +void chomp (char *sz); + +hTab *getRuntimeVariables (void); + +/* strncpy() with guaranteed NULL termination. + */ +char *strncpyz (char *dest, const char *src, size_t n); + +/* like strncat() with guaranteed NULL termination + * The passed size should be the size of the dest buffer, not the number of + * bytes to copy. + */ +char *strncatz (char *dest, const char *src, size_t n); + +/* return SDCC build number + */ +const char *getBuildNumber (void); + +/* return environment used to build SDCC + */ +const char *getBuildEnvironment (void); + +/** snprintf, by hook or by crook. + */ +size_t SDCCsnprintf (char *, size_t, const char *, ...); + +#define SNPRINTF SDCCsnprintf + +/** Pragma tokenizer + */ +enum pragma_token_e +{ TOKEN_UNKNOWN, TOKEN_STR, TOKEN_INT, TOKEN_EOL }; + +struct pragma_token_s +{ + enum pragma_token_e type; + struct dbuf_s dbuf; + union + { + int int_val; + } val; +}; + +void init_pragma_token (struct pragma_token_s *token); +char *get_pragma_token (const char *s, struct pragma_token_s *token); +const char *get_pragma_string (struct pragma_token_s *token); +void free_pragma_token (struct pragma_token_s *token); + +unsigned long int hexEscape (const char **src); +unsigned long int universalEscape (const char **src, unsigned int n); +unsigned long int octalEscape (const char **src); +const char *copyStr (const char *src, size_t *size); + +void getPrefixSuffix(const char *); +char *setPrefixSuffix(const char *); + +char *formatInlineAsm (char *); +#endif diff --git a/src/SDCCval.c b/src/SDCCval.c new file mode 100644 index 0000000..fc2016d --- /dev/null +++ b/src/SDCCval.c @@ -0,0 +1,3295 @@ +/*---------------------------------------------------------------------- + SDCCval.c :- has routine to do all kinds of fun stuff with the + value wrapper & with initialiser lists. + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include +#include +#include +#include +#include "newalloc.h" +#include "dbuf_string.h" + +long cNestLevel; + + +/*-----------------------------------------------------------------*/ +/* newValue - allocates and returns a new value */ +/*-----------------------------------------------------------------*/ +value * +newValue (void) +{ + value *val; + + val = Safe_alloc (sizeof (value)); + + return val; +} + +/*-----------------------------------------------------------------*/ +/* newiList - new initializer list */ +/*-----------------------------------------------------------------*/ +initList * +newiList (int type, void *ilist) +{ + initList *nilist; + + nilist = Safe_alloc (sizeof (initList)); + + nilist->type = type; + nilist->filename = lexFilename; + nilist->lineno = lexLineno; + nilist->designation = NULL; + + switch (type) + { + case INIT_NODE: + nilist->init.node = (struct ast *) ilist; + break; + + case INIT_DEEP: + nilist->init.deep = (struct initList *) ilist; + break; + } + + return nilist; +} + +/*------------------------------------------------------------------*/ +/* revinit - reverses the initial values for a value chain */ +/*------------------------------------------------------------------*/ +initList * +revinit (initList * val) +{ + initList *prev, *curr, *next; + + if (!val) + return NULL; + + prev = val; + curr = val->next; + + while (curr) + { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + val->next = (void *) NULL; + return prev; +} + +bool +convertIListToConstList (initList * src, literalList ** lList, int size) +{ + int cnt = 0; + initList *iLoop; + literalList *head, *last, *newL; + + head = last = NULL; + + if (src && src->type != INIT_DEEP) + { + return FALSE; + } + + iLoop = src ? src->init.deep : NULL; + + while (iLoop) + { + if (iLoop->designation != NULL) + { + return FALSE; + } + + if (iLoop->type != INIT_NODE) + { + return FALSE; + } + + if (!IS_AST_LIT_VALUE (decorateType (resolveSymbols (iLoop->init.node), RESULT_TYPE_NONE))) + { + return FALSE; + } + iLoop = iLoop->next; + cnt++; + } + if (!size) + { + size = cnt; + } + + /* We've now established that the initializer list contains only literal values. */ + + iLoop = src ? src->init.deep : NULL; + while (size--) + { + double val = iLoop ? AST_FLOAT_VALUE (iLoop->init.node) : 0; + + if (last && last->literalValue == val) + { + last->count++; + } + else + { + newL = Safe_alloc (sizeof (literalList)); + newL->literalValue = val; + newL->count = 1; + newL->next = NULL; + + if (last) + { + last->next = newL; + } + else + { + head = newL; + } + last = newL; + } + iLoop = iLoop ? iLoop->next : NULL; + } + + if (!head) + { + return FALSE; + } + + *lList = head; + return TRUE; +} + +literalList * +copyLiteralList (literalList * src) +{ + literalList *head, *prev, *newL; + + head = prev = NULL; + + while (src) + { + newL = Safe_alloc (sizeof (literalList)); + + newL->literalValue = src->literalValue; + newL->count = src->count; + newL->next = NULL; + + if (prev) + { + prev->next = newL; + } + else + { + head = newL; + } + prev = newL; + src = src->next; + } + + return head; +} + +/*------------------------------------------------------------------*/ +/* copyIlist - copy initializer list */ +/*------------------------------------------------------------------*/ +initList * +copyIlist (initList * src) +{ + initList *dest = NULL; + + if (!src) + return NULL; + + switch (src->type) + { + case INIT_DEEP: + dest = newiList (INIT_DEEP, copyIlist (src->init.deep)); + break; + case INIT_NODE: + dest = newiList (INIT_NODE, copyAst (src->init.node)); + break; + } + + if (src->designation) + dest->designation = copyDesignation (src->designation); + + if (src->next) + assert (dest != NULL); + dest->next = copyIlist (src->next); + + return dest; +} + +/*------------------------------------------------------------------*/ +/* list2int - converts the first element of the list to value */ +/*------------------------------------------------------------------*/ +double +list2int (initList * val) +{ + initList *i = val; + + assert (i->type != INIT_HOLE); + + if (i->type == INIT_DEEP) + return list2int (val->init.deep); + + return floatFromVal (constExprValue (val->init.node, TRUE)); +} + +/*------------------------------------------------------------------*/ +/* list2val - converts the first element of the list to value */ +/*------------------------------------------------------------------*/ +value * +list2val (initList * val, int check) +{ + if (!val) + return NULL; + + if(val->type == INIT_HOLE) + return NULL; + + if (val->type == INIT_DEEP) + return list2val (val->init.deep, check); + + if (val->type == INIT_NODE && val->init.node->opval.op == CAST) + return constExprValue (val->init.node->right, check); + + return constExprValue (val->init.node, check); +} + +/*------------------------------------------------------------------*/ +/* list2expr - returns the first expression in the initializer list */ +/*------------------------------------------------------------------*/ +ast * +list2expr (initList * ilist) +{ + if (!ilist) + return NULL; + + assert (ilist->type != INIT_HOLE); + + if (ilist->type == INIT_DEEP) + return list2expr (ilist->init.deep); + return ilist->init.node; +} + +/*------------------------------------------------------------------*/ +/* resolveIvalSym - resolve symbols in initial values */ +/*------------------------------------------------------------------*/ +void +resolveIvalSym (initList *ilist, sym_link *type) +{ + int is_ptr = IS_PTR (type) || (IS_ARRAY(type) && IS_PTR(type->next)); + RESULT_TYPE resultType = getResultTypeFromType (getSpec (type)); + + while (ilist) + { + if (ilist->type == INIT_NODE) + { + ilist->init.node = decorateType (resolveSymbols (ilist->init.node), is_ptr ? RESULT_TYPE_INT : resultType); + } + else if (ilist->type == INIT_DEEP) + { + resolveIvalSym (ilist->init.deep, type); + } + + ilist = ilist->next; + } +} + +/*-----------------------------------------------------------------*/ +/* newDesignation - new designation */ +/*-----------------------------------------------------------------*/ +designation * +newDesignation (int type, void *designator) +{ + designation *ndesignation; + + ndesignation = Safe_alloc (sizeof (designation)); + + ndesignation->type = type; + ndesignation->filename = lexFilename; + ndesignation->lineno = lexLineno; + + switch (type) + { + case DESIGNATOR_STRUCT: + ndesignation->designator.tag = (struct symbol *) designator; + break; + + case DESIGNATOR_ARRAY: + ndesignation->designator.elemno = * ((int *) designator); + break; + } + + return ndesignation; +} + +/*------------------------------------------------------------------*/ +/* revDesignation - reverses the designation chain */ +/*------------------------------------------------------------------*/ +designation * +revDesignation (designation * val) +{ + designation *prev, *curr, *next; + + if (!val) + return NULL; + + prev = val; + curr = val->next; + + while (curr) + { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + val->next = (void *) NULL; + return prev; +} + +/*------------------------------------------------------------------*/ +/* copyDesignation - copy designation list */ +/*------------------------------------------------------------------*/ +designation * +copyDesignation (designation * src) +{ + designation *dest = NULL; + + if (!src) + return NULL; + + switch (src->type) + { + case DESIGNATOR_STRUCT: + dest = newDesignation (DESIGNATOR_STRUCT, copySymbol (src->designator.tag)); + break; + case DESIGNATOR_ARRAY: + dest = newDesignation (DESIGNATOR_ARRAY, &(src->designator.elemno) ); + break; + } + + dest->lineno = src->lineno; + dest->filename = src->filename; + + if (src->next) + dest->next = copyDesignation (src->next); + + return dest; +} + +/*------------------------------------------------------------------*/ +/* moveNestedInit - rewrites an initList node with a nested */ +/* designator to remove one level of nesting. */ +/*------------------------------------------------------------------*/ +static +void moveNestedInit(initList *deepParent, initList *src) +{ + initList *dst = NULL, **eol; + + /** Create new initList element */ + switch (src->type) + { + case INIT_NODE: + dst = newiList(INIT_NODE, src->init.node); + break; + case INIT_DEEP: + dst = newiList(INIT_DEEP, src->init.deep); + break; + } + dst->filename = src->filename; + dst->lineno = src->lineno; + dst->designation = src->designation->next; + + /* add dst to end of deepParent */ + if (deepParent->type != INIT_DEEP) + { + werrorfl (deepParent->filename, deepParent->lineno, + E_INIT_STRUCT, ""); + return; + } + for (eol = &(deepParent->init.deep); *eol ; ) + eol = &((*eol)->next); + *eol = dst; +} + +/*-----------------------------------------------------------------*/ +/* findStructField - find a specific field in a struct definition */ +/*-----------------------------------------------------------------*/ +static int +findStructField (symbol *fields, symbol *target) +{ + int i; + + for (i=0 ; fields; fields = fields->next) + { + /* skip past unnamed bitfields */ + if (IS_BITFIELD (fields->type) && SPEC_BUNNAMED (fields->etype)) + continue; + /* is this it? */ + if (strcmp(fields->name, target->name) == 0) + return i; + i++; + } + + /* not found */ + werrorfl (target->fileDef, target->lineDef, E_NOT_MEMBER, target->name); + return 0; +} + +/*------------------------------------------------------------------*/ +/* reorderIlist - expands an initializer list to match designated */ +/* initializers. */ +/*------------------------------------------------------------------*/ +initList *reorderIlist (sym_link * type, initList * ilist) +{ + initList *iloop, *nlist, **nlistArray; + symbol *sflds; + int size=0, idx; + + if (!IS_AGGREGATE (type)) + /* uninteresting: no designated initializers */ + return ilist; + + if (ilist && ilist->type == INIT_HOLE) + /* ditto; just a uninitialized hole */ + return ilist; + + /* special case: check for string initializer */ + if (IS_ARRAY (type) && IS_CHAR (type->next) && + ilist && ilist->type == INIT_NODE) + { + ast *iast = ilist->init.node; + value *v = (iast->type == EX_VALUE ? iast->opval.val : NULL); + if (v && IS_ARRAY (v->type) && IS_CHAR (v->etype)) + { + /* yep, it's a string; no changes needed here. */ + return ilist; + } + } + + if (ilist && ilist->type != INIT_DEEP) + { + werrorfl (ilist->filename, ilist->lineno, E_INIT_STRUCT, ""); + return NULL; + } + + /* okay, allocate enough space */ + if (IS_ARRAY (type)) + size = getNelements(type, ilist); + else if (IS_STRUCT (type)) + { + /* compute size from struct type. */ + size = 0; + for (sflds = SPEC_STRUCT (type)->fields; sflds; sflds = sflds->next) + { + /* skip past unnamed bitfields */ + if (IS_BITFIELD (sflds->type) && SPEC_BUNNAMED (sflds->etype)) + continue; + size++; + } + } + nlistArray = Safe_calloc ( size, sizeof(initList *) ); + + /* pull together all the initializers into an ordered list */ + iloop = ilist ? ilist->init.deep : NULL; + for (idx = 0 ; iloop ; iloop = iloop->next, idx++) + { + if (iloop->designation) + { + assert (iloop->type != INIT_HOLE); + + if (IS_ARRAY (type)) + { + if (iloop->designation->type == DESIGNATOR_ARRAY) + idx = iloop->designation->designator.elemno; + else + werrorfl (iloop->filename, iloop->lineno, E_BAD_DESIGNATOR); + } + else if (IS_STRUCT (type)) + { + if (iloop->designation->type == DESIGNATOR_STRUCT) + idx = findStructField (SPEC_STRUCT (type)->fields, + iloop->designation->designator.tag); + else + werrorfl (iloop->filename, iloop->lineno, E_BAD_DESIGNATOR); + } + else + { + assert (0); + } + + if (iloop->designation->next) + { + if (idx >= size) + continue; + if (nlistArray[idx] == NULL) + nlistArray[idx] = newiList(INIT_DEEP, NULL); + moveNestedInit(nlistArray[idx], iloop); + continue; + } + } + + /* overwrite any existing entry with iloop */ + if (iloop->type != INIT_HOLE) + { + if (idx >= size) + continue; + if (nlistArray[idx] != NULL) + werrorfl (iloop->filename, iloop->lineno, W_DUPLICATE_INIT, idx); + nlistArray[idx] = iloop; + } + } + + /* create new list from nlistArray/size */ + nlist = NULL; + sflds = IS_STRUCT (type) ? SPEC_STRUCT (type)->fields : NULL; + for ( idx=0; idx < size; idx++ ) + { + initList *src = nlistArray[idx], *dst = NULL; + if (!src || src->type==INIT_HOLE) + { + dst = newiList(INIT_HOLE, NULL); + dst->filename = ilist->filename; + dst->lineno = ilist->lineno; + } + else + { + switch (src->type) + { + case INIT_NODE: + dst = newiList(INIT_NODE, src->init.node); + break; + case INIT_DEEP: + dst = newiList(INIT_DEEP, src->init.deep); + break; + } + dst->filename = src->filename; + dst->lineno = src->lineno; + } + dst->next = nlist; + nlist = dst; + /* advance to next field which is not an unnamed bitfield */ + do + { + sflds = sflds ? sflds->next : NULL; + } + while (sflds && + IS_BITFIELD (sflds->type) && SPEC_BUNNAMED (sflds->etype)); + } + + nlist = newiList(INIT_DEEP, revinit (nlist)); + nlist->filename = ilist->filename; + nlist->lineno = ilist->lineno; + nlist->designation = ilist->designation; + nlist->next = ilist->next; + return nlist; +} + +/*------------------------------------------------------------------*/ +/* symbolVal - creates a value for a symbol */ +/*------------------------------------------------------------------*/ +value * +symbolVal (symbol * sym) +{ + value *val; + + if (!sym) + return NULL; + + val = newValue (); + val->sym = sym; + + if (sym->type) + { + val->type = sym->type; + val->etype = getSpec (val->type); + } + + if (*sym->rname) + { + SNPRINTF (val->name, sizeof (val->name), "%s", sym->rname); + } + else + { + SNPRINTF (val->name, sizeof (val->name), "_%s", sym->name); + } + + return val; +} + +/*--------------------------------------------------------------------*/ +/* cheapestVal - try to reduce 'signed int' to 'char' */ +/*--------------------------------------------------------------------*/ +static value * +cheapestVal (value * val) +{ + /* only int can be reduced */ + if (!IS_INT (val->type)) + return val; + + /* long must not be changed */ + if (SPEC_LONG (val->type) || SPEC_LONGLONG (val->type)) + return val; + + /* unsigned must not be changed */ + if (SPEC_USIGN (val->type)) + return val; + + /* the only possible reduction is from signed int to (un)signed char, + because it's automatically promoted back to signed int. + + a reduction from unsigned int to unsigned char is a bug, + because an _unsigned_ char is promoted to _signed_ int! */ + if (SPEC_CVAL (val->type).v_int < -128 || SPEC_CVAL (val->type).v_int > 255) + { + /* not in the range of (un)signed char */ + return val; + } + + SPEC_NOUN (val->type) = V_CHAR; + + /* 'unsigned char' promotes to 'signed int', so that we can + reduce it the other way */ + if (SPEC_CVAL (val->type).v_int >= 0) + { + /* 'bool' promotes to 'signed int' too */ + if (SPEC_CVAL (val->type).v_int <= 1) + { + /* Do not use V_BIT here because in some contexts it also */ + /* implies a storage class. */ + SPEC_NOUN (val->type) = V_BOOL; + } + else + { + /* Boolean types are intrinsically unsigned, so only */ + /* set the USIGN flag for char types to avoid triggering */ + /* type checking errors/warnings. */ + SPEC_USIGN (val->type) = 1; + } + } + + return (val); +} + +/*-----------------------------------------------------------------*/ +/* double2ul - double to unsigned long conversion */ +/*-----------------------------------------------------------------*/ +unsigned long +double2ul (double val) +{ +/* + * See ISO/IEC 9899, chapter 6.3.1.4 Real floating and integer: + * If the value of the integral part cannot be represented by the integer type, the behavior is undefined. + * This shows up on Mac OS X i386 platform which useus SSE unit instead of the x87 FPU for floating-point operations + */ +/* + * on Mac OS X ppc (long) 2147483648.0 equals to 2147483647, so we explicitely convert it to 0x80000000 + * on other known platforms (long) 2147483648.0 equals to -2147483648 + */ + return ((val) < 0) ? (((val) < -2147483647.0) ? 0x80000000UL : (unsigned long) -((long) -(val))) : (unsigned long) (val); +} + +/*--------------------------------------------------------------------*/ +/* checkConstantRange - check if constant fits in numeric range of */ +/* var type in comparisons and assignments */ +/*--------------------------------------------------------------------*/ +CCR_RESULT +checkConstantRange (sym_link * var, sym_link * lit, int op, bool exchangeLeftRight) +{ + sym_link *reType; + TYPE_TARGET_LONGLONG litVal; + TYPE_TARGET_ULONGLONG ulitVal; + TYPE_TARGET_ULONGLONG signExtMask; + TYPE_TARGET_ULONGLONG signMask; + bool litValUnsigned; + int varBits; + + litVal = ullFromLit (lit); + ulitVal = (TYPE_TARGET_ULONGLONG) litVal; + litValUnsigned = SPEC_USIGN (lit); + varBits = bitsForType (var); + signMask = 1ull << (varBits-1); + signExtMask = varBits >= sizeof(TYPE_TARGET_ULONGLONG)*8 ? 0 : ~((1ull << varBits)-1); + +#if 0 + printf(" ulitVal = 0x%016lx\n", ulitVal); + printf(" signExtMask = 0x%016lx\n", signExtMask); + printf(" signMask = 0x%016lx\n",signMask); +#endif + + //return CCR_OK; /* EEP - debug for long long */ + /* sanity checks */ + if (IS_FLOAT (var) || IS_FIXED (var)) + return CCR_OK; + if (varBits < 1) + return CCR_ALWAYS_FALSE; + if (varBits > 64) + return CCR_ALWAYS_TRUE; + + /* special: assignment */ + if (op == '=') + { + if (IS_BOOLEAN (var)) + return CCR_OK; + + if (1) // Though the else branch is dead, I still would like to keep it. + //if (getenv ("SDCC_VERY_PEDANTIC")) + { + if (SPEC_USIGN (var)) + { + if ((!litValUnsigned && litVal < 0) || (litVal & signExtMask) != 0) + return CCR_OVL; + return CCR_OK; + } + else + { + if (litValUnsigned) + { + if ((ulitVal & (signExtMask | signMask)) == 0) + return CCR_OK; + } + else + { + if ((litVal & (signExtMask | signMask)) == 0) + return CCR_OK; + if ((litVal & (signExtMask | signMask)) == (signExtMask | signMask)) + return CCR_OK; + } + return CCR_OVL; + } + } + else + { + /* ignore signedness, e.g. allow everything + from -127...+255 for (unsigned) char */ + if ((litVal & signExtMask) == 0) + return CCR_OK; + if ((litVal & (signExtMask | signMask)) == (signExtMask | signMask)) + return CCR_OK; + return CCR_OVL; + } + } + + if (exchangeLeftRight) + switch (op) + { + case EQ_OP: + break; + case NE_OP: + break; + case '>': + op = '<'; + break; + case GE_OP: + op = LE_OP; + break; + case '<': + op = '>'; + break; + case LE_OP: + op = GE_OP; + break; + default: + return CCR_ALWAYS_FALSE; + } + + reType = computeType (var, lit, RESULT_TYPE_NONE, op); + + if (SPEC_USIGN (reType)) + { + /* unsigned operation */ + int reBits = bitsForType (reType); + TYPE_TARGET_ULONGLONG minValP, maxValP, minValM, maxValM; + TYPE_TARGET_ULONGLONG opBitsMask = reBits >= sizeof(opBitsMask)*8 ? ~0ull : ((1ull << reBits)-1); + + if (IS_BOOL (var)) + { + minValP = 0; + maxValP = 1; + minValM = 0; + maxValM = 1; + } + else if (SPEC_USIGN (lit) && SPEC_USIGN (var)) + { + /* both operands are unsigned, this is easy */ + minValP = 0; + maxValP = ~signExtMask; + /* there's only range, just copy it to 2nd set */ + minValM = minValP; + maxValM = maxValP; + } + else if (SPEC_USIGN (var)) + { + /* lit is casted from signed to unsigned, e.g.: + unsigned u; + u == (char) -17 + -> u == 0xffef' + */ + minValP = 0; + maxValP = ~signExtMask; + /* there's only one range, just copy it to 2nd set */ + minValM = minValP; + maxValM = maxValP; + + /* it's an unsigned operation */ + ulitVal &= opBitsMask; + } + else /* SPEC_USIGN (lit) */ + { + /* var is casted from signed to unsigned, e.g.: + signed char c; + c == (unsigned) -17 + -> c == 0xffef' + + The possible values after casting var + split up in two, nonconsecutive ranges: + + minValP = 0; positive range: 0...127 + maxValP = 0x7f; + minValM = 0xff80; negative range: -128...-1 + maxValM = 0xffff; + */ + + /* positive range */ + minValP = 0; + maxValP = ~(signExtMask | signMask); + + /* negative range */ + minValM = signExtMask | signMask; + maxValM = (TYPE_TARGET_ULONGLONG)~0ull; /* -1 */ + /* limit number of bits to size of return type */ + minValM &= opBitsMask; + maxValM &= opBitsMask; + } +#if 0 + printf(" reType = "); + printTypeChain (reType, NULL); + printf(" ulitVal = 0x%016lx\n", ulitVal); + printf(" opBitsMask = 0x%016lx\n", opBitsMask); + printf(" maxValP = 0x%016lx\n", maxValP); + printf(" minValP = 0x%016lx\n", minValP); + printf(" maxValM = 0x%016lx\n", maxValM); + printf(" minValM = 0x%016lx\n", minValM); +#endif + + switch (op) + { + case EQ_OP: /* var == lit */ + if (ulitVal <= maxValP && ulitVal >= minValP) /* 0 */ + return CCR_OK; + if (ulitVal <= maxValM && ulitVal >= minValM) + return CCR_OK; + return CCR_ALWAYS_FALSE; + case NE_OP: /* var != lit */ + if (ulitVal <= maxValP && ulitVal >= minValP) /* 0 */ + return CCR_OK; + if (ulitVal <= maxValM && ulitVal >= minValM) + return CCR_OK; + return CCR_ALWAYS_TRUE; + case '>': /* var > lit */ + if (ulitVal >= maxValM) + return CCR_ALWAYS_FALSE; + if (ulitVal < minValP) /* 0 */ + return CCR_ALWAYS_TRUE; + return CCR_OK; + case GE_OP: /* var >= lit */ + if (ulitVal > maxValM) + return CCR_ALWAYS_FALSE; + if (ulitVal <= minValP) /* 0 */ + return CCR_ALWAYS_TRUE; + return CCR_OK; + case '<': /* var < lit */ + if (ulitVal > maxValM) + return CCR_ALWAYS_TRUE; + if (ulitVal <= minValP) /* 0 */ + return CCR_ALWAYS_FALSE; + return CCR_OK; + case LE_OP: /* var <= lit */ + if (ulitVal >= maxValM) + return CCR_ALWAYS_TRUE; + if (ulitVal < minValP) /* 0 */ + return CCR_ALWAYS_FALSE; + return CCR_OK; + default: + return CCR_ALWAYS_FALSE; + } + } + else + { + /* signed operation */ + TYPE_TARGET_LONGLONG minVal, maxVal; + + if (IS_BOOL (var)) + { + minVal = 0; + maxVal = 1; + } + else if (SPEC_USIGN (var)) + { + /* unsigned var, but signed operation. This happens + when var is promoted to signed int. + Set actual min/max values of var. */ + minVal = 0; + maxVal = ~signExtMask; + } + else + { + /* signed var */ + minVal = signExtMask | signMask; + maxVal = ~(signExtMask | signMask); + } + + switch (op) + { + case EQ_OP: /* var == lit */ + if (litVal > maxVal || litVal < minVal) + return CCR_ALWAYS_FALSE; + return CCR_OK; + case NE_OP: /* var != lit */ + if (litVal > maxVal || litVal < minVal) + return CCR_ALWAYS_TRUE; + return CCR_OK; + case '>': /* var > lit */ + if (litVal >= maxVal) + return CCR_ALWAYS_FALSE; + if (litVal < minVal) + return CCR_ALWAYS_TRUE; + return CCR_OK; + case GE_OP: /* var >= lit */ + if (litVal > maxVal) + return CCR_ALWAYS_FALSE; + if (litVal <= minVal) + return CCR_ALWAYS_TRUE; + return CCR_OK; + case '<': /* var < lit */ + if (litVal > maxVal) + return CCR_ALWAYS_TRUE; + if (litVal <= minVal) + return CCR_ALWAYS_FALSE; + return CCR_OK; + case LE_OP: /* var <= lit */ + if (litVal >= maxVal) + return CCR_ALWAYS_TRUE; + if (litVal < minVal) + return CCR_ALWAYS_FALSE; + return CCR_OK; + default: + return CCR_ALWAYS_FALSE; + } + } +} + +#if 0 +CCR_RESULT +checkConstantRange (sym_link * var, sym_link * lit, int op, bool exchangeLeftRight) +{ + CCR_RESULT result; + + printf ("checkConstantRange:\n"); + printf (" var: "); + printTypeChain (var, NULL); + printf (" lit = 0x%lx: ",ullFromLit (lit)); + printTypeChain (lit, NULL); + printf (" op: '"); + switch (op) + { + case '<': printf ("<"); break; + case '=': printf ("="); break; + case '>': printf (">"); break; + case LE_OP: printf ("<="); break; + case GE_OP: printf (">="); break; + case EQ_OP: printf ("=="); break; + case NE_OP: printf ("!="); break; + default: printf ("%d",op); + } + printf("'\n"); + result = checkConstantRange2 (var, lit, op, exchangeLeftRight); + switch (result) + { + case CCR_ALWAYS_TRUE: printf (" CCR_ALWAYS_TRUE\n"); break; + case CCR_ALWAYS_FALSE: printf (" CCR_ALWAYS_FALSE\n"); break; + case CCR_OK: printf (" CCR_OK\n"); break; + case CCR_OVL: printf (" CCR_OVL\n"); break; + default: printf(" CCR_%d\n",result); + } + return result; +} +#endif + + +/*-----------------------------------------------------------------*/ +/* valueFromLit - creates a value from a literal */ +/*-----------------------------------------------------------------*/ +value * +valueFromLit (double lit) +{ + struct dbuf_s dbuf; + value *ret; + + if ((((TYPE_TARGET_LONG) lit) - lit) == 0) + { + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%d", (TYPE_TARGET_LONG) lit); + ret = constVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + return ret; + } + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%f", lit); + ret = constFloatVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + return ret; +} + +/*-----------------------------------------------------------------*/ +/* constFloatVal - converts a FLOAT constant to value */ +/*-----------------------------------------------------------------*/ +value * +constFloatVal (const char *s) +{ + value *val = newValue (); + double sval; + char *p; + + sval = strtod (s, &p); + if (p == s) + { + werror (E_INVALID_FLOAT_CONST, s); + return constCharVal (0); + } + + val->type = val->etype = newLink (SPECIFIER); + SPEC_NOUN (val->type) = V_FLOAT; + SPEC_SCLS (val->type) = S_LITERAL; + SPEC_CONST (val->type) = 1; + SPEC_CVAL (val->type).v_float = sval; + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constFixed16x16Val - converts a FIXED16X16 constant to value */ +/*-----------------------------------------------------------------*/ +value * +constFixed16x16Val (const char *s) +{ + value *val = newValue (); + double sval; + char *p; + + sval = strtod (s, &p); + if (p == s) + { + werror (E_INVALID_FLOAT_CONST, s); + return constCharVal (0); + } + + val->type = val->etype = newLink (SPECIFIER); + SPEC_NOUN (val->type) = V_FLOAT; + SPEC_SCLS (val->type) = S_LITERAL; + SPEC_CONST (val->type) = 1; + SPEC_CVAL (val->type).v_fixed16x16 = fixed16x16FromDouble (sval); + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constVal - converts a constant into a cheap value type */ +/*-----------------------------------------------------------------*/ +value * +constVal (const char *s) +{ + value *val = constIntVal (s); + + wassert (SPEC_NOUN (val->type) == V_INT); + + if (SPEC_LONGLONG (val->type)) + ; + else if (SPEC_LONG (val->type)) + ; + else if (SPEC_USIGN (val->type)) + { + unsigned int i = SPEC_CVAL (val->type).v_uint; + if (i < 256) + SPEC_NOUN (val->type) = V_CHAR; + } + else + { + int i = SPEC_CVAL (val->type).v_int; + if (i >= 0 && i < 256) + { + SPEC_NOUN (val->type) = V_CHAR; + SPEC_USIGN (val->type) = TRUE; + SPEC_CVAL (val->type).v_uint = i; + } + else if (i >= -128 && i < 128) + { + SPEC_NOUN (val->type) = V_CHAR; + } + } + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constIntVal - converts an integer constant into correct type */ +/* See ISO C11, section 6.4.4.1 for the rules. */ +/*-----------------------------------------------------------------*/ +value * +constIntVal (const char *s) +{ + char *p, *p2; + double dval; + long long int llval; + value *val = newValue (); + bool decimal, u_suffix = FALSE, l_suffix = FALSE, ll_suffix = FALSE; + + val->type = val->etype = newLink (SPECIFIER); + SPEC_SCLS (val->type) = S_LITERAL; + SPEC_CONST (val->type) = 1; + SPEC_USIGN (val->type) = 0; + + errno = 0; + + if (s[0] == '0') + { + if (s[1] == 'b' || s[1] == 'B') + llval = strtoull (s + 2, &p, 2); + else + llval = strtoull (s, &p, 0); + dval = (double)(unsigned long long int) llval; + decimal = FALSE; + } + else + { + dval = strtod (s, &p); + if (dval >= 0.0) + llval = strtoull (s, &p, 0); + else + llval = strtoll (s, &p, 0); + decimal = TRUE; + } + + if (errno) + { + dval = 4294967295.0; + werror (W_INVALID_INT_CONST, s, dval); + } + + // Check suffixes + if ((p2 = strchr (p, 'u')) || (p2 = strchr (p, 'U'))) + { + u_suffix = TRUE; + p2++; + if (strchr (p2, 'u') || strchr (p2, 'U')) + werror (E_INTEGERSUFFIX, p); + } + + if ((p2 = strstr (p, "ll")) || (p2 = strstr (p, "LL"))) + { + ll_suffix = TRUE; + p2 += 2; + if (strchr (p2, 'l') || strchr (p2, 'L')) + werror (E_INTEGERSUFFIX, p); + } + else if ((p2 = strchr (p, 'l')) || (p2 = strchr (p, 'L'))) + { + l_suffix = TRUE; + p2++; + if (strchr (p2, 'l') || strchr (p2, 'L')) + werror (E_INTEGERSUFFIX, p); + } + + SPEC_NOUN (val->type) = V_INT; + + if (u_suffix) // Choose first of unsigned int, unsigned long int, unsigned long long int that fits. + { + SPEC_USIGN (val->type) = 1; + if (ll_suffix || dval > 0xffffffff) + SPEC_LONGLONG (val->type) = 1; + else if(l_suffix || dval > 0xffff) + SPEC_LONG (val->type) = 1; + } + else + { + if (decimal) // Choose first of int, long int, long long int that fits. + { + if (ll_suffix || dval > 0x7fffffff || dval < -0x80000000ll) + { + if (!options.std_c99) // C90 exception: Use unsigned long + { + SPEC_USIGN (val->type) = 1; + SPEC_LONG (val->type) = 1; + } + else + SPEC_LONGLONG (val->type) = 1; + } + else if(l_suffix || dval > 0x7fff || dval < -0x8000l) + SPEC_LONG (val->type) = 1; + } + else // Choose first of int, unsigned int, long int, unsigned long int, long long int, unsigned long long int that fits. + { + if (dval > 0x7fffffffffffffff) + { + SPEC_USIGN (val->type) = 1; + SPEC_LONGLONG (val->type) = 1; + } + else if (ll_suffix || dval > 0xffffffff || dval < -0x80000000ll) + { + SPEC_LONGLONG (val->type) = 1; + } + else if (dval > 0x7fffffff) + { + SPEC_USIGN (val->type) = 1; + SPEC_LONG (val->type) = 1; + } + else if (l_suffix || dval > 0xffff || dval < -0x8000l) + { + SPEC_LONG (val->type) = 1; + } + else if (dval > 0x7fff) + { + SPEC_USIGN (val->type) = 1; + } + } + } + + /* check for out of range */ + if (!SPEC_LONGLONG (val->type)) + { + if (dval < -2147483648.0) + { + dval = -2147483648.0; + werror (W_INVALID_INT_CONST, s, dval); + } + if (dval > 2147483648.0 && !SPEC_USIGN (val->type)) + { + dval = 2147483647.0; + werror (W_INVALID_INT_CONST, s, dval); + } + if (dval > 4294967295.0) + { + dval = 4294967295.0; + werror (W_INVALID_INT_CONST, s, dval); + } + } + + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) llval; + } + else + { + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) llval; + } + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) double2ul (dval); + } + else + { + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) double2ul (dval); + } + } + else + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) double2ul (dval); + } + else + { + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) double2ul (dval); + } + } + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constCharacterVal - converts a character constant to value */ +/*-----------------------------------------------------------------*/ +value * +constCharacterVal (unsigned long v, char type) +{ + value *val = newValue (); /* alloc space for value */ + + val->type = val->etype = newLink (SPECIFIER); /* create the specifier */ + SPEC_SCLS (val->type) = S_LITERAL; + SPEC_CONST (val->type) = 1; + + switch (type) + { + case 0: // character constant + SPEC_NOUN (val->type) = V_INT; + SPEC_USIGN (val->type) = 0; + SPEC_CVAL (val->type).v_int = options.signed_char ? (signed char) v : (unsigned char) v; + break; + case 'L': // wide character constant + if (!options.std_c95) + werror (E_WCHAR_CONST_C95); + SPEC_NOUN (val->type) = V_INT; + SPEC_USIGN (val->type) = 1; + SPEC_LONG (val->etype) = 1; + SPEC_CVAL (val->type).v_ulong = (TYPE_UDWORD) v; + break; + case 'u': // wide character constant + if (!options.std_c11) + werror (E_WCHAR_CONST_C11); + SPEC_NOUN (val->type) = V_INT; + SPEC_USIGN (val->type) = 1; + SPEC_CVAL (val->type).v_uint = (TYPE_UWORD) v; + break; + case 'U': // wide character constant + if (!options.std_c11) + werror (E_WCHAR_CONST_C11); + SPEC_NOUN (val->type) = V_INT; + SPEC_USIGN (val->type) = 1; + SPEC_LONG (val->etype) = 1; + SPEC_CVAL (val->type).v_ulong = (TYPE_UDWORD) v; + break; + default: + wassert (0); + } + + return val; +} + +/*-----------------------------------------------------------------*/ +/* constCharVal - converts a character constant to value */ +/*-----------------------------------------------------------------*/ +value * +constCharVal (unsigned char v) +{ + return constCharacterVal (v, 0); +} + +/*-----------------------------------------------------------------*/ +/* constBoolVal - converts a BOOL constant to value */ +/*-----------------------------------------------------------------*/ +value * +constBoolVal (bool v) +{ + value *val = newValue (); /* alloc space for value */ + + val->type = val->etype = newLink (SPECIFIER); /* create the specifier */ + SPEC_SCLS (val->type) = S_LITERAL; + SPEC_CONST (val->type) = 1; + + SPEC_NOUN (val->type) = (bit) ? V_BIT : V_BOOL; + + SPEC_CVAL (val->type).v_uint = (unsigned int) v; + + return val; +} + +// TODO: Move this function to SDCCutil? +static const TYPE_UDWORD *utf_32_from_utf_8 (size_t *utf_32_len, const char *utf_8, size_t utf_8_len) +{ + size_t allocated = 0; + TYPE_UDWORD *utf_32 = 0; + unsigned char first_byte; + TYPE_UDWORD codepoint; + size_t seqlen; + + for (*utf_32_len = 0; utf_8_len; (*utf_32_len)++) + { + if (allocated == *utf_32_len) + { + utf_32 = realloc (utf_32, sizeof(TYPE_UDWORD) * (*utf_32_len + 16)); + wassert (utf_32); + allocated = *utf_32_len + 16; + } + + first_byte = *utf_8; + seqlen = 1; + if (first_byte & 0x80) + { + while (first_byte & (0x80 >> seqlen)) + seqlen++; + first_byte &= (0xff >> (seqlen + 1)); + } + wassert (seqlen <= 6); // seqlen 5 and 6 are deprecated in current unicode standard, but for now, allow them. + + codepoint = first_byte; + utf_8++; + utf_8_len--; + seqlen--; + + for(; seqlen; seqlen--) + { + codepoint <<= 6; + codepoint |= (*utf_8 & 0x3f); + utf_8++; + utf_8_len--; + } + + utf_32[*utf_32_len] = codepoint; + } + return (utf_32); +} + +// TODO: Move this function to SDCCutil? +static const TYPE_UWORD *utf_16_from_utf_32 (size_t *utf_16_len, const TYPE_UDWORD *utf_32, size_t utf_32_len) +{ + size_t allocated = 0; + TYPE_UWORD *utf_16 = 0; + TYPE_UDWORD codepoint; + + for (*utf_16_len = 0; utf_32_len; utf_32_len--, utf_32++) + { + if (allocated <= *utf_16_len + 2) + { + utf_16 = realloc (utf_16, sizeof(TYPE_UWORD) * (*utf_16_len + 16)); + wassert (utf_16); + allocated = *utf_16_len + 16; + } + + codepoint = *utf_32; + + if (codepoint < 0xd7ff || codepoint >= 0xe000 && codepoint <= 0xffff) // Code in basic multilingual plane. + { + utf_16[(*utf_16_len)++] = codepoint; + continue; + } + + // Code point in supplementary plane. + wassert (codepoint >= 0x100000 && codepoint <= 0x10ffff); + codepoint -= 0x100000; + + utf_16[(*utf_16_len)++] = ((codepoint >> 10) & 0x3ff) + 0xd800; + utf_16[(*utf_16_len)++] = (codepoint & 0x3ff) + 0xdc00; + } + + return (utf_16); +} + +/*------------------------------------------------------------------*/ +/* strVal - converts a string constant to a value */ +/*------------------------------------------------------------------*/ +value * +strVal (const char *s) +{ + value *val; + const char *utf_8; + size_t utf_8_size; + + val = newValue (); + + /* get a declarator */ + val->type = newLink (DECLARATOR); + DCL_TYPE (val->type) = ARRAY; + val->type->next = val->etype = newLink (SPECIFIER); + SPEC_SCLS (val->etype) = S_LITERAL; + SPEC_CONST (val->etype) = 1; + + // Convert input string (mixed UTF-8 and UTF-32) to UTF-8 first (handling all escape sequences, etc). + utf_8 = copyStr (s[0] == '"' ? s : s + 1, &utf_8_size); + + if (s[0] == '"') // UTF-8 string literal (any prefix u8 or L in the source would already have been stripped by earlier stages) + { + SPEC_NOUN (val->etype) = V_CHAR; + SPEC_USIGN (val->etype) = !options.signed_char; + val->etype->select.s.b_implicit_sign = true; + SPEC_CVAL (val->etype).v_char = utf_8; + DCL_ELEM (val->type) = utf_8_size; + } + else + { + size_t utf_32_size; + // Convert to UTF-32 next, since converting UTF-32 to UTF-16 is easier than UTF-8 to UTF-16. + const TYPE_UDWORD *utf_32 = utf_32_from_utf_8 (&utf_32_size, utf_8, utf_8_size); + + dbuf_free (utf_8); + + if (s[0] == 'U' || s[0] == 'L') // UTF-32 string literal + { + SPEC_NOUN (val->etype) = V_INT; + SPEC_USIGN (val->etype) = 1; + SPEC_LONG (val->etype) = 1; + SPEC_CVAL (val->etype).v_char32 = utf_32; + DCL_ELEM (val->type) = utf_32_size; + } + else if (s[0] == 'u') // UTF-16 string literal + { + size_t utf_16_size; + const TYPE_UWORD *utf_16 = utf_16_from_utf_32 (&utf_16_size, utf_32, utf_32_size); + + SPEC_NOUN (val->etype) = V_INT; + SPEC_USIGN (val->etype) = 1; + SPEC_CVAL (val->etype).v_char16 = utf_16; + DCL_ELEM (val->type) = utf_16_size; + } + else + wassert (0); + } + + return (val); +} + +/*------------------------------------------------------------------*/ +/* rawStrVal - converts a string to a value */ +/*------------------------------------------------------------------*/ +value * +rawStrVal (const char *s, size_t size) +{ + struct dbuf_s dbuf; + value *val = newValue (); + + dbuf_init (&dbuf, size); + wassert (dbuf_append (&dbuf, s, size)); + + /* get a declarator */ + val->type = newLink (DECLARATOR); + DCL_TYPE (val->type) = ARRAY; + val->type->next = val->etype = newLink (SPECIFIER); + SPEC_SCLS (val->etype) = S_LITERAL; + SPEC_CONST (val->etype) = 1; + + SPEC_NOUN (val->etype) = V_CHAR; + SPEC_USIGN (val->etype) = !options.signed_char; + val->etype->select.s.b_implicit_sign = true; + SPEC_CVAL (val->etype).v_char = dbuf_detach (&dbuf); + DCL_ELEM (val->type) = size; + + return (val); +} + +/*------------------------------------------------------------------*/ +/* reverseValWithType - reverses value chain with type & etype */ +/*------------------------------------------------------------------*/ +value * +reverseValWithType (value * val) +{ + sym_link *type; + sym_link *etype; + + if (!val) + return NULL; + + /* save the type * etype chains */ + type = val->type; + etype = val->etype; + + /* set the current one 2b null */ + val->type = val->etype = NULL; + val = reverseVal (val); + + /* restore type & etype */ + val->type = type; + val->etype = etype; + + return val; +} + +/*------------------------------------------------------------------*/ +/* reverseVal - reverses the values for a value chain */ +/*------------------------------------------------------------------*/ +value * +reverseVal (value * val) +{ + value *prev, *curr, *next; + + if (!val) + return NULL; + + prev = val; + curr = val->next; + + while (curr) + { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + val->next = (void *) NULL; + return prev; +} + +/*------------------------------------------------------------------*/ +/* copyValueChain - will copy a chain of values */ +/*------------------------------------------------------------------*/ +value * +copyValueChain (value * src) +{ + value *dest; + + if (!src) + return NULL; + + dest = copyValue (src); + dest->next = copyValueChain (src->next); + + return dest; +} + +/*------------------------------------------------------------------*/ +/* copyValue - copies contents of a value to a fresh one */ +/*------------------------------------------------------------------*/ +value * +copyValue (value * src) +{ + value *dest; + + dest = newValue (); + dest->sym = copySymbol (src->sym); + strncpyz (dest->name, src->name, SDCC_NAME_MAX); + dest->type = (src->type ? copyLinkChain (src->type) : NULL); + dest->etype = (src->type ? getSpec (dest->type) : NULL); + + return dest; +} + +/*------------------------------------------------------------------*/ +/* charVal - converts a character constant to a value */ +/*------------------------------------------------------------------*/ +value * +charVal (const char *s) +{ + char type; + + if (*s == 'L' || *s == 'u' || *s == 'U') + type = *s++; + else + type = 0; + + s++; // Get rid of quotation. + + /* if \ then special processing */ + if (*s == '\\') + { + switch (*++s) /* go beyond the backslash */ + { + case 'n': + return constCharacterVal ('\n', type); + case 't': + return constCharacterVal ('\t', type); + case 'v': + return constCharacterVal ('\v', type); + case 'b': + return constCharacterVal ('\b', type); + case 'r': + return constCharacterVal ('\r', type); + case 'f': + return constCharacterVal ('\f', type); + case 'a': + return constCharacterVal ('\a', type); + case '\\': + return constCharacterVal ('\\', type); + case '\?': + return constCharacterVal ('\?', type); + case '\'': + return constCharacterVal ('\'', type); + case '\"': + return constCharacterVal ('\"', type); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + return constCharacterVal (octalEscape (&s), type); + + case 'x': + return constCharacterVal (hexEscape (&s), type); + + case 'u': + return constCharacterVal (universalEscape (&s, 4), type); + + case 'U': + return constCharacterVal (universalEscape (&s, 8), type); + + default: + return constCharacterVal (*s, type); + } + } + else if (type) // Wide character constant + { + size_t ulen; + const TYPE_UDWORD *ustr = utf_32_from_utf_8 (&ulen, s, strlen(s) - 1); + value *val = constCharacterVal (*ustr, type); + free ((void *)ustr); + return (val); + } + else // Character constant that is not wide - compability with legacy encodings. + return constCharacterVal (*s, 0); +} + +/*------------------------------------------------------------------*/ +/* valFromType - creates a value from type given */ +/*------------------------------------------------------------------*/ +value * +valFromType (sym_link * type) +{ + value *val = newValue (); + val->type = copyLinkChain (type); + val->etype = getSpec (val->type); + return val; +} + +/*------------------------------------------------------------------*/ +/* floatFromVal - value to double float conversion */ +/*------------------------------------------------------------------*/ +double +floatFromVal (value * val) +{ + if (!val) + return 0; + + if (val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werror (E_CONST_EXPECTED, val->name); + return 0; + } + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (!IS_SPEC (val->type)) + return SPEC_CVAL (val->etype).v_ulong; + + if (SPEC_NOUN (val->etype) == V_FLOAT) + return SPEC_CVAL (val->etype).v_float; + + if (SPEC_NOUN (val->etype) == V_FIXED16X16) + return doubleFromFixed16x16 (SPEC_CVAL (val->etype).v_fixed16x16); + + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return (double)SPEC_CVAL (val->etype).v_ulonglong; + else + return (double)SPEC_CVAL (val->etype).v_longlong; + } + + if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return SPEC_CVAL (val->etype).v_ulong; + else + return SPEC_CVAL (val->etype).v_long; + } + + if (SPEC_NOUN (val->etype) == V_INT) + { + if (SPEC_USIGN (val->etype)) + return SPEC_CVAL (val->etype).v_uint; + else + return SPEC_CVAL (val->etype).v_int; + } + + if (SPEC_NOUN (val->etype) == V_CHAR) + { + if (SPEC_USIGN (val->etype)) + return (unsigned char) SPEC_CVAL (val->etype).v_uint; + else + return (signed char) SPEC_CVAL (val->etype).v_int; + } + + if (IS_BOOL (val->etype) || IS_BITVAR (val->etype)) + return SPEC_CVAL (val->etype).v_uint; + + if (SPEC_NOUN (val->etype) == V_VOID) + return SPEC_CVAL (val->etype).v_ulong; + + if (SPEC_NOUN (val->etype) == V_STRUCT) + return SPEC_CVAL (val->etype).v_ulong; + + /* we are lost ! */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "floatFromVal: unknown value"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* ulFromVal - value to unsigned long conversion */ +/*------------------------------------------------------------------*/ +unsigned long +ulFromVal (value *val) +{ + if (!val) + return 0; + + if (val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werror (E_CONST_EXPECTED, val->name); + return 0; + } + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (!IS_SPEC (val->type)) + return SPEC_CVAL (val->etype).v_ulong; + + if (SPEC_NOUN (val->etype) == V_FLOAT) + return double2ul (SPEC_CVAL (val->etype).v_float); + + if (SPEC_NOUN (val->etype) == V_FIXED16X16) + return double2ul (doubleFromFixed16x16 (SPEC_CVAL (val->etype).v_fixed16x16)); + + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return (unsigned long)SPEC_CVAL (val->etype).v_ulonglong; + else + return (unsigned long)SPEC_CVAL (val->etype).v_longlong; + } + + if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return SPEC_CVAL (val->etype).v_ulong; + else + return SPEC_CVAL (val->etype).v_long; + } + + if (SPEC_NOUN (val->etype) == V_INT) + { + if (SPEC_USIGN (val->etype)) + return SPEC_CVAL (val->etype).v_uint; + else + return SPEC_CVAL (val->etype).v_int; + } + + if (SPEC_NOUN (val->etype) == V_CHAR) + { + if (SPEC_USIGN (val->etype)) + return (unsigned char) SPEC_CVAL (val->etype).v_uint; + else + return (signed char) SPEC_CVAL (val->etype).v_int; + } + + if (IS_BOOL (val->etype) || IS_BITVAR (val->etype)) + return SPEC_CVAL (val->etype).v_uint; + + if (SPEC_NOUN (val->etype) == V_VOID) + return SPEC_CVAL (val->etype).v_ulong; + + if (SPEC_NOUN (val->etype) == V_STRUCT) + return SPEC_CVAL (val->etype).v_ulong; + + /* we are lost ! */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "ulFromVal: unknown value"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* byteOfVal - extract a byte of a value */ +/* offset = 0 (LSB) ... n-1 (MSB) */ +/* higher offsets of signed ints will be sign extended, */ +/* other types will be extended with zero padding */ +/*------------------------------------------------------------------*/ +unsigned char +byteOfVal (value * val, int offset) +{ + unsigned char *p; + int shift = 8*offset; + + wassert (offset >= 0); + + if (!val) + return 0; + + if (val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werror (E_CONST_EXPECTED, val->name); + return 0; + } + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + /* 2012-Apr-30 EEP - Why is this true? */ + if (!IS_SPEC (val->type)) + return offset < 4 ? (SPEC_CVAL (val->etype).v_ulong >> shift) & 0xff : 0; + + if (SPEC_NOUN (val->etype) == V_FLOAT) + { + float f = (float)SPEC_CVAL (val->etype).v_float; + + if (offset > 3) + return 0; + p = (unsigned char *)&f; +#ifdef WORDS_BIGENDIAN + p += 3 - offset; +#else + p += offset; +#endif + return *p; + } + + if (SPEC_NOUN (val->etype) == V_FIXED16X16) + return offset < 4 ? (SPEC_CVAL (val->etype).v_fixed16x16 >> shift) & 0xff : 0; + + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return offset < 8 ? (SPEC_CVAL (val->etype).v_ulonglong >> shift) & 0xff : 0; + else + return offset < 8 ? (SPEC_CVAL (val->etype).v_longlong >> shift) & 0xff : + (SPEC_CVAL (val->etype).v_longlong < 0 ? 0xff : 0); + } + + if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + return offset < 4 ? (SPEC_CVAL (val->etype).v_ulong >> shift) & 0xff : 0; + else + return offset < 4 ? (SPEC_CVAL (val->etype).v_long >> shift) & 0xff : + (SPEC_CVAL (val->etype).v_long < 0 ? 0xff : 0); + } + + if (SPEC_NOUN (val->etype) == V_INT) + { + if (SPEC_USIGN (val->etype)) + return offset < 2 ? (SPEC_CVAL (val->etype).v_uint >> shift) & 0xff : 0; + else + return offset < 2 ? (SPEC_CVAL (val->etype).v_int >> shift) & 0xff : + (SPEC_CVAL (val->etype).v_int < 0 ? 0xff : 0); + } + + if (SPEC_NOUN (val->etype) == V_CHAR) + { + if (SPEC_USIGN (val->etype)) + return offset < 1 ? SPEC_CVAL (val->etype).v_uint & 0xff : 0; + else + return offset < 1 ? SPEC_CVAL (val->etype).v_int & 0xff : + (SPEC_CVAL (val->etype).v_int < 0 ? 0xff : 0); + } + + if (IS_BOOL (val->etype) || IS_BITVAR (val->etype)) + return offset < 2 ? (SPEC_CVAL (val->etype).v_uint >> shift) & 0xff : 0; + + /* we are lost ! */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "byteOfVal: unknown value"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* ullFromLit - literal to unsigned long long conversion */ +/*------------------------------------------------------------------*/ +TYPE_TARGET_ULONGLONG +ullFromLit (sym_link * lit) +{ + sym_link * etype = getSpec(lit); + + if (!lit) + return 0; + + if (etype && SPEC_SCLS (etype) != S_LITERAL) + { + werror (E_CONST_EXPECTED, ""); + return 0; + } + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (!IS_SPEC (lit)) + return SPEC_CVAL (etype).v_ulong; + + if (SPEC_NOUN (etype) == V_FLOAT) + return double2ul (SPEC_CVAL (etype).v_float); /* FIXME: this loses bits */ + + if (SPEC_NOUN (etype) == V_FIXED16X16) + return double2ul (doubleFromFixed16x16 (SPEC_CVAL (etype).v_fixed16x16)); /* FIXME: this loses bits */ + + if (SPEC_LONGLONG (etype)) + { + if (SPEC_USIGN (etype)) + return SPEC_CVAL (etype).v_ulonglong; + else + return SPEC_CVAL (etype).v_longlong; + } + + if (SPEC_LONG (etype)) + { + if (SPEC_USIGN (etype)) + return SPEC_CVAL (etype).v_ulong; + else + return SPEC_CVAL (etype).v_long; + } + + if (SPEC_NOUN (etype) == V_INT) + { + if (SPEC_USIGN (etype)) + return SPEC_CVAL (etype).v_uint; + else + return SPEC_CVAL (etype).v_int; + } + + if (SPEC_NOUN (etype) == V_CHAR) + { + if (SPEC_USIGN (etype)) + return (unsigned char) SPEC_CVAL (etype).v_uint; + else + return (signed char) SPEC_CVAL (etype).v_int; + } + + if (IS_BOOL (etype) || IS_BITVAR (etype)) + return SPEC_CVAL (etype).v_uint; + + if (SPEC_NOUN (etype) == V_VOID) + return SPEC_CVAL (etype).v_ulong; + + if (SPEC_NOUN (etype) == V_STRUCT) /* ??? Why ??? EEP - 23 Nov 2012 */ + return SPEC_CVAL (etype).v_ulong; + + /* we are lost ! */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "ullFromLit: unknown value"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* ullFromVal - value to unsigned long long conversion */ +/*------------------------------------------------------------------*/ +unsigned long long +ullFromVal (value * val) +{ + if (!val) + return 0; + + if (val->etype && SPEC_SCLS (val->etype) != S_LITERAL) + { + werror (E_CONST_EXPECTED, val->name); + return 0; + } + return (unsigned long long) ullFromLit (val->type); +} + +/*------------------------------------------------------------------*/ +/* csdOfVal - return 0 if the value can be represented as csd */ +/* topbit - highest nonzero bit in csd */ +/* nonzero - number of nonzero bits in csd */ +/* csd_add - positive bits in csd */ +/* csd_sub - negative bits in csd */ +/*------------------------------------------------------------------*/ +int csdOfVal (int *topbit, int *nonzero, unsigned long long *csd_add, unsigned long long *csd_sub, value *val) +{ + unsigned long long binary = ullFromVal (val); + bool gamma, theta, a; + int bit, next; + + *topbit = 0; + *nonzero = 0; + *csd_add = 0; + *csd_sub = 0; + + for (a = 0, gamma = 0, bit = 0; bit < 61; bit++) + { + theta = a ^ (binary & 1); + gamma = !gamma && theta; + next = (1 - 2 * (bool)(binary & 2)) * gamma; + if (next > 0) + *csd_add |= (1ull << bit); + else if (next < 0) + *csd_sub |= (1ull << bit); + if (next) + { + (*nonzero)++; + *topbit = bit; + } + a = (binary & 1); + binary >>= 1; + } + return((bool)binary); +} + +/*------------------------------------------------------------------*/ +/* isEqualVal - return 1 if value is equal to specified constant */ +/*------------------------------------------------------------------*/ +int +isEqualVal (value * val, int k) +{ + if (IS_SPEC (val->type)) + { + if (SPEC_NOUN (val->type) == V_FLOAT || SPEC_NOUN (val->type) == V_FIXED16X16) + return floatFromVal (val) == k; + } + return ((TYPE_TARGET_LONGLONG) ullFromVal (val)) == k; +} + + +/*-----------------------------------------------------------------*/ +/* doubleFromFixed16x16 - convert a fixed16x16 to double */ +/*-----------------------------------------------------------------*/ +double +doubleFromFixed16x16 (TYPE_TARGET_ULONG value) +{ +#if 0 + /* This version is incorrect negative values. */ + double tmp = 0, exp = 2; + + tmp = (value & 0xffff0000) >> 16; + + while (value) + { + value &= 0xffff; + if (value & 0x8000) + tmp += 1 / exp; + exp *= 2; + value <<= 1; + } + + return (tmp); +#else + return ((double) (value * 1.0) / (double) (1UL << 16)); +#endif +} + +TYPE_TARGET_ULONG +fixed16x16FromDouble (double value) +{ +#if 0 + /* This version is incorrect negative values. */ + unsigned int tmp = 0, pos = 16; + TYPE_TARGET_ULONG res; + + tmp = floor (value); + res = tmp << 16; + value -= tmp; + + tmp = 0; + while (pos--) + { + value *= 2; + if (value >= 1.0) + tmp |= (1 << pos); + value -= floor (value); + } + + res |= tmp; + + return (res); +#else + return double2ul (value * (double) (1UL << 16)); +#endif +} + +/*------------------------------------------------------------------*/ +/* valUnaryPM - does the unary +/- operation on a constant */ +/*------------------------------------------------------------------*/ +value * +valUnaryPM (value * val) +{ + /* depending on type */ + if (SPEC_NOUN (val->etype) == V_FLOAT) + SPEC_CVAL (val->etype).v_float = -1.0 * SPEC_CVAL (val->etype).v_float; + else if (SPEC_NOUN (val->etype) == V_FIXED16X16) + SPEC_CVAL (val->etype).v_fixed16x16 = (TYPE_TARGET_ULONG) - ((long) SPEC_CVAL (val->etype).v_fixed16x16); + else if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulonglong = 0 - SPEC_CVAL (val->etype).v_ulonglong; + else + SPEC_CVAL (val->etype).v_longlong = -SPEC_CVAL (val->etype).v_longlong; + } + else if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulong = 0 - SPEC_CVAL (val->etype).v_ulong; + else + SPEC_CVAL (val->etype).v_long = -SPEC_CVAL (val->etype).v_long; + } + else + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = 0 - SPEC_CVAL (val->etype).v_uint; + else + SPEC_CVAL (val->etype).v_int = -SPEC_CVAL (val->etype).v_int; + + if (SPEC_NOUN (val->etype) == V_CHAR) + { + /* promote to 'signed int', cheapestVal() might reduce it again */ + SPEC_USIGN (val->etype) = 0; + SPEC_NOUN (val->etype) = V_INT; + } + return cheapestVal (val); + } + return val; +} + +/*------------------------------------------------------------------*/ +/* valueComplement - complements a constant */ +/*------------------------------------------------------------------*/ +value * +valComplement (value * val) +{ + /* depending on type */ + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulonglong = ~SPEC_CVAL (val->etype).v_ulonglong; + else + SPEC_CVAL (val->etype).v_longlong = ~SPEC_CVAL (val->etype).v_longlong; + } + else if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulong = ~SPEC_CVAL (val->etype).v_ulong; + else + SPEC_CVAL (val->etype).v_long = ~SPEC_CVAL (val->etype).v_long; + } + else + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = ~SPEC_CVAL (val->etype).v_uint; + else + SPEC_CVAL (val->etype).v_int = ~SPEC_CVAL (val->etype).v_int; + + if (SPEC_NOUN (val->etype) == V_CHAR) + { + /* promote to 'signed int', cheapestVal() might reduce it again */ + SPEC_USIGN (val->etype) = 0; + SPEC_NOUN (val->etype) = V_INT; + } + return cheapestVal (val); + } + return val; +} + +/*------------------------------------------------------------------*/ +/* valueNot - complements a constant */ +/*------------------------------------------------------------------*/ +value * +valNot (value * val) +{ + /* depending on type */ + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_ulonglong; + else + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_longlong; + } + else if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_ulong; + else + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_long; + } + else + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_uint; + else + SPEC_CVAL (val->etype).v_int = !SPEC_CVAL (val->etype).v_int; + + } + /* ANSI: result type is int, value is 0 or 1 */ + /* sdcc will hold this in an 'unsigned char' */ + SPEC_USIGN (val->etype) = 1; + SPEC_LONG (val->etype) = 0; + SPEC_LONGLONG (val->type) = 0; + SPEC_NOUN (val->etype) = V_CHAR; + return val; +} + +/*------------------------------------------------------------------*/ +/* valMult - multiply constants */ +/*------------------------------------------------------------------*/ +value * +valMult (value * lval, value * rval) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = val->etype = computeType (lval->etype, rval->etype, RESULT_TYPE_INT, '*'); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (IS_FLOAT (val->type)) + SPEC_CVAL (val->type).v_float = floatFromVal (lval) * floatFromVal (rval); + else if (IS_FIXED16X16 (val->type)) + SPEC_CVAL (val->type).v_fixed16x16 = fixed16x16FromDouble (floatFromVal (lval) * floatFromVal (rval)); + /* signed and unsigned mul are the same, as long as the precision of the + result isn't bigger than the precision of the operands. */ + else if (SPEC_LONGLONG (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) * (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else if (SPEC_LONG (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) * (TYPE_TARGET_ULONG) ulFromVal (rval); + else if (SPEC_USIGN (val->type)) /* unsigned int */ + { + TYPE_TARGET_ULONG ul = (TYPE_TARGET_UINT) ulFromVal (lval) * (TYPE_TARGET_UINT) ulFromVal (rval); + + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ul; + if (ul != (TYPE_TARGET_UINT) ul) + werror (W_INT_OVL); + } + else /* signed int */ + { + TYPE_TARGET_LONG l = (TYPE_TARGET_INT) floatFromVal (lval) * (TYPE_TARGET_INT) floatFromVal (rval); + + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) l; + if (l != (TYPE_TARGET_INT) l) + werror (W_INT_OVL); + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valDiv - Divide constants */ +/*------------------------------------------------------------------*/ +value * +valDiv (value * lval, value * rval) +{ + value *val; + + if (isEqualVal (rval, 0) && !IS_FLOAT (computeType (lval->etype, rval->etype, RESULT_TYPE_INT, '/'))) + { + werror (E_DIVIDE_BY_ZERO); + return rval; + } + + /* create a new value */ + val = newValue (); + val->type = val->etype = computeType (lval->etype, rval->etype, RESULT_TYPE_INT, '/'); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (IS_FLOAT (val->type)) + SPEC_CVAL (val->type).v_float = floatFromVal (lval) / floatFromVal (rval); + else if (IS_FIXED16X16 (val->type)) + SPEC_CVAL (val->type).v_fixed16x16 = fixed16x16FromDouble (floatFromVal (lval) / floatFromVal (rval)); + else if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) / (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) / (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) / (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) / (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) / (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) / (TYPE_TARGET_INT) ulFromVal (rval); + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valMod - Modulus constants */ +/*------------------------------------------------------------------*/ +value * +valMod (value * lval, value * rval) +{ + value *val; + + if (isEqualVal (rval, 0)) + { + werror (E_DIVIDE_BY_ZERO); + return rval; + } + + /* create a new value */ + val = newValue (); + val->type = val->etype = computeType (lval->etype, rval->etype, RESULT_TYPE_INT, '%'); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) % (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) % (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) % (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) % (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) % (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) % (TYPE_TARGET_INT) ulFromVal (rval); + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valPlus - Addition constants */ +/*------------------------------------------------------------------*/ +value * +valPlus (value * lval, value * rval) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = computeType (lval->type, rval->type, RESULT_TYPE_INT, '+'); + val->etype = getSpec (val->type); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (!IS_SPEC (val->type)) + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) + (TYPE_TARGET_ULONG) ulFromVal (rval); + else if (IS_FLOAT (val->type)) + SPEC_CVAL (val->type).v_float = floatFromVal (lval) + floatFromVal (rval); + else if (IS_FIXED16X16 (val->type)) + SPEC_CVAL (val->type).v_fixed16x16 = fixed16x16FromDouble (floatFromVal (lval) + floatFromVal (rval)); + else if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) + (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) + (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) + (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) + (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) + (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) + (TYPE_TARGET_INT) ulFromVal (rval); + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valMinus - Addition constants */ +/*------------------------------------------------------------------*/ +value * +valMinus (value * lval, value * rval) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = computeType (lval->type, rval->type, RESULT_TYPE_INT, '-'); + val->etype = getSpec (val->type); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (!IS_SPEC (val->type)) + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) - (TYPE_TARGET_ULONG) ulFromVal (rval); + else if (IS_FLOAT (val->type)) + SPEC_CVAL (val->type).v_float = floatFromVal (lval) - floatFromVal (rval); + else if (IS_FIXED16X16 (val->type)) + SPEC_CVAL (val->type).v_fixed16x16 = fixed16x16FromDouble (floatFromVal (lval) - floatFromVal (rval)); + else if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) - (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) - (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) - (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) - (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) - (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) - (TYPE_TARGET_INT) ulFromVal (rval); + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valShift - Shift left or right */ +/*------------------------------------------------------------------*/ +value * +valShift (value * lval, value * rval, int lr) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = val->etype = computeType (lval->etype, NULL, RESULT_TYPE_INT, 'S'); + SPEC_SCLS (val->etype) = S_LITERAL; /* will remain literal */ + + if (getSize (val->type) * 8 <= (TYPE_TARGET_ULONG) ulFromVal (rval) && + /* left shift */ + (lr || + /* right shift and unsigned */ + (!lr && SPEC_USIGN (rval->type))) && + ((TYPE_TARGET_ULONG) ulFromVal (lval) != (TYPE_TARGET_ULONG) 0)) + { + werror (W_SHIFT_CHANGED, (lr ? "left" : "right")); + } + + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_ulonglong = lr ? + (TYPE_TARGET_ULONGLONG) ullFromVal (lval) << (TYPE_TARGET_ULONGLONG) ullFromVal (rval) : + (TYPE_TARGET_ULONGLONG) ullFromVal (lval) >> (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + } + else + { + SPEC_CVAL (val->type).v_longlong = lr ? + (TYPE_TARGET_LONGLONG) ullFromVal (lval) << (TYPE_TARGET_ULONGLONG) ullFromVal (rval) : + (TYPE_TARGET_LONGLONG) ullFromVal (lval) >> (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + } + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_ulong = lr ? + (TYPE_TARGET_ULONG) ulFromVal (lval) << (TYPE_TARGET_ULONG) ulFromVal (rval) : + (TYPE_TARGET_ULONG) ulFromVal (lval) >> (TYPE_TARGET_ULONG) ulFromVal (rval); + } + else + { + SPEC_CVAL (val->type).v_long = lr ? + (TYPE_TARGET_LONG) ulFromVal (lval) << (TYPE_TARGET_ULONG) ulFromVal (rval) : + (TYPE_TARGET_LONG) ulFromVal (lval) >> (TYPE_TARGET_ULONG) ulFromVal (rval); + } + } + else + { + if (SPEC_USIGN (val->type)) + { + SPEC_CVAL (val->type).v_uint = lr ? + (TYPE_TARGET_UINT) ulFromVal (lval) << (TYPE_TARGET_ULONG) ulFromVal (rval) : + (TYPE_TARGET_UINT) ulFromVal (lval) >> (TYPE_TARGET_ULONG) ulFromVal (rval); + } + else + { + SPEC_CVAL (val->type).v_int = lr ? + (TYPE_TARGET_INT) ulFromVal (lval) << (TYPE_TARGET_ULONG) ulFromVal (rval) : + (TYPE_TARGET_INT) ulFromVal (lval) >> (TYPE_TARGET_ULONG) ulFromVal (rval); + } + } + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valCompare - Compares two literal */ +/*------------------------------------------------------------------*/ +value * +valCompare (value * lval, value * rval, int ctype) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = val->etype = newCharLink (); + val->type->xclass = SPECIFIER; + SPEC_NOUN (val->type) = V_CHAR; /* type is char */ + SPEC_USIGN (val->type) = 1; + SPEC_SCLS (val->type) = S_LITERAL; /* will remain literal */ + + switch (ctype) + { + /* FIXME: need to add long long support to inequalities */ + case '<': + SPEC_CVAL (val->type).v_int = floatFromVal (lval) < floatFromVal (rval); + break; + + case '>': + SPEC_CVAL (val->type).v_int = floatFromVal (lval) > floatFromVal (rval); + break; + + case LE_OP: + SPEC_CVAL (val->type).v_int = floatFromVal (lval) <= floatFromVal (rval); + break; + + case GE_OP: + SPEC_CVAL (val->type).v_int = floatFromVal (lval) >= floatFromVal (rval); + break; + + case EQ_OP: + if (SPEC_NOUN (lval->type) == V_FLOAT || SPEC_NOUN (rval->type) == V_FLOAT) + { + SPEC_CVAL (val->type).v_int = floatFromVal (lval) == floatFromVal (rval); + } + else if (SPEC_NOUN (lval->type) == V_FIXED16X16 || SPEC_NOUN (rval->type) == V_FIXED16X16) + { + SPEC_CVAL (val->type).v_int = floatFromVal (lval) == floatFromVal (rval); + } + else + { + /* integrals: ignore signedness */ + TYPE_TARGET_ULONGLONG l, r; + + l = (TYPE_TARGET_ULONGLONG) ullFromVal (lval); + r = (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + /* In order to correctly compare 'signed int' and 'unsigned int' it's + neccessary to strip them to 16 bit. + Literals are reduced to their cheapest type, therefore left and + right might have different types. It's neccessary to find a + common type: int (used for char too) or long */ + if (!IS_LONGLONG (lval->etype) && !IS_LONGLONG (rval->etype)) + { + r = (TYPE_TARGET_ULONG) r; + l = (TYPE_TARGET_ULONG) l; + } + if (!IS_LONG (lval->etype) && !IS_LONG (rval->etype)) + { + r = (TYPE_TARGET_UINT) r; + l = (TYPE_TARGET_UINT) l; + } + SPEC_CVAL (val->type).v_int = l == r; + } + break; + case NE_OP: + if (SPEC_NOUN (lval->type) == V_FLOAT || SPEC_NOUN (rval->type) == V_FLOAT) + { + SPEC_CVAL (val->type).v_int = floatFromVal (lval) != floatFromVal (rval); + } + else if (SPEC_NOUN (lval->type) == V_FIXED16X16 || SPEC_NOUN (rval->type) == V_FIXED16X16) + { + SPEC_CVAL (val->type).v_int = floatFromVal (lval) != floatFromVal (rval); + } + else + { + /* integrals: ignore signedness */ + TYPE_TARGET_ULONGLONG l, r; + + l = (TYPE_TARGET_ULONGLONG) ullFromVal (lval); + r = (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + /* In order to correctly compare 'signed int' and 'unsigned int' it's + neccessary to strip them to 16 bit. + Literals are reduced to their cheapest type, therefore left and + right might have different types. It's neccessary to find a + common type: int (used for char too) or long */ + if (!IS_LONGLONG (lval->etype) && !IS_LONGLONG (rval->etype)) + { + r = (TYPE_TARGET_ULONG) r; + l = (TYPE_TARGET_ULONG) l; + } + if (!IS_LONG (lval->etype) && !IS_LONG (rval->etype)) + { + r = (TYPE_TARGET_UINT) r; + l = (TYPE_TARGET_UINT) l; + } + SPEC_CVAL (val->type).v_int = l != r; + } + break; + + } + + return val; +} + +/*------------------------------------------------------------------*/ +/* valBitwise - Bitwise operation */ +/*------------------------------------------------------------------*/ +value * +valBitwise (value * lval, value * rval, int op) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = computeType (lval->etype, rval->etype, RESULT_TYPE_CHAR, op); + val->etype = getSpec (val->type); + SPEC_SCLS (val->etype) = S_LITERAL; + + switch (op) + { + case '&': + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) & (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) & (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) & (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) & (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) & (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) & (TYPE_TARGET_INT) ulFromVal (rval); + } + break; + + case '|': + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) | (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) | (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) | (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) | (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) | (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) | (TYPE_TARGET_INT) ulFromVal (rval); + } + + break; + + case '^': + if (SPEC_LONGLONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulonglong = (TYPE_TARGET_ULONGLONG) ullFromVal (lval) ^ (TYPE_TARGET_ULONGLONG) ullFromVal (rval); + else + SPEC_CVAL (val->type).v_longlong = (TYPE_TARGET_LONGLONG) ullFromVal (lval) ^ (TYPE_TARGET_LONGLONG) ullFromVal (rval); + } + else if (SPEC_LONG (val->type)) + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_ulong = (TYPE_TARGET_ULONG) ulFromVal (lval) ^ (TYPE_TARGET_ULONG) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_long = (TYPE_TARGET_LONG) ulFromVal (lval) ^ (TYPE_TARGET_LONG) ulFromVal (rval); + } + else + { + if (SPEC_USIGN (val->type)) + SPEC_CVAL (val->type).v_uint = (TYPE_TARGET_UINT) ulFromVal (lval) ^ (TYPE_TARGET_UINT) ulFromVal (rval); + else + SPEC_CVAL (val->type).v_int = (TYPE_TARGET_INT) ulFromVal (lval) ^ (TYPE_TARGET_INT) ulFromVal (rval); + } + break; + } + + return cheapestVal (val); +} + +/*------------------------------------------------------------------*/ +/* valAndOr - Generates code for and / or operation */ +/*------------------------------------------------------------------*/ +value * +valLogicAndOr (value * lval, value * rval, int op) +{ + value *val; + + /* create a new value */ + val = newValue (); + val->type = val->etype = newCharLink (); + val->type->xclass = SPECIFIER; + SPEC_SCLS (val->type) = S_LITERAL; /* will remain literal */ + SPEC_USIGN (val->type) = 1; + + switch (op) + { + case AND_OP: + SPEC_CVAL (val->type).v_int = !isEqualVal (lval, 0) && !isEqualVal (rval, 0); + break; + + case OR_OP: + SPEC_CVAL (val->type).v_int = !isEqualVal (lval, 0) || !isEqualVal (rval, 0); + break; + } + + return val; +} + +/*------------------------------------------------------------------*/ +/* valCastLiteral - casts a literal value to another type */ +/*------------------------------------------------------------------*/ +value * +valCastLiteral (sym_link * dtype, double fval, TYPE_TARGET_ULONGLONG llval) +{ + value *val; + unsigned long l = double2ul (fval); + + if (!dtype) + return NULL; + + val = newValue (); + if (dtype) + val->etype = getSpec (val->type = copyLinkChain (dtype)); + else + { + val->etype = val->type = newLink (SPECIFIER); + SPEC_NOUN (val->etype) = V_VOID; + } + SPEC_SCLS (val->etype) = S_LITERAL; + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (!IS_SPEC (val->type)) + { + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) l; + return val; + } + + switch (SPEC_NOUN (val->etype)) + { + case V_FLOAT: + SPEC_CVAL (val->etype).v_float = fval; + break; + + case V_FIXED16X16: + SPEC_CVAL (val->etype).v_fixed16x16 = fixed16x16FromDouble (fval); + break; + + case V_BOOL: + case V_BIT: + case V_SBIT: + SPEC_CVAL (val->etype).v_uint = fval ? 1 : 0; + break; + + case V_BITFIELD: + l &= (0xffffffffu >> (32 - SPEC_BLEN (val->etype))); + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UINT) l; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_INT) l; + break; + + case V_CHAR: + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UCHAR) l; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_CHAR) l; + break; + + default: + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulonglong = (TYPE_TARGET_ULONGLONG) llval; + else + SPEC_CVAL (val->etype).v_longlong = (TYPE_TARGET_LONGLONG) llval; + } + else if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) l; + else + SPEC_CVAL (val->etype).v_long = (TYPE_TARGET_LONG) l; + } + else + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UINT) l; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_INT) l; + } + } + + return val; +} + +/*-------------------------------------------------------------------*/ +/* valRecastLitVal - changes type of a literal value to another type */ +/*-------------------------------------------------------------------*/ +value * +valRecastLitVal (sym_link * dtype, value * val) +{ + sym_link * otype = val->type; + double fval; + TYPE_TARGET_ULONGLONG ull; + + if (IS_SPEC (otype) && (SPEC_NOUN (otype) == V_FIXED16X16 || SPEC_NOUN (otype) == V_FLOAT)) + { + fval = floatFromVal (val); + ull = (TYPE_TARGET_ULONGLONG)fval; + } + else + { + ull = (TYPE_TARGET_ULONGLONG) ullFromVal (val); + fval = (double)ull; + } + + if (dtype) + val->etype = getSpec (val->type = copyLinkChain (dtype)); + else + { + val->etype = val->type = newLink (SPECIFIER); + SPEC_NOUN (val->etype) = V_VOID; + } + SPEC_SCLS (val->etype) = S_LITERAL; + + /* if it is not a specifier then we can assume that */ + /* it will be an unsigned long */ + if (!IS_SPEC (val->type)) + { + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) ull; + return val; + } + + switch (SPEC_NOUN (val->etype)) + { + case V_FLOAT: + SPEC_CVAL (val->etype).v_float = fval; + break; + + case V_FIXED16X16: + SPEC_CVAL (val->etype).v_fixed16x16 = fixed16x16FromDouble (fval); + break; + + case V_BOOL: + case V_BIT: + case V_SBIT: + SPEC_CVAL (val->etype).v_uint = fval ? 1 : 0; + break; + + case V_BITFIELD: + ull &= (0xffffffffu >> (32 - SPEC_BLEN (val->etype))); + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UINT) ull; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_INT) ull; + break; + + case V_CHAR: + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UCHAR) ull; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_CHAR) ull; + break; + + default: + if (SPEC_LONGLONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulonglong = (TYPE_TARGET_ULONGLONG) ull; + else + SPEC_CVAL (val->etype).v_longlong = (TYPE_TARGET_LONGLONG) ull; + } + else if (SPEC_LONG (val->etype)) + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_ulong = (TYPE_TARGET_ULONG) ull; + else + SPEC_CVAL (val->etype).v_long = (TYPE_TARGET_LONG) ull; + } + else + { + if (SPEC_USIGN (val->etype)) + SPEC_CVAL (val->etype).v_uint = (TYPE_TARGET_UINT) ull; + else + SPEC_CVAL (val->etype).v_int = (TYPE_TARGET_INT) ull; + } + } + + return val; +} + +/*------------------------------------------------------------------*/ +/* getNelements - determines # of elements from init list */ +/*------------------------------------------------------------------*/ +int +getNelements (sym_link * type, initList * ilist) +{ + int i, size; + + if (!ilist) + return 0; + + if (ilist->type == INIT_DEEP) + ilist = ilist->init.deep; + + /* if type is a character array and there is only one + (string) initialiser then get the length of the string */ + if (IS_ARRAY (type) && (IS_CHAR (type->next) || IS_INT (type->next) && IS_UNSIGNED (type->next)) && !ilist->next) + { + ast *iast = ilist->init.node; + value *v = (iast->type == EX_VALUE ? iast->opval.val : NULL); + + if (v && IS_ARRAY (v->type) && (IS_CHAR (v->etype) || IS_INT (v->etype) && IS_UNSIGNED (v->etype) && IS_LONG (type->next) == IS_LONG (v->etype))) + /* yep, it's a string */ + { + return DCL_ELEM (v->type); + } + } + + size = 0; + i = 0; + while (ilist) + { + if (ilist->designation) + { + if (ilist->designation->type != DESIGNATOR_ARRAY) + { + // structure designator for array, boo. + werrorfl (ilist->filename, ilist->lineno, E_BAD_DESIGNATOR); + } + else + { + i = ilist->designation->designator.elemno; + } + } + if (size <= i) + { + size = i + 1; /* array size is one larger than array init element */ + } + i++; + ilist = ilist->next; + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* valForArray - returns a value with name of array index */ +/*-----------------------------------------------------------------*/ +value * +valForArray (ast * arrExpr) +{ + value *val, *lval = NULL; + int size = getSize (arrExpr->left->ftype->next); + + /* if the right or left is an array + resolve it first */ + if (IS_AST_OP (arrExpr->left)) + { + if (arrExpr->left->opval.op == '[') + lval = valForArray (arrExpr->left); + else if (arrExpr->left->opval.op == '.') + lval = valForStructElem (arrExpr->left->left, arrExpr->left->right); + else if (arrExpr->left->opval.op == PTR_OP) + { + if (IS_ADDRESS_OF_OP (arrExpr->left->left)) + lval = valForStructElem (arrExpr->left->left->left, arrExpr->left->right); + else if (IS_AST_VALUE (arrExpr->left->left) && IS_PTR (arrExpr->left->left->ftype)) + lval = valForStructElem (arrExpr->left->left, arrExpr->left->right); + } + else + return NULL; + } + else if (!IS_AST_SYM_VALUE (arrExpr->left)) + return NULL; + + if (!IS_AST_LIT_VALUE (arrExpr->right)) + return NULL; + + val = newValue (); + val->type = newLink (DECLARATOR); + if (IS_AST_LIT_VALUE (arrExpr->left) && IS_PTR (arrExpr->left->ftype)) + { + SNPRINTF (val->name, sizeof (val->name), "0x%X", + AST_ULONG_VALUE (arrExpr->left) + AST_ULONG_VALUE (arrExpr->right) * size); + memcpy (val->type, arrExpr->left->ftype, sizeof (sym_link)); + } + else if (lval) + { + SNPRINTF (val->name, sizeof (val->name), "(%s + %d)", lval->name, AST_ULONG_VALUE (arrExpr->right) * size); + memcpy (val->type, lval->type, sizeof (sym_link)); + } + else + { + SNPRINTF (val->name, sizeof (val->name), "(%s + %d)", + AST_SYMBOL (arrExpr->left)->rname, AST_ULONG_VALUE (arrExpr->right) * size); + if (SPEC_SCLS (arrExpr->left->etype) == S_CODE) + DCL_TYPE (val->type) = CPOINTER; + else if (SPEC_SCLS (arrExpr->left->etype) == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (SPEC_SCLS (arrExpr->left->etype) == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (SPEC_SCLS (arrExpr->left->etype) == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (SPEC_SCLS (arrExpr->left->etype) == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + } + + val->type->next = arrExpr->left->ftype->next; + val->etype = getSpec (val->type); + return val; +} + +/*-----------------------------------------------------------------*/ +/* valForStructElem - returns value with name of struct element */ +/*-----------------------------------------------------------------*/ +value * +valForStructElem (ast * structT, ast * elemT) +{ + value *val, *lval = NULL; + symbol *sym; + int idxoff = 0; + ast *sast = NULL; + + /* left could be further derefed */ + if (IS_AST_OP (structT)) + { + if (structT->opval.op == '[') + lval = valForArray (structT); + else if (structT->opval.op == '+') + { + if (IS_AST_LIT_VALUE (structT->right) && !IS_AST_OP (structT->left)) + { + idxoff = (int) (AST_ULONG_VALUE (structT->right) * getSize (structT->left->ftype->next)); + sast = structT->left; + } + else if (IS_AST_LIT_VALUE (structT->left) && !IS_AST_OP (structT->right)) + { + idxoff = (int) (AST_ULONG_VALUE (structT->left) * getSize (structT->right->ftype->next)); + sast = structT->right; + } + else + return NULL; + } + else if (structT->opval.op == '-') + { + if (IS_AST_LIT_VALUE (structT->right) && !IS_AST_OP (structT->left)) + { + idxoff = 0 - (int) (AST_ULONG_VALUE (structT->right) * getSize (structT->left->ftype->next)); + sast = structT->left; + } + else + return NULL; + } + else if (structT->opval.op == '.') + lval = valForStructElem (structT->left, structT->right); + else if (structT->opval.op == PTR_OP) + { + if (IS_ADDRESS_OF_OP (structT->left)) + lval = valForStructElem (structT->left->left, structT->right); + else if (IS_AST_VALUE (structT->left) && IS_PTR (structT->left->ftype)) + lval = valForStructElem (structT->left, structT->right); + } + else + return NULL; + } + + if (!IS_AST_SYM_VALUE (elemT)) + return NULL; + + if (!structT || !IS_STRUCT (structT->etype)) + return NULL; + + if ((sym = getStructElement (SPEC_STRUCT (structT->etype), AST_SYMBOL (elemT))) == NULL) + { + return NULL; + } + + val = newValue (); + val->type = newLink (DECLARATOR); + if (IS_AST_LIT_VALUE (structT) && IS_PTR (structT->ftype)) + { + SNPRINTF (val->name, sizeof (val->name), "0x%X", AST_ULONG_VALUE (structT) + (int) sym->offset); + memcpy (val->type, structT->ftype, sizeof (sym_link)); + } + else if (lval) + { + SNPRINTF (val->name, sizeof (val->name), "(%s + %d)", lval->name, (int) sym->offset); + memcpy (val->type, lval->type, sizeof (sym_link)); + } + else + { + if (sast) + SNPRINTF (val->name, sizeof (val->name), "(%s + (%d))", AST_SYMBOL (sast)->rname, ((int) sym->offset) + idxoff); + else + SNPRINTF (val->name, sizeof (val->name), "(%s + %d)", AST_SYMBOL (structT)->rname, (int) sym->offset); + + if (SPEC_SCLS (structT->etype) == S_CODE) + DCL_TYPE (val->type) = CPOINTER; + else if (SPEC_SCLS (structT->etype) == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (SPEC_SCLS (structT->etype) == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (SPEC_SCLS (structT->etype) == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (SPEC_SCLS (structT->etype) == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + } + + val->type->next = sym->type; + val->etype = getSpec (val->type); + return val; +} + +/*-----------------------------------------------------------------*/ +/* valForCastAggr - will return value for a cast of an aggregate */ +/* plus minus a constant */ +/*-----------------------------------------------------------------*/ +value * +valForCastAggr (ast * aexpr, sym_link * type, ast * cnst, int op) +{ + value *val; + + if (!IS_AST_SYM_VALUE (aexpr)) + return NULL; + if (!IS_AST_LIT_VALUE (cnst)) + return NULL; + + val = newValue (); + + SNPRINTF (val->name, sizeof (val->name), "(%s %c %d)", + AST_SYMBOL (aexpr)->rname, op, getSize (type->next) * AST_ULONG_VALUE (cnst)); + + val->type = type; + val->etype = getSpec (val->type); + return val; +} + +/*-----------------------------------------------------------------*/ +/* valForCastArr - will return value for a cast of an aggregate */ +/* with no constant */ +/*-----------------------------------------------------------------*/ +value * +valForCastArr (ast * aexpr, sym_link * type) +{ + value *val; + + if (!IS_AST_SYM_VALUE (aexpr)) + return NULL; + + val = newValue (); + + SNPRINTF (val->name, sizeof (val->name), "(%s)", AST_SYMBOL (aexpr)->rname); + + val->type = type; + val->etype = getSpec (val->type); + return val; +} diff --git a/src/SDCCval.h b/src/SDCCval.h new file mode 100644 index 0000000..5365411 --- /dev/null +++ b/src/SDCCval.h @@ -0,0 +1,178 @@ +/*---------------------------------------------------------------------- + SDCCval.h - value wrapper related header information + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1997) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#ifndef SDCCVAL_H +#define SDCCVAL_H + +#include "SDCCsymt.h" + + +/* value wrapper */ +typedef struct value +{ + char name[SDCC_NAME_MAX + 1]; /* operand accessing this value */ + sym_link *type; /* start of type chain */ + sym_link *etype; /* end of type chain */ + symbol *sym; /* Original Symbol */ + struct value *next; /* used in initializer list */ + unsigned vArgs:1; /* arg list ended with variable arg */ +} +value; + +typedef struct literalList +{ + double literalValue; + unsigned count; + struct literalList *next; +} literalList; + +enum +{ + INIT_NODE, + INIT_DEEP, + INIT_HOLE +}; + +/* initializer lists use this structure */ +typedef struct initList +{ + int type; + int lineno; + char *filename; + struct designation *designation; + union + { + struct ast *node; + struct initList *deep; + } + init; + + struct initList *next; +} +initList; + +enum + { + DESIGNATOR_STRUCT, + DESIGNATOR_ARRAY + }; + +/* designated initializers */ +typedef struct designation + { + int type; + int lineno; + char *filename; + union + { + struct symbol *tag; /* tag part of structure */ + int elemno; /* array element (constant expr) */ + } + designator; + + struct designation *next; /* next part of nested designator */ + } +designation; + +/* return values from checkConstantRange */ +typedef enum +{ + CCR_OK, /* evaluate at runtime */ + CCR_OVL, + CCR_ALWAYS_FALSE, + CCR_ALWAYS_TRUE +} +CCR_RESULT; + +#define IS_VARG(x) (x->vArgs) + +/* forward definitions for the symbol table related functions */ +value *newValue (void); +value *constVal (const char *); +value *constIntVal (const char *); +value *constCharacterVal (unsigned long v, char type); +value *constCharVal (unsigned char v); +value *constBoolVal (bool v); +value *reverseVal (value *); +value *reverseValWithType (value *); +value *copyValue (value *); +value *copyValueChain (value *); +value *strVal (const char *); +value *rawStrVal (const char *, size_t size); +value *charVal (const char *); +value *symbolVal (symbol *); +void printVal (value *); +double floatFromVal (value *); +unsigned long ulFromVal (value *); +unsigned long long ullFromVal (value *); + +/* convert a fixed16x16 type to double */ +double doubleFromFixed16x16 (TYPE_TARGET_ULONG value); + +/* convert a double type to fixed16x16 */ +TYPE_TARGET_ULONG fixed16x16FromDouble (double value); + +CCR_RESULT checkConstantRange (sym_link * var, sym_link * lit, int op, bool exchangeOps); +value *array2Ptr (value *); +value *valUnaryPM (value *); +value *valComplement (value *); +value *valNot (value *); +value *valMult (value *, value *); +value *valDiv (value *, value *); +value *valMod (value *, value *); +value *valPlus (value *, value *); +value *valMinus (value *, value *); +value *valShift (value *, value *, int); +value *valCompare (value *, value *, int); +value *valBitwise (value *, value *, int); +value *valLogicAndOr (value *, value *, int); +value *valCastLiteral (sym_link *, double, TYPE_TARGET_ULONGLONG); +value *valueFromLit (double); +initList *newiList (int, void *); +initList *revinit (initList *); +initList *copyIlist (initList *); +double list2int (initList *); +value *list2val (initList *, int); +struct ast *list2expr (initList *); +void resolveIvalSym (initList *, sym_link *); +designation *newDesignation(int, void *); +designation *revDesignation (designation *); +designation *copyDesignation (designation *); +initList *reorderIlist (sym_link *, initList *); +value *valFromType (sym_link *); +value *constFloatVal (const char *); +value *constFixed16x16Val (const char *); +int getNelements (sym_link *, initList *); +value *valForArray (struct ast *); +value *valForStructElem (struct ast *, struct ast *); +value *valForCastAggr (struct ast *, sym_link *, struct ast *, int); +value *valForCastArr (struct ast *, sym_link *); +bool convertIListToConstList (initList * src, literalList ** lList, int size); +literalList *copyLiteralList (literalList * src); +unsigned long double2ul (double val); +unsigned char byteOfVal (value *, int); +int csdOfVal (int *topbit, int *nonzero, unsigned long long *csd_add, unsigned long long *csd_sub, value *val); +int isEqualVal (value *, int); +TYPE_TARGET_ULONGLONG ullFromLit (sym_link * lit); +value * valRecastLitVal (sym_link * dtype, value * val); + +#endif diff --git a/src/altlex.c b/src/altlex.c new file mode 100644 index 0000000..5253224 --- /dev/null +++ b/src/altlex.c @@ -0,0 +1,1219 @@ +/** @file altlex.c + An alternate lexer to SDCC.lex. + In development - ie messy and just plain wrong. + Inspired by the gcc lexer, c-lex.c. +*/ +#include "common.h" +#include "reswords.h" +#include + +#define DUMP_OUTPUT 0 + +/* Right. What are the parts of the C stream? From SDCC.lex: + D = [0..9] digits + L = [a..z A..Z _] alphanumerics and _ + H = [a..f A..F 0-9] Hex digits + E = [eE+-0-9] Digits in a float + FS = [fFlL] Specifiers for a float + IS = [uUlL] Specifiers for a int + + L[LD]* A 'token' - cant think of a good name + Check tokens against the reserved words. + If match + return the token id. + else + If in the typedef table, do stuff... + Blah. See check_type() + 0[xX]{H}+ Hex number - PENDING: specifiers + 0{D}+ Octal number - PENDING: specifiers + {D}+ Decimal - PENDING: specifiers + Floats PENDING + + Exceptions: + Comment start Strip until end of comment. + ... Ellipses + + So the inputs are: + Skip whitespace + switch class + L Try to read a token + D Try to read a number + Punct Try to read punct + */ + +extern int fatalError; +extern int lineno; +extern char *filename; + +FILE *yyin; + +int mylineno; +char *currFname; +char *yytext; + +static char linebuf[10000]; +static int linepos, linelen; +static int end_of_file; + +#ifdef __GNUC__ +#define INLINE inline +#else +#define INLINE +#endif + +#define ERRSINK stderr + +static void +error (const char *sz,...) +{ + va_list ap; + fatalError++; + + if (filename && lineno) + { + fprintf (ERRSINK, "%s(%d):", filename, lineno); + } + fprintf (ERRSINK, "error *** "); + va_start (ap, sz); + vfprintf (ERRSINK, sz, ap); + va_end (ap); + fprintf (ERRSINK, "\n"); + fflush (ERRSINK); +} + +static int +underflow (void) +{ + linelen = fread (linebuf, 1, sizeof (linebuf), yyin); + if (linelen <= 0) + return EOF; + linepos = 0; + return linebuf[linepos++]; +} + +static int INLINE +ygetc (void) +{ + if (linepos < linelen) + return linebuf[linepos++]; + else + return underflow (); +}; + +static int INLINE +yungetc (int c) +{ + linebuf[--linepos] = c; + return 0; +} + +#define GETC() ygetc() +#define UNGETC(_a) yungetc(_a) + +//#define GETC() fgetc(yyin); +//#define UNGETC(_a) ungetc(_a, yyin) +#define ISL(_a) (isalnum(_a) || _a == '_') +#define ISALNUM(_a) isalnum(_a) +#define ISHEX(_a) isxdigit(_a) + +static char * +stringLiteral (void) +{ + static char line[1000]; + int ch; + char *str = line; + + *str++ = '\"'; + /* put into the buffer till we hit the */ + /* first \" */ + while (1) + { + + ch = GETC (); + if (!ch) + break; /* end of input */ + /* if it is a \ then everything allowed */ + if (ch == '\\') + { + *str++ = ch; /* backslash in place */ + *str++ = GETC (); /* following char in place */ + continue; /* carry on */ + } + + /* if new line we have a new line break */ + if (ch == '\n') + break; + + /* if this is a quote then we have work to do */ + /* find the next non whitespace character */ + /* if that is a double quote then carry on */ + if (ch == '\"') + { + + while ((ch = GETC ()) && isspace (ch)); + if (!ch) + break; + if (ch != '\"') + { + UNGETC (ch); + break; + } + + continue; + } + *str++ = ch; + } + *str++ = '\"'; + *str = '\0'; + return line; +} + +static void +discard_comments (int type) +{ + int c; + if (type == '*') + { + do + { + c = GETC (); + if (c == '*') + { + c = GETC (); + if (c == '/') + return; + } + else if (c == EOF) + return; + } + while (1); + } + else if (type == '/') + { + do + { + c = GETC (); + } + while (c != '\n' && c != EOF); + } + else + { + assert (0); + } +} + +/* will return 1 if the string is a part + of a target specific keyword */ +static INLINE int +isTargetKeyword (const char *s) +{ + int i; + + if (port->keywords == NULL) + return 0; + for (i = 0; port->keywords[i]; i++) + { + if (strcmp (port->keywords[i], s) == 0) + return 1; + } + + return 0; +} + +static INLINE int +check_token (const char *sz) +{ + const struct reserved_words *p; + p = is_reserved_word (sz, strlen (sz)); + if (p) + { + if (!p->is_special || isTargetKeyword (sz)) + return p->token; + } + + /* check if it is in the typedef table */ + if (findSym (TypedefTab, NULL, sz)) + { + strncpyz (yylval.yychar, sz, sizeof(yylval.yychar)); + return TYPE_NAME; + } + else + { + strncpyz (yylval.yychar, sz, sizeof(yylval.yychar)); + return IDENTIFIER; + } +} + +static void +handle_pragma (void) +{ + char line[128], *p; + int c; + + c = GETC (); + while (c == '\t' || c == ' ') + c = GETC (); + p = line; + while (!isspace (c)) + { + *p++ = c; + c = GETC (); + } + *p = '\0'; + if (line[0] == '\0') + error ("Missing argument to pragma"); + else + { + /* First give the port a chance */ + if (port->process_pragma && !port->process_pragma (line)) + return; + /* PENDING: all the SDCC shared pragmas */ + /* Nothing handled it */ + error ("Unrecognised #pragma %s", line); + } +} + +static void +handle_line (void) +{ + int c; + char line[128], *p; + + c = GETC (); + while (c == '\t' || c == ' ') + c = GETC (); + p = line; + while (isdigit (c)) + { + *p++ = c; + c = GETC (); + } + *p = '\0'; + if (line[0] == '\0') + error ("Error in number in #line"); + /* This is weird but cpp seems to add an extra three to the line no */ + mylineno = atoi (line) - 3; + lineno = mylineno; + /* Fetch the filename if there is one */ + while (c == '\t' || c == ' ') + c = GETC (); + if (c == '\"') + { + p = line; + c = GETC (); + while (c != '\"' && c != EOF && c != '\n') + { + *p++ = c; + c = GETC (); + } + if (c == '\"') + { + *p = '\0'; + currFname = Safe_strdup (line); + } + filename = currFname; + } +} + +static INLINE void +invalid_directive (void) +{ + error ("Invalid directive"); +} + +static INLINE int +check_newline (void) +{ + int c; + mylineno++; + lineno = mylineno; + + /* Skip any leading white space */ + c = GETC (); + while (c == '\t' || c == ' ') + c = GETC (); + /* Were only interested in #something */ + if (c != '#') + return c; + c = GETC (); + while (c == '\t' || c == ' ') + c = GETC (); + /* The text in the stream is the type of directive */ + switch (c) + { + case 'l': + /* Start of line? */ + if (GETC () == 'i' && GETC () == 'n' && GETC () == 'e') + { + c = GETC (); + if (c == '\t' || c == ' ') + handle_line (); + else + invalid_directive (); + } + else + invalid_directive (); + break; + case 'p': + /* Start of pragma? */ + if (GETC () == 'r' && GETC () == 'a' && GETC () == 'g' && + GETC () == 'm' && GETC () == 'a') + { + c = GETC (); + if (c == '\t' || c == ' ') + handle_pragma (); + else + invalid_directive (); + } + else + invalid_directive (); + break; + default: + invalid_directive (); + } + /* Discard from here until the start of the next line */ + while (c != '\n' && c != EOF) + c = GETC (); + return c; +} + +static int +skip_whitespace (int c) +{ + while (1) + { + switch (c) + { + case ' ': + case '\t': + case '\f': + case '\v': + case '\b': + case '\r': + c = GETC (); + break; + case '\n': + c = check_newline (); + default: + return c; + } + } +} + +void +yyerror (const char *s) +{ + if (end_of_file) + error ("%s at end of of input", s); + else if (yytext[0] == '\0') + error ("%s at null character", s); + else if (yytext[0] == '"') + error ("%s before string constant", s); + else if (yytext[0] == '\'') + error ("%s before character constant", s); + else + error ("%s before %s", s, yytext); +} + +static int +_yylex (void) +{ + int c; + static char line[128]; + char *p; + + yytext = line; + + c = GETC (); + while (1) + { + switch (c) + { + case ' ': + case '\t': + case '\f': + case '\v': + case '\b': + /* Skip whitespace */ + c = GETC (); + break; + case '\r': + case '\n': + c = skip_whitespace (c); + break; + case '#': + UNGETC (c); + c = check_newline (); + break; + default: + goto past_ws; + } + } + +past_ws: + /* Handle comments first */ + if (c == '/') + { + int c2 = GETC (); + if (c2 == '*' || c2 == '/') + { + discard_comments (c2); + c = GETC (); + } + else + UNGETC (c2); + } + switch (c) + { + case EOF: + end_of_file = TRUE; + line[0] = '\0'; + return 0; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + /* Start of a token. Parse. */ + p = line; + *p++ = c; + c = GETC (); + while (ISL (c)) + { + *p++ = c; + c = GETC (); + } + *p = '\0'; + UNGETC (c); + return check_token (line); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + p = line; + *p++ = c; + c = GETC (); + if (c == 'x' || c == 'X') + { + *p++ = c; + c = GETC (); + } + while (ISHEX (c)) + { + *p++ = c; + c = GETC (); + } + if (c == 'U' || c == 'u' || c == 'L' || c == 'l') + { + *p++ = c; + c = GETC (); + } + if (c == 'U' || c == 'u' || c == 'L' || c == 'l') + { + *p++ = c; + c = GETC (); + } + *p = '\0'; + UNGETC (c); + yylval.val = constVal (line); + return CONSTANT; + case '\"': + /* A string */ + p = stringLiteral (); + yylval.val = strVal (p); + return (STRING_LITERAL); + case '\'': + /* Possible formats: + ['\n', '\\', '\'', '\"'...] + ['a'...] + */ + p = line; + *p++ = c; + c = GETC (); + if (c == '\\') + { + *p++ = c; + c = GETC (); + /* Fall through */ + } + *p++ = c; + c = GETC (); + *p++ = c; + *p = '\0'; + if (c != '\'') + { + error ("Unrecognised character constant %s", line); + } + yylval.val = charVal (line); + return CONSTANT; + case '=': + case '&': + case '!': + case '-': + case '+': + case '*': + case '/': + case '%': + case '<': + case '>': + case '^': + case '|': + { + /* Cases which can be compounds */ + /* The types and classes of composites are: + >>= <<= + += -= *= /= %= &= ^= |= + >> << ++ -- + && || + <= >= == != + -> + So a composite started by char 'x' can be: + 1. Followed by itself then an equals + 2. Followed by itself + 3. Followed by an equals + 4. Be a '->' + 5. Be by itself + */ + int next = GETC (); + /* Class 1 and 2 */ + if (next == c) + { + next = GETC (); + /* Class 1 */ + if (next == '=') + { + switch (c) + { + case '>': // >>= + + yylval.yyint = RIGHT_ASSIGN; + return RIGHT_ASSIGN; + case '<': // <<= + + yylval.yyint = LEFT_ASSIGN; + return LEFT_ASSIGN; + default: + error ("Unrecognised token %c%c=", c, c); + } + } + else + { + /* Push the next char back on and find the class */ + UNGETC (next); + /* Case 2 */ + switch (c) + { + case '>': // >> + + return RIGHT_OP; + case '<': // << + + return LEFT_OP; + case '+': + return INC_OP; + case '-': + return DEC_OP; + case '&': + return AND_OP; + case '|': + return OR_OP; + case '=': + return EQ_OP; + default: + error ("Unrecognised token %c%c", c, c); + } + } + } + /* Case 3 */ + else if (next == '=') + { + int result = 0; + switch (c) + { + case '+': + result = ADD_ASSIGN; + break; + case '-': + result = SUB_ASSIGN; + break; + case '*': + result = MUL_ASSIGN; + break; + case '/': + result = DIV_ASSIGN; + break; + case '%': + result = MOD_ASSIGN; + break; + case '&': + result = AND_ASSIGN; + break; + case '^': + result = XOR_ASSIGN; + break; + case '|': + result = OR_ASSIGN; + break; + case '<': + result = LE_OP; + break; + case '>': + result = GE_OP; + break; + case '!': + result = NE_OP; + break; + default: + error ("Unrecognised token %c=", c); + } + if (result) + { + yylval.yyint = result; + return result; + } + } + /* Case 4 */ + else if (c == '-' && next == '>') + { + return PTR_OP; + } + /* Case 5 */ + else + { + UNGETC (next); + return c; + } + break; + } + case '{': + NestLevel++; + return c; + case '}': + NestLevel--; + return c; + case '.': + c = GETC (); + if (c == '.') + { + c = GETC (); + if (c == '.') + { + return VAR_ARGS; + } + } + UNGETC (c); + return '.'; + case '[': + case ']': + return c; + case ',': + case ':': + case '(': + case ')': + case '~': + case '?': + case ';': + /* Special characters that cant be part of a composite */ + return c; + default: + error ("Unhandled character %c", c); + } + return 0; +} + +#define ENTRY(_a) case (_a): printf(#_a); break; + +int +yylex (void) +{ + int ret = _yylex (); +#if DUMP_OUTPUT + static int lastpos = 0; + char tmp; + + printf ("Returning "); + switch (ret) + { + /* Wrapper */ + ENTRY (IDENTIFIER); + ENTRY (TYPE_NAME); + ENTRY (CONSTANT); + ENTRY (STRING_LITERAL); + ENTRY (SIZEOF); + ENTRY (PTR_OP); + ENTRY (INC_OP); + ENTRY (DEC_OP); + ENTRY (LEFT_OP); + ENTRY (RIGHT_OP); + ENTRY (LE_OP); + ENTRY (GE_OP); + ENTRY (EQ_OP); + ENTRY (NE_OP); + ENTRY (AND_OP); + ENTRY (OR_OP); + ENTRY (MUL_ASSIGN); + ENTRY (DIV_ASSIGN); + ENTRY (MOD_ASSIGN); + ENTRY (ADD_ASSIGN); + ENTRY (SUB_ASSIGN); + ENTRY (LEFT_ASSIGN); + ENTRY (RIGHT_ASSIGN); + ENTRY (AND_ASSIGN); + ENTRY (XOR_ASSIGN); + ENTRY (OR_ASSIGN); + ENTRY (TYPEDEF); + ENTRY (EXTERN); + ENTRY (STATIC); + ENTRY (AUTO); + ENTRY (REGISTER); + ENTRY (CODE); + ENTRY (EEPROM); + ENTRY (INTERRUPT); + ENTRY (SFR); + ENTRY (AT); + ENTRY (SBIT); + ENTRY (REENTRANT); + ENTRY (USING); + ENTRY (XDATA); + ENTRY (DATA); + ENTRY (IDATA); + ENTRY (PDATA); + ENTRY (VAR_ARGS); + ENTRY (CRITICAL); + ENTRY (NONBANKED); + ENTRY (BANKED); + ENTRY (CHAR); + ENTRY (SHORT); + ENTRY (INT); + ENTRY (LONG); + ENTRY (SIGNED); + ENTRY (UNSIGNED); + ENTRY (FLOAT); + ENTRY (FIXED16X16); + ENTRY (DOUBLE); + ENTRY (CONST); + ENTRY (VOLATILE); + ENTRY (VOID); + ENTRY (BIT); + ENTRY (STRUCT); + ENTRY (UNION); + ENTRY (ENUM); + ENTRY (ELIPSIS); + ENTRY (RANGE); + ENTRY (FAR); + ENTRY (_XDATA); + ENTRY (_CODE); + ENTRY (_GENERIC); + ENTRY (_NEAR); + ENTRY (_PDATA); + ENTRY (_IDATA); + ENTRY (_EEPROM); + ENTRY (CASE); + ENTRY (DEFAULT); + ENTRY (IF); + ENTRY (ELSE); + ENTRY (SWITCH); + ENTRY (WHILE); + ENTRY (DO); + ENTRY (FOR); + ENTRY (GOTO); + ENTRY (CONTINUE); + ENTRY (BREAK); + ENTRY (RETURN); + ENTRY (INLINEASM); + ENTRY (IFX); + ENTRY (ADDRESS_OF); + ENTRY (GET_VALUE_AT_ADDRESS); + ENTRY (SPIL); + ENTRY (UNSPIL); + ENTRY (GETHBIT); + ENTRY (BITWISEAND); + ENTRY (UNARYMINUS); + ENTRY (IPUSH); + ENTRY (IPOP); + ENTRY (PCALL); + ENTRY (ENDFUNCTION); + ENTRY (JUMPTABLE); + ENTRY (RRC); + ENTRY (RLC); + ENTRY (CAST); + ENTRY (CALL); + ENTRY (PARAM); + ENTRY (NULLOP); + ENTRY (BLOCK); + ENTRY (LABEL); + ENTRY (RECEIVE); + ENTRY (SEND); + default: + printf ("default: %c", ret); + } + tmp = linebuf[linepos]; + linebuf[linepos] = '\0'; + printf (" for %s (%u bytes)\n", linebuf + lastpos, linepos - lastpos); + linebuf[linepos] = tmp; + lastpos = linepos; + fflush (stdout); +#endif + return ret; +} + +#define TEST(_a) (_a) ? (void)0 : printf("Test %s failed\n", #_a); + +int +altlex_testparse (const char *input) +{ + /* Fiddle with the read-ahead buffer to insert ourselves */ + strncpyz (linebuf, input, sizeof(linebuf)); + linelen = strlen (linebuf) + 1; + linepos = 0; + + return yylex (); +} + +int +altlex_testchar (const char *input) +{ + value *val; + if (altlex_testparse (input) != CONSTANT) + return -2; + val = yylval.val; + if (val->type->class != SPECIFIER) + return -3; + if (SPEC_NOUN (val->type) != V_CHAR) + return -4; + if (SPEC_SCLS (val->type) != S_LITERAL) + return -5; + return SPEC_CVAL (val->type).v_int; +} + +int +altlex_testnum (const char *input) +{ + value *val; + if (altlex_testparse (input) != CONSTANT) + return -2; + val = yylval.val; + if (val->type->class != SPECIFIER) + return -3; + if (SPEC_NOUN (val->type) != V_INT) + return -4; + if (SPEC_SCLS (val->type) != S_LITERAL) + return -5; + if (SPEC_USIGN (val->type)) + return SPEC_CVAL (val->type).v_uint; + else + return SPEC_CVAL (val->type).v_int; +} + +int +altlex_runtests (void) +{ + /* These conditions are ripped directly from SDCC.lex */ + /* First check the parsing of the basic tokens */ + TEST (altlex_testparse (">>=") == RIGHT_ASSIGN); + TEST (altlex_testparse ("<<=") == LEFT_ASSIGN); + TEST (altlex_testparse ("+=") == ADD_ASSIGN); + TEST (altlex_testparse ("-=") == SUB_ASSIGN); + TEST (altlex_testparse ("*=") == MUL_ASSIGN); + TEST (altlex_testparse ("/=") == DIV_ASSIGN); + TEST (altlex_testparse ("%=") == MOD_ASSIGN); + TEST (altlex_testparse ("&=") == AND_ASSIGN); + TEST (altlex_testparse ("^=") == XOR_ASSIGN); + TEST (altlex_testparse ("|=") == OR_ASSIGN); + TEST (altlex_testparse (">>") == RIGHT_OP); + TEST (altlex_testparse ("<<") == LEFT_OP); + TEST (altlex_testparse ("++") == INC_OP); + TEST (altlex_testparse ("--") == DEC_OP); + TEST (altlex_testparse ("->") == PTR_OP); + TEST (altlex_testparse ("&&") == AND_OP); + TEST (altlex_testparse ("||") == OR_OP); + TEST (altlex_testparse ("<=") == LE_OP); + TEST (altlex_testparse (">=") == GE_OP); + TEST (altlex_testparse ("==") == EQ_OP); + TEST (altlex_testparse ("!=") == NE_OP); + TEST (altlex_testparse (";") == ';'); + TEST (altlex_testparse ("{") == '{'); + TEST (altlex_testparse ("}") == '}'); + TEST (altlex_testparse (",") == ','); + TEST (altlex_testparse (":") == ':'); + TEST (altlex_testparse ("=") == '='); + TEST (altlex_testparse ("(") == '('); + TEST (altlex_testparse (")") == ')'); + TEST (altlex_testparse ("[") == '['); + TEST (altlex_testparse ("]") == ']'); + TEST (altlex_testparse (".") == '.'); + TEST (altlex_testparse ("&") == '&'); + TEST (altlex_testparse ("!") == '!'); + TEST (altlex_testparse ("~") == '~'); + TEST (altlex_testparse ("-") == '-'); + TEST (altlex_testparse ("+") == '+'); + TEST (altlex_testparse ("*") == '*'); + TEST (altlex_testparse ("/") == '/'); + TEST (altlex_testparse ("%") == '%'); + TEST (altlex_testparse ("<") == '<'); + TEST (altlex_testparse (">") == '>'); + TEST (altlex_testparse ("^") == '^'); + TEST (altlex_testparse ("|") == '|'); + TEST (altlex_testparse ("?") == '?'); + + /* Now some character constants */ + TEST (altlex_testchar ("'1'") == '1'); + TEST (altlex_testchar ("'a'") == 'a'); + TEST (altlex_testchar ("'A'") == 'A'); + TEST (altlex_testchar ("'z'") == 'z'); + TEST (altlex_testchar ("'Z'") == 'Z'); + TEST (altlex_testchar ("'\n'") == '\n'); + TEST (altlex_testchar ("'\\\\'") == '\\'); + TEST (altlex_testchar ("'\\''") == '\''); + + /* And some numbers */ + TEST (altlex_testnum ("0") == 0); + TEST (altlex_testnum ("1") == 1); + TEST (altlex_testnum ("075") == 075); + TEST (altlex_testnum ("0xfeed") == 0xfeed); + TEST (altlex_testnum ("0xFEED") == 0xFEED); + TEST (altlex_testnum ("0x00005678") == 0x5678); + + /* Keywords */ + TEST (altlex_testparse ("auto") == AUTO); + TEST (altlex_testparse ("break") == BREAK); + TEST (altlex_testparse ("case") == CASE); + TEST (altlex_testparse ("char") == CHAR); + TEST (altlex_testparse ("const") == CONST); + TEST (altlex_testparse ("continue") == CONTINUE); + TEST (altlex_testparse ("default") == DEFAULT); + TEST (altlex_testparse ("do") == DO); + /* Prints a warning */ + // TEST(altlex_testparse("double") == FLOAT); + TEST (altlex_testparse ("else") == ELSE); + TEST (altlex_testparse ("enum") == ENUM); + TEST (altlex_testparse ("extern") == EXTERN); + TEST (altlex_testparse ("float") == FLOAT); + TEST (altlex_testparse ("fixed16x16") == FIXED16X16); + TEST (altlex_testparse ("for") == FOR); + TEST (altlex_testparse ("goto") == GOTO); + TEST (altlex_testparse ("if") == IF); + TEST (altlex_testparse ("int") == INT); + TEST (altlex_testparse ("interrupt") == INTERRUPT); + TEST (altlex_testparse ("long") == LONG); + TEST (altlex_testparse ("register") == REGISTER); + TEST (altlex_testparse ("return") == RETURN); + TEST (altlex_testparse ("short") == SHORT); + TEST (altlex_testparse ("signed") == SIGNED); + TEST (altlex_testparse ("sizeof") == SIZEOF); + TEST (altlex_testparse ("static") == STATIC); + TEST (altlex_testparse ("struct") == STRUCT); + TEST (altlex_testparse ("switch") == SWITCH); + TEST (altlex_testparse ("typedef") == TYPEDEF); + TEST (altlex_testparse ("union") == UNION); + TEST (altlex_testparse ("unsigned") == UNSIGNED); + TEST (altlex_testparse ("void") == VOID); + TEST (altlex_testparse ("volatile") == VOLATILE); + TEST (altlex_testparse ("while") == WHILE); + TEST (altlex_testparse ("...") == VAR_ARGS); + +#if 0 + /* Platform specific keywords */ + TEST (altlex_testparse ("sram") ==) + { + count (); + TKEYWORD (XDATA); + } + TEST (altlex_testparse ("using") ==) + { + count (); + TKEYWORD (USING); + } + TEST (altlex_testparse ("near") ==) + { + count (); + TKEYWORD (DATA); + } + TEST (altlex_testparse ("at") ==) + { + count (); + TKEYWORD (AT); + } + TEST (altlex_testparse ("bit") ==) + { + count (); + TKEYWORD (BIT); + } + TEST (altlex_testparse ("code") ==) + { + count (); + TKEYWORD (CODE); + } + TEST (altlex_testparse ("critical") ==) + { + count (); + TKEYWORD (CRITICAL); + } + TEST (altlex_testparse ("data") ==) + { + count (); + TKEYWORD (DATA); + } + TEST (altlex_testparse ("far") ==) + { + count (); + TKEYWORD (XDATA); + } + TEST (altlex_testparse ("eeprom") ==) + { + count (); + TKEYWORD (EEPROM); + } + TEST (altlex_testparse ("flash") ==) + { + count (); + TKEYWORD (CODE); + } + TEST (altlex_testparse ("idata") ==) + { + count (); + TKEYWORD (IDATA); + } + TEST (altlex_testparse ("nonbanked") ==) + { + count (); + TKEYWORD (NONBANKED); + } + TEST (altlex_testparse ("banked") ==) + { + count (); + TKEYWORD (BANKED); + } + TEST (altlex_testparse ("pdata") ==) + { + count (); + TKEYWORD (PDATA); + } + TEST (altlex_testparse ("reentrant") ==) + { + count (); + TKEYWORD (REENTRANT); + } + TEST (altlex_testparse ("sfr") ==) + { + count (); + TKEYWORD (SFR); + } + TEST (altlex_testparse ("sbit") ==) + { + count (); + TKEYWORD (SBIT); + } + TEST (altlex_testparse ("xdata") ==) + { + count (); + TKEYWORD (XDATA); + } + TEST (altlex_testparse ("_data") ==) + { + count (); + TKEYWORD (_NEAR); + } + TEST (altlex_testparse ("_code") ==) + { + count (); + TKEYWORD (_CODE); + } + TEST (altlex_testparse ("_eeprom") ==) + { + count (); + TKEYWORD (_EEPROM); + } + TEST (altlex_testparse ("_flash") ==) + { + count (); + TKEYWORD (_CODE); + } + TEST (altlex_testparse ("_generic") ==) + { + count (); + TKEYWORD (_GENERIC); + } + TEST (altlex_testparse ("_near") ==) + { + count (); + TKEYWORD (_NEAR); + } + TEST (altlex_testparse ("_sram") ==) + { + count (); + TKEYWORD (_XDATA); + } + TEST (altlex_testparse ("_xdata") ==) + { + count (); + TKEYWORD (_XDATA); + } + TEST (altlex_testparse ("_pdata") ==) + { + count (); + TKEYWORD (_PDATA); + } + TEST (altlex_testparse ("_idata") ==) + { + count (); + TKEYWORD (_IDATA); + } +#endif + + return 0; +} diff --git a/src/avr/Makefile.in b/src/avr/Makefile.in new file mode 100644 index 0000000..bd13649 --- /dev/null +++ b/src/avr/Makefile.in @@ -0,0 +1,8 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk + diff --git a/src/avr/avr.vcxproj b/src/avr/avr.vcxproj new file mode 100644 index 0000000..70753b5 --- /dev/null +++ b/src/avr/avr.vcxproj @@ -0,0 +1,149 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {493CAAA0-FCCC-455F-9127-706B20C3D4C9} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/avr.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/avr.bsc + + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/avr.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/avr.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + {2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1} + false + + + + + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph.rul + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + + + + + + \ No newline at end of file diff --git a/src/avr/avr.vcxproj.filters b/src/avr/avr.vcxproj.filters new file mode 100644 index 0000000..4de7d59 --- /dev/null +++ b/src/avr/avr.vcxproj.filters @@ -0,0 +1,43 @@ + + + + + {07fa3e89-f797-4389-ac20-0323d39b8b1e} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {e6af9e78-fabb-498f-8f93-cd15d05e83d6} + h;hpp;hxx;hm;inl + + + {7ccacde4-11c6-4300-83aa-79d51e17bcf7} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/avr/gen.c b/src/avr/gen.c new file mode 100644 index 0000000..8f04afe --- /dev/null +++ b/src/avr/gen.c @@ -0,0 +1,5361 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for ATMEL AVR + + Copyright (C) 2000, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "SDCCglobl.h" +#include "newalloc.h" + +#include "common.h" +#include "SDCCpeeph.h" +#include "ralloc.h" +#include "gen.h" + +char *aopLiteral (value * val, int offset); +extern int allocInfo; + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ + +static char *zero = "0x00"; +static char *one = "0x01"; +static char *spname; + +char *fReturnAVR[] = { "r16", "r17", "r18", "r19" }; +unsigned fAVRReturnSize = 4; /* shared with ralloc.c */ +char **fAVRReturn = fReturnAVR; +static char *larray[4] = { ">", "<", "hlo8", "hhi8" }; + +static struct { + short xPushed; + short zPushed; + short accInUse; + short inLine; + short debugLine; + short nRegsSaved; + set *sendSet; +} _G; + +extern int avr_ptrRegReq; +extern int avr_nRegs; +extern struct dbuf_s *codeOutBuf; +#define RESULTONSTACK(x) \ + (IC_RESULT(x) && IC_RESULT(x)->aop && \ + IC_RESULT(x)->aop->type == AOP_STK ) + +#define MOVR0(x) if (strcmp(x,"r0")) emitcode("mov","r0,%s",x); +#define MOVR24(x) if (strcmp(x,"r24")) emitcode("mov","r24,%s",x); +#define AOP_ISHIGHREG(a,n) (a->type == AOP_REG && a->aopu.aop_reg[n] && a->aopu.aop_reg[n]->rIdx >= R16_IDX) +#define CLRC emitcode("clc","") +#define SETC emitcode("stc","") +#define MOVA(x) +#define IS_REGIDX(a,r) (a->type == AOP_REG && a->aopu.aop_reg[0]->rIdx == r) + +static lineNode *lineHead = NULL; +static lineNode *lineCurr = NULL; + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +#if 0 +// PENDING: Unused. +/*-----------------------------------------------------------------*/ +/* reAdjustPreg - points a register back to where it should */ +/*-----------------------------------------------------------------*/ +static void +reAdjustPreg (asmop * aop) +{ + int size; + + aop->coff = 0; + if ((size = aop->size) <= 1) + return; + size--; + switch (aop->type) { + case AOP_X: + case AOP_Z: + emitcode ("sbiw", "%s,%d", aop->aopu.aop_ptr->name, size); + break; + } + +} + +/*-----------------------------------------------------------------*/ +/* outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +outBitC (operand * result) +{ + emitcode ("clr", "r0"); + emitcode ("rol", "r0"); + outAcc (result); +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static bool +inExcludeList (char *s) +{ + int i = 0; + + if (options.excludeRegs[i] && + STRCASECMP (options.excludeRegs[i], "none") == 0) + return FALSE; + + for (i = 0; options.excludeRegs[i]; i++) { + if (options.excludeRegs[i] && + STRCASECMP (s, options.excludeRegs[i]) == 0) + return TRUE; + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* findLabelBackwards: walks back through the iCode chain looking */ +/* for the given label. Returns number of iCode instructions */ +/* between that label and given ic. */ +/* Returns zero if label not found. */ +/*-----------------------------------------------------------------*/ +static int +findLabelBackwards (iCode * ic, int key) +{ + int count = 0; + + while (ic->prev) { + ic = ic->prev; + count++; + + if (ic->op == LABEL && IC_LABEL (ic)->key == key) { + /* printf("findLabelBackwards = %d\n", count); */ + return count; + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) { + if (sign) { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (AOP (result), "a", offset++); + } + else + while (size--) + aopPut (AOP (result), zero, offset++); + } +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = { 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +static void +outAcc (operand * result) +{ + int size, offset; + size = getDataSize (result); + if (size) { + aopPut (AOP (result), "r0", 0); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) { + aopPut (AOP (result), zero, offset++); + } + } +} + +#endif // End Unused code section + +/*-----------------------------------------------------------------*/ +/* emitcode - writes the code into a file : for now it is simple */ +/*-----------------------------------------------------------------*/ +static void +emitcode (char *inst, char *fmt, ...) +{ + va_list ap; + const char *lbp, *lb; + + va_start (ap, fmt); + + lbp = lb = format_opcode (inst, va_list ap); + + while (isspace ((unsigned char)*lbp)) + lbp++; + + if (lbp && *lbp) + { + lineCurr = (lineCurr ? connectLine (lineCurr, newLineNode (lb)) : (lineHead = newLineNode (lb))); + lineCurr->isInline = _G.inLine; + lineCurr->isDebug = _G.debugLine; + } + + va_end (ap); +} + +/*-----------------------------------------------------------------*/ +/* avr_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +avr_emitDebuggerSymbol (const char * debugSym) +{ + _G.debugLine = 1; + emitcode ("", "%s ==.", debugSym); + _G.debugLine = 0; +} + +/*-----------------------------------------------------------------*/ +/* hasInc - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +static iCode * +hasInc (operand *op, iCode *ic) +{ + sym_link *type = operandType(op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize ; + + if (IS_BITVAR(retype)||!IS_PTR(type)) return NULL; + if (IS_AGGREGATE(type->next)) return NULL; + isize = getSize(type->next); + while (lic) { + /* if operand of the form op = op + */ + if (lic->op == '+' && isOperandEqual(IC_LEFT(lic),op) && + isOperandEqual(IC_RESULT(lic),op) && + isOperandLiteral(IC_RIGHT(lic)) && + operandLitValue(IC_RIGHT(lic)) == isize) { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue(OP_USES(op),lic->key) || (lic->defKey == op->key)) { + return NULL; + } + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* getFreePtr - returns X or Z whichever is free or can be pushed */ +/*-----------------------------------------------------------------*/ +static regs * +getFreePtr (iCode * ic, asmop ** aopp, bool result, bool zonly) +{ + bool xiu = FALSE, ziu = FALSE; + bool xou = FALSE, zou = FALSE; + + /* the logic: if x & z used in the instruction + then we are in trouble otherwise */ + + /* first check if x & z are used by this + instruction, in which case we are in trouble */ + if ((xiu = bitVectBitValue (ic->rUsed, X_IDX)) && + (ziu = bitVectBitValue (ic->rUsed, Z_IDX))) { + goto endOfWorld; + } + + xou = bitVectBitValue (ic->rMask, X_IDX); + zou = bitVectBitValue (ic->rMask, Z_IDX); + + /* if no usage of Z then return it */ + if (!ziu && !zou) { + ic->rUsed = bitVectSetBit (ic->rUsed, Z_IDX); + (*aopp)->type = AOP_Z; + + (*aopp)->aop_ptr2 = avr_regWithIdx (R31_IDX); + return (*aopp)->aopu.aop_ptr = avr_regWithIdx (R30_IDX); + } + + /* if no usage of X then return it */ + if (!xiu && !xou && !zonly) { + ic->rUsed = bitVectSetBit (ic->rUsed, X_IDX); + (*aopp)->type = AOP_X; + + (*aopp)->aop_ptr2 = avr_regWithIdx (R27_IDX); + return (*aopp)->aopu.aop_ptr = avr_regWithIdx (R26_IDX); + } + + /* if z not used then */ + + if (!ziu) { + /* push it if not already pushed */ + if (!_G.zPushed) { + emitcode ("push", "%s", + avr_regWithIdx (R30_IDX)->dname); + emitcode ("push", "%s", + avr_regWithIdx (R31_IDX)->dname); + _G.zPushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, Z_IDX); + (*aopp)->type = AOP_Z; + (*aopp)->aop_ptr2 = avr_regWithIdx (R31_IDX); + return (*aopp)->aopu.aop_ptr = avr_regWithIdx (R30_IDX); + } + + /* now we know they both have usage */ + /* if x not used in this instruction */ + if (!xiu && !zonly) { + /* push it if not already pushed */ + if (!_G.xPushed) { + emitcode ("push", "%s", + avr_regWithIdx (R26_IDX)->dname); + emitcode ("push", "%s", + avr_regWithIdx (R27_IDX)->dname); + _G.xPushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, X_IDX); + (*aopp)->type = AOP_X; + + (*aopp)->aop_ptr2 = avr_regWithIdx (R27_IDX); + return (*aopp)->aopu.aop_ptr = avr_regWithIdx (R26_IDX); + } + + + endOfWorld: + /* I said end of world but not quite end of world yet */ + /* if this is a result then we can push it on the stack */ + if (result) { + (*aopp)->type = AOP_STK; + return NULL; + } + + /* other wise this is true end of the world */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "getFreePtr should never reach here"); + exit (0); +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + + return PTR_TYPE (SPEC_OCLS (etype)); + +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + return sym->aop; + + /* assign depending on the storage class */ + /* if it is on the stack */ + if (sym->onStack) { + sym->aop = aop = newAsmop (0); + aop->size = getSize (sym->type); + + /* we can use std / ldd instruction */ + if (sym->stack > 0 + && (sym->stack + getSize (sym->type) - 1) <= 63) { + aop->type = AOP_STK_D; + aop->aopu.aop_stk = sym->stack; + return aop; + } + + /* otherwise get a free pointer register X/Z */ + aop->aopu.aop_ptr = getFreePtr (ic, &aop, result, FALSE); + + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) { + emitcode ("movw", "%s,r28", aop->aopu.aop_ptr->name); + if (sym->stack < 0) { + if ((sym->stack - _G.nRegsSaved) > -63) { + emitcode ("sbiw", "%s,0x%02x", + aop->aopu.aop_ptr->name, + (sym->stack - + _G.nRegsSaved)); + } + else { + emitcode ("subi", "%s,<(%d)", + aop->aopu.aop_ptr->name, + sym->stack - _G.nRegsSaved); + emitcode ("sbci", "%s,>(%d)", + aop->aop_ptr2->name, + sym->stack - _G.nRegsSaved); + } + } + else { + if (sym->stack <= 63) { + emitcode ("adiw", "%s,0x%02x", + aop->aopu.aop_ptr->name, + sym->stack); + } + else { + emitcode ("subi", "%s,<(-%d)", + aop->aopu.aop_ptr->name, + sym->stack); + emitcode ("sbci", "%s,>(-%d)", + aop->aop_ptr2->name, + sym->stack); + } + } + } + return aop; + } + + /* if in bit space */ + if (IN_BITSPACE (space)) { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd = Safe_calloc (1, strlen (sym->rname) + 1); + strcpy (aop->aopu.aop_immd, sym->rname); + aop->size = FPTRSIZE; + return aop; + } + + /* only remaining is code / eeprom which will need pointer reg */ + /* if it is in code space */ + + sym->aop = aop = newAsmop (0); + + if (IN_CODESPACE (space)) + aop->code = 1; + + aop->aopu.aop_ptr = getFreePtr (ic, &aop, result, aop->code); + aop->size = getSize (sym->type); + emitcode ("ldi", "%s,<(%s)", aop->aopu.aop_ptr->name, sym->rname); + emitcode ("ldi", "%s,>(%s)", aop->aop_ptr2); + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerialzes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int val = 0; + + for (;;) { + if (ic->op == '+') + val += (int) operandLitValue (IC_RIGHT (ic)); + else if (ic->op == '-') + val -= (int) operandLitValue (IC_RIGHT (ic)); + else + break; + + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + + if (val) + sprintf (buffer, "(%s %c 0x%04x)", + OP_SYMBOL (IC_LEFT (ic))->rname, + val >= 0 ? '+' : '-', abs (val) & 0xffff); + else + strcpy (buffer, OP_SYMBOL (IC_LEFT (ic))->rname); + + aop->aopu.aop_immd = Safe_calloc (1, strlen (buffer) + 1); + strcpy (aop->aopu.aop_immd, buffer); + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && + sym1->isspilt != sym2->isspilt) return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + if (strcmp (sym1->rname, sym2->rname) == 0) + return TRUE; + + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && + !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && + !IS_ITEMP (op1) && + sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + if (aop1->type != AOP_REG || aop2->type != AOP_REG) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* isRegPair - for size 2 if this operand has a register pair */ +/*-----------------------------------------------------------------*/ +static int +isRegPair (asmop * aop) +{ + if (!aop || aop->size < 2) + return 0; + if (aop->type == AOP_X || aop->type == AOP_Z) + return 1; + if (aop->type != AOP_REG) + return 0; + if ( ((aop->aopu.aop_reg[1]->rIdx - aop->aopu.aop_reg[0]->rIdx) == 1) && + (aop->aopu.aop_reg[0]->rIdx & 1) == 0) + + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* allHigh - all registers are high registers */ +/*-----------------------------------------------------------------*/ +static int allHigh (asmop * aop) +{ + int i; + + if (aop->type == AOP_X || aop->type == AOP_Z) + return 1; + if (aop->type != AOP_REG) + return 0; + for (i=0; i < aop->size ; i++ ) + if (aop->aopu.aop_reg[i]->rIdx < R16_IDX) return 0; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + return; + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) { + op->aop = OP_SYMBOL (op)->aop; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) { + op->aop = aopForSym (ic, OP_SYMBOL (op), result); + return; + } + + /* this is a temporary : this has + only four choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + + /* if the type is a conditional */ + if (sym->regType & REG_CND) { + aop = op->aop = sym->aop = newAsmop (AOP_CRY); + aop->size = 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) { + + asmop *oldAsmOp = NULL; + + /* rematerialize it NOW */ + if (sym->remat) { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = getSize (sym->type); + return; + } + + if (sym->accuse) { + assert ("ACC_USE cannot happen in AVR\n"); + } + + if (sym->ruonly) { + int i; + aop = op->aop = sym->aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < (int) fAVRReturnSize; i++) + aop->aopu.aop_str[i] = fAVRReturn[i]; + return; + } + + /* else spill location */ + if (sym->usl.spillLoc && getSize(sym->type) != getSize(sym->usl.spillLoc->type)) { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = + aopForSym (ic, sym->usl.spillLoc, result); + if (getSize(sym->type) != getSize(sym->usl.spillLoc->type)) { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + if (aop->freed) + goto dealloc; + + aop->freed = 1; + + /* depending on the asmop type only three cases need work AOP_RO + , AOP_R1 && AOP_STK */ + switch (aop->type) { + case AOP_X: + if (_G.xPushed) { + if (pop) { + emitcode ("pop", "r26"); + emitcode ("pop", "r27"); + _G.xPushed--; + } + } + bitVectUnSetBit (ic->rUsed, X_IDX); + break; + + case AOP_Z: + if (_G.zPushed) { + if (pop) { + emitcode ("pop", "r30"); + emitcode ("pop", "r31"); + _G.zPushed--; + } + } + bitVectUnSetBit (ic->rUsed, Z_IDX); + break; + + case AOP_STK: + { + int sz = aop->size; + int stk = aop->aopu.aop_stk + aop->size; + bitVectUnSetBit (ic->rUsed, X_IDX); + bitVectUnSetBit (ic->rUsed, Z_IDX); + + getFreePtr (ic, &aop, FALSE, 0); + + emitcode ("movw", "%s,r28"); + if (stk) { + if (stk <= 63 && stk > 0) { + emitcode ("adiw", "%s,0x%02x", + aop->aopu.aop_ptr->name, + stk + 1); + } + else { + emitcode ("subi", "%s,<(%d)", + aop->aopu.aop_ptr->name, + -(stk + 1)); + emitcode ("sbci", "%s,>(%d)", + aop->aop_ptr2->name, + -(stk + 1)); + } + } + + while (sz--) { + emitcode ("pop", "r24"); + emitcode ("st", "-%s,r24", + aop->type == AOP_X ? "X" : "Z"); + if (!sz) + break; + } + op->aop = aop; + freeAsmop (op, NULL, ic, TRUE); + if (_G.xPushed) { + emitcode ("pop", "r26"); + emitcode ("pop", "r27"); + _G.xPushed--; + } + + if (_G.zPushed) { + emitcode ("pop", "r30"); + emitcode ("pop", "r31"); + _G.zPushed--; + } + } + } + + dealloc: + /* all other cases just dealloc */ + if (op) { + op->aop = NULL; + if (IS_SYMOP (op)) { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*-----------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-----------------------------------------------------------------*/ +static char * +aopGet (asmop * aop, int offset) +{ + char *s = buffer; + char *rs; + + /* offset is greater than + size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + return zero; + + /* depending on type */ + switch (aop->type) { + + case AOP_X: + if (offset > aop->coff) { + emitcode ("adiw", "%s,%d", aop->aopu.aop_ptr->name, + offset - aop->coff); + } + + if (offset < aop->coff) { + emitcode ("sbiw", "%s,%d", aop->aopu.aop_ptr->name, + aop->coff - offset); + } + + aop->coff = offset; + emitcode ("ld", "%s,x", + (rs = ((offset & 1) ? "r25" : "r24"))); + return rs; + + case AOP_Z: + if (aop->code) { + if (offset > aop->coff) { + emitcode ("adiw", "r30,%d", + offset - aop->coff); + } + else { + emitcode ("sbiw", "r30,%d", + aop->coff - offset); + } + emitcode ("lpm", "%s,z", + (rs = ((offset & 1) ? "r25" : "r24"))); + } + else { + /* we can use lds */ + if (offset > aop->coff) { + emitcode ("ldd", "%s,z+%d", + (rs = + ((offset & 1) ? "r25" : "r24")), + offset - aop->coff); + } + else { + emitcode ("sbiw", "%s,%d", + aop->aopu.aop_ptr->name, + aop->coff - offset); + aop->coff = offset; + emitcode ("ld", "%s,z", + (rs = + ((offset & 1) ? "r25" : "r24"))); + } + } + return rs; + + case AOP_IMMD: + + emitcode ("lds", "%s,(%s)+%d", + (rs = ((offset & 1) ? "r25" : "r24")), + aop->aopu.aop_immd, offset); + return rs; + + case AOP_DIR: + emitcode ("lds", "%s,(%s)+%d", + (rs = ((offset & 1) ? "r25" : "r24")), + aop->aopu.aop_dir, offset); + return rs; + + case AOP_REG: + return aop->aopu.aop_reg[offset]->name; + + case AOP_CRY: + assert ("cannot be in bit space AOP_CRY\n"); + break; + + case AOP_LIT: + s = aopLiteral (aop->aopu.aop_lit, offset); + emitcode ("ldi", "%s,<(%s)", + (rs = ((offset & 1) ? "r24" : "r25")), s); + return rs; + + case AOP_STR: + aop->coff = offset; + return aop->aopu.aop_str[offset]; + + case AOP_STK_D: + emitcode ("ldd", "%s,Y+%d", + (rs = ((offset & 1) ? "r25" : "r24")), + aop->aopu.aop_stk + offset); + return rs; + } + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "aopget got unsupported aop->type"); + exit (0); +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop */ +/*-----------------------------------------------------------------*/ +static void +aopPut (asmop * aop, char *s, int offset) +{ + char *d = buffer; + + if (aop->size && offset > (aop->size - 1)) { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "aopPut got offset > aop->size"); + exit (0); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) { + case AOP_DIR: + if (offset) { + sprintf (d, "(%s)+%d", aop->aopu.aop_dir, offset); + } + else { + sprintf (d, "%s", aop->aopu.aop_dir); + } + + emitcode ("sts", "%s,%s", d, s); + break; + + case AOP_REG: + if (toupper ((unsigned char)*s) != 'R') { + if (s == zero) { + emitcode ("clr", "%s", + aop->aopu.aop_reg[offset]->name); + } + else { + emitcode ("ldi", "r25,%s", s); + emitcode ("mov", "%s,r35", + aop->aopu.aop_reg[offset]->name); + } + } + else { + if (strcmp (aop->aopu.aop_reg[offset]->name, s)) { + emitcode ("mov", "%s,%s", + aop->aopu.aop_reg[offset]->name, s); + } + } + break; + + case AOP_X: + if (offset > aop->coff) { + emitcode ("adiw", "%s,%d", aop->aopu.aop_ptr->name, + offset - aop->coff); + } + + if (offset < aop->coff) { + emitcode ("sbiw", "%s,%d", aop->aopu.aop_ptr->name, + aop->coff - offset); + } + + aop->coff = offset; + emitcode ("st", "x,%s", s); + break; + + case AOP_Z: + if (aop->code) { + if (offset > aop->coff) { + emitcode ("adiw", "r30,%d", + offset - aop->coff); + } + else { + emitcode ("sbiw", "r30,%d", + aop->coff - offset); + } + emitcode ("lpm", "%s,z", s); + } + else { + /* we can use lds */ + if (offset > aop->coff) { + emitcode ("sdd", "z+%d,%s", + offset - aop->coff, s); + } + else { + emitcode ("sbiw", "%s,%d", + aop->aopu.aop_ptr->name, + aop->coff - offset); + aop->coff = offset; + emitcode ("ld", "%s,z", s); + } + } + break; + + case AOP_STK: + emitcode ("push", "%s", s); + break; + + case AOP_CRY: + /* if used only for a condition code check */ + assert (toupper ((unsigned char)*s) == 'R'); + if (offset == 0) { + emitcode ("xrl", "r0,r0"); + emitcode ("cpi", "%s,0", s); + } + else { + emitcode ("cpc", "r0,%s", s); + } + break; + + case AOP_STR: + aop->coff = offset; + if (strcmp (aop->aopu.aop_str[offset], s)) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], + s); + break; + + case AOP_STK_D: + emitcode ("std", "y+%d,%s", offset, s); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "aopPut got unsupported aop->type"); + exit (0); + } + +} + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define IS_AOP_PREG(x) (AOP(x) && (AOP_TYPE(x) == AOP_X || \ + AOP_TYPE(x) == AOP_Z)) +#define AOP_INPREG(x) (x && (x->type == AOP_REG && \ + ((x->aopu.aop_reg[0] == avr_regWithIdx(R26_IDX) && x->aopu.aop_reg[1] == avr_regWithIdx(R27_IDX)) || \ + (x->aopu.aop_reg[0] == avr_regWithIdx(R30_IDX) && x->aopu.aop_reg[1] == avr_regWithIdx(R31_IDX)) ))) +#define AOP_ISX(x) (x && (x->type == AOP_REG && \ + ((x->aopu.aop_reg[0] == avr_regWithIdx(R26_IDX) && x->aopu.aop_reg[1] == avr_regWithIdx(R27_IDX))))) +#define AOP_ISZ(x) (x && (x->type == AOP_REG && \ + ((x->aopu.aop_reg[0] == avr_regWithIdx(R30_IDX) && x->aopu.aop_reg[1] == avr_regWithIdx(R31_IDX))))) + +/*-----------------------------------------------------------------*/ +/* genNotFloat - generates not for float operations */ +/*-----------------------------------------------------------------*/ +static void +genNotFloat (operand * op, operand * res) +{ + int size, offset; + char *l; + symbol *tlbl; + + /* we will put 127 in the first byte of + the result */ + aopPut (AOP (res), "127", 0); + size = AOP_SIZE (op) - 1; + offset = 1; + + l = aopGet (op->aop, offset++); + MOVR0 (l); + + while (size--) { + emitcode ("or", "R0,%s", aopGet (op->aop, offset++)); + } + tlbl = newiTempLabel (NULL); + + tlbl = newiTempLabel (NULL); + aopPut (res->aop, zero, 1); + emitcode ("cpi", "r0,0"); + emitcode ("breq", "L%05d", tlbl->key); + aopPut (res->aop, one, 1); + emitcode ("", "L%05d:", tlbl->key); + + size = res->aop->size - 2; + offset = 2; + /* put zeros in the rest */ + while (size--) + aopPut (res->aop, zero, offset++); +} + +/*-----------------------------------------------------------------*/ +/* opIsGptr: returns non-zero if the passed operand is */ +/* a generic pointer type. */ +/*-----------------------------------------------------------------*/ +static int +opIsGptr (operand * op) +{ + sym_link *type = operandType (op); + + if ((AOP_SIZE (op) == GPTRSIZE) && IS_GENPTR (type)) { + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size; + size = AOP_SIZE (op); + if (size == GPTRSIZE) { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + } + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +toBoolean (operand * oper, char *r, bool clr) +{ + int size = AOP_SIZE (oper); + int offset = 0; + if (clr) { + emitcode ("clr", "%s", r); + while (size--) + emitcode ("or", "%s,%s", r, aopGet (AOP (oper), offset++)); + } else { + size--; + emitcode("mov","%s,%s",r,aopGet (AOP (oper), offset++)); + if (size) while (size--) emitcode ("or", "%s,%s", r, aopGet (AOP (oper), offset++)); + } +} + + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + symbol *tlbl; + sym_link *optype = operandType (IC_LEFT (ic)); + int size, offset = 1; + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if type float then do float */ + if (IS_FLOAT (optype)) { + genNotFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + emitcode ("clr", "r24"); + tlbl = newiTempLabel (NULL); + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + if (size == 1) { + emitcode ("cpse", "%s,r24", aopGet (AOP (IC_LEFT (ic)), 0)); + } + else { + while (size--) { + if (offset) + emitcode ("cpc", "%s,r24", + aopGet (AOP (IC_LEFT (ic)), + offset)); + else + emitcode ("cpi", "%s,0", + aopGet (AOP (IC_LEFT (ic)), + offset)); + offset++; + } + emitcode ("bne", "L%05d", tlbl->key); + } + emitcode ("ldi", "r24,1"); + emitcode ("", "L%05d:", tlbl->key); + aopPut (AOP (IC_RESULT (ic)), "r24", 0); + size = AOP_SIZE (IC_RESULT (ic)) - 1; + offset = 1; + while (size--) + aopPut (AOP (IC_RESULT (ic)), zero, offset++); + + + release: + /* release the aops */ + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + int samer; + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + samer = sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))); + size = AOP_SIZE (IC_RESULT (ic)); + while (size--) { + char *l = aopGet (AOP (IC_LEFT (ic)), offset); + if (samer) { + emitcode ("com", "%s", l); + } + else { + aopPut (AOP (IC_RESULT (ic)), l, offset); + emitcode ("com", "%s", + aopGet (AOP (IC_RESULT (ic)), offset)); + } + offset++; + } + + /* release the aops */ + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + char *l; + /* for this we just need to flip the + first it then copy the rest in place */ + size = AOP_SIZE (op) - 1; + l = aopGet (AOP (op), 3); + + emitcode ("ldi", "r24,0x80"); + if (sameRegs (AOP (op), AOP (result))) { + emitcode ("eor", "%s,r24", l); + } + else { + aopPut (AOP (result), l, 3); + emitcode ("eor", "%s,r24", aopGet (AOP (result), 3)); + } + while (size--) { + aopPut (AOP (result), aopGet (AOP (op), offset), offset); + offset++; + } +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype, *rtype; + int samer; + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + optype = operandType (IC_LEFT (ic)); + rtype = operandType (IC_RESULT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero */ + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + samer = sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))); + if (size == 1) { + if (samer) { + emitcode ("neg", "%s", + aopGet (AOP (IC_LEFT (ic)), 0)); + } + else { + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_LEFT (ic)), 0), 0); + emitcode ("neg", "%s", + aopGet (AOP (IC_RESULT (ic)), 0)); + } + } + else { + offset = size - 1; + while (size--) { + char *l = aopGet (AOP (IC_LEFT (ic)), offset); + if (!samer) { + aopPut (AOP (IC_RESULT (ic)), l, offset); + l = aopGet (AOP (IC_RESULT (ic)), offset); + } + if (offset) + emitcode ("com", "%s", l); + else + emitcode ("neg", "%s", l); + offset--; + } + size = AOP_SIZE (IC_LEFT (ic)) - 1; + offset = 1; + while (size--) { + emitcode ("sbci", "%s,0xff", + aopGet (AOP (IC_RESULT (ic)), offset++)); + } + } + + /* if any remaining bytes in the result */ + /* we just need to propagate the sign */ + if ((size = (AOP_SIZE (IC_RESULT (ic)) - AOP_SIZE (IC_LEFT (ic))))) { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("clr", "r0"); + emitcode ("brcc", "L%05d", tlbl->key); + emitcode ("com", "r0"); + emitcode ("", "L%05d:", tlbl->key); + while (size--) + aopPut (AOP (IC_RESULT (ic)), "r0", offset++); + } + + release: + /* release the aops */ + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - */ +/*-----------------------------------------------------------------*/ +static void +assignResultValue (operand * oper) +{ + int offset = 0; + int size = AOP_SIZE (oper); + while (size--) { + aopPut (AOP (oper), fAVRReturn[offset], offset); + offset++; + } +} + +/*-----------------------------------------------------------------*/ +/* saveZreg - if indirect call then save z-pointer register */ +/*-----------------------------------------------------------------*/ +static void +saveZreg (iCode * ic) +{ + /* only if live accross this call */ + if (ic->regsSaved == 0 && + (bitVectBitValue (ic->rMask, R30_IDX) || + bitVectBitValue (ic->rMask, R31_IDX))) { + ic->regsSaved = 1; + emitcode ("push", "r30"); + emitcode ("push", "r31"); + } +} + +/*-----------------------------------------------------------------*/ +/* popZreg - restore values of zreg */ +/*-----------------------------------------------------------------*/ +static void +popZreg (iCode * ic) +{ + if (ic->regsSaved) { + emitcode ("pop", "r31"); + emitcode ("pop", "r30"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIpush - genrate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + char *l; + + + if (!ic->parmPush) { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + } + else { + iCode *lic; + for (lic = ic->next; lic; lic = lic->next) + if (lic->op == PCALL) + break; + if (lic) + saveZreg (lic); + } + + /* this is a paramter push */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + while (size--) { + l = aopGet (AOP (IC_LEFT (ic)), offset++); + emitcode ("push", "%s", l); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = (size - 1); + while (size--) + emitcode ("pop", "%s", aopGet (AOP (IC_LEFT (ic)), offset--)); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + + /* if send set is not empty then assign */ + if (_G.sendSet) { + iCode *sic; + int rnum = 16; + for (sic = setFirstItem (_G.sendSet); sic; + sic = setNextItem (_G.sendSet)) { + int size, offset = 0; + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + while (size--) { + char *l = + aopGet (AOP (IC_LEFT (sic)), offset); + char *b = buffer; + sprintf (buffer, "r%d", rnum++); + if (strcmp (l, b)) + emitcode ("mov", "%s,%s", b, l); + offset++; + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + _G.sendSet = NULL; + } + /* make the call */ + emitcode ("call", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : + OP_SYMBOL (IC_LEFT (ic))->name)); + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->spildir)) || + IS_TRUE_SYMOP (IC_RESULT (ic))) { + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) { + if (ic->parmBytes > 63) { + emitcode ("sbiw", "r28,%d", ic->parmBytes); + } + else { + emitcode ("subi", "r28,<(%d)", + ic->parmBytes); + emitcode ("sbci", "r29,>(%d)", + ic->parmBytes); + } + } + +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + + if (!ic->regsSaved) + saveZreg (ic); + + aopOp (IC_LEFT (ic), ic, FALSE); + emitcode ("mov", "r30", aopGet (AOP (IC_LEFT (ic)), 0)); + emitcode ("mov", "r31", aopGet (AOP (IC_RIGHT (ic)), 0)); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + /* if send set is not empty the assign */ + if (_G.sendSet) { + iCode *sic; + int rnum = 16; + for (sic = setFirstItem (_G.sendSet); sic; + sic = setNextItem (_G.sendSet)) { + int size, offset = 0; + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + while (size--) { + char *l = + aopGet (AOP (IC_LEFT (sic)), offset); + char *b = buffer; + sprintf (b, "r%d", rnum++); + if (strcmp (l, b)) + emitcode ("mov", "%s,%s", b, l); + offset++; + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + _G.sendSet = NULL; + } + + emitcode ("icall", ""); + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->spildir)) || + IS_TRUE_SYMOP (IC_RESULT (ic))) { + + aopOp (IC_RESULT (ic), ic, FALSE); + + assignResultValue (IC_RESULT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if + required */ + if (ic->parmBytes) { + int i; + if (ic->parmBytes > 3) { + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", + (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + } + else + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) { + if (ic->parmBytes > 63) { + emitcode ("sbiw", "r28,%d", ic->parmBytes); + } + else { + emitcode ("subi", "r28,<(%d)", + ic->parmBytes); + emitcode ("sbci", "r29,>(%d)", + ic->parmBytes); + } + } + if (ic->regsSaved) + popZreg (ic); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym; + sym_link *ftype; + int i = 0; + + _G.nRegsSaved = 0; + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", + (sym = OP_SYMBOL (IC_LEFT (ic)))->name); + emitcode (";", "-----------------------------------------"); + + emitcode ("", "%s:", sym->rname); + ftype = operandType (IC_LEFT (ic)); + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + emitcode ("cli", ""); + + if (IFFUNC_ISISR (sym->type)) { + } + + /* save the preserved registers that are used in this function */ + for (i = R2_IDX; i <= R15_IDX; i++) { + if (bitVectBitValue (sym->regsUsed, i)) { + _G.nRegsSaved++; + emitcode ("push", "%s", avr_regWithIdx (i)->name); + } + } + /* now for the pointer registers */ + if (bitVectBitValue (sym->regsUsed, R26_IDX)) { + _G.nRegsSaved++; + emitcode ("push", "r26"); + } + if (bitVectBitValue (sym->regsUsed, R27_IDX)) { + _G.nRegsSaved++; + emitcode ("push", "r27"); + } + if (bitVectBitValue (sym->regsUsed, R30_IDX)) { + _G.nRegsSaved++; + emitcode ("push", "r30"); + } + if (bitVectBitValue (sym->regsUsed, R31_IDX)) { + _G.nRegsSaved++; + emitcode ("push", "r31"); + } + /* adjust the stack for the function */ + if (sym->stack) { + emitcode ("push", "r28"); + emitcode ("push", "r29"); + emitcode ("in", "r28,__SP_L__"); + emitcode ("in", "r29,__SP_H__"); + if (sym->stack <= 63) { + emitcode ("sbiw", "r28,%d", sym->stack); + } + else { + emitcode ("subi", "r28,<(%d)", sym->stack); + emitcode ("sbci", "r29,>(%d)", sym->stack); + } + emitcode ("out", "__SP_L__,r28"); + emitcode ("out", "__SP_H__,r29"); + } +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int i; + + /* restore stack pointer */ + if (sym->stack) { + if (sym->stack <= 63) { + emitcode ("adiw", "r28,%d", sym->stack); + } + else { + emitcode ("subi", "r28,<(-%d)", sym->stack); + emitcode ("sbci", "r29,>(-%d)", sym->stack); + } + emitcode ("out", "__SP_L__,r28"); + emitcode ("out", "__SP_H__,r29"); + + /* pop frame pointer */ + emitcode ("pop", "r29"); + emitcode ("pop", "r28"); + } + /* restore preserved registers */ + if (bitVectBitValue (sym->regsUsed, R31_IDX)) { + _G.nRegsSaved--; + emitcode ("pop", "r31"); + } + if (bitVectBitValue (sym->regsUsed, R30_IDX)) { + _G.nRegsSaved--; + emitcode ("pop", "r30"); + } + if (bitVectBitValue (sym->regsUsed, R27_IDX)) { + _G.nRegsSaved--; + emitcode ("pop", "r27"); + } + if (bitVectBitValue (sym->regsUsed, R26_IDX)) { + _G.nRegsSaved--; + emitcode ("pop", "r26"); + } + for (i = R15_IDX; i >= R2_IDX; i--) { + if (bitVectBitValue (sym->regsUsed, i)) { + _G.nRegsSaved--; + emitcode ("pop", "%s", avr_regWithIdx (i)->name); + } + } + + if (IFFUNC_ISCRITICAL (sym->type)) + emitcode ("sti", ""); + + if (options.debug && currFunc) { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (IFFUNC_ISISR (sym->type)) { + emitcode ("rti", ""); + } + else { + emitcode ("ret", ""); + } + +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0; + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + while (size--) { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) { + emitcode ("ldi", "%s,%s(%d)", fAVRReturn[offset], + larray[offset], + (int) ulFromVal (AOP (IC_LEFT (ic))-> + aopu.aop_lit), offset); + } + else { + char *l; + l = aopGet (AOP (IC_LEFT (ic)), offset); + if (strcmp (fAVRReturn[offset], l)) + emitcode ("mov", "%s,%s", fAVRReturn[offset], + l); + } + offset++; + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && + IC_LABEL (ic->next) == returnLabel)) + + emitcode ("rjmp", "L%05d", returnLabel->key); + +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + emitcode ("", "L%05d:", IC_LABEL (ic)->key); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + emitcode ("rjmp", "L%05d", (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode * ic) +{ + unsigned int icount; + int offset = 0; + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu. + aop_lit); + + /* if the sizes are greater than 2 or they are not the same regs + then we cannot */ + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + return FALSE; + + /* so we know LEFT & RESULT in the same registers and add + amount <= 63 */ + /* for short & char types */ + if (AOP_SIZE (IC_RESULT (ic)) < 2) { + if (icount == 1) { + emitcode ("inc", "%s", + aopGet (AOP (IC_LEFT (ic)), 0)); + return TRUE; + } + if (AOP_ISHIGHREG( AOP (IC_LEFT (ic)),0)) { + emitcode ("subi", "%s,<(%d)", + aopGet (AOP (IC_LEFT (ic)), 0), 0-icount); + return TRUE; + } + } + + for (offset = 0 ; offset < AOP_SIZE(IC_RESULT(ic)) ; offset++) { + if (!(AOP_ISHIGHREG(AOP(IC_RESULT(ic)),offset))) return FALSE; + } + + if (AOP_SIZE (IC_RESULT (ic)) <= 3) { + /* if register pair and starts with 26/30 then adiw */ + if (isRegPair (AOP (IC_RESULT (ic))) && icount > 0 + && icount < 64 + && (IS_REGIDX (AOP (IC_RESULT (ic)), R26_IDX) || + IS_REGIDX (AOP (IC_RESULT (ic)), R24_IDX) || + IS_REGIDX (AOP (IC_RESULT (ic)), R30_IDX))) { + emitcode ("adiw", "%s,%d", + aopGet (AOP (IC_RESULT (ic)), 0), icount); + return TRUE; + } + + /* use subi */ + emitcode ("subi", "%s,<(%d)", + aopGet (AOP (IC_RESULT (ic)), 0), 0-icount); + emitcode ("sbci", "%s,>(%d)", + aopGet (AOP (IC_RESULT (ic)), 1), 0-icount); + return TRUE; + } + + /* for 32 bit longs */ + emitcode ("subi", "%s,<(%d)", aopGet (AOP (IC_RESULT (ic)), 0), + 0-icount); + emitcode ("sbci", "%s,>(%d)", aopGet (AOP (IC_RESULT (ic)), 1), + 0-icount); + emitcode ("sbci", "%s,hlo8(%d)", aopGet (AOP (IC_RESULT (ic)), 2), + 0-icount); + emitcode ("sbci", "%s,hhi8(%d)", aopGet (AOP (IC_RESULT (ic)), 3), + 0-icount); + return TRUE; + +} + +/* This is the pure and virtuous version of this code. + * I'm pretty certain it's right, but not enough to toss the old + * code just yet... + */ +static void +adjustArithmeticResult (iCode * ic) +{ + if (opIsGptr (IC_RESULT (ic)) && + opIsGptr (IC_LEFT (ic)) && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) { + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_LEFT (ic)), GPTRSIZE - 1), + GPTRSIZE - 1); + } + + if (opIsGptr (IC_RESULT (ic)) && + opIsGptr (IC_RIGHT (ic)) && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) { + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_RIGHT (ic)), GPTRSIZE - 1), + GPTRSIZE - 1); + } + + if (opIsGptr (IC_RESULT (ic)) && + AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && + AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))) && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) { + char buffer[5]; + sprintf (buffer, "%d", + pointerCode (getSpec (operandType (IC_LEFT (ic))))); + aopPut (AOP (IC_RESULT (ic)), buffer, GPTRSIZE - 1); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + int samer; + char *l; + + /* special cases :- */ + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + samer = sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))); + + while (size--) { + if (!samer) + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_LEFT (ic)), offset), offset); + + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) { + + if (offset == 0) + l = "add"; + else + l = "adc"; + + emitcode (l, "%s,%s", + aopGet (AOP (IC_RESULT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + else { + if (AOP_ISHIGHREG( AOP( IC_RESULT(ic)),offset)) { + if (offset == 0) + l = "subi"; + else + l = "sbci"; + + emitcode (l, "%s,%s(-%d)", + aopGet (AOP (IC_RESULT (ic)), offset), + larray[offset], + (int) ulFromVal (AOP (IC_RIGHT (ic))-> + aopu.aop_lit)); + } else { + if (offset == 0) + l = "add"; + else + l = "adc"; + + emitcode (l, "%s,%s", + aopGet (AOP (IC_RESULT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + } + offset++; + } + + adjustArithmeticResult (ic); + + release: + freeAsmop (IC_LEFT (ic), NULL, ic, + (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RIGHT (ic), NULL, ic, + (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with deccrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + unsigned int icount; + int offset ; + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = + (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu. + aop_lit); + + /* if the sizes are greater than 2 or they are not the same regs + then we cannot */ + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + return FALSE; + + /* so we know LEFT & RESULT in the same registers and add + amount <= 63 */ + /* for short & char types */ + if (AOP_SIZE (IC_RESULT (ic)) < 2) { + if (icount == 1) { + emitcode ("dec", "%s", + aopGet (AOP (IC_LEFT (ic)), 0)); + return TRUE; + } + if (AOP_ISHIGHREG( AOP ( IC_LEFT(ic)),0)) { + emitcode ("subi", "%s,<(%d)", + aopGet (AOP (IC_LEFT (ic)), 0), icount); + return TRUE; + } + } + + for (offset = 0 ; offset < AOP_SIZE(IC_RESULT(ic)) ; offset++) { + if (!(AOP_ISHIGHREG(AOP(IC_RESULT(ic)),offset))) return FALSE; + } + + if (AOP_SIZE (IC_RESULT (ic)) <= 3) { + /* if register pair and starts with 26/30 then adiw */ + if (isRegPair (AOP (IC_RESULT (ic))) && icount > 0 + && icount < 64 + && (IS_REGIDX (AOP (IC_RESULT (ic)), R26_IDX) || + IS_REGIDX (AOP (IC_RESULT (ic)), R24_IDX) || + IS_REGIDX (AOP (IC_RESULT (ic)), R30_IDX))) { + emitcode ("sbiw", "%s,%d", + aopGet (AOP (IC_RESULT (ic)), 0), icount); + return TRUE; + } + + /* use subi */ + emitcode ("subi", "%s,<(%d)", + aopGet (AOP (IC_RESULT (ic)), 0), icount); + emitcode ("sbci", "%s,>(%d)", + aopGet (AOP (IC_RESULT (ic)), 1), icount); + return TRUE; + } + /* for 32 bit longs */ + emitcode ("subi", "%s,<(%d)", aopGet (AOP (IC_RESULT (ic)), 0), + icount); + emitcode ("sbci", "%s,>(%d)", aopGet (AOP (IC_RESULT (ic)), 1), + icount); + emitcode ("sbci", "%s,hlo8(%d)", aopGet (AOP (IC_RESULT (ic)), 2), + icount); + emitcode ("sbci", "%s,hhi8(%d)", aopGet (AOP (IC_RESULT (ic)), 3), + icount); + return TRUE; + +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + int size, offset = 0, samer; + char *l; + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if I can do an decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + samer = sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))); + while (size--) { + if (!samer) + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_LEFT (ic)), offset), offset); + + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) { + + if (offset == 0) + l = "sub"; + else + l = "sbc"; + + emitcode (l, "%s,%s", + aopGet (AOP (IC_RESULT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + else { + if (AOP_ISHIGHREG(AOP (IC_RESULT (ic)),offset)) { + if (offset == 0) + l = "subi"; + else + l = "sbci"; + + emitcode (l, "%s,%s(%d)", + aopGet (AOP (IC_RESULT (ic)), offset), + larray[offset], + (int) ulFromVal (AOP (IC_RIGHT (ic))-> + aopu.aop_lit)); + } else { + if (offset == 0) + l = "sub"; + else + l = "sbc"; + + emitcode (l, "%s,%s", + aopGet (AOP (IC_RESULT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + } + offset++; + } + + adjustArithmeticResult (ic); + + release: + freeAsmop (IC_LEFT (ic), NULL, ic, + (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RIGHT (ic), NULL, ic, + (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8 bit multiplication & division */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + sym_link *opetype = operandType (result); + symbol *lbl; + int size, offset; + + /* (if two literals, the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) { + operand *t = right; + right = left; + left = t; + } + + size = AOP_SIZE (result); + + if (SPEC_USIGN (opetype)) { + emitcode ("mul", "%s,%s", aopGet (AOP (left), 0), + aopGet (AOP (right), 0)); + } + else { + emitcode ("muls", "%s,%s", aopGet (AOP (left), 0), + aopGet (AOP (right), 0)); + } + aopPut (AOP (result), "r0", 0); + if (size > 1) { + aopPut (AOP (result), "r1", 1); + offset = 2; + size -= 2; + if (SPEC_USIGN (opetype)) { + while (size--) { + aopPut (AOP (result), zero, offset++); + } + } + else { + if (size) { + lbl = newiTempLabel (NULL); + emitcode ("ldi", "r24,0"); + emitcode ("brcc", "L%05d", lbl->key); + emitcode ("ldi", "r24,0xff)"); + emitcode ("", "L%05d:", lbl->key); + while (size--) + aopPut (AOP (result), "r24", + offset++); + } + } + } + return; +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + + release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + /* should have been converted to function call */ + assert (0); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + /* should have been converted to function call */ + assert (0); + +} + +enum { + AVR_EQ = 0, + AVR_NE, + AVR_LT, + AVR_GE +}; + +/*-----------------------------------------------------------------*/ +/* revavrcnd - reverse a conditional for avr */ +/*-----------------------------------------------------------------*/ +static int +revavrcnd (int type) +{ + static struct { + int type, rtype; + } rar[] = { + { + AVR_EQ, AVR_NE} + , { + AVR_LT, AVR_GE} + }; + int i; + + for (i = 0; i < (sizeof (rar) / sizeof (rar[0])); i++) { + if (rar[i].type == type) + return rar[i].rtype; + if (rar[i].rtype == type) + return rar[i].type; + } + assert (0); /* cannot happen */ + return 0; /* makes the compiler happy */ +} + +static char *br_name[4] = { "breq", "brne", "brlt", "brge" }; +static char *br_uname[4] = { "breq", "brne", "brlo", "brcc" }; + +/*-----------------------------------------------------------------*/ +/* genBranch - generate the branch instruction */ +/*-----------------------------------------------------------------*/ +static void +genBranch (iCode * ifx, int br_type, int sign) +{ + int tj = (IC_TRUE (ifx) ? 1 : 0); + + if (tj) { /* if true jump */ + char *nm = (sign ? br_name[br_type] : br_uname[br_type]); + emitcode (nm, "L%05d", IC_TRUE (ifx)->key); + } + else { /* if false jump */ + int rtype = revavrcnd (br_type); + char *nm = (sign ? br_name[rtype] : br_uname[rtype]); + emitcode (nm, "L%05d", IC_FALSE (ifx)->key); + } + ifx->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genCmp - compare & jump */ +/*-----------------------------------------------------------------*/ +static void +genCmp (iCode * ic, iCode * ifx, int br_type) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + symbol *lbl; + int sign, size, offset = 0; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + size = AOP_SIZE (left); + + if (ifx) { + if (size == 1) { + if (AOP_TYPE (right) == AOP_LIT) { + emitcode ("cpi", "%s,<(%d)", + aopGet (AOP (left), 0), + (int) ulFromVal (AOP (IC_RIGHT (ic))-> + aopu.aop_lit)); + genBranch (ifx, br_type, sign); + } + else { /* right != literal */ + emitcode ("cp", "%s,%s", + aopGet (AOP (left), 0), + aopGet (AOP (right), 0)); + genBranch (ifx, br_type, sign); + } + } + else { /* size != 1 */ + while (size--) { + if (offset == 0) + emitcode ("cp", "%s,%s", + aopGet (AOP (left), 0), + aopGet (AOP (right), 0)); + else + emitcode ("cpc", "%s,%s", + aopGet (AOP (left), offset), + aopGet (AOP (right), + offset)); + offset++; + } + genBranch (ifx, br_type, sign); + } + } + else { /* no ifx */ + emitcode ("clr", "r0"); + while (size--) { + if (offset == 0) + emitcode ("cp", "%s,%s", + aopGet (AOP (left), 0), + aopGet (AOP (right), 0)); + else + emitcode ("cpc", "%s,%s", + aopGet (AOP (left), offset), + aopGet (AOP (right), offset)); + offset++; + } + lbl = newiTempLabel (NULL); + br_type = revavrcnd (br_type); + if (sign) + emitcode (br_uname[br_type], "L%05d", lbl->key); + else + emitcode (br_name[br_type], "L%05d", lbl->key); + emitcode ("inc", "r0"); + emitcode ("", "L%05d:", lbl->key); + aopPut (AOP (result), "r0", 0); + size = AOP_SIZE (result) - 1; + offset = 1; + while (size--) + aopPut (AOP (result), zero, offset++); + } + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + /* should have transformed by the parser */ + assert (0); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + genCmp (ic, ifx, AVR_LT); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + genCmp (ic, ifx, AVR_EQ); +} + +/*-----------------------------------------------------------------*/ +/* genCmpNe - generates code for not equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpNe (iCode * ic, iCode * ifx) +{ + genCmp (ic, ifx, AVR_NE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpGe - generates code for greater than equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpGe (iCode * ic, iCode * ifx) +{ + genCmp (ic, ifx, AVR_GE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLe - generates code for less than equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpLe (iCode * ic, iCode * ifx) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + IC_RIGHT (ic) = left; + IC_LEFT (ic) = right; + genCmp (ic, ifx, AVR_GE); +} + +/*-----------------------------------------------------------------*/ +/* ifxForOp - returns the icode containing the ifx for operand */ +/*-----------------------------------------------------------------*/ +static iCode * +ifxForOp (operand * op, iCode * ic) +{ + /* if true symbol then needs to be assigned */ + if (IS_TRUE_SYMOP (op)) + return NULL; + + /* if this has register type condition and + the next instruction is ifx with the same operand + and live to of the operand is upto the ifx only then */ + if (ic->next && + ic->next->op == IFX && + IC_COND (ic->next)->key == op->key && + OP_SYMBOL (op)->liveTo <= ic->next->seq) return ic->next; + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + int size, offset; + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + tlbl = newiTempLabel (NULL); + toBoolean (left, "r0", TRUE); + toBoolean (right, "r1", TRUE); + emitcode ("and", "r0,r1"); + emitcode ("ldi", "r24,1"); + emitcode ("breq", "L%05d", tlbl->key); + emitcode ("dec", "r24"); + emitcode ("", "L%05d:", tlbl->key); + aopPut (AOP (result), "r24", 0); + size = AOP_SIZE (result) - 1; + offset = 1; + while (size--) + aopPut (AOP (result), zero, offset++); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + int size, offset; + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + tlbl = newiTempLabel (NULL); + toBoolean (left, "r0", TRUE); + toBoolean (right, "r0", FALSE); + emitcode ("ldi", "r24,1"); + emitcode ("breq", "L%05d", tlbl->key); + emitcode ("dec", "r24"); + emitcode ("", "L%05d:", tlbl->key); + aopPut (AOP (result), "r24", 0); + size = AOP_SIZE (result) - 1; + offset = 1; + while (size--) + aopPut (AOP (result), zero, offset++); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +enum { + AVR_AND = 0, AVR_OR, AVR_XOR +}; +static char *bopnames_lit[] = { "andi", "ori" }; +static char *bopnames[] = { "and", "or", "eor" }; +/*-----------------------------------------------------------------*/ +/* genBitWise - generate bitwise operations */ +/*-----------------------------------------------------------------*/ +static void +genBitWise (iCode * ic, iCode * ifx, int bitop) +{ + operand *left, *right, *result; + int size, offset = 0; + char *l; + symbol *lbl, *lbl1; + int samerl, samerr; + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + size = AOP_SIZE (left); + offset = 0; + if (ifx) { /* used only for jumps */ + if (AOP_TYPE (right) == AOP_LIT && + (bitop == AVR_AND || bitop == AVR_OR)) { + int lit = + (int) ulFromVal (AOP (right)->aopu. + aop_lit); + int p2 = powof2 (lit); + if (bitop == AVR_AND && (p2 >= 0)) { /* right side is a power of 2 */ + l = aopGet (AOP (left), p2 / 8); + if (IC_TRUE (ifx)) { + emitcode ("sbrc", "%s,%d", l, + (p2 % 8)); + emitcode ("rjmp", "L%05d", + IC_TRUE (ifx)->key); + } + else { + emitcode ("sbrs", "%s,%d", l, + (p2 % 8)); + emitcode ("rjmp", "L%05d", + IC_FALSE (ifx)->key); + } + } + else { /* right not power of two */ + int eh = OP_SYMBOL (left)->liveTo <= ic->seq; + if (size == 1) { + if (eh && AOP_ISHIGHREG(AOP(IC_LEFT(ic)),0)) { + emitcode (bopnames_lit[bitop], + "%s,<(%d)", + aopGet (AOP (IC_LEFT (ic)), 0), lit); + } + else { + MOVR24 (aopGet (AOP (IC_LEFT (ic)), 0)); + emitcode (bopnames_lit[bitop], "r24,<(%d)", lit); + } + lbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) { + emitcode ("breq", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_TRUE (ifx)->key); + } + else { + emitcode ("brne", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_FALSE (ifx)-> key); + } + emitcode ("", "L%05d:", lbl->key); + } + else if (size == 2) { + emitcode ("mov", "r24,%s", aopGet (AOP (IC_LEFT (ic)), 0)); + emitcode ("mov", "r25,%s", aopGet (AOP (IC_LEFT (ic)), 1)); + emitcode (bopnames_lit[bitop], "r24,<(%d)", lit); + emitcode (bopnames_lit[bitop], "r25,>(%d)", lit); + emitcode ("sbiw", "r24,0"); + lbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) { + emitcode ("breq", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_TRUE (ifx)->key); + } + else { + emitcode ("brne", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_FALSE (ifx)->key); + } + emitcode ("", "L%05d:", lbl->key); + } + else { + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + while (size--) { + if (eh && AOP_ISHIGHREG(AOP(IC_LEFT(ic)),offset)) { + emitcode (bopnames_lit [bitop], "%s,<(%d)", + aopGet (AOP (IC_LEFT (ic)), offset), + lit); + } + else { + char *l = aopGet (AOP (IC_LEFT (ic)), offset); + MOVR24 (l); + emitcode ("andi", "r24,<(%d)", lit); + } + emitcode ("brne", "L%05d", lbl->key); + offset++; + } + /* all are zero */ + if (IC_FALSE (ifx)) + emitcode ("rjmp", "L%05d", IC_FALSE (ifx)-> key); + else + emitcode ("rjmp", "L%05d", lbl1->key); + emitcode ("", "L%05d:", lbl->key); + /* not zero */ + if (IC_TRUE (ifx)) + emitcode ("rjmp", "L%05d", IC_TRUE (ifx)->key); + emitcode ("", "L%05d:", lbl1->key); + + } + } + } + else { /* right is not a literal */ + int eh = OP_SYMBOL (left)->liveTo <= ic->seq; + int reh = OP_SYMBOL (right)->liveTo <= ic->seq; + if (size == 1) { + if (eh) { + emitcode (bopnames[bitop], "%s,%s", aopGet (AOP (IC_LEFT (ic)), 0), + aopGet (AOP (IC_RIGHT (ic)), 0)); + } + else if (reh) { + emitcode (bopnames[bitop], "%s,%s", + aopGet (AOP (IC_RIGHT (ic)), 0), + aopGet (AOP (IC_LEFT (ic)), 0)); + } + else { + MOVR0 (aopGet (AOP (IC_LEFT (ic)), 0)); + emitcode (bopnames[bitop], "r0,%s", + aopGet (AOP (IC_RIGHT (ic)), 0)); + } + lbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) { + emitcode ("breq", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", + IC_TRUE (ifx)->key); + } + else { + emitcode ("brne", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", + IC_FALSE (ifx)->key); + } + emitcode ("", "L%05d:", lbl->key); + } + else if (size == 2) { + emitcode ("mov", "r24,%s", + aopGet (AOP (IC_LEFT (ic)), 0)); + emitcode ("mov", "r25,%s", + aopGet (AOP (IC_LEFT (ic)), 1)); + emitcode (bopnames[bitop], "r24,%s", + aopGet (AOP (IC_RIGHT (ic)), 0)); + emitcode (bopnames[bitop], "r25,%s", + aopGet (AOP (IC_RIGHT (ic)), 1)); + emitcode ("sbiw", "r24,0"); + lbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) { + emitcode ("breq", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_TRUE (ifx)->key); + } + else { + emitcode ("brne", "L%05d", lbl->key); + emitcode ("rjmp", "L%05d", IC_FALSE (ifx)->key); + } + emitcode ("", "L%05d:", lbl->key); + } + else { + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + while (size--) { + if (eh) { + emitcode (bopnames[bitop], "%s,%s", + aopGet (AOP (IC_LEFT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + else if (reh) { + emitcode (bopnames[bitop], "%s,%s", + aopGet (AOP (IC_RIGHT (ic)), offset), + aopGet (AOP (IC_LEFT (ic)), offset)); + } + else { + MOVR0 (aopGet (AOP (IC_LEFT (ic)), offset)); + emitcode (bopnames[bitop], "r0,%s", + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + emitcode ("brne", "L%05d", lbl->key); + offset++; + } + /* all are zero */ + if (IC_FALSE (ifx)) + emitcode ("rjmp", "L%05d", IC_FALSE (ifx)->key); + else + emitcode ("rjmp", "L%05d", lbl1->key); + emitcode ("", "L%05d:", lbl->key); + /* not zero */ + if (IC_TRUE (ifx)) + emitcode ("rjmp", "L%05d", IC_TRUE (ifx)->key); + emitcode ("", "L%05d:", lbl1->key); + + } + } + goto release; + } + + /* result needs to go a register */ + samerl = sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))); + samerr = sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic))); + while (size--) { + if (AOP_TYPE (right) == AOP_LIT) { + unsigned int lit = + (int) ulFromVal (AOP (right)->aopu. + aop_lit); + if (((lit >> (8 * offset)) & 0xff) == 0) { + if (bitop == AVR_AND) { + aopPut (AOP (result), zero, offset++); + continue; + } + else if (bitop == AVR_OR) { + if (!samerl) + aopPut (AOP (result), + aopGet (AOP (left), + offset), + offset); + offset++; + continue; + } + } + } + if (samerl) { + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT && + AOP_ISHIGHREG(AOP(IC_LEFT(ic)),offset) && + (bitop == AVR_AND || bitop == AVR_OR)) { + emitcode (bopnames_lit[bitop], "%s,%s(%d)", + aopGet (AOP (IC_LEFT (ic)), offset), + larray[offset], + (int) ulFromVal (AOP (right)-> aopu.aop_lit)); + } + else { + emitcode (bopnames[bitop], "%s,%s", + aopGet (AOP (IC_LEFT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + } + else if (samerr) { + emitcode (bopnames[bitop], "%s,%s", + aopGet (AOP (IC_RIGHT (ic)), offset), + aopGet (AOP (IC_LEFT (ic)), offset)); + } + else { + aopPut (AOP (IC_RESULT (ic)), + aopGet (AOP (IC_LEFT (ic)), offset), offset); + emitcode (bopnames[bitop], + aopGet (AOP (IC_RESULT (ic)), offset), + aopGet (AOP (IC_RIGHT (ic)), offset)); + } + offset++; + } + release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + genBitWise (ic, ifx, AVR_AND); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + genBitWise (ic, ifx, AVR_OR); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + genBitWise (ic, ifx, AVR_XOR); +} + +/*-----------------------------------------------------------------*/ +/* genInline - write the inline code out */ +/*-----------------------------------------------------------------*/ +static void +genInline (iCode * ic) +{ + char *buffer, *bp, *bp1; + bool inComment = FALSE; + + _G.inLine += (!options.asmpeep); + + buffer = bp = bp1 = Safe_strdup (IC_INLINE (ic)); + + /* emit each line as a code */ + while (*bp) + { + switch (*bp) + { + case ';': + inComment = TRUE; + ++bp; + break; + + case '\n': + inComment = FALSE; + *bp++ = '\0'; + emitcode (bp1, ""); + bp1 = bp; + break; + + default: + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && (*bp == ':') && (isspace((unsigned char)bp[1]))) + { + ++bp; + *bp = '\0'; + ++bp; + emitcode (bp1, ""); + bp1 = bp; + } + else + ++bp; + break; + } + } + if (bp1 != bp) + emitcode (bp1, ""); + + Safe_free (buffer); + + /* consumed; we can free it here */ + dbuf_free (IC_INLINE (ic)); + + _G.inLine -= (!options.asmpeep); +} + +/*-----------------------------------------------------------------*/ +/* genRotC - rotate right/left with carry , lr = 1 rotate right */ +/*-----------------------------------------------------------------*/ +static void +genRotC (iCode * ic, int lr) +{ + operand *left, *result; + int size, offset = 0; + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + if (!sameRegs (AOP (left), AOP (result))) { + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (left), offset), offset); + offset++; + } + size = AOP_SIZE (result); + } + if (lr) + offset = size - 1; + else + offset = 0; + + CLRC; + emitcode ("sbrc", "%s,%d", aopGet (AOP (result), offset), + (lr ? 0 : 7)); + emitcode ("sec", ""); + + while (size--) { + emitcode ((lr ? "ror" : "rol"), "%s", + aopGet (AOP (result), offset)); + if (lr) + offset--; + else + offset++; + } + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + genRotC (ic, 1); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + genRotC (ic, 0); +} + +/*-----------------------------------------------------------------*/ +/* genGetHbit - generates code get highest order bit */ +/*-----------------------------------------------------------------*/ +static void +genGetHbit (iCode * ic) +{ + operand *left, *result; + int size, offset; + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + size = AOP_SIZE (result); + if (!sameRegs (AOP (left), AOP (result))) { + emitcode ("clr", "%s", aopGet (AOP (result), size - 1)); + emitcode ("sbrc", "%s,7", aopGet (AOP (left), size - 1)); + emitcode ("subi", "%s,<(-1)", + aopGet (AOP (result), size - 1)); + } + else { + emitcode ("clr", "r0"); + emitcode ("sbrc", "%s,7", aopGet (AOP (left), size - 1)); + emitcode ("subi", "r0,<(-1)"); + aopPut (AOP (result), "r0", 0); + } + offset = 1; + size--; + while (size--) { + emitcode ("clr", aopGet (AOP (result), offset++)); + } + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genShiftLeftLit - shift left by a known amount */ +/*-----------------------------------------------------------------*/ +static void +genShiftLeftLit (iCode * ic) +{ + operand *left, *right, *result; + int size, shCount, offset = 0; + int lByteZ = 0; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + + if (shCount > (size * 8 - 1)) { + while (size--) + aopPut (AOP (result), zero, offset++); + goto release; + } + switch (size) { + case 1: + if (!sameRegs (AOP (left), AOP (result))) + aopPut (AOP (result), aopGet (AOP (left), 0), 0); + if (shCount >= 4) { + if (AOP_ISHIGHREG(AOP(result),0)) { + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + emitcode ("andi", "%s,0xf0"); + } else { + emitcode ("ldi","r24,0xf0"); + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + emitcode ("and", "%s,r24"); + } + shCount -= 4; + } + if (shCount == 1) { + emitcode ("add", "%s,%s", aopGet (AOP (result), 0), + aopGet (AOP (result), 0)); + shCount--; + } + while (shCount--) + emitcode ("lsl", "%s", aopGet (AOP (result), 0)); + break; + case 2: + if (shCount >= 12) { + aopPut (AOP (result), aopGet (AOP (left), 0), 1); + aopPut (AOP (result), zero, 0); + emitcode ("swap", "%s", aopGet (AOP (result), 1)); + if (AOP_ISHIGHREG(AOP(result),1)) { + emitcode ("andi", "%s,0xf0", aopGet (AOP (result), 1)); + } else { + emitcode ("ldi","r24,0xf0"); + emitcode ("and", "%s,r24", aopGet (AOP (result), 1)); + } + shCount -= 12; + lByteZ = 1; + } + if (shCount >= 8) { + aopPut (AOP (result), aopGet (AOP (left), 0), 1); + aopPut (AOP (result), zero, 0); + shCount -= 8; + lByteZ = 1; + } + if (shCount >= 4) { + shCount -= 4; + if (!sameRegs (AOP (left), AOP (result))) { + aopPut (AOP (result), aopGet (AOP (left), 0), + 0); + aopPut (AOP (result), aopGet (AOP (left), 1), + 1); + } + emitcode ("mov", "r24,%s", aopGet (AOP (result), 0)); + emitcode ("andi", "r24,0x0f"); + if (!(AOP_ISHIGHREG(AOP(result),0) && AOP_ISHIGHREG(AOP(result),1))) { + emitcode("ldi","r25,0xf0"); + } + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + if (AOP_ISHIGHREG(AOP(result),0)) { + emitcode ("andi", "%s,0xf0", aopGet (AOP (result), 0)); + } else { + emitcode ("and", "%s,r25", aopGet (AOP (result), 0)); + } + emitcode ("swap", "%s", aopGet (AOP (result), 1)); + if (AOP_ISHIGHREG(AOP(result),1)) { + emitcode ("andi", "%s,0xf0", aopGet (AOP (result), 1)); + } else { + emitcode ("and", "%s,r25", aopGet (AOP (result), 1)); + } + emitcode ("or", "%s,r24", aopGet (AOP (result), 1)); + while (shCount--) { + emitcode ("lsl", "%s", aopGet (AOP (result), 0)); + emitcode ("rol", "%s", aopGet (AOP (result), 1)); + } + } + if (!lByteZ && !sameRegs (AOP (result), AOP (left)) + && shCount) { + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (left), offset), offset); + offset++; + } + } + while (shCount--) { + if (lByteZ) { + emitcode ("lsl", "%s", aopGet (AOP (result), 1)); + } + else { + emitcode ("lsl", "%s", aopGet (AOP (result), 0)); + emitcode ("rol", "%s", aopGet (AOP (result), 1)); + } + } + break; + case 3: + assert ("shifting generic pointer ?\n"); + break; + case 4: + /* 32 bits we do only byte boundaries */ + if (shCount >= 24) { + aopPut (AOP (result), aopGet (AOP (left), 0), 3); + aopPut (AOP (result), zero, 2); + aopPut (AOP (result), zero, 1); + aopPut (AOP (result), zero, 0); + lByteZ = 3; + shCount -= 24; + } + if (shCount >= 16) { + aopPut (AOP (result), aopGet (AOP (left), 0), 3); + aopPut (AOP (result), aopGet (AOP (left), 1), 2); + aopPut (AOP (result), zero, 1); + aopPut (AOP (result), zero, 0); + lByteZ = 2; + shCount -= 16; + } + if (shCount >= 8) { + aopPut (AOP (result), aopGet (AOP (left), 0), 3); + aopPut (AOP (result), aopGet (AOP (left), 1), 2); + aopPut (AOP (result), aopGet (AOP (left), 2), 1); + aopPut (AOP (result), zero, 0); + shCount -= 8; + lByteZ = 1; + } + if (!lByteZ && !sameRegs (AOP (left), AOP (right))) { + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (left), offset), offset); + offset++; + } + offset = 0; + size = AOP_SIZE (result); + } + if (shCount) { + switch (lByteZ) { + case 0: + while (shCount--) { + emitcode ("lsl", "%s", aopGet (AOP (result), 0)); + emitcode ("rol", "%s", aopGet (AOP (result), 1)); + emitcode ("rol", "%s", aopGet (AOP (result), 2)); + emitcode ("rol", "%s", aopGet (AOP (result), 3)); + } + break; + case 1: + while (shCount--) { + emitcode ("lsl", "%s", aopGet (AOP (result), 1)); + emitcode ("rol", "%s", aopGet (AOP (result), 2)); + emitcode ("rol", "%s", aopGet (AOP (result), 3)); + } + break; + case 2: + while (shCount--) { + emitcode ("lsl", "%s", aopGet (AOP (result), 2)); + emitcode ("rol", "%s", aopGet (AOP (result), 3)); + } + break; + case 3: + while (shCount--) { + emitcode ("lsl", "%s", aopGet (AOP (result), 3)); + } + break; + } + } + } + + release: + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) { + genShiftLeftLit (ic); + return; + } + + /* unknown count */ + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + offset = 0; + if (AOP_SIZE (right) > 1) { + if (isRegPair (AOP (right))) { + emitcode ("movw", "r24,%s", aopGet (AOP (right), 0)); + } + else { + emitcode ("mov", "r24,%s", aopGet (AOP (right), 0)); + emitcode ("mov", "r25,%s", aopGet (AOP (right), 1)); + } + } + else { + emitcode ("mov", "r24,%s", aopGet (AOP (right), 0)); + } + if (!sameRegs (AOP (left), AOP (result))) { + while (size--) { + aopPut (AOP (result), aopGet (AOP (left), offset), + offset); + offset++; + } + size = AOP_SIZE (result); + } + tlbl = newiTempLabel (NULL); + emitcode ("", "L%05d:", tlbl->key); + offset = 0; + while (size--) { + if (offset) + emitcode ("rol", "%s", aopGet (AOP (result), offset)); + else + emitcode ("lsl", "%s", aopGet (AOP (result), 0)); + offset++; + } + if (AOP_SIZE (right) > 1) + emitcode ("sbiw", "r24,1"); + else + emitcode ("dec", "r24"); + emitcode ("brne", "L%05d", tlbl->key); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genShiftRightLit - generate for right shift with known count */ +/*-----------------------------------------------------------------*/ +static void +genShiftRightLit (iCode * ic) +{ + operand *left = IC_LEFT (ic) + , *right = IC_RIGHT (ic) + , *result = IC_RESULT (ic); + int size, shCount, offset = 0; + int hByteZ = 0; + sym_link *letype = getSpec (operandType (left)); + int sign = !SPEC_USIGN (letype); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + + /* if signed then give up and use a loop to shift */ + if (sign) { + symbol *tlbl; + if (!sameRegs (AOP (left), AOP (result))) { + while (size--) { + aopPut (AOP (result), + aopGet (AOP (left), offset), offset); + offset++; + } + size = AOP_SIZE (result); + offset = 0; + } + /* be as economical as possible */ + if (shCount <= 4) { + while (shCount--) { + size = AOP_SIZE (result); + offset = size - 1; + while (size--) { + /* highest order byte */ + if (offset == (AOP_SIZE(result)-1)) + emitcode ("asr", "%s", aopGet (AOP (result), offset)); + else + emitcode ("ror", "%s", aopGet (AOP (result), offset)); + offset--; + } + } + } + else { + emitcode ("ldi", "r24,<(%d)", shCount); + tlbl = newiTempLabel (NULL); + emitcode ("", "L%05d:", tlbl->key); + offset = size - 1; + while (size--) { + if (offset == (AOP_SIZE(result) - 1)) + emitcode ("asr", "%s", aopGet (AOP (result), offset)); + else + emitcode ("ror", "%s", aopGet (AOP (result), offset)); + offset--; + } + emitcode ("dec", "r24"); + emitcode ("brne", "L%05d", tlbl->key); + } + goto release; + } + if (shCount > (size * 8 - 1)) { + while (size--) + aopPut (AOP (result), zero, offset++); + goto release; + } + /* for unsigned we can much more efficient */ + switch (size) { + case 1: + if (!sameRegs (AOP (left), AOP (result))) + aopPut (AOP (result), aopGet (AOP (left), 0), 0); + if (shCount >= 4) { + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + if (AOP_ISHIGHREG(AOP(result),0)) { + emitcode ("andi", "%s,0x0f",aopGet(AOP(result),0)); + } else { + emitcode ("ldi","r24,0x0f"); + emitcode ("and", "%s,r24",aopGet(AOP(result),0)); + } + shCount -= 4; + } + while (shCount--) + emitcode ("lsr", "%s", aopGet (AOP (result), 0)); + break; + case 2: + if (shCount >= 12) { + aopPut (AOP (result), aopGet (AOP (left), 1), 0); + aopPut (AOP (result), zero, 1); + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + if (AOP_ISHIGHREG(AOP(result),0)) { + emitcode ("andi", "%s,0x0f", aopGet (AOP (result), 0)); + } else { + emitcode ("ldi","r24,0x0f"); + emitcode ("and", "%s,r24",aopGet(AOP(result),0)); + } + shCount -= 12; + hByteZ = 1; + } + if (shCount >= 8) { + aopPut (AOP (result), aopGet (AOP (left), 1), 0); + aopPut (AOP (result), zero, 1); + shCount -= 8; + hByteZ = 1; + } + if (shCount >= 4) { + shCount -= 4; + if (!sameRegs (AOP (left), AOP (result))) { + aopPut (AOP (result), aopGet (AOP (left), 0), 0); + aopPut (AOP (result), aopGet (AOP (left), 1), 1); + } + if (!(AOP_ISHIGHREG(AOP(result),0) && AOP_ISHIGHREG(AOP(result),1))) { + emitcode("ldi","r25,0x0f"); + } + emitcode ("mov", "r24,%s", aopGet (AOP (result), 1)); + emitcode ("andi", "r24,0xf0"); + emitcode ("swap", "%s", aopGet (AOP (result), 0)); + if (AOP_ISHIGHREG(AOP(result),0)) { + emitcode ("andi", "%s,0x0f", aopGet (AOP (result), 0)); + } else { + emitcode ("and", "%s,r25", aopGet (AOP (result), 0)); + } + emitcode ("or", "%s,r24", aopGet (AOP (result), 0)); + emitcode ("swap", "%s", aopGet (AOP (result), 1)); + if (AOP_ISHIGHREG(AOP(result),1)) { + emitcode ("andi", "%s,0x0f", aopGet (AOP (result), 1)); + } else { + emitcode ("and", "%s,r24", aopGet (AOP (result), 1)); + } + while (shCount--) { + emitcode ("lsr", "%s", aopGet (AOP (result), 1)); + emitcode ("ror", "%s", aopGet (AOP (result), 0)); + } + + } + if (!hByteZ && !sameRegs (AOP (result), AOP (left)) + && shCount) { + offset = 0; + while (size--) { + aopPut (AOP (result), aopGet (AOP (left), offset), offset); + offset++; + } + } + while (shCount--) { + if (hByteZ) { + emitcode ("lsr", "%s", aopGet (AOP (result), 0)); + } + else { + emitcode ("lsr", "%s", aopGet (AOP (result), 1)); + emitcode ("ror", "%s", aopGet (AOP (result), 0)); + } + } + break; + + case 3: + assert ("shifting generic pointer ?\n"); + break; + case 4: + /* 32 bits we do only byte boundaries */ + if (shCount >= 24) { + aopPut (AOP (result), aopGet (AOP (left), 3), 0); + aopPut (AOP (result), zero, 1); + aopPut (AOP (result), zero, 2); + aopPut (AOP (result), zero, 3); + hByteZ = 3; + shCount -= 24; + } + if (shCount >= 16) { + aopPut (AOP (result), aopGet (AOP (left), 3), 1); + aopPut (AOP (result), aopGet (AOP (left), 2), 0); + aopPut (AOP (result), zero, 2); + aopPut (AOP (result), zero, 3); + hByteZ = 2; + shCount -= 16; + } + if (shCount >= 8) { + aopPut (AOP (result), aopGet (AOP (left), 1), 0); + aopPut (AOP (result), aopGet (AOP (left), 2), 1); + aopPut (AOP (result), aopGet (AOP (left), 3), 2); + aopPut (AOP (result), zero, 3); + shCount -= 8; + hByteZ = 1; + } + if (!hByteZ && !sameRegs (AOP (left), AOP (right))) { + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (left), offset), offset); + offset++; + } + offset = 0; + size = AOP_SIZE (result); + } + if (shCount) { + switch (hByteZ) { + case 0: + while (shCount--) { + emitcode ("lsr", "%s", aopGet (AOP (result), 3)); + emitcode ("ror", "%s", aopGet (AOP (result), 2)); + emitcode ("ror", "%s", aopGet (AOP (result), 1)); + emitcode ("ror", "%s", aopGet (AOP (result), 0)); + } + break; + case 1: + while (shCount--) { + emitcode ("lsr", "%s", aopGet (AOP (result), 2)); + emitcode ("ror", "%s", aopGet (AOP (result), 1)); + emitcode ("ror", "%s", aopGet (AOP (result), 0)); + } + break; + case 2: + while (shCount--) { + emitcode ("lsr", "%s", aopGet (AOP (result), 1)); + emitcode ("ror", "%s", aopGet (AOP (result), 0)); + } + break; + case 3: + while (shCount--) { + emitcode ("lsr", "%s", aopGet (AOP (result), 0)); + } + break; + } + } + } + release: + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *letype; + int size, offset; + int sign = 0, first = 1; + symbol *tlbl; + + aopOp (right = IC_RIGHT (ic), ic, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) { + genShiftRightLit (ic); + return; + } + /* unknown count */ + if (AOP_SIZE (right) > 1) { + if (isRegPair (AOP (right))) { + emitcode ("movw", "r24,%s", aopGet (AOP (right), 0)); + } + else { + emitcode ("mov", "r24,%s", aopGet (AOP (right), 0)); + emitcode ("mov", "r25,%s", aopGet (AOP (right), 1)); + } + } + else { + emitcode ("mov", "r24,%s", aopGet (AOP (right), 0)); + } + aopOp (left = IC_LEFT (ic), ic, FALSE); + aopOp (result = IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (result); + tlbl = newiTempLabel (NULL); + emitcode ("", "L%05d:", tlbl->key); + offset = size - 1; + letype = getSpec (operandType (left)); + sign = !SPEC_USIGN (letype); + if (!sameRegs (AOP (left), AOP (result))) { + while (size--) { + aopPut (AOP (result), aopGet (AOP (left), offset), offset); + offset++; + } + size = AOP_SIZE (result); + } + size = AOP_SIZE (result); + while (size--) { + if (first) { + if (sign) + emitcode ("asr", "%s", aopGet (AOP (result), offset)); + else + emitcode ("lsr", "%s", aopGet (AOP (result), offset)); + first = 0; + } + else + emitcode ("ror", "%s", aopGet (AOP (result), offset)); + offset--; + } + if (AOP_SIZE (right) > 1) + emitcode ("sbiw", "r24,1"); + else + emitcode ("dec", "r24"); + emitcode ("brne", "L%05d", tlbl->key); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* RRsh - shift right rn by known count */ +/*-----------------------------------------------------------------*/ +static void +RRsh (int shCount,int reg) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) { + case 0: + break; + case 1: + emitcode ("lsr", "r%d",reg); + break; + case 2: + emitcode ("lsr", "r%d",reg); + emitcode ("lsr", "r%d",reg); + break; + case 3: + emitcode ("swap", "r%d",reg); + emitcode ("lsl", "r%d",reg); + break; + case 4: + emitcode ("swap", "r%d",reg); + break; + case 5: + emitcode ("swap", "r%d",reg); + emitcode ("lsr", "r%d",reg); + break; + case 6: + emitcode ("swap","r%d",reg); + emitcode ("lsr", "r%d",reg); + emitcode ("lsr", "r%d",reg); + break; + case 7: + emitcode ("swap","r%d",reg); + emitcode ("lsr", "r%d",reg); + emitcode ("lsr", "r%d",reg); + emitcode ("lsr", "r%d",reg); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* RLsh - shift left rn by known count */ +/*-----------------------------------------------------------------*/ +static void +RLsh (int shCount, int reg) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) { + case 0: + break; + case 1: + emitcode ("lsl", "r%d",reg); + break; + case 2: + emitcode ("lsl", "r%d",reg); + emitcode ("lsl", "r%d",reg); + break; + case 3: + emitcode ("swap","r%d",reg); + emitcode ("lsr", "r%d",reg); + break; + case 4: + emitcode ("swap", "r%d",reg); + break; + case 5: + emitcode ("swap","r%d",reg); + emitcode ("lsl", "r%d",reg); + break; + case 6: + emitcode ("swap","r%d",reg); + emitcode ("lsl", "r%d",reg); + emitcode ("lsl", "r%d",reg); + break; + case 7: + emitcode ("swap","r%d",reg); + emitcode ("lsl", "r%d",reg); + emitcode ("lsl", "r%d",reg); + emitcode ("lsl", "r%d",reg); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, char *rname, int ptype) +{ + int shCnt; + int rlen = 0; + sym_link *etype; + int offset = 0; + int rsize; + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + /* read the first byte */ + switch (ptype) { + + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("ld", "r24,%s+", rname); + break; + + case CPOINTER: + emitcode ("lpm", "r24,%s+", rname); + break; + + case GPOINTER: + emitcode ("call","__gptrget_pi"); + emitcode ("mov","r24,r0"); + break; + } + + rlen = SPEC_BLEN (etype); + + /* if we have bitdisplacement then it fits */ + /* into this byte completely or if length is */ + /* less than a byte */ + if ((shCnt = SPEC_BSTR (etype)) || (SPEC_BLEN (etype) <= 8)) { + + /* shift right acc */ + RRsh (shCnt,24); + + emitcode ("andi", "r24,lo(0x%x)", + ((unsigned char) -1) >> (8 - SPEC_BLEN (etype))); + aopPut (AOP (result), "r24", offset++); + goto finish; + } + + /* bit field did not fit in a byte */ + aopPut (AOP (result), "r24", offset++); + + while (1) { + + switch (ptype) { + + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("ld", "r24,%s+"); + break; + + case CPOINTER: + emitcode ("lpm", "r24,%s+"); + break; + + case GPOINTER: + emitcode ("call", "__gptrget_pi"); + break; + } + + rlen -= 8; + /* if we are done */ + if (rlen < 8) + break; + + aopPut (AOP (result), "r24", offset++); + + } + + if (rlen) { + aopPut (AOP (result), "r24", offset++); + } + + finish: + if (offset < rsize) { + rsize -= offset; + while (rsize--) + aopPut (AOP (result), zero, offset++); + } + return; +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + char *l; + char buffer[256]; + int size, offset = 0; + aopOp (result, ic, TRUE); + + /* get the string representation of the name */ + l = aopGet (AOP (left), 0); + size = AOP_SIZE (result); + while (size--) { + if (offset) + sprintf (buffer, "(%s + %d)", l, offset); + else + sprintf (buffer, "%s", l); + emitcode ("lds", "%s,%s", aopGet (AOP (result), offset++), + buffer); + } + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genMemPointerGet (operand * left, operand * result, iCode * ic, iCode *pi) +{ + asmop *aop = NULL; + regs *preg = NULL; + int gotFreePtr = 0; + char *rname, *frname = NULL; + sym_link *rtype, *retype; + sym_link *ltype = operandType (left); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable and + result is not bit variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_IMMD && + !IS_BITVAR (retype) && DCL_TYPE (ltype) == POINTER) { + genDataPointerGet (left, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, &aop, FALSE, 0); + if (isRegPair (AOP (left) )) { + emitcode ("movw", "%s,%s", + aop->aopu.aop_ptr->name, + aopGet(AOP(left),0)); + } else { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, + aopGet (AOP (left), 0)); + emitcode ("mov", "%s,%s", aop->aop_ptr2->name, + aopGet (AOP (left), 1)); + } + gotFreePtr = 1; + } + else { + aop = AOP(left); + frname = aopGet(aop,0); + } + if (AOP_ISX(aop)) { + rname = "X"; + } else if (AOP_ISZ(aop)) { + rname = "Z"; + } else { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "pointer not in correct register"); + exit (0); + } + + aopOp (result, ic, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITVAR (retype)) + genUnpackBits (result, rname, POINTER); + else { + /* we have can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) { + if (size || pi) { + emitcode ("ld","%s,%s+",aopGet(AOP(result),offset), rname); + } else { + emitcode ("ld","%s,%s",aopGet(AOP(result),offset), rname); + } + } + } + + /* now some housekeeping stuff */ + if (gotFreePtr) { + /* we had to allocate for this iCode */ + if (pi) { + if (isRegPair (AOP (left) )) { + emitcode ("movw", "%s,%s", + aopGet (AOP(left),0), + aop->aopu.aop_ptr->name); + } else { + emitcode ("mov", "%s,%s", + aopGet (AOP (left), 0), + aop->aopu.aop_ptr->name); + emitcode ("mov", "%s,%s", + aopGet (AOP (left), 1), + aop->aop_ptr2->name); + } + } + freeAsmop (NULL, aop, ic, TRUE); + } else { + + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && + !OP_SYMBOL (left)->remat && + (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) { + int size = AOP_SIZE (result) - 1; + emitcode ("sbiw", "%s,%d",frname,size); + } + } + + /* done */ + if (pi) pi->generated = 1; + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genCodePointerGet - gget value from code space */ +/*-----------------------------------------------------------------*/ +static void +genCodePointerGet (operand * left, operand * result, iCode * ic, iCode *pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (result)); + asmop *aop = NULL; + int gotFreePtr = 0; + + aopOp (left, ic, FALSE); + + /* if the operand is already in Z register + then we do nothing else we move the value to Z register */ + if (AOP_ISZ(AOP(left))) { + aop = AOP (left); + } else { + aop = newAsmop(0); + getFreePtr(ic,&aop,FALSE,TRUE); + if (isRegPair(AOP (left))) { + emitcode ("movw","r30,%s",aopGet (AOP (left), 0)); + } else { + emitcode ("mov", "r30,%s", aopGet (AOP (left), 0)); + emitcode ("mov", "r31,%s", aopGet (AOP (left), 1)); + } + gotFreePtr = 1; + } + + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITVAR (retype)) + genUnpackBits (result, "Z", CPOINTER); + else { + size = AOP_SIZE (result); + offset = 0; + + while (size--) { + if (size || pi) { + emitcode ("lpm","%s,Z+",aopGet(AOP(result),offset++)); + } else { + emitcode ("lpm","%s,Z",aopGet(AOP(result),offset++)); + } + } + } + + /* now some housekeeping stuff */ + if (gotFreePtr) { + /* we had to allocate for this iCode */ + if (pi) { + if (isRegPair(AOP (left))) { + emitcode ("movw","%s,r30",aopGet (AOP (left), 0)); + } else { + emitcode ("mov", "%s,r30", aopGet (AOP (left), 0)); + emitcode ("mov", "%s,r31", aopGet (AOP (left), 1)); + } + } + freeAsmop (NULL, aop, ic, TRUE); + } else { + + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && + !OP_SYMBOL (left)->remat && + (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && + !pi) { + int size = AOP_SIZE (result) - 1; + emitcode ("sbiw", "r30,%d",size); + } + } + + /* done */ + if (pi) pi->generated=1; + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - gget value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic, iCode *pi) +{ + int size, offset; + int gotFreePtr = 0; + sym_link *retype = getSpec (operandType (result)); + asmop *aop = NULL; + + aopOp (left, ic, FALSE); + + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_ISZ(AOP(left))) { + aop = AOP(left); + } else { + aop = newAsmop(0); + getFreePtr(ic,&aop,FALSE,TRUE); + if (isRegPair(AOP(left))) { + emitcode ("movw", "r30,%s", aopGet (AOP (left), 0)); + } else { + emitcode ("mov", "r30,%s", aopGet (AOP (left), 0)); + emitcode ("mov", "r31,%s", aopGet (AOP (left), 1)); + } + emitcode ("mov", "r24,%s", aopGet (AOP (left), 2)); + gotFreePtr=1; + } + + /* so Z register now contains the address */ + + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITVAR (retype)) + genUnpackBits (result, "Z", GPOINTER); + else { + size = AOP_SIZE (result); + offset = 0; + + while (size--) { + if (size || pi) + emitcode ("call", "__gptrget_pi"); + else + emitcode ("call", "__gptrget"); + aopPut (AOP (result), "r0", offset++); + } + } + + + /* now some housekeeping stuff */ + if (gotFreePtr) { + /* we had to allocate for this iCode */ + if (pi) { + if (isRegPair(AOP (left))) { + emitcode ("movw","%s,r30",aopGet (AOP (left), 0)); + } else { + emitcode ("mov", "%s,r30", aopGet (AOP (left), 0)); + emitcode ("mov", "%s,r31", aopGet (AOP (left), 1)); + } + } + freeAsmop (NULL, aop, ic, TRUE); + } else { + + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && + !OP_SYMBOL (left)->remat && + (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && + !pi) { + int size = AOP_SIZE (result) - 1; + emitcode ("sbiw", "r30,%d",size); + } + } + if (pi) pi->generated=1; + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode *pi) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + p_type = DCL_TYPE (type); + else { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) { + + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + genMemPointerGet (left, result, ic, pi); + break; + + case CPOINTER: + genCodePointerGet (left, result, ic, pi); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic, pi); + break; + } + +} + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, + operand * right, + char *rname, int p_type) +{ + int shCount = 0; + int offset = 0; + int rLen = 0; + int blen, bstr; + char *l; + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + l = aopGet (AOP (right), offset++); + MOVR24 (l); + + /* if the bit lenth is less than or */ + /* it exactly fits a byte then */ + if (SPEC_BLEN (etype) <= 8) { + shCount = SPEC_BSTR (etype); + + /* shift left acc */ + RLsh (shCount,24); + + if (SPEC_BLEN (etype) < 8) { /* if smaller than a byte */ + + switch (p_type) { + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("ld", "r1,%s",rname); + break; + + case GPOINTER: + emitcode ("push", "r1"); + emitcode ("push", "r24"); + emitcode ("call", "__gptrget"); + emitcode ("pop", "r1"); + emitcode ("mov","r24,r0"); + break; + } + + emitcode ("andi", "r24,#0x%02x", (unsigned char) + ((unsigned char) (0xFF << (blen + bstr)) | + (unsigned char) (0xFF >> (8 - bstr)))); + emitcode ("or", "r24,r1"); + if (p_type == GPOINTER) + emitcode ("pop", "r1"); + } + } + + switch (p_type) { + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode("st","%s+,r24"); + break; + + case GPOINTER: + emitcode("mov","r0,r24"); + emitcode ("call", "__gptrput_pi"); + break; + } + + /* if we r done */ + if (SPEC_BLEN (etype) <= 8) + return; + + rLen = SPEC_BLEN (etype); + + /* now generate for lengths greater than one byte */ + while (1) { + + l = aopGet (AOP (right), offset++); + + rLen -= 8; + if (rLen < 8) + break; + + switch (p_type) { + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("st", "%s+,%s",rname,l); + break; + + case GPOINTER: + MOVR0 (l); + emitcode ("lcall", "__gptrput_pi"); + break; + } + } + + MOVR24 (l); + + /* last last was not complete */ + if (rLen) { + /* save the byte & read byte */ + switch (p_type) { + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("st","%s+,r24",rname); + break; + case GPOINTER: + emitcode ("push", "r1"); + emitcode ("push", "r24"); + emitcode ("lcall", "__gptrget"); + emitcode ("mov","r24,r0"); + emitcode ("pop", "r1"); + break; + } + + emitcode ("andi", "r24,0x%02x", (((unsigned char) -1 << rLen) & 0xff)); + emitcode ("or", "r24,r1"); + } + + if (p_type == GPOINTER) + emitcode ("pop", "r1"); + + switch (p_type) { + + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + emitcode ("st", "%s,r24", rname); + break; + + case GPOINTER: + emitcode ("mov","r0,r24"); + emitcode ("call", "__gptrput"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset = 0; + char *l, buffer[256]; + + aopOp (right, ic, FALSE); + + l = aopGet (AOP (result), 0); + size = AOP_SIZE (right); + while (size--) { + if (offset) + sprintf (buffer, "(%s + %d)", l, offset); + else + sprintf (buffer, "%s", l); + emitcode ("sts", "%s,%s", buffer, + aopGet (AOP (right), offset++)); + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genMemPointerSet (operand * right, operand * result, iCode * ic, iCode *pi) +{ + asmop *aop = NULL; + char *frname = NULL, *rname, *l; + int gotFreePtr = 0; + sym_link *retype; + sym_link *ptype = operandType (result); + + retype = getSpec (operandType (right)); + + aopOp (result, ic, FALSE); + + /* if the result is rematerializable & + in data space & not a bit variable */ + if (AOP_TYPE (result) == AOP_IMMD && + DCL_TYPE (ptype) == POINTER && !IS_BITVAR (retype)) { + genDataPointerSet (right, result, ic); + return; + } + if (!AOP_INPREG(AOP(result))) { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + getFreePtr (ic, &aop, FALSE, 0); + if (isRegPair (AOP (result) )) { + emitcode ("movw", "%s,%s",aop->aopu.aop_ptr->name, + aopGet(AOP (result), 0)); + } else { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, + aopGet (AOP (result), 0)); + emitcode ("mov", "%s,%s", aop->aop_ptr2->name, + aopGet (AOP (result), 1)); + } + gotFreePtr = 1; + } else { + aop = AOP(result); + frname = aopGet(aop,0); + } + + aopOp (right, ic, FALSE); + if (AOP_ISX(aop)) { + rname = "X"; + } else if (AOP_ISZ(aop)) { + rname = "Z"; + } else { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "pointer not in correct register"); + exit (0); + } + /* if bitfield then unpack the bits */ + if (IS_BITVAR (retype)) + genPackBits (retype, right, rname, POINTER); + else { + /* we have can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) { + l = aopGet (AOP (right), offset); + if (size || pi) + emitcode ("st", "%s+,%s", rname,l); + else + emitcode ("st", "%s,%s", rname,l); + offset++; + } + } + + /* now some housekeeping stuff */ + if (gotFreePtr) { + /* we had to allocate for this iCode */ + if (pi) { + if (isRegPair (AOP (result) )) { + emitcode ("movw", "%s,%s", + aopGet(AOP(result),0), + aop->aopu.aop_ptr->name); + } else { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, + aopGet (AOP (result), 0)); + emitcode ("mov", "%s,%s", aop->aop_ptr2->name, + aopGet (AOP (result), 1)); + } + } + freeAsmop (NULL, aop, ic, TRUE); + } else { + + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && + !OP_SYMBOL (result)->remat && + (OP_SYMBOL (right)->liveTo > ic->seq || ic->depth)) && !pi) { + int size = AOP_SIZE (right) - 1; + emitcode ("sbiw", "%s,%d",frname,size); + } + } + + /* done */ + if (pi) pi->generated = 1; + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic, iCode *pi) +{ + int size, offset; + int gotFreePtr = 0; + sym_link *retype = getSpec (operandType (right)); + asmop *aop = NULL; + + aopOp (result, ic, FALSE); + + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_ISZ(AOP(result))) { + aop = AOP(right); + } else { + aop = newAsmop(0); + getFreePtr(ic,&aop,FALSE,TRUE); + if (isRegPair(AOP(result))) { + emitcode ("movw", "r30,%s", aopGet (AOP (result), 0)); + } else { + emitcode ("mov", "r30,%s", aopGet (AOP (result), 0)); + emitcode ("mov", "r31,%s", aopGet (AOP (result), 1)); + } + emitcode ("mov", "r24,%s", aopGet (AOP (result), 2)); + gotFreePtr=1; + } + + /* so Z register now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITVAR (retype)) + genUnpackBits (result, "Z", GPOINTER); + else { + size = AOP_SIZE (right); + offset = 0; + + while (size--) { + char *l = aopGet(AOP (right), offset++); + MOVR0(l); + + if (size || pi) + emitcode ("call", "__gptrput_pi"); + else + emitcode ("call", "__gptrput"); + } + } + + /* now some housekeeping stuff */ + if (gotFreePtr) { + /* we had to allocate for this iCode */ + if (pi) { + if (isRegPair(AOP(result))) { + emitcode ("movw", "%s,r30", aopGet (AOP (result), 0)); + } else { + emitcode ("mov", "%s,r30", aopGet (AOP (result), 0)); + emitcode ("mov", "%s,r31", aopGet (AOP (result), 1)); + } + } + freeAsmop (NULL, aop, ic, TRUE); + } else { + + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && + !OP_SYMBOL (result)->remat && + (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) && !pi) { + int size = AOP_SIZE (right) - 1; + emitcode ("sbiw", "r30,%d",size); + } + } + if (pi) pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode *pi) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) { + p_type = DCL_TYPE (type); + } + else { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) { + + case POINTER: + case IPOINTER: + case PPOINTER: + case FPOINTER: + genMemPointerSet (right, result, ic, pi); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic, pi); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "genPointerSet: illegal pointer type"); + } + +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + char *cname ; + symbol *lbl; + int tob = 0; + + aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_SIZE(cond) == 1 && AOP_ISHIGHREG(AOP(cond),0)) { + cname = aopGet(AOP(cond),0); + } else { + toBoolean (cond, "r24", 0); + tob = 1; + cname = "r24"; + } + /* the result is now in the accumulator */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if there was something to be popped then do it */ + if (popIc) { + genIpop (popIc); + emitcode("cpi","%s,0",cname); + } else if (!tob) emitcode("cpi","%s,0",cname); + + lbl = newiTempLabel(NULL); + if (IC_TRUE(ic)) { + emitcode ("breq","L%05d",lbl->key); + emitcode ("jmp","L%05d",IC_TRUE(ic)->key); + emitcode ("","L%05d:",lbl->key); + } else { + emitcode ("brne","L%05d",lbl->key); + emitcode ("jmp","L%05d",IC_FALSE(ic)->key); + emitcode ("","L%05d:",lbl->key); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + + aopOp (IC_RESULT (ic), ic, FALSE); + assert(AOP_SIZE(IC_RESULT(ic)) >= 2); + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) { + /* if it has an offset then we need to compute it */ + if (sym->stack) { + if (allHigh(AOP(IC_RESULT(ic)))) { + if (isRegPair (AOP(IC_RESULT(ic)))) { + emitcode ("movw","%s,r28",aopGet(AOP(IC_RESULT(ic)),0)); + } else { + emitcode ("mov","%s,r28",aopGet(AOP(IC_RESULT(ic)),0)); + emitcode ("mov","%s,r29",aopGet(AOP(IC_RESULT(ic)),1)); + } + if (sym->stack < 0) { + emitcode("subi","%s,<(%d)",aopGet(AOP(IC_RESULT(ic)),0),-sym->stack); + emitcode("sbci","%s,>(%d)",aopGet(AOP(IC_RESULT(ic)),1),-sym->stack); + } else { + emitcode("subi","%s,<(-%d)",aopGet(AOP(IC_RESULT(ic)),0),sym->stack); + emitcode("sbci","%s,>(-%d)",aopGet(AOP(IC_RESULT(ic)),1),sym->stack); + } + } else { + emitcode("movw","r24,r28"); + if (sym->stack > -63 && sym->stack < 63) { + if (sym->stack < 0) + emitcode("sbiw","r24,%d",-sym->stack); + else + emitcode("sbiw","r24,%d",sym->stack); + } else { + if (sym->stack < 0) { + emitcode("subi","r24,<(%d)",-sym->stack); + emitcode("sbci","r25,>(%d)",-sym->stack); + } else { + emitcode("subi","r24,<(-%d)",sym->stack); + emitcode("sbci","r25,>(-%d)",sym->stack); + } + } + + aopPut(AOP(IC_RESULT(ic)),"r24",0); + aopPut(AOP(IC_RESULT(ic)),"r25",1); + } + } + else { + aopPut(AOP(IC_RESULT(ic)),"r28",0); + aopPut(AOP(IC_RESULT(ic)),"r29",1); + } + /* fill the result with zero */ + size = AOP_SIZE (IC_RESULT (ic)) - 2; + offset = 2; + while (size--) { + aopPut (AOP (IC_RESULT (ic)), zero, offset++); + } + + goto release; + } + + /* object not on stack then we need the name */ + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + assert(size<=2); + while (size--) { + char s[SDCC_NAME_MAX]; + if (offset) + sprintf (s, ">(%s)", sym->rname); + else + sprintf (s, "<(%s)", sym->rname); + aopPut (AOP (IC_RESULT (ic)), s, offset++); + } + + release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genFarFarAssign - assignment when both are in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarFarAssign (operand * result, operand * right, iCode * ic) +{ + int size = AOP_SIZE (right); + int offset = 0; + char *l; + /* first push the right side on to the stack */ + while (size--) { + l = aopGet (AOP (right), offset++); + MOVA (l); + emitcode ("push", "acc"); + } + + freeAsmop (right, NULL, ic, FALSE); + /* now assign DPTR to result */ + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + while (size--) { + emitcode ("pop", "acc"); + aopPut (AOP (result), "a", --offset); + } + freeAsmop (result, NULL, ic, FALSE); + +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset; + unsigned long lit = 0L; + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* if they are the same */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + + /* special case both in far space */ + if (AOP_TYPE (right) == AOP_DPTR && + IS_TRUE_SYMOP (result) && isOperandInFarSpace (result)) { + + genFarFarAssign (result, right, ic); + return; + } + + aopOp (result, ic, TRUE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) { + + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) { + if (((int) operandLitValue (right))) + aopPut (AOP (result), one, 0); + else + aopPut (AOP (result), zero, 0); + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + aopPut (AOP (result), "c", 0); + goto release; + } + + /* we need to or */ + toBoolean (right, "", 0); + aopPut (AOP (result), "a", 0); + goto release; + } + + /* bit variables done */ + /* general case */ + size = AOP_SIZE (result); + offset = 0; + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu. + aop_lit); + if ((size > 1) && (AOP_TYPE (result) != AOP_REG) + && (AOP_TYPE (right) == AOP_LIT) + && !IS_FLOAT (operandType (right)) && (lit < 256L)) { + emitcode ("clr", "a"); + while (size--) { + if ((unsigned int) ((lit >> (size * 8)) & 0x0FFL) == + 0) aopPut (AOP (result), "a", size); + else + aopPut (AOP (result), + aopGet (AOP (right), size), size); + } + } + else { + while (size--) { + aopPut (AOP (result), + aopGet (AOP (right), offset), offset); + offset++; + } + } + + release: + freeAsmop (right, NULL, ic, FALSE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - genrates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + symbol *jtab; + char *l; + + aopOp (IC_JTCOND (ic), ic, FALSE); + /* get the condition into accumulator */ + l = aopGet (AOP (IC_JTCOND (ic)), 0); + MOVA (l); + /* multiply by three */ + emitcode ("add", "a,acc"); + emitcode ("add", "a,%s", aopGet (AOP (IC_JTCOND (ic)), 0)); + freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); + + jtab = newiTempLabel (NULL); + emitcode ("mov", "dptr,#%05d$", jtab->key + 100); + emitcode ("jmp", "@a+dptr"); + emitcode ("", "%05d$:", jtab->key + 100); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; + jtab = setNextItem (IC_JTLABELS (ic))) + emitcode ("ljmp", "%05d$", jtab->key + 100); + +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) { + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) { + if (((int) operandLitValue (right))) + aopPut (AOP (result), one, 0); + else + aopPut (AOP (result), zero, 0); + + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + aopPut (AOP (result), "c", 0); + goto release; + } + + /* we need to or */ + toBoolean (right, "", 0); + aopPut (AOP (result), "a", 0); + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) { + + /* if they are in the same place */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if they in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (right), offset), offset); + offset++; + } + goto release; + } + + + /* if the result is of type pointer */ + if (IS_PTR (ctype)) { + + int p_type; + sym_link *type = operandType (right); + sym_link *etype = getSpec (type); + + /* pointer to generic pointer */ + if (IS_GENPTR (ctype)) { + if (IS_PTR (type)) + p_type = DCL_TYPE (type); + else { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* the first two bytes are known */ + size = GPTRSIZE - 1; + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (right), offset), offset); + offset++; + } + + /* the last byte depending on type */ + { + int gpVal = pointerTypeToGPByte(p_type, NULL, NULL); + char gpValStr[10]; + + if (gpVal == -1) + { + // pointerTypeToGPByte will have bitched. + exit(1); + } + + sprintf(gpValStr, "#0x%x", gpVal); + aopPut (AOP (result), gpValStr, GPTRSIZE - 1); + } + goto release; + } + + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + while (size--) { + aopPut (AOP (result), + aopGet (AOP (right), offset), offset); + offset++; + } + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source */ + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + while (size--) { + aopPut (AOP (result), aopGet (AOP (right), offset), offset); + offset++; + } + + /* now depending on the sign of the source && destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + if (SPEC_USIGN (rtype) || !IS_SPEC (rtype)) { + while (size--) + aopPut (AOP (result), zero, offset++); + } + else { + /* we need to extend the sign :{ */ + // PENDING: Does nothing on avr +#if 0 + char *l = aopGet (AOP (right), AOP_SIZE (right) - 1); + MOVA (l); +#endif + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (AOP (result), "a", offset++); + } + + /* we are done hurray !!!! */ + + release: + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instrucion */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + symbol *lbl, *lbl1; + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form + a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || + !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no + saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + + aopOp (IC_RESULT (ic), ic, FALSE); + + if (IS_AOP_PREG (IC_RESULT (ic))) { + emitcode ("dec", "%s", aopGet (AOP (IC_RESULT (ic)), 0)); + emitcode ("mov", "a,%s", aopGet (AOP (IC_RESULT (ic)), 0)); + emitcode ("jnz", "%05d$", lbl->key + 100); + } + else { + emitcode ("djnz", "%s,%05d$", + aopGet (AOP (IC_RESULT (ic)), 0), lbl->key + 100); + } + emitcode ("sjmp", "%05d$", lbl1->key + 100); + emitcode ("", "%05d$:", lbl->key + 100); + emitcode ("ljmp", "%05d$", IC_TRUE (ifx)->key + 100); + emitcode ("", "%05d$:", lbl1->key + 100); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +static char *recvregs[8] = { + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23" +}; + +static int recvCnt = 0; + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size, offset = 0; + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + while (size--) { + aopPut (AOP (IC_RESULT (ic)), recvregs[recvCnt++], offset); + offset++; + } + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + emitcode ("; genDummyRead",""); + emitcode ("; not implemented",""); + + ic = ic; +} + +/*-----------------------------------------------------------------*/ +/* gen51Code - generate code for 8051 based controllers */ +/*-----------------------------------------------------------------*/ +void +genAVRCode (iCode * lic) +{ + iCode *ic; + int cln = 0; + + lineHead = lineCurr = NULL; + recvCnt = 0; + /* print the allocation information */ + if (allocInfo) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc) { + debugFile->writeFunction (currFunc, lic); + } + /* stack pointer name */ + spname = "sp"; + + + for (ic = lic; ic; ic = ic->next) { + + if (cln != ic->lineno) { + if (options.debug) { + debugFile->writeCLine (ic); + } + emitcode (";", "%s %d", ic->filename, ic->lineno); + cln = ic->lineno; + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop then the if statement might + be using some of the registers being popped which + would destory the contents of the register so + we need to check for this condition and handle it */ + if (ic->next && + ic->next->op == IFX && + regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) + genIfx (ic->next, ic); + else + genIpop (ic); + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + genCmpLe (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case GE_OP: + genCmpGe (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case NE_OP: + genCmpNe (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + genGetHbit (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInc(IC_LEFT(ic),ic)); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInc(IC_RESULT(ic),ic)); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + default: + ic = ic; + } + } + + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&lineHead); + + /* now do the actual printing */ + printLine (lineHead, codeOutBuf); + return; +} diff --git a/src/avr/gen.h b/src/avr/gen.h new file mode 100644 index 0000000..0d3bd0d --- /dev/null +++ b/src/avr/gen.h @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + SDCCgen51.h - header file for code generation for 8051 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGEN51_H +#define SDCCGEN51_H + +enum + { + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_DPTR, AOP_X, AOP_Z, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, AOP_ACC, AOP_STK_D + }; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop + { + + short type; /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_DPTR - dptr contains address of operand + AOP_R0/R1 - r0/r1 contains address of operand + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_ACC - result is in the acc:b pair + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned freed:1; /* already freed */ + union + { + value *aop_lit; /* if literal */ + regs *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + regs *aop_ptr; /* either -> R26 or R30 */ + char *aop_immd; /* if immediate others are implied */ + int aop_stk; /* stack offset when AOP_STK */ + char *aop_str[4]; /* just a string array containing the location */ + } + aopu; + regs *aop_ptr2; /* either -> R27 or R31 */ + } +asmop; + +void genAVRCode (iCode *); +void avr_emitDebuggerSymbol (const char *); + +extern char *fReturn8051[]; +extern char *fReturn390[]; +extern unsigned fAVRReturnSize; +extern char **fAVRReturn; + +#endif diff --git a/src/avr/main.c b/src/avr/main.c new file mode 100644 index 0000000..0d62d84 --- /dev/null +++ b/src/avr/main.c @@ -0,0 +1,290 @@ +/** @file main.c + avr specific general functions. + + Note that mlh prepended _avr_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" + +static char _defaultRules[] = { +#include "peeph.rul" +}; + +/* list of key words used by msc51 */ +static char *_avr_keywords[] = { + "at", + "code", + "critical", + "eeprom", + "interrupt", + "sfr", + "xdata", + "_code", + "_eeprom", + "_generic", + "_xdata", + "sram", + "_sram", + "flash", + "_flash", + NULL +}; + +static int regParmFlg = 0; /* determine if we can register a parameter */ + +static void +_avr_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_avr_reset_regparm (void) +{ + regParmFlg = 0; +} + +static int +_avr_regparm (sym_link * l, bool reentrant) +{ + /* the first eight bytes will be passed in + registers r16-r23. but we won't split variables + i.e. if not enough registers left to hold + the parameter then the whole parameter along + with rest of the parameters go onto the stack */ + if (regParmFlg < 8) { + int size; + if ((size = getSize (l)) > (8 - regParmFlg)) { + /* all remaining go on stack */ + regParmFlg = 8; + return 0; + } + regParmFlg += size; + return 1; + } + + return 0; +} + +void avr_assignRegisters (ebbIndex *); + +static bool +_avr_parseOptions (int *pargc, char **argv, int *i) +{ + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + return FALSE; +} + +static void +_avr_finaliseOptions (void) +{ + port->mem.default_local_map = port->mem.default_globl_map = xdata; + /* change stack to be in far space */ + /* internal stack segment ; + SFRSPACE - NO + FAR-SPACE - YES + PAGED - NO + DIRECT-ACCESS - NO + BIT-ACCESS - NO + CODE-ACESS - NO + DEBUG-NAME - 'B' + POINTER-TYPE - FPOINTER + */ + istack = + allocMap (0, 1, 0, 0, 0, 0, options.stack_loc, ISTACK_NAME, + 'B', FPOINTER); + + /* also change xdata to be direct space since we can use lds/sts */ + xdata->direct = 1; + +} + +static void +_avr_setDefaultOptions (void) +{ + options.stackAuto = 1; +} + +static const char * +_avr_getRegName (struct regs *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_avr_genAssemblerPreamble (FILE * of) +{ + +} + +/* Generate interrupt vector table. */ +static int +_avr_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + return TRUE; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETHBIT + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = { + "linkavr", "", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = { + "asavr", "$l" , "$3", "$1.s", NULL +}; + +/* Globals */ +PORT avr_port = { + TARGET_ID_AVR, + "avr", + "ATMEL AVR", /* Target name */ + NULL, /* processor */ + { + glue, + TRUE, /* Emit glue around main */ + MODEL_LARGE | MODEL_SMALL, + MODEL_SMALL, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-plosgff", /* Options with debug */ + "-plosgff", /* Options without debug */ + 0, + ".s", + NULL, /* no do_assemble */ + }, + { + _linkCmd, + NULL, + NULL, + ".rel", + 1}, + { + _defaultRules}, + { + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + 1, 2, 2, 4, 8, 2, 2, 3, 1, 4, 4}, + + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + + { + "XSEG", + "STACK", + "CSEG", + "DSEG", + "ISEG", + NULL, //PSEG + "XSEG", + "BSEG", + "RSEG", + "GSINIT", + "OSEG", + "GSFINAL", + "HOME", + NULL, // initialized xdata + NULL, // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, + NULL, + 0, + }, + { NULL, NULL }, + { + -1, 1, 4, 1, 1, 0, 1}, + /* avr has an 8 bit mul */ + { + 1, -1, FALSE + }, + { + avr_emitDebuggerSymbol + }, + { + 255/3, /* maxCount */ + 3, /* sizeofElement */ + /* The rest of these costs are bogus. They approximate */ + /* the behavior of src/SDCCicode.c 1.207 and earlier. */ + {4,4,4}, /* sizeofMatchJump[] */ + {0,0,0}, /* sizeofRangeCompare[] */ + 0, /* sizeofSubtract */ + 3, /* sizeofDispatch */ + }, + "_", + _avr_init, + _avr_parseOptions, + NULL, + NULL, + _avr_finaliseOptions, + _avr_setDefaultOptions, + avr_assignRegisters, + _avr_getRegName, + _avr_keywords, + _avr_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _avr_genIVT, + NULL, // _avr_genXINIT + NULL, /* genInitStartup */ + _avr_reset_regparm, + _avr_regparm, + NULL, + NULL, + NULL, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, + TRUE, /* little endian */ + 0, /* leave lt */ + 1, /* transform gt ==> not le */ + 0, /* leave le */ + 0, /* leave ge */ + 0, /* leave != */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + 0, /* no CSE cost estimation yet */ + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local static allowed */ + PORT_MAGIC +}; diff --git a/src/avr/main.h b/src/avr/main.h new file mode 100644 index 0000000..6555225 --- /dev/null +++ b/src/avr/main.h @@ -0,0 +1,8 @@ +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + +#endif diff --git a/src/avr/peeph.def b/src/avr/peeph.def new file mode 100644 index 0000000..5d111fb --- /dev/null +++ b/src/avr/peeph.def @@ -0,0 +1,1701 @@ +//replace restart { +// pop %1 +// push %1 +//} by { +// ; Peephole 1 removed pop %1 push %1 (not push pop) +//} + +//replace restart { +// pop %1 +// mov %2,%3 +// push %1 +//} by { +// ; Peephole 2 removed pop %1 push %1 (not push pop) +// mov %2,%3 +//} + +// +// added by Jean Louis VERN for +// his shift stuff +replace restart { + xch a,%1 + xch a,%1 +} by { + ; Peephole 2.a removed redundant xch xch +} + +replace restart { +// saving 2 byte + mov %1,#0x00 + mov a,#0x00 +} by { + ; Peephole 3.a changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte + mov %1,#0x00 + clr a +} by { + ; Peephole 3.b changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte, loosing 1 cycle but maybe allowing peephole 3.b to start + mov %1,#0x00 + mov %2,#0x00 + mov a,%3 +} by { + ; Peephole 3.c changed mov to clr + clr a + mov %1,a + mov %2,a + mov a,%3 +} + + + +replace { + mov %1,a + mov dptr,#%2 + mov a,%1 + movx @dptr,a +} by { + ; Peephole 100 removed redundant mov + mov %1,a + mov dptr,#%2 + movx @dptr,a +} + +replace { + mov a,acc +} by { + ; Peephole 100.a removed redundant mov +} + +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + ; Peephole 101 removed redundant mov + mov a,%1 + movx @dptr,a + inc dptr + movx @dptr,a +} + +replace { + mov %1,%2 + ljmp %3 +%4: + mov %1,%5 +%3: + mov dpl,%1 +%7: + mov sp,bp + pop bp +} by { + ; Peephole 102 removed redundant mov + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,bp + pop bp +} + +replace { + mov %1,%2 + ljmp %3 +%4: + mov a%1,%5 +%3: + mov dpl,%1 +%7: + mov sp,bp + pop bp +} by { + ; Peephole 103 removed redundant mov + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,bp + pop bp +} + +replace { + mov a,bp + clr c + add a,#0x01 + mov r%1,a +} by { + ; Peephole 104 optimized increment (acc not set to r%1, flags undefined) + mov r%1,bp + inc r%1 +} + +replace { + mov %1,a + mov a,%1 +} by { + ; Peephole 105 removed redundant mov + mov %1,a +} + +replace { + mov %1,a + clr c + mov a,%1 +} by { + ; Peephole 106 removed redundant mov + mov %1,a + clr c +} + +replace { + ljmp %1 +%1: +} by { + ; Peephole 107 removed redundant ljmp +%1: +} + +replace { + jc %1 + ljmp %5 +%1: +} by { + ; Peephole 108 removed ljmp by inverse jump logic + jnc %5 +%1: +} if labelInRange + +replace { + jz %1 + ljmp %5 +%1: +} by { + ; Peephole 109 removed ljmp by inverse jump logic + jnz %5 +%1: +} if labelInRange + +replace { + jnz %1 + ljmp %5 +%1: +} by { + ; Peephole 110 removed ljmp by inverse jump logic + jz %5 +%1: +} if labelInRange + +replace { + jb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 111 removed ljmp by inverse jump logic + jnb %1,%5 +%2: +} if labelInRange + +replace { + jnb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 112 removed ljmp by inverse jump logic + jb %1,%5 +%2: +} if labelInRange + +replace { + ljmp %5 +%1: +} by { + ; Peephole 132 changed ljmp to sjmp + sjmp %5 +%1: +} if labelInRange + + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113 optimized misc sequence + clr %4 + cjne %1,%2,%3 + setb %4 +%3: +} + +replace { + clr a + cjne %1,%2,%3 + cjne %10,%11,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 114 optimized misc sequence + clr %4 + cjne %1,%2,%3 + cjne %10,%11,%3 + setb %4 +%3: +} + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115 jump optimization + cjne %1,%2,%3 + sjmp %4 +%3: +} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 116 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + sjmp %4 +%3: +} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 117 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + sjmp %4 +%3: +} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 118 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + sjmp %4 +%3: +} +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 119 jump optimization + cjne %1,%2,%4 +%3: +} + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 120 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 +%3: +} +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 121 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 +%3: +} + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 122 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 + cjne %14,%15,%4 +%3: +} + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 123 jump optimization + cjne %1,%2,%3 + smp %4 +%3: +} +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 124 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + smp %4 +%3: +} + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 125 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + sjmp %4 +%3: +} + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 126 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + sjmp %4 +%3: +} + +replace { + push psw + mov psw,%1 + push bp + mov bp,%2 +%3: + mov %2,bp + pop bp + pop psw + ret +} by { + ; Peephole 127 removed misc sequence + ret +} + +replace { + clr a + rlc a + jz %1 +} by { + ; Peephole 128 jump optimization + jnc %1 +} + +replace { + clr a + rlc a + jnz %1 +} by { + ; Peephole 129 jump optimization + jc %1 +} + +replace { + mov r%1,@r%2 +} by { + ; Peephole 130 changed target address mode r%1 to ar%1 + mov ar%1,@r%2 +} + +replace { + mov a,%1 + subb a,#0x01 + mov %2,a + mov %1,%2 +} by { + ; Peephole 131 optimized decrement (not caring for c) + dec %1 + mov %2,%1 +} + +replace { + mov r%1,%2 + mov ar%3,@r%1 + inc r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + ; Peephole 133 removed redundant moves + mov r%1,%2 + inc @r%1 + mov ar%3,@r%1 +} + +replace { + mov r%1,%2 + mov ar%3,@r%1 + dec r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + ; Peephole 134 removed redundant moves + mov r%1,%2 + dec @r%1 + mov ar%3,@r%1 +} + +replace { + mov r%1,a + mov a,r%2 + orl a,r%1 +} by { + ; Peephole 135 removed redundant mov + mov r%1,a + orl a,r%2 +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov dpx,%4 + mov a,%1 +} by { + ; Peephole 136a removed redundant moves + mov %1,a + mov dpl,%2 + mov dph,%3 + mov dpx,%4 +} if 24bitMode + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov a,%1 +} by { + ; Peephole 136 removed redundant moves + mov %1,a + mov dpl,%2 + mov dph,%3 +} + +replace { + mov b,#0x00 + mov a,%1 + cjne %2,%3,%4 + mov b,#0x01 +%4: + mov a,b + jz %5 +} by { + ; Peephole 137 optimized misc jump sequence + mov a,%1 + cjne %2,%3,%5 +%4: +} + +replace { + mov b,#0x00 + mov a,%1 + cjne %2,%3,%4 + mov b,#0x01 +%4: + mov a,b + jnz %5 +} by { + ; Peephole 138 optimized misc jump sequence + mov a,%1 + cjne %2,%3,%4 + sjmp %5 +%4: +} + +replace { + mov r%1,a + anl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139 removed redundant mov + anl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + orl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 140 removed redundant mov + orl a,%2 + mov r%1,a } + +replace { + mov r%1,a + xrl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 141 removed redundant mov + xrl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + mov r%2,ar%1 + mov ar%1,@r%2 +} by { + ; Peephole 142 removed redundant moves + mov r%2,a + mov ar%1,@r%2 +} + +replace { + rlc a + mov acc.0,c +} by { + ; Peephole 143 converted rlc to rl + rl a +} + +replace { + rrc a + mov acc.7,c +} by { + ; Peephole 144 converted rrc to rc + rr a +} + +replace { + clr c + addc a,%1 +} by { + ; Peephole 145 changed to add without carry + add a,%1 +} + +replace { + clr c + mov a,%1 + addc a,%2 +} by { + ; Peephole 146 changed to add without carry + mov a,%1 + add a,%2 +} + +replace { + orl r%1,a +} by { + ; Peephole 147 changed target address mode r%1 to ar%1 + orl ar%1,a +} + +replace { + anl r%1,a +} by { + ; Peephole 148 changed target address mode r%1 to ar%1 + anl ar%1,a +} + +replace { + xrl r%1,a +} by { + ; Peephole 149 changed target address mode r%1 to ar%1 + xrl ar%1,a +} + +replace { + mov %1,dpl + mov dpl,%1 +%9: + ret +} by { + ; Peephole 150 removed misc moves via dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 151 removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 +%9: + ret +} by { + ; Peephole 152 removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 + mov dph,%2 + mov b,%3 +%9: + ret +} by { + ; Peephole 153 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 +%9: + ret +} by { + ; Peephole 154 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 155 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 + mov dph,%2 + mov b,%3 + mov a,%4 +%9: + ret +} by { + ; Peephole 156 removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 157 removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 +%9: + ret } by { + ; Peephole 158 removed misc moves via dph, dpl, b, a before return +%9: + ret } + +replace { + mov %1,#%2 + xrl %1,#0x80 +} by { + ; Peephole 159 avoided xrl during execution + mov %1,#(%2 ^ 0x80) +} + +replace { + jnc %1 + sjmp %2 +%1: +} by { + ; Peephole 160 removed sjmp by inverse jump logic + jc %2 +%1:} + +replace { + jc %1 + sjmp %2 +%1: +} by { + ; Peephole 161 removed sjmp by inverse jump logic + jnc %2 +%1:} + +replace { + jnz %1 + sjmp %2 +%1: +} by { + ; Peephole 162 removed sjmp by inverse jump logic + jz %2 +%1:} + +replace { + jz %1 + sjmp %2 +%1: +} by { + ; Peephole 163 removed sjmp by inverse jump logic + jnz %2 +%1:} + +replace { + jnb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 164 removed sjmp by inverse jump logic + jb %3,%2 +%1: +} + +replace { + jb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 165 removed sjmp by inverse jump logic + jnb %3,%2 +%1: +} + +replace { + mov %1,%2 + mov %3,%1 + mov %2,%1 +} by { + ; Peephole 166 removed redundant mov + mov %1,%2 + mov %3,%1 } + +replace { + mov c,%1 + cpl c + mov %1,c +} by { + ; Peephole 167 removed redundant bit moves (c not set to %1) + cpl %1 } + +replace { + jnb %1,%2 + sjmp %3 +%2:} by { + ; Peephole 168 jump optimization + jb %1,%3 +%2:} + +replace { + jb %1,%2 + sjmp %3 +%2:} by { + ; Peephole 169 jump optimization + jnb %1,%3 +%2:} + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 170 jump optimization + cjne %1,%2,%4 +%3:} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 171 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 +%3:} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 172 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 +%3:} + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 173 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 + cjne %13,%14,%4 +%3:} + +replace { + mov r%1,%2 + clr c + mov a,r%1 + subb a,#0x01 + mov %2,a +} by { + ; Peephole 174 optimized decrement (acc not set to %2, flags undefined) + mov r%1,%2 + dec %2 +} + + +replace { + mov r%1,%2 + mov a,r%1 + add a,#0x01 + mov %2,a +} by { + ; Peephole 175 optimized increment (acc not set to %2, flags undefined) + mov r%1,%2 + inc %2 +} + +replace { + mov %1,@r%2 + inc %1 + mov @r%2,%1 +} by { + ; Peephole 176 optimized increment, removed redundant mov + inc @r%2 + mov %1,@r%2 +} + +replace { + mov %1,%2 + mov %2,%1 +} by { + ; Peephole 177 removed redundant mov + mov %1,%2 +} + +replace { + mov a,%1 + mov b,a + mov a,%2 +} by { + ; Peephole 178 removed redundant mov + mov b,%1 + mov a,%2 +} + +// rules 179-182 provided by : Frieder +// saving 2 byte, 1 cycle +replace { + mov b,#0x00 + mov a,#0x00 +} by { + ; Peephole 179 changed mov to clr + clr a + mov b,a +} + +// saving 1 byte, 0 cycles +replace { + mov a,#0x00 +} by { + ; Peephole 180 changed mov to clr + clr a +} + +replace { + mov dpl,#0x00 + mov dph,#0x00 + mov dpx,#0x00 +} by { + ; Peephole 181a used 24 bit load of dptr + mov dptr,#0x0000 +} if 24bitMode + +// saving 3 byte, 2 cycles, return(NULL) profits here +replace { + mov dpl,#0x00 + mov dph,#0x00 +} by { + ; Peephole 181 used 16 bit load of dptr + mov dptr,#0x0000 +} + +// saves 2 bytes, ?? cycles. +replace { + mov dpl,#%1 + mov dph,#(%1 >> 8) + mov dpx,#(%1 >> 16) +} by { + ; Peephole 182a used 24 bit load of dptr + mov dptr,#%1 +} if 24bitMode + +// saving 3 byte, 2 cycles, return(float_constant) profits here +replace { + mov dpl,#%1 + mov dph,#%2 +} by { + ; Peephole 182 used 16 bit load of dptr + mov dptr,#(((%2)<<8) + %1) +} + +replace { + anl %1,#%2 + anl %1,#%3 +} by { + ; Peephole 183 avoided anl during execution + anl %1,#(%2 & %3) +} + +replace { + mov %1,a + cpl a + mov %1,a +} by { + ; Peephole 184 removed redundant mov + cpl a + mov %1,a +} + +replace { +// acc being incremented might cause problems + mov %1,a + inc %1 +} by { + ; Peephole 185 changed order of increment (acc incremented also!) + inc a + mov %1,a +} + +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr + mov %4,a + inc dptr + clr a +} by { + ; Peephole 186.a optimized movc sequence + mov dptr,#%1 + mov b,acc + movc a,@a+dptr + mov %2,a + mov acc,b + inc dptr + movc a,@a+dptr + mov %3,a + mov acc,b + inc dptr + mov %4,a + mov acc,b + inc dptr +} + +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a +} by { + ; Peephole 186.b optimized movc sequence + mov dptr,#%1 + mov b,acc + movc a,@a+dptr + mov %2,a + mov acc,b + inc dptr + movc a,@a+dptr + mov %3,a + mov acc,b + inc dptr +} + +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a +} by { + ; Peephole 186.c optimized movc sequence + mov dptr,#%1 + mov b,acc + movc a,@a+dptr + mov %2,a + mov acc,b + inc dptr +} + +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr +} by { + ; Peephole 186 optimized movc sequence + mov dptr,#%1 + movc a,@a+dptr +} + +replace { + mov r%1,%2 + anl ar%1,#%3 + mov a,r%1 +} by { + ; Peephole 187 used a instead of ar%1 for anl + mov a,%2 + anl a,#%3 + mov r%1,a +} + +replace { + mov %1,a + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} by { + ; Peephole 188 removed redundant mov + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} + +replace { + anl a,#0x0f + mov %1,a + mov a,#0x0f + anl a,%1 +} by { + ; Peephole 189 removed redundant mov and anl + anl a,#0x0f + mov %1,a +} + +// rules 190 & 191 need to be in order +replace { + mov a,%1 + lcall __gptrput + mov a,%1 +} by { + ; Peephole 190 removed redundant mov + mov a,%1 + lcall __gptrput +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + ; Peephole 191 removed redundant mov + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 +} + +replace { + mov r%1,a + mov @r%2,ar%1 +} by { + ; Peephole 192 used a instead of ar%1 as source + mov r%1,a + mov @r%2,a +} + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + jnz %3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + jnz %8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 194 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + cjne %13,%14,%8 + sjmp %7 +%3: +} + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 196 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + sjmp %7 +%3: +} + +replace { + jnz %3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.a optimized misc jump sequence + jnz %8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 198 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + sjmp %7 +%3: +} + +replace { + cjne %1,%2,%3 + sjmp %4 +%3: + sjmp %5 +} by { + ; Peephole 199 optimized misc jump sequence + cjne %1,%2,%5 + sjmp %4 +%3: +} + +replace { + sjmp %1 +%1: +} by { + ; Peephole 200 removed redundant sjmp +%1: +} + +replace { + sjmp %1 +%2: +%1: +} by { + ; Peephole 201 removed redundant sjmp +%2: +%1: +} + +replace { + push acc + mov dptr,%1 + pop acc +} by { + ; Peephole 202 removed redundant push pop + mov dptr,%1 +} + +replace { + mov r%1,_spx + lcall %2 + mov r%1,_spx +} by { + ; Peephole 203 removed mov r%1,_spx + lcall %2 +} + +replace { + mov %1,a + add a,acc + mov %1,a +} by { + ; Peephole 204 removed redundant mov + add a,acc + mov %1,a +} + +replace { + djnz %1,%2 + sjmp %3 +%2: + sjmp %4 +%3: +} by { + ; Peephole 205 optimized misc jump sequence + djnz %1,%4 +%2: +%3: +} + +replace { + mov %1,%1 +} by { + ; Peephole 206 removed redundant mov %1,%1 +} + +replace { + mov a,_bp + add a,#0x00 + mov %1,a +} by { + ; Peephole 207 removed zero add (acc not set to %1, flags undefined) + mov %1,_bp +} + +replace { + push acc + mov r%1,_bp + pop acc +} by { + ; Peephole 208 removed redundant push pop + mov r%1,_bp +} + +replace { + mov a,_bp + add a,#0x00 + inc a + mov %1,a +} by { + ; Peephole 209 optimized increment (acc not set to %1, flags undefined) + mov %1,_bp + inc %1 +} + +replace { + mov dptr,#((((%1 >> 16)) <<16) + (((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210a simplified expression + mov dptr,#%1 +} if 24bitMode + +replace { + mov dptr,#((((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210 simplified expression + mov dptr,#%1 +} + +replace { + push %1 + pop %1 +} by { + ; Peephole 211 removed redundant push %1 pop %1 +} + +replace { + mov a,_bp + add a,#0x01 + mov r%1,a +} by { + ; Peephole 212 reduced add sequence to inc + mov r%1,_bp + inc r%1 +} + +replace { + mov %1,#(( %2 >> 8 ) ^ 0x80) +} by { + mov %1,#(%2 >> 8) + xrl %1,#0x80 +} + +replace { + mov %1,#(( %2 + %3 >> 8 ) ^ 0x80) +} by { + mov %1,#((%2 + %3) >> 8) + xrl %1,#0x80 +} + +replace { + mov %1,a + mov a,%2 + add a,%1 +} by { + ; Peephole 214 reduced some extra movs + mov %1,a + add a,%2 +} if notSame(%1 %2) + +replace { + mov %1,a + add a,%2 + mov %1,a +} by { + ; Peephole 215 removed some movs + add a,%2 + mov %1,a +} if notSame(%1 %2) + +replace { + mov r%1,%2 + clr a + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 216 simplified clear (2bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 217 simplified clear (3bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 218 simplified clear (4bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + clr a + movx @dptr,a +} by { + ; Peephole 219 removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + clr a + movx @dptr,a +} by { + ; Peephole 219a removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + movx @dptr,a +} diff --git a/src/avr/ralloc.c b/src/avr/ralloc.c new file mode 100644 index 0000000..2026c06 --- /dev/null +++ b/src/avr/ralloc.c @@ -0,0 +1,2307 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. (ATMEL AVR) specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +extern void genAVRCode (iCode *); + +/* Global data */ +static struct { + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; +} _G; + +/* Shared with gen.c */ +int avr_ptrRegReq; /* pointer register required */ + +/* AVR registers */ +regs regsAVR[] = { + {REG_GPR|REG_PAIR, R0_IDX, REG_GPR|REG_PAIR, "r0", "r0", "", 0, 0, 0}, /* scratch */ + {REG_GPR, R1_IDX, REG_GPR , "r1", "r1", "", 0, 0, 0}, /* scratch */ + {REG_GPR|REG_PAIR, R2_IDX, REG_GPR|REG_PAIR, "r2", "r2", "", 0, 1, 1}, /* gpr */ + {REG_GPR, R3_IDX, REG_GPR , "r3", "r3", "", 0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R4_IDX, REG_GPR|REG_PAIR, "r4", "r4", "", 0, 1, 1}, /* gpr */ + {REG_GPR, R5_IDX, REG_GPR , "r5", "r5", "", 0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R6_IDX, REG_GPR|REG_PAIR, "r6", "r6", "", 0, 1, 1}, /* gpr */ + {REG_GPR, R7_IDX, REG_GPR , "r7", "r7", "", 0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R8_IDX, REG_GPR|REG_PAIR, "r8", "r8", "", 0, 1, 1}, /* gpr */ + {REG_GPR, R9_IDX, REG_GPR , "r9", "r9", "", 0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R10_IDX,REG_GPR|REG_PAIR, "r10", "r10","",0, 1, 1}, /* gpr */ + {REG_GPR, R11_IDX,REG_GPR , "r11", "r11","",0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R12_IDX,REG_GPR|REG_PAIR, "r12", "r12","",0, 1, 1}, /* gpr */ + {REG_GPR, R13_IDX,REG_GPR , "r13", "r13","",0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R14_IDX,REG_GPR|REG_PAIR, "r14", "r14","",0, 1, 1}, /* gpr */ + {REG_GPR, R15_IDX,REG_GPR , "r15", "r15","",0, 1, 1}, /* gpr */ + {REG_GPR|REG_PAIR, R16_IDX,REG_GPR|REG_PAIR, "r16", "r16","",0, 1, 0}, /* parm/gpr */ + {REG_GPR, R17_IDX,REG_GPR , "r17", "r17","",0, 1, 0}, /* parm/gpr */ + {REG_GPR|REG_PAIR, R18_IDX,REG_GPR|REG_PAIR, "r18", "r18","",0, 1, 0}, /* parm/gpr */ + {REG_GPR, R19_IDX,REG_GPR , "r19", "r19","",0, 1, 0}, /* parm/gpr */ + {REG_GPR|REG_PAIR, R20_IDX,REG_GPR|REG_PAIR, "r20", "r20","",0, 1, 0}, /* parm/gpr */ + {REG_GPR, R21_IDX,REG_GPR , "r21", "r21","",0, 1, 0}, /* parm/gpr */ + {REG_GPR|REG_PAIR, R22_IDX,REG_GPR|REG_PAIR, "r22", "r22","",0, 1, 0}, /* parm/gpr */ + {REG_GPR, R23_IDX,REG_GPR , "r23", "r23","",0, 1, 0}, /* parm/gpr */ + {REG_GPR|REG_PAIR, R24_IDX,REG_GPR|REG_PAIR, "r24", "r24","",0, 0, 0}, /* scratch */ + {REG_GPR, R25_IDX,REG_GPR , "r25", "r25","",0, 0, 0}, /* scratch */ + {REG_GPR|REG_PAIR, R26_IDX,REG_GPR|REG_PAIR, "r26", "r26","",0, 1, 1}, /* used as pointer reg X */ + {REG_GPR, R27_IDX,REG_GPR , "r27", "r27","",0, 1, 1}, /* used as pointer reg X */ + {REG_GPR|REG_PAIR, R28_IDX,REG_GPR|REG_PAIR, "r28", "r28","",0, 1, 0}, /* stack frame Y */ + {REG_GPR, R29_IDX,REG_GPR , "r29", "r29","",0, 1, 0}, /* stack frame Y */ + {REG_GPR|REG_PAIR, R30_IDX,REG_GPR|REG_PAIR, "r30", "r30","",0, 1, 1}, /* used as pointer reg Z */ + {REG_GPR, R31_IDX,REG_GPR , "r31", "r31","",0, 1, 1}, /* used as pointer reg Z */ + {REG_PTR, X_IDX, REG_PTR, "X", "X", "", 0, 1, 0}, + {REG_PTR, Z_IDX, REG_PTR, "Z", "Z", "", 0, 1, 0}, +}; +int avr_nRegs = 32; +int avr_fReg = 0; /* first allocatable register */ + +static void spillThis (symbol *); + +#if 0 +// PENDING: Unused +/*-----------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*-----------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + for (dic = ic->prev; dic; dic = dic->prev) { + + /* if definition by assignment */ + if (dic->op == '=' && + !POINTER_SET (dic) && IC_RESULT (dic)->key == op->key +/* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + ) { + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then return + true */ + if (!IS_SYMOP (IC_RIGHT (dic))) + break; + + /* if the symbol is in far space then + we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && + OP_SYMBOL (IC_RIGHT (dic))->onStack) { + + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key + && IC_LEFT (ic)->key != + IC_RIGHT (dic)->key + && IC_RIGHT (ic)->key != + IC_RIGHT (dic)->key) return NULL; + } + + break; + + } + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; + + +} + +/*-----------------------------------------------------------------*/ +/* packForPush - hueristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + + if (ic->op != IPUSH || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS + (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + IC_LEFT (ic) = IC_RIGHT (dic); + + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + int change = 0; + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) { + iCode *dic = findAssignToSym (IC_LEFT (ic), ic); + iCode *sic; + + if (!dic) + goto right; + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_LEFT (ic)->key); + + IC_LEFT (ic)->operand.symOperand = + IC_RIGHT (dic)->operand.symOperand; + IC_LEFT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + /* do the same for the right operand */ + right: + if (!change && + IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + iCode *sic; + + if (!dic) + return change; + + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) { + sym_link *etype = + getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return change; + } + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_RIGHT (ic)->key); + + IC_RIGHT (ic)->operand.symOperand = + IC_RIGHT (dic)->operand.symOperand; + IC_RIGHT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + return change; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) { + + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && + getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > + 1) return NULL; + + if (POINTER_SET (dic) && + getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) + > 1) + return NULL; + + /* if any three is a true symbol in far space */ + if (IC_RESULT (dic) && + IS_TRUE_SYMOP (IC_RESULT (dic)) && + isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && + !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && + !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) { + if ((dic->op == LEFT_OP || + dic->op == RIGHT_OP || + dic->op == '-') && + IS_OP_LITERAL (IC_RIGHT (dic))) return NULL; + else + return dic; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static char * +rematStr (symbol * sym) +{ + char *s = buffer; + iCode *ic = sym->rematiCode; + + while (1) { + + /* if plus or minus print the right hand side */ + if (ic->op == '+' || ic->op == '-') { + sprintf (s, "0x%04x %c ", + (int) operandLitValue (IC_RIGHT (ic)), + ic->op); + s += strlen (s); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* we reached the end */ + sprintf (s, "%s", OP_SYMBOL (IC_LEFT (ic))->rname); + break; + } + + return buffer; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + + + if (!sym->usl.spillLoc) + return FALSE; + + etype = getSpec (sym->usl.spillLoc->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + regs *X, *Z, *X1, *Z1; + int k; + + if (!_G.regAssigned || bitVectIsZero (_G.regAssigned)) + return; + + X = avr_regWithIdx (R26_IDX); + X1= avr_regWithIdx (R27_IDX); + Z = avr_regWithIdx (R30_IDX); + Z1= avr_regWithIdx (R31_IDX); + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; + lrsym = hTabNextItem (liveRanges, &k)) { + int j; + + /* if no registers assigned to it or + spilt */ + /* if it does not overlap with this then + not need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || + (lrsym->liveTo < forSym->liveFrom)) continue; + + /* go thru the registers : if it is either + r0 or r1 then spil it */ + for (j = 0; j < lrsym->nRegs; j++) + if (lrsym->regs[j] == X || lrsym->regs[j] == Z || + lrsym->regs[j] == X1 || lrsym->regs[j] == Z1) { + spillThis (lrsym); + break; + } + } + +} +#endif + +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static regs * +allocReg (short type) +{ + int i; + + for (i = avr_fReg; i < avr_nRegs; i++) { + + /* if type is given as 0 then any + free register will do */ + if (!type && regsAVR[i].isFree) { + regsAVR[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = + bitVectSetBit (currFunc->regsUsed, i); + return ®sAVR[i]; + } + + /* other wise look for specific type + of register */ + if (regsAVR[i].isFree && (regsAVR[i].type & type)) { + regsAVR[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = + bitVectSetBit (currFunc->regsUsed, i); + return ®sAVR[i]; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* allocRegPair - allocates register pair of given */ +/*-----------------------------------------------------------------*/ +static regs * +allocRegPair (short type) +{ + int i; + + for (i = avr_fReg; i < avr_nRegs; i++) { + + /* look for specific type of register pair */ + if (regsAVR[i].isFree && (regsAVR[i].type & type) + && (regsAVR[i].type & REG_PAIR) && regsAVR[i+1].isFree) { + + regsAVR[i].isFree = 0; + regsAVR[i+1].isFree = 0; + if (currFunc) { + currFunc->regsUsed = + bitVectSetBit (currFunc->regsUsed, i); + currFunc->regsUsed = + bitVectSetBit (currFunc->regsUsed, i+1); + } + return ®sAVR[i]; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* avr_regWithIdx - returns pointer to register wit index number */ +/*-----------------------------------------------------------------*/ +regs * +avr_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < avr_nRegs; i++) + if (regsAVR[i].rIdx == idx) + return ®sAVR[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (regs * reg) +{ + reg->isFree = 1; +} + + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ + int i; + int nfr = 0; + + for (i = avr_fReg; i < avr_nRegs; i++) + if (regsAVR[i].isFree && regsAVR[i].type & type) + nfr++; + return nfr; +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + if (type == REG_PTR) { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; + +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 0 : 1); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil location */ +/* but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((sym->usl.spillLoc && !sym->uptr) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && + allDefsOutOfRange (sym->defs, ic->seq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, + int (func) (symbol *, eBBlock *, iCode *), + eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) + && bitVectBitValue (_G.regAssigned, + sym->key)) addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) { + + /* if usage is the same then prefer + the spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) { + if (sym->liveTo > fsym->liveFrom) + return 0; + + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model, noOverlay; + int stackAuto; + + char slocBuffer[30]; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + if (sprintf (slocBuffer, "sloc%d", _G.slocNum++) >= + sizeof (slocBuffer)) { + fprintf (stderr, + "***Internal error: slocBuffer overflowed: %s:%d\n", + __FILE__, __LINE__); + exit (1); + } + + sloc = newiTemp (slocBuffer); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_AUTO; + SPEC_EXTR (sloc->etype) = 0; + + /* we don't allow it to be allocated` + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + and turn off overlaying + */ + + useXstack = options.useXstack; + model = options.model; + noOverlay = options.noOverlay; + stackAuto = options.stackAuto; + options.noOverlay = 1; + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; + options.noOverlay = noOverlay; + options.stackAuto = stackAuto; + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + + /* mark it has spilt & put it in the spilt set */ + sym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + + if (sym->regs[i]) { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq = 1; + return; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* get all live ranges that are rematerizable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) { + + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) { + + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && + (selectS = + liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that not + used in the remainder of the block */ + if (!_G.blockSpil && + !isiCodeInFunctionCall (ic) && + (selectS = + liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) { + sym = leastUsedLR (selectS); + if (sym != forSym) { + if (!sym->remat) { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) { + + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) { + + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack , for which one? the least + used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) { + + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; + +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? + ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) { + + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static regs * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + regs *reg; + + tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR|REG_PAIR))) + return reg; + + /* try for gpr type / pair */ + if ((reg = allocReg (REG_GPR|REG_PAIR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegScr - will try for SCR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static regs * +getRegScr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + regs *reg; + + tryAgain: + + /* try for a scratch non-pair */ + if ((reg = allocReg (REG_SCR))) + return reg; + + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static regs * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym ) +{ + regs *reg; + + tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!avr_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol * sym, regs * reg) +{ + int i; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + symbol *result; + + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) { + + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) { + if (sym->stackSpil) { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && + ic->prev->op == IPOP && + !ic->prev->parmPush && + !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && !(SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == RETURN || + POINTER_SET (ic)) && + (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && + !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + sym->nRegs) >= result->nRegs)) { + + for (i = 0; i < result->nRegs; i++) { + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else if (result->regType == REG_SCR) + result->regs[i] = getRegScr (ic, ebp, result); + else + result->regs[i] = getRegGpr (ic, ebp, result); + } + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) { + if (psym) { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + /* first check if there are any avlb registers + of te type required */ + if (rt == REG_PTR) { + /* special case for pointer type + if pointer type not avlb then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return 0; + if (nFreeRegs (REG_GPR) >= nr) + return 0; + } + else { + if (avr_ptrRegReq) { + if (nFreeRegs (rt) >= nr) + return 0; + } + else { + if (nFreeRegs (REG_PTR) + nFreeRegs (REG_GPR) >= nr) + return 0; + } + } + + /* it will cause a spil */ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to res- */ +/* ult and operand, if this happens make sure they are in the same */ +/* position as the operand otherwise chaos results */ +/*-----------------------------------------------------------------*/ +static void +positionRegs (symbol * result, symbol * opsym, int lineno) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return; + again: + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + if (result->regs[i] == opsym->regs[j] && i != j) { + shared = 1; + goto xchgPositions; + } + } + } + xchgPositions: + if (shared) { + regs *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + goto again; + } +} + +/*-----------------------------------------------------------------*/ +/* needsPair - heuristic to determine if a pair would be good */ +/*-----------------------------------------------------------------*/ +static int needsPair (iCode *ic) +{ + symbol *sym = OP_SYMBOL(IC_RESULT(ic)); + bitVect *uses_defs = + bitVectUnion(OP_USES (IC_RESULT(ic)),OP_DEFS(IC_RESULT(ic))); + + /* if size is less than 2 then NO */ + if (sym->nRegs < 2) return 0; + /* if type Pointer then YES */ + if (IS_PTR(sym->type)) return 1; + + /* go thru the usages of this operand if used with + a constant then yes */ + while (!bitVectIsZero(uses_defs)) { + int ikey = bitVectFirstBit(uses_defs); + iCode *uic = hTabItemWithKey(iCodehTab,ikey); + sym_link *otype = NULL; + bitVectUnSetBit(uses_defs,ikey); + if (!uic) continue; + otype = (IC_RIGHT(uic) ? operandType(IC_RIGHT(uic)) : NULL); + if (otype && IS_LITERAL(otype)) return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) { + + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* of all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq = 1; + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) continue; + + /* now we need to allocate registers + only for the result */ + if (IC_RESULT (ic)) { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j=0; + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) + || sym->liveTo <= ic->seq) + continue; + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil + && sym->liveTo > ebbs[i]->lSeq) { + spillThis (sym); + continue; + } + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + willCS = + willCauseSpill (sym->nRegs, + sym->regType); + spillable = computeSpillable (ic); + if (sym->remat || (willCS && bitVectIsZero (spillable))) { + spillThis (sym); + continue; + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) { + if (sym->usl.spillLoc) { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) { + spillThis (sym); + continue; + } + } else { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith(spillable,noSpilLoc,ebbs[i],ic)) { + spillThis (sym); + continue; + } + } + } + + /* we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + if (needsPair(ic)) { + short regtype ; + regs *preg; + if (sym->regType == REG_PTR) regtype = REG_PTR; + else if (sym->regType == REG_SCR) regtype = REG_SCR; + else regtype = REG_GPR; + preg = allocRegPair(regtype); + if (preg) { + sym->regs[j++] = preg; + sym->regs[j++] = ®sAVR[preg->rIdx+1]; + } + } + for (; j < sym->nRegs; j++) { + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else if (sym->regType == REG_SCR) + sym->regs[j] = getRegScr (ic, ebbs[i], sym); + else + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + /* if the allocation falied which means + this was spilt then break */ + if (!sym->regs[j]) break; + } + + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->nRegs + && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_LEFT (ic)), ic->lineno); + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) + && OP_SYMBOL (IC_RIGHT (ic))->nRegs) + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_RIGHT (ic)), ic->lineno); + + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +static bitVect * +rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (avr_nRegs); + + for (j = 0; j < sym->nRegs; j++) { + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (avr_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) { + rmask = bitVectUnion (rmask, rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) { + rmask = bitVectUnion (rmask, rUmaskForOp (IC_JTCOND (ic))); + + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_LEFT (ic))); + + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_RESULT (ic))); + + ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) { + + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (avr_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) { + werror (E_INTERNAL_ERROR, __FILE__, + __LINE__, + "createRegMask cannot find live range"); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) { + if (sym->regs[k]) { + int rIdx = sym->regs[k]->rIdx; + ic->rMask = bitVectSetBit (ic-> rMask,rIdx); + /* special case for X & Z registers */ + if (rIdx == R26_IDX || rIdx == R27_IDX) + ic->rMask = bitVectSetBit (ic->rMask, X_IDX); + if (rIdx == R30_IDX || rIdx == R31_IDX) + ic->rMask = bitVectSetBit (ic->rMask, Z_IDX); + } + } + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum () +{ + symbol *sym; + int k; + iCode *ic; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) { + + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + + /* if the live range is a temporary */ + if (sym->isitmp) { + + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = + aggrToPtr (sym->type, FALSE); + continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer and the + pointer we are getting is rematerializable and + in "data" space */ + + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, bitVectFirstBit (sym-> defs))) + && POINTER_GET (ic) && !IS_BITVAR (sym->etype)) { + + /* if in data space or idata space then try to + allocate pointer register */ + + } + + /* if not then we require registers */ + sym->nRegs = + ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = + aggrToPtr (sym->type, + FALSE)) : getSize (sym-> + type)); + + if (sym->nRegs > 4) { + fprintf (stderr, + "allocated more than 4 or 0 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 2 && /* size is two */ + IS_PTR (sym->type) && /* is a pointer */ + sym->uptr) { /* has pointer usage i.e. get/set pointer */ + sym->regType = REG_PTR; + avr_ptrRegReq++; + } + else { + /* live accross a function call then gpr else scratch */ + if (sym->isLiveFcall) + sym->regType = REG_GPR; + else + sym->regType = REG_SCR; + } + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } + +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || + OP_SYMBOL (IC_RIGHT (ic))->isind || + OP_LIVETO (IC_RIGHT (ic)) > ic->seq) { + return 0; + } + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) { + + /* if there is a function call and this is + a parameter & not my parameter then don't pack it */ + if ((dic->op == CALL || dic->op == PCALL) && + (OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->ismyparm)) { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) { + if (POINTER_SET (dic)) + dic = NULL; + + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) { + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) { + dic = NULL; + break; + } + } + + if (!dic) + return 0; /* did not find */ + + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess) { + + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || + (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || + (IC_RIGHT (dic) && + IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) return 0; + } + pack: + /* if in far space & tru symbol then don't */ + if ((IS_TRUE_SYMOP (IC_RESULT (ic))) + && isOperandInFarSpace (IC_RESULT (ic))) return 0; + /* found the definition */ + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) + && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + return 1; + +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* returns only */ + if (ic->op != RETURN) + return NULL; + + /* this routine will mark the a symbol as used in one + instruction use only && if the defintion is local + (ie. within the basic block) && has only one definition && + that definiion is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one defintion */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get the that definition */ + if (!(dic = + hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (op))))) return NULL; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from + a function call */ + if (dic->op == CALL || dic->op == PCALL) { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + + + /* otherwise check that the definition does + not contain any symbols in far space */ + if (IS_OP_RUONLY (IC_LEFT (ic)) || IS_OP_RUONLY (IC_RIGHT (ic))) { + return NULL; + } + + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + sic = dic; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (dic = dic->next; dic && dic != ic; dic = dic->next) { + + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr + (operandType (IC_RESULT (dic)), + FALSE))) return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr + (operandType (IC_LEFT (dic)), + FALSE))) return NULL; + + /* if address of & the result is remat the okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 3) + return NULL; + + /* if left or right or result is in far space */ + if (IS_OP_RUONLY (IC_LEFT (dic)) || + IS_OP_RUONLY (IC_RIGHT (dic)) || + IS_OP_RUONLY (IC_RESULT (dic))) { + return NULL; + } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; + +} + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + while (1) { + + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) { + + + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) { + + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) { + + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1) { + + OP_SYMBOL (IC_RESULT (ic))->remat = + OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = + OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well only + if the literal value is within the range -255 and + 255 + the assembler cannot handle it other wise */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + IS_OP_LITERAL (IC_RIGHT (ic)))) { + + int i = (int) operandLitValue (IC_RIGHT (ic)); + if (i < 255 && i > -255) { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = + NULL; + } + } + + /* mark the pointer usages */ + if (POINTER_SET (ic)) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic)) { + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + if (OP_SYMBOL (IC_LEFT(ic))->remat) + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if the condition of an if instruction + is defined in the previous instruction then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + ((ic->op == BITWISEAND || + ic->op == '|' || + ic->op == '^') && + isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) { + + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* some cases the redundant moves can + can be eliminated for return statements */ + if ((ic->op == RETURN || ic->op == SEND)) + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + + /* if this is cast for intergral promotion then + check if only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) { + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType) && + SPEC_USIGN (fromType) == SPEC_USIGN (toType)) { + + iCode *dic = + packRegsForOneuse (ic, IC_RIGHT (ic), + ebp); + if (dic) { + if (IS_ARITHMETIC_OP (dic)) { + IC_RESULT (dic) = + IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, + ic->key, ic, + DELETE_ITEM, + NULL); + ic = ic->prev; + } + else + OP_SYMBOL (IC_RIGHT (ic))-> + ruonly = 0; + } + } + else { + + /* if the type from and type to are the same + then if this is the only use then packit */ + if (compareType (operandType (IC_RIGHT (ic)), + operandType (IC_LEFT (ic))) == + 1) { + iCode *dic = + packRegsForOneuse (ic, + IC_RIGHT + (ic), ebp); + if (dic) { + IC_RESULT (dic) = + IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, + ic->key, ic, + DELETE_ITEM, + NULL); + ic = ic->prev; + } + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* preAssignParms - we have a leaf function preassign registers */ +/*-----------------------------------------------------------------*/ +static void +preAssignParms (iCode * ic) +{ + int i = R16_IDX; + /* look for receives and assign registers + to the result of the receives */ + while (ic) { + /* if it is a receive */ + if (ic->op == RECEIVE) { + symbol *r = OP_SYMBOL (IC_RESULT (ic)); + int size = getSize (r->type); + if (r->regType == REG_GPR || r->regType == REG_SCR) { + int j = 0; + while (size--) { + r->regs[j++] = ®sAVR[i++]; + regsAVR[i - 1].isFree = 0; + } + /* put in the regassigned vector */ + _G.regAssigned = + bitVectSetBit (_G.regAssigned, + r->key); + } + else { + /* not a GPR then we should mark as free */ + while (size--) { + regsAVR[i++].isFree = 1; + } + } + } + ic = ic->next; + } + /* mark anything remaining as free */ + while (i <= R23_IDX) + regsAVR[i++].isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* setdefaultRegs - do setup stuff for register allocation */ +/*-----------------------------------------------------------------*/ +static void +setDefaultRegs (eBBlock ** ebbs, int count) +{ + int i; + + /* if no pointer registers required in this function + then mark r26-27 & r30-r31 as GPR & free */ + regsAVR[R26_IDX].isFree = + regsAVR[R27_IDX].isFree = + regsAVR[R30_IDX].isFree = regsAVR[R31_IDX].isFree = 1; + + if (!avr_ptrRegReq) { + regsAVR[R26_IDX].type = (regsAVR[R26_IDX].type & ~REG_MASK) | REG_GPR; + regsAVR[R27_IDX].type = (regsAVR[R27_IDX].type & ~REG_MASK) | REG_GPR; + regsAVR[R28_IDX].type = (regsAVR[R28_IDX].type & ~REG_MASK) | REG_GPR; + regsAVR[R29_IDX].type = (regsAVR[R29_IDX].type & ~REG_MASK) | REG_GPR; + } + else { + regsAVR[R26_IDX].type = (regsAVR[R26_IDX].type & ~REG_MASK) | REG_PTR; + regsAVR[R27_IDX].type = (regsAVR[R27_IDX].type & ~REG_MASK) | REG_PTR; + regsAVR[R30_IDX].type = (regsAVR[R30_IDX].type & ~REG_MASK) | REG_PTR; + regsAVR[R31_IDX].type = (regsAVR[R31_IDX].type & ~REG_MASK) | REG_PTR; + } + + /* registers 0-1 / 24-25 used as scratch */ + regsAVR[R0_IDX].isFree = + regsAVR[R1_IDX].isFree = + regsAVR[R24_IDX].isFree = regsAVR[R25_IDX].isFree = 0; + + /* if this has no function calls then we need + to do something special + a) pre-assign registers to parameters RECEIVE + b) mark the remaining parameter regs as free */ + /* mark the parameter regs as SCRACH */ + for (i = R16_IDX; i <= R23_IDX; i++) { + regsAVR[i].type = (regsAVR[i].type & ~REG_MASK) | REG_SCR; + regsAVR[i].isFree = 1; + } + if (!IFFUNC_HASFCALL(currFunc->type)) { + preAssignParms (ebbs[0]->sch); + } + /* Y - is not allocated (it is the stack frame) */ + regsAVR[R28_IDX].isFree = regsAVR[R28_IDX].isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +avr_assignRegisters (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + avr_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + packRegisters (ebbs[i]); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* setup the default registers */ + setDefaultRegs (ebbs, count); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) { + /* werror(W_TOOMANY_SPILS,"stack", */ + /* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) { + /* werror(W_TOOMANY_SPILS,"data space", */ + /* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + redoStackOffsets (); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + + genAVRCode (ic); + /* for (; ic ; ic = ic->next) */ + /* piCode(ic,stdout); */ + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + + return; +} diff --git a/src/avr/ralloc.h b/src/avr/ralloc.h new file mode 100644 index 0000000..b146bca --- /dev/null +++ b/src/avr/ralloc.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum + { + R0_IDX = 0, R1_IDX, R2_IDX, R3_IDX, R4_IDX, + R5_IDX, R6_IDX, R7_IDX, R8_IDX, R9_IDX, + R10_IDX, R11_IDX, R12_IDX, R13_IDX, R14_IDX, + R15_IDX, R16_IDX, R17_IDX, R18_IDX, R19_IDX, + R20_IDX, R21_IDX, R22_IDX, R23_IDX, R24_IDX, + R25_IDX, R26_IDX, R27_IDX, R28_IDX, R29_IDX, + R30_IDX, R31_IDX, X_IDX, Z_IDX, CND_IDX + }; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_SCR 0x04 +#define REG_CND 0x08 +#define REG_MASK 0x0f +#define REG_PAIR 0x10 + +/* definition for the registers */ +typedef struct regs + { + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + short otype; + char *name; /* name */ + char *dname; /* name when direct access needed */ + char *base; /* base address */ + short offset; /* offset from the base */ + unsigned isFree:1; /* is currently unassigned */ + unsigned saveReq:1; /* save required @ function entry ? */ + } +regs; +extern regs regsAVR[]; + +regs *avr_regWithIdx (int); + +#endif diff --git a/src/cdbFile.c b/src/cdbFile.c new file mode 100644 index 0000000..4c2b945 --- /dev/null +++ b/src/cdbFile.c @@ -0,0 +1,523 @@ +#include "common.h" + + +/************************************************************* + * + * + * + * + *************************************************************/ + +int cdbOpenFile (const char *file); +int cdbCloseFile (void); +int cdbWriteFunction (symbol *pSym, iCode *ic); +int cdbWriteEndFunction (symbol *pSym, iCode *ic, int offset); +int cdbWriteLabel (symbol *pSym, const iCode *ic); +int cdbWriteScope (iCode *ic); +int cdbWriteSymbol (symbol *pSym); +int cdbWriteType (structdef *sdef, int block, int inStruct, const char *tag); +int cdbWriteModule (const char *name); +int cdbWriteCLine (iCode *ic); +int cdbWriteALine (const char *module, int Line); +int cdbWriteFrameAddress (const char *variable, struct reg_info *reg, int offset); +int cdbWriteBasicSymbol (symbol *sym, int isStructSym, int isFunc); +void cdbTypeInfo (sym_link * type); + + +DEBUGFILE cdbDebugFile = + { + &cdbOpenFile, + &cdbCloseFile, + &cdbWriteModule, + &cdbWriteFunction, + &cdbWriteEndFunction, + &cdbWriteLabel, + &cdbWriteScope, + &cdbWriteSymbol, + &cdbWriteType, + &cdbWriteCLine, + &cdbWriteALine, + &cdbWriteFrameAddress + }; + +FILE *cdbFilePtr = NULL; +const char *cdbModuleName = NULL; + +/****************************************************************** + * spacesToUnderscores - replace all non alpha-numerics with + * underscores + * + * + *****************************************************************/ + +static char * +spacesToUnderscores (char *dest, const char *src, size_t len) +{ + unsigned int i; + char *p; + + assert (dest != NULL); + assert (src != NULL); + assert (len > 0); + + --len; + for (p = dest, i = 0; *src != '\0' && i < len; ++src, ++i) { + *p++ = (isspace ((unsigned char)*src) || (*src == '-')) ? '_' : *src; + } + *p = '\0'; + + return dest; +} + + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbOpenFile (const char *file) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbOpenFile (%s)\n", file); + + return (cdbFilePtr = fopen(file, "w")) ? 1 : 0; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ +int +cdbCloseFile (void) +{ + if (getenv("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbCloseFile()\n"); + + if(!cdbFilePtr) return 0; + + fclose(cdbFilePtr); + cdbFilePtr = NULL; + cdbModuleName = NULL; + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteFunction (symbol *pSym, iCode *ic) +{ + char debugSym[INITIAL_INLINEASM]; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteFunction()\n"); + + if (!cdbFilePtr) return 0; + + if (IS_STATIC (pSym->etype)) + sprintf (debugSym, "F%s$%s$0$0", moduleName, pSym->name); + else + sprintf (debugSym, "G$%s$0$0", pSym->name); + emitDebuggerSymbol (debugSym); + + return cdbWriteBasicSymbol (pSym, FALSE, TRUE); +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteEndFunction (symbol *pSym, iCode *ic, int offset) +{ + char debugSym[INITIAL_INLINEASM]; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteEndFunction()\n"); + + if (!cdbFilePtr) return 0; + + if (ic) + { + sprintf (debugSym, "C$%s$%d$%ld_%ld$%d", + FileBaseName (ic->filename), pSym->lastLine, + ic->level / LEVEL_UNIT, ic->level % LEVEL_UNIT, ic->block); + spacesToUnderscores (debugSym, debugSym, sizeof (debugSym)); + emitDebuggerSymbol (debugSym); + } + + if (IS_STATIC (pSym->etype)) + sprintf (debugSym, "XF%s$%s$0$0", moduleName, pSym->name); + else + sprintf (debugSym, "XG$%s$0$0", pSym->name); + emitDebuggerSymbol (debugSym); + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteLabel (symbol *pSym, const iCode *ic) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteLabel()\n"); + + if (!cdbFilePtr) return 0; + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteScope (iCode *ic) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteScope()\n"); + + if (!cdbFilePtr) return 0; + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteSymbol(symbol *pSym) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteSymbol()\n"); + + if (!cdbFilePtr) return 0; + + return cdbWriteBasicSymbol(pSym, FALSE, FALSE); +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteType (structdef *sdef, int block, int inStruct, const char *tag) +{ + symbol *sym; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteType()\n"); + + if (!cdbFilePtr) return 0; + + fprintf (cdbFilePtr, "T:"); + + /* if block # then must have function scope */ + fprintf (cdbFilePtr, "F%s$", moduleName); + + fprintf (cdbFilePtr, "%s[", (tag ? tag : sdef->tag)); + + for (sym = sdef->fields; sym; sym = sym->next) + { + fprintf (cdbFilePtr, "({%d}", sym->offset); + cdbWriteBasicSymbol (sym, TRUE, FALSE); + fprintf (cdbFilePtr, ")"); + } + + fprintf (cdbFilePtr, "]"); + + if (!inStruct) + fprintf (cdbFilePtr, "\n"); + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteModule(const char *name) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteModule()\n"); + + if (!cdbFilePtr) return 0; + cdbModuleName = name; + + fprintf(cdbFilePtr, "M:%s\n", cdbModuleName); + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ +int +cdbWriteCLine (iCode *ic) +{ + char debugSym[INITIAL_INLINEASM]; + + if (!cdbFilePtr) return 0; + + sprintf (debugSym, "C$%s$%d$%ld_%ld$%d", + FileBaseName (ic->filename), ic->lineno, + ic->level / LEVEL_UNIT, ic->level % LEVEL_UNIT, ic->block); + spacesToUnderscores (debugSym, debugSym, sizeof (debugSym)); + emitDebuggerSymbol (debugSym); + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteALine (const char *module, int Line) +{ + if (!cdbFilePtr) return 0; + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteFrameAddress (const char *variable, struct reg_info *reg, int offset) +{ + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteFrameAddress()\n"); + + if (!cdbFilePtr) return 0; + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +int +cdbWriteBasicSymbol (symbol *sym, int isStructSym, int isFunc) +{ + memmap *map; + symbol *sym2; + + if (getenv ("SDCC_DEBUG_FUNCTION_POINTERS")) + fprintf (stderr, "cdbFile.c:cdbWriteBasicSymbol()\n"); + + if (!cdbFilePtr) return 0; + + if (!sym) return 0; + + /* WRITE HEADER, Function or Symbol */ + if (isFunc) + fprintf (cdbFilePtr, "F:"); + else + fprintf (cdbFilePtr, "S:"); + + /* STRUCTS do not have scope info.. */ + + if (!isStructSym) + { + if (sym->level && sym->localof) /* symbol is local */ + { + fprintf (cdbFilePtr, "L%s.%s$", moduleName, sym->localof->name); + } + else if (IS_STATIC (sym->etype)) /* scope is file */ + { + fprintf (cdbFilePtr, "F%s$", moduleName); + } + else /* scope is global */ + { + fprintf (cdbFilePtr, "G$"); + } + } + else + { + fprintf (cdbFilePtr, "S$"); /* scope is structure */ + } + + /* print the name, & mangled name */ + fprintf (cdbFilePtr, "%s$%ld_%ld$%d(", sym->name, sym->level / LEVEL_UNIT, sym->level % LEVEL_UNIT, sym->block); + + cdbTypeInfo (sym->type); + + fprintf (cdbFilePtr, "),"); + + /* CHECK FOR REGISTER SYMBOL... */ + if (!sym->allocreq && sym->reqv) + { + int a; + symbol *TempSym = OP_SYMBOL (sym->reqv); + + if (!TempSym->isspilt || TempSym->remat) + { + fprintf (cdbFilePtr, "R,0,0,["); + + for (a = 0; a < 4; a++) + { + if (TempSym->regs[a]) + { + fprintf (cdbFilePtr, "%s%s", port->getRegName(TempSym->regs[a]), + ((a < 3) && (TempSym->regs[a+1])) ? "," : ""); + } + } + + fprintf (cdbFilePtr, "]"); + sym2 = NULL; + } + else + { + sym2 = TempSym->usl.spillLoc; + } + } + else + { + sym2 = sym; + } + + if (sym2) + { + /* print the address space */ + map = SPEC_OCLS (sym2->etype); + + fprintf (cdbFilePtr, "%c,%d,%d", + (map ? map->dbName : 'Z'), sym2->onStack, SPEC_STAK (sym2->etype)); + } + + /* if assigned to registers then output register names */ + /* if this is a function then print + if is it an interrupt routine & interrupt number + and the register bank it is using */ + if (isFunc) + fprintf (cdbFilePtr, ",%d,%d,%d", FUNC_ISISR (sym->type), + FUNC_INTNO (sym->type), FUNC_REGBANK (sym->type)); + +/* alternate location to find this symbol @ : eg registers + or spillocation */ + + if (!isStructSym) + fprintf (cdbFilePtr, "\n"); + + return 1; +} + +/****************************************************************** + * + * + * + * + *****************************************************************/ + +/*-----------------------------------------------------------------*/ +/* cdbTypeInfo - print the type information for debugger */ +/*-----------------------------------------------------------------*/ +void +cdbTypeInfo (sym_link * type) +{ + fprintf (cdbFilePtr, "{%d}", getSize (type)); + + while (type) + { + if (IS_DECL (type)) + { + switch (DCL_TYPE (type)) + { + case FUNCTION: fprintf (cdbFilePtr, "DF,"); break; + case GPOINTER: fprintf (cdbFilePtr, "DG,"); break; + case CPOINTER: fprintf (cdbFilePtr, "DC,"); break; + case FPOINTER: fprintf (cdbFilePtr, "DX,"); break; + case POINTER: fprintf (cdbFilePtr, "DD,"); break; + case IPOINTER: fprintf (cdbFilePtr, "DI,"); break; + case PPOINTER: fprintf (cdbFilePtr, "DP,"); break; + case EEPPOINTER: fprintf (cdbFilePtr, "DA,"); break; + case ARRAY: fprintf (cdbFilePtr, "DA%ud,", (unsigned int) DCL_ELEM (type)); break; + default: break; + } + } + else + { + switch (SPEC_NOUN (type)) + { + case V_INT: + if (IS_LONG (type)) + fprintf (cdbFilePtr, "SL"); + else + fprintf (cdbFilePtr, "SI"); + break; + + case V_CHAR: fprintf (cdbFilePtr, "SC"); break; + case V_VOID: fprintf (cdbFilePtr, "SV"); break; + case V_FLOAT: fprintf (cdbFilePtr, "SF"); break; + case V_FIXED16X16: fprintf(cdbFilePtr, "SQ"); break; + case V_STRUCT: + fprintf (cdbFilePtr, "ST%s", SPEC_STRUCT (type)->tag); + break; + + case V_SBIT: fprintf (cdbFilePtr, "SX"); break; + case V_BIT: + case V_BITFIELD: + fprintf (cdbFilePtr, "SB%d$%d", SPEC_BSTR (type), + SPEC_BLEN (type)); + break; + + default: + break; + } + fputs (":", cdbFilePtr); + if (SPEC_USIGN (type)) + fputs ("U", cdbFilePtr); + else + fputs ("S", cdbFilePtr); + } + type = type->next; + } +} diff --git a/src/clean.mk b/src/clean.mk new file mode 100644 index 0000000..ea9b12e --- /dev/null +++ b/src/clean.mk @@ -0,0 +1,43 @@ +CLEANALLPORTS = avr ds390 ds400 hc08 mcs51 pic14 pic16 stm8 z80 xa51 + +# Deleting all files created by building the program +# -------------------------------------------------- +clean: + rm -f *core *[%~] *.[oa] *.output + rm -f .[a-z]*~ \#* + rm -f version.h + rm -f SDCCy.c SDCCy.h SDCClex.c + rm -f $(top_builddir)/bin/sdcc$(EXEEXT) sdcc$(EXEEXT) + for port in $(CLEANALLPORTS) ; do\ + if [ -f $$port/Makefile ]; then\ + $(MAKE) -C $$port clean ;\ + fi;\ + done + + +# Deleting all files created by configuring or building the program +# ----------------------------------------------------------------- +distclean: clean + rm -f Makefile *.dep + for port in $(CLEANALLPORTS) ; do\ + if [ -f $$port/Makefile ]; then\ + $(MAKE) -C $$port distclean ;\ + fi;\ + done + + +# Like clean but some files may still exist +# ----------------------------------------- +mostlyclean: clean + rm -f SDCCy.c + rm -f SDCCy.h + rm -f SDCClex.c + + +# Deleting everything that can reconstructed by this Makefile. It deletes +# everything deleted by distclean plus files created by bison, etc. +# ----------------------------------------------------------------------- +realclean: distclean + rm -f SDCCy.c + rm -f SDCCy.h + rm -f SDCClex.c diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..d4d9201 --- /dev/null +++ b/src/common.h @@ -0,0 +1,76 @@ +/*------------------------------------------------------------------------- + common.h - include common header files + + Copyright (C) 2000, KEvin Vigor + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#ifndef COMMON_H +#define COMMON_H + +#if defined(__APPLE__) && (__MACH__) +#ifdef _G +#undef _G +#endif +#endif + +/* C++ incompatible header files */ +#ifdef __cplusplus +/* TODO: the extern "C" should be moved to each header file */ +extern "C" { +#endif + +#include "SDCCglobl.h" +#include "SDCCmem.h" +#include "SDCCast.h" +#include "SDCCy.h" +#include "SDCChasht.h" +#include "SDCCbitv.h" +#include "SDCCset.h" +#include "SDCCicode.h" +#include "SDCClabel.h" +#include "SDCCBBlock.h" +#include "SDCCloop.h" +#include "SDCCcse.h" +#include "SDCCcflow.h" +#include "SDCCdflow.h" +#include "SDCClrange.h" +#include "SDCCptropt.h" +#include "SDCCopt.h" +#include "SDCCglue.h" +#include "SDCCpeeph.h" +#include "SDCCdebug.h" +#include "SDCCutil.h" +#include "SDCCasm.h" +#include "SDCCsystem.h" + +#include "port.h" + +#include "newalloc.h" + +#ifdef __cplusplus +} +#endif + +/* C++ compatible header files */ +#include "SDCCgen.h" + +#endif diff --git a/src/conf.mk b/src/conf.mk new file mode 100644 index 0000000..ac65f24 --- /dev/null +++ b/src/conf.mk @@ -0,0 +1,10 @@ +# +# Makefile targets to remake configuration +# + +freshconf: Makefile + +Makefile: $(srcdir)/Makefile.in $(top_srcdir)/configure.in + cd $(top_builddir) && $(SHELL) ./config.status + +# End of conf.mk diff --git a/src/ds390/Makefile b/src/ds390/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/ds390/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/ds390/Makefile.in b/src/ds390/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/ds390/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/ds390/ds390.vcxproj b/src/ds390/ds390.vcxproj new file mode 100644 index 0000000..5d9e3df --- /dev/null +++ b/src/ds390/ds390.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {14A8A991-BAD6-49EB-84FB-6F6CF12B436D} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/ds390.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/ds390.bsc + + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/ds390.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/ds390.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + {2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1} + false + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + + \ No newline at end of file diff --git a/src/ds390/ds390.vcxproj.filters b/src/ds390/ds390.vcxproj.filters new file mode 100644 index 0000000..1dbe72d --- /dev/null +++ b/src/ds390/ds390.vcxproj.filters @@ -0,0 +1,43 @@ + + + + + {ffbdd525-702b-4916-87ec-0dc8643d6ed5} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {72ff9e14-dc43-44b3-b62b-b2c627e1029b} + h;hpp;hxx;hm;inl + + + {e572d796-594f-4954-afda-65ca354e9b7f} + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/ds390/gen.c b/src/ds390/gen.c new file mode 100644 index 0000000..8ecc330 --- /dev/null +++ b/src/ds390/gen.c @@ -0,0 +1,14414 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for DS80C390 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + DS390 adaptation: + Copyright (C) 2000, Kevin Vigor + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +-------------------------------------------------------------------------*/ + +//#define D(x) +#define D(x) do if (options.verboseAsm) {x;} while(0) + +#include +#include +#include +#include + +#include "common.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +extern int allocInfo; + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ + +static char *zero = "#0x00"; +static char *one = "#0x01"; +static char *spname; + +#define TR_DPTR(s) if (options.model != MODEL_FLAT24) { emitcode(";", " Use_DPTR1 %s ", s); } +#define TR_AP(s) if (options.model != MODEL_FLAT24) { emitcode(";", " Use_AP %s ", s); } + +unsigned fReturnSizeDS390 = 9; +static char *fReturn24[] = { "dpl", "dph", "dpx", "b", "a", "r4", "r5", "r6", "r7" }; +static char *fReturn16[] = { "dpl", "dph", "b", "a", "r4", "r5", "r6", "r7" }; + +static char **fReturn = fReturn24; +char **fReturnDS390 = fReturn24; +static char *accUse[] = { "a", "b" }; + +static char *dptrn[2][3]; +static char *javaRet[] = { "r0", "r1", "r2", "r3" }; + +static short rbank = -1; + +#define REG_WITH_INDEX ds390_regWithIdx + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define IS_AOP_PREG(x) (AOP(x) && (AOP_TYPE(x) == AOP_R1 || \ + AOP_TYPE(x) == AOP_R0)) + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || \ + AOP_TYPE(x) == AOP_DPTR || AOP_TYPE(x) == AOP_DPTR2 || \ + AOP(x)->paged)) + +#define AOP_INPREG(x) (x && (x->type == AOP_REG && \ + (x->aopu.aop_reg[0] == REG_WITH_INDEX(R0_IDX) || \ + x->aopu.aop_reg[0] == REG_WITH_INDEX(R1_IDX) ))) +#define AOP_INDPTRn(x) (AOP_TYPE(x) == AOP_DPTRn) +#define AOP_USESDPTR(x) ((AOP_TYPE(x) == AOP_DPTR) || (AOP_TYPE(x) == AOP_STR)) +#define AOP_USESDPTR2(x) ((AOP_TYPE(x) == AOP_DPTR2) || (AOP_TYPE(x) == AOP_DPTRn)) + +// The following macro can be used even if the aop has not yet been aopOp'd. +#define AOP_IS_DPTRn(x) (IS_SYMOP(x) && OP_SYMBOL(x)->dptr) + +/* Workaround for DS80C390 bug: div ab may return bogus results + * if A is accessed in instruction immediately before the div. + * + * Will be fixed in B4 rev of processor, Dallas claims. + */ + +#define LOAD_AB_FOR_DIV(LEFT, RIGHT) \ + if (!AOP_NEEDSACC (RIGHT)) \ + { \ + /* We can load A first, then B, since \ + * B (the RIGHT operand) won't clobber A, \ + * thus avoiding touching A right before the div. \ + */ \ + D (emitcode (";", "DS80C390 div bug: rearranged ops.")); \ + MOVA (aopGet (LEFT, 0, FALSE, FALSE, NULL)); \ + MOVB (aopGet (RIGHT, 0, FALSE, FALSE, "b")); \ + } \ + else \ + { \ + /* Just stuff in a nop after loading A. */ \ + emitcode ("mov", "b,%s", aopGet (RIGHT, 0, FALSE, FALSE, NULL)); \ + MOVA (aopGet (LEFT, 0, FALSE, FALSE, NULL)); \ + emitcode("nop", "; workaround for DS80C390 div bug."); \ + } + +#define EQ(a, b) (strcmp (a, b) == 0) + +#define R0INB _G.bu.bs.r0InB +#define R1INB _G.bu.bs.r1InB +#define OPINB _G.bu.bs.OpInB +#define BINUSE _G.bu.BInUse + +static struct +{ + short r0Pushed; + short r1Pushed; + union + { + struct + { + short r0InB: 2; //2 so we can see it overflow + short r1InB: 2; //2 so we can see it overflow + short OpInB: 2; //2 so we can see it overflow + } bs; + short BInUse; + } bu; + short accInUse; + short nRegsSaved; + short dptrInUse; + short dptr1InUse; + set *sendSet; + symbol *currentFunc; +} +_G; + +static char *rb1regs[] = +{ + "b1_0", "b1_1", "b1_2", "b1_3", "b1_4", "b1_5", "b1_6", "b1_7", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7" +}; + +static void saveRBank (int, iCode *, bool); + +#define RESULTONSTACK(x) \ + (IC_RESULT (x) && IC_RESULT (x)->aop && \ + IC_RESULT (x)->aop->type == AOP_STK ) + +#define MOVA(x) mova (x) /* use function to avoid multiple eval */ +#define MOVB(x) movb (x) + +#define CLRC emitcode ("clr","c") +#define SETC emitcode ("setb","c") + +/* A scratch register which will be used to hold + * result bytes from operands in far space via DPTR2. */ +#define DP2_RESULT_REG "acc1" + +static unsigned char SLMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00 + }; + +static unsigned char SRMask[] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x03, 0x01, 0x00 + }; + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 +#define PROTECT_SP { \ + if (options.protect_sp_update) \ + { \ + symbol *lbl = newiTempLabel (NULL); \ + emitcode ("setb", "F1"); \ + emitcode ("jbc", "EA,!tlabel", labelKey2num (lbl->key)); \ + emitcode ("clr", "F1"); \ + emitLabel (lbl); \ + } \ + } +#define UNPROTECT_SP { \ + if (options.protect_sp_update) \ + { \ + emitcode ("mov", "EA,F1"); \ + } \ + } + +static int _currentDPS; /* Current processor DPS. */ +static int _desiredDPS; /* DPS value compiler thinks we should be using. */ +static int _lazyDPS = 0; /* if non-zero, we are doing lazy evaluation of DPS changes. */ + +/*-----------------------------------------------------------------*/ +/* ds390_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +ds390_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emitcode ("", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + +/*-----------------------------------------------------------------*/ +/* mova - moves specified value into accumulator */ +/*-----------------------------------------------------------------*/ +static void +mova (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "a", 2) || !strncmp (x, "acc", 4)) + return; + + /* another early peephole optimization */ + if (EQ (x, "#0x00")) + { + emitcode ("clr", "a"); + return; + } + + emitcode ("mov", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* movb - moves specified value into register b */ +/*-----------------------------------------------------------------*/ +static void +movb (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "b", 2)) + return; + + emitcode ("mov", "b,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* emitpush - push something on internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpush (const char *arg) +{ + char buf[] = "ar?"; + + if (!arg) + { + emitcode ("inc", "sp"); + return; + } + else if (EQ (arg, "a")) + { + arg = "acc"; + } + else if ((*arg == '@') || (*arg == '#')) + { + MOVA (arg); + arg = "acc"; + } + else if (EQ (arg, "r0") || EQ (arg, "r1") || EQ (arg, "r2") || EQ (arg, "r3") || + EQ (arg, "r4") || EQ (arg, "r5") || EQ (arg, "r6") || EQ (arg, "r7")) + { + buf[2] = arg[1]; + arg = buf; + } + emitcode ("push", arg); +} + +/*-----------------------------------------------------------------*/ +/* emitpop - pop something from internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpop (const char *arg) +{ + if (!arg) + emitcode ("dec", "sp"); + else + emitcode ("pop", arg); +} + +/*-----------------------------------------------------------------*/ +/* pushB - saves register B if necessary */ +/*-----------------------------------------------------------------*/ +static bool +pushB (void) +{ + bool pushedB = FALSE; + + if (BINUSE) + { + emitpush ("b"); +// printf("B was in use !\n"); + pushedB = TRUE; + } + else + { + OPINB++; + } + return pushedB; +} + +/*-----------------------------------------------------------------*/ +/* popB - restores value of register B if necessary */ +/*-----------------------------------------------------------------*/ +static void +popB (bool pushedB) +{ + if (pushedB) + { + emitpop ("b"); + } + else + { + OPINB--; + } +} + +/*-----------------------------------------------------------------*/ +/* pushReg - saves register */ +/*-----------------------------------------------------------------*/ +static bool +pushReg (int index, bool bits_pushed) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_pushed) + emitpush (reg->base); + return TRUE; + } + else + emitpush (reg->dname); + return bits_pushed; +} + +/*-----------------------------------------------------------------*/ +/* popReg - restores register */ +/*-----------------------------------------------------------------*/ +static bool +popReg (int index, bool bits_popped) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_popped) + emitpop (reg->base); + return TRUE; + } + else + emitpop (reg->dname); + return bits_popped; +} + +/*-----------------------------------------------------------------*/ +/* getFreePtr - returns r0 or r1 whichever is free or can be pushed */ +/*-----------------------------------------------------------------*/ +static reg_info * +getFreePtr (iCode * ic, asmop ** aopp, bool result) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + /* the logic: if r0 & r1 used in the instruction + then we are in trouble otherwise */ + + /* first check if r0 & r1 are used by this + instruction, in which case we are in trouble */ + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + if (r0iu && r1iu) + { + goto endOfWorld; + } + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + + /* if no usage of r0 then return it */ + if (!r0iu && !r0ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + (*aopp)->type = AOP_R0; + + return (*aopp)->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if no usage of r1 then return it */ + if (!r1iu && !r1ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + (*aopp)->type = AOP_R1; + + return (*aopp)->aopu.aop_ptr = REG_WITH_INDEX (R1_IDX); + } + + /* now we know they both have usage */ + /* if r0 not used in this instruction */ + if (!r0iu) + { + /* push it if not already pushed */ + if (!_G.r0Pushed) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + _G.r0Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + (*aopp)->type = AOP_R0; + + return (*aopp)->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if r1 not used then */ + + if (!r1iu) + { + /* push it if not already pushed */ + if (!_G.r1Pushed) + { + emitpush (REG_WITH_INDEX (R1_IDX)->dname); + _G.r1Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + (*aopp)->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + +endOfWorld: + /* I said end of world, but not quite end of world yet */ + /* if this is a result then we can push it on the stack */ + if (result) + { + (*aopp)->type = AOP_STK; + return NULL; + } + + /* now this is REALLY the end of the world */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "getFreePtr should never reach here"); + exit (EXIT_FAILURE); + + return NULL; // notreached, but makes compiler happy. +} + + +/*-----------------------------------------------------------------*/ +/* genSetDPTR: generate code to select which DPTR is in use (zero */ +/* selects standard DPTR (DPL/DPH/DPX), non-zero selects DS390 */ +/* alternate DPTR (DPL1/DPH1/DPX1). */ +/*-----------------------------------------------------------------*/ +static void +genSetDPTR (int n) +{ + /* If we are doing lazy evaluation, simply note the desired + * change, but don't emit any code yet. + */ + if (_lazyDPS) + { + _desiredDPS = n; + return; + } + + if (!n) + { + emitcode ("mov", "dps,#0"); + } + else + { + TR_DPTR ("#1"); + emitcode ("mov", "dps,#1"); + } +} + +/*------------------------------------------------------------------*/ +/* _startLazyDPSEvaluation: call to start doing lazy DPS evaluation */ +/* */ +/* Any code that operates on DPTR (NB: not on the individual */ +/* components, like DPH) *must* call _flushLazyDPS() before using */ +/* DPTR within a lazy DPS evaluation block. */ +/* */ +/* Note that aopPut and aopGet already contain the proper calls to */ +/* _flushLazyDPS, so it is safe to use these calls within a lazy */ +/* DPS evaluation block. */ +/* */ +/* Also, _flushLazyDPS must be called before any flow control */ +/* operations that could potentially branch out of the block. */ +/* */ +/* Lazy DPS evaluation is simply an optimization (though an */ +/* important one), so if in doubt, leave it out. */ +/*------------------------------------------------------------------*/ +static void +_startLazyDPSEvaluation (void) +{ + _currentDPS = 0; + _desiredDPS = 0; + _lazyDPS++; +} + +/*------------------------------------------------------------------*/ +/* _flushLazyDPS: emit code to force the actual DPS setting to the */ +/* desired one. Call before using DPTR within a lazy DPS evaluation */ +/* block. */ +/*------------------------------------------------------------------*/ +static void +_flushLazyDPS (void) +{ + if (!_lazyDPS) + { + /* nothing to do. */ + return; + } + + if (_desiredDPS != _currentDPS) + { + if (_desiredDPS) + { + emitcode ("inc", "dps"); + } + else + { + emitcode ("dec", "dps"); + } + _currentDPS = _desiredDPS; + } +} + +/*-----------------------------------------------------------------*/ +/* _endLazyDPSEvaluation: end lazy DPS evaluation block. */ +/* */ +/* Forces us back to the safe state (standard DPTR selected). */ +/*-----------------------------------------------------------------*/ +static void +_endLazyDPSEvaluation (void) +{ + _lazyDPS--; + if (!_lazyDPS) + { + if (_currentDPS) + { + genSetDPTR (0); + _flushLazyDPS (); + } + _currentDPS = 0; + _desiredDPS = 0; + } +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + aop->allocated = 1; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + return PTR_TYPE (SPEC_OCLS (etype)); +} + +/*-----------------------------------------------------------------*/ +/* leftRightUseAcc - returns size of accumulator use by operands */ +/*-----------------------------------------------------------------*/ +static int +leftRightUseAcc (iCode * ic) +{ + operand *op; + int size; + int accuseSize = 0; + int accuse = 0; + + if (!ic) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "null iCode pointer"); + return 0; + } + + if (ic->op == IFX) + { + op = IC_COND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else if (ic->op == JUMPTABLE) + { + op = IC_JTCOND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else + { + op = IC_LEFT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + op = IC_RIGHT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + + if (accuseSize) + return accuseSize; + else + return accuse; +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result, bool useDP2) +{ + asmop *aop; + memmap *space; + bool accuse = leftRightUseAcc (ic) || _G.accInUse; + char *dpl = useDP2 ? "dpl1" : "dpl"; + char *dph = useDP2 ? "dph1" : "dph"; + char *dpx = useDP2 ? "dpx1" : "dpx"; + + wassertl (ic != NULL, "Got a null iCode"); + wassertl (sym != NULL, "Got a null symbol"); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + if ((sym->aop->type == AOP_DPTR && useDP2) || (sym->aop->type == AOP_DPTR2 && !useDP2)) + sym->aop = NULL; + else + { + sym->aop->allocated++; + return sym->aop; + } + } + + /* assign depending on the storage class */ + /* if it is on the stack or indirectly addressable */ + /* space we need to assign either r0 or r1 to it */ + if ((sym->onStack && !options.stack10bit) || sym->iaccess) + { + sym->aop = aop = newAsmop (0); + aop->aopu.aop_ptr = getFreePtr (ic, &aop, result); + aop->size = getSize (sym->type); + + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) + { + if (sym->onStack) + { + signed char offset = ((sym->stack < 0) ? + ((signed char) (sym->stack - _G.nRegsSaved)) : ((signed char) sym->stack)) & 0xff; + + if ((abs (offset) <= 3) || (accuse && (abs (offset) <= 7))) + { + emitcode ("mov", "%s,_bp", aop->aopu.aop_ptr->name); + while (offset < 0) + { + emitcode ("dec", aop->aopu.aop_ptr->name); + offset++; + } + while (offset > 0) + { + emitcode ("inc", aop->aopu.aop_ptr->name); + offset--; + } + } + else + { + if (accuse) + emitcode ("push", "acc"); + emitcode ("mov", "a,_bp"); + emitcode ("add", "a,#!constbyte", offset); + emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + if (accuse) + emitcode ("pop", "acc"); + } + } + else + { + emitcode ("mov", "%s,#%s", aop->aopu.aop_ptr->name, sym->rname); + } + aop->paged = space->paged; + } + else + aop->aopu.aop_stk = sym->stack; + return aop; + } + + if (sym->onStack && options.stack10bit) + { + short stack_val = -((sym->stack < 0) ? ((short) (sym->stack - _G.nRegsSaved)) : ((short) sym->stack)); + if (_G.dptrInUse) + { + emitcode ("push", dpl); + emitcode ("push", dph); + emitcode ("push", dpx); + } + /* It's on the 10 bit stack, which is located in + * far data space. + */ + if (stack_val < 0 && stack_val > -5) + { + /* between -5 & -1 */ + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "%s,#!constbyte", dpx, (options.stack_loc >> 16) & 0xff); + } + emitcode ("mov", "%s,_bpx+1", dph); + emitcode ("mov", "%s,_bpx", dpl); + if (useDP2) + { + emitcode ("mov", "dps,#1"); + } + stack_val = -stack_val; + while (stack_val--) + { + emitcode ("inc", "dptr"); + } + if (useDP2) + { + emitcode ("mov", "dps,#0"); + } + } + else + { + if (accuse) + emitcode ("push", "acc"); + + emitcode ("mov", "a,_bpx"); + emitcode ("clr", "c"); + emitcode ("subb", "a,#!constbyte", stack_val & 0xff); + emitcode ("mov", "%s,a", dpl); + emitcode ("mov", "a,_bpx+1"); + emitcode ("subb", "a,#!constbyte", (stack_val >> 8) & 0xff); + emitcode ("mov", "%s,a", dph); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "%s,#!constbyte", dpx, (options.stack_loc >> 16) & 0xff); + } + + if (accuse) + emitcode ("pop", "acc"); + } + sym->aop = aop = newAsmop ((short) (useDP2 ? AOP_DPTR2 : AOP_DPTR)); + aop->size = getSize (sym->type); + return aop; + } + + /* if in bit space */ + if (IN_BITSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type) && !(sym->isitmp)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_strdup (sym->rname); + aop->size = getSize (sym->type); + return aop; + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + sym->aop = aop = newAsmop ((short) (useDP2 ? AOP_DPTR2 : AOP_DPTR)); + if (useDP2) + { + genSetDPTR (1); + _flushLazyDPS (); + emitcode ("mov", "dptr,#%s", sym->rname); + genSetDPTR (0); + } + else + { + emitcode ("mov", "dptr,#%s", sym->rname); + } + aop->size = getSize (sym->type); + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int ptr_type = 0; + int val = 0; + struct dbuf_s dbuf; + + for (;;) + { + if (ic->op == '+') + val += (int) operandLitValue (IC_RIGHT (ic)); + else if (ic->op == '-') + val -= (int) operandLitValue (IC_RIGHT (ic)); + else if (IS_CAST_ICODE (ic)) + { + sym_link *from_type = operandType (IC_RIGHT (ic)); + aop->aopu.aop_immd.from_cast_remat = 1; + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + ptr_type = pointerTypeToGPByte (DCL_TYPE (from_type), + IS_SYMOP (IC_RIGHT (ic)) ? OP_SYMBOL (IC_RIGHT (ic))->name : NULL, sym->name); + continue; + } + else + break; + + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + + dbuf_init (&dbuf, 128); + if (val) + { + dbuf_printf (&dbuf, "(%s %c 0x%06x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffffff); + } + else + { + if (IS_ASSIGN_ICODE (ic) && isOperandLiteral (IC_RIGHT (ic))) + { + dbuf_printf (&dbuf, "0x%06x", (int) operandLitValue (IC_RIGHT (ic))); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + } + + aop->aopu.aop_immd.aop_immd1 = dbuf_detach_c_str (&dbuf); + /* set immd2 field if required */ + if (aop->aopu.aop_immd.from_cast_remat) + { + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "#!constbyte", ptr_type); + aop->aopu.aop_immd.aop_immd2 = dbuf_detach_c_str (&dbuf); + } + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopHasRegs - returns true if aop has regs between from-to */ +/*-----------------------------------------------------------------*/ +static int +aopHasRegs (asmop * aop, int from, int to) +{ + int size = 0; + + if (aop->type != AOP_REG) + return 0; /* if not assigned to regs */ + + for (; size < aop->size; size++) + { + int reg; + for (reg = from; reg <= to; reg++) + if (aop->aopu.aop_reg[size] == REG_WITH_INDEX (reg)) + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they're not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + /* if they have the same rname */ + if (sym1->rname[0] && sym2->rname[0] && EQ (sym1->rname, sym2->rname) && !(IS_PARM (op2) && IS_ITEMP (op1))) + return TRUE; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + /* are they spilt to the same location */ + if (IS_ITEMP (op2) && IS_ITEMP (op1) && sym2->isspilt && sym1->isspilt && (sym1->usl.spillLoc == sym2->usl.spillLoc)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameByte - two asmops have the same address at given offsets */ +/*-----------------------------------------------------------------*/ +static bool +sameByte (asmop * aop1, int off1, asmop * aop2, int off2) +{ + if (aop1 == aop2 && off1 == off2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->aopu.aop_reg[off1] != aop2->aopu.aop_reg[off2]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + { + if (aop1->type == AOP_DPTR || aop1->type == AOP_DPTR2) + { + return FALSE; + } + return TRUE; + } + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand * op, iCode * ic, bool result, bool useDP2) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + { + if ((op->aop->type == AOP_DPTR && useDP2) || (op->aop->type == AOP_DPTR2 && !useDP2)) + op->aop = NULL; + else + { + op->aop->allocated++; + return; + } + } + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = OP_SYMBOL (op)->aop; + if ((op->aop->type == AOP_DPTR && useDP2) || (op->aop->type == AOP_DPTR2 && !useDP2)) + op->aop = NULL; + else + { + op->aop->allocated++; + return; + } + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, OP_SYMBOL (op), result, useDP2); + return; + } + + /* this is a temporary : this has + only five choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = operandSize (op); + return; + } + + if (sym->accuse) + { + int i; + sym->aop = op->aop = aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); + for (i = 0; i < 2; i++) + aop->aopu.aop_str[i] = accUse[i]; + return; + } + + if (sym->ruonly) + { + unsigned i; + + if (useDP2) + { + /* a AOP_STR uses DPTR, but DPTR is already in use; + * we're just hosed. + */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "AOP_STR with DPTR in use!"); + } + + sym->aop = op->aop = aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < fReturnSizeDS390; i++) + aop->aopu.aop_str[i] = fReturn[i]; + return; + } + + if (sym->dptr) + { + /* has been allocated to a DPTRn */ + sym->aop = op->aop = aop = newAsmop (AOP_DPTRn); + aop->size = getSize (sym->type); + aop->aopu.dptr = sym->dptr; + return; + } + + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result, useDP2); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + return; + } + + /* if the type is a bit register */ + if (sym->regType == REG_BIT) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->nRegs; //1??? + aop->aopu.aop_reg[0] = sym->regs[0]; + aop->aopu.aop_dir = sym->regs[0]->name; + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*-----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + aop->allocated--; + + if (aop->allocated) + goto dealloc; + + /* depending on the asmop type only three cases need work + AOP_R0, AOP_R1 & AOP_STK */ + switch (aop->type) + { + case AOP_R0: + if (_G.r0Pushed) + { + if (pop) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R0_IDX); + break; + + case AOP_R1: + if (_G.r1Pushed) + { + if (pop) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R1_IDX); + break; + + case AOP_STK: + { + int sz = aop->size; + int stk = aop->aopu.aop_stk + aop->size; + bitVectUnSetBit (ic->rUsed, R0_IDX); + bitVectUnSetBit (ic->rUsed, R1_IDX); + + getFreePtr (ic, &aop, FALSE); + + if (options.stack10bit) + { + /* I'm not sure what to do here yet... */ + /* #STUB */ + fprintf (stderr, "*** Warning: probably generating bad code for " "10 bit stack mode.\n"); + } + + if (stk) + { + emitcode ("mov", "a,_bp"); + emitcode ("add", "a,#!constbyte", ((char) stk) & 0xff); + emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + } + else + { + emitcode ("mov", "%s,_bp", aop->aopu.aop_ptr->name); + } + + while (sz--) + { + emitpop ("acc"); + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + if (!sz) + break; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + op->aop = aop; + freeAsmop (op, NULL, ic, TRUE); + if (_G.r1Pushed) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + if (_G.r0Pushed) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + } + case AOP_DPTR2: + if (_G.dptr1InUse) + { + emitpop ("dpx1"); + emitpop ("dph1"); + emitpop ("dpl1"); + } + break; + case AOP_DPTR: + if (_G.dptrInUse) + { + emitpop ("dpx"); + emitpop ("dph"); + emitpop ("dpl"); + } + break; + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +#define DEFAULT_ACC_WARNING 0 +static int saveAccWarn = DEFAULT_ACC_WARNING; + +/*-----------------------------------------------------------------*/ +/* opIsGptr: returns non-zero if the passed operand is */ +/* a generic pointer type. */ +/*-----------------------------------------------------------------*/ +static int +opIsGptr (operand * op) +{ + if (op && (AOP_SIZE (op) == GPTRSIZE) && IS_GENPTR (operandType (op))) + { + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* swapOperands - swap two operands */ +/*-----------------------------------------------------------------*/ +static void +swapOperands (operand ** left, operand ** right) +{ + operand *t = *right; + *right = *left; + *left = t; +} + +/*-----------------------------------------------------------------*/ +/* aopGetUsesAcc - indicates ahead of time whether aopGet() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopGetUsesAcc (operand * oper, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + case AOP_R0: + case AOP_R1: + if (aop->paged) + return TRUE; + return FALSE; + case AOP_DPTR: + case AOP_DPTR2: + case AOP_DPTRn: + return TRUE; + case AOP_IMMD: + return FALSE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_CRY: + return TRUE; + case AOP_ACC: + if (offset) + return FALSE; + return TRUE; + case AOP_LIT: + return FALSE; + case AOP_STR: + if (EQ (aop->aopu.aop_str[offset], "a")) + return TRUE; + return FALSE; + case AOP_DUMMY: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-------------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/* */ +/* Set saveAcc to NULL if you are sure it is OK to clobber the value */ +/* in the accumulator. Set it to the name of a free register */ +/* if acc must be preserved; the register will be used to preserve */ +/* acc temporarily and to return the result byte. */ +/*-------------------------------------------------------------------*/ +/* + * NOTE: function returns a pointer to a reusable dynamically allocated + * buffer, which should never be freed! + * Subsequent call to aopGet() will rewrite the result of the previous + * call, so the content of the result should be copied to an other + * location, usually using Safe_strdup(), in order to perserve it. + */ +static const char * +aopGet (operand * oper, int offset, bool bit16, bool dname, char *saveAcc) +{ + asmop *aop = AOP (oper); + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + /* offset is greater than + size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + { + dbuf_append_str (&dbuf, zero); + } + else + { + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + dbuf_append_str (&dbuf, zero); + break; + + case AOP_R0: + case AOP_R1: + /* if we need to increment it */ + while (offset > aop->coff) + { + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + aop->coff--; + } + + aop->coff = offset; + if (aop->paged) + { + emitcode ("movx", "a,@%s", aop->aopu.aop_ptr->name); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + } + else + { + dbuf_printf (&dbuf, "@%s", aop->aopu.aop_ptr->name); + } + break; + + case AOP_DPTRn: + assert (offset <= 3); + dbuf_append_str (&dbuf, dptrn[aop->aopu.dptr][offset]); + break; + + case AOP_DPTR: + case AOP_DPTR2: + + if (aop->type == AOP_DPTR2) + { + genSetDPTR (1); + } + + if (saveAcc) + { + TR_AP ("#1"); +// if (aop->type != AOP_DPTR2) +// { +// if (saveAccWarn) { fprintf(stderr, "saveAcc for DPTR...\n"); } +// emitcode(";", "spanky: saveAcc for DPTR"); +// } + + emitcode ("xch", "a, %s", saveAcc); + } + + _flushLazyDPS (); + + while (offset > aop->coff) + { + emitcode ("inc", "dptr"); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("lcall", "__decdptr"); + aop->coff--; + } + + aop->coff = offset; + if (aop->code) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + { + emitcode ("movx", "a,@dptr"); + } + + if (aop->type == AOP_DPTR2) + { + genSetDPTR (0); + } + + if (saveAcc) + { + TR_AP ("#2"); + emitcode ("xch", "a, %s", saveAcc); +// if (strcmp(saveAcc, "acc1")) +// { +// emitcode(";", "spiffy: non acc1 return from aopGet."); +// } + + dbuf_append_str (&dbuf, saveAcc); + } + else + { + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + } + break; + + case AOP_IMMD: + if (aop->aopu.aop_immd.from_cast_remat && (offset == (aop->size - 1))) + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_immd.aop_immd2); + } + else if (bit16) + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + else if (offset) + { + switch (offset) + { + case 1: + dbuf_tprintf (&dbuf, "#!his", aop->aopu.aop_immd.aop_immd1); + break; + case 2: + dbuf_tprintf (&dbuf, "#!hihis", aop->aopu.aop_immd.aop_immd1); + break; + case 3: + dbuf_tprintf (&dbuf, "#!hihihis", aop->aopu.aop_immd.aop_immd1); + break; + default: /* should not need this (just in case) */ + dbuf_printf (&dbuf, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, offset * 8); + } + } + else + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (oper))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_dir); + } + break; + + case AOP_REG: + dbuf_append_str (&dbuf, dname ? aop->aopu.aop_reg[offset]->dname : aop->aopu.aop_reg[offset]->name); + break; + + case AOP_CRY: + emitcode ("mov", "c,%s", aop->aopu.aop_dir); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_ACC: + dbuf_append_str (&dbuf, (!offset && dname) ? "acc" : aop->aopu.aop_str[offset]); + break; + + case AOP_LIT: + { + int size = 1 + (bit16 ? (options.model == MODEL_FLAT24 ? 2 : 1) : 0); + dbuf_append_str (&dbuf, aopLiteralLong (aop->aopu.aop_lit, offset, size)); + } + break; + + case AOP_STR: + aop->coff = offset; + if (EQ (aop->aopu.aop_str[offset], "a") && dname) + dbuf_append_str (&dbuf, "acc"); + else + dbuf_append_str (&dbuf, aop->aopu.aop_str[offset]); + break; + + default: + dbuf_destroy (&dbuf); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (EXIT_FAILURE); + } + } + return dbuf_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* aopPutUsesAcc - indicates ahead of time whether aopPut() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopPutUsesAcc (operand * oper, const char *s, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + case AOP_DUMMY: + return TRUE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_DPTRn: + return FALSE; + case AOP_DPTR: + case AOP_DPTR2: + return TRUE; + case AOP_R0: + case AOP_R1: + return ((aop->paged) || (*s == '@')); + case AOP_STK: + return (*s == '@'); + case AOP_CRY: + return (!aop->aopu.aop_dir || !EQ (s, aop->aopu.aop_dir)); + case AOP_STR: + return FALSE; + case AOP_IMMD: + return FALSE; + case AOP_ACC: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +aopPut (operand * result, const char *s, int offset) +{ + bool bvolatile = isOperandVolatile (result, FALSE); + bool accuse = FALSE; + asmop *aop = AOP (result); + const char *d = NULL; + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (EXIT_FAILURE); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DUMMY: + MOVA (s); /* read s in case it was volatile */ + accuse = TRUE; + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (result))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_append_str (&dbuf, aop->aopu.aop_dir); + } + + if (!EQ (dbuf_c_str (&dbuf), s) || bvolatile) + { + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), s); + } + if (EQ (dbuf_c_str (&dbuf), "acc")) + { + accuse = TRUE; + } + break; + + case AOP_REG: + if (!EQ (aop->aopu.aop_reg[offset]->name, s) && !EQ (aop->aopu.aop_reg[offset]->dname, s)) + { + if (*s == '@' || + EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->dname, s); + } + else + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->name, s); + } + } + break; + + case AOP_DPTRn: + emitcode ("mov", "%s,%s", dptrn[aop->aopu.dptr][offset], s); + break; + + case AOP_DPTR: + case AOP_DPTR2: + + if (aop->type == AOP_DPTR2) + { + genSetDPTR (1); + } + _flushLazyDPS (); + + if (aop->code) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut writing to code space"); + exit (EXIT_FAILURE); + } + + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "dptr"); + } + + while (offset < aop->coff) + { + aop->coff--; + emitcode ("lcall", "__decdptr"); + } + + aop->coff = offset; + + /* if not in accumulator */ + MOVA (s); + + emitcode ("movx", "@dptr,a"); + + if (aop->type == AOP_DPTR2) + { + genSetDPTR (0); + } + break; + + case AOP_R0: + case AOP_R1: + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + } + while (offset < aop->coff) + { + aop->coff--; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + aop->coff = offset; + + if (aop->paged) + { + MOVA (s); + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (*s == '@') + { + MOVA (s); + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + dbuf_printf (&dbuf, "a%s", s); + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, dbuf_c_str (&dbuf)); + } + else + { + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, s); + } + break; + + case AOP_STK: + emitpush (s); + break; + + case AOP_CRY: + // destination is carry for return-use-only + d = (IS_OP_RUONLY (result)) ? "c" : aop->aopu.aop_dir; + + // source is no literal and not in carry + if (!EQ (s, zero) && !EQ (s, one) && !EQ (s, "c")) + { + MOVA (s); + /* set C, if a >= 1 */ + emitcode ("add", "a,#!constbyte", 0xff); + s = "c"; + } + // now source is zero, one or carry + + /* if result no bit variable */ + if (!d) + { + if (EQ (s, "c")) + { + /* inefficient: move carry into A and use jz/jnz */ + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + accuse = TRUE; + } + else + { + MOVA (s); + accuse = TRUE; + } + } + else if (EQ (s, zero)) + emitcode ("clr", "%s", d); + else if (EQ (s, one)) + emitcode ("setb", "%s", d); + else if (!EQ (s, d)) + emitcode ("mov", "%s,c", d); + break; + + case AOP_STR: + aop->coff = offset; + if (!EQ (aop->aopu.aop_str[offset], s) || bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + case AOP_ACC: + accuse = TRUE; + aop->coff = offset; + if (!offset && EQ (s, "acc") && !bvolatile) + break; + + if (!EQ (aop->aopu.aop_str[offset], s) && !bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit (EXIT_FAILURE); + } + + return accuse; +} + +/*--------------------------------------------------------------------*/ +/* loadDptrFromOperand - load dptr (and optionally B) from operand op */ +/*--------------------------------------------------------------------*/ +static int +loadDptrFromOperand (operand * op, bool loadBToo) +{ + int dopi = 1; + + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (op) != AOP_STR && !AOP_INDPTRn (op)) + { + /* if this is rematerializable */ + if (AOP_TYPE (op) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE, NULL)); + if (loadBToo) + { + if (AOP (op)->aopu.aop_immd.from_cast_remat) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE, NULL)); + else + { + wassertl (FALSE, "need pointerCode"); + emitcode (";", "mov b,???"); + /* genPointerGet and genPointerSet originally did different + ** things for this case. Both seem wrong. + ** from genPointerGet: + ** emitcode ("mov", "b,#%d", pointerCode (retype)); + ** from genPointerSet: + ** emitcode ("mov", "b,%s + 1", aopGet (result, 0, TRUE, ANY)); + */ + } + } + } + else if (AOP_TYPE (op) == AOP_LIT) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE, NULL)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE, NULL)); + } + else if (AOP_TYPE (op) == AOP_DPTR) + { + /* we need to get it byte by byte */ + _startLazyDPSEvaluation (); + /* We need to generate a load to DPTR indirect through DPTR. */ + D (emitcode (";", "genFarPointerGet -- indirection special case.")); + emitpush (aopGet (op, 0, FALSE, TRUE, NULL)); + if (loadBToo || options.model == MODEL_FLAT24) + { + emitpush (aopGet (op, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + { + if (loadBToo) + { + emitpush (aopGet (op, 2, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE, NULL)); + emitpop ("dpx"); + } + else + { + emitcode ("mov", "dpx,%s", aopGet (op, 2, FALSE, FALSE, NULL)); + } + } + else + { + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE, NULL)); + } + emitpop ("dph"); + } + emitpop ("dpl"); + _endLazyDPSEvaluation (); + dopi = 0; + } + else + { + /* we need to get it byte by byte */ + _startLazyDPSEvaluation (); + emitcode ("mov", "dpl,%s", aopGet (op, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (op, 2, FALSE, FALSE, NULL)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE, NULL)); + _endLazyDPSEvaluation (); + } + } + return dopi; +} + +/*--------------------------------------------------------------------*/ +/* reAdjustPreg - points a register back to where it should (coff==0) */ +/*--------------------------------------------------------------------*/ +static void +reAdjustPreg (asmop * aop) +{ + if ((aop->coff == 0) || (aop->size <= 1)) + return; + + switch (aop->type) + { + case AOP_R0: + case AOP_R1: + while (aop->coff--) + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + break; + case AOP_DPTR: + case AOP_DPTR2: + if (aop->type == AOP_DPTR2) + { + genSetDPTR (1); + _flushLazyDPS (); + } + while (aop->coff--) + { + emitcode ("lcall", "__decdptr"); + } + + if (aop->type == AOP_DPTR2) + { + genSetDPTR (0); + } + break; + } + aop->coff = 0; +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size = AOP_SIZE (op); + + if (size == GPTRSIZE) + { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) + { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + } + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +static void +outAcc (operand * result) +{ + int size, offset; + size = getDataSize (result); + if (size) + { + aopPut (result, "a", 0); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + { + aopPut (result, zero, offset++); + } + } +} + +/*-----------------------------------------------------------------*/ +/* outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +outBitC (operand * result) +{ + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + aopPut (result, "c", 0); + } + else if (AOP_TYPE (result) != AOP_DUMMY) + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper) - 1; + int offset = 1; + bool AccUsed; + sym_link *type = operandType (oper); + bool pushedB; + + /* always need B for float */ + AccUsed = IS_FLOAT (type); + + while (!AccUsed && size--) + { + AccUsed |= aopGetUsesAcc (oper, offset++); + } + + /* The generic part of a generic pointer should + * not participate in it's truth value. + * + * i.e. 0x10000000 is zero. + */ + if (opIsGptr (oper)) + { + D (emitcode (";", "toBoolean: generic ptr special case.")); + size = AOP_SIZE (oper) - 2; + } + else + { + size = AOP_SIZE (oper) - 1; + } + + offset = 0; + _startLazyDPSEvaluation (); + if (size && AccUsed && (AOP (oper)->type != AOP_ACC)) + { + pushedB = pushB (); + MOVB (aopGet (oper, offset++, FALSE, FALSE, NULL)); + while (--size) + { + MOVA (aopGet (oper, offset++, FALSE, FALSE, NULL)); + emitcode ("orl", "b,a"); + } + MOVA (aopGet (oper, offset++, FALSE, FALSE, NULL)); + if (IS_FLOAT (type)) + emitcode ("anl", "a,#0x7F"); //clear sign bit + emitcode ("orl", "a,b"); + popB (pushedB); + } + else + { + MOVA (aopGet (oper, offset++, FALSE, FALSE, NULL)); + while (size--) + { + emitcode ("orl", "a,%s", aopGet (oper, offset++, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); +} + +/*-----------------------------------------------------------------*/ +/* toCarry - make boolean and move into carry */ +/*-----------------------------------------------------------------*/ +static void +toCarry (operand * oper) +{ + /* if the operand is a literal then + we know what the value is */ + if (AOP_TYPE (oper) == AOP_LIT) + { + if ((int) operandLitValue (oper)) + SETC; + else + CLRC; + } + else if (AOP_TYPE (oper) == AOP_CRY) + { + if (!IS_OP_ACCUSE (oper)) + emitcode ("mov", "c,%s", oper->aop->aopu.aop_dir); + } + else + { + /* or the operand into a */ + toBoolean (oper); + /* set C, if a >= 1 */ + emitcode ("add", "a,#0xff"); + } +} +#if 0 // as of yet still unused +/*-----------------------------------------------------------------*/ +/* assignBit - assign operand to bit operand */ +/*-----------------------------------------------------------------*/ +static void +assignBit(operand * result, operand * right) +{ + /* if the right side is a literal then + we know what the value is */ + if (AOP_TYPE(right) == AOP_LIT) + { + if ((int)operandLitValue(right)) + aopPut(result, one, 0); + else + aopPut(result, zero, 0); + } + else + { + toCarry(right); + outBitC(result); + } +} +#endif +/*-------------------------------------------------------------------*/ +/* xch_a_aopGet - for exchanging acc with value of the aop */ +/*-------------------------------------------------------------------*/ +static const char * +xch_a_aopGet (operand * oper, int offset, bool bit16, bool dname, char *saveAcc) +{ + const char *l; + + if (aopGetUsesAcc (oper, offset)) + { + emitcode ("mov", "b,a"); + MOVA (aopGet (oper, offset, bit16, dname, saveAcc)); + emitcode ("xch", "a,b"); + aopPut (oper, "a", offset); + emitcode ("xch", "a,b"); + l = "b"; + } + else + { + l = aopGet (oper, offset, bit16, dname, saveAcc); + emitcode ("xch", "a,%s", l); + } + return l; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + symbol *tlbl; + + D (emitcode (";", "genNot")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR (IC_LEFT (ic))); + + /* if in bit space then a special case */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + /* if left==result then cpl bit */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + emitcode ("cpl", "%s", IC_LEFT (ic)->aop->aopu.aop_dir); + } + else + { + toCarry (IC_LEFT (ic)); + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + goto release; + } + + toBoolean (IC_LEFT (ic)); + + /* set C, if a == 0 */ + tlbl = newiTempLabel (NULL); + emitcode ("cjne", "a,#0x01,!tlabel", labelKey2num (tlbl->key)); + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + symbol *tlbl; + sym_link *letype = getSpec (operandType (IC_LEFT (ic))); + + D (emitcode (";", "genCpl")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR (IC_LEFT (ic))); + + /* special case if in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + const char *l; + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY || (SPEC_USIGN (letype) && IS_CHAR (letype))) + { + /* promotion rules are responsible for this strange result: + bit -> int -> ~int -> bit + uchar -> int -> ~int -> bit + */ + emitcode ("setb", "%s", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_LEFT (ic), offset++, FALSE, FALSE, NULL); + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && offset == 0) || + AOP_TYPE (IC_LEFT (ic)) == AOP_REG || IS_AOP_PREG (IC_LEFT (ic))) + { + emitcode ("cjne", "%s,#0xFF,!tlabel", l, labelKey2num (tlbl->key)); + } + else + { + MOVA (l); + emitcode ("cjne", "a,#0xFF,!tlabel", labelKey2num (tlbl->key)); + } + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + goto release; + } + + size = AOP_SIZE (IC_RESULT (ic)); + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + emitcode ("cpl", "a"); + aopPut (IC_RESULT (ic), "a", offset++); + } + _endLazyDPSEvaluation (); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + + D (emitcode (";", "genUminusFloat")); + + /* for this we just copy and then flip the bit */ + + _startLazyDPSEvaluation (); + size = AOP_SIZE (op) - 1; + + while (size--) + { + aopPut (result, aopGet (op, offset, FALSE, FALSE, NULL), offset); + offset++; + } + + MOVA (aopGet (op, offset, FALSE, FALSE, NULL)); + + emitcode ("cpl", "acc[7]"); + aopPut (result, "a", offset); + _endLazyDPSEvaluation (); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype; + + D (emitcode (";", "genUminus")); + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR)); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + emitcode ("mov", "c,%s", IC_LEFT (ic)->aop->aopu.aop_dir); + emitcode ("cpl", "c"); + emitcode ("mov", "%s,c", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) + { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero */ + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL); + if (EQ (l, "a")) + { + if (offset == 0) + SETC; + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0x00"); + } + else + { + if (offset == 0) + CLRC; + emitcode ("clr", "a"); + emitcode ("subb", "a,%s", l); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + _endLazyDPSEvaluation (); + + /* if any remaining bytes in the result */ + /* we just need to propagate the sign */ + if ((size = (AOP_SIZE (IC_RESULT (ic)) - AOP_SIZE (IC_LEFT (ic))))) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* savermask - saves registers in the mask */ +/*-----------------------------------------------------------------*/ +static void +savermask (bitVect * rs_mask) +{ + int i; + + if (options.useXstack) + { + if (bitVectBitValue (rs_mask, R0_IDX)) + emitcode ("mov", "b,r0"); + emitcode ("mov", "r0,%s", spname); + for (i = 0; i < ds390_nRegs; i++) + { + if (bitVectBitValue (rs_mask, i)) + { + if (i == R0_IDX) + emitcode ("mov", "a,b"); + else + emitcode ("mov", "a,%s", REG_WITH_INDEX (i)->name); + emitcode ("movx", "@r0,a"); + emitcode ("inc", "r0"); + } + } + emitcode ("mov", "%s,r0", spname); + if (bitVectBitValue (rs_mask, R0_IDX)) + emitcode ("mov", "r0,b"); + } + else + { + bool bits_pushed = FALSE; + for (i = 0; i < ds390_nRegs; i++) + { + if (bitVectBitValue (rs_mask, i)) + { + bits_pushed = pushReg (i, bits_pushed); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode * lic) +{ + iCode *ic; + bitVect *rsave; + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already or don't need to be then + do nothing */ + if (ic->regsSaved) + return; + if (IS_SYMOP (IC_LEFT (ic))) + { + sym_link *type = OP_SYM_TYPE (IC_LEFT (ic)); + if (IFFUNC_ISNAKED (type) && !TARGET_IS_DS400) + return; + } + + /* special case if DPTR alive across a function call then must save it + even though callee saves */ + if (IS_SYMOP (IC_LEFT (ic)) && IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type)) + { + int i; + rsave = newBitVect (ic->rMask->size); + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectCplAnd (rsave, ds390_rUmaskForOp (IC_RESULT (ic))); + } + else + { + /* save the registers in use at this time but skip the + ones for the result */ + rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic))); + } + ic->regsSaved = 1; + savermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* unsavermask - restore registers with mask */ +/*-----------------------------------------------------------------*/ +static void +unsavermask (bitVect * rs_mask) +{ + int i; + + if (options.useXstack) + { + emitcode ("mov", "r0,%s", spname); + for (i = ds390_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rs_mask, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + if (i == R0_IDX) + { + emitcode ("push", "acc"); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + } + } + emitcode ("mov", "%s,r0", spname); + if (bitVectBitValue (rs_mask, R0_IDX)) + { + emitcode ("pop", "ar0"); + } + } + else + { + bool bits_popped = FALSE; + for (i = ds390_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rs_mask, i)) + { + bits_popped = popReg (i, bits_popped); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode * ic) +{ + bitVect *rsave; + + if (IS_SYMOP (IC_LEFT (ic)) && IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type)) + { + int i; + rsave = newBitVect (ic->rMask->size); + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectCplAnd (rsave, ds390_rUmaskForOp (IC_RESULT (ic))); + } + else + { + /* restore the registers in use at this time but skip the + ones for the result */ + rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic))); + } + unsavermask (rsave); +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand * oper, int size, iCode * ic) +{ + int offset = 0; + int nPushed = _G.r0Pushed + _G.r1Pushed; + + aopOp (oper, ic, FALSE, FALSE); + + if (nPushed != _G.r0Pushed + _G.r1Pushed) + { + while (offset < size) + { + const char *l = aopGet (oper, offset, FALSE, TRUE, NULL); + emitcode ("mov", "%s,%s", fReturn[offset++], l); + } + freeAsmop (oper, NULL, ic, TRUE); + offset = 0; + while (offset < size) + { + emitpush (fReturn[offset++]); + } + return; + } + + _startLazyDPSEvaluation (); + while (size--) + { + const char *l = aopGet (oper, offset++, FALSE, TRUE, NULL); + if (AOP_TYPE (oper) != AOP_REG && AOP_TYPE (oper) != AOP_DIR && strcmp (l, "a")) + { + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + } + _endLazyDPSEvaluation (); + freeAsmop (oper, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - also indicates if acc is in use afterwards */ +/*-----------------------------------------------------------------*/ +static bool +assignResultValue (operand * oper, operand * func) +{ + int offset = 0; + int size = AOP_SIZE (oper); + bool accuse = FALSE; + bool pushedA = FALSE; + + if (func && IS_BIT (OP_SYM_ETYPE (func))) + { + outBitC (oper); + return FALSE; + } + + if (AOP_NEEDSACC (oper)) + { + int i; + for (i=0; iname); + emitcode ("inc", "_spx"); // allocate space first + emitcode ("movx", "@%s,a", r->name); + } + else + { + // allocate space first + emitcode ("mov", "%s,_spx", r->name); + MOVA (r->name); + emitcode ("add", "a,#%d", size); + emitcode ("mov", "_spx,a"); + + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (IC_LEFT (ic), offset++, FALSE, FALSE, NULL)); + emitcode ("movx", "@%s,a", r->name); + emitcode ("inc", "%s", r->name); + } + _endLazyDPSEvaluation (); + } + + freeAsmop (NULL, aop, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + char *prev; + + D (emitcode (";", "genIpush")); + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt || OP_SYMBOL (IC_LEFT (ic))->dptr) + return; + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + _startLazyDPSEvaluation (); + while (size--) + { + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE, NULL)); + } + _endLazyDPSEvaluation (); + return; + } + + /* this is a parameter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + saveRegisters (ic); + + /* if use external stack then call the external + stack pushing routine */ + if (options.useXstack) + { + genXpush (ic); + return; + } + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + + _startLazyDPSEvaluation (); + prev = Safe_strdup (""); + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset++, FALSE, TRUE, NULL); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG && AOP_TYPE (IC_LEFT (ic)) != AOP_DIR && strcmp (l, "acc")) + { + if (!EQ (l, prev) || *l == '@') + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + Safe_free (prev); + prev = Safe_strdup (l); + } + Safe_free (prev); + _endLazyDPSEvaluation (); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genIpop")); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt || OP_SYMBOL (IC_LEFT (ic))->dptr) + return; + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = (size - 1); + _startLazyDPSEvaluation (); + while (size--) + { + emitpop (aopGet (IC_LEFT (ic), offset--, FALSE, TRUE, NULL)); + } + _endLazyDPSEvaluation (); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* popForBranch - recover the spilt registers for a branch */ +/*-----------------------------------------------------------------*/ +static void +popForBranch (iCode * ic, bool markGenerated) +{ + while (ic && ic->op == IPOP) + { + genIpop (ic); + if (markGenerated) + ic->generated = 1; /* mark the icode as generated */ + ic = ic->next; + } +} + +/*-----------------------------------------------------------------*/ +/* saveRBank - saves an entire register bank on the stack */ +/*-----------------------------------------------------------------*/ +static void +saveRBank (int bank, iCode * ic, bool pushPsw) +{ + int i; + int count = 8 + (ds390_nBitRegs / 8) + (pushPsw ? 1 : 0); + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, &aop, FALSE); + } + // allocate space first + emitcode ("mov", "%s,_spx", r->name); + MOVA (r->name); + emitcode ("add", "a,#!constbyte", count); + emitcode ("mov", "_spx,a"); + } + + for (i = 0; i < 8; i++) /* only R0-R7 needs saving */ + { + if (options.useXstack) + { + emitcode ("mov", "a,(%s+%d)", regs390[i].base, 8 * bank + regs390[i].offset); + emitcode ("movx", "@%s,a", r->name); + if (--count) + emitcode ("inc", "%s", r->name); + } + else + emitcode ("push", "(%s+%d)", regs390[i].base, 8 * bank + regs390[i].offset); + } + + if (ds390_nBitRegs > 0) + { + if (options.useXstack) + { + emitcode ("mov", "a,bits"); + emitcode ("movx", "@%s,a", r->name); + if (--count) + emitcode ("inc", "%s", r->name); + } + else + { + emitpush ("bits"); + } + BitBankUsed = 1; + } + + if (pushPsw) + { + if (options.useXstack) + { + emitcode ("mov", "a,psw"); + emitcode ("movx", "@%s,a", r->name); + } + else + { + emitpush ("psw"); + } + + emitcode ("mov", "psw,#!constbyte", (bank << 3) & 0x00ff); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + + if (ic) + { + ic->bankSaved = 1; + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRBank - restores the register bank from stack */ +/*-----------------------------------------------------------------*/ +static void +unsaveRBank (int bank, iCode * ic, bool popPsw) +{ + int i; + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, &aop, FALSE); + } + emitcode ("mov", "%s,_spx", r->name); + } + + if (popPsw) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "psw,a"); + } + else + { + emitpop ("psw"); + } + } + + if (ds390_nBitRegs > 0) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "bits,a"); + } + else + { + emitpop ("bits"); + } + } + + for (i = 7; i >= 0; i--) /* only R7-R0 needs to be popped */ + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "(%s+%d),a", regs390[i].base, 8 * bank + regs390[i].offset); + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs390[i].base, 8 * bank + regs390[i].offset); + emitpop (buf); + } + } + + if (options.useXstack) + { + emitcode ("mov", "_spx,%s", r->name); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genSend - gen code for SEND */ +/*-----------------------------------------------------------------*/ +static void +genSend (set * sendSet) +{ + iCode *sic; + int bit_count = 0; + int sendCount = 0; + static int rb1_count = 0; + + /* first we do all bit parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg > 12) + { + int bit = sic->argreg - 13; + + aopOp (IC_LEFT (sic), sic, FALSE, (IS_OP_RUONLY (IC_LEFT (sic)) ? FALSE : TRUE)); + + /* if left is a literal then + we know what the value is */ + if (AOP_TYPE (IC_LEFT (sic)) == AOP_LIT) + { + if (((int) operandLitValue (IC_LEFT (sic)))) + emitcode ("setb", "b[%d]", bit); + else + emitcode ("clr", "b[%d]", bit); + } + else if (AOP_TYPE (IC_LEFT (sic)) == AOP_CRY) + { + char *l = AOP (IC_LEFT (sic))->aopu.aop_dir; + if (strcmp (l, "c")) + emitcode ("mov", "c,%s", l); + emitcode ("mov", "b[%d],c", bit); + } + else + { + /* we need to or */ + toCarry (IC_LEFT (sic)); + emitcode ("mov", "b[%d],c", bit); + } + bit_count++; + BitBankUsed = 1; + + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } + + if (bit_count) + { + saveRegisters (setFirstItem (sendSet)); + emitcode ("mov", "bits,b"); + } + + /* then we do all other parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg <= 12) + { + int size, offset = 0; + + size = getSize (operandType (IC_LEFT (sic))); + D (emitcode (";", "genSend argreg = %d, size = %d ", sic->argreg, size)); + if (sendCount == 0) + { + bool pushedA = FALSE; + /* first parameter */ + // we know that dpl(hxb) is the result, so + rb1_count = 0; + _startLazyDPSEvaluation (); + if (size > 1) + { + aopOp (IC_LEFT (sic), sic, FALSE, (IS_OP_RUONLY (IC_LEFT (sic)) ? FALSE : TRUE)); + } + else + { + aopOp (IC_LEFT (sic), sic, FALSE, FALSE); + } + while (size--) + { + const char *l = aopGet (IC_LEFT (sic), offset, FALSE, FALSE, NULL); + if (!EQ (l, fReturn[offset])) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (sic)) == AOP_REG || AOP_TYPE (IC_LEFT (sic)) == AOP_R0 || AOP_TYPE (IC_LEFT (sic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + if (size && (strcmp(fReturn[offset],"a")==0) && AOP_NEEDSACC( IC_LEFT (sic))) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + if (pushedA) + { + emitpop ("acc"); + pushedA = FALSE; + } + _endLazyDPSEvaluation (); + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + rb1_count = 0; + } + else + { + /* if more parameter in registers */ + aopOp (IC_LEFT (sic), sic, FALSE, TRUE); + while (size--) + { + emitcode ("mov", "b1_%d,%s", rb1_count++, aopGet (IC_LEFT (sic), offset, FALSE, FALSE, NULL)); + offset++; + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + sendCount++; + } + } +} + +static void +adjustEsp (const char *reg) +{ + emitcode ("anl", "%s,#3", reg); + if (TARGET_IS_DS400) + { + emitcode ("orl", "%s,#!constbyte", reg, (options.stack_loc >> 8) & 0xff); + } +} + +/*-----------------------------------------------------------------*/ +/* selectRegBank - emit code to select the register bank */ +/*-----------------------------------------------------------------*/ +static void +selectRegBank (short bank, bool keepFlags) +{ + /* if f.e. result is in carry */ + if (keepFlags) + { + emitcode ("anl", "psw,#0xE7"); + if (bank) + emitcode ("orl", "psw,#0x%02x", (bank << 3) & 0xff); + } + else + { + emitcode ("mov", "psw,#0x%02x", (bank << 3) & 0xff); + } +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool restoreBank = FALSE; + bool swapBanks = FALSE; + bool accuse = FALSE; + bool accPushed = FALSE; + bool resultInF0 = FALSE; + bool assignResultGenerated = FALSE; + + D (emitcode (";", "genCall")); + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + dtype = operandType (IC_LEFT (ic)); + etype = getSpec (dtype); + if (currFunc && dtype && (!IFFUNC_ISNAKED (dtype) || TARGET_IS_DS400) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && IFFUNC_ISISR (currFunc->type)) + { + if (!ic->bankSaved) + { + /* This is unexpected; the bank should have been saved in + * genFunction. + */ + saveRBank (FUNC_REGBANK (dtype), ic, FALSE); + restoreBank = TRUE; + } + swapBanks = TRUE; + } + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if send set is not empty then assign */ + /* We've saved all the registers we care about; + * therefore, we may clobber any register not used + * in the calling convention (i.e. anything not in + * fReturn. + */ + if (_G.sendSet) + { + if (IFFUNC_ISREENT (dtype)) + { + /* need to reverse the send set */ + genSend (reverseSet (_G.sendSet)); + } + else + { + genSend (_G.sendSet); + } + _G.sendSet = NULL; + } + + if (swapBanks) + { + emitcode ("mov", "psw,#!constbyte", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + if (IS_LITERAL (etype)) + { + if (options.model == MODEL_FLAT24) + emitcode ("lcall", "0x%06X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + else + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + else + { + emitcode ("lcall", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->accuse || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + if (isOperandInFarSpace (IC_RESULT (ic)) && getSize (operandType (IC_RESULT (ic))) <= 2) + { + int size = getSize (operandType (IC_RESULT (ic))); + bool pushedB = FALSE; + + /* Special case for 1 or 2 byte return in far space. */ + MOVA (fReturn[0]); + if (size > 1) + { + pushedB = pushB (); + emitcode ("mov", "b,%s", fReturn[1]); + } + + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + _G.accInUse--; + + popB (pushedB); + + aopPut (IC_RESULT (ic), "a", 0); + + if (size > 1) + { + aopPut (IC_RESULT (ic), "b", 1); + } + assignResultGenerated = TRUE; + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + else + { + bool pushedB = pushB (); + aopOp (IC_RESULT (ic), ic, FALSE, TRUE); + popB (pushedB); + + accuse = assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + assignResultGenerated = TRUE; + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (options.stack10bit) + { + if (ic->parmBytes <= 10) + { + emitcode (";", "stack adjustment for parms"); + for (i = 0; i < ic->parmBytes; i++) + { + emitpop ("acc"); + } + } + else + { + PROTECT_SP; + emitcode ("clr", "c"); + emitcode ("mov", "a,sp"); + emitcode ("subb", "a,#!constbyte", ic->parmBytes & 0xff); + emitcode ("mov", "sp,a"); + emitcode ("mov", "a,esp"); + adjustEsp ("a"); + emitcode ("subb", "a,#!constbyte", (ic->parmBytes >> 8) & 0xff); + emitcode ("mov", "esp,a"); + UNPROTECT_SP; + } + } + else + { + if (ic->parmBytes > 3) + { + if (accuse) + { + emitcode ("push", "acc"); + accPushed = TRUE; + } + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#!constbyte", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + + /* unsaveRegisters from xstack needs acc, but */ + /* unsaveRegisters from stack needs this popped */ + if (accPushed && !options.useXstack) + { + emitpop ("acc"); + accPushed = FALSE; + } + } + else + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + { + if (accuse && !accPushed && options.useXstack) + { + /* xstack needs acc, but doesn't touch normal stack */ + emitpush ("acc"); + accPushed = TRUE; + } + unsaveRegisters (ic); + } + + /* if register bank was saved then pop them */ + if (restoreBank) + unsaveRBank (FUNC_REGBANK (dtype), ic, FALSE); + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + if (accPushed) + emitpop ("acc"); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + symbol *rlbl = newiTempLabel (NULL); + bool restoreBank = FALSE; + bool resultInF0 = FALSE; + + D (emitcode (";", "genPcall")); + + dtype = operandType (IC_LEFT (ic))->next; + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && (!IFFUNC_ISNAKED (dtype) || TARGET_IS_DS400) && + IFFUNC_ISISR (currFunc->type) && (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype))) + { + saveRBank (FUNC_REGBANK (dtype), ic, TRUE); + restoreBank = TRUE; + } + + /* push the return address on to the stack */ + emitcode ("mov", "a,#!tlabel", labelKey2num (rlbl->key)); + emitpush ("acc"); + emitcode ("mov", "a,#!hil", labelKey2num (rlbl->key)); + emitpush ("acc"); + + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "a,#!hihil", labelKey2num (rlbl->key)); + emitpush ("acc"); + } + + /* now push the function address */ + pushSide (IC_LEFT (ic), FARPTRSIZE, ic); + + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + /* make the call */ + emitcode ("ret", ""); + emitLabel (rlbl); + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE, TRUE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (options.stack10bit) + { + if (ic->parmBytes <= 10) + { + emitcode (";", "stack adjustment for parms"); + for (i = 0; i < ic->parmBytes; i++) + { + emitpop ("acc"); + } + } + else + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + PROTECT_SP; + emitcode ("clr", "c"); + emitcode ("mov", "a,sp"); + emitcode ("subb", "a,#!constbyte", ic->parmBytes & 0xff); + emitcode ("mov", "sp,a"); + emitcode ("mov", "a,esp"); + adjustEsp ("a"); + emitcode ("subb", "a,#!constbyte", (ic->parmBytes >> 8) & 0xff); + emitcode ("mov", "esp,a"); + UNPROTECT_SP; + } + } + else + { + if (ic->parmBytes > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#!constbyte", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + } + else + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + } + } + /* if register bank was saved then unsave them */ + if (restoreBank) + unsaveRBank (FUNC_REGBANK (dtype), ic, TRUE); + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved) + unsaveRegisters (ic); + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static int +regsCmp (void *p1, void *p2) +{ + return (STRCASECMP ((char *) p1, (char *) (p2)) == 0); +} + +static bool +inExcludeList (char *s) +{ + const char *p = setFirstItem (options.excludeRegsSet); + + if (p == NULL || STRCASECMP (p, "none") == 0) + return FALSE; + + return isinSetWith (options.excludeRegsSet, s, regsCmp); +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype; + bool switchedPSW = FALSE; + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + + D (emitcode (";", "genFunction")); + + _G.nRegsSaved = 0; + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", sym->name); + emitcode (";", "-----------------------------------------"); + + emitcode ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + ftype = operandType (IC_LEFT (ic)); + _G.currentFunc = sym; + + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no prologue."); + return; + } + + if (options.stack_probe) + emitcode ("lcall", "__stack_probe"); + + /* here we need to generate the equates for the + register bank if required */ + if (FUNC_REGBANK (ftype) != rbank) + { + int i; + + rbank = FUNC_REGBANK (ftype); + for (i = 0; i < ds390_nRegs; i++) + { + if (regs390[i].print) + { + if (strcmp (regs390[i].base, "0") == 0) + emitcode ("", "%s !equ !constbyte", regs390[i].dname, 8 * rbank + regs390[i].offset); + else + emitcode ("", "%s !equ %s + !constbyte", regs390[i].dname, regs390[i].base, 8 * rbank + regs390[i].offset); + } + } + } + + /* if this is an interrupt service routine then + save acc, b, dpl, dph */ + if (IFFUNC_ISISR (sym->type)) + { + if (!inExcludeList ("acc")) + emitpush ("acc"); + if (!inExcludeList ("b")) + emitpush ("b"); + if (!inExcludeList ("dpl")) + emitpush ("dpl"); + if (!inExcludeList ("dph")) + emitpush ("dph"); + if (options.model == MODEL_FLAT24 && !inExcludeList ("dpx")) + { + emitpush ("dpx"); + /* Make sure we're using standard DPTR */ + emitpush ("dps"); + emitcode ("mov", "dps,#0"); + if (options.stack10bit) + { + /* This ISR could conceivably use DPTR2. Better save it. */ + emitpush ("dpl1"); + emitpush ("dph1"); + emitpush ("dpx1"); + emitpush (DP2_RESULT_REG); + } + } + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (sym->type)) + { + int i; + + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (sym->type)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + bool bits_pushed = FALSE; + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + bits_pushed = pushReg (i, bits_pushed); + } + } + } + else + { + /* this function has a function call. We cannot + determine register usage so we will have to push the + entire bank */ + saveRBank (0, ic, FALSE); + if (options.parms_in_bank1) + { + for (i = 0; i < 8; i++) + { + emitpush (rb1regs[i]); + } + } + } + } + else + { + /* This ISR uses a non-zero bank. + * + * We assume that the bank is available for our + * exclusive use. + * + * However, if this ISR calls a function which uses some + * other bank, we must save that bank entirely. + */ + unsigned long banksToSave = 0; + + if (IFFUNC_HASFCALL (sym->type)) + { + +#define MAX_REGISTER_BANKS 4 + + iCode *i; + int ix; + + for (i = ic; i; i = i->next) + { + sym_link *dtype = NULL; + + if (i->op == ENDFUNCTION) + { + /* we got to the end OK. */ + break; + } + + if (i->op == CALL) + { + dtype = operandType (IC_LEFT (i)); + } + if (i->op == PCALL) + { + /* This is a mess; we have no idea what + * register bank the called function might + * use. + * + * The only thing I can think of to do is + * throw a warning and hope. + */ +// werror (W_FUNCPTR_IN_USING_ISR); + dtype = operandType (IC_LEFT (i))->next; + } + if (dtype && FUNC_REGBANK (dtype) != FUNC_REGBANK (sym->type)) + { + /* Mark this bank for saving. */ + if (FUNC_REGBANK (dtype) >= MAX_REGISTER_BANKS) + { + werror (E_NO_SUCH_BANK, FUNC_REGBANK (dtype)); + } + else + { + banksToSave |= (1 << FUNC_REGBANK (dtype)); + } + + /* And note that we don't need to do it in + * genCall. + */ + i->bankSaved = 1; + } + } + + if (banksToSave && options.useXstack) + { + /* Since we aren't passing it an ic, + * saveRBank will assume r0 is available to abuse. + * + * So switch to our (trashable) bank now, so + * the caller's R0 isn't trashed. + */ + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + switchedPSW = TRUE; + } + + for (ix = 0; ix < MAX_REGISTER_BANKS; ix++) + { + if (banksToSave & (1 << ix)) + { + saveRBank (ix, NULL, FALSE); + } + } + } + // TODO: this needs a closer look + SPEC_ISR_SAVED_BANKS (currFunc->etype) = banksToSave; + } + } + else + { + /* if callee-save to be used for this function + then save the registers being used in this function */ + if (IFFUNC_CALLEESAVES (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + bool bits_pushed = FALSE; + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + bits_pushed = pushReg (i, bits_pushed); + _G.nRegsSaved++; + } + } + } + } + } + + /* set the register bank to the desired value */ + if ((FUNC_REGBANK (sym->type) || FUNC_ISISR (sym->type)) && !switchedPSW) + { + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + } + + if (fReentrant && (sym->stack || FUNC_HASSTACKPARM (sym->type))) + { + if (options.stack10bit) + { + emitcode ("push", "_bpx"); + emitcode ("push", "_bpx+1"); + emitcode ("mov", "_bpx,%s", spname); + emitcode ("mov", "_bpx+1,esp"); + adjustEsp ("_bpx+1"); + } + else + { + if (options.useXstack) + { + emitcode ("mov", "r0,%s", spname); + emitcode ("mov", "a,_bp"); + emitcode ("movx", "@r0,a"); + emitcode ("inc", "%s", spname); + } + else + { + /* set up the stack */ + emitcode ("push", "_bp"); /* save the callers stack */ + } + emitcode ("mov", "_bp,%s", spname); + } + } + + /* adjust the stack for the function */ + if (sym->stack) + { + int i = sym->stack; + if (options.stack10bit) + { + if (i > 1024) + werror (W_STACK_OVERFLOW, sym->name); + assert (sym->recvSize <= 4); + if (sym->stack <= 8) + { + while (i--) + emitcode ("push", "acc"); + } + else + { + PROTECT_SP; + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", ((short) sym->stack & 0xff)); + emitcode ("mov", "sp,a"); + emitcode ("mov", "a,esp"); + adjustEsp ("a"); + emitcode ("addc", "a,#!constbyte", (((short) sym->stack) >> 8) & 0xff); + emitcode ("mov", "esp,a"); + UNPROTECT_SP; + } + } + else + { + if (i > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && sym->recvSize < 4) + { + + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", ((char) sym->stack & 0xff)); + emitcode ("mov", "sp,a"); + + } + else + { + while (i--) + emitcode ("inc", "sp"); + } + } + } + + if (sym->xstack) + { + + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", ((char) sym->xstack & 0xff)); + emitcode ("mov", "_spx,a"); + } + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw */ + } +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + lineNode *lnp = genLine.lineCurr; + bitVect *regsUsed; + bitVect *regsUsedPrologue; + bitVect *regsUnneeded; + int idx; + + D (emitcode (";", "genEndFunction")); + + _G.currentFunc = NULL; + if (IFFUNC_ISNAKED (sym->type)) + { + emitcode (";", "naked function: no epilogue."); + if (options.debug && currFunc) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + if (IFFUNC_ISCRITICAL (sym->type)) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("rlc", "a"); /* save c in a */ + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + emitcode ("rrc", "a"); /* restore c from a */ + } + else + { + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + } + } + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto) && (sym->stack || FUNC_HASSTACKPARM (sym->type))) + { + + if (options.stack10bit) + { + PROTECT_SP; + emitcode ("mov", "sp,_bpx", spname); + emitcode ("mov", "esp,_bpx+1", spname); + UNPROTECT_SP; + } + else + { + emitcode ("mov", "%s,_bp", spname); + } + } + + /* if use external stack but some variables were + added to the local stack then decrement the + local stack */ + if (options.useXstack && sym->stack) + { + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", ((char) - sym->stack) & 0xff); + emitcode ("mov", "sp,a"); + } + + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto) && (sym->stack || FUNC_HASSTACKPARM (sym->type))) + { + + if (options.useXstack) + { + emitcode ("mov", "r0,%s", spname); + emitcode ("movx", "a,@r0"); + emitcode ("mov", "_bp,a"); + emitcode ("dec", "%s", spname); + } + else + { + if (options.stack10bit) + { + emitcode ("pop", "_bpx+1"); + emitcode ("pop", "_bpx"); + } + else + { + emitcode ("pop", "_bp"); + } + } + } + + /* restore the register bank */ + if (FUNC_REGBANK (sym->type) || IFFUNC_ISISR (sym->type)) + { + if (!FUNC_REGBANK (sym->type) || !IFFUNC_ISISR (sym->type) || !options.useXstack) + { + /* Special case of ISR using non-zero bank with useXstack + * is handled below. + */ + emitpop ("psw"); + } + } + + if (IFFUNC_ISISR (sym->type)) + { + + /* now we need to restore the registers */ + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (sym->type)) + { + int i; + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (sym->type)) + { + /* if any registers used */ + if (sym->regsUsed) + { + bool bits_popped = FALSE; + /* restore the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + bits_popped = popReg (i, bits_popped); + } + } + } + else + { + /* this function has a function call. We cannot + determine register usage so we will have to pop the + entire bank */ + if (options.parms_in_bank1) + { + for (i = 7; i >= 0; i--) + { + emitpop (rb1regs[i]); + } + } + unsaveRBank (0, ic, FALSE); + } + } + else + { + /* This ISR uses a non-zero bank. + * + * Restore any register banks saved by genFunction + * in reverse order. + */ + unsigned savedBanks = SPEC_ISR_SAVED_BANKS (currFunc->etype); + int ix; + + for (ix = MAX_REGISTER_BANKS - 1; ix >= 0; ix--) + { + if (savedBanks & (1 << ix)) + { + unsaveRBank (ix, NULL, FALSE); + } + } + + if (options.useXstack) + { + /* Restore bank AFTER calling unsaveRBank, + * since it can trash r0. + */ + emitpop ("psw"); + } + } + + if (options.model == MODEL_FLAT24 && !inExcludeList ("dpx")) + { + if (options.stack10bit) + { + emitpop (DP2_RESULT_REG); + emitpop ("dpx1"); + emitpop ("dph1"); + emitpop ("dpl1"); + } + emitpop ("dps"); + emitpop ("dpx"); + } + if (!inExcludeList ("dph")) + emitpop ("dph"); + if (!inExcludeList ("dpl")) + emitpop ("dpl"); + if (!inExcludeList ("b")) + emitpop ("b"); + if (!inExcludeList ("acc")) + emitpop ("acc"); + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("reti", ""); + } + else + { + if (IFFUNC_CALLEESAVES (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + emitpop (REG_WITH_INDEX (i)->dname); + } + } + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("ret", ""); + } + + if (!port->peep.getRegsRead || !port->peep.getRegsWritten || options.nopeep) + return; + + /* If this was an interrupt handler using bank 0 that called another */ + /* function, then all registers must be saved; nothing to optimize. */ + if (IFFUNC_ISISR (sym->type) && IFFUNC_HASFCALL (sym->type) && !FUNC_REGBANK (sym->type)) + return; + + /* There are no push/pops to optimize if not callee-saves or ISR */ + if (!(FUNC_CALLEESAVES (sym->type) || FUNC_ISISR (sym->type))) + return; + + /* If there were stack parameters, we cannot optimize without also */ + /* fixing all of the stack offsets; this is too dificult to consider. */ + if (FUNC_HASSTACKPARM (sym->type)) + return; + + /* Compute the registers actually used */ + regsUsed = newBitVect (ds390_nRegs); + regsUsedPrologue = newBitVect (ds390_nRegs); + while (lnp) + { + if (lnp->ic && lnp->ic->op == FUNCTION) + regsUsedPrologue = bitVectUnion (regsUsedPrologue, port->peep.getRegsWritten (lnp)); + else + regsUsed = bitVectUnion (regsUsed, port->peep.getRegsWritten (lnp)); + + if (lnp->ic && lnp->ic->op == FUNCTION && lnp->prev && lnp->prev->ic && lnp->prev->ic->op == ENDFUNCTION) + break; + if (!lnp->prev) + break; + lnp = lnp->prev; + } + + if (bitVectBitValue (regsUsedPrologue, DPS_IDX) && !bitVectBitValue (regsUsed, DPS_IDX)) + { + bitVectUnSetBit (regsUsedPrologue, DPS_IDX); + } + + if (bitVectBitValue (regsUsedPrologue, CND_IDX) && !bitVectBitValue (regsUsed, CND_IDX)) + { + regsUsed = bitVectUnion (regsUsed, regsUsedPrologue); + if (IFFUNC_ISISR (sym->type) && !FUNC_REGBANK (sym->type) && !sym->stack && !FUNC_ISCRITICAL (sym->type)) + bitVectUnSetBit (regsUsed, CND_IDX); + } + else + regsUsed = bitVectUnion (regsUsed, regsUsedPrologue); + + /* If this was an interrupt handler that called another function */ + /* function, then assume working registers may be modified by it. */ + if (IFFUNC_ISISR (sym->type) && IFFUNC_HASFCALL (sym->type)) + { + regsUsed = bitVectSetBit (regsUsed, AP_IDX); + regsUsed = bitVectSetBit (regsUsed, DPX1_IDX); + regsUsed = bitVectSetBit (regsUsed, DPL1_IDX); + regsUsed = bitVectSetBit (regsUsed, DPH1_IDX); + regsUsed = bitVectSetBit (regsUsed, DPX_IDX); + regsUsed = bitVectSetBit (regsUsed, DPL_IDX); + regsUsed = bitVectSetBit (regsUsed, DPH_IDX); + regsUsed = bitVectSetBit (regsUsed, DPS_IDX); + regsUsed = bitVectSetBit (regsUsed, B_IDX); + regsUsed = bitVectSetBit (regsUsed, A_IDX); + regsUsed = bitVectSetBit (regsUsed, CND_IDX); + } + + /* Remove the unneeded push/pops */ + regsUnneeded = newBitVect (ds390_nRegs); + while (lnp) + { + if (lnp->ic && (lnp->ic->op == FUNCTION || lnp->ic->op == ENDFUNCTION)) + { + if (!strncmp (lnp->line, "push", 4)) + { + idx = bitVectFirstBit (port->peep.getRegsRead (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + if (!strncmp (lnp->line, "pop", 3) || !strncmp (lnp->line, "mov", 3)) + { + idx = bitVectFirstBit (port->peep.getRegsWritten (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + } + lnp = lnp->next; + } + + for (idx = 0; idx < regsUnneeded->size; idx++) + if (bitVectBitValue (regsUnneeded, idx)) + emitcode (";", "eliminated unneeded push/pop %s", REG_WITH_INDEX (idx)->dname); + + freeBitVect (regsUnneeded); + freeBitVect (regsUsed); + freeBitVect (regsUsedPrologue); +} + +/*-----------------------------------------------------------------*/ +/* genJavaNativeRet - generate code for return JavaNative */ +/*-----------------------------------------------------------------*/ +static void +genJavaNativeRet (iCode * ic) +{ + int i, size; + + aopOp (IC_LEFT (ic), ic, FALSE, IS_OP_RUONLY (IC_LEFT (ic)) ? FALSE : TRUE); + size = AOP_SIZE (IC_LEFT (ic)); + + assert (size <= 4); + + /* it is assigned to GPR0-R3 then push them */ + if (aopHasRegs (AOP (IC_LEFT (ic)), R0_IDX, R1_IDX) || aopHasRegs (AOP (IC_LEFT (ic)), R2_IDX, R3_IDX)) + { + for (i = 0; i < size; i++) + { + emitcode ("push", "%s", aopGet (IC_LEFT (ic), i, FALSE, TRUE, DP2_RESULT_REG)); + } + for (i = (size - 1); i >= 0; i--) + { + emitcode ("pop", "a%s", javaRet[i]); + } + } + else + { + for (i = 0; i < size; i++) + emitcode ("mov", "%s,%s", javaRet[i], aopGet (IC_LEFT (ic), i, FALSE, TRUE, DP2_RESULT_REG)); + } + for (i = size; i < 4; i++) + emitcode ("mov", "%s,#0", javaRet[i]); + return; +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0, pushed = 0; + bool pushedA = FALSE; + + D (emitcode (";", "genRet")); + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* if this is a JavaNative function then return + value in different register */ + if (IFFUNC_ISJAVANATIVE (currFunc->type)) + { + genJavaNativeRet (ic); + goto jumpret; + } + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE, (IS_OP_RUONLY (IC_LEFT (ic)) ? FALSE : TRUE)); + size = AOP_SIZE (IC_LEFT (ic)); + + _startLazyDPSEvaluation (); + + if (IS_BIT (_G.currentFunc->etype)) + { + if (!IS_OP_RUONLY (IC_LEFT (ic))) + toCarry (IC_LEFT (ic)); + _endLazyDPSEvaluation (); + } + else + { + while (size--) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE, NULL)); + pushed++; + } + else + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL); + if (!EQ (fReturn[offset], l)) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_R0 || AOP_TYPE (IC_LEFT (ic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + if (size && !strcmp(fReturn[offset], "a") && aopGetUsesAcc (IC_LEFT (ic), offset+1)) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + } + _endLazyDPSEvaluation (); + + if (pushedA) + { + emitpop ("acc"); + } + + while (pushed) + { + pushed--; + if (!EQ (fReturn[pushed], "a")) + emitpop (fReturn[pushed]); + else + emitpop ("acc"); + } + } + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + D (emitcode (";", "genLabel")); + + emitLabel (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + D (emitcode (";", "genGoto")); + + emitcode ("ljmp", "!tlabel", labelKey2num (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* findLabelBackwards: walks back through the iCode chain looking */ +/* for the given label. Returns number of iCode instructions */ +/* between that label and given ic. */ +/* Returns zero if label not found. */ +/*-----------------------------------------------------------------*/ +static int +findLabelBackwards (iCode * ic, int key) +{ + int count = 0; + + while (ic->prev) + { + ic = ic->prev; + count++; + + /* If we have any pushes or pops, we cannot predict the distance. + I don't like this at all, this should be dealt with in the + back-end */ + if (ic->op == IPUSH || ic->op == IPOP) + { + return 0; + } + + if (ic->op == LABEL && IC_LABEL (ic)->key == key) + { + /* printf("findLabelBackwards = %d\n", count); */ + return count; + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode *ic) +{ + unsigned long long icount; + unsigned int size = getDataSize (IC_RESULT (ic)), offset; + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 4) + return FALSE; + + if (size == 1 && AOP (IC_LEFT (ic)) == AOP (IC_RESULT (ic)) && AOP_TYPE (IC_LEFT (ic)) == AOP_DIR) + { + while (icount--) + { + emitcode ("inc", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL)); + } + return TRUE; + } + /* if increment 16 bits in register */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && + AOP_TYPE (IC_RESULT (ic)) == AOP_REG && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + int emitTlbl; + int labelRange; + const char *l; + + /* If the next instruction is a goto and the goto target + * is <= 5 instructions previous to this, we can generate + * jumps straight to that target. + */ + if (ic->next && ic->next->op == GOTO + && (labelRange = findLabelBackwards (ic, IC_LABEL (ic->next)->key)) != 0 && labelRange <= 5) + { + D (emitcode (";", "tail increment optimized (range %d)", labelRange)); + tlbl = IC_LABEL (ic->next); + emitTlbl = 0; + } + else + { + tlbl = newiTempLabel (NULL); + emitTlbl = 1; + } + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE, NULL); + emitcode ("inc", "%s", l); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("clr", "a"); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE, NULL); + emitcode ("inc", "%s", l); + for(offset = 2; size > 2; size--, offset++) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), offset, FALSE, FALSE, NULL); + emitcode ("inc", "%s", l); + } + + if (emitTlbl) + { + emitLabel (tlbl); + } + return TRUE; + } + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_STR && IS_ITEMP (IC_RESULT (ic)) && + !AOP_USESDPTR (IC_LEFT (ic)) && icount <= 5 && size <= 3 && options.model == MODEL_FLAT24) + { + if (AOP_SIZE (IC_RESULT (ic)) == 4) + { + emitcode ("mov", "b,%s", aopGet (IC_LEFT (ic), 3, FALSE, FALSE, NULL)); + } + switch (size) + { + case 3: + emitcode ("mov", "dpx,%s", aopGet (IC_LEFT (ic), 2, FALSE, FALSE, NULL)); + case 2: + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE, NULL)); + case 1: + emitcode ("mov", "dpl,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE, NULL)); + break; + } + while (icount--) + emitcode ("inc", "dptr"); + + return TRUE; + } + + if (AOP_INDPTRn (IC_LEFT (ic)) && AOP_INDPTRn (IC_RESULT (ic)) && + AOP (IC_LEFT (ic))->aopu.dptr == AOP (IC_RESULT (ic))->aopu.dptr && icount <= 5) + { + emitcode ("mov", "dps,#!constbyte", AOP (IC_LEFT (ic))->aopu.dptr); + while (icount--) + emitcode ("inc", "dptr"); + emitcode ("mov", "dps,#0"); + return TRUE; + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && + AOP_TYPE (IC_RESULT (ic)) == AOP_REG && sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + if (icount > 3) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE, NULL)); + emitcode ("add", "a,#!constbyte", ((char) icount) & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + else + { + _startLazyDPSEvaluation (); + while (icount--) + { + emitcode ("inc", "%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE, NULL)); + } + _endLazyDPSEvaluation (); + } + + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +static void +outBitAcc (operand * result) +{ + symbol *tlbl = newiTempLabel (NULL); + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + aopPut (result, "a", 0); + } + else + { + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + emitcode ("mov", "a,%s", one); + emitLabel (tlbl); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlusBits - generates code for addition of two bits */ +/*-----------------------------------------------------------------*/ +static void +genPlusBits (iCode * ic) +{ + D (emitcode (";", "genPlusBits")); + + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("addc", "a,%s", zero); + outAcc (IC_RESULT (ic)); + } +} + +static void +adjustArithmeticResult (iCode * ic) +{ + if (opIsGptr (IC_RESULT (ic)) && opIsGptr (IC_LEFT (ic)) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), GPTRSIZE - 1, FALSE, FALSE, NULL), GPTRSIZE - 1); + } + + if (opIsGptr (IC_RESULT (ic)) && opIsGptr (IC_RIGHT (ic)) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_RIGHT (ic), GPTRSIZE - 1, FALSE, FALSE, NULL), GPTRSIZE - 1); + } + + if (opIsGptr (IC_RESULT (ic)) && + IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && + IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } +} + +// The guts of AOP_OP_3_NOFATAL. Generates the left & right opcodes of an IC, +// generates the result if possible. If result is generated, returns TRUE; otherwise +// returns false and caller must deal with fact that result isn't aopOp'd. +bool +aopOp3 (iCode * ic) +{ + bool dp1InUse, dp2InUse; + bool useDp2; + + // First, generate the right opcode. DPTR may be used if neither left nor result are + // of type AOP_STR. + +// D (emitcode(";", "aopOp3: IS_OP_RUONLY left: %s right: %s result: %s", +// IS_OP_RUONLY(IC_LEFT(ic)) ? "true" : "false", +// IS_OP_RUONLY(IC_RIGHT(ic)) ? "true" : "false", +// IS_OP_RUONLY(IC_RESULT(ic)) ? "true" : "false"); +// ); +// D (emitcode(";", "aopOp3: AOP_IS_DPTRn left: %s right: %s result: %s", +// AOP_IS_DPTRn(IC_LEFT(ic)) ? "true" : "false", +// AOP_IS_DPTRn(IC_RIGHT(ic)) ? "true" : "false", +// AOP_IS_DPTRn(IC_RESULT(ic)) ? "true" : "false"); +// ); + + // Right uses DPTR unless left or result is an AOP_STR; however, + // if right is an AOP_STR, it must use DPTR regardless. + if ((IS_OP_RUONLY (IC_LEFT (ic)) || IS_OP_RUONLY (IC_RESULT (ic))) && !IS_OP_RUONLY (IC_RIGHT (ic))) + { + useDp2 = TRUE; + } + else + { + useDp2 = FALSE; + } + + aopOp (IC_RIGHT (ic), ic, FALSE, useDp2); + + // if the right used DPTR, left MUST use DPTR2. + // if the right used DPTR2, left MUST use DPTR. + // if both are still available, we prefer to use DPTR. But if result is an AOP_STR + // and left is not an AOP_STR, then we will get better code if we use DP2 for left, + // enabling us to assign DPTR to result. + + if (AOP_USESDPTR (IC_RIGHT (ic))) + { + useDp2 = TRUE; + } + else if (AOP_USESDPTR2 (IC_RIGHT (ic))) + { + useDp2 = FALSE; + } + else + { + if (IS_OP_RUONLY (IC_RESULT (ic)) && !IS_OP_RUONLY (IC_LEFT (ic))) + { + useDp2 = TRUE; + } + else + { + useDp2 = FALSE; + } + } + + aopOp (IC_LEFT (ic), ic, FALSE, useDp2); + + + // We've op'd the left & right. So, if left or right are the same operand as result, + // we know aopOp will succeed, and we can just do it & bail. + if (isOperandEqual (IC_LEFT (ic), IC_RESULT (ic))) + { + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR2 (IC_LEFT (ic))); + return TRUE; + } + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (ic))) + { +// D (emitcode(";", "aopOp3: (left | right) & result equal")); + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR2 (IC_RIGHT (ic))); + return TRUE; + } + + // Operands may be equivalent (but not equal) if they share a spill location. If + // so, use the same DPTR or DPTR2. + if (operandsEqu (IC_LEFT (ic), IC_RESULT (ic))) + { + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR2 (IC_LEFT (ic))); + return TRUE; + } + if (operandsEqu (IC_RIGHT (ic), IC_RESULT (ic))) + { + aopOp (IC_RESULT (ic), ic, TRUE, AOP_USESDPTR2 (IC_RIGHT (ic))); + return TRUE; + } + + // Note which dptrs are currently in use. + dp1InUse = AOP_USESDPTR (IC_LEFT (ic)) || AOP_USESDPTR (IC_RIGHT (ic)); + dp2InUse = AOP_USESDPTR2 (IC_LEFT (ic)) || AOP_USESDPTR2 (IC_RIGHT (ic)); + + // OK, now if either left or right uses DPTR and the result is an AOP_STR, we cannot + // generate it. + if (dp1InUse && IS_OP_RUONLY (IC_RESULT (ic))) + { + return FALSE; + } + + // Likewise, if left or right uses DPTR2 and the result is a DPTRn, we cannot generate it. + if (dp2InUse && AOP_IS_DPTRn (IC_RESULT (ic))) + { + return FALSE; + } + + // or, if both dp1 & dp2 are in use and the result needs a dptr, we're out of luck + if (dp1InUse && dp2InUse && isOperandInFarSpace (IC_RESULT (ic))) + { + return FALSE; + } + + aopOp (IC_RESULT (ic), ic, TRUE, dp1InUse); + + // Some sanity checking... + if (dp1InUse && AOP_USESDPTR (IC_RESULT (ic))) + { + fprintf (stderr, "Internal error: got unexpected DPTR (%s:%d %s:%d)\n", __FILE__, __LINE__, ic->filename, ic->lineno); + emitcode (";", ">>> unexpected DPTR here."); + } + + if (dp2InUse && AOP_USESDPTR2 (IC_RESULT (ic))) + { + fprintf (stderr, "Internal error: got unexpected DPTR2 (%s:%d %s:%d)\n", __FILE__, __LINE__, ic->filename, ic->lineno); + emitcode (";", ">>> unexpected DPTR2 here."); + } + + return TRUE; +} + +// Macro to aopOp all three operands of an ic. If this cannot be done, +// the IC_LEFT and IC_RIGHT operands will be aopOp'd, and the rc parameter +// will be set TRUE. The caller must then handle the case specially, noting +// that the IC_RESULT operand is not aopOp'd. +// +#define AOP_OP_3_NOFATAL(ic, rc) \ + do { rc = !aopOp3(ic); } while (0) + +// aopOp the left & right operands of an ic. +#define AOP_OP_2(ic) \ + aopOp (IC_RIGHT (ic), ic, FALSE, IS_OP_RUONLY (IC_LEFT (ic))); \ + aopOp (IC_LEFT (ic), ic, FALSE, AOP_USESDPTR (IC_RIGHT (ic))); + +// convienience macro. +#define AOP_SET_LOCALS(ic) \ + left = IC_LEFT(ic); \ + right = IC_RIGHT(ic); \ + result = IC_RESULT(ic); + + +// Given an integer value of pushedSize bytes on the stack, +// adjust it to be resultSize bytes, either by discarding +// the most significant bytes or by zero-padding. +// +// On exit from this macro, pushedSize will have been adjusted to +// equal resultSize, and ACC may be trashed. +#define ADJUST_PUSHED_RESULT(pushedSize, resultSize) \ + /* If the pushed data is bigger than the result, \ + * simply discard unused bytes. Icky, but works. \ + */ \ + while (pushedSize > resultSize) \ + { \ + D (emitcode (";", "discarding unused result byte.")); \ + emitcode ("pop", "acc"); \ + pushedSize--; \ + } \ + if (pushedSize < resultSize) \ + { \ + emitcode ("clr", "a"); \ + /* Conversly, we haven't pushed enough here. \ + * just zero-pad, and all is well. \ + */ \ + while (pushedSize < resultSize) \ + { \ + emitcode("push", "acc"); \ + pushedSize++; \ + } \ + } \ + assert(pushedSize == resultSize); + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + bool pushResult; + int rSize; + bool swappedLR = FALSE; + + D (emitcode (";", "genPlus")); + + /* special cases :- */ + if (IS_OP_RUONLY (IC_LEFT (ic)) && isOperandLiteral (IC_RIGHT (ic)) && IS_OP_RUONLY (IC_RESULT (ic))) + { + aopOp (IC_RIGHT (ic), ic, TRUE, FALSE); + size = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + if (size <= 9) + { + while (size--) + emitcode ("inc", "dptr"); + } + else + { + emitcode ("mov", "a,dpl"); + emitcode ("add", "a,#!constbyte", size & 0xff); + emitcode ("mov", "dpl,a"); + emitcode ("mov", "a,dph"); + emitcode ("addc", "a,#!constbyte", (size >> 8) & 0xff); + emitcode ("mov", "dph,a"); + emitcode ("mov", "a,dpx"); + emitcode ("addc", "a,#!constbyte", (size >> 16) & 0xff); + emitcode ("mov", "dpx,a"); + } + freeAsmop (IC_RIGHT (ic), NULL, ic, FALSE); + return; + } + if (IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->remat && isOperandInFarSpace (IC_RIGHT (ic))) + { + operand *op = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = op; + } + + AOP_OP_3_NOFATAL (ic, pushResult); + + if (pushResult) + { + D (emitcode (";", "genPlus: must push result: 3 ops in far space")); + } + + if (!pushResult) + { + /* if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || + ((AOP_NEEDSACC (IC_LEFT (ic))) && !(AOP_NEEDSACC (IC_RIGHT (ic)))) || AOP_TYPE (IC_RIGHT (ic)) == AOP_ACC) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + swappedLR = TRUE; + D (emitcode (";", "Swapped plus args.")); + } + + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genPlusBits (ic); + goto release; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + /* if result in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + if (ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + else + { + size = getDataSize (IC_RESULT (ic)); + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + emitcode ("addc", "a,%s", zero); + aopPut (IC_RESULT (ic), "a", offset++); + } + _endLazyDPSEvaluation (); + } + goto release; + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + { + D (emitcode (";", "did genPlusIncr")); + goto release; + } + + } + size = getDataSize (pushResult ? IC_LEFT (ic) : IC_RESULT (ic)); + + _startLazyDPSEvaluation (); + while (size--) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && !AOP_NEEDSACC (IC_RIGHT (ic))) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + if (offset == 0) + emitcode ("add", "a,%s", aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + else + emitcode ("addc", "a,%s", aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + } + else + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && (offset == 0)) + { + /* right is going to use ACC or we would have taken the + * above branch. + */ + assert (AOP_NEEDSACC (IC_RIGHT (ic))); + TR_AP ("#3"); + D (emitcode (";", "+ AOP_ACC special case.");); + emitcode ("xch", "a, %s", DP2_RESULT_REG); + } + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + if (offset == 0) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_ACC) + { + TR_AP ("#4"); + emitcode ("add", "a, %s", DP2_RESULT_REG); + } + else + { + emitcode ("add", "a,%s", aopGet (IC_LEFT (ic), offset, FALSE, FALSE, DP2_RESULT_REG)); + } + } + else + { + emitcode ("addc", "a,%s", aopGet (IC_LEFT (ic), offset, FALSE, FALSE, DP2_RESULT_REG)); + } + } + if (!pushResult) + { + aopPut (IC_RESULT (ic), "a", offset); + } + else + { + emitcode ("push", "acc"); + } + offset++; + } + _endLazyDPSEvaluation (); + + if (pushResult) + { + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + adjustArithmeticResult (ic); + + size = getDataSize (IC_LEFT (ic)); + rSize = getDataSize (IC_RESULT (ic)); + + ADJUST_PUSHED_RESULT (size, rSize); + + _startLazyDPSEvaluation (); + while (size--) + { + emitcode ("pop", "acc"); + aopPut (IC_RESULT (ic), "a", size); + } + _endLazyDPSEvaluation (); + } + else + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + if (swappedLR) + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + unsigned long long icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + + /* will try to generate a decrement */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 4) + return FALSE; + + if (size == 1 && AOP (IC_LEFT (ic)) == AOP (IC_RESULT (ic)) && AOP_TYPE (IC_LEFT (ic)) == AOP_DIR) + { + while (icount--) + { + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL)); + } + return TRUE; + } + /* if decrement 16 bits in register */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && + AOP_TYPE (IC_RESULT (ic)) == AOP_REG && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + int emitTlbl; + int labelRange; + const char *l; + + /* If the next instruction is a goto and the goto target + * is <= 5 instructions previous to this, we can generate + * jumps straight to that target. + */ + if (ic->next && ic->next->op == GOTO + && (labelRange = findLabelBackwards (ic, IC_LABEL (ic->next)->key)) != 0 && labelRange <= 5) + { + D (emitcode (";", "tail decrement optimized (range %d)", labelRange)); + tlbl = IC_LABEL (ic->next); + emitTlbl = 0; + } + else + { + tlbl = newiTempLabel (NULL); + emitTlbl = 1; + } + + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE, NULL); + emitcode ("dec", "%s", l); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || AOP_TYPE (IC_RESULT (ic)) == AOP_DPTR || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("mov", "a,#!constbyte", 0xff); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE, NULL); + emitcode ("dec", "%s", l); + if (size > 2) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || + AOP_TYPE (IC_RESULT (ic)) == AOP_DPTR || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB24, FALSE, FALSE, NULL); + emitcode ("dec", "%s", l); + } + if (size > 3) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || + AOP_TYPE (IC_RESULT (ic)) == AOP_DPTR || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), MSB32, FALSE, FALSE, NULL)); + } + if (emitTlbl) + { + emitLabel (tlbl); + } + return TRUE; + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && + AOP_TYPE (IC_RESULT (ic)) == AOP_REG && sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + const char *l; + + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + { + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL)); + l = "a"; + } + else + { + l = aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL); + } + + _startLazyDPSEvaluation (); + while (icount--) + { + emitcode ("dec", "%s", l); + } + _endLazyDPSEvaluation (); + + if (AOP_NEEDSACC (IC_RESULT (ic))) + aopPut (IC_RESULT (ic), "a", 0); + + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) + { + _startLazyDPSEvaluation (); + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + { + aopPut (result, "a", offset++); + } + } + else + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + _endLazyDPSEvaluation (); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinusBits - generates code for subtraction of two bits */ +/*-----------------------------------------------------------------*/ +static void +genMinusBits (iCode * ic) +{ + symbol *lbl = newiTempLabel (NULL); + + D (emitcode (";", "genMinusBits")); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("subb", "a,acc"); + emitcode ("jnb", "%s,!tlabel", AOP (IC_LEFT (ic))->aopu.aop_dir, labelKey2num ((lbl->key))); + emitcode ("inc", "a"); + emitLabel (lbl); + aopPut (IC_RESULT (ic), "a", 0); + addSign (IC_RESULT (ic), MSB16, SPEC_USIGN (getSpec (operandType (IC_RESULT (ic))))); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + int size, offset = 0; + int rSize; + long long lit = 0L; + bool pushResult; + + D (emitcode (";", "genMinus")); + + AOP_OP_3_NOFATAL (ic, pushResult); + + if (!pushResult) + { + /* special cases :- */ + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genMinusBits (ic); + goto release; + } + + /* if I can do a decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + } + + size = getDataSize (pushResult ? IC_LEFT (ic) : IC_RESULT (ic)); + + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + { + CLRC; + } + else + { + lit = ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + lit = -lit; + } + + + /* if literal, add a,#-lit, else normal subb */ + _startLazyDPSEvaluation (); + while (size--) + { + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + { + if (AOP_USESDPTR (IC_RIGHT (ic))) + { + emitcode ("mov", "b,%s", aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + emitcode ("subb", "a,b"); + } + else + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + emitcode ("subb", "a,%s", aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, DP2_RESULT_REG)); + } + } + else + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + /* first add without previous c */ + if (!offset) + { + if (!size && lit == -1) + { + emitcode ("dec", "a"); + } + else + { + emitcode ("add", "a,#!constbyte", (unsigned int) (lit & 0x0FFL)); + } + } + else + { + emitcode ("addc", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0FFL)); + } + } + + if (pushResult) + { + emitcode ("push", "acc"); + } + else + { + aopPut (IC_RESULT (ic), "a", offset); + } + offset++; + } + _endLazyDPSEvaluation (); + + if (pushResult) + { + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + adjustArithmeticResult (ic); + + size = getDataSize (IC_LEFT (ic)); + rSize = getDataSize (IC_RESULT (ic)); + + ADJUST_PUSHED_RESULT (size, rSize); + + _startLazyDPSEvaluation (); + while (size--) + { + emitcode ("pop", "acc"); + aopPut (IC_RESULT (ic), "a", size); + } + _endLazyDPSEvaluation (); + } + else + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result, iCode * ic) +{ + D (emitcode (";", "genMultbits")); + + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + aopOp (result, ic, TRUE, FALSE); + outBitC (result); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8*8=8/16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result, iCode * ic) +{ + symbol *lbl; + int size, offset = 0; + bool runtimeSign, compiletimeSign; + bool lUnsigned, rUnsigned, pushedB; + + /* (if two literals: the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + /* emitcode (";", "swapped left and right"); */ + } + /* if no literal, unsigned on the right: shorter code */ + if (AOP_TYPE (right) != AOP_LIT && SPEC_USIGN (getSpec (operandType (left)))) + { + operand *t = right; + right = left; + left = t; + } + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + if ((lUnsigned && rUnsigned) + /* sorry, I don't know how to get size + without calling aopOp (result,...); + see Feature Request */ + /* || size == 1 */ ) + /* no, this is not a bug; with a 1 byte result there's + no need to take care about the signedness! */ + { + /* just an unsigned 8 * 8 = 8 multiply + or 8u * 8u = 16u */ + /* emitcode (";","unsigned"); */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mul", "ab"); + + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result); + + if (size < 1) + { + /* this should never happen */ + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", size, __FILE__, lineno); + exit (EXIT_FAILURE); + } + + aopPut (result, "a", offset++); + _G.accInUse--; + if (size != 1) + aopPut (result, "b", offset++); + + while (size-- > 2) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* we have to do a signed multiply */ + /* emitcode (";", "signed"); */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#!constbyte", -val); + else + emitcode ("mov", "b,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) /* emitcode (";", "signed"); */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#!constbyte", -val); + else + emitcode ("mov", "a,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + + if (!lUnsigned) /* emitcode (";", "signed"); */ + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the multiplication */ + emitcode ("mul", "ab"); + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result); + + if (size < 1) + { + /* this should never happen */ + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", size, __FILE__, lineno); + exit (EXIT_FAILURE); + } + + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + if (size == 1) + emitcode ("inc", "a"); /* inc doesn't set carry flag */ + else + { + emitcode ("add", "a,#0x01"); /* this sets carry flag */ + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); /* msb 2's complement */ + emitcode ("addc", "a,#0x00"); + emitcode ("xch", "a,b"); + } + emitLabel (lbl); + } + aopPut (result, "a", offset++); + _G.accInUse--; + if (size != 1) + aopPut (result, "b", offset++); + + if (size > 2) + { + emitcode ("mov", "c,b.7"); + emitcode ("subb", "a,acc"); + _G.accInUse++; + while (size-- > 2) + aopPut (result, "a", offset++); + _G.accInUse--; + } + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMultTwoByte - use the DS390 MAC unit to do 16*16 multiply */ +/*-----------------------------------------------------------------*/ +static void +genMultTwoByte (operand * left, operand * right, operand * result, iCode * ic) +{ + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (left)); + int umult = SPEC_USIGN (retype) | SPEC_USIGN (letype); + symbol *lbl; + + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + /* save EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("setb", "F1"); + emitcode ("jbc", "EA,!tlabel", labelKey2num (lbl->key)); + emitcode ("clr", "F1"); + emitLabel (lbl); + + /* load up MB with right */ + if (!umult) + { + emitcode ("clr", "F0"); + if (AOP_TYPE (right) == AOP_LIT) + { + int val = (int) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + { + emitcode ("setb", "F0"); + val = -val; + } + emitcode ("mov", "mb,#!constbyte", val & 0xff); + emitcode ("mov", "mb,#!constbyte", (val >> 8) & 0xff); + } + else + { + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitcode ("setb", "F0"); + emitLabel (lbl); + emitcode ("mov", "mb,b"); + emitcode ("mov", "mb,a"); + } + } + else + { + emitcode ("mov", "mb,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "mb,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + } + /* load up MA with left */ + if (!umult) + { + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitcode ("jbc", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); + emitLabel (lbl); + emitcode ("mov", "ma,b"); + emitcode ("mov", "ma,a"); + } + else + { + emitcode ("mov", "ma,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "ma,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + } + /* wait for multiplication to finish */ + lbl = newiTempLabel (NULL); + emitLabel (lbl); + emitcode ("mov", "a,mcnt1"); + emitcode ("anl", "a,#!constbyte", 0x80); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + aopOp (result, ic, TRUE, FALSE); + + /* if unsigned then simple */ + if (umult) + { + emitcode ("mov", "a,ma"); + if (AOP_SIZE (result) >= 4) + aopPut (result, "a", 3); + emitcode ("mov", "a,ma"); + if (AOP_SIZE (result) >= 3) + aopPut (result, "a", 2); + aopPut (result, "ma", 1); + aopPut (result, "ma", 0); + } + else + { + emitcode ("push", "ma"); + emitcode ("push", "ma"); + emitcode ("push", "ma"); + MOVA ("ma"); + /* negate result if needed */ + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitLabel (lbl); + if (AOP_TYPE (result) == AOP_ACC) + { + D (emitcode (";", "ACC special case.")); + /* We know result is the only live aop, and + * it's obviously not a DPTR2, so AP is available. + */ + emitcode ("mov", "%s,acc", DP2_RESULT_REG); + } + else + { + aopPut (result, "a", 0); + } + + emitcode ("pop", "acc"); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0"); + emitLabel (lbl); + aopPut (result, "a", 1); + emitcode ("pop", "acc"); + if (AOP_SIZE (result) >= 3) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0"); + emitLabel (lbl); + aopPut (result, "a", 2); + } + emitcode ("pop", "acc"); + if (AOP_SIZE (result) >= 4) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0"); + emitLabel (lbl); + aopPut (result, "a", 3); + } + if (AOP_TYPE (result) == AOP_ACC) + { + /* We stashed the result away above. */ + emitcode ("mov", "acc,%s", DP2_RESULT_REG); + } + + } + freeAsmop (result, NULL, ic, TRUE); + + /* restore EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F1,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "EA"); + emitLabel (lbl); + return; +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMult")); + + /* assign the asmops */ + AOP_OP_2 (ic); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result, ic); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genMultOneByte (left, right, result, ic); + goto release; + } + + if (AOP_SIZE (left) == 2 && AOP_SIZE (right) == 2) + { + /* use the ds390 ARITHMETIC accel UNIT */ + genMultTwoByte (left, right, result, ic); + return; + } + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result, iCode * ic) +{ + bool pushedB; + + D (emitcode (";", "genDivbits")); + + pushedB = pushB (); + + /* the result must be bit */ + LOAD_AB_FOR_DIV (left, right); + emitcode ("div", "ab"); + emitcode ("rrc", "a"); + aopOp (result, ic, TRUE, FALSE); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result, iCode * ic) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genDivOneByte")); + + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + LOAD_AB_FOR_DIV (left, right); + emitcode ("div", "ab"); + + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + aopPut (result, "a", 0); + _G.accInUse--; + + size = AOP_SIZE (result) - 1; + + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#0x%02x", -val); + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the division */ + emitcode ("nop", "; workaround for DS80C390 div bug."); + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result) - 1; + + if (size > 0) + { + /* 123 look strange, but if (OP_SYMBOL (op)->accuse == 1) + then the result will be in b, a */ + emitcode ("mov", "b,a"); /* 1 */ + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + emitcode ("xch", "a,b"); /* 2 */ + while (size--) + aopPut (result, "b", offset++); /* write msb's */ + } + else /* compiletimeSign */ + while (size--) + aopPut (result, "#0xff", offset++); /* write msb's */ + } + aopPut (result, "a", 0); /* 3: write lsb */ + } + else + { + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result) - 1; + + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + } + _G.accInUse--; + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genDivTwoByte - use the DS390 MAC unit to do 16/16 divide */ +/*-----------------------------------------------------------------*/ +static void +genDivTwoByte (operand * left, operand * right, operand * result, iCode * ic) +{ + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (left)); + int umult = SPEC_USIGN (retype) | SPEC_USIGN (letype); + symbol *lbl; + + /* save EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("setb", "F1"); + emitcode ("jbc", "EA,!tlabel", labelKey2num (lbl->key)); + emitcode ("clr", "F1"); + emitLabel (lbl); + + /* load up MA with left */ + if (!umult) + { + emitcode ("clr", "F0"); + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitcode ("setb", "F0"); + emitLabel (lbl); + emitcode ("mov", "ma,b"); + emitcode ("mov", "ma,a"); + } + else + { + emitcode ("mov", "ma,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "ma,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + } + + /* load up MB with right */ + if (!umult) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int val = (int) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + { + lbl = newiTempLabel (NULL); + emitcode ("jbc", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); + emitLabel (lbl); + val = -val; + } + emitcode ("mov", "mb,#!constbyte", val & 0xff); + emitcode ("mov", "mb,#!constbyte", (val >> 8) & 0xff); + } + else + { + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitcode ("jbc", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); + emitLabel (lbl); + emitcode ("mov", "mb,b"); + emitcode ("mov", "mb,a"); + } + } + else + { + emitcode ("mov", "mb,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "mb,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + } + + /* wait for multiplication to finish */ + lbl = newiTempLabel (NULL); + emitLabel (lbl); + emitcode ("mov", "a,mcnt1"); + emitcode ("anl", "a,#!constbyte", 0x80); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + aopOp (result, ic, TRUE, FALSE); + + /* if unsigned then simple */ + if (umult) + { + aopPut (result, "ma", 1); + aopPut (result, "ma", 0); + } + else + { + emitcode ("push", "ma"); + MOVA ("ma"); + /* negate result if needed */ + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitLabel (lbl); + aopPut (result, "a", 0); + emitcode ("pop", "acc"); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0"); + emitLabel (lbl); + aopPut (result, "a", 1); + } + freeAsmop (result, NULL, ic, TRUE); + /* restore EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F1,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "EA"); + emitLabel (lbl); + return; +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genDiv")); + + /* assign the asmops */ + AOP_OP_2 (ic); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result, ic); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result, ic); + goto release; + } + + if (AOP_SIZE (left) == 2 && AOP_SIZE (right) == 2) + { + /* use the ds390 ARITHMETIC accel UNIT */ + genDivTwoByte (left, right, result, ic); + return; + } + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genModbits :- modulus of bits */ +/*-----------------------------------------------------------------*/ +static void +genModbits (operand * left, operand * right, operand * result, iCode * ic) +{ + bool pushedB; + + D (emitcode (";", "genModbits")); + + pushedB = pushB (); + + /* the result must be bit */ + LOAD_AB_FOR_DIV (left, right); + emitcode ("div", "ab"); + emitcode ("mov", "a,b"); + emitcode ("rrc", "a"); + aopOp (result, ic, TRUE, FALSE); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result, iCode * ic) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genModOneByte")); + + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + LOAD_AB_FOR_DIV (left, right); + emitcode ("div", "ab"); + aopOp (result, ic, TRUE, FALSE); + aopPut (result, "b", 0); + + for (size = AOP_SIZE (result) - 1; size--;) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* modulus: sign of the right operand has no influence on the result! */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (signed char) operandLitValue (right); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* not literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + /* sign adjust left side */ + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + compiletimeSign = TRUE; /* set sign flag */ + emitcode ("mov", "a,#0x%02x", -val); + } + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + + if (!lUnsigned) + { + runtimeSign = TRUE; + emitcode ("clr", "F0"); /* clear sign flag */ + + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); /* set sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the modulus */ + emitcode ("nop", "; workaround for DS80C390 div bug."); + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + emitcode ("mov", "a,b"); + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result) - 1; + + if (size > 0) + { + /* 123 look strange, but if (OP_SYMBOL (op)->accuse == 1) + then the result will be in b, a */ + emitcode ("mov", "b,a"); /* 1 */ + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + emitcode ("xch", "a,b"); /* 2 */ + while (size--) + aopPut (result, "b", offset++); /* write msb's */ + } + else /* compiletimeSign */ + while (size--) + aopPut (result, "#0xff", offset++); /* write msb's */ + } + aopPut (result, "a", 0); /* 3: write lsb */ + } + else + { + _G.accInUse++; + aopOp (result, ic, TRUE, FALSE); + size = AOP_SIZE (result) - 1; + + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + } + _G.accInUse--; + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genModTwoByte - use the DS390 MAC unit to do 16%16 modulus */ +/*-----------------------------------------------------------------*/ +static void +genModTwoByte (operand * left, operand * right, operand * result, iCode * ic) +{ + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (left)); + int umult = SPEC_USIGN (retype) | SPEC_USIGN (letype); + symbol *lbl; + + /* load up MA with left */ + /* save EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("setb", "F1"); + emitcode ("jbc", "EA,!tlabel", labelKey2num (lbl->key)); + emitcode ("clr", "F1"); + emitLabel (lbl); + + if (!umult) + { + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitLabel (lbl); + emitcode ("mov", "ma,b"); + emitcode ("mov", "ma,a"); + } + else + { + emitcode ("mov", "ma,%s", aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "ma,%s", aopGet (left, 1, FALSE, FALSE, NULL)); + } + + /* load up MB with right */ + if (!umult) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int val = (int) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + { + val = -val; + } + emitcode ("mov", "mb,#!constbyte", val & 0xff); + emitcode ("mov", "mb,#!constbyte", (val >> 8) & 0xff); + } + else + { + lbl = newiTempLabel (NULL); + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "a,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + emitcode ("jnb", "acc[7],!tlabel", labelKey2num (lbl->key)); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); + emitcode ("add", "a,#1"); + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); // msb + emitcode ("addc", "a,#0"); + emitLabel (lbl); + emitcode ("mov", "mb,b"); + emitcode ("mov", "mb,a"); + } + } + else + { + emitcode ("mov", "mb,%s", aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "mb,%s", aopGet (right, 1, FALSE, FALSE, NULL)); + } + + /* wait for multiplication to finish */ + lbl = newiTempLabel (NULL); + emitLabel (lbl); + emitcode ("mov", "a,mcnt1"); + emitcode ("anl", "a,#!constbyte", 0x80); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + aopOp (result, ic, TRUE, FALSE); + + aopPut (result, "mb", 1); + aopPut (result, "mb", 0); + freeAsmop (result, NULL, ic, TRUE); + + /* restore EA bit in F1 */ + lbl = newiTempLabel (NULL); + emitcode ("jnb", "F1,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "EA"); + emitLabel (lbl); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMod")); + + /* assign the asmops */ + AOP_OP_2 (ic); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genModbits (left, right, result, ic); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result, ic); + goto release; + } + + if (AOP_SIZE (left) == 2 && AOP_SIZE (right) == 2) + { + /* use the ds390 ARITHMETIC accel UNIT */ + genModTwoByte (left, right, result, ic); + return; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, const char *jval, iCode * popIc) +{ + symbol *jlbl; + symbol *tlbl = newiTempLabel (NULL); + char *inst; + + /* if there is something to be popped then do it first */ + popForBranch (popIc, TRUE); + + D (emitcode (";", "genIfxJump")); + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + inst = ((EQ (jval, "a") ? "jz" : (EQ (jval, "c") ? "jnc" : "jnb"))); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + inst = ((EQ (jval, "a") ? "jnz" : (EQ (jval, "c") ? "jc" : "jb"))); + } + if (EQ (inst, "jb") || EQ (inst, "jnb")) + emitcode (inst, "%s,!tlabel", jval, labelKey2num ((tlbl->key))); + else + emitcode (inst, "!tlabel", labelKey2num (tlbl->key)); + emitcode ("ljmp", "!tlabel", labelKey2num (jlbl->key)); + emitLabel (tlbl); + + /* mark the icode as generated */ + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* isHexChar :- check if a char is a digit or 'a-f' or 'A-F' */ +/*-----------------------------------------------------------------*/ +static int isHexChar (const char c) +{ + const char hc[] = "0123456789ABCDEFabcdef"; + size_t i; + for (i = 0; i < strlen (hc); i++) + if (c == hc[i]) + return 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (operand * left, operand * right, iCode * ic, iCode * ifx, int sign) +{ + int size, offset = 0; + unsigned long long lit = 0; + operand *result; + + D (emitcode (";", "genCmp")); + + result = IC_RESULT (ic); + + /* if left & right are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + /* generic pointers require special handling since all NULL pointers must compare equal */ + else if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < GPTRSIZE) + { + emitpush (aopGet (left, offset++, FALSE, TRUE, NULL)); + } + loadDptrFromOperand (right, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + } + else + { + /* subtract right from left if at the + end the carry flag is set then we know that + left is greater than right */ + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + /* if unsigned char cmp with lit, do cjne left,#right,zz */ + if (size == 1 && !sign && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) != AOP_DIR && AOP_TYPE (left) != AOP_STR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE, NULL)); + symbol *lbl = newiTempLabel (NULL); + if (AOP(left)->type != AOP_IMMD || ((AOP(right)->type != AOP_IMMD) && AOP(right)->type != AOP_LIT)) + emitcode ("cjne", "%s,%s,!tlabel", l, aopGet (right, offset, FALSE, FALSE, NULL), labelKey2num (lbl->key)); + else + { + const char *l0 = l; + const char *r0 = aopGet (right, offset, FALSE, FALSE, NULL); + long l1, r1; + while (!isHexChar (*l0)) + l0++; + while (!isHexChar (*r0)) + r0++; + assert (sscanf(l0, "0x%lx", &l1) == 1); + assert (sscanf(r0, "0x%lx", &r1) == 1); + if (l1 != r1) + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + } + Safe_free (l); + emitLabel (lbl); + } + else + { + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + /* optimize if(x < 0) or if(x >= 0) */ + if (lit == 0L) + { + if (!sign) + { + CLRC; + } + else + { + MOVA (aopGet (left, AOP_SIZE (left) - 1, FALSE, FALSE, NULL)); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx) + { + freeAsmop (result, NULL, ic, TRUE); + genIfxJump (ifx, "acc[7]", ic->next); + return; + } + else + { + emitcode ("rlc", "a"); + } + goto release_freedLR; + } + goto release; + } + } + CLRC; + while (size--) + { + // emitcode (";", "genCmp #1: %d/%d/%d", size, sign, offset); + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + // emitcode (";", "genCmp #2"); + if (sign && (size == 0)) + { + // emitcode (";", "genCmp #3"); + emitcode ("xrl", "a,#!constbyte", 0x80); + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + // emitcode (";", "genCmp #3.1"); + emitcode ("subb", "a,#!constbyte", 0x80 ^ (unsigned int) ((lit >> (offset * 8)) & 0x0FFL)); + } + else + { + // emitcode (";", "genCmp #3.2"); + saveAccWarn = 0; + MOVB (aopGet (right, offset++, FALSE, FALSE, "b")); + saveAccWarn = DEFAULT_ACC_WARNING; + emitcode ("xrl", "b,#!constbyte", 0x80); + emitcode ("subb", "a,b"); + } + } + else + { + const char *s; + + // emitcode (";", "genCmp #4"); + saveAccWarn = 0; + s = aopGet (right, offset++, FALSE, FALSE, "b"); + saveAccWarn = DEFAULT_ACC_WARNING; + + emitcode ("subb", "a,%s", s); + } + } + } + } + +release: + /* Don't need the left & right operands any more; do need the result. */ + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + +release_freedLR: + + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + } + else + { + /* if the result is used in the next + ifx conditional branch then generate + code a little differently */ + if (ifx) + { + genIfxJump (ifx, "c", ic->next); + } + else + { + outBitC (result); + } + /* leave the result in acc */ + } + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpGt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the left & right asmops */ + AOP_OP_2 (ic); + + genCmp (right, left, ic, ifx, sign); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpLt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the left & right asmops */ + AOP_OP_2 (ic); + + genCmp (left, right, ic, ifx, sign); +} + +/*-----------------------------------------------------------------*/ +/* gencjneshort - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjneshort (operand * left, operand * right, symbol * lbl) +{ + int size = max (AOP_SIZE (left), AOP_SIZE (right)); + int offset = 0; + unsigned long long lit = 0; + + D (emitcode (";", "gencjneshort")); + + /* if the left side is a literal or + if the right is in a pointer register and left is not */ + if ((AOP_TYPE (left) == AOP_LIT) || + (AOP_TYPE (left) == AOP_IMMD) || (AOP_TYPE (left) == AOP_DIR) || (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + operand *t = right; + right = left; + left = t; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + /* generic pointers require special handling since all NULL pointers must compare equal */ + if (opIsGptr (left) || opIsGptr (right)) + { + /* push left */ + while (offset < size) + { + emitpush (aopGet (left, offset++, FALSE, TRUE, NULL)); + } + loadDptrFromOperand (right, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + + /* if the right side is a literal then anything goes */ + else if (AOP_TYPE (right) == AOP_LIT && + AOP_TYPE (left) != AOP_DIR && AOP_TYPE (left) != AOP_IMMD && + AOP_TYPE (left) != AOP_STR && AOP_TYPE (left) != AOP_DPTRn) + { + while (size--) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("cjne", "%s,%s,!tlabel", l, aopGet (right, offset, FALSE, FALSE, NULL), labelKey2num (lbl->key)); + Safe_free (l); + offset++; + } + } + + /* if the right side is in a register or in direct space or + if the left is a pointer register & right is not */ + else if (AOP_TYPE (right) == AOP_REG || + AOP_TYPE (right) == AOP_DIR || + AOP_TYPE (right) == AOP_LIT || + AOP_TYPE (right) == AOP_IMMD || + (AOP_TYPE (left) == AOP_DIR && AOP_TYPE (right) == AOP_LIT) || (IS_AOP_PREG (left) && !IS_AOP_PREG (right))) + { + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + if ((AOP_TYPE (left) == AOP_DIR && AOP_TYPE (right) == AOP_LIT) && + ((unsigned int) ((lit >> (offset * 8)) & 0x0FFL) == 0)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "a,%s,!tlabel", aopGet (right, offset, FALSE, TRUE, DP2_RESULT_REG), labelKey2num (lbl->key)); + offset++; + } + } + else + { + /* right is a pointer reg need both a & b */ + while (size--) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("cjne", "a,b,!tlabel", labelKey2num (lbl->key)); + offset++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* gencjne - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjne (operand * left, operand * right, symbol * lbl) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "gencjne")); + + gencjneshort (left, right, lbl); + + MOVA (one); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl->key)); + emitLabel (lbl); + MOVA (zero); + emitLabel (tlbl); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + iCode *popIc = ic->next; + + D (emitcode (";", "genCmpEq")); + + AOP_OP_2 (ic); + AOP_SET_LOCALS (ic); + + /* if literal, literal on the right or + if the right is in a pointer register and left + is not */ + if ((AOP_TYPE (left) == AOP_LIT) || (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + swapOperands (&left, &right); + } + + if (ifx && /* !AOP_SIZE(result) */ + OP_SYMBOL (result) && OP_SYMBOL (result)->regType == REG_CND) + { + symbol *tlbl; + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* if true label then we jump if condition + supplied is true */ + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + emitcode ("jnc", "!tlabel", labelKey2num (tlbl->key)); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + } + else + { + emitcode ("jc", "!tlabel", labelKey2num (tlbl->key)); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + } + emitLabel (tlbl); + } + else + { + tlbl = newiTempLabel (NULL); + gencjneshort (left, right, tlbl); + if (IC_TRUE (ifx)) + { + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (tlbl); + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (tlbl); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + emitLabel (lbl); + } + } + /* mark the icode as generated */ + ifx->generated = 1; + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + return; + } + + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, TRUE, FALSE); + + /* c = 1 if egal */ + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "c", popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + outBitC (result); + } + else + { + gencjne (left, right, newiTempLabel (NULL)); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, TRUE, FALSE); + + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + aopPut (result, "a", 0); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "a", popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + if (AOP_TYPE (result) != AOP_CRY) + outAcc (result); + /* leave the result in acc */ + } + +release: + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* hasInc - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +static iCode * +hasInc (operand * op, iCode * ic, int osize) +{ + sym_link *type = operandType (op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize; + + /* this could from a cast, e.g.: "(char xdata *) 0x7654;" */ + if (!IS_SYMOP (op)) + return NULL; + + if (IS_BITVAR (retype) || !IS_PTR (type)) + return NULL; + if (IS_AGGREGATE (type->next)) + return NULL; + if (osize != (isize = getSize (type->next))) + return NULL; + + while (lic) + { + /* if operand of the form op = op + */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genAndOp")); + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + AOP_OP_2 (ic); + AOP_SET_LOCALS (ic); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genOrOp")); + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + AOP_OP_2 (ic); + AOP_SET_LOCALS (ic); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + + aopOp (result, ic, FALSE, FALSE); + + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = { 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic) +{ + if (IC_TRUE (ic)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic) +{ + if (!IC_TRUE (ic)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl) +{ + // ugly but optimized by peephole + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (nlbl->key)); + emitLabel (tlbl); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + emitLabel (nlbl); + } + else + { + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + emitLabel (tlbl); + } + ic->generated = 1; +} + +// Generate code to perform a bit-wise logic operation +// on two operands in far space (assumed to already have been +// aopOp'd by the AOP_OP_3_NOFATAL macro), storing the result +// in far space. This requires pushing the result on the stack +// then popping it into the result. +static void +genFarFarLogicOp (iCode * ic, char *logicOp) +{ + int size, resultSize, compSize; + int offset = 0; + + TR_AP ("#5"); + D (emitcode (";", "%s special case for 3 far operands.", logicOp);); + compSize = AOP_SIZE (IC_LEFT (ic)) < AOP_SIZE (IC_RIGHT (ic)) ? AOP_SIZE (IC_LEFT (ic)) : AOP_SIZE (IC_RIGHT (ic)); + + _startLazyDPSEvaluation (); + for (size = compSize; (size--); offset++) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE, NULL)); + emitcode ("mov", "%s, acc", DP2_RESULT_REG); + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE, NULL)); + + emitcode (logicOp, "a,%s", DP2_RESULT_REG); + emitcode ("push", "acc"); + } + _endLazyDPSEvaluation (); + + freeAsmop (IC_LEFT (ic), NULL, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + + resultSize = AOP_SIZE (IC_RESULT (ic)); + + ADJUST_PUSHED_RESULT (compSize, resultSize); + + _startLazyDPSEvaluation (); + while (compSize--) + { + emitcode ("pop", "acc"); + aopPut (IC_RESULT (ic), "a", compSize); + } + _endLazyDPSEvaluation (); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0L; + int bytelit = 0; + bool pushResult; + + D (emitcode (";", "genAnd")); + + AOP_OP_3_NOFATAL (ic, pushResult); + AOP_SET_LOCALS (ic); + + if (pushResult) + { + genFarFarLogicOp (ic, "anl"); + return; + } + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) +#ifdef LOGIC_OPS_BROKEN + || AOP_NEEDSACC (left) +#endif + ) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit & 1) + { + if (size && sameRegs (AOP (result), AOP (left))) + // no change + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx); + goto release; + } + emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else if (AOP_TYPE (right) == AOP_DIR && IS_BOOL (operandType (right)) && AOP_TYPE (left) == AOP_CRY) + { + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("anl", "c,acc.0"); + } + else + { + // c = bit & val; + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + // c = lsb + emitcode ("rrc", "a"); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", ic->next); + goto release; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + int posbit = isLiteralBit (lit); + /* left & 2^n */ + if (posbit) + { + posbit--; + MOVA (aopGet (left, posbit >> 3, FALSE, FALSE, NULL)); + // bit = left & 2^n + if (size) + { + switch (posbit & 0x07) + { + case 0: + emitcode ("rrc", "a"); + break; + case 7: + emitcode ("rlc", "a"); + break; + default: + emitcode ("mov", "c,acc[%d]", posbit & 0x07); + break; + } + } + // if(left & 2^n) + else + { + if (ifx) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "acc[%d]", posbit & 0x07); + genIfxJump (ifx, dbuf_c_str (&dbuf), ic->next); + dbuf_destroy (&dbuf); + } + else + { + emitcode ("anl", "a,#!constbyte", 1 << (posbit & 0x07)); + } + goto release; + } + } + else + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + if (size) + emitcode ("setb", "c"); + while (sizel--) + { + if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + // byte == 2^n ? + if ((posbit = isLiteralBit (bytelit)) != 0) + emitcode ("jb", "acc[%d],!tlabel", (posbit - 1) & 0x07, labelKey2num (tlbl->key)); + else + { + if (bytelit != 0x0FFL) + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + } + offset++; + } + // bit = left & literal + if (size) + { + emitcode ("clr", "c"); + emitLabel (tlbl); + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl); + else + emitLabel (tlbl); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + else + continue; + } + else if (bytelit == 0) + { + aopPut (result, zero, offset); + } + else if (IS_AOP_PREG (result)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE, NULL)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE, NULL)); + Safe_free (l); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("anl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + if (IS_AOP_PREG (result)) + { + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + emitcode ("anl", "%s,a", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, NULL)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0x0FF) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE, NULL), offset); + continue; + } + else if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + aopPut (result, zero, offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE, NULL)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, anl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, NULL)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0; + int bytelit = 0; + bool pushResult; + + D (emitcode (";", "genOr")); + + AOP_OP_3_NOFATAL (ic, pushResult); + AOP_SET_LOCALS (ic); + + if (pushResult) + { + genFarFarLogicOp (ic, "orl"); + return; + } + + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) +#ifdef LOGIC_OPS_BROKEN + || AOP_NEEDSACC (left) // I think this is a net loss now. +#endif + ) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit | literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == 0 => result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit | bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + // c = bit | val; + symbol *tlbl = newiTempLabel (NULL); + if (!((AOP_TYPE (result) == AOP_CRY) && ifx)) + emitcode ("setb", "c"); + emitcode ("jb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + toBoolean (right); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jmpTrueOrFalse (ifx, tlbl); + goto release; + } + else + { + CLRC; + emitLabel (tlbl); + } + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", ic->next); + goto release; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + if (lit) + { + // result = 1 + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx); + goto release; + } + else + { + // lit = 0, result = boolean(left) + if (size) + SETC; + toBoolean (left); + if (size) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + CLRC; + emitLabel (tlbl); + } + else + { + /* FIXME, thats pretty fishy, check for ifx!=0, testcase .. */ + assert (ifx); + genIfxJump (ifx, "a", ic->next); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + else + continue; + } + else if (bytelit == 0x0FF) + { + aopPut (result, "#0xff", offset); + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE, NULL)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + Safe_free (l); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + if (IS_AOP_PREG (left)) + { + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + emitcode ("orl", "%s,a", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + else + emitLabel (tlbl); + } + else + { + _startLazyDPSEvaluation (); + for (; (size--); offset++) + { + // normal case + // result = left | right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE, NULL), offset); + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + aopPut (result, "#0xff", offset); + continue; + } + } + // faster than result <- left, orl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + aopPut (result, "a", offset); + } + _endLazyDPSEvaluation (); + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0; + int bytelit = 0; + bool pushResult; + + D (emitcode (";", "genXor")); + + AOP_OP_3_NOFATAL (ic, pushResult); + AOP_SET_LOCALS (ic); + + if (pushResult) + { + genFarFarLogicOp (ic, "xrl"); + return; + } + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) +#ifdef LOGIC_OPS_BROKEN + || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right)) +#endif + ) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit ^ literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && sameRegs (AOP (result), AOP (left))) + { + emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + } + } + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + if (IS_OP_ACCUSE (left)) + { + // left already is in the carry + operand *tmp = right; + right = left; + left = tmp; + } + else + { + toCarry (right); + } + } + else + { + // c = bit ^ val + toCarry (right); + } + emitcode ("jnb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + emitcode ("cpl", "c"); + emitLabel (tlbl); + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit ^ ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", ic->next); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + else + continue; + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE, NULL)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + Safe_free (l); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("xrl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + if (IS_AOP_PREG (left)) + { + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + aopPut (result, "a", offset); + } + else + { + emitcode ("xrl", "%s,a", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + } + else if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, TRUE, DP2_RESULT_REG)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + else // need a target here + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left ^ right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE, NULL), offset); + continue; + } + D (emitcode (";", "better literal XOR.")); + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a, %s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + // faster than result <- left, anl result,right + // and better if result is SFR + if (AOP_TYPE (left) == AOP_ACC) + { + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + const char *rOp = aopGet (right, offset, FALSE, FALSE, NULL); + if (!strcmp (rOp, "a") || !strcmp (rOp, "acc")) + { + emitcode ("mov", "b,a"); + rOp = "b"; + } + + rOp = Safe_strdup (rOp); + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("xrl", "a,%s", rOp); + Safe_free ((void *) rOp); + } + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRRC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = size - 1; + _startLazyDPSEvaluation (); + /* no need to clear carry, bit7 will be written later */ + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("rrc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset--); + } + _endLazyDPSEvaluation (); + + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, AOP_SIZE (result) - 1, FALSE, FALSE, NULL)); + } + emitcode ("mov", "acc[7],c"); + aopPut (result, "a", AOP_SIZE (result) - 1); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRLC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = 0; + if (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); /* bit0 will be written later */ + if (AOP_SIZE (result) > 1) + { + aopPut (result, "a", offset++); + } + + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset++); + } + _endLazyDPSEvaluation (); + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, 0, FALSE, FALSE, NULL)); + } + emitcode ("mov", "acc[0],c"); + aopPut (result, "a", 0); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetHbit - generates code get highest order bit */ +/*-----------------------------------------------------------------*/ +static void +genGetHbit (iCode * ic) +{ + operand *left, *result; + + D (emitcode (";", "genGetHbit")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* get the highest order byte into a */ + MOVA (aopGet (left, AOP_SIZE (left) - 1, FALSE, FALSE, NULL)); + if (AOP_TYPE (result) == AOP_CRY) + { + emitcode ("rlc", "a"); + outBitC (result); + } + else + { + emitcode ("rl", "a"); + emitcode ("anl", "a,#0x01"); + outAcc (result); + } + + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSwap - generates code to swap nibbles or bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwap (iCode * ic) +{ + operand *left, *result; + + D (emitcode (";", "genSwap")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + _startLazyDPSEvaluation (); + switch (AOP_SIZE (left)) + { + case 1: /* swap nibbles in byte */ + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("swap", "a"); + aopPut (result, "a", 0); + break; + case 2: /* swap bytes in word */ + if (AOP_TYPE (left) == AOP_REG && sameRegs (AOP (left), AOP (result))) + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + aopPut (result, aopGet (left, 1, FALSE, FALSE, NULL), 0); + aopPut (result, "a", 1); + } + else if (operandsEqu (left, result)) + { + char *reg = "a"; + bool pushedB = FALSE, leftInB = FALSE; + + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + if (aopGetUsesAcc (left, 1) || aopGetUsesAcc (result, 0)) + { + pushedB = pushB (); + emitcode ("mov", "b,a"); + reg = "b"; + leftInB = TRUE; + } + aopPut (result, aopGet (left, 1, FALSE, FALSE, NULL), 0); + aopPut (result, reg, 1); + + if (leftInB) + popB (pushedB); + } + else + { + aopPut (result, aopGet (left, 1, FALSE, FALSE, NULL), 0); + aopPut (result, aopGet (left, 0, FALSE, FALSE, NULL), 1); + } + break; + default: + wassertl (FALSE, "unsupported SWAP operand size"); + } + _endLazyDPSEvaluation (); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("rl", "a"); + break; + case 2: + emitcode ("rl", "a"); + emitcode ("rl", "a"); + break; + case 3: + emitcode ("swap", "a"); + emitcode ("rr", "a"); + break; + case 4: + emitcode ("swap", "a"); + break; + case 5: + emitcode ("swap", "a"); + emitcode ("rl", "a"); + break; + case 6: + emitcode ("rr", "a"); + emitcode ("rr", "a"); + break; + case 7: + emitcode ("rr", "a"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + emitcode ("add", "a,acc"); + else if (shCount == 2) + { + emitcode ("add", "a,acc"); + emitcode ("add", "a,acc"); + } + else + { + /* rotate left accumulator */ + AccRol (shCount); + /* and kill the lower order bits */ + emitcode ("anl", "a,#!constbyte", SLMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + { + CLRC; + emitcode ("rrc", "a"); + } + else + { + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccSRsh - signed right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccSRsh (int shCount) +{ + symbol *tlbl; + if (shCount != 0) + { + if (shCount == 1) + { + emitcode ("mov", "c,acc[7]"); + emitcode ("rrc", "a"); + } + else if (shCount == 2) + { + emitcode ("mov", "c,acc[7]"); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc[7]"); + emitcode ("rrc", "a"); + } + else + { + tlbl = newiTempLabel (NULL); + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + emitcode ("jnb", "acc[%d],!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#!constbyte", (unsigned char) ~SRMask[shCount]); + emitLabel (tlbl); + } + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + /* shift right accumulator */ + if (sign) + AccSRsh (shCount); + else + AccRsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + /* shift left accumulator */ + AccLsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) +{ + const char *l; + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + l = aopGet (left, offl, FALSE, FALSE, NULL); + + if (*l == '@' && (IS_AOP_PREG (result))) + { + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offr); + } + else + { + if (!sign) + { + aopPut (result, l, offr); + } + else + { + /* MSB sign in acc.7 ! */ + if (getDataSize (left) == offl + 1) + { + MOVA (l); + aopPut (result, "a", offr); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRrl1 - right rotate c->a:x->c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXRrl1 (const char *x) +{ + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLrl1 - left rotate c<-a:x<-c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLrl1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh1 - left shift a:x<-0 by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("add", "a,acc"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh - left shift a:x by known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh (const char *x, int shCount) +{ + unsigned char mask; + + switch (shCount) + { + case 0: + break; + case 1: + AccAXLsh1 (x); + break; + case 2: + AccAXLsh1 (x); + AccAXLsh1 (x); + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD + mask = SLMask[shCount]; + AccRol (shCount); // BBBAAAAA:CCCCCDDD + emitcode ("anl", "a,#!constbyte", mask); // BBB00000:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBB00000 + AccRol (shCount); // DDDCCCCC:BBB00000 + emitcode ("xch", "a,%s", x); // BBB00000:DDDCCCCC + emitcode ("xrl", "a,%s", x); // (BBB^DDD)CCCCC:DDDCCCCC + emitcode ("xch", "a,%s", x); // DDDCCCCC:(BBB^DDD)CCCCC + emitcode ("anl", "a,#!constbyte", mask); // DDD00000:(BBB^DDD)CCCCC + emitcode ("xch", "a,%s", x); // (BBB^DDD)CCCCC:DDD00000 + emitcode ("xrl", "a,%s", x); // BBBCCCCC:DDD00000 + break; + case 6: // AAAAAABB:CCCCCCDD + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 000000BB:CCCCCCDD + emitcode ("mov", "c,acc[0]"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCDD:000000BB + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc[0]"); //<< get correct bit + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + break; + case 7: // a:x <<= 7 + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 0000000B:CCCCCCCD + emitcode ("mov", "c,acc[0]"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCCD:0000000B + AccAXRrl1 (x); // BCCCCCCC:D0000000 + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRsh - right shift a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRsh (const char *x, int shCount) +{ + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 2: + CLRC; + AccAXRrl1 (x); // 0->a:x + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + AccRol (8 - shCount); // BBBAAAAA:DDDCCCCC + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000AAAAA:BBBCCCCC + break; + case 6: // AABBBBBB:CCDDDDDD + emitcode ("mov", "c,acc[7]"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc[7]"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + break; + case 7: // ABBBBBBB:CDDDDDDD + emitcode ("mov", "c,acc[7]"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRshS - right shift signed a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRshS (const char *x, int shCount) +{ + symbol *tlbl; + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("mov", "c,acc[7]"); + AccAXRrl1 (x); // s->a:x + break; + case 2: + emitcode ("mov", "c,acc[7]"); + AccAXRrl1 (x); // s->a:x + emitcode ("mov", "c,acc[7]"); + AccAXRrl1 (x); // s->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + tlbl = newiTempLabel (NULL); + AccRol (8 - shCount); // BBBAAAAA:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000SAAAA:BBBCCCCC + emitcode ("jnb", "acc[%d],!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111AAAAA:BBBCCCCC + emitLabel (tlbl); + break; // SSSSAAAA:BBBCCCCC + case 6: // AABBBBBB:CCDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc[7]"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc[7]"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + emitcode ("jnb", "acc[%d],!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111111AA:BBBBBBCC + emitLabel (tlbl); + break; + case 7: // ABBBBBBB:CDDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc[7]"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + emitcode ("jnb", "acc[%d],!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 1111111A:BBBBBBBC + emitLabel (tlbl); + break; + default: + break; + } +} + +static void +_loadLeftIntoAx (const char **lsb, operand * left, operand * result, int offl, int offr) +{ + // Get the initial value from left into a pair of registers. + // MSB must be in A, LSB can be any register. + // + // If the result is held in registers, it is an optimization + // if the LSB can be held in the register which will hold the, + // result LSB since this saves us from having to copy it into + // the result following AccAXLsh. + // + // If the result is addressed indirectly, this is not a gain. + if (AOP_NEEDSACC (result)) + { + _startLazyDPSEvaluation (); + if (AOP_TYPE (left) == AOP_DPTR2) + { + // Get MSB in A. + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE, NULL)); + // get LSB in DP2_RESULT_REG. + assert (!strcmp (aopGet (left, offl, FALSE, FALSE, DP2_RESULT_REG), DP2_RESULT_REG)); + } + else + { + const char *leftByte; + + // get LSB into DP2_RESULT_REG + leftByte = aopGet (left, offl, FALSE, FALSE, NULL); + if (strcmp (leftByte, DP2_RESULT_REG)) + { + TR_AP ("#7"); + emitcode ("mov", "%s,%s", DP2_RESULT_REG, leftByte); + } + // And MSB in A. + leftByte = aopGet (left, offl + MSB16, FALSE, FALSE, NULL); + assert (strcmp (leftByte, DP2_RESULT_REG)); + MOVA (leftByte); + } + _endLazyDPSEvaluation (); + *lsb = DP2_RESULT_REG; + } + else + { + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + emitcode ("xch", "a,%s", aopGet (left, offl + MSB16, FALSE, FALSE, DP2_RESULT_REG)); + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE, NULL)); + } + *lsb = aopGet (result, offr, FALSE, FALSE, DP2_RESULT_REG); + assert (strcmp (*lsb, "a")); + } +} + +static void +_storeAxResults (const char *lsb, operand * result, int offr) +{ + _startLazyDPSEvaluation (); + if (AOP_NEEDSACC (result)) + { + /* We have to explicitly update the result LSB. + */ + emitcode ("xch", "a,%s", lsb); + aopPut (result, "a", offr); + emitcode ("mov", "a,%s", lsb); + } + if (getDataSize (result) > 1) + { + aopPut (result, "a", offr + MSB16); + } + _endLazyDPSEvaluation (); +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + const char *lsb; + + _loadLeftIntoAx (&lsb, left, result, offl, offr); + + AccAXLsh (lsb, shCount); + + _storeAxResults (lsb, result, offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + const char *lsb; + + _loadLeftIntoAx (&lsb, left, result, offl, offr); + + /* a:x >> shCount (x = lsb(result)) */ + if (sign) + AccAXRshS (lsb, shCount); + else + AccAXRsh (lsb, shCount); + + + _storeAxResults (lsb, result, offr); +} + +/*------------------------------------------------------------------*/ +/* shiftLLeftOrResult - shift left one byte from left, or to result */ +/*------------------------------------------------------------------*/ +static void +shiftLLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + /* shift left accumulator */ + AccLsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE, DP2_RESULT_REG)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result */ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + /* shift right accumulator */ + AccRsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE, DP2_RESULT_REG)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* genlshTwo - left shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshTwo (operand * result, operand * left, int shCount) +{ + int size; + + D (emitcode (";", "genlshTwo")); + + size = getDataSize (result); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + + if (size > 1) + { + if (shCount) + { + _startLazyDPSEvaluation (); + _endLazyDPSEvaluation (); + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + aopPut (result, zero, LSB); + } + else + { + _startLazyDPSEvaluation (); + movLeft2Result (left, LSB, result, MSB16, 0); + aopPut (result, zero, LSB); + _endLazyDPSEvaluation (); + } + } + else + { + _startLazyDPSEvaluation (); + aopPut (result, zero, LSB); + _endLazyDPSEvaluation (); + } + } + + /* 1 <= shCount <= 7 */ + else + { + if (size == 1) + shiftL1Left2Result (left, LSB, result, LSB, shCount); + else + shiftL2Left2Result (left, LSB, result, LSB, shCount); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ + int offl = LSB; + int size = AOP_SIZE (result); + int useXch = (sameRegs (AOP (left), AOP (result)) && size >= MSB16 + offr && offr != LSB); + + if (size > offl + offr) + { + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + emitcode ("add", "a,acc"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE, NULL); + else + aopPut (result, "a", offl + offr); + } + + for (offl = LSB + 1; offl < LSB + 8; offl++) + { + if (size > offl + offr) + { + if (!useXch) + MOVA (aopGet (left, offl, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE, NULL); + else + aopPut (result, "a", offl + offr); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genlshFixed - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFixed (operand *result, operand *left, int shCount) +{ + int size, b; + int full_bytes; + + D (emitcode (";", "genlshFixed")); + + size = AOP_SIZE (result); + + full_bytes = shCount / 8; + shCount -= full_bytes * 8; + if (shCount == 0) + { + for (b = size - 1; b > full_bytes - 1; b--) + movLeft2Result (left, b - full_bytes, result, b, 0); + } + else if ((shCount == 1) && (full_bytes < 2)) + { + shiftLLong (left, result, full_bytes); + } + else if ((shCount == 2) && (full_bytes == 0)) + { + shiftLLong (left, result, full_bytes); + shiftLLong (result, result, full_bytes); + } + else + { + int off; + for (off = size - 2; off - full_bytes >= 0; off -= 2) + { + shiftL2Left2Result (left, off - full_bytes, result, off, shCount); + if (off - full_bytes - 1 >= 0) + shiftRLeftOrResult (left, off - full_bytes - 1, result, off, 8 - shCount); + } + if (off - full_bytes == -1) + { + shiftL1Left2Result (left, LSB, result, full_bytes, shCount); + } + } + for (b = LSB; b < full_bytes; b++) + aopPut (result, zero, b); + return; +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + size = getSize (operandType (result)); + + D (emitcode (";", "genLeftShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + +#if VIEW_SIZE + emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left)); +#endif + + /* I suppose that the left size >= result size */ + if (shCount == 0) + { + _startLazyDPSEvaluation (); + while (size--) + { + movLeft2Result (left, size, result, size, 0); + } + _endLazyDPSEvaluation (); + } + else if (shCount >= (size * 8)) + { + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, zero, size); + } + _endLazyDPSEvaluation (); + } + else + { + switch (size) + { + case 2: + genlshTwo (result, left, shCount); + break; + + case 1: + case 4: + case 8: + genlshFixed (result, left, shCount); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "*** ack! mystery literal shift!\n"); + break; + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genLeftShift")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genLeftShiftLiteral (left, right, result, ic); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits makes no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genLeftShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE, "b")); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* now move the left to the result if they are not the same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE, NULL); + if (*l == '@' && (IS_AOP_PREG (result))) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + _endLazyDPSEvaluation (); + } + + tlbl = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = newiTempLabel (NULL); + + /* if it is only one byte then */ + if (size == 1) + { + symbol *tlbl1 = newiTempLabel (NULL); + + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("add", "a,acc"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + MOVA (aopGet (result, offset, FALSE, FALSE, NULL)); + emitcode ("add", "a,acc"); + aopPut (result, "a", offset++); + _startLazyDPSEvaluation (); + while (--size) + { + MOVA (aopGet (result, offset, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset++); + } + _endLazyDPSEvaluation (); + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshOne")); + + shiftR1Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshTwo")); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + _startLazyDPSEvaluation (); + if (shCount) + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB16, result, LSB, sign); + addSign (result, MSB16, sign); + _endLazyDPSEvaluation (); + } + + /* 1 <= shCount <= 7 */ + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, sign); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + bool overlapping = regsInCommon (left, result) || operandsEqu (left, result); + + if (overlapping && offl > 1) + { + // we are in big trouble, but this shouldn't happen + werror (E_INTERNAL_ERROR, __FILE__, __LINE__); + } + + MOVA (aopGet (left, MSB32, FALSE, FALSE, NULL)); + + if (offl == MSB16) + { + // shift is > 8 + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + if (overlapping && sameByte (AOP (left), MSB32, AOP (result), MSB32)) + { + xch_a_aopGet (left, MSB32, FALSE, FALSE, DP2_RESULT_REG); + } + else + { + aopPut (result, "a", MSB32); + MOVA (aopGet (left, MSB32, FALSE, FALSE, DP2_RESULT_REG)); + } + } + else + { + if (aopPutUsesAcc (result, zero, MSB32)) + { + emitcode ("xch", "a,b"); + aopPut (result, zero, MSB32); + emitcode ("xch", "a,b"); + } + else + { + aopPut (result, zero, MSB32); + } + } + } + + if (!sign) + { + emitcode ("clr", "c"); + } + else + { + emitcode ("mov", "c,acc[7]"); + } + + emitcode ("rrc", "a"); + + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB24, AOP (result), MSB32 - offl)) + { + xch_a_aopGet (left, MSB24, FALSE, FALSE, DP2_RESULT_REG); + } + else + { + aopPut (result, "a", MSB32 - offl); + MOVA (aopGet (left, MSB24, FALSE, FALSE, NULL)); + } + + emitcode ("rrc", "a"); + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB16, AOP (result), MSB24 - offl)) + { + xch_a_aopGet (left, MSB16, FALSE, FALSE, DP2_RESULT_REG); + } + else + { + aopPut (result, "a", MSB24 - offl); + MOVA (aopGet (left, MSB16, FALSE, FALSE, NULL)); + } + + emitcode ("rrc", "a"); + if (offl != LSB) + { + aopPut (result, "a", MSB16 - offl); + } + else + { + if (overlapping && sameByte (AOP (left), LSB, AOP (result), MSB16 - offl)) + { + xch_a_aopGet (left, LSB, FALSE, FALSE, DP2_RESULT_REG); + } + else + { + aopPut (result, "a", MSB16 - offl); + MOVA (aopGet (left, LSB, FALSE, FALSE, NULL)); + } + emitcode ("rrc", "a"); + aopPut (result, "a", LSB); + } +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshFour")); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + _startLazyDPSEvaluation (); + if (shCount) + shiftR1Left2Result (left, MSB32, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB32, result, LSB, sign); + addSign (result, MSB16, sign); + _endLazyDPSEvaluation (); + } + else if (shCount >= 16) + { + shCount -= 16; + _startLazyDPSEvaluation (); + if (shCount) + shiftR2Left2Result (left, MSB24, result, LSB, shCount, sign); + else + { + movLeft2Result (left, MSB24, result, LSB, 0); + movLeft2Result (left, MSB32, result, MSB16, sign); + } + addSign (result, MSB24, sign); + _endLazyDPSEvaluation (); + } + else if (shCount >= 8) + { + shCount -= 8; + _startLazyDPSEvaluation (); + if (shCount == 1) + { + shiftRLong (left, MSB16, result, sign); + } + else if (shCount == 0) + { + movLeft2Result (left, MSB16, result, LSB, 0); + movLeft2Result (left, MSB24, result, MSB16, 0); + movLeft2Result (left, MSB32, result, MSB24, sign); + addSign (result, MSB32, sign); + } + else + { + shiftR2Left2Result (left, MSB16, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB32, result, MSB16, 8 - shCount); + /* the last shift is signed */ + shiftR1Left2Result (left, MSB32, result, MSB24, shCount, sign); + addSign (result, MSB32, sign); + } + _endLazyDPSEvaluation (); + } + else + { + /* 1 <= shCount <= 7 */ + if (shCount <= 2) + { + shiftRLong (left, LSB, result, sign); + if (shCount == 2) + shiftRLong (result, LSB, result, sign); + } + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB24, result, MSB16, 8 - shCount); + shiftR2Left2Result (left, MSB24, result, MSB24, shCount, sign); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genrshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshAny (operand *result, operand *left, int shCount, int sign) +{ + int size, size2, offset; + + D (emitcode (";", "genrshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE, NULL), offset); + + while (shCount--) + { + MOVA (aopGet (result, size - 1, FALSE, FALSE, NULL)); + if (!sign) + emitcode ("clr", "c"); + else + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + aopPut (result, "a", size - 1); + + for(size2 = size - 1, offset = size - 2; size2 > 0; size2--, offset--) + { + + MOVA (aopGet (result, offset, FALSE, FALSE, NULL)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + size = getSize (operandType (result)); + + D (emitcode (";", "genRightShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + +#if VIEW_SIZE + emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left)); +#endif + + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + if (shCount == 0) + { + size = getDataSize (result); + _startLazyDPSEvaluation (); + while (size--) + movLeft2Result (left, size, result, size, 0); + _endLazyDPSEvaluation (); + } + else if (shCount >= (size * 8)) + { + if (sign) + { + /* get sign in acc.7 */ + MOVA (aopGet (left, size - 1, FALSE, FALSE, NULL)); + } + addSign (result, LSB, sign); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + + default: + genrshAny (result, left, shCount, sign); + break; + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSignedRightShift - right shift of signed number */ +/*-----------------------------------------------------------------*/ +static void +genSignedRightShift (iCode * ic) +{ + operand *right, *left, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genSignedRightShift")); + + /* we do it the hard way put the shift count in b + and loop thru preserving the sign */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 1); + return; + } + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE, "b")); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + + size = AOP_SIZE (result); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE, NULL); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + _endLazyDPSEvaluation (); + } + + /* mov the highest order bit to OVR */ + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + + size = AOP_SIZE (result); + offset = size - 1; + MOVA (aopGet (left, offset, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); + emitcode ("mov", "ov,c"); + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE, NULL)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + _endLazyDPSEvaluation (); + reAdjustPreg (AOP (result)); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *letype; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genRightShift")); + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + letype = getSpec (operandType (IC_LEFT (ic))); + + if (!SPEC_USIGN (letype)) + { + genSignedRightShift (ic); + return; + } + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 0); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE, "b")); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, AOP_USESDPTR (left)); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE, NULL); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + _endLazyDPSEvaluation (); + } + + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = size - 1; + + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE, NULL)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE, NULL)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + _endLazyDPSEvaluation (); + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteGet - emits code to get a byte into A through a */ +/* pointer register (R0, R1, or DPTR). The */ +/* original value of A can be preserved in B. */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteGet (const char *rname, int p_type, bool preserveAinB) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("mov", "a,@%s", rname); + break; + + case PPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@%s", rname); + break; + + case FPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@dptr"); + break; + + case CPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + break; + + case GPOINTER: + if (preserveAinB) + { + emitpush ("b"); + emitpush ("acc"); + } + emitcode ("lcall", "__gptrget"); + if (preserveAinB) + emitpop ("b"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteSet - emits code to set a byte from src through a */ +/* pointer register (R0, R1, or DPTR). */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteSet (const char *rname, int p_type, const char *src) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (*src == '@') + { + MOVA (src); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, src); + break; + + case PPOINTER: + MOVA (src); + emitcode ("movx", "@%s,a", rname); + break; + + case FPOINTER: + MOVA (src); + emitcode ("movx", "@dptr,a"); + break; + + case GPOINTER: + MOVA (src); + emitcode ("lcall", "__gptrput"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, const char *rname, int ptype) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + + D (emitcode (";", "genUnpackBits")); + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + emitPtrByteGet (rname, ptype, FALSE); + AccRol (8 - bstr); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - blen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc[%d],!tlabel", blen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << blen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + aopPut (result, "a", offset++); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* Handle the partial byte at the end */ + if (rlen) + { + emitPtrByteGet (rname, ptype, FALSE); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - rlen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc[%d],!tlabel", rlen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << rlen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + } + +finish: + if (offset < rsize) + { + char *source; + + if (SPEC_USIGN (etype)) + source = zero; + else + { + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + + source = "a"; + } + rsize -= offset; + while (rsize--) + aopPut (result, source, offset++); + } +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + const char *l; + int size, offset = 0; + + D (emitcode (";", "genDataPointerGet")); + + aopOp (result, ic, TRUE, FALSE); + + /* get the string representation of the name */ + l = aopGet (left, 0, FALSE, TRUE, NULL) + 1; // remove # + size = AOP_SIZE (result); + _startLazyDPSEvaluation (); + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + } + else + { + dbuf_append_str (&dbuf, l); + } + aopPut (result, dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + _endLazyDPSEvaluation (); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *rtype, *retype, *letype; + sym_link *ltype = operandType (left); + + D (emitcode (";", "genNearPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + letype = getSpec (ltype); + + aopOp (left, ic, FALSE, FALSE); + + /* if left is rematerialisable and + result is not bitfield variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_IMMD && !IS_BITFIELD (retype) && !IS_BITFIELD (letype) && DCL_TYPE (ltype) == POINTER) + { + genDataPointerGet (left, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, &aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (left, 0, FALSE, TRUE, DP2_RESULT_REG)); + rname = preg->name; + } + else + rname = aopGet (left, 0, FALSE, FALSE, DP2_RESULT_REG); + + freeAsmop (left, NULL, ic, TRUE); + aopOp (result, ic, FALSE, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genUnpackBits (result, rname, POINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + if (IS_AOP_PREG (result) || AOP_TYPE (result) == AOP_STK) + { + + emitcode ("mov", "a,@%s", rname); + aopPut (result, "a", offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "@%s", rname); + aopPut (result, dbuf_c_str (&dbuf), offset); + dbuf_destroy (&dbuf); + } + offset++; + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + { + /* post increment present */ + aopPut (left, rname, 0); + } + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + /* done */ + freeAsmop (result, NULL, ic, TRUE); + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerGet - emitcode for paged pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerGet (operand * left, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *rtype, *retype, *letype; + + D (emitcode (";", "genPagedPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + letype = getSpec (operandType (left)); + aopOp (left, ic, FALSE, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, &aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (left, 0, FALSE, TRUE, NULL)); + rname = preg->name; + } + else + rname = aopGet (left, 0, FALSE, FALSE, NULL); + + freeAsmop (left, NULL, ic, TRUE); + aopOp (result, ic, FALSE, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genUnpackBits (result, rname, PPOINTER); + else + { + /* we have can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + + emitcode ("movx", "a,@%s", rname); + aopPut (result, "a", offset); + + offset++; + + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (left, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + /* done */ + freeAsmop (result, NULL, ic, TRUE); + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerGet - get value from far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerGet (operand * left, operand * result, iCode * ic, iCode * pi) +{ + int size, offset, dopi; + sym_link *retype = getSpec (operandType (result)); + sym_link *letype = getSpec (operandType (left)); + + D (emitcode (";", "genFarPointerGet")); + + aopOp (left, ic, FALSE, FALSE); + dopi = loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE, (AOP_INDPTRn (left) ? FALSE : TRUE)); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + genUnpackBits (result, "dptr", FPOINTER); + if (AOP_INDPTRn (left)) + { + genSetDPTR (0); + } + } + else + { + size = AOP_SIZE (result); + offset = 0; + + if (AOP_INDPTRn (left) && AOP_USESDPTR (result)) + { + while (size--) + { + genSetDPTR (AOP (left)->aopu.dptr); + emitcode ("movx", "a,@dptr"); + if (size || (dopi && pi && AOP_TYPE (left) != AOP_IMMD)) + emitcode ("inc", "dptr"); + genSetDPTR (0); + aopPut (result, "a", offset++); + } + } + else + { + _startLazyDPSEvaluation (); + while (size--) + { + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + else + { + genSetDPTR (0); + } + _flushLazyDPS (); + + emitcode ("movx", "a,@dptr"); + if (size || (dopi && pi && AOP_TYPE (left) != AOP_IMMD)) + emitcode ("inc", "dptr"); + + aopPut (result, "a", offset++); + } + _endLazyDPSEvaluation (); + } + } + if (dopi && pi && AOP_TYPE (left) != AOP_IMMD) + { + if (!AOP_INDPTRn (left)) + { + _startLazyDPSEvaluation (); + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + if (options.model == MODEL_FLAT24) + aopPut (left, "dpx", 2); + _endLazyDPSEvaluation (); + } + pi->generated = 1; + } + else if ((IS_OP_RUONLY (left) || AOP_INDPTRn (left)) && + AOP_SIZE (result) > 1 && IS_SYMOP (left) && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) + { + size = AOP_SIZE (result) - 1; + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + while (size--) + emitcode ("lcall", "__decdptr"); + if (AOP_INDPTRn (left)) + { + genSetDPTR (0); + } + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCodePointerGet - get value from code space */ +/*-----------------------------------------------------------------*/ +static void +genCodePointerGet (operand * left, operand * result, iCode * ic, iCode * pi) +{ + int size, offset, dopi; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genCodePointerGet")); + + aopOp (left, ic, FALSE, FALSE); + dopi = loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE, (AOP_INDPTRn (left) ? FALSE : TRUE)); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + { + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + genUnpackBits (result, "dptr", CPOINTER); + if (AOP_INDPTRn (left)) + { + genSetDPTR (0); + } + } + else + { + size = AOP_SIZE (result); + offset = 0; + if (AOP_INDPTRn (left) && AOP_USESDPTR (result)) + { + while (size--) + { + genSetDPTR (AOP (left)->aopu.dptr); + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + if (size || (dopi && pi && AOP_TYPE (left) != AOP_IMMD)) + emitcode ("inc", "dptr"); + genSetDPTR (0); + aopPut (result, "a", offset++); + } + } + else + { + _startLazyDPSEvaluation (); + while (size--) + { + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + else + { + genSetDPTR (0); + } + _flushLazyDPS (); + + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + if (size || (dopi && pi && AOP_TYPE (left) != AOP_IMMD)) + emitcode ("inc", "dptr"); + aopPut (result, "a", offset++); + } + _endLazyDPSEvaluation (); + } + } + if (dopi && pi && AOP_TYPE (left) != AOP_IMMD) + { + if (!AOP_INDPTRn (left)) + { + _startLazyDPSEvaluation (); + + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + if (options.model == MODEL_FLAT24) + aopPut (left, "dpx", 2); + + _endLazyDPSEvaluation (); + } + pi->generated = 1; + } + else if (IS_SYMOP (left) && + (IS_OP_RUONLY (left) || AOP_INDPTRn (left)) && + AOP_SIZE (result) > 1 && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) + { + size = AOP_SIZE (result) - 1; + if (AOP_INDPTRn (left)) + { + genSetDPTR (AOP (left)->aopu.dptr); + } + while (size--) + emitcode ("lcall", "__decdptr"); + if (AOP_INDPTRn (left)) + { + genSetDPTR (0); + } + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - get value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic, iCode * pi) +{ + int size, offset, dopi; + bool pushedB; + sym_link *retype = getSpec (operandType (result)); + sym_link *letype = getSpec (operandType (left)); + + D (emitcode (";", "genGenPointerGet")); + + aopOp (left, ic, FALSE, FALSE); + + pushedB = pushB (); + dopi = loadDptrFromOperand (left, TRUE); + + /* so dptr-b now contains the address */ + aopOp (result, ic, FALSE, (AOP_INDPTRn (left) ? FALSE : TRUE)); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + genUnpackBits (result, "dptr", GPOINTER); + } + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + if (size) + { + // Get two bytes at a time, results in _AP & A. + // dptr will be incremented ONCE by __gptrgetWord. + // + // Note: any change here must be coordinated + // with the implementation of __gptrgetWord + // in device/lib/_gptrget.c + emitcode ("lcall", "__gptrgetWord"); + aopPut (result, "a", offset++); + aopPut (result, DP2_RESULT_REG, offset++); + size--; + } + else + { + // Only one byte to get. + emitcode ("lcall", "__gptrget"); + aopPut (result, "a", offset++); + } + + if (size || (dopi && pi && AOP_TYPE (left) != AOP_IMMD)) + { + emitcode ("inc", "dptr"); + } + } + } + + if (dopi && pi && AOP_TYPE (left) != AOP_IMMD) + { + _startLazyDPSEvaluation (); + + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + if (options.model == MODEL_FLAT24) + { + aopPut (left, "dpx", 2); + aopPut (left, "b", 3); + } + else + aopPut (left, "b", 2); + + _endLazyDPSEvaluation (); + + pi->generated = 1; + } + else if (IS_OP_RUONLY (left) && AOP_SIZE (result) > 1 && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) + { + size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("lcall", "__decdptr"); + } + popB (pushedB); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode * pi) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerGet")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + if (p_type == GPOINTER && IS_SYMOP (left) && OP_SYMBOL (left)->remat && IS_CAST_ICODE (OP_SYMBOL (left)->rematiCode)) + { + left = IC_RIGHT (OP_SYMBOL (left)->rematiCode); + type = operandType (left); + p_type = DCL_TYPE (type); + } + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerGet (left, result, ic, pi); + break; + + case PPOINTER: + genPagedPointerGet (left, result, ic, pi); + break; + + case FPOINTER: + genFarPointerGet (left, result, ic, pi); + break; + + case CPOINTER: + genCodePointerGet (left, result, ic, pi); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic, pi); + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * right, const char *rname, int p_type) +{ + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + + D (emitcode (";", "genPackBits")); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + if ((blen == 1) && (p_type != GPOINTER)) + { + /* Case with a bitfield length == 1 and no generic pointer + */ + if (AOP_TYPE (right) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + emitcode ("rrc", "a"); + } + emitPtrByteGet (rname, p_type, FALSE); + emitcode ("mov", "acc[%d],c", bstr); + } + else + { + bool pushedB; + /* Case with a bitfield length < 8 and arbitrary source + */ + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + } + + emitPtrByteSet (rname, p_type, "a"); + return; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteSet (rname, p_type, aopGet (right, offset++, FALSE, TRUE, NULL)); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + bool pushedB; + /* Case with partial byte and arbitrary source + */ + MOVA (aopGet (right, offset++, FALSE, FALSE, NULL)); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + emitPtrByteSet (rname, p_type, "a"); + } +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset; + char *l; + + D (emitcode (";", "genDataPointerSet")); + + aopOp (right, ic, FALSE, FALSE); + + size = max (AOP_SIZE (right), AOP_SIZE (result)); + //remove # + l = Safe_strdup (aopGet (result, 0, FALSE, TRUE, NULL) + 1); + for (offset = 0; offset < size; offset++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + else + dbuf_append_str (&dbuf, l); + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), aopGet (right, offset, FALSE, FALSE, NULL)); + dbuf_destroy (&dbuf); + } + Safe_free (l); + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + sym_link *ptype = operandType (result); + + D (emitcode (";", "genNearPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (ptype); + + aopOp (result, ic, FALSE, FALSE); + + /* if the result is rematerializable & + in data space & not a bit variable */ + if (AOP_TYPE (result) == AOP_IMMD && DCL_TYPE (ptype) == POINTER && !IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (right, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + /* otherwise get a free pointer register */ + + aop = newAsmop (0); + preg = getFreePtr (ic, &aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE, NULL)); + rname = preg->name; + } + else + { + rname = aopGet (result, 0, FALSE, FALSE, NULL); + } + + aopOp (right, ic, FALSE, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, POINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + const char *l = aopGet (right, offset, FALSE, TRUE, NULL); + if ((*l == '@') || (EQ (l, "acc"))) + { + MOVA (l); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, l); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerSet - emitcode for Paged pointer put */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + + D (emitcode (";", "genPagedPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (operandType (result)); + + aopOp (result, ic, FALSE, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + /* otherwise get a free pointer register */ + + aop = newAsmop (0); + preg = getFreePtr (ic, &aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE, NULL)); + rname = preg->name; + } + else + { + rname = aopGet (result, 0, FALSE, FALSE, NULL); + } + + aopOp (right, ic, FALSE, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, PPOINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, TRUE, NULL)); + emitcode ("movx", "@%s,a", rname); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerSet - set value from far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset, dopi; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerSet")); + + aopOp (result, ic, FALSE, FALSE); + dopi = loadDptrFromOperand (result, FALSE); + + /* so dptr now contains the address */ + aopOp (right, ic, FALSE, (AOP_INDPTRn (result) ? FALSE : TRUE)); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + if (AOP_INDPTRn (result)) + { + genSetDPTR (AOP (result)->aopu.dptr); + } + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", FPOINTER); + if (AOP_INDPTRn (result)) + { + genSetDPTR (0); + } + } + else + { + size = AOP_SIZE (right); + offset = 0; + if (AOP_INDPTRn (result) && AOP_USESDPTR (right)) + { + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE, NULL)); + + genSetDPTR (AOP (result)->aopu.dptr); + emitcode ("movx", "@dptr,a"); + if (size || (dopi && pi && AOP_TYPE (result) != AOP_IMMD)) + emitcode ("inc", "dptr"); + genSetDPTR (0); + } + } + else if (AOP_USESDPTR (result) && AOP_USESDPTR (right)) + { + int i; + _startLazyDPSEvaluation (); + for (i = size - 1; i > 0; i--) + emitcode ("push", aopGet (right, i, FALSE, FALSE, NULL)); + while (size--) + { + if (offset++) + emitcode ("pop", "acc"); + else + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + genSetDPTR (0); + _flushLazyDPS (); + emitcode ("movx", "@dptr,a"); + if (size || (dopi && pi && AOP_TYPE (result) != AOP_IMMD)) + emitcode ("inc", "dptr"); + } + _endLazyDPSEvaluation (); + } + else + { + _startLazyDPSEvaluation (); + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE, NULL)); + + if (AOP_INDPTRn (result)) + { + genSetDPTR (AOP (result)->aopu.dptr); + } + else + { + genSetDPTR (0); + } + _flushLazyDPS (); + + emitcode ("movx", "@dptr,a"); + if (size || (dopi && pi && AOP_TYPE (result) != AOP_IMMD)) + emitcode ("inc", "dptr"); + } + _endLazyDPSEvaluation (); + } + } + + if (dopi && pi && AOP_TYPE (result) != AOP_IMMD) + { + if (!AOP_INDPTRn (result)) + { + _startLazyDPSEvaluation (); + + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + if (options.model == MODEL_FLAT24) + aopPut (result, "dpx", 2); + + _endLazyDPSEvaluation (); + } + pi->generated = 1; + } + else if (IS_SYMOP (result) && + (IS_OP_RUONLY (result) || AOP_INDPTRn (result)) && + (AOP_SIZE (right) > 1) && + (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) + { + size = AOP_SIZE (right) - 1; + if (AOP_INDPTRn (result)) + { + genSetDPTR (AOP (result)->aopu.dptr); + } + while (size--) + emitcode ("lcall", "__decdptr"); + if (AOP_INDPTRn (result)) + { + genSetDPTR (0); + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset, dopi; + bool pushedB; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerSet")); + + aopOp (result, ic, FALSE, FALSE); + pushedB = pushB (); + dopi = loadDptrFromOperand (result, TRUE); + + /* so dptr-b now contains the address */ + aopOp (right, ic, FALSE, (AOP_INDPTRn (result) ? FALSE : TRUE)); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", GPOINTER); + } + else + { + size = AOP_SIZE (right); + offset = 0; + + _startLazyDPSEvaluation (); + while (size--) + { + if (size) + { + // Set two bytes at a time, passed in AP & A. + // dptr will be incremented ONCE by __gptrputWord. + // + // Note: any change here must be coordinated + // with the implementation of __gptrputWord + // in device/lib/_gptrput.c + emitcode ("mov", "acc1, %s", aopGet (right, offset++, FALSE, FALSE, NULL)); + MOVA (aopGet (right, offset++, FALSE, FALSE, NULL)); + + genSetDPTR (0); + _flushLazyDPS (); + emitcode ("lcall", "__gptrputWord"); + size--; + } + else + { + // Only one byte to put. + MOVA (aopGet (right, offset++, FALSE, FALSE, NULL)); + + genSetDPTR (0); + _flushLazyDPS (); + emitcode ("lcall", "__gptrput"); + } + + if (size || (dopi && pi && AOP_TYPE (result) != AOP_IMMD)) + { + emitcode ("inc", "dptr"); + } + } + _endLazyDPSEvaluation (); + } + + if (dopi && pi && AOP_TYPE (result) != AOP_IMMD) + { + _startLazyDPSEvaluation (); + + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + if (options.model == MODEL_FLAT24) + { + aopPut (result, "dpx", 2); + aopPut (result, "b", 3); + } + else + { + aopPut (result, "b", 2); + } + _endLazyDPSEvaluation (); + + pi->generated = 1; + } + else if (IS_SYMOP (result) && IS_OP_RUONLY (result) && AOP_SIZE (right) > 1 && + (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) + { + size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("lcall", "__decdptr"); + } + popB (pushedB); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode * pi) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerSet")); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + if (p_type == GPOINTER && IS_SYMOP (result) && OP_SYMBOL (result)->remat && IS_CAST_ICODE (OP_SYMBOL (result)->rematiCode)) + { + result = IC_RIGHT (OP_SYMBOL (result)->rematiCode); + type = operandType (result); + p_type = DCL_TYPE (type); + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerSet (right, result, ic, pi); + break; + + case PPOINTER: + genPagedPointerSet (right, result, ic, pi); + break; + + case FPOINTER: + genFarPointerSet (right, result, ic, pi); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic, pi); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + char *dup = NULL; + + D (emitcode (";", "genIfx")); + + aopOp (cond, ic, FALSE, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + { + toBoolean (cond); + } + else + { + isbit = 1; + if (AOP (cond)->aopu.aop_dir) + /* TODO: borutr: is really necessary to strdup it? */ + dup = Safe_strdup (AOP (cond)->aopu.aop_dir); + } + + /* the result is now in the accumulator or a directly addressable bit */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if the condition is a bit variable */ + if (isbit && dup) + genIfxJump (ic, dup, popIc); + else if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + genIfxJump (ic, SPIL_LOC (cond)->rname, popIc); + else if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname, popIc); + else + genIfxJump (ic, "a", popIc); + + if (dup) + Safe_free (dup); + + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + bool pushedA = FALSE; + + D (emitcode (";", "genAddrOf")); + + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) + { + /* if 10 bit stack */ + if (options.stack10bit) + { + struct dbuf_s dbuf; + int offset; + + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "#!constbyte", (options.stack_loc >> 16) & 0xff); + /* if it has an offset then we need to compute it */ + /* emitcode ("subb", "a,#!constbyte", */ + /* -((sym->stack < 0) ? */ + /* ((short) (sym->stack - _G.nRegsSaved)) : */ + /* ((short) sym->stack)) & 0xff); */ + /* emitcode ("mov","b,a"); */ + /* emitcode ("mov","a,#!constbyte",(-((sym->stack < 0) ? */ + /* ((short) (sym->stack - _G.nRegsSaved)) : */ + /* ((short) sym->stack)) >> 8) & 0xff); */ + if (sym->stack) + { + emitcode ("mov", "a,_bpx"); + emitcode ("add", "a,#!constbyte", ((sym->stack < 0) ? + ((char) (sym->stack - _G.nRegsSaved)) : ((char) sym->stack)) & 0xff); + emitcode ("mov", "b,a"); + emitcode ("mov", "a,_bpx+1"); + + offset = (((sym->stack < 0) ? ((short) (sym->stack - _G.nRegsSaved)) : ((short) sym->stack)) >> 8) & 0xff; + + emitcode ("addc", "a,#!constbyte", offset); + + if (aopPutUsesAcc (IC_RESULT (ic), "b", 0)) + { + emitpush ("acc"); + pushedA = TRUE; + } + aopPut (IC_RESULT (ic), "b", 0); + if (pushedA) + emitpop ("acc"); + aopPut (IC_RESULT (ic), "a", 1); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), 2); + } + else + { + /* we can just move _bp */ + aopPut (IC_RESULT (ic), "_bpx", 0); + aopPut (IC_RESULT (ic), "_bpx+1", 1); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), 2); + } + dbuf_destroy (&dbuf); + } + else + { + /* if it has an offset then we need to compute it */ + if (sym->stack) + { + emitcode ("mov", "a,_bp"); + emitcode ("add", "a,#!constbyte", ((char) sym->stack & 0xff)); + aopPut (IC_RESULT (ic), "a", 0); + } + else + { + /* we can just move _bp */ + aopPut (IC_RESULT (ic), "_bp", 0); + } + /* fill the result with zero */ + size = AOP_SIZE (IC_RESULT (ic)) - 1; + + + if (options.stack10bit && size < (FARPTRSIZE - 1)) + { + fprintf (stderr, "*** warning: pointer to stack var truncated.\n"); + } + + offset = 1; + while (size--) + { + aopPut (IC_RESULT (ic), zero, offset++); + } + } + goto release; + } + + /* object not on stack then we need the name */ + size = getDataSize (IC_RESULT (ic)); + offset = 0; + + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + { + switch (offset) + { + case 1: + dbuf_tprintf (&dbuf, "#!his", sym->rname); + break; + case 2: + dbuf_tprintf (&dbuf, "#!hihis", sym->rname); + break; + case 3: + dbuf_tprintf (&dbuf, "#!hihihis", sym->rname); + break; + default: /* should not need this (just in case) */ + dbuf_printf (&dbuf, "#(%s >> %d)", sym->rname, offset * 8); + } + } + else + { + dbuf_printf (&dbuf, "#%s", sym->rname); + } + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarFarAssign - assignment when both are in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarFarAssign (operand * result, operand * right, iCode * ic) +{ + int size = AOP_SIZE (right); + int offset = 0; + symbol *rSym = NULL; + + if (size == 1) + { + /* quick & easy case. */ + D (emitcode (";", "genFarFarAssign (1 byte case)")); + MOVA (aopGet (right, 0, FALSE, FALSE, NULL)); + freeAsmop (right, NULL, ic, FALSE); + /* now assign DPTR to result */ + _G.accInUse++; + aopOp (result, ic, FALSE, FALSE); + _G.accInUse--; + aopPut (result, "a", 0); + freeAsmop (result, NULL, ic, FALSE); + return; + } + + /* See if we've got an underlying symbol to abuse. */ + if (IS_SYMOP (result) && OP_SYMBOL (result)) + { + if (IS_TRUE_SYMOP (result)) + { + rSym = OP_SYMBOL (result); + } + else if (IS_ITEMP (result) && OP_SYMBOL (result)->isspilt && OP_SYMBOL (result)->usl.spillLoc) + { + rSym = OP_SYMBOL (result)->usl.spillLoc; + } + } + + if (size > 1 && rSym && rSym->rname && !rSym->onStack && !IS_OP_RUONLY (right)) + { + /* We can use the '390 auto-toggle feature to good effect here. */ + + D (emitcode (";", "genFarFarAssign (390 auto-toggle fun)")); + emitcode ("mov", "dps,#!constbyte", 0x21); /* Select DPTR2 & auto-toggle. */ + emitcode ("mov", "dptr,#%s", rSym->rname); + /* DP2 = result, DP1 = right, DP1 is current. */ + while (size) + { + if (AOP (right)->code) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + { + emitcode ("movx", "a,@dptr"); + } + emitcode ("movx", "@dptr,a"); + if (--size) + { + emitcode ("inc", "dptr"); + emitcode ("inc", "dptr"); + } + } + emitcode ("mov", "dps,#0"); + freeAsmop (right, NULL, ic, FALSE); +#if 0 + some alternative code for processors without auto - toggle + no time to test now, so later well put in ... kpb D (emitcode (";", "genFarFarAssign (dual-dptr fun)")); + emitcode ("mov", "dps,#1"); /* Select DPTR2. */ + emitcode ("mov", "dptr,#%s", rSym->rname); + /* DP2 = result, DP1 = right, DP1 is current. */ + while (size) + { + --size; + emitcode ("movx", "a,@dptr"); + if (size) + emitcode ("inc", "dptr"); + emitcode ("inc", "dps"); + emitcode ("movx", "@dptr,a"); + if (size) + emitcode ("inc", "dptr"); + emitcode ("inc", "dps"); + } + emitcode ("mov", "dps,#0"); + freeAsmop (right, NULL, ic, FALSE); +#endif + } + else + { + D (emitcode (";", "genFarFarAssign")); + aopOp (result, ic, TRUE, TRUE); + + _startLazyDPSEvaluation (); + + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + freeAsmop (result, NULL, ic, FALSE); + freeAsmop (right, NULL, ic, FALSE); + } +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset; + unsigned long long lit = 0ull; + + D (emitcode (";", "genAssign")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* if they are the same */ + if (operandsEqu (result, right) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + return; + + /* if both are ruonly */ + if (IS_OP_RUONLY (right) && IS_OP_RUONLY (result)) + return; + + aopOp (right, ic, FALSE, IS_OP_RUONLY (result)); + + emitcode (";", "genAssign: resultIsFar = %s", isOperandInFarSpace (result) ? "TRUE" : "FALSE"); + + /* special case both in far space */ + if ((AOP_TYPE (right) == AOP_DPTR || AOP_TYPE (right) == AOP_DPTR2 || IS_OP_RUONLY (right)) && + /* IS_TRUE_SYMOP(result) && */ + isOperandInFarSpace (result)) + { + genFarFarAssign (result, right, ic); + return; + } + + aopOp (result, ic, TRUE, FALSE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result)) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) /* works only for true symbols */ + { + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + if (((int) operandLitValue (right))) + aopPut (result, one, 0); + else + aopPut (result, zero, 0); + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + aopPut (result, "c", 0); + goto release; + } + + /* we need to or */ + toBoolean (right); + aopPut (result, "a", 0); + goto release; + } + + /* bit variables done */ + /* general case */ + if (AOP_TYPE (right) == AOP_LIT) + { + if (!IS_FLOAT (operandType (right))) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + else + { + union + { + float f; + unsigned char c[4]; + } fl; + + fl.f = (float) floatFromVal (AOP (right)->aopu.aop_lit); +#ifdef WORDS_BIGENDIAN + lit = (fl.c[3] << 0) | (fl.c[2] << 8) | (fl.c[1] << 16) | (fl.c[0] << 24); +#else + lit = (fl.c[0] << 0) | (fl.c[1] << 8) | (fl.c[2] << 16) | (fl.c[3] << 24); +#endif + } + } + + size = getDataSize (result); + offset = 0; + + if ((size > 1) && (AOP_TYPE (result) != AOP_REG) && (AOP_TYPE (right) == AOP_LIT)) + { + _startLazyDPSEvaluation (); + while (size && ((unsigned long long) (lit >> (offset * 8)) != 0)) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + size--; + } + /* And now fill the rest with zeros. */ + if (size) + { + emitcode ("clr", "a"); + } + while (size--) + { + aopPut (result, "a", offset++); + } + _endLazyDPSEvaluation (); + } + else + { + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + } + adjustArithmeticResult (ic); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + symbol *jtab; + + D (emitcode (";", "genJumpTab")); + + aopOp (IC_JTCOND (ic), ic, FALSE, FALSE); + /* get the condition into accumulator */ + MOVA (aopGet (IC_JTCOND (ic), 0, FALSE, FALSE, NULL)); + /* multiply by four! */ + emitcode ("add", "a,acc"); + emitcode ("add", "a,acc"); + freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); + + jtab = newiTempLabel (NULL); + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtab->key)); + emitcode ("jmp", "@a+dptr"); + emitLabel (jtab); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode ("ljmp", "!tlabel", labelKey2num (jtab->key)); +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + + D (emitcode (";", "genCast")); + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + /* if casting to <= size and both ruonly then do nothing */ + if (IS_OP_RUONLY (right) && IS_OP_RUONLY (result)) + return; + + aopOp (right, ic, FALSE, IS_OP_RUONLY (result)); + aopOp (result, ic, FALSE, (AOP_TYPE (right) == AOP_DPTR)); + + /* if the result is a bit (and not a bitfield) */ + if (IS_BOOLEAN (OP_SYMBOL (result)->type)) + { + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + if (((int) operandLitValue (right))) + aopPut (result, one, 0); + else + aopPut (result, zero, 0); + + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + aopPut (result, "c", 0); + goto release; + } + + /* we need to or */ + toCarry (right); + outBitC (result); + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right) && !IS_BOOLEAN (operandType (result))) + { + /* if they are in the same place */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if they in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + goto release; + } + + /* if the result is of type pointer */ + if (IS_PTR (ctype) && !IS_INTEGRAL (rtype)) + { + int p_type; + sym_link *type = operandType (right); + + /* pointer to generic pointer */ + if (IS_GENPTR (ctype)) + { + if (IS_PTR (type) || IS_FUNC (type)) + { + p_type = DCL_TYPE (type); + } + else + { +#if OLD_CAST_BEHAVIOR + /* KV: we are converting a non-pointer type to + * a generic pointer. This (ifdef'd out) code + * says that the resulting generic pointer + * should have the same class as the storage + * location of the non-pointer variable. + * + * For example, converting an int (which happens + * to be stored in DATA space) to a pointer results + * in a DATA generic pointer; if the original int + * in XDATA space, so will be the resulting pointer. + * + * I don't like that behavior, and thus this change: + * all such conversions will be forced to XDATA and + * throw a warning. If you want some non-XDATA + * type, or you want to suppress the warning, you + * must go through an intermediate cast, like so: + * + * char _generic *gp = (char _xdata *)(intVar); + */ + sym_link *etype = getSpec (type); + + /* we have to go by the storage class */ + if (SPEC_OCLS (etype) != generic) + { + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + else +#endif + { + /* Converting unknown class (i.e. register variable) + * to generic pointer. This is not good, but + * we'll make a guess (and throw a warning). + */ + p_type = FPOINTER; + werror (W_INT_TO_GEN_PTR_CAST); + } + } + + /* the first two bytes are known */ + size = GPTRSIZE - 1; + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + + /* the last byte depending on type */ + { + int gpVal = pointerTypeToGPByte (p_type, NULL, NULL); + char gpValStr[10]; + + if (gpVal == -1) + { + // pointerTypeToGPByte will have warned, just copy. + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + } + else + { + SNPRINTF (gpValStr, sizeof (gpValStr), "#0x%02x", gpVal); + aopPut (result, gpValStr, GPTRSIZE - 1); + } + } + goto release; + } + + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source */ + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + _startLazyDPSEvaluation (); + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE, NULL), offset); + offset++; + } + _endLazyDPSEvaluation (); + + /* now depending on the sign of the source && destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + /* also, if the source is a bit, we don't need to sign extend, because + * it can't possibly have set the sign bit. + */ + if (!IS_SPEC (rtype) || SPEC_USIGN (rtype) || AOP_TYPE (right) == AOP_CRY) + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + else + { + /* we need to extend the sign :{ */ + MOVA (aopGet (right, AOP_SIZE (right) - 1, FALSE, FALSE, NULL)); + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + + /* we are done hurray !!!! */ + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genMemcpyX2X - gen code for memcpy xdata to xdata */ +/*-----------------------------------------------------------------*/ +static void +genMemcpyX2X (iCode * ic, int nparms, operand ** parms, int fromc) +{ + operand *from, *to, *count; + symbol *lbl; + bitVect *rsave; + int i; + + /* we know it has to be 3 parameters */ + assert (nparms == 3); + + rsave = newBitVect (16); + /* save DPTR if it needs to be saved */ + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectIntersect (rsave, bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + savermask (rsave); + + to = parms[0]; + from = parms[1]; + count = parms[2]; + + aopOp (from, ic->next, FALSE, FALSE); + + /* get from into DPTR1 */ + emitcode ("mov", "dpl1,%s", aopGet (from, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph1,%s", aopGet (from, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx1,%s", aopGet (from, 2, FALSE, FALSE, NULL)); + } + + freeAsmop (from, NULL, ic, FALSE); + aopOp (to, ic, FALSE, FALSE); + /* get "to" into DPTR */ + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (to) != AOP_STR) + { + /* if already in DPTR then we need to push */ + if (AOP_TYPE (to) == AOP_DPTR) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, NULL)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + emitcode ("pop", "dph"); + emitcode ("pop", "dpl"); + } + else + { + _startLazyDPSEvaluation (); + /* if this is remateriazable */ + if (AOP_TYPE (to) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (to, 0, TRUE, FALSE, NULL)); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (to, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (to, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); + } + } + freeAsmop (to, NULL, ic, FALSE); + _G.dptrInUse = _G.dptr1InUse = 1; + aopOp (count, ic->next->next, FALSE, FALSE); + lbl = newiTempLabel (NULL); + + /* now for the actual copy */ + if (AOP_TYPE (count) == AOP_LIT && (int) ulFromVal (AOP (count)->aopu.aop_lit) <= 256) + { + emitcode ("mov", "b,%s", aopGet (count, 0, FALSE, FALSE, NULL)); + if (fromc) + { + emitcode ("lcall", "__bi_memcpyc2x_s"); + } + else + { + emitcode ("lcall", "__bi_memcpyx2x_s"); + } + freeAsmop (count, NULL, ic, FALSE); + } + else + { + symbol *lbl1 = newiTempLabel (NULL); + + emitcode (";", " Auto increment but no djnz"); + emitcode ("mov", "acc1,%s", aopGet (count, 0, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 1, FALSE, TRUE, NULL)); + freeAsmop (count, NULL, ic, FALSE); + emitcode ("mov", "dps,#!constbyte", 0x21); /* Select DPTR2 & auto-toggle. */ + emitLabel (lbl); + if (fromc) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + emitcode ("movx", "a,@dptr"); + emitcode ("movx", "@dptr,a"); + emitcode ("inc", "dptr"); + emitcode ("inc", "dptr"); + emitcode ("mov", "a,b"); + emitcode ("orl", "a,acc1"); + emitcode ("jz", "!tlabel", labelKey2num (lbl1->key)); + emitcode ("mov", "a,acc1"); + emitcode ("add", "a,#!constbyte", 0xFF); + emitcode ("mov", "acc1,a"); + emitcode ("mov", "a,b"); + emitcode ("addc", "a,#!constbyte", 0xFF); + emitcode ("mov", "b,a"); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (lbl1); + } + emitcode ("mov", "dps,#0"); + _G.dptrInUse = _G.dptr1InUse = 0; + unsavermask (rsave); + +} + +/*-----------------------------------------------------------------*/ +/* genMemcmpX2X - gen code for memcmp xdata to xdata */ +/*-----------------------------------------------------------------*/ +static void +genMemcmpX2X (iCode * ic, int nparms, operand ** parms, int fromc) +{ + operand *from, *to, *count; + symbol *lbl, *lbl2; + bitVect *rsave; + int i; + + /* we know it has to be 3 parameters */ + assert (nparms == 3); + + rsave = newBitVect (16); + /* save DPTR if it needs to be saved */ + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectIntersect (rsave, bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + savermask (rsave); + + to = parms[0]; + from = parms[1]; + count = parms[2]; + + aopOp (from, ic->next, FALSE, FALSE); + + /* get from into DPTR1 */ + emitcode ("mov", "dpl1,%s", aopGet (from, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph1,%s", aopGet (from, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx1,%s", aopGet (from, 2, FALSE, FALSE, NULL)); + } + + freeAsmop (from, NULL, ic, FALSE); + aopOp (to, ic, FALSE, FALSE); + /* get "to" into DPTR */ + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (to) != AOP_STR) + { + /* if already in DPTR then we need to push */ + if (AOP_TYPE (to) == AOP_DPTR) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, NULL)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + emitcode ("pop", "dph"); + emitcode ("pop", "dpl"); + } + else + { + _startLazyDPSEvaluation (); + /* if this is remateriazable */ + if (AOP_TYPE (to) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (to, 0, TRUE, FALSE, NULL)); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (to, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (to, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); + } + } + freeAsmop (to, NULL, ic, FALSE); + _G.dptrInUse = _G.dptr1InUse = 1; + aopOp (count, ic->next->next, FALSE, FALSE); + lbl = newiTempLabel (NULL); + lbl2 = newiTempLabel (NULL); + + /* now for the actual compare */ + if (AOP_TYPE (count) == AOP_LIT && (int) ulFromVal (AOP (count)->aopu.aop_lit) <= 256) + { + emitcode ("mov", "b,%s", aopGet (count, 0, FALSE, FALSE, NULL)); + if (fromc) + emitcode ("lcall", "__bi_memcmpc2x_s"); + else + emitcode ("lcall", "__bi_memcmpx2x_s"); + freeAsmop (count, NULL, ic, FALSE); + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + else + { + symbol *lbl1 = newiTempLabel (NULL); + + emitcode ("push", "ar0"); + emitcode (";", " Auto increment but no djnz"); + emitcode ("mov", "acc1,%s", aopGet (count, 0, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 1, FALSE, TRUE, NULL)); + freeAsmop (count, NULL, ic, FALSE); + emitcode ("mov", "dps,#!constbyte", 0x21); /* Select DPTR2 & auto-toggle. */ + emitLabel (lbl); + if (fromc) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + emitcode ("movx", "a,@dptr"); + emitcode ("mov", "r0,a"); + emitcode ("movx", "a,@dptr"); + emitcode ("clr", "c"); + emitcode ("subb", "a,r0"); + emitcode ("jnz", "!tlabel", labelKey2num (lbl2->key)); + emitcode ("inc", "dptr"); + emitcode ("inc", "dptr"); + emitcode ("mov", "a,b"); + emitcode ("orl", "a,acc1"); + emitcode ("jz", "!tlabel", labelKey2num (lbl1->key)); + emitcode ("mov", "a,acc1"); + emitcode ("add", "a,#!constbyte", 0xFF); + emitcode ("mov", "acc1,a"); + emitcode ("mov", "a,b"); + emitcode ("addc", "a,#!constbyte", 0xFF); + emitcode ("mov", "b,a"); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (lbl1); + emitcode ("clr", "a"); + emitLabel (lbl2); + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + emitcode ("pop", "ar0"); + emitcode ("mov", "dps,#0"); + } + _G.dptrInUse = _G.dptr1InUse = 0; + unsavermask (rsave); + +} + +/*-----------------------------------------------------------------*/ +/* genInp - gen code for __builtin_inp read data from a mem mapped */ +/* port, first parameter output area second parameter pointer to */ +/* port third parameter count */ +/*-----------------------------------------------------------------*/ +static void +genInp (iCode * ic, int nparms, operand ** parms) +{ + operand *from, *to, *count; + symbol *lbl; + bitVect *rsave; + int i; + + /* we know it has to be 3 parameters */ + assert (nparms == 3); + + rsave = newBitVect (16); + /* save DPTR if it needs to be saved */ + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectIntersect (rsave, bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + savermask (rsave); + + to = parms[0]; + from = parms[1]; + count = parms[2]; + + aopOp (from, ic->next, FALSE, FALSE); + + /* get from into DPTR1 */ + emitcode ("mov", "dpl1,%s", aopGet (from, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph1,%s", aopGet (from, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx1,%s", aopGet (from, 2, FALSE, FALSE, NULL)); + } + + freeAsmop (from, NULL, ic, FALSE); + aopOp (to, ic, FALSE, FALSE); + /* get "to" into DPTR */ + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (to) != AOP_STR) + { + /* if already in DPTR then we need to push */ + if (AOP_TYPE (to) == AOP_DPTR) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, NULL)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + emitcode ("pop", "dph"); + emitcode ("pop", "dpl"); + } + else + { + _startLazyDPSEvaluation (); + /* if this is remateriazable */ + if (AOP_TYPE (to) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (to, 0, TRUE, FALSE, NULL)); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (to, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (to, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); + } + } + freeAsmop (to, NULL, ic, FALSE); + + _G.dptrInUse = _G.dptr1InUse = 1; + aopOp (count, ic->next->next, FALSE, FALSE); + lbl = newiTempLabel (NULL); + + /* now for the actual copy */ + if (AOP_TYPE (count) == AOP_LIT && (int) ulFromVal (AOP (count)->aopu.aop_lit) <= 256) + { + emitcode (";", "OH JOY auto increment with djnz (very fast)"); + emitcode ("mov", "dps,#!constbyte", 0x1); /* Select DPTR2 */ + emitcode ("mov", "b,%s", aopGet (count, 0, FALSE, FALSE, NULL)); + freeAsmop (count, NULL, ic, FALSE); + emitLabel (lbl); + emitcode ("movx", "a,@dptr"); /* read data from port */ + emitcode ("dec", "dps"); /* switch to DPTR */ + emitcode ("movx", "@dptr,a"); /* save into location */ + emitcode ("inc", "dptr"); /* point to next area */ + emitcode ("inc", "dps"); /* switch to DPTR2 */ + emitcode ("djnz", "b,!tlabel", labelKey2num (lbl->key)); + } + else + { + symbol *lbl1 = newiTempLabel (NULL); + + emitcode (";", " Auto increment but no djnz"); + emitcode ("mov", "acc1,%s", aopGet (count, 0, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 1, FALSE, TRUE, NULL)); + freeAsmop (count, NULL, ic, FALSE); + emitcode ("mov", "dps,#!constbyte", 0x1); /* Select DPTR2 */ + emitLabel (lbl); + emitcode ("movx", "a,@dptr"); + emitcode ("dec", "dps"); /* switch to DPTR */ + emitcode ("movx", "@dptr,a"); + emitcode ("inc", "dptr"); + emitcode ("inc", "dps"); /* switch to DPTR2 */ + /* emitcode ("djnz","b,!tlabel",lbl->key+100); */ + /* emitcode ("djnz","acc1,!tlabel",lbl->key+100); */ + emitcode ("mov", "a,b"); + emitcode ("orl", "a,acc1"); + emitcode ("jz", "!tlabel", labelKey2num (lbl1->key)); + emitcode ("mov", "a,acc1"); + emitcode ("add", "a,#!constbyte", 0xFF); + emitcode ("mov", "acc1,a"); + emitcode ("mov", "a,b"); + emitcode ("addc", "a,#!constbyte", 0xFF); + emitcode ("mov", "b,a"); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (lbl1); + } + emitcode ("mov", "dps,#0"); + _G.dptrInUse = _G.dptr1InUse = 0; + unsavermask (rsave); + +} + +/*-----------------------------------------------------------------*/ +/* genOutp - gen code for __builtin_inp write data to a mem mapped */ +/* port, first parameter output area second parameter pointer to */ +/* port third parameter count */ +/*-----------------------------------------------------------------*/ +static void +genOutp (iCode * ic, int nparms, operand ** parms) +{ + operand *from, *to, *count; + symbol *lbl; + bitVect *rsave; + int i; + + /* we know it has to be 3 parameters */ + assert (nparms == 3); + + rsave = newBitVect (16); + /* save DPTR if it needs to be saved */ + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectIntersect (rsave, bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + savermask (rsave); + + to = parms[0]; + from = parms[1]; + count = parms[2]; + + aopOp (from, ic->next, FALSE, FALSE); + + /* get from into DPTR1 */ + emitcode ("mov", "dpl1,%s", aopGet (from, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph1,%s", aopGet (from, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx1,%s", aopGet (from, 2, FALSE, FALSE, NULL)); + } + + freeAsmop (from, NULL, ic, FALSE); + aopOp (to, ic, FALSE, FALSE); + /* get "to" into DPTR */ + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (to) != AOP_STR) + { + /* if already in DPTR then we need to push */ + if (AOP_TYPE (to) == AOP_DPTR) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, NULL)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + emitcode ("pop", "dph"); + emitcode ("pop", "dpl"); + } + else + { + _startLazyDPSEvaluation (); + /* if this is remateriazable */ + if (AOP_TYPE (to) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (to, 0, TRUE, FALSE, NULL)); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (to, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (to, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); + } + } + freeAsmop (to, NULL, ic, FALSE); + + _G.dptrInUse = _G.dptr1InUse = 1; + aopOp (count, ic->next->next, FALSE, FALSE); + lbl = newiTempLabel (NULL); + + /* now for the actual copy */ + if (AOP_TYPE (count) == AOP_LIT && (int) ulFromVal (AOP (count)->aopu.aop_lit) <= 256) + { + emitcode (";", "OH JOY auto increment with djnz (very fast)"); + emitcode ("mov", "dps,#!constbyte", 0x0); /* Select DPTR */ + emitcode ("mov", "b,%s", aopGet (count, 0, FALSE, FALSE, NULL)); + emitLabel (lbl); + emitcode ("movx", "a,@dptr"); /* read data from port */ + emitcode ("inc", "dps"); /* switch to DPTR2 */ + emitcode ("movx", "@dptr,a"); /* save into location */ + emitcode ("inc", "dptr"); /* point to next area */ + emitcode ("dec", "dps"); /* switch to DPTR */ + emitcode ("djnz", "b,!tlabel", labelKey2num (lbl->key)); + freeAsmop (count, NULL, ic, FALSE); + } + else + { + symbol *lbl1 = newiTempLabel (NULL); + + emitcode (";", " Auto increment but no djnz"); + emitcode ("mov", "acc1,%s", aopGet (count, 0, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 1, FALSE, TRUE, NULL)); + freeAsmop (count, NULL, ic, FALSE); + emitcode ("mov", "dps,#!constbyte", 0x0); /* Select DPTR */ + emitLabel (lbl); + emitcode ("movx", "a,@dptr"); + emitcode ("inc", "dptr"); + emitcode ("inc", "dps"); /* switch to DPTR2 */ + emitcode ("movx", "@dptr,a"); + emitcode ("dec", "dps"); /* switch to DPTR */ + emitcode ("mov", "a,b"); + emitcode ("orl", "a,acc1"); + emitcode ("jz", "!tlabel", labelKey2num (lbl1->key)); + emitcode ("mov", "a,acc1"); + emitcode ("add", "a,#!constbyte", 0xFF); + emitcode ("mov", "acc1,a"); + emitcode ("mov", "a,b"); + emitcode ("addc", "a,#!constbyte", 0xFF); + emitcode ("mov", "b,a"); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (lbl1); + } + emitcode ("mov", "dps,#0"); + _G.dptrInUse = _G.dptr1InUse = 0; + unsavermask (rsave); + +} + +/*-----------------------------------------------------------------*/ +/* genSwapW - swap lower & high order bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwapW (iCode * ic, int nparms, operand ** parms) +{ + operand *dest; + operand *src; + assert (nparms == 1); + + src = parms[0]; + dest = IC_RESULT (ic); + + assert (getSize (operandType (src)) == 2); + + aopOp (src, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (src, 0, FALSE, FALSE, NULL)); + _G.accInUse++; + MOVB (aopGet (src, 1, FALSE, FALSE, "b")); + _G.accInUse--; + freeAsmop (src, NULL, ic, FALSE); + + aopOp (dest, ic, FALSE, FALSE); + aopPut (dest, "b", 0); + aopPut (dest, "a", 1); + freeAsmop (dest, NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genMemsetX - gencode for memSetX data */ +/*-----------------------------------------------------------------*/ +static void +genMemsetX (iCode * ic, int nparms, operand ** parms) +{ + operand *to, *val, *count; + symbol *lbl; + int i; + bitVect *rsave; + + /* we know it has to be 3 parameters */ + assert (nparms == 3); + + to = parms[0]; + val = parms[1]; + count = parms[2]; + + /* save DPTR if it needs to be saved */ + rsave = newBitVect (16); + for (i = DPL_IDX; i <= B_IDX; i++) + { + if (bitVectBitValue (ic->rMask, i)) + rsave = bitVectSetBit (rsave, i); + } + rsave = bitVectIntersect (rsave, bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + savermask (rsave); + + aopOp (to, ic, FALSE, FALSE); + /* get "to" into DPTR */ + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (to) != AOP_STR) + { + /* if already in DPTR then we need to push */ + if (AOP_TYPE (to) == AOP_DPTR) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, NULL)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, NULL)); + if (options.model == MODEL_FLAT24) + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + emitcode ("pop", "dph"); + emitcode ("pop", "dpl"); + } + else + { + _startLazyDPSEvaluation (); + /* if this is remateriazable */ + if (AOP_TYPE (to) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (to, 0, TRUE, FALSE, NULL)); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (to, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (to, 1, FALSE, FALSE, NULL)); + if (options.model == MODEL_FLAT24) + { + emitcode ("mov", "dpx,%s", aopGet (to, 2, FALSE, FALSE, NULL)); + } + } + _endLazyDPSEvaluation (); + } + } + freeAsmop (to, NULL, ic, FALSE); + + aopOp (val, ic->next->next, FALSE, FALSE); + aopOp (count, ic->next->next, FALSE, FALSE); + lbl = newiTempLabel (NULL); + /* now for the actual copy */ + if (AOP_TYPE (count) == AOP_LIT && (int) ulFromVal (AOP (count)->aopu.aop_lit) <= 256) + { + char *l = Safe_strdup (aopGet (val, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 0, FALSE, FALSE, NULL)); + MOVA (l); + Safe_free (l); + emitLabel (lbl); + emitcode ("movx", "@dptr,a"); + emitcode ("inc", "dptr"); + emitcode ("djnz", "b,!tlabel", labelKey2num (lbl->key)); + } + else + { + symbol *lbl1 = newiTempLabel (NULL); + + emitcode ("mov", "acc1,%s", aopGet (count, 0, FALSE, TRUE, NULL)); + emitcode ("mov", "b,%s", aopGet (count, 1, FALSE, TRUE, NULL)); + emitLabel (lbl); + MOVA (aopGet (val, 0, FALSE, FALSE, NULL)); + emitcode ("movx", "@dptr,a"); + emitcode ("inc", "dptr"); + emitcode ("mov", "a,b"); + emitcode ("orl", "a,acc1"); + emitcode ("jz", "!tlabel", labelKey2num (lbl1->key)); + emitcode ("mov", "a,acc1"); + emitcode ("add", "a,#!constbyte", 0xFF); + emitcode ("mov", "acc1,a"); + emitcode ("mov", "a,b"); + emitcode ("addc", "a,#!constbyte", 0xFF); + emitcode ("mov", "b,a"); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (lbl1); + } + freeAsmop (count, NULL, ic, FALSE); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genNatLibLoadPrimitive - calls TINI api function to load primitive */ +/*-----------------------------------------------------------------*/ +static void +genNatLibLoadPrimitive (iCode * ic, int nparms, operand ** parms, int size) +{ + bitVect *rsave; + operand *pnum, *result; + int i; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + pnum = parms[0]; + aopOp (pnum, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (pnum, 0, FALSE, FALSE, DP2_RESULT_REG)); + freeAsmop (pnum, NULL, ic, FALSE); + emitcode ("lcall", "NatLib_LoadPrimitive"); + aopOp (result = IC_RESULT (ic), ic, FALSE, FALSE); + if (aopHasRegs (AOP (result), R0_IDX, R1_IDX) || aopHasRegs (AOP (result), R2_IDX, R3_IDX)) + { + for (i = (size - 1); i >= 0; i--) + { + emitcode ("push", "a%s", javaRet[i]); + } + for (i = 0; i < size; i++) + { + emitcode ("pop", "a%s", aopGet (result, i, FALSE, FALSE, DP2_RESULT_REG)); + } + } + else + { + for (i = 0; i < size; i++) + { + aopPut (result, javaRet[i], i); + } + } + freeAsmop (result, NULL, ic, FALSE); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genNatLibLoadPointer - calls TINI api function to load pointer */ +/*-----------------------------------------------------------------*/ +static void +genNatLibLoadPointer (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *pnum, *result; + int size = 3; + int i; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + pnum = parms[0]; + aopOp (pnum, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (pnum, 0, FALSE, FALSE, DP2_RESULT_REG)); + freeAsmop (pnum, NULL, ic, FALSE); + emitcode ("lcall", "NatLib_LoadPointer"); + aopOp (result = IC_RESULT (ic), ic, FALSE, FALSE); + if (AOP_TYPE (result) != AOP_STR) + { + for (i = 0; i < size; i++) + { + aopPut (result, fReturn[i], i); + } + } + freeAsmop (result, NULL, ic, FALSE); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genNatLibInstallStateBlock - */ +/*-----------------------------------------------------------------*/ +static void +genNatLibInstallStateBlock (iCode * ic, int nparms, operand ** parms, const char *name) +{ + bitVect *rsave; + operand *psb, *handle; + assert (nparms == 2); + + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + psb = parms[0]; + handle = parms[1]; + + /* put pointer to state block into DPTR1 */ + aopOp (psb, ic, FALSE, FALSE); + if (AOP_TYPE (psb) == AOP_IMMD) + { + emitcode ("mov", "dps,#1"); + emitcode ("mov", "dptr,%s", aopGet (psb, 0, TRUE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dps,#0"); + } + else + { + emitcode ("mov", "dpl1,%s", aopGet (psb, 0, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dph1,%s", aopGet (psb, 1, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dpx1,%s", aopGet (psb, 2, FALSE, FALSE, DP2_RESULT_REG)); + } + freeAsmop (psb, NULL, ic, FALSE); + + /* put libraryID into DPTR */ + emitcode ("mov", "dptr,#LibraryID"); + + /* put handle into r3:r2 */ + aopOp (handle, ic, FALSE, FALSE); + if (aopHasRegs (AOP (handle), R2_IDX, R3_IDX)) + { + emitcode ("push", "%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + } + else + { + emitcode ("mov", "r2,%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + } + freeAsmop (psb, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "NatLib_Install%sStateBlock", name); + + /* put return value into place */ + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + _G.accInUse--; + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genNatLibRemoveStateBlock - */ +/*-----------------------------------------------------------------*/ +static void +genNatLibRemoveStateBlock (iCode * ic, int nparms, const char *name) +{ + bitVect *rsave; + + assert (nparms == 0); + + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + /* put libraryID into DPTR */ + emitcode ("mov", "dptr,#LibraryID"); + /* make the call */ + emitcode ("lcall", "NatLib_Remove%sStateBlock", name); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genNatLibGetStateBlock - */ +/*-----------------------------------------------------------------*/ +static void +genNatLibGetStateBlock (iCode * ic, int nparms, operand ** parms, const char *name) +{ + bitVect *rsave; + symbol *lbl = newiTempLabel (NULL); + + assert (nparms == 0); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + /* put libraryID into DPTR */ + emitcode ("mov", "dptr,#LibraryID"); + /* make the call */ + emitcode ("lcall", "NatLib_Remove%sStateBlock", name); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + + /* put return value into place */ + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + if (aopHasRegs (AOP (IC_RESULT (ic)), R2_IDX, R3_IDX)) + { + emitcode ("push", "ar3"); + emitcode ("push", "ar2"); + emitcode ("pop", "%s", aopGet (IC_RESULT (ic), 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "%s", aopGet (IC_RESULT (ic), 1, FALSE, TRUE, DP2_RESULT_REG)); + } + else + { + aopPut (IC_RESULT (ic), "r2", 0); + aopPut (IC_RESULT (ic), "r3", 1); + } + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + emitLabel (lbl); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genMMMalloc - */ +/*-----------------------------------------------------------------*/ +static void +genMMMalloc (iCode * ic, int nparms, operand ** parms, int size, const char *name) +{ + bitVect *rsave; + operand *bsize; + symbol *rsym; + symbol *lbl = newiTempLabel (NULL); + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + bsize = parms[0]; + aopOp (bsize, ic, FALSE, FALSE); + + /* put the size in R4-R2 */ + if (aopHasRegs (AOP (bsize), R2_IDX, (size == 3 ? R4_IDX : R3_IDX))) + { + emitcode ("push", "%s", aopGet (bsize, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (bsize, 1, FALSE, TRUE, DP2_RESULT_REG)); + if (size == 3) + { + emitcode ("push", "%s", aopGet (bsize, 2, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar4"); + } + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + } + else + { + emitcode ("mov", "r2,%s", aopGet (bsize, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (bsize, 1, FALSE, TRUE, DP2_RESULT_REG)); + if (size == 3) + { + emitcode ("mov", "r4,%s", aopGet (bsize, 2, FALSE, TRUE, DP2_RESULT_REG)); + } + } + freeAsmop (bsize, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "MM_%s", name); + emitcode ("jz", "!tlabel", labelKey2num (lbl->key)); + emitcode ("mov", "r2,#!constbyte", 0xff); + emitcode ("mov", "r3,#!constbyte", 0xff); + emitLabel (lbl); + /* we don't care about the pointer : we just save the handle */ + rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + if (aopHasRegs (AOP (IC_RESULT (ic)), R2_IDX, R3_IDX)) + { + emitcode ("push", "ar3"); + emitcode ("push", "ar2"); + emitcode ("pop", "%s", aopGet (IC_RESULT (ic), 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "%s", aopGet (IC_RESULT (ic), 1, FALSE, TRUE, DP2_RESULT_REG)); + } + else + { + aopPut (IC_RESULT (ic), "r2", 0); + aopPut (IC_RESULT (ic), "r3", 1); + } + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genMMDeref - */ +/*-----------------------------------------------------------------*/ +static void +genMMDeref (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *handle; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + handle = parms[0]; + aopOp (handle, ic, FALSE, FALSE); + + /* put the size in R4-R2 */ + if (aopHasRegs (AOP (handle), R2_IDX, R3_IDX)) + { + emitcode ("push", "%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + } + else + { + emitcode ("mov", "r2,%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + } + freeAsmop (handle, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "MM_Deref"); + + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + if (AOP_TYPE (IC_RESULT (ic)) != AOP_STR) + { + _startLazyDPSEvaluation (); + + aopPut (IC_RESULT (ic), "dpl", 0); + aopPut (IC_RESULT (ic), "dph", 1); + aopPut (IC_RESULT (ic), "dpx", 2); + + _endLazyDPSEvaluation (); + + } + } + } + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genMMUnrestrictedPersist - */ +/*-----------------------------------------------------------------*/ +static void +genMMUnrestrictedPersist (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *handle; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + handle = parms[0]; + aopOp (handle, ic, FALSE, FALSE); + + /* put the size in R3-R2 */ + if (aopHasRegs (AOP (handle), R2_IDX, R3_IDX)) + { + emitcode ("push", "%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + } + else + { + emitcode ("mov", "r2,%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + } + freeAsmop (handle, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "MM_UnrestrictedPersist"); + + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + } + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemExecJavaProcess - */ +/*-----------------------------------------------------------------*/ +static void +genSystemExecJavaProcess (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *handle, *pp; + + assert (nparms == 2); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + pp = parms[0]; + handle = parms[1]; + + /* put the handle in R3-R2 */ + aopOp (handle, ic, FALSE, FALSE); + if (aopHasRegs (AOP (handle), R2_IDX, R3_IDX)) + { + emitcode ("push", "%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + } + else + { + emitcode ("mov", "r2,%s", aopGet (handle, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (handle, 1, FALSE, TRUE, DP2_RESULT_REG)); + } + freeAsmop (handle, NULL, ic, FALSE); + + /* put pointer in DPTR */ + aopOp (pp, ic, FALSE, FALSE); + if (AOP_TYPE (pp) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (pp, 0, TRUE, FALSE, NULL)); + } + else if (AOP_TYPE (pp) != AOP_STR) + { + /* not already in dptr */ + emitcode ("mov", "dpl,%s", aopGet (pp, 0, FALSE, FALSE, NULL)); + emitcode ("mov", "dph,%s", aopGet (pp, 1, FALSE, FALSE, NULL)); + emitcode ("mov", "dpx,%s", aopGet (pp, 2, FALSE, FALSE, NULL)); + } + freeAsmop (handle, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "System_ExecJavaProcess"); + + /* put result in place */ + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + } + + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemRTCRegisters - */ +/*-----------------------------------------------------------------*/ +static void +genSystemRTCRegisters (iCode * ic, int nparms, operand ** parms, char *name) +{ + bitVect *rsave; + operand *pp; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + pp = parms[0]; + /* put pointer in DPTR */ + aopOp (pp, ic, FALSE, FALSE); + if (AOP_TYPE (pp) == AOP_IMMD) + { + emitcode ("mov", "dps,#1"); + emitcode ("mov", "dptr,%s", aopGet (pp, 0, TRUE, FALSE, NULL)); + emitcode ("mov", "dps,#0"); + } + else + { + emitcode ("mov", "dpl1,%s", aopGet (pp, 0, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dph1,%s", aopGet (pp, 1, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dpx1,%s", aopGet (pp, 2, FALSE, FALSE, DP2_RESULT_REG)); + } + freeAsmop (pp, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "System_%sRTCRegisters", name); + + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemThreadSleep - */ +/*-----------------------------------------------------------------*/ +static void +genSystemThreadSleep (iCode * ic, int nparms, operand ** parms, char *name) +{ + bitVect *rsave; + operand *to, *s; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + to = parms[0]; + aopOp (to, ic, FALSE, FALSE); + if (aopHasRegs (AOP (to), R2_IDX, R3_IDX) || aopHasRegs (AOP (to), R0_IDX, R1_IDX)) + { + emitcode ("push", "%s", aopGet (to, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (to, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (to, 2, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("push", "%s", aopGet (to, 3, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("pop", "ar3"); + emitcode ("pop", "ar2"); + emitcode ("pop", "ar1"); + emitcode ("pop", "ar0"); + } + else + { + emitcode ("mov", "r0,%s", aopGet (to, 0, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r1,%s", aopGet (to, 1, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r2,%s", aopGet (to, 2, FALSE, TRUE, DP2_RESULT_REG)); + emitcode ("mov", "r3,%s", aopGet (to, 3, FALSE, TRUE, DP2_RESULT_REG)); + } + freeAsmop (to, NULL, ic, FALSE); + + /* suspend in acc */ + s = parms[1]; + aopOp (s, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (s, 0, FALSE, TRUE, NULL)); + freeAsmop (s, NULL, ic, FALSE); + + /* make the call */ + emitcode ("lcall", "System_%s", name); + + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemThreadResume - */ +/*-----------------------------------------------------------------*/ +static void +genSystemThreadResume (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *tid, *pid; + + assert (nparms == 2); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + tid = parms[0]; + pid = parms[1]; + + /* PID in R0 */ + aopOp (pid, ic, FALSE, FALSE); + emitcode ("mov", "r0,%s", aopGet (pid, 0, FALSE, TRUE, DP2_RESULT_REG)); + freeAsmop (pid, NULL, ic, FALSE); + + /* tid into ACC */ + aopOp (tid, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (tid, 0, FALSE, TRUE, DP2_RESULT_REG)); + freeAsmop (tid, NULL, ic, FALSE); + + emitcode ("lcall", "System_ThreadResume"); + + /* put result into place */ + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + } + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemProcessResume - */ +/*-----------------------------------------------------------------*/ +static void +genSystemProcessResume (iCode * ic, int nparms, operand ** parms) +{ + bitVect *rsave; + operand *pid; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + pid = parms[0]; + + /* pid into ACC */ + aopOp (pid, ic, FALSE, FALSE); + emitcode ("mov", "a,%s", aopGet (pid, 0, FALSE, TRUE, DP2_RESULT_REG)); + freeAsmop (pid, NULL, ic, FALSE); + + emitcode ("lcall", "System_ProcessResume"); + + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystem - */ +/*-----------------------------------------------------------------*/ +static void +genSystem (iCode * ic, int nparms, char *name) +{ + assert (nparms == 0); + + emitcode ("lcall", "System_%s", name); +} + +/*-----------------------------------------------------------------*/ +/* genSystemPoll - */ +/*-----------------------------------------------------------------*/ +static void +genSystemPoll (iCode * ic, int nparms, operand ** parms, char *name) +{ + bitVect *rsave; + operand *fp; + + assert (nparms == 1); + /* save registers that need to be saved */ + savermask (rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ds390_rUmaskForOp (IC_RESULT (ic)))); + + fp = parms[0]; + aopOp (fp, ic, FALSE, FALSE); + if (AOP_TYPE (fp) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (fp, 0, TRUE, FALSE, DP2_RESULT_REG)); + } + else if (AOP_TYPE (fp) != AOP_STR) + { + /* not already in dptr */ + emitcode ("mov", "dpl,%s", aopGet (fp, 0, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dph,%s", aopGet (fp, 1, FALSE, FALSE, DP2_RESULT_REG)); + emitcode ("mov", "dpx,%s", aopGet (fp, 2, FALSE, FALSE, DP2_RESULT_REG)); + } + freeAsmop (fp, NULL, ic, FALSE); + + emitcode ("lcall", "System_%sPoll", name); + + /* put result into place */ + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + } + unsavermask (rsave); +} + +/*-----------------------------------------------------------------*/ +/* genSystemGetCurrentID - */ +/*-----------------------------------------------------------------*/ +static void +genSystemGetCurrentID (iCode * ic, int nparms, operand ** parms, char *name) +{ + assert (nparms == 0); + + emitcode ("lcall", "System_GetCurrent%sId", name); + /* put result into place */ + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ic)); + if (rsym->liveFrom != rsym->liveTo) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + aopPut (IC_RESULT (ic), "a", 0); + freeAsmop (IC_RESULT (ic), NULL, ic, FALSE); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instruction */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + symbol *lbl, *lbl1; + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + + popForBranch (ic->next, TRUE); + + D (emitcode (";", "genDjnz")); + + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + if (AOP_NEEDSACC (IC_RESULT (ic))) + { + /* If the result is accessed indirectly via + * the accumulator, we must explicitly write + * it back after the decrement. + */ + const char *rByte = aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL); + + if (!EQ (rByte, "a")) + { + /* Something is hopelessly wrong */ + fprintf (stderr, "*** warning: internal error at %s:%d\n", __FILE__, __LINE__); + /* We can just give up; the generated code will be inefficient, + * but what the hey. + */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 0; + } + emitcode ("dec", "%s", rByte); + aopPut (IC_RESULT (ic), rByte, 0); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else if (IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL)); + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE, NULL)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else + { + emitcode ("djnz", "%s,!tlabel", aopGet (IC_RESULT (ic), 0, FALSE, TRUE, NULL), labelKey2num (lbl->key)); + } + emitcode ("sjmp", "!tlabel", labelKey2num (lbl1->key)); + emitLabel (lbl); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (lbl1); + + if (!ifx->generated) + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size = getSize (operandType (IC_RESULT (ic))); + int offset = 0; + int rb1off; + + D (emitcode (";", "genReceive")); + + if (ic->argreg == 1) + { + /* first parameter */ + if (IS_OP_RUONLY (IC_RESULT (ic))) + { + /* Nothing to do: it's already in the proper place. */ + return; + } + else + { + bool useDp2; + + useDp2 = isOperandInFarSpace (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic))); + + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE, useDp2); + _G.accInUse--; + + /* Sanity checking... */ + if (AOP_USESDPTR (IC_RESULT (ic))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genReceive got unexpected DPTR."); + } + assignResultValue (IC_RESULT (ic), NULL); + } + } + else if (ic->argreg > 12) + { + /* bit parameters */ + reg_info *reg = OP_SYMBOL (IC_RESULT (ic))->regs[0]; + + if (!reg || reg->rIdx != ic->argreg - 5) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + emitcode ("mov", "c,%s", rb1regs[ic->argreg - 5]); + outBitC (IC_RESULT (ic)); + } + } + else + { + /* second receive onwards */ + /* this gets a little tricky since unused receives will be + eliminated, we have saved the reg in the type field . and + we use that to figure out which register to use */ + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + rb1off = ic->argreg; + while (size--) + { + aopPut (IC_RESULT (ic), rb1regs[rb1off++ - 5], offset++); + } + } + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + int size, offset; + + D (emitcode (";", "genDummyRead")); + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } + + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (iCode * ic) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "genCritical")); + + if (IC_RESULT (ic)) + { + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + aopPut (IC_RESULT (ic), one, 0); /* save old ea in an operand */ + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + aopPut (IC_RESULT (ic), zero, 0); + emitLabel (tlbl); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + else + { + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw on top of stack */ + } +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (iCode * ic) +{ + D (emitcode (";", "genEndCritical")); + + if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE, FALSE); + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", IC_RIGHT (ic)->aop->aopu.aop_dir); + emitcode ("mov", "ea,c"); + } + else + { + MOVA (aopGet (IC_RIGHT (ic), 0, FALSE, FALSE, FALSE)); + emitcode ("rrc", "a"); + emitcode ("mov", "ea,c"); + } + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + } + else + { + emitpop ("psw"); /* restore ea via c in psw on top of stack */ + emitcode ("mov", "ea,c"); + } +} + + + +/*-----------------------------------------------------------------*/ +/* genBuiltIn - calls the appropriate function to generating code */ +/* for a built in function */ +/*-----------------------------------------------------------------*/ +static void +genBuiltIn (iCode * ic) +{ + operand *bi_parms[MAX_BUILTIN_ARGS]; + int nbi_parms; + iCode *bi_iCode; + symbol *bif; + + /* get all the arguments for a built in function */ + bi_iCode = getBuiltinParms (ic, &nbi_parms, bi_parms); + + /* which function is it */ + bif = OP_SYMBOL (IC_LEFT (bi_iCode)); + if (strcmp (bif->name, "__builtin_memcpy_x2x") == 0) + { + genMemcpyX2X (bi_iCode, nbi_parms, bi_parms, 0); + } + else if (strcmp (bif->name, "__builtin_memcpy_c2x") == 0) + { + genMemcpyX2X (bi_iCode, nbi_parms, bi_parms, 1); + } + else if (strcmp (bif->name, "__builtin_memcmp_x2x") == 0) + { + genMemcmpX2X (bi_iCode, nbi_parms, bi_parms, 0); + } + else if (strcmp (bif->name, "__builtin_memcmp_c2x") == 0) + { + genMemcmpX2X (bi_iCode, nbi_parms, bi_parms, 1); + } + else if (strcmp (bif->name, "__builtin_memset_x") == 0) + { + genMemsetX (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "__builtin_inp") == 0) + { + genInp (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "__builtin_outp") == 0) + { + genOutp (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "__builtin_swapw") == 0) + { + genSwapW (bi_iCode, nbi_parms, bi_parms); + /* JavaNative builtIns */ + } + else if (strcmp (bif->name, "NatLib_LoadByte") == 0) + { + genNatLibLoadPrimitive (bi_iCode, nbi_parms, bi_parms, 1); + } + else if (strcmp (bif->name, "NatLib_LoadShort") == 0) + { + genNatLibLoadPrimitive (bi_iCode, nbi_parms, bi_parms, 2); + } + else if (strcmp (bif->name, "NatLib_LoadInt") == 0) + { + genNatLibLoadPrimitive (bi_iCode, nbi_parms, bi_parms, 4); + } + else if (strcmp (bif->name, "NatLib_LoadPointer") == 0) + { + genNatLibLoadPointer (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "NatLib_InstallImmutableStateBlock") == 0) + { + genNatLibInstallStateBlock (bi_iCode, nbi_parms, bi_parms, "Immutable"); + } + else if (strcmp (bif->name, "NatLib_InstallEphemeralStateBlock") == 0) + { + genNatLibInstallStateBlock (bi_iCode, nbi_parms, bi_parms, "Ephemeral"); + } + else if (strcmp (bif->name, "NatLib_RemoveImmutableStateBlock") == 0) + { + genNatLibRemoveStateBlock (bi_iCode, nbi_parms, "Immutable"); + } + else if (strcmp (bif->name, "NatLib_RemoveEphemeralStateBlock") == 0) + { + genNatLibRemoveStateBlock (bi_iCode, nbi_parms, "Ephemeral"); + } + else if (strcmp (bif->name, "NatLib_GetImmutableStateBlock") == 0) + { + genNatLibGetStateBlock (bi_iCode, nbi_parms, bi_parms, "Immutable"); + } + else if (strcmp (bif->name, "NatLib_GetEphemeralStateBlock") == 0) + { + genNatLibGetStateBlock (bi_iCode, nbi_parms, bi_parms, "Ephemeral"); + } + else if (strcmp (bif->name, "MM_XMalloc") == 0) + { + genMMMalloc (bi_iCode, nbi_parms, bi_parms, 3, "XMalloc"); + } + else if (strcmp (bif->name, "MM_Malloc") == 0) + { + genMMMalloc (bi_iCode, nbi_parms, bi_parms, 2, "Malloc"); + } + else if (strcmp (bif->name, "MM_ApplicationMalloc") == 0) + { + genMMMalloc (bi_iCode, nbi_parms, bi_parms, 2, "ApplicationMalloc"); + } + else if (strcmp (bif->name, "MM_Free") == 0) + { + genMMMalloc (bi_iCode, nbi_parms, bi_parms, 2, "Free"); + } + else if (strcmp (bif->name, "MM_Deref") == 0) + { + genMMDeref (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "MM_UnrestrictedPersist") == 0) + { + genMMUnrestrictedPersist (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_ExecJavaProcess") == 0) + { + genSystemExecJavaProcess (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_GetRTCRegisters") == 0) + { + genSystemRTCRegisters (bi_iCode, nbi_parms, bi_parms, "Get"); + } + else if (strcmp (bif->name, "System_SetRTCRegisters") == 0) + { + genSystemRTCRegisters (bi_iCode, nbi_parms, bi_parms, "Set"); + } + else if (strcmp (bif->name, "System_ThreadSleep") == 0) + { + genSystemThreadSleep (bi_iCode, nbi_parms, bi_parms, "ThreadSleep"); + } + else if (strcmp (bif->name, "System_ThreadSleep_ExitCriticalSection") == 0) + { + genSystemThreadSleep (bi_iCode, nbi_parms, bi_parms, "ThreadSleep_ExitCriticalSection"); + } + else if (strcmp (bif->name, "System_ProcessSleep") == 0) + { + genSystemThreadSleep (bi_iCode, nbi_parms, bi_parms, "ProcessSleep"); + } + else if (strcmp (bif->name, "System_ProcessSleep_ExitCriticalSection") == 0) + { + genSystemThreadSleep (bi_iCode, nbi_parms, bi_parms, "ProcessSleep_ExitCriticalSection"); + } + else if (strcmp (bif->name, "System_ThreadResume") == 0) + { + genSystemThreadResume (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_SaveThread") == 0) + { + genSystemThreadResume (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_ThreadResume") == 0) + { + genSystemThreadResume (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_ProcessResume") == 0) + { + genSystemProcessResume (bi_iCode, nbi_parms, bi_parms); + } + else if (strcmp (bif->name, "System_SaveJavaThreadState") == 0) + { + genSystem (bi_iCode, nbi_parms, "SaveJavaThreadState"); + } + else if (strcmp (bif->name, "System_RestoreJavaThreadState") == 0) + { + genSystem (bi_iCode, nbi_parms, "RestoreJavaThreadState"); + } + else if (strcmp (bif->name, "System_ProcessYield") == 0) + { + genSystem (bi_iCode, nbi_parms, "ProcessYield"); + } + else if (strcmp (bif->name, "System_ProcessSuspend") == 0) + { + genSystem (bi_iCode, nbi_parms, "ProcessSuspend"); + } + else if (strcmp (bif->name, "System_RegisterPoll") == 0) + { + genSystemPoll (bi_iCode, nbi_parms, bi_parms, "Register"); + } + else if (strcmp (bif->name, "System_RemovePoll") == 0) + { + genSystemPoll (bi_iCode, nbi_parms, bi_parms, "Remove"); + } + else if (strcmp (bif->name, "System_GetCurrentThreadId") == 0) + { + genSystemGetCurrentID (bi_iCode, nbi_parms, bi_parms, "Thread"); + } + else if (strcmp (bif->name, "System_GetCurrentProcessId") == 0) + { + genSystemGetCurrentID (bi_iCode, nbi_parms, bi_parms, "Process"); + } + else + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unknown builtin function encountered\n"); + return; + } + return; +} + +/*-----------------------------------------------------------------*/ +/* gen390Code - generate code for Dallas 390 based controllers */ +/*-----------------------------------------------------------------*/ +void +gen390Code (iCode * lic) +{ + iCode *ic; + int cln = 0; + + _G.currentFunc = NULL; + + dptrn[1][0] = "dpl1"; + dptrn[1][1] = "dph1"; + dptrn[1][2] = "dpx1"; + + if (options.model == MODEL_FLAT24) + { + fReturnSizeDS390 = 5; + fReturn = fReturn24; + } + else + { + fReturnSizeDS390 = 4; + fReturn = fReturn16; + options.stack10bit = 0; + } + /* print the allocation information */ + if (allocInfo && currFunc) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + /* stack pointer name */ + if (options.useXstack) + spname = "_spx"; + else + spname = "sp"; + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + if (!options.noCcodeInAsm) + { + emitcode (";", "%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + } + cln = ic->lineno; + } + if (options.iCodeInAsm) + { + const char *iLine; + iLine = printILine (ic); + emitcode (";", "ic:%d: %s", ic->key, iLine); + dbuf_free (iLine); + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + { + iCode *ifxIc, *popIc; + bool CommonRegs = FALSE; + + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop (or several) then the if statement might + be using some of the registers being popped which + would destroy the contents of the register so + we need to check for this condition and handle it */ + for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next); + for (popIc = ic; popIc && popIc->op == IPOP; popIc = popIc->next) + CommonRegs |= (ifxIc && ifxIc->op == IFX && !ifxIc->generated && regsInCommon (IC_LEFT (popIc), IC_COND (ifxIc))); + if (CommonRegs) + genIfx (ifxIc, ic); + else + genIpop (ic); + } + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + in decorateType() in SDCCast.c */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + genGetHbit (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInc (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic))))); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInc (IC_RESULT (ic), ic, getSize (operandType (IC_RIGHT (ic))))); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + if (ic->builtinSEND) + genBuiltIn (ic); + else + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + case SWAP: + genSwap (ic); + break; + + default: + /* This should never happen, right? */ + fprintf (stderr, "*** Probable error: unsupported op 0x%x (%c) in %s @ %d\n", ic->op, ic->op, __FILE__, __LINE__); + ic = ic; + } + } + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} diff --git a/src/ds390/gen.h b/src/ds390/gen.h new file mode 100644 index 0000000..c680715 --- /dev/null +++ b/src/ds390/gen.h @@ -0,0 +1,83 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for DS80C390 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef SDCCGEN390_H +#define SDCCGEN390_H + +enum +{ + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_DPTR, AOP_DPTR2, AOP_R0, AOP_R1, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, AOP_ACC, AOP_DPTRn, AOP_DUMMY +}; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop +{ + + short type; /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_DPTR - dptr contains address of operand + AOP_DPTR2 - dptr2 contains address of operand (DS80C390 only). + AOP_R0/R1 - r0/r1 contains address of operand + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_ACC - result is in the acc:b pair + AOP_DPTRn - is in dptr(n) + AOP_DUMMY - read as 0, discard writes + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned short allocated; /* number of times allocated */ + union + { + short dptr; /* if AOP_DPTRn */ + value *aop_lit; /* if literal */ + reg_info *aop_reg[8]; /* array of registers */ + char *aop_dir; /* if direct */ + reg_info *aop_ptr; /* either -> to r0 or r1 */ + struct + { + int from_cast_remat; /* cast remat created this : immd2 field used for highest order */ + char *aop_immd1; /* if immediate others are implied */ + char *aop_immd2; /* cast remat will generate this */ + } aop_immd; + int aop_stk; /* stack offset when AOP_STK */ + char *aop_str[5]; /* just a string array containing the location */ + } + aopu; +} +asmop; + +void gen390Code (iCode *); +void ds390_emitDebuggerSymbol (const char *); + +#endif diff --git a/src/ds390/main.c b/src/ds390/main.c new file mode 100644 index 0000000..2d7708b --- /dev/null +++ b/src/ds390/main.c @@ -0,0 +1,1710 @@ +/*------------------------------------------------------------------------- + main.h - ds390 specific general functions + + Copyright (C) 2000, Kevin Vigor + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ +/* + Note that mlh prepended _ds390_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ + +#include "common.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +static char _defaultRules[] = +{ +#include "peeph.rul" +}; + +#define OPTION_STACK_8BIT "--stack-8bit" +#define OPTION_FLAT24_MODEL "--model-flat24" +#define OPTION_STACK_SIZE "--stack-size" + +static OPTION _ds390_options[] = + { + { 0, OPTION_FLAT24_MODEL, NULL, "use the flat24 model for the ds390 (default)" }, + { 0, OPTION_STACK_8BIT, NULL, "use the 8bit stack for the ds390 (not supported yet)" }, + { 0, OPTION_STACK_SIZE, &options.stack_size, "Tells the linker to allocate this space for stack", CLAT_INTEGER }, + { 0, "--pack-iram", NULL, "Tells the linker to pack variables in internal ram (default)"}, + { 0, "--no-pack-iram", &options.no_pack_iram, "Deprecated: Tells the linker not to pack variables in internal ram"}, + { 0, "--stack-10bit", &options.stack10bit, "use the 10bit stack for ds390 (default)" }, + { 0, "--use-accelerator", &options.useAccelerator, "generate code for ds390 arithmetic accelerator"}, + { 0, "--protect-sp-update", &options.protect_sp_update, "will disable interrupts during ESP:SP updates"}, + { 0, "--parms-in-bank1", &options.parms_in_bank1, "use Bank1 for parameter passing"}, + { 0, NULL } + }; + + +/* list of key words used by msc51 */ +static char *_ds390_keywords[] = +{ + "at", + "bit", + "code", + "critical", + "data", + "far", + "idata", + "interrupt", + "near", + "pdata", + "reentrant", + "sfr", + "sfr16", + "sfr32", + "sbit", + "using", + "xdata", + "_data", + "_code", + "_generic", + "_near", + "_xdata", + "_pdata", + "_idata", + "_naked", + NULL +}; + +static builtins __ds390_builtins[] = { + { "__builtin_memcpy_x2x","v",3,{"cx*","cx*","i"}}, /* void __builtin_memcpy_x2x (xdata char *,xdata char *,int) */ + { "__builtin_memcpy_c2x","v",3,{"cx*","cp*","i"}}, /* void __builtin_memcpy_c2x (xdata char *,code char *,int) */ + { "__builtin_memset_x","v",3,{"cx*","c","i"}}, /* void __builtin_memset (xdata char *,char,int) */ + /* __builtin_inp - used to read from a memory mapped port, increment first pointer */ + { "__builtin_inp","v",3,{"cx*","cx*","i"}}, /* void __builtin_inp (xdata char *,xdata char *,int) */ + /* __builtin_inp - used to write to a memory mapped port, increment first pointer */ + { "__builtin_outp","v",3,{"cx*","cx*","i"}}, /* void __builtin_outp (xdata char *,xdata char *,int) */ + { "__builtin_swapw","Us",1,{"Us"}}, /* unsigned short __builtin_swapw (unsigned short) */ + { "__builtin_memcmp_x2x","c",3,{"cx*","cx*","i"}}, /* void __builtin_memcmp_x2x (xdata char *,xdata char *,int) */ + { "__builtin_memcmp_c2x","c",3,{"cx*","cp*","i"}}, /* void __builtin_memcmp_c2x (xdata char *,code char *,int) */ + { NULL , NULL,0, {NULL}} /* mark end of table */ +}; +void ds390_assignRegisters (ebbIndex * ebbi); + +static int regParmFlg = 0; /* determine if we can register a parameter */ + +static void +_ds390_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_ds390_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; +} + +static int +_ds390_regparm (sym_link * l, bool reentrant) +{ + if (IS_SPEC(l) && (SPEC_NOUN(l) == V_BIT)) + return 0; + if (options.parms_in_bank1 == 0) { + /* simple can pass only the first parameter in a register */ + if (regParmFlg) + return 0; + + regParmFlg = 1; + return 1; + } else { + int size = getSize(l); + int remain ; + + /* first one goes the usual way to DPTR */ + if (regParmFlg == 0) { + regParmFlg += 4 ; + return 1; + } + /* second one onwards goes to RB1_0 thru RB1_7 */ + remain = regParmFlg - 4; + if (size > (8 - remain)) { + regParmFlg = 12 ; + return 0; + } + regParmFlg += size ; + return regParmFlg - size + 1; + } +} + +static bool +_ds390_parseOptions (int *pargc, char **argv, int *i) +{ + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + if (!strcmp (argv[*i], OPTION_STACK_8BIT)) + { + options.stack10bit = 0; + return TRUE; + } + else if (!strcmp (argv[*i], OPTION_FLAT24_MODEL)) + { + options.model = MODEL_FLAT24; + return TRUE; + } + return FALSE; +} + +static void +_ds390_finaliseOptions (void) +{ + if (options.noXinitOpt) + { + port->genXINIT=0; + } + + /* Hack-o-matic: if we are using the flat24 model, + * adjust pointer sizes. + */ + if (options.model != MODEL_FLAT24) + { + fprintf (stderr, + "*** warning: ds390 port small and large model experimental.\n"); + if (options.model == MODEL_LARGE) + { + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + } + else + { + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + } + } + else + { + port->s.far_ptr_size = 3; + port->s.funcptr_size = 3; + port->s.ptr_size = 4; + + port->stack.isr_overhead += 2; /* Will save dpx on ISR entry. */ + + port->stack.call_overhead += 2; /* This acounts for the extra byte + * of return addres on the stack. + * but is ugly. There must be a + * better way. + */ + + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + + if (!options.stack10bit) + { + fprintf (stderr, + "*** error: ds390 port only supports the 10 bit stack mode.\n"); + } + else + { + if (!options.stack_loc) options.stack_loc = 0x400008; + } + + /* Fixup the memory map for the stack; it is now in + * far space and requires an FPOINTER to access it. + */ + istack->fmap = 1; + istack->ptrType = FPOINTER; + + } /* MODEL_FLAT24 */ +} + +static void +_ds390_setDefaultOptions (void) +{ + options.model=MODEL_FLAT24; + options.stack10bit=1; +} + +static const char * +_ds390_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +extern char * iComments2; + +static void +_ds390_genAssemblerPreamble (FILE * of) +{ + fputs (iComments2, of); + fputs ("; CPU specific extensions\n",of); + fputs (iComments2, of); + + fputs ("\t.DS80C390\n", of); + + if (options.model == MODEL_FLAT24) + fputs ("\t.amode\t2\t; 24 bit flat addressing\n", of); + + fputs ("dpl\t=\t0x82\n", of); + fputs ("dph\t=\t0x83\n", of); + fputs ("dpl1\t=\t0x84\n", of); + fputs ("dph1\t=\t0x85\n", of); + fputs ("dps\t=\t0x86\n", of); + fputs ("dpx\t=\t0x93\n", of); + fputs ("dpx1\t=\t0x95\n", of); + fputs ("esp\t=\t0x9B\n", of); + fputs ("ap\t=\t0x9C\n", of); + fputs ("acc1\t=\t0x9C\n", of); + fputs ("mcnt0\t=\t0xD1\n", of); + fputs ("mcnt1\t=\t0xD2\n", of); + fputs ("ma\t=\t0xD3\n", of); + fputs ("mb\t=\t0xD4\n", of); + fputs ("mc\t=\t0xD5\n", of); + fputs ("acon\t=\t0x9D\n", of); + fputs ("mcon\t=\t0xC6\n", of); + fputs ("F1\t=\t0xD1\t; user flag\n", of); + if (options.parms_in_bank1) + { + int i ; + for (i=0; i < 8 ; i++ ) + fprintf (of,"b1_%d\t=\t0x%02X\n",i,8+i); + } +} + +/* Generate interrupt vector table. */ +static int +_ds390_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + int i; + + if (options.model != MODEL_FLAT24) + { + dbuf_printf (oBuf, "\tljmp\t__sdcc_gsinit_startup\n"); + + /* now for the other interrupts */ + for (i = 0; i < maxInterrupts; i++) + { + if (interrupts[i]) + { + dbuf_printf (oBuf, "\tljmp\t%s\n", interrupts[i]->rname); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t5\n"); + } + else + { + dbuf_printf (oBuf, "\treti\n"); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t7\n"); + } + } + } + else + { + dbuf_printf (oBuf, "\t.amode\t0\t; 16 bit addressing\n"); + dbuf_printf (oBuf, "\tljmp\t__reset_vect\n"); + dbuf_printf (oBuf, "\t.amode\t2\t; 24 bit flat addressing\n"); + + /* now for the other interrupts */ + for (i = 0; i < maxInterrupts; i++) + { + if (interrupts[i]) + { + dbuf_printf (oBuf, "\tljmp\t%s\n\t.ds\t4\n", interrupts[i]->rname); + } + else + { + dbuf_printf (oBuf, "\treti\n\t.ds\t7\n"); + } + } + + dbuf_printf (oBuf, "__reset_vect:\n"); + if (options.stack10bit) + { + dbuf_printf (oBuf, "\tmov _TA,#0xAA\n"); + dbuf_printf (oBuf, "\tmov _TA,#0x55\n"); + dbuf_printf (oBuf, "\tmov acon,#0x06\t;24 bit addresses, 10 bit stack\n"); + dbuf_printf (oBuf, "\tmov _TA,#0xAA\n"); + dbuf_printf (oBuf, "\tmov _TA,#0x55\n"); + dbuf_printf (oBuf, "\tmov mcon,#0x90\t;10 bit stack at 0x400000\n"); + dbuf_printf (oBuf, "\tmov _ESP,#0x00\t; reinitialize the stack\n"); + dbuf_printf (oBuf, "\tmov _SP,#0x00\n"); + } + else + { + dbuf_printf (oBuf, "\tmov _TA,#0xAA\n"); + dbuf_printf (oBuf, "\tmov _TA,#0x55\n"); + dbuf_printf (oBuf, "\tmov acon,#0x02\t;24 bit addresses, default 8 bit stack\n"); + } + dbuf_printf (oBuf, "\tljmp\t__sdcc_gsinit_startup\n"); + } + return TRUE; +} + +static void +_ds390_genInitStartup (FILE *of) +{ + fprintf (of, "__sdcc_gsinit_startup:\n"); + /* if external stack is specified then the + higher order byte of the xdata location is + going into P2 and the lower order going into + spx */ + if (options.useXstack) + { + fprintf (of, "\tmov\tP2,#0x%02x\n", + (((unsigned int) options.xdata_loc) >> 8) & 0xff); + fprintf (of, "\tmov\t_spx,#0x%02x\n", + (unsigned int) options.xdata_loc & 0xff); + } + + // This should probably be a port option, but I'm being lazy. + // on the 400, the firmware boot loader gives us a valid stack + // (see '400 data sheet pg. 85 (TINI400 ROM Initialization code) + if (!TARGET_IS_DS400 && !options.stack10bit) + { + /* initialise the stack pointer. JCF: sdld takes care of the location */ + fprintf (of, "\tmov\tsp,#__start__stack - 1\n"); /* MOF */ + } + + fprintf (of, "\tlcall\t__sdcc_external_startup\n"); + fprintf (of, "\tmov\ta,dpl\n"); + fprintf (of, "\tjz\t__sdcc_init_data\n"); + fprintf (of, "\tljmp\t__sdcc_program_startup\n"); + fprintf (of, "__sdcc_init_data:\n"); + + // if the port can copy the XINIT segment to XISEG + if (port->genXINIT) + { + port->genXINIT(of); + } +} + +/* Generate code to copy XINIT to XISEG */ +static void _ds390_genXINIT (FILE * of) +{ + fprintf (of, "; _ds390_genXINIT() start\n"); + fprintf (of, " mov a,#l_XINIT\n"); + fprintf (of, " orl a,#l_XINIT>>8\n"); + fprintf (of, " jz 00003$\n"); + fprintf (of, " mov a,#s_XINIT\n"); + fprintf (of, " add a,#l_XINIT\n"); + fprintf (of, " mov r1,a\n"); + fprintf (of, " mov a,#s_XINIT>>8\n"); + fprintf (of, " addc a,#l_XINIT>>8\n"); + fprintf (of, " mov r2,a\n"); + fprintf (of, " mov dptr,#s_XINIT\n"); + fprintf (of, " mov dps,#0x21\n"); + fprintf (of, " mov dptr,#s_XISEG\n"); + fprintf (of, "00001$: clr a\n"); + fprintf (of, " movc a,@a+dptr\n"); + fprintf (of, " movx @dptr,a\n"); + fprintf (of, " inc dptr\n"); + fprintf (of, " inc dptr\n"); + fprintf (of, "00002$: mov a,dpl\n"); + fprintf (of, " cjne a,ar1,00001$\n"); + fprintf (of, " mov a,dph\n"); + fprintf (of, " cjne a,ar2,00001$\n"); + fprintf (of, " mov dps,#0\n"); + fprintf (of, "00003$:\n"); + fprintf (of, "; _ds390_genXINIT() end\n"); + + fprintf (of, "; _ds390_genXRAMCLEAR() start\n"); + fprintf (of, " mov r0,#l_PSEG\n"); + fprintf (of, " mov a,r0\n"); + fprintf (of, " orl a,#(l_PSEG >> 8)\n"); + fprintf (of, " jz 00006$\n"); + fprintf (of, " mov r1,#s_PSEG\n"); + fprintf (of, " mov _P2,#(s_PSEG >> 8)\n"); + fprintf (of, " clr a\n"); + fprintf (of, "00005$: movx @r1,a\n"); + fprintf (of, " inc r1\n"); + fprintf (of, " djnz r0,00005$\n"); + fprintf (of, "00006$: mov r0,#l_XSEG\n"); + fprintf (of, " mov a,r0\n"); + fprintf (of, " orl a,#(l_XSEG >> 8)\n"); + fprintf (of, " jz 00008$\n"); + fprintf (of, " mov r1,#((l_XSEG + 255) >> 8)\n"); + fprintf (of, " mov dptr,#s_XSEG\n"); + fprintf (of, " clr a\n"); + fprintf (of, "00007$: movx @dptr,a\n"); + fprintf (of, " inc dptr\n"); + fprintf (of, " djnz r0,00007$\n"); + fprintf (of, " djnz r1,00007$\n"); + fprintf (of, "00008$:\n"); + fprintf (of, "; _ds390_genXRAMCLEAR() end\n"); +} + +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ + operand *result = IC_RESULT(ic); + //operand *right = IC_RIGHT(ic); + //operand *left = IC_LEFT(ic); + sym_link *result_type = operandType(result); + //sym_link *right_type = (right ? operandType(right) : 0); + //sym_link *left_type = (left ? operandType(left) : 0); + + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + /* if bitwise | add & subtract then no since mcs51 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } + + /* for others it is cheaper to do the cse */ + return 1; +} + +bool _ds390_nativeMulCheck(iCode *ic, sym_link *left, sym_link *right) +{ + return + getSize (left) == 1 && getSize (right) == 1 || + options.useAccelerator && getSize (left) == 2 && getSize (right) == 2; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETHBIT + || (op == SWAP && size <= 2) + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +static int +instructionSize(char *inst, char *op1, char *op2) +{ + int isflat24 = (options.model == MODEL_FLAT24); + + #define ISINST(s) (strncmp(inst, (s), sizeof(s)-1) == 0) + #define IS_A(s) (*(s) == 'a' && *(s+1) == '\0') + #define IS_C(s) (*(s) == 'c' && *(s+1) == '\0') + #define IS_Rn(s) (*(s) == 'r' && *(s+1) >= '0' && *(s+1) <= '7') + #define IS_atRi(s) (*(s) == '@' && *(s+1) == 'r') + + /* Based on the current (2003-08-22) code generation for the + small library, the top instruction probability is: + + 57% mov/movx/movc + 6% push + 6% pop + 4% inc + 4% lcall + 4% add + 3% clr + 2% subb + */ + /* mov, push, & pop are the 69% of the cases. Check them first! */ + if (ISINST ("mov")) + { + if (*(inst+3)=='x') return 1; /* movx */ + if (*(inst+3)=='c') return 1; /* movc */ + if (IS_C (op1) || IS_C (op2)) return 2; + if (IS_A (op1)) + { + if (IS_Rn (op2) || IS_atRi (op2)) return 1; + return 2; + } + if (IS_Rn(op1) || IS_atRi(op1)) + { + if (IS_A(op2)) return 1; + return 2; + } + if (strcmp (op1, "dptr") == 0) return 3+isflat24; + if (IS_A (op2) || IS_Rn (op2) || IS_atRi (op2)) return 2; + return 3; + } + + if (ISINST ("push")) return 2; + if (ISINST ("pop")) return 2; + + if (ISINST ("lcall")) return 3+isflat24; + if (ISINST ("ret")) return 1; + if (ISINST ("ljmp")) return 3+isflat24; + if (ISINST ("sjmp")) return 2; + if (ISINST ("rlc")) return 1; + if (ISINST ("rrc")) return 1; + if (ISINST ("rl")) return 1; + if (ISINST ("rr")) return 1; + if (ISINST ("swap")) return 1; + if (ISINST ("jc")) return 2; + if (ISINST ("jnc")) return 2; + if (ISINST ("jb")) return 3; + if (ISINST ("jnb")) return 3; + if (ISINST ("jbc")) return 3; + if (ISINST ("jmp")) return 1; // always jmp @a+dptr + if (ISINST ("jz")) return 2; + if (ISINST ("jnz")) return 2; + if (ISINST ("cjne")) return 3; + if (ISINST ("mul")) return 1; + if (ISINST ("div")) return 1; + if (ISINST ("da")) return 1; + if (ISINST ("xchd")) return 1; + if (ISINST ("reti")) return 1; + if (ISINST ("nop")) return 1; + if (ISINST ("acall")) return 2+isflat24; + if (ISINST ("ajmp")) return 2+isflat24; + + + if (ISINST ("add") || ISINST ("addc") || ISINST ("subb") || ISINST ("xch")) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + if (ISINST ("inc") || ISINST ("dec")) + { + if (IS_A(op1) || IS_Rn(op1) || IS_atRi(op1)) return 1; + if (strcmp(op1, "dptr") == 0) return 1; + return 2; + } + if (ISINST ("anl") || ISINST ("orl") || ISINST ("xrl")) + { + if (IS_C(op1)) return 2; + if (IS_A(op1)) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + else + { + if (IS_A(op2)) return 2; + return 3; + } + } + if (ISINST ("clr") || ISINST ("setb") || ISINST ("cpl")) + { + if (IS_A(op1) || IS_C(op1)) return 1; + return 2; + } + if (ISINST ("djnz")) + { + if (IS_Rn(op1)) return 2; + return 3; + } + + /* If the instruction is unrecognized, we shouldn't try to optimize. */ + /* Return a large value to discourage optimization. */ + return 999; +} + +asmLineNode * +ds390newAsmLineNode (int currentDPS) +{ + asmLineNode *aln; + + aln = Safe_alloc ( sizeof (asmLineNode)); + aln->size = 0; + aln->regsRead = NULL; + aln->regsWritten = NULL; + aln->initialized = 0; + aln->currentDPS = currentDPS; + + return aln; +} + + +typedef struct ds390operanddata + { + char name[6]; + int regIdx1; + int regIdx2; + } +ds390operanddata; + +static ds390operanddata ds390operandDataTable[] = + { + {"acc1", AP_IDX, -1}, + {"a", A_IDX, -1}, + {"ab", A_IDX, B_IDX}, + {"ac", CND_IDX, -1}, + {"ap", AP_IDX, -1}, + {"acc", A_IDX, -1}, + {"ar0", R0_IDX, -1}, + {"ar1", R1_IDX, -1}, + {"ar2", R2_IDX, -1}, + {"ar3", R3_IDX, -1}, + {"ar4", R4_IDX, -1}, + {"ar5", R5_IDX, -1}, + {"ar6", R6_IDX, -1}, + {"ar7", R7_IDX, -1}, + {"b", B_IDX, -1}, + {"c", CND_IDX, -1}, + {"cy", CND_IDX, -1}, + {"dph", DPH_IDX, -1}, + {"dph0", DPH_IDX, -1}, + {"dph1", DPH1_IDX, -1}, + {"dpl", DPL_IDX, -1}, + {"dpl0", DPL_IDX, -1}, + {"dpl1", DPL1_IDX, -1}, +/* {"dptr", DPL_IDX, DPH_IDX}, */ /* dptr is special, based on currentDPS */ + {"dps", DPS_IDX, -1}, + {"dpx", DPX_IDX, -1}, + {"dpx0", DPX_IDX, -1}, + {"dpx1", DPX1_IDX, -1}, + {"f0", CND_IDX, -1}, + {"f1", CND_IDX, -1}, + {"ov", CND_IDX, -1}, + {"p", CND_IDX, -1}, + {"psw", CND_IDX, -1}, + {"r0", R0_IDX, -1}, + {"r1", R1_IDX, -1}, + {"r2", R2_IDX, -1}, + {"r3", R3_IDX, -1}, + {"r4", R4_IDX, -1}, + {"r5", R5_IDX, -1}, + {"r6", R6_IDX, -1}, + {"r7", R7_IDX, -1}, + }; + +static int +ds390operandCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((ds390operanddata *)member)->name); +} + +static void +updateOpRW (asmLineNode *aln, char *op, char *optype, int currentDPS) +{ + ds390operanddata *opdat; + char *dot; + int regIdx1 = -1; + int regIdx2 = -1; + int regIdx3 = -1; + + dot = strchr(op, '.'); + if (dot) + *dot = '\0'; + + opdat = bsearch (op, ds390operandDataTable, + sizeof(ds390operandDataTable)/sizeof(ds390operanddata), + sizeof(ds390operanddata), ds390operandCompare); + + if (opdat) + { + regIdx1 = opdat->regIdx1; + regIdx2 = opdat->regIdx2; + } + if (!strcmp(op, "dptr")) + { + if (!currentDPS) + { + regIdx1 = DPL_IDX; + regIdx2 = DPH_IDX; + regIdx3 = DPX_IDX; + } + else + { + regIdx1 = DPL1_IDX; + regIdx2 = DPH1_IDX; + regIdx3 = DPX1_IDX; + } + } + + if (strchr(optype,'r')) + { + if (regIdx1 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, regIdx1); + if (regIdx2 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, regIdx2); + if (regIdx3 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, regIdx3); + } + if (strchr(optype,'w')) + { + if (regIdx1 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, regIdx1); + if (regIdx2 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, regIdx2); + if (regIdx3 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, regIdx3); + } + if (op[0] == '@') + { + if (!strcmp(op, "@r0")) + aln->regsRead = bitVectSetBit (aln->regsRead, R0_IDX); + if (!strcmp(op, "@r1")) + aln->regsRead = bitVectSetBit (aln->regsRead, R1_IDX); + if (strstr(op, "dptr")) + { + if (!currentDPS) + { + aln->regsRead = bitVectSetBit (aln->regsRead, DPL_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPH_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPX_IDX); + } + else + { + aln->regsRead = bitVectSetBit (aln->regsRead, DPL1_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPH1_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPX1_IDX); + } + } + if (strstr(op, "a+")) + aln->regsRead = bitVectSetBit (aln->regsRead, A_IDX); + } +} + +typedef struct ds390opcodedata + { + char name[6]; + char class[3]; + char pswtype[3]; + char op1type[3]; + char op2type[3]; + } +ds390opcodedata; + +static ds390opcodedata ds390opcodeDataTable[] = + { + {"acall","j", "", "", ""}, + {"add", "", "w", "rw", "r"}, + {"addc", "", "rw", "rw", "r"}, + {"ajmp", "j", "", "", ""}, + {"anl", "", "", "rw", "r"}, + {"cjne", "j", "w", "r", "r"}, + {"clr", "", "", "w", ""}, + {"cpl", "", "", "rw", ""}, + {"da", "", "rw", "rw", ""}, + {"dec", "", "", "rw", ""}, + {"div", "", "w", "rw", ""}, + {"djnz", "j", "", "rw", ""}, + {"inc", "", "", "rw", ""}, + {"jb", "j", "", "r", ""}, + {"jbc", "j", "", "rw", ""}, + {"jc", "j", "", "", ""}, + {"jmp", "j", "", "", ""}, + {"jnb", "j", "", "r", ""}, + {"jnc", "j", "", "", ""}, + {"jnz", "j", "", "", ""}, + {"jz", "j", "", "", ""}, + {"lcall","j", "", "", ""}, + {"ljmp", "j", "", "", ""}, + {"mov", "", "", "w", "r"}, + {"movc", "", "", "w", "r"}, + {"movx", "", "", "w", "r"}, + {"mul", "", "w", "rw", ""}, + {"nop", "", "", "", ""}, + {"orl", "", "", "rw", "r"}, + {"pop", "", "", "w", ""}, + {"push", "", "", "r", ""}, + {"ret", "j", "", "", ""}, + {"reti", "j", "", "", ""}, + {"rl", "", "", "rw", ""}, + {"rlc", "", "rw", "rw", ""}, + {"rr", "", "", "rw", ""}, + {"rrc", "", "rw", "rw", ""}, + {"setb", "", "", "w", ""}, + {"sjmp", "j", "", "", ""}, + {"subb", "", "rw", "rw", "r"}, + {"swap", "", "", "rw", ""}, + {"xch", "", "", "rw", "rw"}, + {"xchd", "", "", "rw", "rw"}, + {"xrl", "", "", "rw", "r"}, + }; + +static int +ds390opcodeCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((ds390opcodedata *)member)->name); +} + +static asmLineNode * +asmLineNodeFromLineNode (lineNode *ln, int currentDPS) +{ + asmLineNode *aln = ds390newAsmLineNode(currentDPS); + char *op, op1[256], op2[256]; + int opsize; + const char *p; + char inst[8]; + ds390opcodedata *opdat; + + aln->initialized = 1; + + p = ln->line; + + while (*p && isspace(*p)) p++; + for (op = inst, opsize=1; *p; p++) + { + if (isspace(*p) || *p == ';' || *p == ':' || *p == '=') + break; + else + if (opsize < sizeof(inst)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ';' || *p == ':' || *p == '=') + return aln; + + while (*p && isspace(*p)) p++; + if (*p == '=') + return aln; + + for (op = op1, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op1)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ',') p++; + for (op = op2, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op2)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + aln->size = instructionSize(inst, op1, op2); + + aln->regsRead = newBitVect (END_IDX); + aln->regsWritten = newBitVect (END_IDX); + + opdat = bsearch (inst, ds390opcodeDataTable, + sizeof(ds390opcodeDataTable)/sizeof(ds390opcodedata), + sizeof(ds390opcodedata), ds390opcodeCompare); + + if (opdat) + { + updateOpRW (aln, op1, opdat->op1type, currentDPS); + updateOpRW (aln, op2, opdat->op2type, currentDPS); + if (strchr(opdat->pswtype,'r')) + aln->regsRead = bitVectSetBit (aln->regsRead, CND_IDX); + if (strchr(opdat->pswtype,'w')) + aln->regsWritten = bitVectSetBit (aln->regsWritten, CND_IDX); + } + + return aln; +} + +static void +initializeAsmLineNode (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line, 0); + else if (line->aln && !((asmLineNode *)line->aln)->initialized) + { + int currentDPS = ((asmLineNode *)line->aln)->currentDPS; + free(line->aln); + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line, currentDPS); + } +} + +static int +getInstructionSize (lineNode *line) +{ + initializeAsmLineNode (line); + return line->aln->size; +} + +static bitVect * +getRegsRead (lineNode *line) +{ + initializeAsmLineNode (line); + return line->aln->regsRead; +} + +static bitVect * +getRegsWritten (lineNode *line) +{ + initializeAsmLineNode (line); + return line->aln->regsWritten; +} + +static const char * +get_model (void) +{ + switch (options.model) + { + case MODEL_SMALL: + if (options.stackAuto) + return "small-stack-auto"; + else + return "small"; + + case MODEL_LARGE: + if (options.stackAuto) + return "large-stack-auto"; + else + return "large"; + + case MODEL_FLAT24: + return port->target; + + default: + werror (W_UNKNOWN_MODEL, __FILE__, __LINE__); + return "unknown"; + } +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdld", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = +{ + "sdas390", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char * const _libs_ds390[] = { STD_DS390_LIB, NULL, }; + +/* Globals */ +PORT ds390_port = +{ + TARGET_ID_DS390, + "ds390", + "DS80C390", /* Target name */ + NULL, + { + glue, + TRUE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE | MODEL_FLAT24, + MODEL_SMALL, + get_model, + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_ds390, /* libs */ + }, + { + _defaultRules, + getInstructionSize, + getRegsRead, + getRegsWritten, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, bit, float */ + { 1, 2, 2, 4, 8, 1, 2, 3, 2, 3, 1, 4 }, + + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + + { + "XSEG (XDATA)", + "STACK (DATA)", + "CSEG (CODE)", + "DSEG (DATA)", + "ISEG (DATA)", + "PSEG (PAG,XDATA)", + "XSEG (XDATA)", + "BSEG (BIT)", + "RSEG (DATA)", + "GSINIT (CODE)", + "OSEG (OVR,DATA)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG (XDATA)", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { NULL, NULL }, + { +1, 1, 4, 1, 1, 0, 0 }, + /* ds390 has an 16 bit mul & div */ + { -1, FALSE }, + { ds390_emitDebuggerSymbol }, + { + 255/4, /* maxCount */ + 4, /* sizeofElement */ + {8,12,20}, /* sizeofMatchJump[] */ + {10,14,22}, /* sizeofRangeCompare[] */ + 4, /* sizeofSubtract */ + 7, /* sizeofDispatch */ + }, + "_", + _ds390_init, + _ds390_parseOptions, + _ds390_options, + NULL, + _ds390_finaliseOptions, + _ds390_setDefaultOptions, + ds390_assignRegisters, + _ds390_getRegName, + 0, + NULL, + _ds390_keywords, + _ds390_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _ds390_genIVT, + _ds390_genXINIT, + _ds390_genInitStartup, + _ds390_reset_regparm, + _ds390_regparm, + NULL, + NULL, + _ds390_nativeMulCheck, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + __ds390_builtins, /* table of builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local static allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/*---------------------------------------------------------------------------------*/ +/* TININative specific */ +/*---------------------------------------------------------------------------------*/ + +#define OPTION_TINI_LIBID "--tini-libid" + +static OPTION _tininative_options[] = + { + { 0, OPTION_FLAT24_MODEL, NULL, "use the flat24 model for the ds390 (default)" }, + { 0, OPTION_STACK_8BIT, NULL, "use the 8bit stack for the ds390 (not supported yet)" }, + { 0, OPTION_STACK_SIZE, &options.stack_size, "Tells the linker to allocate this space for stack", CLAT_INTEGER }, + { 0, "--pack-iram", NULL, "Tells the linker to pack variables in internal ram (default)"}, + { 0, "--no-pack-iram", &options.no_pack_iram, "Deprecated: Tells the linker not to pack variables in internal ram"}, + { 0, "--stack-10bit", &options.stack10bit, "use the 10bit stack for ds390 (default)" }, + { 0, "--use-accelerator", &options.useAccelerator, "generate code for ds390 arithmetic accelerator"}, + { 0, "--protect-sp-update", &options.protect_sp_update, "will disable interrupts during ESP:SP updates"}, + { 0, "--parms-in-bank1", &options.parms_in_bank1, "use Bank1 for parameter passing"}, + { 0, OPTION_TINI_LIBID, &options.tini_libid, " LibraryID used in -mTININative", CLAT_INTEGER }, + { 0, NULL } + }; + +static void _tininative_init (void) +{ + asm_addTree (&asm_a390_mapping); +} + +static void _tininative_setDefaultOptions (void) +{ + options.model=MODEL_FLAT24; + options.stack10bit=1; + options.stackAuto = 1; +} + +static void _tininative_finaliseOptions (void) +{ + /* Hack-o-matic: if we are using the flat24 model, + * adjust pointer sizes. + */ + if (options.model != MODEL_FLAT24) { + options.model = MODEL_FLAT24 ; + fprintf(stderr,"TININative supports only MODEL FLAT24\n"); + } + port->s.far_ptr_size = 3; + port->s.funcptr_size = 3; + port->s.ptr_size = 4; + + port->stack.isr_overhead += 2; /* Will save dpx on ISR entry. */ + + port->stack.call_overhead += 2; /* This acounts for the extra byte + * of return address on the stack. + * but is ugly. There must be a + * better way. + */ + + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + + if (!options.stack10bit) { + options.stack10bit = 1; + fprintf(stderr,"TININative supports only stack10bit \n"); + } + + if (!options.stack_loc) options.stack_loc = 0x400008; + + /* Fixup the memory map for the stack; it is now in + * far space and requires a FPOINTER to access it. + */ + istack->fmap = 1; + istack->ptrType = FPOINTER; + options.cc_only =1; +} + +static int _tininative_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + return TRUE; +} + +static void _tininative_genAssemblerPreamble (FILE * of) +{ + fputs("$include(tini.inc)\n", of); + fputs("$include(ds80c390.inc)\n", of); + fputs("$include(tinimacro.inc)\n", of); + fputs("$include(apiequ.inc)\n", of); + fputs("_bpx EQU 01Eh \t\t; _bpx (frame pointer) mapped to R7_B3:R6_B3\n", of); + fputs("acc1 EQU 01Dh \t\t; acc1 mapped to R5_B3\n", of); + /* Must be first and return 0 */ + fputs("Lib_Native_Init:\n",of); + fputs("\tclr\ta\n",of); + fputs("\tret\n",of); + fputs("LibraryID:\n",of); + fputs("\tdb \"DS\"\n",of); + if (options.tini_libid) { + fprintf(of,"\tdb 0,0,0%02xh,0%02xh,0%02xh,0%02xh\n", + (options.tini_libid>>24 & 0xff), + (options.tini_libid>>16 & 0xff), + (options.tini_libid>>8 & 0xff), + (options.tini_libid & 0xff)); + } else { + fprintf(of,"\tdb 0,0,0,0,0,1\n"); + } + +} +static void _tininative_genAssemblerEnd (FILE * of) +{ + fputs("\tend\n",of); +} +/* tininative assembler , calls "macro", if it succeeds calls "a390" */ +static void _tininative_do_assemble (set *asmOptions) +{ + char *buf; + static const char *macroCmd[] = { + "macro","$1.a51",NULL + }; + static const char *a390Cmd[] = { + "a390","$1.mpp",NULL + }; + + buf = buildCmdLine(macroCmd, dstFileName, NULL, NULL, NULL); + if (sdcc_system(buf)) { + Safe_free (buf); + exit(1); + } + Safe_free (buf); + buf = buildCmdLine(a390Cmd, dstFileName, NULL, NULL, asmOptions); + if (sdcc_system(buf)) { + Safe_free (buf); + exit(1); + } + Safe_free (buf); +} + +/* list of key words used by TININative */ +static char *_tininative_keywords[] = +{ + "at", + "bit", + "code", + "critical", + "data", + "far", + "idata", + "interrupt", + "near", + "pdata", + "reentrant", + "sfr", + "sbit", + "using", + "xdata", + "_data", + "_code", + "_generic", + "_near", + "_xdata", + "_pdata", + "_idata", + "_naked", + "_JavaNative", + NULL +}; + +static builtins __tininative_builtins[] = { + { "__builtin_memcpy_x2x","v",3,{"cx*","cx*","i"}}, /* void __builtin_memcpy_x2x (xdata char *,xdata char *,int) */ + { "__builtin_memcpy_c2x","v",3,{"cx*","cp*","i"}}, /* void __builtin_memcpy_c2x (xdata char *,code char *,int) */ + { "__builtin_memset_x","v",3,{"cx*","c","i"}}, /* void __builtin_memset (xdata char *,char,int) */ + /* TINI NatLib */ + { "NatLib_LoadByte","c",1,{"c"}}, /* char Natlib_LoadByte (0 based parameter number) */ + { "NatLib_LoadShort","s",1,{"c"}}, /* short Natlib_LoadShort (0 based parameter number) */ + { "NatLib_LoadInt","l",1,{"c"}}, /* long Natlib_LoadLong (0 based parameter number) */ + { "NatLib_LoadPointer","cx*",1,{"c"}}, /* long Natlib_LoadPointer (0 based parameter number) */ + /* TINI StateBlock related */ + { "NatLib_InstallImmutableStateBlock","c",2,{"vx*","us"}},/* char NatLib_InstallImmutableStateBlock(state block *,int handle) */ + { "NatLib_InstallEphemeralStateBlock","c",2,{"vx*","us"}},/* char NatLib_InstallEphemeralStateBlock(state block *,int handle) */ + { "NatLib_RemoveImmutableStateBlock","v",0,{NULL}},/* void NatLib_RemoveImmutableStateBlock() */ + { "NatLib_RemoveEphemeralStateBlock","v",0,{NULL}},/* void NatLib_RemoveEphemeralStateBlock() */ + { "NatLib_GetImmutableStateBlock","i",0,{NULL}}, /* int NatLib_GetImmutableStateBlock () */ + { "NatLib_GetEphemeralStateBlock","i",0,{NULL}}, /* int NatLib_GetEphemeralStateBlock () */ + /* Memory manager */ + { "MM_XMalloc","i",1,{"l"}}, /* int MM_XMalloc (long) */ + { "MM_Malloc","i",1,{"i"}}, /* int MM_Malloc (int) */ + { "MM_ApplicationMalloc","i",1,{"i"}}, /* int MM_ApplicationMalloc (int) */ + { "MM_Free","i",1,{"i"}}, /* int MM_Free (int) */ + { "MM_Deref","cx*",1,{"i"}}, /* char *MM_Free (int) */ + { "MM_UnrestrictedPersist","c",1,{"i"}}, /* char MM_UnrestrictedPersist (int) */ + /* System functions */ + { "System_ExecJavaProcess","c",2,{"cx*","i"}}, /* char System_ExecJavaProcess (char *,int) */ + { "System_GetRTCRegisters","v",1,{"cx*"}}, /* void System_GetRTCRegisters (char *) */ + { "System_SetRTCRegisters","v",1,{"cx*"}}, /* void System_SetRTCRegisters (char *) */ + { "System_ThreadSleep","v",2,{"l","c"}}, /* void System_ThreadSleep (long,char) */ + { "System_ThreadSleep_ExitCriticalSection","v",2,{"l","c"}},/* void System_ThreadSleep_ExitCriticalSection (long,char) */ + { "System_ProcessSleep","v",2,{"l","c"}}, /* void System_ProcessSleep (long,char) */ + { "System_ProcessSleep_ExitCriticalSection","v",2,{"l","c"}},/* void System_ProcessSleep_ExitCriticalSection (long,char) */ + { "System_ThreadResume","c",2,{"c","c"}}, /* char System_ThreadResume(char,char) */ + { "System_SaveJavaThreadState","v",0,{NULL}}, /* void System_SaveJavaThreadState() */ + { "System_RestoreJavaThreadState","v",0,{NULL}}, /* void System_RestoreJavaThreadState() */ + { "System_ProcessYield","v",0,{NULL}}, /* void System_ProcessYield() */ + { "System_ProcessSuspend","v",0,{NULL}}, /* void System_ProcessSuspend() */ + { "System_ProcessResume","v",1,{"c"}}, /* void System_ProcessResume(char) */ + { "System_RegisterPoll","c",1,{"vF*"}}, /* char System_RegisterPoll ((void *func pointer)()) */ + { "System_RemovePoll","c",1,{"vF*"}}, /* char System_RemovePoll ((void *func pointer)()) */ + { "System_GetCurrentProcessId","c",0,{NULL}}, /* char System_GetCurrentProcessId() */ + { "System_GetCurrentThreadId","c",0,{NULL}}, /* char System_GetCurrentThreadId() */ + { NULL , NULL,0, {NULL}} /* mark end of table */ +}; + +static const char *_a390Cmd[] = +{ + "macro", "$l", "$3", "$1.a51", NULL +}; + +PORT tininative_port = +{ + TARGET_ID_DS390, + "TININative", + "DS80C390", /* Target name */ + NULL, /* processor */ + { + glue, + FALSE, /* Emit glue around main */ + MODEL_FLAT24, + MODEL_FLAT24, + get_model, + }, + { + _a390Cmd, + NULL, + "-l", /* Options with debug */ + "-l", /* Options without debug */ + 0, + ".a51", + _tininative_do_assemble + }, + { /* Linker */ + NULL, + NULL, + NULL, + ".tlib", + 1, + NULL, /* crt */ + _libs_ds390, /* libs */ + }, + { + _defaultRules, + getInstructionSize, + getRegsRead, + getRegsWritten, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 1, 3, 3, 3, 3, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + + { + "XSEG (XDATA)", + "STACK (DATA)", + "CSEG (CODE)", + "DSEG (DATA)", + "ISEG (DATA)", + "PSEG (PAG,XDATA)", + "XSEG (XDATA)", + "BSEG (BIT)", + "RSEG (DATA)", + "GSINIT (CODE)", + "OSEG (OVR,DATA)", + "GSFINAL (CODE)", + "HOME (CODE)", + NULL, + NULL, + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { NULL, NULL }, + { +1, 1, 4, 1, 1, 0, 0 }, + /* ds390 has an 16 bit mul & div */ + { -1, FALSE }, + { ds390_emitDebuggerSymbol }, + { + 255/4, /* maxCount */ + 4, /* sizeofElement */ + {8,12,20}, /* sizeofMatchJump[] */ + {10,14,22}, /* sizeofRangeCompare[] */ + 4, /* sizeofSubtract */ + 7, /* sizeofDispatch */ + }, + "", + _tininative_init, + _ds390_parseOptions, + _tininative_options, + NULL, + _tininative_finaliseOptions, + _tininative_setDefaultOptions, + ds390_assignRegisters, + _ds390_getRegName, + 0, + NULL, + _tininative_keywords, + _tininative_genAssemblerPreamble, + _tininative_genAssemblerEnd, + _tininative_genIVT, + NULL, + _ds390_genInitStartup, + _ds390_reset_regparm, + _ds390_regparm, + NULL, + NULL, + _ds390_nativeMulCheck, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + __tininative_builtins, /* table of builtin functions */ + FPOINTER, /* treat unqualified pointers as far pointers */ + 0, /* DONOT reset labelKey */ + 0, /* globals & local static NOT allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +static int +_ds400_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + /* We can't generate a static IVT, since the boot rom creates one + * for us in rom_init. + * + * we must patch it as part of the C startup. + */ + dbuf_printf (oBuf, ";\tDS80C400 IVT must be generated at runtime.\n"); + dbuf_printf (oBuf, "\tsjmp\t__sdcc_400boot\n"); + dbuf_printf (oBuf, "\t.ascii\t'TINI'\t; required signature for 400 boot loader.\n"); + dbuf_printf (oBuf, "\t.db\t0\t; selected bank: zero *should* work...\n"); + dbuf_printf (oBuf, "\t__sdcc_400boot:\tljmp\t__sdcc_gsinit_startup\n"); + + return TRUE; +} + + +/*---------------------------------------------------------------------------------*/ +/* _ds400 specific */ +/*---------------------------------------------------------------------------------*/ + +static OPTION _ds400_options[] = + { + { 0, OPTION_FLAT24_MODEL, NULL, "use the flat24 model for the ds400 (default)" }, + { 0, OPTION_STACK_8BIT, NULL, "use the 8bit stack for the ds400 (not supported yet)" }, + { 0, OPTION_STACK_SIZE, &options.stack_size, "Tells the linker to allocate this space for stack", CLAT_INTEGER }, + { 0, "--pack-iram", NULL, "Tells the linker to pack variables in internal ram (default)"}, + { 0, "--no-pack-iram", &options.no_pack_iram, "Deprecated: Tells the linker not to pack variables in internal ram"}, + { 0, "--stack-10bit", &options.stack10bit, "use the 10bit stack for ds400 (default)" }, + { 0, "--use-accelerator", &options.useAccelerator, "generate code for ds400 arithmetic accelerator"}, + { 0, "--protect-sp-update", &options.protect_sp_update, "will disable interrupts during ESP:SP updates"}, + { 0, "--parms-in-bank1", &options.parms_in_bank1, "use Bank1 for parameter passing"}, + { 0, NULL } + }; + +static void +_ds400_finaliseOptions (void) +{ + if (options.noXinitOpt) + { + port->genXINIT=0; + } + + // hackhack: we're a superset of the 390. + addSet(&preArgvSet, Safe_strdup("-D__SDCC_ds390")); + addSet(&preArgvSet, Safe_strdup("-D__ds390")); + + /* Hack-o-matic: if we are using the flat24 model, + * adjust pointer sizes. + */ + if (options.model != MODEL_FLAT24) + { + fprintf (stderr, + "*** warning: ds400 port small and large model experimental.\n"); + if (options.model == MODEL_LARGE) + { + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + } + else + { + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + } + } + else + { + port->s.far_ptr_size = 3; + port->s.funcptr_size = 3; + port->s.ptr_size = 4; + + port->stack.isr_overhead += 2; /* Will save dpx on ISR entry. */ + + port->stack.call_overhead += 2; /* This acounts for the extra byte + * of return addres on the stack. + * but is ugly. There must be a + * better way. + */ + + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + + if (!options.stack10bit) + { + fprintf (stderr, + "*** error: ds400 port only supports the 10 bit stack mode.\n"); + } + else + { + if (!options.stack_loc) + options.stack_loc = 0xffdc00; + // assumes IDM1:0 = 1:0, CMA = 1. + } + + /* Fixup the memory map for the stack; it is now in + * far space and requires a FPOINTER to access it. + */ + istack->fmap = 1; + istack->ptrType = FPOINTER; + + // the DS400 rom calling interface uses register bank 3. + RegBankUsed[3] = 1; + + } /* MODEL_FLAT24 */ +} + +static void _ds400_generateRomDataArea(FILE *fp, bool isMain) +{ + /* Only do this for the file containing main() */ + if (isMain) + { + fprintf(fp, "%s", iComments2); + fprintf(fp, "; the direct data area used by the DS80c400 ROM code.\n"); + fprintf(fp, "%s", iComments2); + fprintf(fp, ".area ROMSEG (ABS,CON,DATA)\n\n"); + fprintf(fp, ".ds 24 ; 24 bytes of directs used starting at 0x68\n\n"); + } +} + +static void _ds400_linkRomDataArea(FILE *fp) +{ + fprintf(fp, "-b ROMSEG = 0x0068\n"); +} + +static const char * const _libs_ds400[] = { STD_DS400_LIB, NULL, }; + +PORT ds400_port = +{ + TARGET_ID_DS400, + "ds400", + "DS80C400", /* Target name */ + NULL, + { + glue, + TRUE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE | MODEL_FLAT24, + MODEL_SMALL, + get_model, + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_ds400, /* libs */ + }, + { /* Peephole optimizer */ + _defaultRules, + getInstructionSize, + getRegsRead, + getRegsWritten, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 1, 2, 3, 2, 3, 1, 4 }, + + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + + { + "XSEG (XDATA)", + "STACK (DATA)", + "CSEG (CODE)", + "DSEG (DATA)", + "ISEG (DATA)", + "PSEG (PAG,XDATA)", + "XSEG (XDATA)", + "BSEG (BIT)", + "RSEG (DATA)", + "GSINIT (CODE)", + "OSEG (OVR,DATA)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG (XDATA)", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1 + }, + { _ds400_generateRomDataArea, _ds400_linkRomDataArea }, + { +1, 1, 4, 1, 1, 0, 0 }, + { -1, FALSE }, + { ds390_emitDebuggerSymbol }, + { + 255/4, /* maxCount */ + 4, /* sizeofElement */ + {8,12,20}, /* sizeofMatchJump[] */ + {10,14,22}, /* sizeofRangeCompare[] */ + 4, /* sizeofSubtract */ + 7, /* sizeofDispatch */ + }, + "_", + _ds390_init, + _ds390_parseOptions, + _ds400_options, + NULL, + _ds400_finaliseOptions, + _ds390_setDefaultOptions, + ds390_assignRegisters, + _ds390_getRegName, + 0, + NULL, + _ds390_keywords, + _ds390_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _ds400_genIVT, + _ds390_genXINIT, + _ds390_genInitStartup, + _ds390_reset_regparm, + _ds390_regparm, + NULL, + NULL, + _ds390_nativeMulCheck, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + __ds390_builtins, /* table of builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local static allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; diff --git a/src/ds390/main.h b/src/ds390/main.h new file mode 100644 index 0000000..839c055 --- /dev/null +++ b/src/ds390/main.h @@ -0,0 +1,46 @@ +/*------------------------------------------------------------------------- + main.h - ds390 specific general header file + + Copyright (C) 2000, Kevin Vigor + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +#include "SDCCgen.h" + +typedef struct asmLineNode + { +#ifdef UNNAMED_STRUCT_TAG + struct asmLineNodeBase; +#else + /* exactly the same members as of struct asmLineNodeBase from SDCCgen.h */ + int size; + bitVect *regsRead; + bitVect *regsWritten; +#endif + int currentDPS; + unsigned initialized:1; + } +asmLineNode; + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); +asmLineNode * ds390newAsmLineNode (int currentDPS); + +#endif diff --git a/src/ds390/peeph.def b/src/ds390/peeph.def new file mode 100644 index 0000000..2159286 --- /dev/null +++ b/src/ds390/peeph.def @@ -0,0 +1,2856 @@ +//replace restart { +// pop %1 +// push %1 +//} by { +// ; Peephole 1 removed pop %1 push %1 (not push pop) +//} + +//replace restart { +// pop %1 +// mov %2,%3 +// push %1 +//} by { +// ; Peephole 2 removed pop %1 push %1 (not push pop) +// mov %2,%3 +//} + +// +// added by Jean Louis VERN for +// his shift stuff +replace restart { + xch a,%1 + xch a,%1 +} by { + ; Peephole 2.a removed redundant xch xch +} + +replace restart { +// saving 2 byte + mov %1,#0x00 + mov a,#0x00 +} by { + ; Peephole 3.a changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte + mov %1,#0x00 + clr a +} by { + ; Peephole 3.b changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte, loosing 1 cycle but maybe allowing peephole 3.b to start + mov %1,#0x00 + mov %2,#0x00 + mov a,%3 +} by { + ; Peephole 3.c changed mov to clr + clr a + mov %1,a + mov %2,a + mov a,%3 +} + +replace restart { + mov a,#0 +} by { + ; Peephole 3.d changed mov to clr + clr a +} + +replace { + mov %1,a + mov dptr,#%2 + mov a,%1 + movx @dptr,a +} by { + ; Peephole 100 removed redundant mov + mov %1,a + mov dptr,#%2 + movx @dptr,a +} + +replace { + mov a,acc +} by { + ; Peephole 100.a removed redundant mov +} + +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + ; Peephole 101 removed redundant mov + mov a,%1 + movx @dptr,a + inc dptr + movx @dptr,a +} + +replace { + mov %1,%2 + ljmp %3 +%4: + mov %1,%5 +%3: + mov dpl,%1 +%7: + mov sp,bp + pop bp +} by { + ; Peephole 102 removed redundant mov + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,bp + pop bp +} + +replace { + mov %1,%2 + ljmp %3 +%4: + mov a%1,%5 +%3: + mov dpl,%1 +%7: + mov sp,bp + pop bp +} by { + ; Peephole 103 removed redundant mov + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,bp + pop bp +} + +replace { + mov a,bp + clr c + add a,#0x01 + mov r%1,a +} by { + ; Peephole 104 optimized increment (acc not set to r%1, flags undefined) + mov r%1,bp + inc r%1 +} + +replace { + mov %1,a + mov a,%1 +} by { + ; Peephole 105 removed redundant mov + mov %1,a +} + +replace { + mov %1,a + clr c + mov a,%1 +} by { + ; Peephole 106 removed redundant mov + mov %1,a + clr c +} + +replace { + ljmp %1 +%1: +} by { + ; Peephole 107 removed redundant ljmp +%1: +} + +replace { + jc %1 + ljmp %5 +%1: +} by { + ; Peephole 108 removed ljmp by inverse jump logic + jnc %5 +%1: +} if labelInRange(%5) + +replace { + jz %1 + ljmp %5 +%1: +} by { + ; Peephole 109 removed ljmp by inverse jump logic + jnz %5 +%1: +} if labelInRange(%5) + +replace { + jnz %1 + ljmp %5 +%1: +} by { + ; Peephole 110 removed ljmp by inverse jump logic + jz %5 +%1: +} if labelInRange(%5) + +replace { + jb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 111 removed ljmp by inverse jump logic + jnb %1,%5 +%2: +} if labelInRange(%5) + +replace { + jnb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 112 removed ljmp by inverse jump logic + jb %1,%5 +%2: +} if labelInRange(%5) + +replace { + ljmp %5 +%1: +} by { + ; Peephole 132 changed ljmp to sjmp + sjmp %5 +%1: +} if labelInRange(%5) + + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113 optimized misc sequence + clr %4 + cjne %1,%2,%3 + setb %4 +%3: +} if labelRefCount %3 1 + +replace { + clr a + cjne %1,%2,%3 + cjne %10,%11,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 114 optimized misc sequence + clr %4 + cjne %1,%2,%3 + cjne %10,%11,%3 + setb %4 +%3: +} if labelRefCount %3 2 + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115 jump optimization + cjne %1,%2,%3 + sjmp %4 +%3: +} if labelRefCount %3 1 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 116 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 117 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 118 jump optimization + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 119 jump optimization + cjne %1,%2,%4 +%3: +} if labelRefCount %3 1 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 120 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 +%3: +} if labelRefCount %3 2 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 121 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 +%3: +} if labelRefCount %3 3 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 122 jump optimization + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 + cjne %14,%15,%4 +%3: +} if labelRefCount %3 4 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 123 jump optimization + cjne %1,%2,%3 + smp %4 +%3: +} if labelRefCount %3 1 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 124 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 125 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 126 jump optimization + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + push psw + mov psw,%1 + push bp + mov bp,%2 +%3: + mov %2,bp + pop bp + pop psw + ret +} by { + ; Peephole 127 removed misc sequence + ret +} if labelRefCount %3 0 + +replace { + clr a + rlc a + jz %1 +} by { + ; Peephole 128 jump optimization + jnc %1 +} + +replace { + clr a + rlc a + jnz %1 +} by { + ; Peephole 129 jump optimization + jc %1 +} + +replace { + mov r%1,@r%2 +} by { + ; Peephole 130 changed target address mode r%1 to ar%1 + mov ar%1,@r%2 +} + +replace { + mov a,%1 + subb a,#0x01 + mov %2,a + mov %1,%2 +} by { + ; Peephole 131 optimized decrement (not caring for c) + dec %1 + mov %2,%1 +} + +replace { + mov r%1,%2 + mov ar%3,@r%1 + inc r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + ; Peephole 133 removed redundant moves + mov r%1,%2 + inc @r%1 + mov ar%3,@r%1 +} + +replace { + mov r%1,%2 + mov ar%3,@r%1 + dec r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + ; Peephole 134 removed redundant moves + mov r%1,%2 + dec @r%1 + mov ar%3,@r%1 +} + +replace { + mov r%1,a + mov a,r%2 + orl a,r%1 +} by { + ; Peephole 135 removed redundant mov + mov r%1,a + orl a,r%2 +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov dpx,%4 + mov a,%1 +} by { + ; Peephole 136a removed redundant moves + mov %1,a + mov dpl,%2 + mov dph,%3 + mov dpx,%4 +} if 24bitMode + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov a,%1 +} by { + ; Peephole 136 removed redundant moves + mov %1,a + mov dpl,%2 + mov dph,%3 +} + +// WTF? Doesn't look sensible to me... +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jz %5 +//} by { +// ; Peephole 137 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%5 +//%4: +//} if labelRefCount %4 1 +// +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jnz %5 +//} by { +// ; Peephole 138 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%4 +// sjmp %5 +//%4: +//} if labelRefCount %4 1 + +replace { + mov r%1,a + anl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139 removed redundant mov + anl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + orl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 140 removed redundant mov + orl a,%2 + mov r%1,a } + +replace { + mov r%1,a + xrl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 141 removed redundant mov + xrl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + mov r%2,ar%1 + mov ar%1,@r%2 +} by { + ; Peephole 142 removed redundant moves + mov r%2,a + mov ar%1,@r%2 +} + +replace { + rlc a + mov acc.0,c +} by { + ; Peephole 143 converted rlc to rl + rl a +} + +replace { + rrc a + mov acc.7,c +} by { + ; Peephole 144 converted rrc to rc + rr a +} + +replace { + clr c + addc a,%1 +} by { + ; Peephole 145 changed to add without carry + add a,%1 +} + +replace { + clr c + mov a,%1 + addc a,%2 +} by { + ; Peephole 146 changed to add without carry + mov a,%1 + add a,%2 +} + +replace { + orl r%1,a +} by { + ; Peephole 147 changed target address mode r%1 to ar%1 + orl ar%1,a +} + +replace { + anl r%1,a +} by { + ; Peephole 148 changed target address mode r%1 to ar%1 + anl ar%1,a +} + +replace { + xrl r%1,a +} by { + ; Peephole 149 changed target address mode r%1 to ar%1 + xrl ar%1,a +} + +replace { + mov %1,dpl + mov dpl,%1 +%9: + ret +} by { + ; Peephole 150 removed misc moves via dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 151 removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 +%9: + ret +} by { + ; Peephole 152 removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 + mov dph,%2 + mov b,%3 +%9: + ret +} by { + ; Peephole 153 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 +%9: + ret +} by { + ; Peephole 154 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 155 removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 + mov dph,%2 + mov b,%3 + mov a,%4 +%9: + ret +} by { + ; Peephole 156 removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 + mov dph,%2 +%9: + ret +} by { + ; Peephole 157 removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov %1,dpl + mov %2,dph + mov %3,b + mov %4,a + mov dpl,%1 +%9: + ret } by { + ; Peephole 158 removed misc moves via dph, dpl, b, a before return +%9: + ret } + +replace { + mov %1,#%2 + xrl %1,#0x80 +} by { + ; Peephole 159 avoided xrl during execution + mov %1,#(%2 ^ 0x80) +} + +replace { + jnc %1 + sjmp %2 +%1: +} by { + ; Peephole 160 removed sjmp by inverse jump logic + jc %2 +%1:} + +replace { + jc %1 + sjmp %2 +%1: +} by { + ; Peephole 161 removed sjmp by inverse jump logic + jnc %2 +%1:} + +replace { + jnz %1 + sjmp %2 +%1: +} by { + ; Peephole 162 removed sjmp by inverse jump logic + jz %2 +%1:} + +replace { + jz %1 + sjmp %2 +%1: +} by { + ; Peephole 163 removed sjmp by inverse jump logic + jnz %2 +%1:} + +replace { + jnb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 164 removed sjmp by inverse jump logic + jb %3,%2 +%1: +} + +replace { + jb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 165 removed sjmp by inverse jump logic + jnb %3,%2 +%1: +} + +replace { + mov %1,%2 + mov %3,%1 + mov %2,%1 +} by { + ; Peephole 166 removed redundant mov + mov %1,%2 + mov %3,%1 } + +replace { + mov c,%1 + cpl c + mov %1,c +} by { + ; Peephole 167 removed redundant bit moves (c not set to %1) + cpl %1 } + +replace { + jnb %1,%2 + sjmp %3 +%2:} by { + ; Peephole 168 jump optimization + jb %1,%3 +%2:} + +replace { + jb %1,%2 + sjmp %3 +%2:} by { + ; Peephole 169 jump optimization + jnb %1,%3 +%2:} + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 170 jump optimization + cjne %1,%2,%4 +%3: +} if labelRefCount %3 1 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 171 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 +%3: +} if labelRefCount %3 2 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 172 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 +%3: +} if labelRefCount %3 3 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 173 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 + cjne %13,%14,%4 +%3: +} if labelRefCount %3 4 + +replace { + mov r%1,%2 + clr c + mov a,r%1 + subb a,#0x01 + mov %2,a +} by { + ; Peephole 174 optimized decrement (acc not set to %2, flags undefined) + mov r%1,%2 + dec %2 +} + + +replace { + mov r%1,%2 + mov a,r%1 + add a,#0x01 + mov %2,a +} by { + ; Peephole 175 optimized increment (acc not set to %2, flags undefined) + mov r%1,%2 + inc %2 +} + +replace { + mov %1,@r%2 + inc %1 + mov @r%2,%1 +} by { + ; Peephole 176 optimized increment, removed redundant mov + inc @r%2 + mov %1,@r%2 +} + +// this one will screw assignes to volatile/sfr's +//replace { +// mov %1,%2 +// mov %2,%1 +//} by { +// ; Peephole 177 removed redundant mov +// mov %1,%2 +//} + +replace { + mov a,%1 + mov b,a + mov a,%2 +} by { + ; Peephole 178 removed redundant mov + mov b,%1 + mov a,%2 +} + +// rules 179-182 provided by : Frieder +// saving 2 byte, 1 cycle +replace { + mov b,#0x00 + mov a,#0x00 +} by { + ; Peephole 179 changed mov to clr + clr a + mov b,a +} + +// saving 1 byte, 0 cycles +replace { + mov a,#0x00 +} by { + ; Peephole 180 changed mov to clr + clr a +} + +replace { + mov dpl,#0x00 + mov dph,#0x00 + mov dpx,#0x00 +} by { + ; Peephole 181a used 24 bit load of dptr + mov dptr,#0x0000 +} if 24bitMode + +// saving 3 byte, 2 cycles, return(NULL) profits here +replace { + mov dpl,#0x00 + mov dph,#0x00 +} by { + ; Peephole 181 used 16 bit load of dptr + mov dptr,#0x0000 +} + +replace { + mov dpl,#%1 + mov dph,#(%1 >> 8) + mov dpx,#(%1 >> 16) +} by { + ; Peephole 182b used 24 bit load of DPTR + mov dptr,#%1 +} + +// saves 2 bytes, ?? cycles. +replace { + mov dpl,#0x%1 + mov dph,#0x%2 + mov dpx,#0x%3 +} by { + ; Peephole 182a used 24 bit load of dptr + mov dptr,#0x%3%2%1 +} if 24bitMode(), portIsDS390 + +// saves 2 bytes, ?? cycles. +replace { + mov dpl,#%1 + mov dph,#%2 + mov dpx,#%3 +} by { + ; Peephole 182b used 24 bit load of dptr + mov dptr,#((%3 << 16) + (%2 << 8) + %1) +} if 24bitMode(), portIsDS390 + +// saving 3 byte, 2 cycles, return(float_constant) profits here +replace { + mov dpl,#0x%1 + mov dph,#0x%2 +} by { + ; Peephole 182c used 16 bit load of dptr + mov dptr,#0x%2%1 +} + +// saving 3 byte, 2 cycles, return(float_constant) profits here +replace { + mov dpl,#%1 + mov dph,#%2 +} by { + ; Peephole 182 used 16 bit load of dptr + mov dptr,#(((%2)<<8) + %1) +} + +replace { + anl %1,#%2 + anl %1,#%3 +} by { + ; Peephole 183 avoided anl during execution + anl %1,#(%2 & %3) +} + +replace { + mov %1,a + cpl a + mov %1,a +} by { + ; Peephole 184 removed redundant mov + cpl a + mov %1,a +} + +replace { +// acc being incremented might cause problems + mov %1,a + inc %1 +} by { + ; Peephole 185 changed order of increment (acc incremented also!) + inc a + mov %1,a +} + +// char indexed access to: long code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr + mov %4,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.a optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr + mov %4,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: void* code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.b optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.c optimized movc sequence + mov %2,a + mov dptr,#%1 + movc a,@a+dptr + xch a,%2 + inc dptr + movc a,@a+dptr +} + +// char indexed access to: char code table[] = {4,3,2,1}; +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr +} by { + ; Peephole 186.d optimized movc sequence + mov dptr,#%1 + movc a,@a+dptr +} + +replace { + mov r%1,%2 + anl ar%1,#%3 + mov a,r%1 +} by { + ; Peephole 187 used a instead of ar%1 for anl + mov a,%2 + anl a,#%3 + mov r%1,a +} + +replace { + mov %1,a + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} by { + ; Peephole 188 removed redundant mov + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} + +replace { + anl a,#0x0f + mov %1,a + mov a,#0x0f + anl a,%1 +} by { + ; Peephole 189 removed redundant mov and anl + anl a,#0x0f + mov %1,a +} + +// rules 190 & 191 need to be in order +replace { + mov a,%1 + lcall __gptrput + mov a,%1 +} by { + ; Peephole 190 removed redundant mov + mov a,%1 + lcall __gptrput +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + ; Peephole 191 removed redundant mov + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 +} + +replace { + mov r%1,a + mov @r%2,ar%1 +} by { + ; Peephole 192 used a instead of ar%1 as source + mov r%1,a + mov @r%2,a +} + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + jnz %3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + jnz %8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +;%3: +} if labelRefCount %3 4 + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +;%3: +} if labelRefCount %3 4 + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + inc %1 + cjne @%1,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.a optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + inc %1 + cjne @%1,%14,%8 + sjmp %7 +;%3: +} if labelRefCount %3 4 + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 194 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + cjne %13,%14,%8 + sjmp %7 +;%3: +} if labelRefCount %3 4 + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +;%3: +} if labelRefCount %3 3 + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +;%3: +} if labelRefCount %3 3 + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.a optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + sjmp %7 +;%3: +} if labelRefCount %3 3 + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 196 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + sjmp %7 +;%3: +} if labelRefCount %3 3 + +replace { + jnz %3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.a optimized misc jump sequence + jnz %8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +;%3: +} if labelRefCount %3 2 + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197 optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +;%3: +} if labelRefCount %3 2 + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.a optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + sjmp %7 +;%3: +} if labelRefCount %3 2 + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 198 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + sjmp %7 +;%3: +} if labelRefCount %3 2 + +replace { + cjne %1,%2,%3 + sjmp %4 +%3: + sjmp %5 +} by { + ; Peephole 199 optimized misc jump sequence + cjne %1,%2,%5 + sjmp %4 +;%3: +} if labelRefCount %3 1 + +replace { + sjmp %1 +%1: +} by { + ; Peephole 200 removed redundant sjmp +%1: +} + +replace { + sjmp %1 +%2: +%1: +} by { + ; Peephole 201 removed redundant sjmp +%2: +%1: +} + +replace { + push acc + mov dptr,%1 + pop acc +} by { + ; Peephole 202 removed redundant push pop + mov dptr,%1 +} + +replace { + push acc + pop acc +} by { + ; Peephole 202b removed redundant push pop +} + +replace { + mov r%1,_spx + lcall %2 + mov r%1,_spx +} by { + ; Peephole 203 removed mov r%1,_spx + lcall %2 +} + +replace { + mov %1,a + add a,acc + mov %1,a +} by { + ; Peephole 204 removed redundant mov + add a,acc + mov %1,a +} + +replace { + djnz %1,%2 + sjmp %3 +%2: + sjmp %4 +%3: +} by { + ; Peephole 205 optimized misc jump sequence + djnz %1,%4 +%2: +%3: +} if labelRefCount %2 1 + +replace { + mov %1,%1 +} by { + ; Peephole 206 removed redundant mov %1,%1 +} + +replace { + mov a,_bp + add a,#0x00 + mov %1,a +} by { + ; Peephole 207 removed zero add (acc not set to %1, flags undefined) + mov %1,_bp +} + +replace { + push acc + mov r%1,_bp + pop acc +} by { + ; Peephole 208 removed redundant push pop + mov r%1,_bp +} + +replace { + mov a,_bp + add a,#0x00 + inc a + mov %1,a +} by { + ; Peephole 209 optimized increment (acc not set to %1, flags undefined) + mov %1,_bp + inc %1 +} + +replace { + mov dptr,#((((%1 >> 16)) <<16) + (((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210a simplified expression + mov dptr,#%1 +} if 24bitMode + +replace { + mov dptr,#((((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210 simplified expression + mov dptr,#%1 +} + +replace restart { + push ar%1 + pop ar%1 +} by { + ; Peephole 211 removed redundant push r%1 pop r%1 +} + +replace { + mov a,_bp + add a,#0x01 + mov r%1,a +} by { + ; Peephole 212 reduced add sequence to inc + mov r%1,_bp + inc r%1 +} + +replace { + mov %1,#(( %2 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.a inserted fix + mov %1,#(%2 >> 8) + xrl %1,#0x80 +} if portIsDS390 + +replace { + mov %1,#(( %2 >> 16 ) ^ 0x80) +} by { + ; Peephole 213.b inserted fix + mov %1,#(%2 >> 16) + xrl %1,#0x80 +} if portIsDS390 + +replace { + mov %1,#(( %2 + %3 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.c inserted fix + mov %1,#((%2 + %3) >> 8) + xrl %1,#0x80 +} if portIsDS390 + +replace { + mov %1,a + mov a,%2 + add a,%1 +} by { + ; Peephole 214 reduced some extra movs + mov %1,a + add a,%2 +} if notSame(%1 %2) + +replace { + mov %1,a + add a,%2 + mov %1,a +} by { + ; Peephole 215 removed some movs + add a,%2 + mov %1,a +} if notSame(%1 %2) + +replace { + mov r%1,%2 + clr a + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 216 simplified clear (2bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 217 simplified clear (3bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + ; Peephole 218 simplified clear (4bytes) + mov r%1,%2 + clr a + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + clr a + movx @dptr,a +} by { + ; Peephole 219 removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + clr a + movx @dptr,a +} by { + ; Peephole 219a removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + movx @dptr,a +} + +replace { + mov dps, #0 + mov dps, #1 +} by { + ; Peephole 220a removed bogus DPS set + mov dps, #1 +} + +replace { + mov dps, #1 + mov dps, #0 +} by { + ; Peephole 220b removed bogus DPS set + mov dps, #0 +} + +replace { + mov dps, #0 + mov dps, #0x01 +} by { + ; Peephole 220c removed bogus DPS set +} + +replace { + mov dps,#1 + inc dptr + mov dps,#1 +} by { + ; Peephole 220d removed bogus DPS set + mov dps,#1 + inc dptr +} + +replace { + mov %1 + %2,(%2 + %1) +} by { + ; Peephole 221a remove redundant move +} + +replace { + mov (%1 + %2 + %3),((%2 + %1) + %3) +} by { + ; Peephole 221b remove redundant move +} + +replace { + dec r%1 + inc r%1 +} by { + ; removed dec/inc pair +} + +replace { + mov dps, #0 + mov %1,a + mov dps, #1 +} by { + ; Peephole 222 removed DPS abuse. + mov %1,a + mov dps, #1 +} + +replace { + mov dps, #0 + xch a, ap + mov %1, ap + mov dps, #1 +} by { + ; Peephole 222a removed DPS abuse. + xch a, ap + mov %1, ap + mov dps, #1 +} + +replace { + mov dps, #%1 + inc dptr + movx a,@dptr + mov %2,a + mov dps, #%1 +} by { + mov dps, #%1 + inc dptr + movx a,@dptr + mov %2,a +; Peephole 223: yet more DPS abuse removed. +} + +replace { + mov dps, #0 + inc dps +} by { + mov dps, #1 +} + +replace { + mov dps, #%1 + mov dptr, %2 + mov dps, #%1 +} by { + mov dps, #%1 + mov dptr, %2 +} + +replace { + mov dps, #1 + mov dptr, %1 + mov dps, #0 + mov dptr, %2 + inc dps +} by { + mov dps, #0 + mov dptr, %2 + inc dps + mov dptr, %1 +; Peephole 224a: DPS usage re-arranged. +} + +replace { + mov dps, #%1 + mov dptr, %2 + mov dps, #%3 + mov dptr, %4 + mov dps, #%1 +} by { + mov dps, #%3 + mov dptr, %4 + mov dps, #%1 + mov dptr, %2 +; Peephole 224: DPS usage re-arranged. +} + +replace { + mov dps, #1 + mov dptr, %1 + mov dps, #0 +} by { + mov dps, #1 + mov dptr, %1 + dec dps +} + +replace { + xch a, ap + add a, ap +} by { + add a, ap +} + +replace { + xch a, ap + addc a, ap +} by { + addc a, ap +} + +replace { + inc dps + mov dps, #%1 +} by { + mov dps, #%1 +} + +replace { + dec dps + mov dps, #%1 +} by { + mov dps, #%1 +} + + +// char indexed access to: long code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + addc a,#(%1 >> 16) + mov dpx,a + clr a + movc a,@a+dptr + inc dptr + mov %2,a + clr a + movc a,@a+dptr + inc dptr + mov %3,a + clr a + movc a,@a+dptr + inc dptr + mov %4,a + clr a + movc a,@a+dptr +} by { + ; Peephole 227.a movc optimize + mov b,a + mov dptr,#%1 + movc a,@a+dptr + inc dptr + mov %2,a + mov a,b + movc a,@a+dptr + inc dptr + mov %3,a + mov a,b + movc a,@a+dptr + inc dptr + mov %4,a + mov a,b + movc a,@a+dptr +} + +// char indexed access to: void* code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + addc a,#(%1 >> 16) + mov dpx,a + clr a + movc a,@a+dptr + inc dptr + mov %2,a + clr a + movc a,@a+dptr + inc dptr + mov %3,a + clr a + movc a,@a+dptr +} by { + ; Peephole 227.b movc optimize + mov b,a + mov dptr,#%1 + movc a,@a+dptr + inc dptr + mov %2,a + mov a,b + movc a,@a+dptr + inc dptr + mov %3,a + mov a,b + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + addc a,#(%1 >> 16) + mov dpx,a + clr a + movc a,@a+dptr + inc dptr + mov %2,a + clr a + movc a,@a+dptr +} by { + ; Peephole 227.c movc optimize + mov %2,a + mov dptr,#%1 + movc a,@a+dptr + inc dptr + xch a,%2 + movc a,@a+dptr +} + +// char indexed access to: char code table[] = {4,3,2,1}; +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + addc a,#(%1 >> 16) + mov dpx,a + clr a + movc a,@a+dptr +} by { + ; Peephole 227.d movc optimize + mov dptr,#%1 + movc a,@a+dptr +} + +replace { + mov r%1,%2 + mov ar%1,%3 +} by { + ; Peephole 228 redundant move + mov ar%1,%3 +} + +replace { + mov r%1,a + dec r%1 + mov a,r%1 +} by { + ; Peephole 229.a redundant move + dec a + mov r%1,a +} + +replace { + mov r%1,a + mov r%2,b + mov a,r%1 +} by { + ; Peephole 229.b redundant move + mov r%1,a + mov r%2,b +} + +replace { + mov r%1,a + mov r%2,b + add a,#%3 + mov r%1,a + mov a,r%2 + addc a,#(%3 >> 8) + mov r%2,a +} by { + ; Peephole 229.c redundant move + add a,#%3 + mov r%1,a + mov a,b + addc a,#(%3 >> 8) + mov r%2,a +} + +replace { + mov a,%1 + mov b,a + movx a,%2 +} by { + ; Peephole 229.d redundant move + mov b,%1 + movx a,%2 +} + +replace { + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + mov r%4,a + add a,#0x01 + mov r%5,a + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx @dptr,a +} by { + ; Peephole 230.a save reload dptr + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + mov r%4,a + add a,#0x01 + mov r%5,a + movx @dptr,a +} + +replace { + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + mov r%4,a + dec r%4 + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + mov a,r%4 + movx @dptr,a +} by { + ; Peephole 230.b save reload dptr + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + dec a + mov r%4,a + movx @dptr,a +} + +replace { + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + inc a + mov r%4,a + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + mov a,r%4 + movx @dptr,a +} by { + ; Peephole 230.c save reload dptr + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + inc a + mov r%4,a + movx @dptr,a +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,dpx + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 +} by { + ; Peephole 230.d save reload dptr + mov r%1,dpl + mov r%2,dph + mov r%3,dpx +} + +replace { + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + mov r%4,a + orl ar%4,#%5 + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + mov a,r1 + movx @dptr,a +} by { + ; Peephole 230.e save reload dptr + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + orl a,#%5 + mov r%4,a + movx @dptr,a +} + +replace { + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + mov r%4,a + anl ar%4,#%5 + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + mov a,r1 + movx @dptr,a +} by { + ; Peephole 230.e save reload dptr + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 + movx a,@dptr + anl a,#%5 + mov r%4,a + movx @dptr,a +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,dpx + mov a,r%4 + inc dps + movx @dptr,a + inc dptr + mov dps,#0 + mov dpl,r%1 + mov dph,r%2 + mov dpx,r%3 +} by { + ; Peephole 230.f save reload dptr + mov r%1,dpl + mov r%2,dph + mov r%3,dpx + mov a,r%4 + inc dps + movx @dptr,a + inc dptr + mov dps,#0 +} + +replace { + mov ar%1,r%2 + mov ar%3,r%1 + mov r%1,#0x00 + mov ar%2,r%4 + mov r3,#0x00 +} by { + ; Peephole 231.a simplified moves + mov ar%3,r%2 + mov ar%2,r%4 + mov r%4,#0 + mov r%1,#0 +} + +replace { + mov r%1,#0 + mov r%2,#0 + mov a,r%2 + orl a,r%3 + mov %4,a + mov a,r%5 + orl a,r%1 + mov %6,a +} by { + ; Peephole 231.b simplified or + mov r%1,#0 + mov r%2,#0 + mov a,r%3 + mov %4,a + mov a,r%5 + mov %6,a +} + +replace { + mov a,r%1 + mov b,r%2 + mov r%1,b + mov r%2,a +} by { + ; Peehole 232.a simplified xch + mov a,r%1 + xch a,r%2 + mov r%1,a +} + +replace { + mov a,#%1 + mov b,#%2 + mov r%3,b + mov r%4,a +} by { + ; Peehole 232.b simplified xch + mov r%3,#%2 + mov r%4,#%1 +} + +replace { + mov dpl1,#%1 + mov dph1,#(%1 >> 8) + mov dpx1,#(%1 >> 16) +} by { + ; Peephole 233 24 bit load of dptr1 + inc dps + mov dptr,#%1 + dec dps +} + +// 14 rules by Fiorenzo D. Ramaglia + +replace { + add a,ar%1 +} by { + ; Peephole 236a + add a,r%1 +} + +replace { + addc a,ar%1 +} by { + ; Peephole 236b + addc a,r%1 +} + +replace { + anl a,ar%1 +} by { + ; Peephole 236c + anl a,r%1 +} + +replace { + dec ar%1 +} by { + ; Peephole 236d + dec r%1 +} + +replace { + djnz ar%1,%2 +} by { + ; Peephole 236e + djnz r%1,%2 +} + +replace { + inc ar%1 +} by { + ; Peephole 236f + inc r%1 +} + +replace { + mov a,ar%1 +} by { + ; Peephole 236g + mov a,r%1 +} + +replace { + mov ar%1,#%2 +} by { + ; Peephole 236h + mov r%1,#%2 +} + +replace { + mov ar%1,a +} by { + ; Peephole 236i + mov r%1,a +} + +replace { + mov ar%1,ar%2 +} by { + ; Peephole 236j + mov r%1,ar%2 +} + +replace { + orl a,ar%1 +} by { + ; Peephole 236k + orl a,r%1 +} + +replace { + subb a,ar%1 +} by { + ; Peephole 236l + subb a,r%1 +} + +replace { + xch a,ar%1 +} by { + ; Peephole 236m + xch a,r%1 +} + +replace { + xrl a,ar%1 +} by { + ; Peephole 236n + xrl a,r%1 +} + +replace { + sjmp %1 +%2: + mov %3,%4 +%1: + ret +} by { + ; Peephole 237a removed sjmp to ret + ret +%2: + mov %3,%4 +%1: + ret +} + +replace { + sjmp %1 +%2: + mov %3,%4 + mov dpl,%5 + mov dph,%6 +%1: + ret +} by { + ; Peephole 237b removed sjmp to ret + ret +%2: + mov %3,%4 + mov dpl,%5 + mov dph,%6 +%1: + ret +} + +// applies to f.e. device/lib/log10f.c +replace { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + + mov %9,%1 + mov %10,%2 + mov %11,%3 + mov %12,%4 +} by { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + ; Peephole 238.a removed 4 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7 %8) + +// applies to device/lib/log10f.c +replace { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 + mov %7,%3 +} by { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + ; Peephole 238.b removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7) + +// applies to f.e. device/lib/time.c +replace { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 +} by { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + ; Peephole 238.c removed 2 redundant moves +} if notSame(%1 %2 %3 %4) + +// applies to f.e. support/regression/tests/bug-524209.c +replace { + mov %1,%4 + mov %2,%5 + mov %3,%6 + + mov %4,%1 + mov %5,%2 + mov %6,%3 +} by { + mov %1,%4 + mov %2,%5 + mov %3,%6 + ; Peephole 238.d removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6) + +// applies to f.e. ser_ir.asm +replace { + mov r%1,acc +} by { + ; Peephole 239 used a instead of acc + mov r%1,a +} + +replace restart { + mov a,%1 + addc a,#0x00 +} by { + ; Peephole 240 use clr instead of addc a,#0 + clr a + addc a,%1 +} + +// peepholes 241.a to 241.c and 241.d to 241.f need to be in order +replace { + cjne r%1,#%2,%3 + cjne r%4,#%5,%3 + cjne r%6,#%7,%3 + cjne r%8,#%9,%3 + mov a,#0x01 + sjmp %10 +%3: + clr a +%10: +} by { + ; Peephole 241.a optimized compare + clr a + cjne r%1,#%2,%3 + cjne r%4,#%5,%3 + cjne r%6,#%7,%3 + cjne r%8,#%9,%3 + inc a +%3: +%10: +} + +// applies to f.e. time.c +replace { + cjne r%1,#%2,%3 + cjne r%4,#%5,%3 + mov a,#0x01 + sjmp %6 +%3: + clr a +%6: +} by { + ; Peephole 241.b optimized compare + clr a + cjne r%1,#%2,%3 + cjne r%4,#%5,%3 + inc a +%3: +%6: +} + +// applies to f.e. malloc.c +replace { + cjne r%1,#%2,%3 + mov a,#0x01 + sjmp %4 +%3: + clr a +%4: +} by { + ; Peephole 241.c optimized compare + clr a + cjne r%1,#%2,%3 + inc a +%3: +%4: +} + +// applies to f.e. j = (k!=0x1000); +// with volatile idata long k; +replace { + cjne @r%1,#%2,%3 + inc r%1 + cjne @r%1,#%4,%3 + inc r%1 + cjne @r%1,#%5,%3 + inc r%1 + cjne @r%1,#%6,%3 + mov a,#0x01 + sjmp %7 +%3: + clr a +%7: +} by { + ; Peephole 241.d optimized compare + clr a + cjne @r%1,#%2,%3 + inc r%1 + cjne @r%1,#%4,%3 + inc r%1 + cjne @r%1,#%5,%3 + inc r%1 + cjne @r%1,#%6,%3 + inc a +%3: +%7: +} + +// applies to f.e. j = (k!=0x1000); +// with volatile idata int k; +replace { + cjne @r%1,#%2,%3 + inc r%1 + cjne @r%1,#%4,%3 + mov a,#0x01 + sjmp %7 +%3: + clr a +%7: +} by { + ; Peephole 241.e optimized compare + clr a + cjne @r%1,#%2,%3 + inc r%1 + cjne @r%1,#%4,%3 + inc a +%3: +%7: +} + +// applies to f.e. vprintf.asm (--stack-auto) +replace { + cjne @r%1,#%2,%3 + mov a,#0x01 + sjmp %7 +%3: + clr a +%7: +} by { + ; Peephole 241.f optimized compare + clr a + cjne @r%1,#%2,%3 + inc a +%3: +%7: +} + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 +%1: + jz %4 +} by { + ; Peephole 242.a avoided branch jnz to jz + jnz %1 + mov %2,%3 + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 +%1: + jz %4 +} by { + ; Peephole 242.b avoided branch jnz to jz + jnz %1 + mov %2,%3 + orl a,%5 + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. logic.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 +%1: + jz %4 +} by { + ; Peephole 242.c avoided branch jnz to jz + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 + jz %4 +%1: +} if labelRefCount %1 1 + +replace { + jnz %1 +%1: +} by { + ; Peephole 243a jump optimization +} if labelRefCount %1 1 + +replace { + jz %1 +%1: +} by { + ; Peephole 243b jump optimization +} if labelRefCount %1 1 + + +// This allows non-interrupt and interrupt code to safely compete +// for a resource without the non-interrupt code having to disable +// interrupts: +// volatile bit resource_is_free; +// if( resource_is_free ) { +// resource_is_free=0; do_something; resource_is_free=1; +// } +replace { + jnb %1,%2 +%3: + clr %1 +} by { + ; Peephole 244.a using atomic test and clear + jbc %1,%3 + sjmp %2 +%3: +} if labelRefCount %3 0 + +replace { + jb %1,%2 + ljmp %3 +%2: + clr %1 +} by { + ; Peephole 244.b using atomic test and clear + jbc %1,%2 + ljmp %3 +%2: +} if labelRefCount %2 1 + diff --git a/src/ds390/ralloc.c b/src/ds390/ralloc.c new file mode 100644 index 0000000..77bbc5a --- /dev/null +++ b/src/ds390/ralloc.c @@ -0,0 +1,3501 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. (DS80C390) specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +#define D(x) + +extern char **fReturnDS390; + +/* Global data */ +static struct +{ + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + bitVect *allBitregs; /* all bit registers */ +} +_G; + +/* Shared with gen.c */ +int ds390_ptrRegReq; /* one byte pointer register required */ + +/* 8051 registers */ +reg_info regs390[] = { + + {REG_GPR, R2_IDX, REG_GPR, "r2", "ar2", "0", 2, 1, 1}, + {REG_GPR, R3_IDX, REG_GPR, "r3", "ar3", "0", 3, 1, 1}, + {REG_GPR, R4_IDX, REG_GPR, "r4", "ar4", "0", 4, 1, 1}, + {REG_GPR, R5_IDX, REG_GPR, "r5", "ar5", "0", 5, 1, 1}, + {REG_GPR, R6_IDX, REG_GPR, "r6", "ar6", "0", 6, 1, 1}, + {REG_GPR, R7_IDX, REG_GPR, "r7", "ar7", "0", 7, 1, 1}, + {REG_PTR, R0_IDX, REG_PTR, "r0", "ar0", "0", 0, 1, 1}, + {REG_PTR, R1_IDX, REG_PTR, "r1", "ar1", "0", 1, 1, 1}, + {REG_GPR, DPL_IDX, REG_GPR, "dpl", "dpl", "dpl", 0, 0, 0}, + {REG_GPR, DPH_IDX, REG_GPR, "dph", "dph", "dph", 0, 0, 0}, + {REG_GPR, DPX_IDX, REG_GPR, "dpx", "dpx", "dpx", 0, 0, 0}, + {REG_GPR, B_IDX, REG_GPR, "b", "b", "b", 0, 0, 0}, + {REG_BIT, B0_IDX, REG_BIT, "b0", "b0", "bits", 0, 1, 0}, + {REG_BIT, B1_IDX, REG_BIT, "b1", "b1", "bits", 1, 1, 0}, + {REG_BIT, B2_IDX, REG_BIT, "b2", "b2", "bits", 2, 1, 0}, + {REG_BIT, B3_IDX, REG_BIT, "b3", "b3", "bits", 3, 1, 0}, + {REG_BIT, B4_IDX, REG_BIT, "b4", "b4", "bits", 4, 1, 0}, + {REG_BIT, B5_IDX, REG_BIT, "b5", "b5", "bits", 5, 1, 0}, + {REG_BIT, B6_IDX, REG_BIT, "b6", "b6", "bits", 6, 1, 0}, + {REG_BIT, B7_IDX, REG_BIT, "b7", "b7", "bits", 7, 1, 0}, + {REG_GPR, X8_IDX, REG_GPR, "x8", "x8", "xreg", 0, 0, 0}, + {REG_GPR, X9_IDX, REG_GPR, "x9", "x9", "xreg", 1, 0, 0}, + {REG_GPR, X10_IDX, REG_GPR, "x10", "x10", "xreg", 2, 0, 0}, + {REG_GPR, X11_IDX, REG_GPR, "x11", "x11", "xreg", 3, 0, 0}, + {REG_GPR, X12_IDX, REG_GPR, "x12", "x12", "xreg", 4, 0, 0}, + {REG_CND, CND_IDX, REG_GPR, "C", "psw", "xreg", 0, 0, 0}, + {0, DPL1_IDX, 0, "dpl1", "dpl1", "dpl1", 0, 0, 0}, + {0, DPH1_IDX, 0, "dph1", "dph1", "dph1", 0, 0, 0}, + {0, DPX1_IDX, 0, "dpx1", "dpx1", "dpx1", 0, 0, 0}, + {0, DPS_IDX, 0, "dps", "dps", "dps", 0, 0, 0}, + {0, A_IDX, 0, "a", "acc", "acc", 0, 0, 0}, + {0, AP_IDX, 0, "ap", "ap", "ap", 0, 0, 0}, +}; + +int ds390_nRegs = 13; +int ds390_nBitRegs = 0; + +static void spillThis (symbol *); +static void freeAllRegs (); +static iCode *packRegsDPTRuse (operand *); +static int packRegsDPTRnuse (operand *, unsigned); + + + +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocReg (short type) +{ + int i; + + for (i = 0; i < ds390_nRegs; i++) + { + + /* if type is given as 0 then any + free register will do */ + if (!type && regs390[i].isFree) + { + regs390[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s390[i]; + } + /* otherwise look for specific type of register */ + if (regs390[i].isFree && regs390[i].type == type) + { + regs390[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s390[i]; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* ds390_regWithIdx - returns pointer to register with index number*/ +/*-----------------------------------------------------------------*/ +reg_info * +ds390_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < sizeof (regs390) / sizeof (reg_info); i++) + if (regs390[i].rIdx == idx) + return ®s390[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (reg_info *reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +static void +useReg (reg_info *reg) +{ + reg->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ + int i; + int nfr = 0; + + for (i = 0; i < ds390_nRegs; i++) + if (regs390[i].isFree && regs390[i].type == type) + nfr++; + return nfr; +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} + +/*-----------------------------------------------------------------*/ +/* isOperandInReg - returns true if operand is currently in regs */ +/*-----------------------------------------------------------------*/ +static int +isOperandInReg (operand * op) +{ + if (!IS_SYMOP (op)) + return 0; + if (OP_SYMBOL (op)->ruonly) + return 1; + if (OP_SYMBOL (op)->accuse) + return 1; + if (OP_SYMBOL (op)->dptr) + return 1; + return bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (op)->key); +} + +/* When the spill locations aren't fully initialized, the usual + * isOperandInFarSpace() function may return false for a spilled + * operand that will ultimately end up in far space, but is not + * quite there yet. This function returns TRUE if the operand + * is either currently in far space or will be by the time code + * generation begins */ +static bool +isOperandInFarSpace2 (operand * op) +{ + symbol * opsym; + + if (isOperandInFarSpace (op)) + return TRUE; + + if (!IS_ITEMP (op)) + return FALSE; + + opsym = OP_SYMBOL (op); + if (isOperandInReg (op)) + return FALSE; + + if ((currFunc && IFFUNC_ISREENT (currFunc->type)) || options.stackAuto) + return FALSE; /* Will spill to internal stack */ + + if (options.model == MODEL_SMALL) + return FALSE; /* Will spill to internal data memory */ + + return TRUE; /* No other option; must spill to external data memory */ +} + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; + +} + +/*-----------------------------------------------------------------*/ +/* bitType - will return 1 if the symbol has type REG_BIT */ +/*-----------------------------------------------------------------*/ +static int +bitType (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->regType == REG_BIT ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 0 : 1); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* directSpilLoc - will return 1 if the spillocation is in direct */ +/*-----------------------------------------------------------------*/ +static int +directSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + if (sym->usl.spillLoc && (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil location */ +/* but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((sym->usl.spillLoc && !sym->uptr) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + + /* if usage is the same then prefer + to spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; sym = setNextItem (itmpStack)) + { + if (bitVectBitValue (sym->clashes, fsym->key)) + return 0; + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && noOverLap (sym->usl.itmpStack, fsym) && getSize (sym->type) >= getSize (fsym->type) + && (IS_BIT (sym->type) == IS_BIT (fsym->type))) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + reg_info *r0, *r1; + int k; + + if (!_G.regAssigned || bitVectIsZero (_G.regAssigned)) + return; + + r0 = ds390_regWithIdx (R0_IDX); + r1 = ds390_regWithIdx (R1_IDX); + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; lrsym = hTabNextItem (liveRanges, &k)) + { + int j; + + /* if no registers assigned to it or spilt */ + /* if it does not overlap this then + no need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || (lrsym->liveTo < forSym->liveFrom)) + continue; + + /* go thru the registers : if it is either + r0 or r1 then spill it */ + for (j = 0; j < lrsym->nRegs; j++) + if (lrsym->regs[j] == r0 || lrsym->regs[j] == r1) + { + spillThis (lrsym); + break; + } + } + +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model, noOverlay; + struct dbuf_s dbuf; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + if (!IS_BIT (sloc->etype)) + { + if (options.model == MODEL_SMALL) + { + SPEC_SCLS (sloc->etype) = S_DATA; + } + else + { + SPEC_SCLS (sloc->etype) = S_XDATA; + } + } + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE (sloc->etype) = 0; + SPEC_ABSA (sloc->etype) = 0; + + /* we don't allow it to be allocated + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + and turn off overlaying + */ + + useXstack = options.useXstack; + model = options.model; + noOverlay = options.noOverlay; + options.noOverlay = 1; + + /* options.model = options.useXstack = 0; */ + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; + options.noOverlay = noOverlay; + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + +/* if (sym->_G.stackSpil) */ +/* return TRUE; */ + + if (!sym->usl.spillLoc) + return FALSE; + + etype = getSpec (sym->usl.spillLoc->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it has spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + + if (sym->regs[i]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to some + LIVE ranges */ + if (!ds390_ptrRegReq && isSpiltOnStack (sym) && !options.stack10bit) + { + ds390_ptrRegReq++; + spillLRWithPtrReg (sym); + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* remove incompatible registers */ + if ((forSym->regType == REG_PTR) || (forSym->regType == REG_GPR)) + { + selectS = liveRangesWith (lrcs, bitType, ebp, ic); + + for (sym = setFirstItem (selectS); sym; sym = setNextItem (selectS)) + { + bitVectUnSetBit (lrcs, sym->key); + } + } + + /* get all live ranges that are rematerializable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strncpyz (sym->rname, + sym->usl.spillLoc->rname[0] ? sym->usl.spillLoc->rname : sym->usl.spillLoc->name, sizeof (sym->rname)); + sym->spildir = 1; + /* mark it as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that not + used in the remainder of the block */ + if (!_G.blockSpil && !isiCodeInFunctionCall (ic) && (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (sym != forSym) + { + if (!sym->remat) + { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack , for which one? the least + used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; + +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = ssym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + bitVectUnSetBit (_G.totRegAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!ds390_ptrRegReq && isSpiltOnStack (ssym) && !options.stack10bit) + { + ds390_ptrRegReq++; + spillLRWithPtrReg (ssym); + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!ds390_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegBit - will try for Bit if not spill this */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBit (symbol * sym) +{ + reg_info *reg; + + /* try for a bit type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + spillThis (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtrNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtrNoSpil () +{ + reg_info *reg; + + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegGprNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGprNoSpil () +{ + + reg_info *reg; + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!ds390_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegBitNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBitNoSpil () +{ + reg_info *reg; + + /* try for a bit type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol *sym, reg_info *reg) +{ + int i; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + for (reg = 0; reg < ds390_nRegs; reg++) + { + if (regs390[reg].isFree) + { + ic->riu &= ~(1 << regs390[reg].offset); + } + else + { + ic->riu |= (1 << regs390[reg].offset); + BitBankUsed |= (reg >= B0_IDX); + } + } +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + symbol *result; + + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && ic->prev->op == IPOP && !ic->prev->parmPush && !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && !(SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || ic->op == RETURN || POINTER_SET (ic)) && (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + sym->nRegs) >= result->nRegs)) + { + + for (i = 0; i < result->nRegs; i++) + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, result->key); + + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + /* first check if there are any available registers + of the type required */ + if (rt == REG_PTR) + { + /* special case for pointer type + if pointer type not avlb then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return 0; + if (nFreeRegs (REG_GPR) >= nr) + return 0; + } + else if (rt == REG_BIT) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (ds390_ptrRegReq) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (nFreeRegs (REG_PTR) + nFreeRegs (REG_GPR) >= nr) + return 0; + } + } + + /* it will cause a spil */ + return 1; +} + +/*------------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to res- */ +/* ult and operand, if this happens make sure they are in the same */ +/* position as the operand otherwise chaos results */ +/*------------------------------------------------------------------*/ +static int +positionRegs (symbol *result, symbol *opsym, int chOp) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + int change = 0; + + /* if the result has been spilt then cannot share */ + if (result->isspilt || opsym->isspilt) + return 0; + + for (;;) + { + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = 1; + goto xchgPositions; + } +xchgPositions: + if (shared) + if (!chOp) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + change++; + } + else + { + reg_info *tmp = opsym->regs[i]; + opsym->regs[i] = opsym->regs[j]; + opsym->regs[j] = tmp; + change++; + } + else + return change; + } +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate the registers of the */ +/* return value to the result, if this happens make sure they are */ +/* in the same position as the return value otherwise chaos results*/ +/*-----------------------------------------------------------------*/ +static int +positionRegsReturned (symbol *result) +{ + int count = result->nRegs; + int i, j = 0, shared = 0; + int change = 0; + + /* if the result has been spilt then cannot share */ + if (result->isspilt) + return 0; + + for (;;) + { + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (!strcmp(result->regs[i]->name, fReturnDS390[j]) && i != j) + { + shared = 1; + goto xchgPositions; + } +xchgPositions: + if (shared) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + change++; + } + else + return change; + } +} + +/*-----------------------------------------------------------------*/ +/* unusedLRS - returns a bitVector of liveranges not used in 'ebp' */ +/*-----------------------------------------------------------------*/ +bitVect * +unusedLRs (eBBlock * ebp) +{ + bitVect *ret = NULL; + symbol *sym; + int key; + + if (!ebp) + return NULL; + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + + if (notUsedInBlock (sym, ebp, NULL)) + { + ret = bitVectSetBit (ret, sym->key); + } + } + + return ret; +} + +/*-----------------------------------------------------------------*/ +/* deassignUnsedLRs - if this baisc block ends in a return then */ +/* deassign symbols not used in this block */ +/*-----------------------------------------------------------------*/ +bitVect * +deassignUnsedLRs (eBBlock * ebp) +{ + bitVect *unused = NULL; + int i; + + switch (returnAtEnd (ebp)) + { + case 2: /* successor block ends in a return */ + unused = unusedLRs ((eBBlock *) setFirstItem (ebp->succList)); + /* fall thru */ + case 1: /* this block ends in a return */ + unused = bitVectIntersect (unused, unusedLRs (ebp)); + break; + } + + if (unused) + { + for (i = 0; i < unused->size; i++) + { + + /* if unused */ + if (bitVectBitValue (unused, i)) + { + + /* if assigned to registers */ + if (bitVectBitValue (_G.regAssigned, i)) + { + symbol *sym; + int j; + + sym = hTabItemWithKey (liveRanges, i); + /* remove it from regassigned & mark the + register free */ + bitVectUnSetBit (_G.regAssigned, i); + for (j = 0; j < sym->nRegs; j++) + freeReg (sym->regs[j]); + } + else + { + /* not assigned to registers : remove from set */ + bitVectUnSetBit (unused, i); + } + } + } + } + return unused; +} + +/*-----------------------------------------------------------------*/ +/* reassignUnusedLRs - put registers to unused Live ranges */ +/*-----------------------------------------------------------------*/ +void +reassignUnusedLRs (bitVect * unused) +{ + int i; + if (!unused) + return; + + for (i = 0; i < unused->size; i++) + { + /* if unused : means it was assigned to registers before */ + if (bitVectBitValue (unused, i)) + { + symbol *sym; + int j; + + /* put it back into reg set */ + bitVectSetBit (_G.regAssigned, i); + + sym = hTabItemWithKey (liveRanges, i); + /* make registers busy */ + for (j = 0; j < sym->nRegs; j++) + sym->regs[j]->isFree = 0; + } + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand * op, iCode * ic) +{ + symbol *sym; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) + return; + if (!sym->nRegs) + return; + if (sym->regs[0]) + return; + + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name); + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { /* ebbs */ + + iCode *ic; + bitVect *unusedLRs = NULL; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + unusedLRs = deassignUnsedLRs (ebbs[i]); + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage (ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || (IC_RESULT (ic) && POINTER_SET (ic))) + continue; + + /* now we need to allocate registers + only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j; + int ptrRegSet = 0; + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || sym->isspilt || bitVectBitValue (_G.regAssigned, sym->key) || sym->liveTo <= ic->seq) + continue; + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + + willCS = willCauseSpill (sym->nRegs, sym->regType); + /* if this is a bit variable then don't use precious registers + along with expensive bit-to-char conversions but just spill + it */ + if (willCS && SPEC_NOUN (sym->etype) == V_BIT) + { + spillThis (sym); + continue; + } + + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + spillable = computeSpillable (ic); + if (sym->remat || (willCS && bitVectIsZero (spillable))) + { + spillThis (sym); + continue; + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) + { + if (sym->usl.spillLoc) + { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) + { + spillThis (sym); + continue; + } + } + else + { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith (spillable, noSpilLoc, ebbs[i], ic)) + { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) + { + spillThis (sym); + continue; + } + } + } + } + + /* if we need ptr regs for the right side + then mark it */ + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) && getSize (OP_SYMBOL (IC_LEFT (ic))->type) <= (unsigned) NEARPTRSIZE) + { + ds390_ptrRegReq++; + ptrRegSet = 1; + } + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + for (j = 0; j < sym->nRegs; j++) + { + sym->regs[j] = NULL; + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else if (sym->regType == REG_BIT) + sym->regs[j] = getRegBit (sym); + else + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + + /* if the allocation failed which means + this was spilt then break */ + if (!sym->regs[j]) + break; + } + + /* if it shares registers with operands make sure + that they are in the same position */ + if (!POINTER_SET (ic) && !POINTER_GET (ic)) + { + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 0); + } + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 0); + } + } + + if (ic->op == CALL || ic->op == PCALL || ic->op == RECEIVE) + { + positionRegsReturned (OP_SYMBOL (IC_RESULT (ic))); + } + + if (ptrRegSet) + { + ds390_ptrRegReq--; + ptrRegSet = 0; + } + + } + } + reassignUnusedLRs (unusedLRs); + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* fillGaps - Try to fill in the Gaps left by Pass1 */ +/*-----------------------------------------------------------------*/ +static void +fillGaps () +{ + symbol *sym = NULL; + int key = 0; + int loop = 0, change; + int pass; + + if (getenv ("DISABLE_FILL_GAPS")) + return; + + /* First try to do DPTRuse once more since now we know what got into + registers */ + + while (loop++ < 10) + { + change = 0; + + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + int size = getSize (sym->type); + + if (sym->liveFrom == sym->liveTo) + continue; + + if (sym->uptr && sym->dptr == 0 && !sym->ruonly && size < 4 && size > 1) + { + + if (packRegsDPTRuse (operandFromSymbol (sym))) + { + + /* if this was assigned to registers then */ + if (bitVectBitValue (_G.totRegAssigned, sym->key)) + { + /* take it out of the register assigned set */ + bitVectUnSetBit (_G.totRegAssigned, sym->key); + } + else if (sym->usl.spillLoc) + { + sym->usl.spillLoc->allocreq--; + sym->usl.spillLoc = NULL; + } + + sym->nRegs = 0; + sym->isspilt = sym->spillA = 0; + continue; + } + + /* try assigning other dptrs */ + if (sym->dptr == 0 && packRegsDPTRnuse (operandFromSymbol (sym), 1) && !getenv ("DPTRnDISABLE")) + { + /* if this was ssigned to registers then */ + if (bitVectBitValue (_G.totRegAssigned, sym->key)) + { + /* take it out of the register assigned set */ + bitVectUnSetBit (_G.totRegAssigned, sym->key); + } + else if (sym->usl.spillLoc) + { + sym->usl.spillLoc->allocreq--; + sym->usl.spillLoc = NULL; + } + sym->nRegs = 0; + sym->isspilt = sym->spillA = 0; + } + } + } + + /* look for liveranges that were spilt by the allocator */ + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + + int i; + int pdone = 0; + + if (!sym->spillA || !sym->clashes || sym->remat) + continue; + if (!sym->uses || !sym->defs) + continue; + /* find the liveRanges this one clashes with, that are + still assigned to registers & mark the registers as used */ + for (i = 0; i < sym->clashes->size; i++) + { + int k; + symbol *clr; + + if (bitVectBitValue (sym->clashes, i) == 0 || /* those that clash with this */ + bitVectBitValue (_G.totRegAssigned, i) == 0) /* and are still assigned to registers */ + continue; + + clr = hTabItemWithKey (liveRanges, i); + assert (clr); + + /* mark these registers as used */ + for (k = 0; k < clr->nRegs; k++) + useReg (clr->regs[k]); + } + + if (willCauseSpill (sym->nRegs, sym->regType)) + { + /* NOPE :( clear all registers & and continue */ + freeAllRegs (); + continue; + } + + /* THERE IS HOPE !!!! */ + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regType == REG_PTR) + sym->regs[i] = getRegPtrNoSpil (); + else if (sym->regType == REG_BIT) + sym->regs[i] = getRegBitNoSpil (); + else + sym->regs[i] = getRegGprNoSpil (); + } + + /* For all its definitions check if the registers + allocated needs positioning NOTE: we can position + only ONCE if more than One positioning required + then give up. + We may need to perform the checks twice; once to + position the registers as needed, the second to + verify any register repositioning is still + compatible. + */ + sym->isspilt = 0; + for (pass = 0; pass < 2; pass++) + { + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (SKIP_IC (ic)) + continue; + assert (isSymbolEqual (sym, OP_SYMBOL (IC_RESULT (ic)))); /* just making sure */ + /* if left is assigned to registers */ + if (IS_SYMOP (IC_LEFT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_LEFT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_LEFT (ic)), 0) > 0); + } + if (IS_SYMOP (IC_RIGHT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RIGHT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RIGHT (ic)), 0) > 0); + } + if (pdone > 1) + break; + } + } + for (i = 0; i < sym->uses->size; i++) + { + if (bitVectBitValue (sym->uses, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (SKIP_IC (ic)) + continue; + if (POINTER_SET (ic) || POINTER_GET (ic)) + continue; + + /* if result is assigned to registers */ + if (IS_SYMOP (IC_RESULT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RESULT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RESULT (ic)), 0) > 0); + } + if (pdone > 1) + break; + } + } + if (pdone == 0) + break; /* second pass only if regs repositioned */ + if (pdone > 1) + break; + } + /* had to position more than once GIVE UP */ + if (pdone > 1) + { + /* UNDO all the changes we made to try this */ + sym->isspilt = 1; + for (i = 0; i < sym->nRegs; i++) + { + sym->regs[i] = NULL; + } + freeAllRegs (); + D (fprintf (stderr, "Fill Gap gave up due to positioning for " + "%s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + continue; + } + D (fprintf (stderr, "FILLED GAP for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + sym->isspilt = sym->spillA = 0; + sym->usl.spillLoc->allocreq--; + sym->usl.spillLoc = NULL; + freeAllRegs (); + change++; + } + if (!change) + break; + } +} + +/*-----------------------------------------------------------------*/ +/* findAllBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +static bitVect * +findAllBitregs (void) +{ + bitVect *rmask = newBitVect (ds390_nRegs); + int j; + + for (j = 0; j < ds390_nRegs; j++) + { + if (regs390[j].type == REG_BIT) + rmask = bitVectSetBit (rmask, regs390[j].rIdx); + } + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* ds390_allBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +bitVect * +ds390_allBitregs (void) +{ + return _G.allBitregs; +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +ds390_rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (ds390_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (ds390_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, ds390_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, ds390_rUmaskForOp (IC_JTCOND (ic))); + + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, ds390_rUmaskForOp (IC_LEFT (ic))); + + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, ds390_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, ds390_rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (ds390_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "createRegMask cannot find live range"); + fprintf (stderr, "\tmissing live range: key=%d\n", j); + exit (0); + } +#if 0 + /* special case for ruonly */ + if (sym->ruonly && sym->liveFrom != sym->liveTo) + { + int size = getSize (sym->type); + int j = DPL_IDX; + for (k = 0; k < size; k++) + ic->rMask = bitVectSetBit (ic->rMask, j++); + continue; + } +#endif + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + if (sym->regs[k]) + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static char * +rematStr (symbol * sym) +{ + iCode *ic = sym->rematiCode; + int offset = 0; + static struct dbuf_s dbuf; + + while (1) + { + /* if plus adjust offset to right hand side */ + if (ic->op == '+') + { + offset += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* if minus adjust offset to right hand side */ + if (ic->op == '-') + { + offset -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* cast then continue */ + if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + continue; + } + /* we reached the end */ + break; + } + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, offset >= 0 ? '+' : '-', abs (offset) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + return dbuf_detach_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum () +{ + symbol *sym; + int k; + iCode *ic; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + + /* if the live range is a temporary */ + if (sym->isitmp) + { + + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer */ + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (sym->defs))) && + POINTER_GET (ic) && !IS_BITVAR (sym->etype) && (aggrToPtrDclType (operandType (IC_LEFT (ic)), FALSE) == POINTER)) + { + + if (ptrPseudoSymSafe (sym, ic)) + { + char *s = rematStr (OP_SYMBOL (IC_LEFT (ic))); + ptrPseudoSymConvert (sym, ic, s); + Safe_free (s); + continue; + } + + /* if in data space or idata space then try to + allocate pointer register */ + + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 or 0 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && IS_PTR (sym->type) && sym->uptr) + sym->regType = REG_PTR; + else if (IS_BIT (sym->type)) + sym->regType = REG_BIT; + else + sym->regType = REG_GPR; + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } + +} + +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +static void +freeAllRegs () +{ + int i; + + for (i = 0; i < ds390_nRegs; i++) + regs390[i].isFree = 1; + + for (i = B0_IDX; i < ds390_nBitRegs; i++) + regs390[i].isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > 1) + return NULL; + + if (POINTER_SET (dic) && getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) > 1) + return NULL; + + /* if any tree is a true symbol in far space */ + if (IC_RESULT (dic) && IS_TRUE_SYMOP (IC_RESULT (dic)) && isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) + { + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP || dic->op == '-') && IS_OP_LITERAL (IC_RIGHT (dic))) + return NULL; + else + return dic; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ +#if 0 + if (isOperandInFarSpace (IC_RESULT (ic))) + { + if ((dic = farSpacePackable (ic))) + goto pack; + else + return 0; + } +#else + if (isOperandInFarSpace (IC_RESULT (ic)) && !farSpacePackable (ic)) + { + return 0; + } +#endif + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if there is a function call then don't pack it */ + if ((dic->op == CALL || dic->op == PCALL)) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && (IC_LEFT (dic)->key == IC_RESULT (ic)->key || IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/*------------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*------------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + /* This routine is used to find sequences like + iTempAA = FOO; + ...; (intervening ops don't use iTempAA or modify FOO) + blah = blah + iTempAA; + + and eliminate the use of iTempAA, freeing up its register for + other uses. + */ + + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if definition by assignment */ + if (dic->op == '=' && !POINTER_SET (dic) && IC_RESULT (dic)->key == op->key +/* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + ) + { + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then return + FALSE */ + if (!IS_SYMOP (IC_RIGHT (dic))) + return NULL; + + /* if the symbol is in far space then we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + return NULL; + } + + break; + + } + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; + + +} + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + int change = 0; + + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + if (IS_ITEMP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_LEFT (ic), ic); + iCode *sic; + + if (!dic) + goto right; + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_LEFT (ic)->key); + sic->rlive = bitVectSetBit (sic->rlive, IC_RIGHT (dic)->key); + } + + wassert (IS_SYMOP (IC_LEFT (ic))); + wassert (IS_SYMOP (IC_RIGHT (dic))); + OP_SYMBOL (IC_LEFT (ic)) = OP_SYMBOL (IC_RIGHT (dic)); + OP_SYMBOL (IC_LEFT (ic))->liveTo = ic->seq; + IC_LEFT (ic)->key = OP_KEY (IC_RIGHT (dic)); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + /* do the same for the right operand */ +right: + if (!change && IS_ITEMP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + iCode *sic; + + if (!dic) + return change; + + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) + { + sym_link *etype = getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return change; + } + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RIGHT (ic)->key); + sic->rlive = bitVectSetBit (sic->rlive, IC_RIGHT (dic)->key); + } + + wassert (IS_SYMOP (IC_RIGHT (ic))); + wassert (IS_SYMOP (IC_RIGHT (dic))); + OP_SYMBOL (IC_RIGHT (ic)) = OP_SYMBOL (IC_RIGHT (dic)); + IC_RIGHT (ic)->key = OP_KEY (IC_RIGHT (dic)); + OP_SYMBOL (IC_RIGHT (ic))->liveTo = ic->seq; + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + return change; +} + + + +/*-------------------------------------------------------------------*/ +/* packRegsDPTRnuse - color live ranges that can go into extra DPTRS */ +/*-------------------------------------------------------------------*/ +static int +packRegsDPTRnuse (operand * op, unsigned dptr) +{ + symbol * opsym; + int i, key; + iCode *ic; + + if (!IS_SYMOP (op) || !IS_ITEMP (op)) + return 0; + opsym = OP_SYMBOL (op); + + if (opsym->remat || opsym->ruonly || opsym->dptr) + return 0; + + /* first check if any overlapping liverange has already been + assigned to this DPTR */ + if (opsym->clashes) + { + for (i = 0; i < opsym->clashes->size; i++) + { + symbol *sym; + if (bitVectBitValue (opsym->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->dptr == dptr) + return 0; + } + } + } + + /* future for more dptrs */ + if (dptr > 1) + { + opsym->dptr = dptr; + return 1; + } + + /* DPTR1 is special since it is also used as a scratch by the backend. + so we walk thru the entire live range of this operand and make sure + DPTR1 will not be used by the backend. The logic here is to find out if + more than one operand in an icode is in far space then we give up : we + don't keep it live across functions for now + */ + + ic = hTabFirstItemWK (iCodeSeqhTab, opsym->liveFrom); + for (; ic && ic->seq <= opsym->liveTo; ic = hTabNextItem (iCodeSeqhTab, &key)) + { + int nfs = 0; + + if (ic->op == CALL || ic->op == PCALL) + return 0; + + /* single operand icode are ok */ + if (ic->op == IFX || ic->op == IPUSH) + continue; + + if (ic->op == SEND) + { + if (ic->argreg != 1) + return 0; + else + continue; + } + /* four special cases first */ + if (POINTER_GET (ic) && !isOperandEqual (IC_LEFT (ic), op) && /* pointer get */ + !OP_SYMBOL (IC_LEFT (ic))->ruonly && /* with result in far space */ + (isOperandInFarSpace2 (IC_RESULT (ic)) && !isOperandInReg (IC_RESULT (ic)))) + { + return 0; + } + + if (POINTER_GET (ic) && !isOperandEqual (IC_LEFT (ic), op) && /* pointer get */ + !OP_SYMBOL (IC_LEFT (ic))->ruonly && /* with left in far space */ + (isOperandInFarSpace2 (IC_LEFT (ic)) && !isOperandInReg (IC_LEFT (ic)))) + { + return 0; + } + + if (POINTER_SET (ic) && !isOperandEqual (IC_RESULT (ic), op) && /* pointer set */ + !OP_SYMBOL (IC_RESULT (ic))->ruonly && /* with right in far space */ + (isOperandInFarSpace2 (IC_RIGHT (ic)) && !isOperandInReg (IC_RIGHT (ic)))) + { + return 0; + } + + if (POINTER_SET (ic) && !isOperandEqual (IC_RESULT (ic), op) && /* pointer set */ + !OP_SYMBOL (IC_RESULT (ic))->ruonly && /* with result in far space */ + (isOperandInFarSpace2 (IC_RESULT (ic)) && !isOperandInReg (IC_RESULT (ic)))) + { + return 0; + } + + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && /* if symbol operand */ + !isOperandEqual (IC_RESULT (ic), op) && /* not the same as this */ + ((isOperandInFarSpace2 (IC_RESULT (ic)) || /* in farspace or */ + OP_SYMBOL (IC_RESULT (ic))->onStack) && /* on the stack */ + !isOperandInReg (IC_RESULT (ic)))) /* and not in register */ + { + nfs++; + } + /* same for left */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && /* if symbol operand */ + !isOperandEqual (IC_LEFT (ic), op) && /* not the same as this */ + ((isOperandInFarSpace2 (IC_LEFT (ic)) || /* in farspace or */ + OP_SYMBOL (IC_LEFT (ic))->onStack) && /* on the stack */ + !isOperandInReg (IC_LEFT (ic)))) /* and not in register */ + { + nfs++; + } + /* same for right */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && /* if symbol operand */ + !isOperandEqual (IC_RIGHT (ic), op) && /* not the same as this */ + ((isOperandInFarSpace2 (IC_RIGHT (ic)) || /* in farspace or */ + OP_SYMBOL (IC_RIGHT (ic))->onStack) && /* on the stack */ + !isOperandInReg (IC_RIGHT (ic)))) /* and not in register */ + { + nfs++; + } + + // Check that no other ops in this range have been assigned to dptr1. + // I don't understand why this is not caught by the first check, above. + // But it isn't always, see bug 769624. + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->dptr == 1)) + { + //fprintf(stderr, "dptr1 already in use in live range # 1\n"); + return 0; + } + + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && (OP_SYMBOL (IC_LEFT (ic))->dptr == 1)) + { + //fprintf(stderr, "dptr1 already in use in live range # 2\n"); + return 0; + } + + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && (OP_SYMBOL (IC_RIGHT (ic))->dptr == 1)) + { + //fprintf(stderr, "dptr1 already in use in live range # 3\n"); + return 0; + } + + if (nfs && IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->ruonly) + return 0; + + if (nfs > 1) + return 0; + } + opsym->dptr = dptr; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* packRegsDPTRuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsDPTRuse (operand * op) +{ + /* go thru entire liveRange of this variable & check for + other possible usage of DPTR, if we don't find it then + assign this to DPTR (ruonly) + */ + int i, key; + symbol *sym; + iCode *ic, *dic; + sym_link *type; + + if (!IS_SYMOP (op) || !IS_ITEMP (op)) + return NULL; + if (OP_SYMBOL (op)->remat || OP_SYMBOL (op)->ruonly) + return NULL; + + /* first check if any overlapping liverange has already been + assigned to DPTR */ + if (OP_SYMBOL (op)->clashes) + { + for (i = 0; i < OP_SYMBOL (op)->clashes->size; i++) + { + if (bitVectBitValue (OP_SYMBOL (op)->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->ruonly) + return NULL; + } + } + } + + /* no then go thru this guys live range */ + dic = ic = hTabFirstItemWK (iCodeSeqhTab, OP_SYMBOL (op)->liveFrom); + for (; ic && ic->seq <= OP_SYMBOL (op)->liveTo; ic = hTabNextItem (iCodeSeqhTab, &key)) + { + if (SKIP_IC3 (ic)) + continue; + + /* if PCALL cannot be sure give up */ + if (ic->op == PCALL) + return NULL; + + /* if SEND & not the first parameter then give up */ + if (ic->op == SEND && ic->argreg != 1 && + ((isOperandInFarSpace2 (IC_LEFT (ic)) && !isOperandInReg (IC_LEFT (ic))) || isOperandEqual (op, IC_LEFT (ic)))) + return NULL; + + /* if CALL then make sure it is VOID || return value not used + or the return value is assigned to this one */ + if (ic->op == CALL) + { + if (OP_SYMBOL (IC_RESULT (ic))->liveTo == OP_SYMBOL (IC_RESULT (ic))->liveFrom) + continue; + type = operandType (IC_RESULT (ic)); + if (getSize (type) == 0 || isOperandEqual (op, IC_RESULT (ic))) + continue; + return NULL; + } + + /* special case of add with a [remat] */ + if (ic->op == '+' && + IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->remat && + isOperandInFarSpace2 (IC_RIGHT (ic)) && !isOperandInReg (IC_RIGHT (ic))) + { + return NULL; + } + + /* special cases */ + /* pointerGet */ + if (POINTER_GET (ic) && !isOperandEqual (IC_LEFT (ic), op) && getSize (operandType (IC_LEFT (ic))) > 1) + return NULL; + + /* pointerSet */ + if (POINTER_SET (ic) && !isOperandEqual (IC_RESULT (ic), op) && getSize (operandType (IC_RESULT (ic))) > 1) + return NULL; + + /* conditionals can destroy 'b' - make sure B wont + be used in this one */ + if ((IS_CONDITIONAL (ic) || ic->op == '*' || ic->op == '/' || + ic->op == LEFT_OP || ic->op == RIGHT_OP) && getSize (operandType (op)) > 3) + return NULL; + + /* if this is a cast to a bigger type */ + if (ic->op == CAST) + { + if (!IS_PTR (OP_SYM_TYPE (IC_RESULT (ic))) && + getSize (OP_SYM_TYPE (IC_RESULT (ic))) > getSize (OP_SYM_TYPE (IC_RIGHT (ic)))) + { + return NULL; + } + } + + /* general case */ + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && + !isOperandEqual (IC_RESULT (ic), op) && + (((isOperandInFarSpace2 (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->onStack) && + !isOperandInReg (IC_RESULT (ic))) || OP_SYMBOL (IC_RESULT (ic))->ruonly)) + return NULL; + + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && + !isOperandEqual (IC_RIGHT (ic), op) && + (OP_SYMBOL (IC_RIGHT (ic))->liveTo >= ic->seq || + IS_TRUE_SYMOP (IC_RIGHT (ic)) || + OP_SYMBOL (IC_RIGHT (ic))->ruonly) && + ((isOperandInFarSpace2 (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->onStack) && !isOperandInReg (IC_RIGHT (ic)))) + return NULL; + + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && + !isOperandEqual (IC_LEFT (ic), op) && + (OP_SYMBOL (IC_LEFT (ic))->liveTo >= ic->seq || + IS_TRUE_SYMOP (IC_LEFT (ic)) || + OP_SYMBOL (IC_LEFT (ic))->ruonly) && + ((isOperandInFarSpace2 (IC_LEFT (ic)) || OP_SYMBOL (IC_LEFT (ic))->onStack) && !isOperandInReg (IC_LEFT (ic)))) + return NULL; + + if (IC_LEFT (ic) && IC_RIGHT (ic) && + IS_ITEMP (IC_LEFT (ic)) && IS_ITEMP (IC_RIGHT (ic)) && + (isOperandInFarSpace2 (IC_LEFT (ic)) && !isOperandInReg (IC_LEFT (ic))) && + (isOperandInFarSpace2 (IC_RIGHT (ic)) && !isOperandInReg (IC_RIGHT (ic)))) + return NULL; + } + OP_SYMBOL (op)->ruonly = 1; + if (OP_SYMBOL (op)->usl.spillLoc) + { + if (OP_SYMBOL (op)->spillA) + OP_SYMBOL (op)->usl.spillLoc->allocreq--; + OP_SYMBOL (op)->usl.spillLoc = NULL; + } + return dic; +} + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype) || (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static void +packRegsForAccUse (iCode * ic) +{ + iCode *uic; + + /* if this is an aggregate, e.g. a one byte char array */ + if (IS_AGGREGATE (operandType (IC_RESULT (ic)))) + { + return; + } + + /* if we are calling a reentrant function that has stack parameters */ + if (ic->op == CALL && IFFUNC_ISREENT (operandType (IC_LEFT (ic))) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic)))) + return; + + if (ic->op == PCALL && + IFFUNC_ISREENT (operandType (IC_LEFT (ic))->next) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic))->next)) + return; + + /* if + or - then it has to be one byte result */ + if ((ic->op == '+' || ic->op == '-') && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* if shift operation make sure right side is not a literal */ + if (ic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (ic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (IS_BITWISE_OP (ic) && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) > 1) + return; + + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + return; + + if (ic->next != uic) + return; + + /* if it is a conditional branch then we definitely can */ + if (uic->op == IFX) + goto accuse; + + if (uic->op == JUMPTABLE) + return; + + /* if the usage is not is an assignment + or an arithmetic / bitwise / shift operation then not */ + if (POINTER_SET (uic) && getSize (aggrToPtr (operandType (IC_RESULT (uic)), FALSE)) > 1) + return; + + if (uic->op != '=' && !IS_ARITHMETIC_OP (uic) && !IS_BITWISE_OP (uic) && uic->op != LEFT_OP && uic->op != RIGHT_OP) + return; + + /* if used in ^ operation then make sure right is not a + literal */ + if (uic->op == '^' && isOperandLiteral (IC_RIGHT (uic))) + return; + + /* if shift operation make sure right side is not a literal */ + if (uic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + if (uic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + /* make sure that the result of this icode is not on the + stack, since acc is used to compute stack offset */ + if (isOperandOnStack (IC_RESULT (uic))) + return; + + /* if either one of them in far space then we cannot */ + if ((IS_TRUE_SYMOP (IC_LEFT (uic)) && + isOperandInFarSpace (IC_LEFT (uic))) || (IS_TRUE_SYMOP (IC_RIGHT (uic)) && isOperandInFarSpace (IC_RIGHT (uic)))) + return; + + /* if the usage has only one operand then we can */ + if (IC_LEFT (uic) == NULL || IC_RIGHT (uic) == NULL) + goto accuse; + + /* make sure this is on the left side if not + a '+' since '+' is commutative */ + if (ic->op != '+' && IC_LEFT (uic)->key != IC_RESULT (ic)->key) + return; + + /* if the other one is not on stack then we can */ + if (IC_LEFT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_RIGHT (uic)) || (IS_TRUE_SYMOP (IC_RIGHT (uic)) && !OP_SYMBOL (IC_RIGHT (uic))->onStack))) + goto accuse; + + if (IC_RIGHT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_LEFT (uic)) || (IS_TRUE_SYMOP (IC_LEFT (uic)) && !OP_SYMBOL (IC_LEFT (uic))->onStack))) + goto accuse; + + return; + +accuse: + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + +} + +/*-----------------------------------------------------------------*/ +/* packForPush - heuristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock ** ebpp, int blockno) +{ + iCode *dic, *lic; + bitVect *dbv; + struct eBBlock *ebp = ebpp[blockno]; + int disallowHiddenAssignment = 0; + + if ((ic->op != IPUSH && ic->op != SEND) || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + if (dic->eBBlockNum != ic->eBBlockNum) + return; + + if (IS_OP_VOLATILE (IC_RIGHT (dic))) + return; + + if ((IS_SYMOP (IC_RIGHT (dic)) && OP_SYMBOL (IC_RIGHT (dic))->addrtaken) || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS (IC_RIGHT (dic)); + for (lic = ic; lic && lic != dic; lic = lic->prev) + { + if (bitVectBitValue (dbv, lic->key)) + return; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC (operandType (IC_LEFT (ic)))) + { + sym_link *itype = operandType (IC_LEFT (ic)); + sym_link *ditype = operandType (IC_RIGHT (dic)); + + if (SPEC_USIGN (itype) != SPEC_USIGN (ditype) || SPEC_LONG (itype) != SPEC_LONG (ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL (IC_RIGHT (dic))->liveTo < OP_SYMBOL (IC_LEFT (ic))->liveTo) + { + OP_SYMBOL (IC_RIGHT (dic))->liveTo = OP_SYMBOL (IC_LEFT (ic))->liveTo; + OP_SYMBOL (IC_RIGHT (dic))->clashes = + bitVectUnion (OP_SYMBOL (IC_RIGHT (dic))->clashes, OP_SYMBOL (IC_LEFT (ic))->clashes); + } + for (lic = ic; lic && lic != dic; lic = lic->prev) + { + bitVectUnSetBit (lic->rlive, IC_LEFT (ic)->key); + if (IS_ITEMP (IC_RIGHT (dic))) + bitVectSetBit (lic->rlive, IC_RIGHT (dic)->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + IC_LEFT (ic) = IC_RIGHT (dic); + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock ** ebpp, int blockno) +{ + iCode *ic; + int change = 0; + eBBlock *ebp = ebpp[blockno]; + + while (1) + { + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) + { + /* Fix for bug #979599: */ + /* P0 &= ~1; */ + + /* Look for two subsequent iCodes with */ + /* iTemp := _c; */ + /* _c = iTemp & op; */ + /* and replace them by */ + /* iTemp := _c; */ + /* _c = _c & op; */ + if ((ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') && + ic->prev && + ic->prev->op == '=' && + IS_ITEMP (IC_LEFT (ic)) && + IC_LEFT (ic) == IC_RESULT (ic->prev) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev))) + { + iCode *ic_prev = ic->prev; + symbol *prev_result_sym = OP_SYMBOL (IC_RESULT (ic_prev)); + + ReplaceOpWithCheaperOp (&IC_LEFT (ic), IC_RESULT (ic)); + if (IC_RESULT (ic_prev) != IC_RIGHT (ic)) + { + bitVectUnSetBit (OP_USES (IC_RESULT (ic_prev)), ic->key); + if ( /*IS_ITEMP (IC_RESULT (ic_prev)) && */ + prev_result_sym->liveTo == ic->seq) + { + prev_result_sym->liveTo = ic_prev->seq; + } + } + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + + bitVectSetBit (ic->rlive, IC_RESULT (ic)->key); + + if (bitVectIsZero (OP_USES (IC_RESULT (ic_prev)))) + { + bitVectUnSetBit (ic->rlive, IC_RESULT (ic)->key); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic_prev)), ic_prev->key); + remiCodeFromeBBlock (ebp, ic_prev); + hTabDeleteItem (&iCodehTab, ic_prev->key, ic_prev, DELETE_ITEM, NULL); + } + } + + /* if this is an itemp & result of an address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + + } + + /* if this is an itemp & used as a pointer + & assigned to a literal then remat */ + if (IS_ASSIGN_ICODE (ic) && + IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && isOperandLiteral (IC_RIGHT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && !POINTER_SET (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode) && !isOperandGlobal (IC_RESULT (ic)) && /* due to bug 1618050 */ + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && IS_SYMOP (IC_RIGHT (ic)) && !OP_SYMBOL (IC_RESULT (ic))->isreqv && OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + sym_link *to_type = operandType (IC_LEFT (ic)); + sym_link *from_type = operandType (IC_RIGHT (ic)); + if (IS_GENPTR (to_type) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && IS_OP_LITERAL (IC_RIGHT (ic)))) + { + + //int i = operandLitValue(IC_RIGHT(ic)); + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + if (ic->op == RETURN && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + if (ic->op == RECEIVE && ic->argreg == 1 && IS_SYMOP (IC_RESULT (ic)) && getSize (operandType (IC_RESULT (ic))) <= 3) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (ic->op == SEND && ic->argreg == 1 && + IS_SYMOP (IC_LEFT (ic)) && getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) <= 3) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + if (!SKIP_IC2 (ic)) + { + /* if we are using a symbol on the stack + then we should say ds390_ptrRegReq */ + if (options.useXstack && ic->parmPush && (ic->op == IPUSH || ic->op == IPOP)) + ds390_ptrRegReq++; + if (ic->op == IFX && IS_SYMOP (IC_COND (ic))) + ds390_ptrRegReq += ((OP_SYMBOL (IC_COND (ic))->onStack ? !options.stack10bit : 0) + + OP_SYMBOL (IC_COND (ic))->iaccess + (SPEC_OCLS (OP_SYMBOL (IC_COND (ic))->etype) == idata)); + else if (ic->op == JUMPTABLE && IS_SYMOP (IC_JTCOND (ic))) + ds390_ptrRegReq += ((OP_SYMBOL (IC_JTCOND (ic))->onStack ? !options.stack10bit : 0) + + OP_SYMBOL (IC_JTCOND (ic))->iaccess + (SPEC_OCLS (OP_SYMBOL (IC_JTCOND (ic))->etype) == idata)); + else + { + if (IS_SYMOP (IC_LEFT (ic))) + ds390_ptrRegReq += ((OP_SYMBOL (IC_LEFT (ic))->onStack ? !options.stack10bit : 0) + + OP_SYMBOL (IC_LEFT (ic))->iaccess + (SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype) == idata)); + if (IS_SYMOP (IC_RIGHT (ic))) + ds390_ptrRegReq += ((OP_SYMBOL (IC_RIGHT (ic))->onStack ? !options.stack10bit : 0) + + OP_SYMBOL (IC_RIGHT (ic))->iaccess + + (SPEC_OCLS (OP_SYMBOL (IC_RIGHT (ic))->etype) == idata)); + if (IS_SYMOP (IC_RESULT (ic))) + ds390_ptrRegReq += ((OP_SYMBOL (IC_RESULT (ic))->onStack ? !options.stack10bit : 0) + + OP_SYMBOL (IC_RESULT (ic))->iaccess + + (SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype) == idata)); + } + } + + /* if the condition of an if instruction + is defined in the previous instruction and + this is the only usage then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + (IS_BITWISE_OP (ic) && isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } +#if 1 + /* reduce for support function calls */ + if (ic->supportRtn || ic->op == '+' || ic->op == '-') + packRegsForSupport (ic, ebp); +#endif + /* some cases the redundant moves can + can be eliminated for return statements . Can be elminated for the first SEND */ + if ((ic->op == RETURN || + ((ic->op == SEND || ic->op == RECEIVE) && ic->argreg == 1)) && !isOperandInFarSpace (IC_LEFT (ic)) && !options.model) + { + + packRegsDPTRuse (IC_LEFT (ic)); + } + + if (ic->op == CALL) + { + sym_link *ftype = operandType (IC_LEFT (ic)); + if (getSize (operandType (IC_RESULT (ic))) <= 4 && !IFFUNC_ISBUILTIN (ftype)) + { + packRegsDPTRuse (IC_RESULT (ic)); + } + } + + /* if pointer set & left has a size more than + one and right is not in far space */ + if (POINTER_SET (ic) && + !isOperandInFarSpace2 (IC_RIGHT (ic)) && + IS_SYMOP (IC_RESULT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + { + + packRegsDPTRuse (IC_RESULT (ic)); + } + + /* if pointer get */ + if (POINTER_GET (ic) && + !isOperandInFarSpace2 (IC_RESULT (ic)) && + IS_SYMOP (IC_LEFT (ic)) && + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + { + + packRegsDPTRuse (IC_LEFT (ic)); + } + + /* if this is a cast for intergral promotion then + check if it's the only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) + { + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType) && SPEC_USIGN (fromType) == SPEC_USIGN (toType)) + { + + iCode *dic = packRegsDPTRuse (IC_RIGHT (ic)); + if (dic) + { + if (IS_ARITHMETIC_OP (dic)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + else + OP_SYMBOL (IC_RIGHT (ic))->ruonly = 0; + } + } + else + { + + /* if the type from and type to are the same + then if this is the only use then packit */ + if (compareType (operandType (IC_RIGHT (ic)), operandType (IC_LEFT (ic))) == 1) + { + iCode *dic = packRegsDPTRuse (IC_RIGHT (ic)); + if (dic) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + IC_RESULT (dic) = IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + } + } + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH || ic->op == SEND) + { + packForPush (ic, ebpp, blockno); + } + + /* pack registers for accumulator use, when the + result of an arithmetic or bit wise operation + has only one use, that use is immediately following + the defintion and the using iCode has only one + operand or has two operands but one is literal & + the result of that operation is not on stack then + we can leave the result of this operation in acc:b + combination */ + if ((IS_ARITHMETIC_OP (ic) + || IS_CONDITIONAL (ic) + || IS_BITWISE_OP (ic) + || ic->op == LEFT_OP || ic->op == RIGHT_OP + || (ic->op == ADDRESS_OF && isOperandOnStack (IC_LEFT (ic)))) && + IS_ITEMP (IC_RESULT (ic)) && getSize (operandType (IC_RESULT (ic))) <= 2) + + packRegsForAccUse (ic); + } +} + +/*------------------------------------------------------------------------*/ +/* positionRegsReverse - positioning registers from end to begin to avoid */ +/* conflict among result, left and right operands in some extrem cases */ +/*------------------------------------------------------------------------*/ +static void +positionRegsReverse (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + + for (i = count - 1; i >= 0; i--) + for (ic = ebbs[i]->ech; ic; ic = ic->prev) + { + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 1); + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 1); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +ds390_assignRegisters (ebbIndex * ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + setToNull ((void *) &_G.funcrUsed); + ds390_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + if ((currFunc && IFFUNC_ISREENT (currFunc->type)) || options.stackAuto) + { + ds390_nBitRegs = 8; + } + else + { + ds390_nBitRegs = 0; + } + ds390_nRegs = 12 + ds390_nBitRegs; + _G.allBitregs = findAllBitregs (); + + if (options.model != MODEL_FLAT24) + options.stack10bit = 0; + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + ds390_nRegs = 8; + freeAllRegs (); + fillGaps (); + positionRegsReverse (ebbs, count); + ds390_nRegs = 12 + ds390_nBitRegs; + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + redoStackOffsets (); + + /* make sure r0 & r1 are flagged as used if they might be used */ + /* as pointers */ + if (currFunc && ds390_ptrRegReq) + { + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R0_IDX); + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R1_IDX); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + gen390Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + ds390_nRegs = 8; + freeAllRegs (); + + return; +} diff --git a/src/ds390/ralloc.h b/src/ds390/ralloc.h new file mode 100644 index 0000000..e9dae2e --- /dev/null +++ b/src/ds390/ralloc.h @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum +{ + R2_IDX = 0, R3_IDX, R4_IDX, R5_IDX, + R6_IDX, R7_IDX, R0_IDX, R1_IDX, + DPL_IDX, DPH_IDX, DPX_IDX, B_IDX, + B0_IDX, B1_IDX, B2_IDX, B3_IDX, + B4_IDX, B5_IDX, B6_IDX, B7_IDX, + X8_IDX, X9_IDX, X10_IDX, X11_IDX, + X12_IDX, CND_IDX, + A_IDX, DPL1_IDX, DPH1_IDX, + DPX1_IDX, DPS_IDX, AP_IDX, + END_IDX +}; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_CND 0x04 +#define REG_BIT 0x08 +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + short otype; + char *name; /* name */ + char *dname; /* name when direct access needed */ + char *base; /* base address */ + short offset; /* offset from the base */ + unsigned isFree:1; /* is currently unassigned */ + int print; /* needs to be printed */ +} +reg_info; +extern reg_info regs390[]; + +reg_info *ds390_regWithIdx (int); + +bitVect *ds390_rUmaskForOp (operand * op); +bitVect *ds390_allBitregs (void); + +extern int ds390_ptrRegReq; +extern int ds390_nRegs; +extern int ds390_nBitRegs; + +#endif diff --git a/src/hc08/Makefile b/src/hc08/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/hc08/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/hc08/Makefile.in b/src/hc08/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/hc08/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/hc08/gen.c b/src/hc08/gen.c new file mode 100644 index 0000000..4ab37b3 --- /dev/null +++ b/src/hc08/gen.c @@ -0,0 +1,10849 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for the 68HC08 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + Hacked for the 68HC08: + Copyright (C) 2003, Erik Petrich + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* Use the D macro for basic (unobtrusive) debugging messages */ +#define D(x) do if (options.verboseAsm) { x; } while (0) +/* Use the DD macro for detailed debugging messages */ +#define DD(x) +//#define DD(x) x + +#include +#include +#include +#include + +#include "common.h" +#include "hc08.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +extern int allocInfo; +static int pushReg (reg_info * reg, bool freereg); +static void pullReg (reg_info * reg); +static void transferAopAop (asmop * srcaop, int srcofs, asmop * dstaop, int dstofs); +static void adjustStack (int n); + +static char *zero = "#0x00"; +static char *one = "#0x01"; + +unsigned fReturnSizeHC08 = 4; /* shared with ralloc.c */ + + +static struct +{ + short hxPushed; + short accInUse; + short nRegsSaved; + int stackOfs; + int stackPushes; + short regsinuse; + set *sendSet; + int tsxStackPushes; +} +_G; + +static asmop *hc08_aop_pass[8]; +static asmop tsxaop; + +extern int hc08_ptrRegReq; +extern int hc08_nRegs; +extern struct dbuf_s *codeOutBuf; +static bool operandsEqu (operand * op1, operand * op2); +static void loadRegFromConst (reg_info * reg, int c); +static asmop *newAsmop (short type); +static const char *aopAdrStr (asmop * aop, int loffset, bool bit16); +static void updateiTempRegisterUse (operand * op); +#define RESULTONSTACK(x) \ + (IC_RESULT(x) && IC_RESULT(x)->aop && \ + IC_RESULT(x)->aop->type == AOP_STK ) +#define IS_AOP_HX(x) ((x)->regmask == HC08MASK_HX) +#define IS_AOP_XA(x) ((x)->regmask == HC08MASK_XA) +#define IS_AOP_AX(x) ((x)->regmask == HC08MASK_AX) +#define IS_AOP_A(x) ((x)->regmask == HC08MASK_A) +#define IS_AOP_X(x) ((x)->regmask == HC08MASK_X) +#define IS_AOP_H(x) ((x)->regmask == HC08MASK_H) +#define IS_AOP_WITH_A(x) (((x)->regmask & HC08MASK_A) != 0) +#define IS_AOP_WITH_X(x) (((x)->regmask & HC08MASK_X) != 0) +#define IS_AOP_WITH_H(x) (((x)->regmask & HC08MASK_H) != 0) + +#define IS_AOPOFS_A(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_A)) +#define IS_AOPOFS_X(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_X)) +#define IS_AOPOFS_H(x,o) (((x)->type == AOP_REG) && ((x)->aopu.aop_reg[o]->mask == HC08MASK_H)) + + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define AOP_OP(aop) aop->op + +static bool regalloc_dry_run; +static unsigned char regalloc_dry_run_cost; + +static void +emitBranch (char *branchop, symbol * tlbl) +{ + if (!regalloc_dry_run) + emitcode (branchop, "%05d$", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += (!strcmp(branchop, "jmp") || !strcmp(branchop, "brclr") || !strcmp(branchop, "brset") ? 3 : 2); +} + +/*-----------------------------------------------------------------*/ +/* hc08_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +hc08_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emitcode ("", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + + +/*--------------------------------------------------------------------------*/ +/* transferRegReg - Transfer from register(s) sreg to register(s) dreg. If */ +/* freesrc is true, sreg is marked free and available for */ +/* reuse. sreg and dreg must be of equal size */ +/*--------------------------------------------------------------------------*/ +static void +transferRegReg (reg_info *sreg, reg_info *dreg, bool freesrc) +{ + int srcidx; + int dstidx; + char error = 0; + + /* Nothing to do if no destination. */ + if (!dreg) + return; + + /* But it's definately an error if there's no source. */ + if (!sreg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "NULL sreg in transferRegReg"); + return; + } + + DD (emitcode ("", "; transferRegReg(%s,%s)", sreg->name, dreg->name)); + + srcidx = sreg->rIdx; + dstidx = dreg->rIdx; + + if (srcidx == dstidx) + { + hc08_useReg (dreg); + return; + } + + switch (dstidx) + { + case A_IDX: + switch (srcidx) + { + case H_IDX: /* H to A */ + pushReg (hc08_reg_h, FALSE); + pullReg (hc08_reg_a); + break; + case X_IDX: /* X to A */ + emitcode ("txa", ""); + regalloc_dry_run_cost++; + break; + default: + error = 1; + } + break; + case H_IDX: + switch (srcidx) + { + case A_IDX: /* A to H */ + pushReg (hc08_reg_a, FALSE); + pullReg (hc08_reg_h); + break; + case X_IDX: /* X to H */ + pushReg (hc08_reg_x, FALSE); + pullReg (hc08_reg_h); + break; + default: + error = 1; + } + break; + case X_IDX: + switch (srcidx) + { + case A_IDX: /* A to X */ + emitcode ("tax", ""); + regalloc_dry_run_cost++; + break; + case H_IDX: /* H to X */ + pushReg (hc08_reg_h, FALSE); + pullReg (hc08_reg_x); + break; + default: + error = 1; + } + break; + case HX_IDX: + switch (srcidx) + { + case XA_IDX: /* XA to HX */ + pushReg (hc08_reg_x, FALSE); + pullReg (hc08_reg_h); + emitcode ("tax", ""); + regalloc_dry_run_cost++; + break; + default: + error = 1; + } + break; + case XA_IDX: + switch (srcidx) + { + case HX_IDX: /* HX to XA */ + emitcode ("txa", ""); + regalloc_dry_run_cost++; + pushReg (hc08_reg_h, FALSE); + pullReg (hc08_reg_x); + break; + default: + error = 1; + } + break; + default: + error = 1; + } + + wassertl (!error, "bad combo in transferRegReg"); + + if (freesrc) + hc08_freeReg (sreg); + + dreg->aop = sreg->aop; + dreg->aopofs = sreg->aopofs; + dreg->isFree = FALSE; + hc08_dirtyReg (dreg, FALSE); + hc08_useReg (dreg); +} + +/*--------------------------------------------------------------------------*/ +/* updateCFA - update the debugger information to reflect the current */ +/* connonical frame address relative to the stack pointer */ +/*--------------------------------------------------------------------------*/ +static void +updateCFA (void) +{ + /* there is no frame unless there is a function */ + if (!currFunc) + return; + + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, hc08_reg_sp, 1 + _G.stackOfs + _G.stackPushes); +} + +/*--------------------------------------------------------------------------*/ +/* pushReg - Push register reg onto the stack. If freereg is true, reg is */ +/* marked free and available for reuse. */ +/*--------------------------------------------------------------------------*/ +static int +pushReg (reg_info * reg, bool freereg) +{ + int regidx = reg->rIdx; + + switch (regidx) + { + case A_IDX: + emitcode ("psha", ""); + regalloc_dry_run_cost++; + _G.stackPushes++; + updateCFA (); + break; + case X_IDX: + emitcode ("pshx", ""); + regalloc_dry_run_cost++; + _G.stackPushes++; + updateCFA (); + break; + case H_IDX: + emitcode ("pshh", ""); + regalloc_dry_run_cost++; + _G.stackPushes++; + updateCFA (); + break; + case HX_IDX: + emitcode ("pshx", ""); + regalloc_dry_run_cost++; + _G.stackPushes++; + updateCFA (); + emitcode ("pshh", ""); + regalloc_dry_run_cost++; + _G.stackPushes++; + updateCFA (); + break; + case XA_IDX: + emitcode ("psha", ""); + regalloc_dry_run_cost++; + updateCFA (); + _G.stackPushes++; + emitcode ("pshx", ""); + regalloc_dry_run_cost++; + updateCFA (); + _G.stackPushes++; + break; + default: + break; + } + if (freereg) + hc08_freeReg (reg); + return -_G.stackOfs - _G.stackPushes; +} + +/*--------------------------------------------------------------------------*/ +/* pullReg - Pull register reg off the stack. */ +/*--------------------------------------------------------------------------*/ +static void +pullReg (reg_info * reg) +{ + int regidx = reg->rIdx; + + switch (regidx) + { + case A_IDX: + emitcode ("pula", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + break; + case X_IDX: + emitcode ("pulx", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + break; + case H_IDX: + emitcode ("pulh", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + break; + case HX_IDX: + emitcode ("pulh", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + emitcode ("pulx", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + break; + case XA_IDX: + emitcode ("pulx", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + emitcode ("pula", ""); + regalloc_dry_run_cost++; + _G.stackPushes--; + updateCFA (); + break; + default: + break; + } + hc08_useReg (reg); + hc08_dirtyReg (reg, FALSE); +} + +/*--------------------------------------------------------------------------*/ +/* pullNull - Discard n bytes off the top of the stack */ +/*--------------------------------------------------------------------------*/ +static void +pullNull (int n) +{ + wassert (n >= 0); + adjustStack (n); +} + +/*--------------------------------------------------------------------------*/ +/* pushConst - Push a constant byte value onto stack */ +/*--------------------------------------------------------------------------*/ +static void +pushConst (int c) +{ + if (hc08_reg_a->isFree) + { + loadRegFromConst (hc08_reg_a, c); + pushReg (hc08_reg_a, TRUE); + } + else if (hc08_reg_x->isFree) + { + loadRegFromConst (hc08_reg_x, c); + pushReg (hc08_reg_x, TRUE); + } + else if (hc08_reg_h->isFree && !c) + { + loadRegFromConst (hc08_reg_h, c); + pushReg (hc08_reg_h, TRUE); + } + else if (!c) + { + adjustStack (-1); + emitcode ("clr", "1,s"); + regalloc_dry_run_cost += 3; + } + else + { + pushReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, c); + emitcode ("sta", "2,s"); + regalloc_dry_run_cost += 3; + pullReg (hc08_reg_a); + } +} + +/*--------------------------------------------------------------------------*/ +/* pushRegIfUsed - Push register reg if marked in use. Returns true if the */ +/* push was performed, false otherwise. */ +/*--------------------------------------------------------------------------*/ +static bool +pushRegIfUsed (reg_info *reg) +{ + if (!reg->isFree) + { + pushReg (reg, TRUE); + return TRUE; + } + else + return FALSE; +} + +/*--------------------------------------------------------------------------*/ +/* pushRegIfSurv - Push register reg if marked surviving. Returns true if */ +/* the push was performed, false otherwise. */ +/*--------------------------------------------------------------------------*/ +static bool +pushRegIfSurv (reg_info *reg) +{ + if (!reg->isDead) + { + pushReg (reg, TRUE); + return TRUE; + } + else + return FALSE; +} + +/*--------------------------------------------------------------------------*/ +/* pullOrFreeReg - If needpull is true, register reg is pulled from the */ +/* stack. Otherwise register reg is marked as free. */ +/*--------------------------------------------------------------------------*/ +static void +pullOrFreeReg (reg_info * reg, bool needpull) +{ + if (needpull) + pullReg (reg); + else + hc08_freeReg (reg); +} + +/*--------------------------------------------------------------------------*/ +/* adjustStack - Adjust the stack pointer by n bytes. */ +/*--------------------------------------------------------------------------*/ +static void +adjustStack (int n) +{ + while (n) + { + if (n > 127) + { + emitcode ("ais", "#127"); + regalloc_dry_run_cost += 2; + n -= 127; + _G.stackPushes -= 127; + updateCFA (); + } + else if (n < -128) + { + emitcode ("ais", "#-128"); + regalloc_dry_run_cost += 2; + n += 128; + _G.stackPushes += 128; + updateCFA (); + } + else + { + if (n == -1) + { + emitcode ("pshh", ""); /* 1 byte, 2 cycles */ + regalloc_dry_run_cost++; + } + else if (n == 1 && optimize.codeSize && hc08_reg_h->isFree) + { + emitcode ("pulh", ""); /* 1 byte, 3 cycles */ + regalloc_dry_run_cost++; + } + else + { + emitcode ("ais", "#%d", n); /* 2 bytes, 2 cycles */ + regalloc_dry_run_cost += 2; + } + _G.stackPushes -= n; + n = 0; + updateCFA (); + } + } +} + +#if DD(1) -1 == 0 +/*--------------------------------------------------------------------------*/ +/* aopName - Return a string with debugging information about an asmop. */ +/*--------------------------------------------------------------------------*/ +static char * +aopName (asmop * aop) +{ + static char buffer[256]; + char *buf = buffer; + + if (!aop) + return "(asmop*)NULL"; + + switch (aop->type) + { + case AOP_IMMD: + sprintf (buf, "IMMD(%s)", aop->aopu.aop_immd.aop_immd1); + return buf; + case AOP_LIT: + sprintf (buf, "LIT(%s)", aopLiteral (aop->aopu.aop_lit, 0)); + return buf; + case AOP_DIR: + sprintf (buf, "DIR(%s)", aop->aopu.aop_dir); + return buf; + case AOP_EXT: + sprintf (buf, "EXT(%s)", aop->aopu.aop_dir); + return buf; + case AOP_SOF: + sprintf (buf, "SOF(%s)", OP_SYMBOL (aop->op)->name); + return buf; + case AOP_REG: + sprintf (buf, "REG(%s,%s,%s,%s)", + aop->aopu.aop_reg[3] ? aop->aopu.aop_reg[3]->name : "-", + aop->aopu.aop_reg[2] ? aop->aopu.aop_reg[2]->name : "-", + aop->aopu.aop_reg[1] ? aop->aopu.aop_reg[1]->name : "-", + aop->aopu.aop_reg[0] ? aop->aopu.aop_reg[0]->name : "-"); + return buf; + case AOP_STK: + return "STK"; + default: + sprintf (buf, "?%d", aop->type); + return buf; + } + + return "?"; +} +#endif + +/*--------------------------------------------------------------------------*/ +/* loadRegFromAop - Load register reg from logical offset loffset of aop. */ +/* For multi-byte registers, loffset is of the lsb reg. */ +/*--------------------------------------------------------------------------*/ +static void +loadRegFromAop (reg_info * reg, asmop * aop, int loffset) +{ + int regidx = reg->rIdx; + + if (aop->stacked && aop->stk_aop[loffset]) + { + loadRegFromAop (reg, aop->stk_aop[loffset], 0); + return; + } + + DD (emitcode ("", "; loadRegFromAop (%s, %s, %d)", reg->name, aopName (aop), loffset)); + +#if 0 + /* If operand is volatile, we cannot optimize. */ + if (!aop->op || isOperandVolatile (aop->op, FALSE)) + goto forceload; + + + /* If this register already has this offset of the operand + then we need only mark it as in use. */ + if (reg->aop && reg->aop->op && aop->op && operandsEqu (reg->aop->op, aop->op) && (reg->aopofs == loffset)) + { + hc08_useReg (reg); + DD (emitcode ("", "; already had correct value for %s", reg->name)); + return; + } + + /* TODO: check to see if we can transfer from another register */ + + if (hc08_reg_h->aop && hc08_reg_h->aop->op && aop->op + && operandsEqu (hc08_reg_h->aop->op, aop->op) && (hc08_reg_h->aopofs == loffset)) + { + DD (emitcode ("", "; found correct value for %s in h", reg->name)); + transferRegReg (hc08_reg_h, reg, FALSE); + hc08_useReg (reg); + return; + } + + + if (hc08_reg_x->aop && hc08_reg_x->aop->op && aop->op + && operandsEqu (hc08_reg_x->aop->op, aop->op) && (hc08_reg_x->aopofs == loffset)) + { + DD (emitcode ("", "; found correct value for %s in x", reg->name)); + transferRegReg (hc08_reg_x, reg, FALSE); + hc08_useReg (reg); + return; + } + + if (hc08_reg_a->aop && hc08_reg_a->aop->op && aop->op + && operandsEqu (hc08_reg_a->aop->op, aop->op) && (hc08_reg_a->aopofs == loffset)) + { + DD (emitcode ("", "; found correct value for %s in a", reg->name)); + transferRegReg (hc08_reg_a, reg, FALSE); + hc08_useReg (reg); + return; + } + +forceload: +#endif + + switch (regidx) + { + case A_IDX: + if (aop->type == AOP_REG) + { + if (loffset < aop->size) + transferRegReg (aop->aopu.aop_reg[loffset], reg, FALSE); + else + loadRegFromConst (reg, 0); /* TODO: handle sign extension */ + } + else + { + if (aop->type == AOP_LIT) + { + loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); + } + else + { + const char *l = aopAdrStr (aop, loffset, FALSE); + emitcode ("lda", "%s", l); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD || aop->type == AOP_LIT) ? 2 : 3); + hc08_dirtyReg (reg, FALSE); + } + } + break; + case X_IDX: + if (aop->type == AOP_REG) + { + if (loffset < aop->size) + transferRegReg (aop->aopu.aop_reg[loffset], reg, FALSE); + else + loadRegFromConst (reg, 0); /* TODO: handle sign extension */ + } + else + { + if (aop->type == AOP_LIT) + { + loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); + } + else + { + const char *l = aopAdrStr (aop, loffset, FALSE); + emitcode ("ldx", "%s", l); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD || aop->type == AOP_LIT) ? 2 : 3); + hc08_dirtyReg (reg, FALSE); + } + } + break; + case H_IDX: + if (aop->type == AOP_LIT) + { + loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset)); + break; + } + if (aop->type == AOP_SOF && !(_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1)) + { + pullReg (hc08_reg_h); + pushReg (hc08_reg_h, FALSE); + break; + } + if (aop->type == AOP_REG && loffset < aop->size) + transferRegReg (aop->aopu.aop_reg[loffset], hc08_reg_h, TRUE); + else if (!(aop->op && isOperandVolatile (aop->op, FALSE)) && (loffset - 1 >= 0 || aop->type == AOP_LIT) && (aop->type == AOP_LIT || aop->type == AOP_IMMD || IS_S08 && aop->type == AOP_EXT)) /* TODO: Allow negative loffset - 1 */ + { + bool pushedx = FALSE; + if (!hc08_reg_x->isFree) + { + pushReg (hc08_reg_x, TRUE); + pushedx = TRUE; + } + loadRegFromAop (hc08_reg_hx, aop, loffset - 1); + pullOrFreeReg (hc08_reg_x, pushedx); + } + else if (hc08_reg_a->isFree) + { + loadRegFromAop (hc08_reg_a, aop, loffset); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + } + else if (hc08_reg_x->isFree) + { + loadRegFromAop (hc08_reg_x, aop, loffset); + transferRegReg (hc08_reg_x, hc08_reg_h, TRUE); + } + else + { + pushReg (hc08_reg_a, TRUE); + loadRegFromAop (hc08_reg_a, aop, loffset); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + pullReg (hc08_reg_a); + } + break; + case HX_IDX: + if (aop->type == AOP_LIT) + { + loadRegFromConst (reg, byteOfVal (aop->aopu.aop_lit, loffset) | + (byteOfVal (aop->aopu.aop_lit, loffset+1) << 8)); + break; + } + if (aop->type == AOP_SOF) + { + int offset = (_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1); + if (IS_S08 && offset >= 0 && offset <= 0xff) + { + emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (reg, FALSE); + break; + } + else if (offset == 1) + { + pullReg (hc08_reg_h); + pullReg (hc08_reg_x); + pushReg (hc08_reg_x, FALSE); + pushReg (hc08_reg_h, FALSE); + break; + } + } + if (IS_AOP_HX (aop)) + break; + else if (IS_AOP_XA (aop)) + transferRegReg (hc08_reg_xa, hc08_reg_hx, FALSE); + else if (aop->type == AOP_DIR || IS_S08 && aop->type == AOP_EXT) + { + if (aop->size >= (loffset + 2)) + { + emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); + regalloc_dry_run_cost += (aop->type == AOP_DIR ? 2 : 3); + hc08_dirtyReg (reg, FALSE); + } + else + { + loadRegFromConst (hc08_reg_h, 0); + loadRegFromAop (hc08_reg_x, aop, loffset); + } + } + else if ((aop->type == AOP_LIT) || (aop->type == AOP_IMMD)) + { + emitcode ("ldhx", "%s", aopAdrStr (aop, loffset, TRUE)); + regalloc_dry_run_cost += 3; + hc08_dirtyReg (reg, FALSE); + } + else + { + loadRegFromAop (hc08_reg_h, aop, loffset + 1); + loadRegFromAop (hc08_reg_x, aop, loffset); + } + break; + case XA_IDX: + if (IS_AOP_XA (aop)) + break; + else if (IS_AOP_HX (aop)) + transferRegReg (hc08_reg_hx, hc08_reg_xa, FALSE); + else if (IS_AOP_AX (aop)) + { + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + } + else + { + loadRegFromAop (hc08_reg_a, aop, loffset); + loadRegFromAop (hc08_reg_x, aop, loffset + 1); + } + break; + } + + hc08_useReg (reg); +} + +/*--------------------------------------------------------------------------*/ +/* loadRegHXAfromAop - Load registers A, H, and X from aops. */ +/* This takes care of the tricky cases where the */ +/* sources may be registers that overlap with the */ +/* destination registers. An aop may be NULL if the */ +/* corresponding register does not need to be loaded. */ +/*--------------------------------------------------------------------------*/ +static void +loadRegHXAfromAop(asmop * aopH, int ofsH, asmop * aopX, int ofsX, asmop * aopA, int ofsA) +{ + /* There are three cases where pairs of registers need to be swapped */ + if (aopA && aopX && IS_AOPOFS_X (aopA, ofsA) && IS_AOPOFS_A (aopX, ofsX)) + { + /* Swap A and X, load H */ + pushReg (hc08_reg_a, TRUE); + if (aopH) + loadRegFromAop (hc08_reg_h, aopH, ofsH); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + return; + } + if (aopA && aopH && IS_AOPOFS_H (aopA, ofsA) && IS_AOPOFS_A (aopH, ofsH)) + { + /* Swap A and H, load X */ + pushReg (hc08_reg_a, TRUE); + if (aopX) + loadRegFromAop (hc08_reg_x, aopX, ofsX); + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + pullReg (hc08_reg_h); + return; + } + if (aopX && aopH && IS_AOPOFS_H (aopX, ofsX) && IS_AOPOFS_X (aopH, ofsH)) + { + /* Swap X and H, load A */ + pushReg (hc08_reg_x, TRUE); + if (aopA) + loadRegFromAop (hc08_reg_a, aopA, ofsA); + transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); + pullReg (hc08_reg_h); + return; + } + + /* There are two cases where the registers need to rotate */ + if (aopA && aopH && aopX && IS_AOPOFS_A (aopH, ofsH) && IS_AOPOFS_H (aopX, ofsX) && IS_AOPOFS_X (aopA, ofsA)) + { + /* Rotate A->H->X->A */ + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); + pullReg (hc08_reg_h); + return; + } + if (aopA && aopH && aopX && IS_AOPOFS_A (aopX, ofsX) && IS_AOPOFS_X (aopH, ofsH) && IS_AOPOFS_H (aopA, ofsA)) + { + /* Rotate A->X->H->A */ + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_h, FALSE); + pullReg (hc08_reg_x); + return; + } + + /* At this point there can be at most 1 overlapping register source. */ + if (aopX && (aopX->type == AOP_REG) && !IS_AOP_WITH_X (aopX)) + { + loadRegFromAop (hc08_reg_x, aopX, ofsX); + if (aopA) + hc08_freeReg (hc08_reg_a); /* in case it's needed to load H */ + if (aopH) + loadRegFromAop (hc08_reg_h, aopH, ofsH); + if (aopA) + loadRegFromAop (hc08_reg_a, aopA, ofsA); + return; + } + if (aopH && (aopH->type == AOP_REG) && !IS_AOP_WITH_H (aopH)) + { + if (aopA) + hc08_freeReg (hc08_reg_a); /* in case it's needed to load H */ + if (aopX) + hc08_freeReg (hc08_reg_x); /* in case it's needed to load H */ + loadRegFromAop (hc08_reg_h, aopH, ofsH); + if (aopX) + loadRegFromAop (hc08_reg_x, aopX, ofsX); + if (aopA) + loadRegFromAop (hc08_reg_a, aopA, ofsA); + return; + } + + /* Either A needs to be loaded first or the order no longer matters */ + if (aopA) + loadRegFromAop (hc08_reg_a, aopA, ofsA); + if (aopX) + hc08_freeReg (hc08_reg_x); /* in case it's needed to load H */ + if (aopH && aopX && (aopH == aopX) && (ofsH == (ofsX+1))) + loadRegFromAop (hc08_reg_hx, aopX, ofsX); + else + { + if (aopH) + loadRegFromAop (hc08_reg_h, aopH, ofsH); + if (aopX) + loadRegFromAop (hc08_reg_x, aopX, ofsX); + } +} + +/*--------------------------------------------------------------------------*/ +/* forceStackedAop - Reserve space on the stack for asmop aop; when */ +/* freeAsmop is called with aop, the stacked data will */ +/* be copied to the original aop location. */ +/*--------------------------------------------------------------------------*/ +static asmop * +forceStackedAop (asmop * aop, bool copyOrig) +{ + reg_info *reg; + int loffset; + asmop *newaop = newAsmop (aop->type); + memcpy (newaop, aop, sizeof (*newaop)); + + DD (emitcode ("", "; forcedStackAop %s", aopName (aop))); + + if (copyOrig && hc08_reg_a->isFree) + reg = hc08_reg_a; + else if (copyOrig && hc08_reg_x->isFree) + reg = hc08_reg_x; + else + reg = NULL; + + for (loffset = 0; loffset < newaop->size; loffset++) + { + asmop *aopsof = newAsmop (AOP_SOF); + aopsof->size = 1; + if (copyOrig && reg) + { + loadRegFromAop (reg, aop, loffset); + aopsof->aopu.aop_stk = pushReg (reg, FALSE); + } + else + { + aopsof->aopu.aop_stk = pushReg (hc08_reg_a, FALSE); + } + aopsof->op = aop->op; + newaop->stk_aop[loffset] = aopsof; + } + newaop->stacked = 1; + + if (!reg && copyOrig) + { + for (loffset = 0; loffset < newaop->size; loffset++) + { + transferAopAop (aop, loffset, newaop, loffset); + } + } + + return newaop; +} + + +/*--------------------------------------------------------------------------*/ +/* storeRegToAop - Store register reg to logical offset loffset of aop. */ +/* For multi-byte registers, loffset is of the lsb reg. */ +/*--------------------------------------------------------------------------*/ +static void +storeRegToAop (reg_info *reg, asmop * aop, int loffset) +{ + int regidx = reg->rIdx; + + DD (emitcode ("", "; storeRegToAop (%s, %s, %d), stacked=%d", + reg->name, aopName (aop), loffset, aop->stacked)); + + if ((reg->rIdx == HX_IDX) && aop->stacked && (aop->stk_aop[loffset] || aop->stk_aop[loffset + 1])) + { + storeRegToAop (hc08_reg_h, aop, loffset + 1); + storeRegToAop (hc08_reg_x, aop, loffset); + return; + } + + if ((reg->rIdx == XA_IDX) && aop->stacked && (aop->stk_aop[loffset] || aop->stk_aop[loffset + 1])) + { + storeRegToAop (hc08_reg_x, aop, loffset + 1); + storeRegToAop (hc08_reg_a, aop, loffset); + return; + } + + if (aop->stacked && aop->stk_aop[loffset]) + { + storeRegToAop (reg, aop->stk_aop[loffset], 0); + return; + } + + if (aop->type == AOP_DUMMY) + return; + + if (aop->type == AOP_CRY) /* This can only happen if IFX was optimized */ + return; /* away, so just toss the result */ + + switch (regidx) + { + case A_IDX: + if ((aop->type == AOP_REG) && (loffset < aop->size)) + transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); + else + { + emitcode ("sta", "%s", aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 :3); + } + break; + case X_IDX: + if ((aop->type == AOP_REG) && (loffset < aop->size)) + transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); + else + { + emitcode ("stx", "%s", aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 :3); + } + break; + case H_IDX: + if ((aop->type == AOP_REG) && (loffset < aop->size)) + transferRegReg (reg, aop->aopu.aop_reg[loffset], FALSE); + else if (hc08_reg_a->isFree) + { + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, aop, loffset); + hc08_freeReg (hc08_reg_a); + } + else if (hc08_reg_x->isFree) + { + transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); + storeRegToAop (hc08_reg_x, aop, loffset); + hc08_freeReg (hc08_reg_x); + } + else + { + pushReg (hc08_reg_a, TRUE); + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, aop, loffset); + pullReg (hc08_reg_a); + } + break; + case HX_IDX: + if (aop->type == AOP_SOF) + { + int offset = (_G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + aop->size - loffset - 1); + if (IS_S08 && offset >= 0 && offset <= 0xff) + { + emitcode ("sthx", "%s", aopAdrStr (aop, loffset, TRUE)); + regalloc_dry_run_cost += 2; + break; + } + } + if (aop->type == AOP_DIR || IS_S08 && aop->type == AOP_EXT) + { + emitcode ("sthx", "%s", aopAdrStr (aop, loffset, TRUE)); + regalloc_dry_run_cost += (aop->type == AOP_DIR ? 2 : 3);; + } + else if (IS_AOP_XA (aop)) + transferRegReg (reg, hc08_reg_xa, FALSE); + else if (IS_AOP_HX (aop)) + break; + else if (hc08_reg_a->isFree) + { + bool needpula; + needpula = pushRegIfUsed (hc08_reg_a); + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, aop, loffset + 1); + storeRegToAop (hc08_reg_x, aop, loffset); + pullOrFreeReg (hc08_reg_a, needpula); + } + else + { + bool needpulx; + storeRegToAop (hc08_reg_x, aop, loffset); + needpulx = pushRegIfUsed (hc08_reg_x); + transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); + storeRegToAop (hc08_reg_x, aop, loffset + 1); + pullOrFreeReg (hc08_reg_x, needpulx); + } + break; + case XA_IDX: + if (IS_AOP_HX (aop)) + transferRegReg (reg, hc08_reg_hx, FALSE); + else if (IS_AOP_XA (aop)) + break; + else if (IS_AOP_AX (aop)) + { + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + } + else + { + storeRegToAop (hc08_reg_a, aop, loffset); + storeRegToAop (hc08_reg_x, aop, loffset + 1); + } + break; + default: + wassert (0); + } + + /* Disable the register tracking for now */ +#if 0 + //if (!reg->aop || (reg->aop && (reg->aop != aop))) + { + //if (reg->aop!=aop) + for (otheridx = 0; otheridx < hc08_nRegs; otheridx++) + { + otherreg = hc08_regWithIdx (otheridx); + if (otherreg && otherreg->aop + && otherreg->aop->op && aop->op && operandsEqu (otherreg->aop->op, aop->op) && (otherreg->aopofs == loffset)) + { + DD (emitcode ("", "; marking %s stale", otherreg->name)); + otherreg->aop = NULL; + } + } + if ((!hc08_reg_x->aop || !hc08_reg_h->aop) && hc08_reg_hx->aop) + { + hc08_reg_hx->aop = NULL; + DD (emitcode ("", "; marking hx stale")); + } + if ((!hc08_reg_x->aop || !hc08_reg_a->aop) && hc08_reg_xa->aop) + { + hc08_reg_xa->aop = NULL; + DD (emitcode ("", "; marking xa stale")); + } + + reg->aop = aop; + reg->aopofs = loffset; + } +#endif +} + +/*--------------------------------------------------------------------------*/ +/* loadRegFromConst - Load register reg from constant c. */ +/*--------------------------------------------------------------------------*/ +static void +loadRegFromConst (reg_info * reg, int c) +{ + switch (reg->rIdx) + { + case A_IDX: + c &= 0xff; + if (reg->isLitConst) + { + if (reg->litConst == c) + break; + if (((reg->litConst + 1) & 0xff) == c) + { + emitcode ("inca", ""); + regalloc_dry_run_cost++; + break; + } + if (((reg->litConst - 1) & 0xff) == c) + { + emitcode ("deca", ""); + regalloc_dry_run_cost++; + break; + } + } + + if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) + transferRegReg (hc08_reg_x, reg, FALSE); + else if (!c) + { + emitcode ("clra", ""); + regalloc_dry_run_cost++; + } + else + { + emitcode ("lda", "!immedbyte", c); + regalloc_dry_run_cost += 2; + } + break; + case X_IDX: + c &= 0xff; + if (reg->isLitConst) + { + if (reg->litConst == c) + break; + if (((reg->litConst + 1) & 0xff) == c) + { + emitcode ("incx", ""); + regalloc_dry_run_cost++; + break; + } + if (((reg->litConst - 1) & 0xff) == c) + { + emitcode ("decx", ""); + regalloc_dry_run_cost++; + break; + } + } + + if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) + transferRegReg (hc08_reg_a, reg, FALSE); + else if (!c) + { + emitcode ("clrx", ""); + regalloc_dry_run_cost++; + } + else + { + emitcode ("ldx", "!immedbyte", c); + regalloc_dry_run_cost += 2; + } + break; + case H_IDX: + c &= 0xff; + if (reg->isLitConst && reg->litConst == c) + break; + + if (!c) + { + emitcode ("clrh", ""); + regalloc_dry_run_cost++; + } + else if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) + transferRegReg (hc08_reg_a, reg, FALSE); + else if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) + transferRegReg (hc08_reg_x, reg, FALSE); + else if (hc08_reg_a->isFree) + { + loadRegFromConst (hc08_reg_a, c); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + } + else if (hc08_reg_x->isFree) + { + loadRegFromConst (hc08_reg_hx, c << 8); + } + else + { + pushReg (hc08_reg_x, TRUE); + loadRegFromConst (hc08_reg_hx, c << 8); + pullReg (hc08_reg_x); + } + break; + case HX_IDX: + c &= 0xffff; + if (reg->isLitConst) + { + int delta = (c - reg->litConst) & 0xffff; + if (delta & 0x8000) + delta = -0x8000 + (delta & 0x7fff); + if (reg->litConst == c) + break; + if ((reg->litConst & 0xff) == c) + { + loadRegFromConst (hc08_reg_h, 0); + break; + } + if ((reg->litConst & 0xff00) == (c & 0xff00)) + { + loadRegFromConst (hc08_reg_x, c & 0xff); + break; + } + if ((delta <= 127) && (delta >= -128)) + { + emitcode ("aix","#%d", delta); + regalloc_dry_run_cost += 2; + break; + } + } + if (!c) + { + loadRegFromConst (hc08_reg_h, 0); + loadRegFromConst (hc08_reg_x, 0); + break; + } + emitcode ("ldhx", "!immedword", c); + regalloc_dry_run_cost += 3; + break; + case XA_IDX: + c &= 0xffff; + if (reg->isLitConst && reg->litConst == c) + break; + loadRegFromConst (hc08_reg_a, c); + loadRegFromConst (hc08_reg_x, c >> 8); + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in loadRegFromConst"); + return; + } + + hc08_dirtyReg (reg, FALSE); + reg->isLitConst = 1; + reg->litConst = c; + if (reg->rIdx == HX_IDX) + { + hc08_reg_x->isLitConst = 1; + hc08_reg_x->litConst = c & 0xff; + hc08_reg_h->isLitConst = 1; + hc08_reg_h->litConst = (c >> 8) & 0xff; + } + hc08_useReg (reg); +} + +/*--------------------------------------------------------------------------*/ +/* loadRegFromImm - Load register reg from immediate value c. */ +/*--------------------------------------------------------------------------*/ +static void +loadRegFromImm (reg_info * reg, char * c) +{ + if (*c == '#') + c++; + switch (reg->rIdx) + { + case A_IDX: + emitcode ("lda", "#%s", c); + regalloc_dry_run_cost += 2; + break; + case X_IDX: + emitcode ("ldx", "#%s", c); + regalloc_dry_run_cost += 2; + break; + case H_IDX: + if (hc08_reg_a->isFree) + { + loadRegFromImm (hc08_reg_a, c); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + } + else if (hc08_reg_x->isFree) + { + loadRegFromImm (hc08_reg_x, c); + transferRegReg (hc08_reg_x, hc08_reg_h, TRUE); + } + else + { + pushReg (hc08_reg_a, TRUE); + loadRegFromImm (hc08_reg_a, c); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + pullReg (hc08_reg_a); + } + break; + case HX_IDX: + emitcode ("ldhx", "#%s", c); + regalloc_dry_run_cost += 3; + break; + case XA_IDX: + emitcode ("lda", "#%s", c); + emitcode ("ldx", "#%s >> 8", c); + regalloc_dry_run_cost += 4; + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in loadRegFromConst"); + return; + } + hc08_dirtyReg (reg, FALSE); + hc08_useReg (reg); +} + +/*--------------------------------------------------------------------------*/ +/* storeConstToAop- Store constant c to logical offset loffset of asmop aop.*/ +/*--------------------------------------------------------------------------*/ +static void +storeConstToAop (int c, asmop * aop, int loffset) +{ + if (aop->stacked && aop->stk_aop[loffset]) + { + storeConstToAop (c, aop->stk_aop[loffset], 0); + return; + } + + /* If the value needed is already in A or X, just store it */ + if (hc08_reg_a->isLitConst && hc08_reg_a->litConst == c) + { + storeRegToAop (hc08_reg_a, aop, loffset); + return; + } + if (hc08_reg_x->isLitConst && hc08_reg_x->litConst == c) + { + storeRegToAop (hc08_reg_x, aop, loffset); + return; + } + + switch (aop->type) + { + case AOP_DIR: + /* clr operates with read-modify-write cycles, so don't use if the */ + /* destination is volatile to avoid the read side-effect. */ + if (!c && !(aop->op && isOperandVolatile (aop->op, FALSE)) && optimize.codeSize) + { + /* clr dst : 2 bytes, 5 cycles */ + emitcode ("clr", "%s", aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += 2; + } + else + { + /* mov #0,dst : 3 bytes, 4 cycles */ + emitcode ("mov", "!immedbyte,%s", c, aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += 3; + } + break; + case AOP_REG: + if (loffset > (aop->size - 1)) + break; + loadRegFromConst (aop->aopu.aop_reg[loffset], c); + break; + case AOP_DUMMY: + break; + default: + if (hc08_reg_a->isFree) + { + loadRegFromConst (hc08_reg_a, c); + storeRegToAop (hc08_reg_a, aop, loffset); + hc08_freeReg (hc08_reg_a); + } + else if (hc08_reg_x->isFree) + { + loadRegFromConst (hc08_reg_x, c); + storeRegToAop (hc08_reg_x, aop, loffset); + hc08_freeReg (hc08_reg_x); + } + else + { + pushReg (hc08_reg_a, TRUE); + loadRegFromConst (hc08_reg_a, c); + storeRegToAop (hc08_reg_a, aop, loffset); + pullReg (hc08_reg_a); + } + } +} + +/*--------------------------------------------------------------------------*/ +/* storeImmToAop- Store immediate value c to logical offset loffset of asmop aop.*/ +/*--------------------------------------------------------------------------*/ +static void +storeImmToAop (char *c, asmop * aop, int loffset) +{ + if (aop->stacked && aop->stk_aop[loffset]) + { + storeImmToAop (c, aop->stk_aop[loffset], 0); + return; + } + + switch (aop->type) + { + case AOP_DIR: + /* clr operates with read-modify-write cycles, so don't use if the */ + /* destination is volatile to avoid the read side-effect. */ + if (!strcmp (c, zero) && !(aop->op && isOperandVolatile (aop->op, FALSE)) && optimize.codeSize) + { + /* clr dst : 2 bytes, 5 cycles */ + emitcode ("clr", "%s", aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += 2; + } + else + { + /* mov #0,dst : 3 bytes, 4 cycles */ + emitcode ("mov", "%s,%s", c, aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += 3; + } + break; + case AOP_REG: + if (loffset > (aop->size - 1)) + break; + loadRegFromImm (aop->aopu.aop_reg[loffset], c); + break; + case AOP_DUMMY: + break; + default: + if (hc08_reg_a->isFree) + { + loadRegFromImm (hc08_reg_a, c); + storeRegToAop (hc08_reg_a, aop, loffset); + hc08_freeReg (hc08_reg_a); + } + else if (hc08_reg_x->isFree) + { + loadRegFromImm (hc08_reg_x, c); + storeRegToAop (hc08_reg_x, aop, loffset); + hc08_freeReg (hc08_reg_x); + } + else + { + pushReg (hc08_reg_a, TRUE); + loadRegFromImm (hc08_reg_a, c); + storeRegToAop (hc08_reg_a, aop, loffset); + pullReg (hc08_reg_a); + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* storeRegSignToUpperAop - If isSigned is true, the sign bit of register */ +/* reg is extended to fill logical offsets loffset */ +/* and above of asmop aop. Otherwise, logical */ +/* offsets loffset and above of asmop aop are */ +/* zeroed. reg must be an 8-bit register. */ +/*--------------------------------------------------------------------------*/ +static void +storeRegSignToUpperAop (reg_info * reg, asmop * aop, int loffset, bool isSigned) +{ +// int regidx = reg->rIdx; + int size = aop->size; + + if (size <= loffset) + return; + + if (!isSigned) + { + /* Unsigned case */ + while (loffset < size) + storeConstToAop (0, aop, loffset++); + } + else + { + /* Signed case */ + transferRegReg (reg, hc08_reg_a, FALSE); + emitcode ("rola", ""); + emitcode ("clra", ""); + emitcode ("sbc", "#0"); + regalloc_dry_run_cost += 4; + hc08_useReg (hc08_reg_a); + while (loffset < size) + storeRegToAop (hc08_reg_a, aop, loffset++); + hc08_freeReg (hc08_reg_a); + } +} + +/*--------------------------------------------------------------------------*/ +/* storeRegToFullAop - Store register reg to asmop aop with appropriate */ +/* padding and/or truncation as needed. If isSigned is */ +/* true, sign extension will take place in the padding. */ +/*--------------------------------------------------------------------------*/ +static void +storeRegToFullAop (reg_info *reg, asmop *aop, bool isSigned) +{ + int regidx = reg->rIdx; + int size = aop->size; + + switch (regidx) + { + case A_IDX: + case X_IDX: + case H_IDX: + storeRegToAop (reg, aop, 0); + if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX) + pushReg (hc08_reg_a, TRUE); + storeRegSignToUpperAop (reg, aop, 1, isSigned); + if (size > 1 && isSigned && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == A_IDX) + pullReg (hc08_reg_a); + break; + case HX_IDX: + if (size == 1) + { + storeRegToAop (hc08_reg_x, aop, 0); + } + else + { + storeRegToAop (reg, aop, 0); + storeRegSignToUpperAop (hc08_reg_h, aop, 2, isSigned); + } + break; + case XA_IDX: + if (size == 1) + { + storeRegToAop (hc08_reg_a, aop, 0); + } + else + { + storeRegToAop (reg, aop, 0); + storeRegSignToUpperAop (hc08_reg_x, aop, 2, isSigned); + } + break; + default: + wassert (0); + } +} + +/*--------------------------------------------------------------------------*/ +/* transferAopAop - Transfer the value at logical offset srcofs of asmop */ +/* srcaop to logical offset dstofs of asmop dstaop. */ +/*--------------------------------------------------------------------------*/ +static void +transferAopAop (asmop *srcaop, int srcofs, asmop *dstaop, int dstofs) +{ + bool needpula = FALSE; + reg_info *reg = NULL; + bool keepreg = FALSE; + bool afree; + + wassert (srcaop && dstaop); + + /* ignore transfers at the same byte, unless its volatile */ + if (srcaop->op && !isOperandVolatile (srcaop->op, FALSE) + && dstaop->op && !isOperandVolatile (dstaop->op, FALSE) + && operandsEqu (srcaop->op, dstaop->op) && srcofs == dstofs && dstaop->type == srcaop->type) + return; + + if (srcaop->stacked && srcaop->stk_aop[srcofs]) + { + transferAopAop (srcaop->stk_aop[srcofs], 0, dstaop, dstofs); + return; + } + + if (dstaop->stacked && dstaop->stk_aop[srcofs]) + { + transferAopAop (srcaop, srcofs, dstaop->stk_aop[dstofs], 0); + return; + } + +// DD(emitcode ("", "; transferAopAop (%s, %d, %s, %d)", +// aopName (srcaop), srcofs, aopName (dstaop), dstofs)); +// DD(emitcode ("", "; srcaop->type = %d", srcaop->type)); +// DD(emitcode ("", "; dstaop->type = %d", dstaop->type)); + + if (dstofs >= dstaop->size) + return; + + if (srcaop->type == AOP_LIT) + { + storeConstToAop (byteOfVal (srcaop->aopu.aop_lit, srcofs), dstaop, dstofs); + return; + } + + if ((dstaop->type == AOP_DIR) && (srcaop->type == AOP_DIR)) + { + const char *src = aopAdrStr (srcaop, srcofs, FALSE); + /* mov src,dst : 3 bytes, 5 cycles */ + emitcode ("mov", "%s,%s", src, aopAdrStr (dstaop, dstofs, FALSE)); + regalloc_dry_run_cost += 3; + return; + } + + if (dstaop->type == AOP_REG) + { + reg = dstaop->aopu.aop_reg[dstofs]; + keepreg = TRUE; + } + else if ((srcaop->type == AOP_REG) && (srcaop->aopu.aop_reg[srcofs])) + { + reg = srcaop->aopu.aop_reg[srcofs]; + keepreg = TRUE; + } + + afree = hc08_reg_a->isFree; + + if (!reg) + { + if (hc08_reg_a->isFree) + reg = hc08_reg_a; + else if (hc08_reg_x->isFree) + reg = hc08_reg_x; + else + { + pushReg (hc08_reg_a, TRUE); + needpula = TRUE; + reg = hc08_reg_a; + } + } + + loadRegFromAop (reg, srcaop, srcofs); + storeRegToAop (reg, dstaop, dstofs); + + if (!keepreg) + pullOrFreeReg (hc08_reg_a, needpula); + + hc08_reg_a->isFree = afree; +} + + +/*--------------------------------------------------------------------------*/ +/* accopWithMisc - Emit accumulator modifying instruction accop with the */ +/* parameter param. */ +/*--------------------------------------------------------------------------*/ +static void +accopWithMisc (char *accop, char *param) +{ + emitcode (accop, "%s", param); + regalloc_dry_run_cost += ((!param[0] || !strcmp(param, ",x")) ? 1 : ((param[0]=='#' || param[0]=='*') ? 2 : 3)); + if (strcmp (accop, "bit") && strcmp (accop, "cmp") && strcmp (accop, "cpx")) + hc08_dirtyReg (hc08_reg_a, FALSE); +} + +/*--------------------------------------------------------------------------*/ +/* accopWithAop - Emit accumulator modifying instruction accop with the */ +/* byte at logical offset loffset of asmop aop. */ +/* Supports: adc, add, and, bit, cmp, eor, ora, sbc, sub */ +/*--------------------------------------------------------------------------*/ +static void +accopWithAop (char *accop, asmop *aop, int loffset) +{ + if (aop->stacked && aop->stk_aop[loffset]) + { + accopWithAop (accop, aop->stk_aop[loffset], 0); + return; + } + + if (aop->type == AOP_DUMMY) + return; + + if (loffset >= aop->size) + { + emitcode (accop, "#0"); + regalloc_dry_run_cost += 2; + } + else if (aop->type == AOP_REG) + { + pushReg (aop->aopu.aop_reg[loffset], FALSE); + emitcode (accop, "1,s"); + regalloc_dry_run_cost += 3; + pullNull (1); + } + else + { + emitcode (accop, "%s", aopAdrStr (aop, loffset, FALSE)); + if (aop->type == AOP_DIR || aop->type == AOP_LIT) + regalloc_dry_run_cost +=2; + else + regalloc_dry_run_cost += 3; + } + + if (strcmp (accop, "bit") && strcmp (accop, "cmp") && strcmp (accop, "cpx")) + hc08_dirtyReg (hc08_reg_a, FALSE); +} + + +/*--------------------------------------------------------------------------*/ +/* rmwWithReg - Emit read/modify/write instruction rmwop with register reg. */ +/* byte at logical offset loffset of asmop aop. Register reg */ +/* must be 8-bit. */ +/* Supports: com, dec, inc, lsl, lsr, neg, rol, ror */ +/*--------------------------------------------------------------------------*/ +static void +rmwWithReg (char *rmwop, reg_info * reg) +{ + char rmwbuf[10]; + char *rmwaop = rmwbuf; + + if (reg->rIdx == A_IDX) + { + sprintf (rmwaop, "%sa", rmwop); + emitcode (rmwaop, ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_a, FALSE); + } + else if (reg->rIdx == X_IDX) + { + sprintf (rmwaop, "%sx", rmwop); + emitcode (rmwaop, ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_x, FALSE); + } + else + { + pushReg (reg, FALSE); + emitcode (rmwop, "1,s"); + regalloc_dry_run_cost += 3; + pullReg (reg); + hc08_dirtyReg (reg, FALSE); + } +} + +/*--------------------------------------------------------------------------*/ +/* rmwWithAop - Emit read/modify/write instruction rmwop with the byte at */ +/* logical offset loffset of asmop aop. */ +/* Supports: com, dec, inc, lsl, lsr, neg, rol, ror, tst */ +/*--------------------------------------------------------------------------*/ +static void +rmwWithAop (char *rmwop, asmop * aop, int loffset) +{ + bool needpull = FALSE; + reg_info * reg; + + if (aop->stacked && aop->stk_aop[loffset]) + { + rmwWithAop (rmwop, aop->stk_aop[loffset], 0); + return; + } + + /* If we need a register: */ + /* use A if it's free, */ + /* otherwise use X if it's free */ + /* otherwise use A (and preserve original value via the stack) */ + if (!hc08_reg_a->isFree && hc08_reg_x->isFree) + reg = hc08_reg_x; + else + reg = hc08_reg_a; + + switch (aop->type) + { + case AOP_REG: + rmwWithReg (rmwop, aop->aopu.aop_reg[loffset]); + break; + case AOP_EXT: + needpull = pushRegIfUsed (reg); + loadRegFromAop (reg, aop, loffset); + rmwWithReg (rmwop, reg); + if (strcmp ("tst", rmwop)) + storeRegToAop (reg, aop, loffset); + pullOrFreeReg (reg, needpull); + break; + case AOP_DUMMY: + break; + case AOP_SOF: + { + int offset = aop->size - 1 - loffset; + offset += _G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + 1; + if ((offset > 0xff) || (offset < 0)) + { + /* Unfortunately, the rmw class of instructions only support a */ + /* single byte stack pointer offset and we need two. */ + needpull = pushRegIfUsed (reg); + loadRegFromAop (reg, aop, loffset); + rmwWithReg (rmwop, reg); + if (strcmp ("tst", rmwop)) + storeRegToAop (reg, aop, loffset); + pullOrFreeReg (reg, needpull); + break; + } + /* If the offset is small enough, fall through to default case */ + } + default: + emitcode (rmwop, "%s", aopAdrStr (aop, loffset, FALSE)); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 : 3); + } + +} + +/*--------------------------------------------------------------------------*/ +/* loadRegIndexed - Load a register using indexed addressing mode. */ +/* NOTE: offset is physical (not logical) */ +/*--------------------------------------------------------------------------*/ +static void +loadRegIndexed (reg_info * reg, int offset, char * rematOfs) +{ + bool needpula = FALSE; + + /* The rematerialized offset may have a "#" prefix; skip over it */ + if (rematOfs && rematOfs[0] == '#') + rematOfs++; + if (rematOfs && !rematOfs[0]) + rematOfs = NULL; + + /* force offset to signed 16-bit range */ + offset &= 0xffff; + if (offset & 0x8000) + offset = 0x10000 - offset; + + switch (reg->rIdx) + { + case A_IDX: + if (rematOfs) + { + if (!offset) + emitcode ("lda", "(%s),x", rematOfs); + else + emitcode ("lda", "(%s+%d),x", rematOfs, offset); + regalloc_dry_run_cost += 3; + } + else if (offset) + { + emitcode ("lda", "%d,x", offset); + if (offset > 1 && offset <= 0xff) + regalloc_dry_run_cost += 2; + else + regalloc_dry_run_cost += 3; + } + else + { + emitcode ("lda", ",x"); + regalloc_dry_run_cost++; + } + hc08_dirtyReg (reg, FALSE); + break; + case X_IDX: + if (rematOfs) + { + if (!offset) + emitcode ("ldx", "(%s),x", rematOfs); + else + emitcode ("ldx", "(%s+%d),x", rematOfs, offset); + regalloc_dry_run_cost += 3; + } + else if (offset) + { + emitcode ("ldx", "%d,x", offset); + if (offset > 1 && offset <= 0xff) + regalloc_dry_run_cost += 2; + else + regalloc_dry_run_cost += 3; + } + else + { + emitcode ("ldx", ",x"); + regalloc_dry_run_cost++; + } + hc08_dirtyReg (reg, FALSE); + break; + case H_IDX: + needpula = pushRegIfUsed (hc08_reg_a); + loadRegIndexed (hc08_reg_a, offset, rematOfs); + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + pullOrFreeReg (hc08_reg_a, needpula); + break; + case HX_IDX: + if (!IS_S08) + { + needpula = pushRegIfUsed (hc08_reg_a); + loadRegIndexed (hc08_reg_a, offset, rematOfs); + pushReg (hc08_reg_a, TRUE); + loadRegIndexed (hc08_reg_x, offset+1, rematOfs); + pullReg (hc08_reg_h); + pullOrFreeReg (hc08_reg_a, needpula); + break; + } + else if (rematOfs) + { + if (!offset) + emitcode ("ldhx", "(%s),x", rematOfs); + else + emitcode ("ldhx", "(%s+%d),x", rematOfs, offset); + regalloc_dry_run_cost += 4; + } + else if (offset) + { + emitcode ("ldhx", "%d,x", offset); + if (offset > 1 && offset <= 0xff) + regalloc_dry_run_cost += 3; + else + regalloc_dry_run_cost += 4; + } + else + { + emitcode ("ldhx", ",x"); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (reg, FALSE); + break; + case XA_IDX: + loadRegIndexed (hc08_reg_a, offset+1, rematOfs); + loadRegIndexed (hc08_reg_x, offset, rematOfs); + break; + default: + wassert (0); + } +} + +/*--------------------------------------------------------------------------*/ +/* storeRegIndexed - Store a register using indexed addressing mode. */ +/* NOTE: offset is physical (not logical) */ +/*--------------------------------------------------------------------------*/ +static void +storeRegIndexed (reg_info * reg, int offset, char * rematOfs) +{ + bool needpula = FALSE; + + /* The rematerialized offset may have a "#" prefix; skip over it */ + if (rematOfs && rematOfs[0] == '#') + rematOfs++; + if (rematOfs && !rematOfs[0]) + rematOfs = NULL; + + /* force offset to signed 16-bit range */ + offset &= 0xffff; + if (offset & 0x8000) + offset = offset - 0x10000; + + switch (reg->rIdx) + { + case A_IDX: + if (rematOfs) + { + if (!offset) + emitcode ("sta", "(%s),x", rematOfs); + else + emitcode ("sta", "(%s+%d),x", rematOfs, offset); + regalloc_dry_run_cost += 3; + } + else if (offset) + { + emitcode ("sta", "%d,x", offset); + if (offset > 1 && offset <= 0xff) + regalloc_dry_run_cost += 2; + else + regalloc_dry_run_cost += 3; + } + else + { + emitcode ("sta", ",x"); + regalloc_dry_run_cost++; + } + break; + case X_IDX: + if (rematOfs) + { + if (!offset) + emitcode ("stx", "(%s),x", rematOfs); + else + emitcode ("stx", "(%s+%d),x", rematOfs, offset); + regalloc_dry_run_cost += 3; + } + else if (offset) + { + emitcode ("stx", "%d,x", offset); + if (offset > 1 && offset <= 0xff) + regalloc_dry_run_cost += 2; + else + regalloc_dry_run_cost += 3; + } + else + { + emitcode ("stx", ",x"); + regalloc_dry_run_cost++; + } + break; + case H_IDX: + needpula = pushRegIfUsed (hc08_reg_a); + transferRegReg (hc08_reg_h, hc08_reg_a, TRUE); + storeRegIndexed (hc08_reg_a, offset, rematOfs); + pullOrFreeReg (hc08_reg_a, needpula); + break; + case HX_IDX: + storeRegIndexed (hc08_reg_h, offset, rematOfs); + storeRegIndexed (hc08_reg_x, offset+1, rematOfs); + break; + case XA_IDX: + /* This case probably won't happen, but it's easy to implement */ + storeRegIndexed (hc08_reg_x, offset, rematOfs); + storeRegIndexed (hc08_reg_a, offset+1, rematOfs); + break; + default: + wassert (0); + } +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + aop->op = NULL; + return aop; +} + + +/*-----------------------------------------------------------------*/ +/* operandConflictsWithHX - true if operand in h and/or x register */ +/*-----------------------------------------------------------------*/ +static bool +operandConflictsWithHX (operand *op) +{ + symbol *sym; + int i; + + if (IS_ITEMP (op)) + { + sym = OP_SYMBOL (op); + if (!sym->isspilt) + { + for(i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == hc08_reg_h || sym->regs[i] == hc08_reg_x) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandOnStack - returns True if operand is on the stack */ +/*-----------------------------------------------------------------*/ +static bool +operandOnStack(operand *op) +{ + symbol *sym; + + if (!op || !IS_SYMOP (op)) + return FALSE; + sym = OP_SYMBOL (op); + if (!sym->isspilt && sym->onStack) + return TRUE; + if (sym->isspilt) + { + sym = sym->usl.spillLoc; + if (sym && sym->onStack) + return TRUE; + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* tsxUseful - returns True if tsx could help at least two */ +/* anticipated stack references */ +/*-----------------------------------------------------------------*/ +static bool +tsxUseful(iCode *ic) +{ + int uses = 0; + + if (ic->op == CALL) + { + if (IC_RESULT (ic) && operandSize (IC_RESULT (ic)) < 2 && operandOnStack (IC_RESULT (ic))) + { + uses++; + ic = ic->next; + } + } + + while (ic && uses < 2) + { + if (ic->op == IFX) + { + if (operandOnStack (IC_COND (ic))) + uses += operandSize(IC_COND (ic)); + break; + } + else if (ic->op == JUMPTABLE) + { + if (operandOnStack (IC_JTCOND (ic))) + uses++; + break; + } + else if (ic->op == ADDRESS_OF) + { + if (operandOnStack (IC_RIGHT (ic))) + break; + } + else if (ic->op == LABEL || ic->op == GOTO || ic->op == CALL || ic->op == PCALL) + break; + else if (POINTER_SET (ic) || POINTER_GET (ic)) + break; + else + { + if (operandConflictsWithHX (IC_RESULT (ic))) + break; + if (operandOnStack (IC_LEFT (ic))) + uses += operandSize (IC_LEFT (ic)); + if (operandOnStack (IC_RIGHT (ic))) + uses += operandSize (IC_RIGHT (ic)); + if (operandOnStack (IC_RESULT (ic))) + uses += operandSize (IC_RESULT (ic)); + } + + ic = ic->next; + } + + return uses>=2; +} + + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space; + + wassertl (ic != NULL, "Got a null iCode"); + wassertl (sym != NULL, "Got a null symbol"); + +// printf("in aopForSym for symbol %s\n", sym->name); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + return sym->aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_calloc (1, strlen (sym->rname) + 1); + strcpy (aop->aopu.aop_immd.aop_immd1, sym->rname); + aop->size = FARPTRSIZE; + return aop; + } + + /* if it is on the stack */ + if (sym->onStack) + { + sym->aop = aop = newAsmop (AOP_SOF); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + aop->aopu.aop_stk = sym->stack; + + if (!regalloc_dry_run && hc08_reg_hx->isFree && hc08_reg_hx->aop != &tsxaop) + { + if (!hc08_reg_h->isDead || !hc08_reg_x->isDead) + return aop; + if (ic->op == IFX && operandConflictsWithHX (IC_COND (ic))) + return aop; + else if (ic->op == JUMPTABLE && operandConflictsWithHX (IC_JTCOND (ic))) + return aop; + else + { + /* If this is a pointer gen/set, then hx is definitely in use */ + if (POINTER_SET (ic) || POINTER_GET (ic)) + return aop; + if (ic->op == ADDRESS_OF) + return aop; + if (operandConflictsWithHX (IC_LEFT (ic))) + return aop; + if (operandConflictsWithHX (IC_RIGHT (ic))) + return aop; + } + /* It's safe to use tsx here. tsx costs 1 byte and 2 cycles but */ + /* can save us 1 byte and 1 cycle for each time we can use x */ + /* instead of sp. For a single use, we break even on bytes, but */ + /* lose a cycle. Make sure there are at least two uses. */ + if (!tsxUseful (ic)) + return aop; + emitcode ("tsx", ""); + hc08_dirtyReg (hc08_reg_hx, FALSE); + hc08_reg_hx->aop = &tsxaop; + _G.tsxStackPushes = _G.stackPushes; + } + return aop; + } + + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* default to far space */ + sym->aop = aop = newAsmop (AOP_EXT); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = NULL; + int val = 0; + + if (!ic) + { + fprintf (stderr, "Symbol %s to be rematerialized, but has no rematiCode.\n", sym->name); + wassert (0); + } + + for (;;) + { + if (ic->op == '+') + val += (int) operandLitValue (IC_RIGHT (ic)); + else if (ic->op == '-') + val -= (int) operandLitValue (IC_RIGHT (ic)); + else if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + continue; + } + else + break; + + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + + if (ic->op == ADDRESS_OF) + { + if (val) + { + SNPRINTF (buffer, sizeof (buffer), + "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); + } + else + { + strncpyz (buffer, OP_SYMBOL (IC_LEFT (ic))->rname, sizeof (buffer)); + } + + aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_strdup (buffer); + /* set immd2 field if required */ + } + else if (ic->op == '=') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + val &= 0xffff; + SNPRINTF (buffer, sizeof (buffer), "0x%04x", val); + aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = constVal (buffer); + } + else + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unexpected rematerialization"); + } + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand *op1, operand *op2) +{ + symbol *sym1, *sym2; + + /* if they not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + /* if they have the same rname */ + if (sym1->rname[0] && sym2->rname[0] && strcmp (sym1->rname, sym2->rname) == 0) + return TRUE; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + +// if (aop1->size != aop2->size) +// return FALSE; + + if (aop1->type == aop2->type) + { + switch (aop1->type) + { + case AOP_REG: + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + return TRUE; + case AOP_SOF: + return (aop1->aopu.aop_stk == aop2->aopu.aop_stk); + case AOP_DIR: + if (regalloc_dry_run) + return FALSE; + case AOP_EXT: + return (!strcmp (aop1->aopu.aop_dir, aop2->aopu.aop_dir)); + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand *op, iCode * ic, bool result) +{ + asmop *aop = NULL; + symbol *sym; + int i; + + if (!op) + return; + + // Is this a pointer set result? + // + if ((op == IC_RESULT (ic)) && POINTER_SET (ic)) + { + } + +// printf("checking literal\n"); + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + aop->op = op; + return; + } + +// printf("checking pre-existing\n"); + /* if already has a asmop then continue */ + if (op->aop) + { + op->aop->op = op; + return; + } + +// printf("checking underlying sym\n"); + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = aop = Safe_calloc (1, sizeof (*aop)); + memcpy (aop, OP_SYMBOL (op)->aop, sizeof (*aop)); + //op->aop = aop = OP_SYMBOL (op)->aop; + aop->size = getSize (operandType (op)); + //printf ("reusing underlying symbol %s\n",OP_SYMBOL (op)->name); + //printf (" with size = %d\n", aop->size); + + aop->op = op; + return; + } + +// printf("checking true sym\n"); + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aop = aopForSym (ic, OP_SYMBOL (op), result); + aop->op = op; + //printf ("new symbol %s\n", OP_SYMBOL (op)->name); + //printf (" with size = %d\n", aop->size); + return; + } + + /* this is a temporary : this has + only five choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + if (!IS_SYMOP (op)) + piCode (ic, NULL); + sym = OP_SYMBOL (op); + +// printf("checking conditional\n"); + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = 0; + aop->op = op; + return; + } + +// printf("checking spilt\n"); + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { +// printf("checking remat\n"); + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = getSize (sym->type); + aop->op = op; + return; + } + + wassertl (!sym->ruonly, "sym->ruonly not supported"); + + if (regalloc_dry_run) // Todo: Handle dummy iTemp correctly + { + if (options.stackAuto || (currFunc && IFFUNC_ISREENT (currFunc->type))) + { + sym->aop = op->aop = aop = newAsmop (AOP_SOF); + aop->aopu.aop_stk = 8; /* bogus stack offset, high enough to prevent optimization */ + } + else + sym->aop = op->aop = aop = newAsmop (AOP_DIR); + aop->size = getSize (sym->type); + aop->op = op; + return; + } + + /* else spill location */ + if (sym->isspilt && sym->usl.spillLoc || regalloc_dry_run) + { + asmop *oldAsmOp = NULL; + + if (sym->usl.spillLoc->aop && sym->usl.spillLoc->aop->size != getSize (sym->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + //printf ("forcing new aop\n"); + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result); + if (sym->usl.spillLoc->aop->size != getSize (sym->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + aop->op = op; + //printf ("spill symbol %s\n", OP_SYMBOL (op)->name); + //printf (" with size = %d\n", aop->size); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + aop->op = op; + return; + } + +// printf("assuming register\n"); + /* must be in a register */ + wassert (sym->nRegs); + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + { + wassert (sym->regs[i] < regshc08 + 3); + aop->aopu.aop_reg[i] = sym->regs[i]; + aop->regmask |= sym->regs[i]->mask; + } + if ((sym->nRegs > 1) && (sym->regs[0]->mask > sym->regs[1]->mask)) + aop->regmask |= HC08MASK_REV; + aop->op = op; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + if (aop->freed) + goto dealloc; + + aop->freed = 1; + + if (aop->stacked) + { + int stackAdjust; + int loffset; + + DD (emitcode ("", "; freeAsmop restoring stacked %s", aopName (aop))); + aop->stacked = 0; + stackAdjust = 0; + for (loffset = 0; loffset < aop->size; loffset++) + if (aop->stk_aop[loffset]) + { + transferAopAop (aop->stk_aop[loffset], 0, aop, loffset); + stackAdjust++; + } + pullNull (stackAdjust); + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + + +/*-----------------------------------------------------------------*/ +/* aopDerefAop - treating the aop parameter as a pointer, return */ +/* an asmop for the object it references */ +/*-----------------------------------------------------------------*/ +asmop * +aopDerefAop (asmop * aop, int offset) +{ + int adr; + asmop *newaop = NULL; + sym_link *type, *etype; + int p_type; + struct dbuf_s dbuf; + + DD (emitcode ("", "; aopDerefAop(%s)", aopName (aop))); + if (aop->op) + { + + type = operandType (aop->op); + etype = getSpec (type); + /* if op is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + p_type = DCL_TYPE (type); + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + } + else + p_type = UPOINTER; + + switch (aop->type) + { + case AOP_IMMD: + if (p_type == POINTER) + newaop = newAsmop (AOP_DIR); + else + newaop = newAsmop (AOP_EXT); + if (!offset) + newaop->aopu.aop_dir = aop->aopu.aop_immd.aop_immd1; + else + { + dbuf_init (&dbuf, 64); + dbuf_printf (&dbuf, "(%s+%d)", aop->aopu.aop_immd.aop_immd1, offset); + newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf); + } + break; + case AOP_LIT: + adr = (int) ulFromVal (aop->aopu.aop_lit); + if (p_type == POINTER) + adr &= 0xff; + adr = (adr + offset) & 0xffff; + dbuf_init (&dbuf, 64); + + if (adr < 0x100) + { + newaop = newAsmop (AOP_DIR); + dbuf_printf (&dbuf, "0x%02x", adr); + } + else + { + newaop = newAsmop (AOP_EXT); + dbuf_printf (&dbuf, "0x%04x", adr); + } + newaop->aopu.aop_dir = dbuf_detach_c_str (&dbuf); + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "unsupported asmop"); + return NULL; + } + + + return newaop; +} + + +/*-----------------------------------------------------------------*/ +/* aopOpExtToIdx - attempt to convert AOP_EXT to AOP_IDX */ +/*-----------------------------------------------------------------*/ +static void +aopOpExtToIdx(asmop * result, asmop *left, asmop *right) +{ + int accesses=0; + int resultAccesses=0; + int leftAccesses=0; + int rightAccesses=0; + asmop * winner; + int winnerAccesses; + + if (!hc08_reg_x->isFree || !hc08_reg_h->isFree) + return; + + /* Need to replace at least two extended mode accesses with indexed */ + /* to break even with the extra cost of loading HX. Do a quick check */ + /* to see if anything is using extended mode at all. */ + if (result && result->type == AOP_EXT) + accesses += result->size; + if (left && left->type == AOP_EXT) + accesses += left->size; + if (right && right->type == AOP_EXT) + accesses += right->size; + if (accesses<2) + return; + + /* If an operand is already using or going to H or X then we cannot */ + /* use indexed addressing mode at the same time. */ + if (result && (IS_AOP_WITH_H (result) || IS_AOP_WITH_X (result))) + return; + if (left && (IS_AOP_WITH_H (left) || IS_AOP_WITH_X (left))) + return; + if (right && (IS_AOP_WITH_H (right) || IS_AOP_WITH_X (right))) + return; + + /* Decide which is the best asmop to make indexed. */ + if (result && result->type == AOP_EXT) + { + resultAccesses = result->size; + if (result->op && left && left->op && result->op->key == left->op->key) + resultAccesses += result->size; + if (result->op && right && right->op && result->op->key == right->op->key) + resultAccesses += result->size; + } + if (left && left->type == AOP_EXT) + { + leftAccesses = left->size; + if (left->op && right && right->op && left->op->key == right->op->key) + leftAccesses += left->size; + } + if (right && right->type == AOP_EXT) + { + rightAccesses = right->size; + } + + winner = result; winnerAccesses = resultAccesses; + if (leftAccesses > winnerAccesses) + { + winnerAccesses = leftAccesses; + winner = left; + } + if (rightAccesses > winnerAccesses) + { + winnerAccesses = rightAccesses; + winner = right; + } + + /* Make sure there were enough accesses of a single variable to be worthwhile. */ + if (winnerAccesses < 2) + return; + + if (winner->op && result && result->op && winner->op->key == result->op->key) + result->type = AOP_IDX; + if (winner->op && left && left->op && winner->op->key == left->op->key) + left->type = AOP_IDX; + if (winner->op && right && right->op && winner->op->key == right->op->key) + right->type = AOP_IDX; + loadRegFromImm (hc08_reg_hx, winner->aopu.aop_dir); +} + + +/*-----------------------------------------------------------------*/ +/* aopAdrStr - for referencing the address of the aop */ +/*-----------------------------------------------------------------*/ +/* loffset seems to have a weird meaning here. It seems to be nonzero in some places where one would expect an offset to be zero */ +static const char * +aopAdrStr (asmop * aop, int loffset, bool bit16) +{ + char *s = buffer; + char *rs; + int offset = aop->size - 1 - loffset - (bit16 ? 1 : 0); + int xofs; + + /* offset is greater than + size then zero */ + if (loffset > (aop->size - 1) && aop->type != AOP_LIT) + return zero; + + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + return zero; + + case AOP_IMMD: + if (loffset) + { + if (loffset > 1) + sprintf (s, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, loffset * 8); + else + sprintf (s, "#>%s", aop->aopu.aop_immd.aop_immd1); + } + else + sprintf (s, "#%s", aop->aopu.aop_immd.aop_immd1); + rs = Safe_calloc (1, strlen (s) + 1); + strcpy (rs, s); + return rs; + + case AOP_DIR: + if (regalloc_dry_run) + return "*dry"; + if (offset) + sprintf (s, "*(%s + %d)", aop->aopu.aop_dir, offset); + else + sprintf (s, "*%s", aop->aopu.aop_dir); + rs = Safe_calloc (1, strlen (s) + 1); + strcpy (rs, s); + return rs; + + case AOP_EXT: + if (regalloc_dry_run) + return "dry"; + if (offset) + sprintf (s, "(%s + %d)", aop->aopu.aop_dir, offset); + else + sprintf (s, "%s", aop->aopu.aop_dir); + rs = Safe_calloc (1, strlen (s) + 1); + strcpy (rs, s); + return rs; + + case AOP_REG: + return aop->aopu.aop_reg[loffset]->name; + + case AOP_LIT: + if (bit16) + return aopLiteralLong (aop->aopu.aop_lit, loffset, 2); + else + return aopLiteral (aop->aopu.aop_lit, loffset); + + case AOP_SOF: + if (!regalloc_dry_run && hc08_reg_hx->aop == &tsxaop) + { + xofs = _G.stackOfs + _G.tsxStackPushes + aop->aopu.aop_stk + offset; + if (xofs) + sprintf (s, "%d,x", xofs); + else + sprintf (s, ",x"); + } + else + sprintf (s, "%d,s", _G.stackOfs + _G.stackPushes + aop->aopu.aop_stk + offset + 1); + rs = Safe_calloc (1, strlen (s) + 1); + strcpy (rs, s); + return rs; + case AOP_IDX: + xofs = offset; /* For now, assume hx points to the base address of operand */ + if (xofs) + { + if (regalloc_dry_run) /* Don't worry about the exact offset during the dry run */ + return "1,x"; + sprintf (s, "%d,x", xofs); + rs = Safe_calloc (1, strlen (s) + 1); + strcpy (rs, s); + return rs; + } + else + return ",x"; + } + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopAdrStr got unsupported aop->type"); + exit (1); +} + + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand *op) +{ + int size; + size = AOP_SIZE (op); + return size; +} + + +/*-----------------------------------------------------------------*/ +/* asmopToBool - Emit code to convert an asmop to a boolean. */ +/* Result left in A (0=FALSE, 1=TRUE) if ResultInA, */ +/* otherwise result left in Z flag (1=FALSE, 0=TRUE) */ +/*-----------------------------------------------------------------*/ +static void +asmopToBool (asmop *aop, bool resultInA) +{ + bool isFloat; + symbol *tlbl, *tlbl1; + int size = aop->size; + bool needpula = FALSE; + bool flagsonly = TRUE; + int offset = size - 1; + sym_link *type; + + wassert (aop); + type = operandType (AOP_OP (aop)); + isFloat = IS_FLOAT (type); + + if (resultInA) + hc08_freeReg (hc08_reg_a); + + if (IS_BOOL (type)) + { + if (resultInA) + loadRegFromAop (hc08_reg_a, aop, 0); + else + rmwWithAop ("tst", aop, 0); + return; + } + + if (resultInA && size == 1) + { + loadRegFromAop (hc08_reg_a, aop, 0); + rmwWithReg ("neg", hc08_reg_a); + loadRegFromConst (hc08_reg_a, 0); + rmwWithReg ("rol", hc08_reg_a); + return; + } + + switch (aop->type) + { + case AOP_REG: + if (IS_AOP_A (aop)) + { + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + flagsonly = FALSE; + } + else if (IS_AOP_X (aop)) + { + emitcode ("tstx", ""); + regalloc_dry_run_cost++; + } + else if (IS_AOP_H (aop)) + { + if (hc08_reg_a->isFree) + { + transferRegReg (hc08_reg_h, hc08_reg_a, FALSE); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + flagsonly = FALSE; + hc08_freeReg (hc08_reg_a); + } + else if (hc08_reg_x->isFree) + { + transferRegReg (hc08_reg_h, hc08_reg_x, FALSE); + emitcode ("tstx", ""); + regalloc_dry_run_cost++; + hc08_freeReg (hc08_reg_x); + } + else + { + emitcode ("pshh", ""); + emitcode ("tst", "1,s"); + regalloc_dry_run_cost += 4; + if (IS_S08 && optimize.codeSpeed) + { + emitcode ("ais", "#1"); // 2 Bytes, 2 cycles. + regalloc_dry_run_cost += 2; + } + else + { + emitcode ("pulh", ""); // 1 Byte, 2 (hc08) / 3 (s08) cycles. + regalloc_dry_run_cost += 1; + } + + } + } + else if (IS_AOP_HX (aop)) + { + emitcode ("cphx", zero); + regalloc_dry_run_cost += 3; + } + else if (IS_AOP_XA (aop) || IS_AOP_AX (aop)) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tsta", ""); + if (!regalloc_dry_run) + emitcode ("bne", "%05d$", labelKey2num (tlbl->key)); + emitcode ("tstx", ""); + regalloc_dry_run_cost += 4; + if (!regalloc_dry_run) + emitLabel (tlbl); + } + else + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "Bad rIdx in asmopToBool"); + return; + } + break; + case AOP_EXT: + if (!resultInA && (size == 1) && !IS_AOP_A (aop) && !hc08_reg_a->isFree && hc08_reg_x->isFree) + { + loadRegFromAop (hc08_reg_x, aop, 0); + break; + } + if (resultInA) + needpula = FALSE; + else + needpula = pushRegIfUsed (hc08_reg_a); + loadRegFromAop (hc08_reg_a, aop, offset--); + if (isFloat) + { + emitcode ("and", "#0x7F"); //clear sign bit + regalloc_dry_run_cost += 2; + } + while (--size) + accopWithAop ("ora", aop, offset--); + if (needpula) + pullReg (hc08_reg_a); + else + { + hc08_freeReg (hc08_reg_a); + flagsonly = FALSE; + } + break; + case AOP_LIT: + /* Higher levels should optimize this case away but let's be safe */ + if (ulFromVal (aop->aopu.aop_lit)) + loadRegFromConst (hc08_reg_a, 1); + else + loadRegFromConst (hc08_reg_a, 0); + hc08_freeReg (hc08_reg_a); + break; + default: + if (size == 1) + { + if (resultInA) + { + loadRegFromAop (hc08_reg_a, aop, 0); + hc08_freeReg (hc08_reg_a); + flagsonly = FALSE; + } + else + { + emitcode ("tst", "%s", aopAdrStr (aop, 0, FALSE)); + regalloc_dry_run_cost += ((aop->type == AOP_DIR || aop->type == AOP_IMMD) ? 2 : 3); + } + break; + } + else if (size == 2) + { + if (hc08_reg_a->isFree) + { + loadRegFromAop (hc08_reg_a, aop, 0); + accopWithAop ("ora", aop, 1); + hc08_freeReg (hc08_reg_a); + flagsonly = FALSE; + } + else + { + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tst", "%s", aopAdrStr (aop, 0, FALSE)); + if (!regalloc_dry_run) + emitcode ("bne", "%05d$", labelKey2num (tlbl->key)); + emitcode ("tst", "%s", aopAdrStr (aop, 1, FALSE)); + regalloc_dry_run_cost += 4; + if (!regalloc_dry_run) + emitLabel (tlbl); + break; + } + } + else + { + needpula = pushRegIfUsed (hc08_reg_a); + loadRegFromAop (hc08_reg_a, aop, offset--); + if (isFloat) + { + emitcode ("and", "#0x7F"); + regalloc_dry_run_cost += 2; + } + while (--size) + accopWithAop ("ora", aop, offset--); + if (needpula) + pullReg (hc08_reg_a); + else + { + hc08_freeReg (hc08_reg_a); + flagsonly = FALSE; + } + } + } + + if (resultInA) + { + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (flagsonly) + { + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitBranch ("bne", tlbl1); + loadRegFromConst (hc08_reg_a, 0); + emitBranch ("bra", tlbl); + if (!regalloc_dry_run) + emitLabel (tlbl1); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 1); + } + else + { + emitBranch ("beq", tlbl); + loadRegFromConst (hc08_reg_a, 1); + } + if (!regalloc_dry_run) + emitLabel (tlbl); + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_useReg (hc08_reg_a); + } +} + +/*-----------------------------------------------------------------*/ +/* genCopy - Copy the value from one operand to another */ +/* The caller is responsible for aopOp and freeAsmop */ +/*-----------------------------------------------------------------*/ +static void +genCopy (operand * result, operand * source) +{ + int size = AOP_SIZE (result); + int srcsize = AOP_SIZE (source); + int offset = 0; + + /* if they are the same and not volatile */ + if (operandsEqu (result, source) && !isOperandVolatile (result, FALSE) && + !isOperandVolatile (source, FALSE)) + return; + + /* The source and destinations may be different size due to optimizations. */ + /* This is not a cast, so there is no need to worry about sign extension. */ + /* When this happens, it is usually just 1 byte source to 2 byte dest, so */ + /* nothing significant to optimize. */ + if (srcsize < size) + { + size -= srcsize; + while (srcsize) + { + transferAopAop (AOP (source), offset, AOP (result), offset); + offset++; + srcsize--; + } + while (size) + { + storeConstToAop (0, AOP (result), offset); + offset++; + size--; + } + + return; + } + + /* if they are the same registers */ + if (sameRegs (AOP (source), AOP (result))) + return; + + if (IS_AOP_HX (AOP (result)) && srcsize == 2) + { + loadRegFromAop (hc08_reg_hx, AOP (source), 0); + return; + } + if (IS_AOP_HX (AOP (source)) && size == 2) + { + storeRegToAop (hc08_reg_hx, AOP (result), 0); + return; + } + + /* If the result and right are 2 bytes and both in registers, we have to be careful */ + /* to make sure the registers are not overwritten prematurely. */ + if (AOP_SIZE (result) == 2 && AOP (result)->type == AOP_REG && AOP (source)->type == AOP_REG) + { + if (AOP (result)->aopu.aop_reg[0] == AOP (source)->aopu.aop_reg[1] && + AOP (result)->aopu.aop_reg[1] == AOP (source)->aopu.aop_reg[0]) + { + pushReg (AOP (source)->aopu.aop_reg[1], TRUE); + transferAopAop (AOP (source), 0, AOP (result), 0); + pullReg (AOP (result)->aopu.aop_reg[1]); + } + else if (AOP (result)->aopu.aop_reg[0] == AOP (source)->aopu.aop_reg[1]) + { + transferAopAop (AOP (source), 1, AOP (result), 1); + transferAopAop (AOP (source), 0, AOP (result), 0); + } + else + { + transferAopAop (AOP (source), 0, AOP (result), 0); + transferAopAop (AOP (source), 1, AOP (result), 1); + } + return; + } + + if (IS_HC08 && (size > 2)) + aopOpExtToIdx (AOP (result), NULL, AOP (source)); + + /* general case */ + /* Copy in msb to lsb order, since some multi-byte hardware registers */ + /* expect this order. */ + offset = size - 1; + while (size) + { + if (size >= 2 && hc08_reg_h->isDead && hc08_reg_x->isDead && + (AOP_TYPE (source) == AOP_IMMD || AOP_TYPE (source) == AOP_LIT ||IS_S08 && AOP_TYPE (source) == AOP_EXT) && + (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT)) + { + loadRegFromAop (hc08_reg_hx, AOP (source), offset - 1); + storeRegToAop (hc08_reg_hx, AOP (result), offset - 1); + offset -= 2; + size -= 2; + } + + else + { + if (AOP_TYPE (source) == AOP_IDX && AOP_TYPE (result) == AOP_DIR) + { + emitcode ("mov", ",x+,%s", aopAdrStr (AOP (result), offset, FALSE)); + regalloc_dry_run_cost += 2; + } + else if (AOP_TYPE (source) == AOP_DIR && AOP_TYPE (result) == AOP_IDX) + { + emitcode ("mov", "%s,x+", aopAdrStr (AOP (source), offset, FALSE)); + regalloc_dry_run_cost += 2; + } + else + transferAopAop (AOP (source), offset, AOP (result), offset); + offset--; + size--; + } + } +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + bool needpulla; + + D (emitcode ("; genNot", "")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + needpulla = pushRegIfSurv (hc08_reg_a); + asmopToBool (AOP (IC_LEFT (ic)), TRUE); + + emitcode ("eor", one); + regalloc_dry_run_cost += 2; + storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), FALSE); + pullOrFreeReg (hc08_reg_a, needpulla); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + reg_info *reg; + bool needpullreg; + + D (emitcode ("; genCpl", "")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + size = AOP_SIZE (IC_RESULT (ic)); + + if(AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && + AOP (IC_RESULT (ic))->aopu.aop_reg[0] == AOP (IC_LEFT (ic))->aopu.aop_reg[0] && + (size < 2 || AOP (IC_RESULT (ic))->aopu.aop_reg[1] == AOP (IC_LEFT (ic))->aopu.aop_reg[1])) + { + while (size--) + rmwWithReg ("com", AOP (IC_RESULT (ic))->aopu.aop_reg[offset++]); + goto release; + } + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + { + while (size--) + { + if ((reg = AOP (IC_RESULT (ic))->aopu.aop_reg[offset]) != hc08_reg_h) + { + transferAopAop (AOP (IC_LEFT (ic)), offset, AOP (IC_RESULT (ic)), offset); + rmwWithReg ("com", reg); + } + else + { + if ((reg = AOP (IC_LEFT (ic))->aopu.aop_reg[offset]) == hc08_reg_h) + reg = hc08_reg_a->isDead ? hc08_reg_a : hc08_reg_x; + needpullreg = pushRegIfSurv (reg); + loadRegFromAop (reg, AOP (IC_LEFT (ic)), offset); + rmwWithReg ("com", reg); + storeRegToAop (reg, AOP (IC_RESULT (ic)), offset); + if (needpullreg) + pullReg (reg); + } + offset++; + } + goto release; + } + + reg = (hc08_reg_a->isDead && !(AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[0] == hc08_reg_a) ? hc08_reg_a : hc08_reg_x); + + needpullreg = pushRegIfSurv (reg); + while (size--) + { + bool needpullreg2 = (!size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[0] == reg || size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[1] == reg); + if (needpullreg2) + pushReg (reg, TRUE); + loadRegFromAop (reg, AOP (IC_LEFT (ic)), offset); + rmwWithReg ("com", reg); + hc08_useReg (reg); + storeRegToAop (reg, AOP (IC_RESULT (ic)), offset); + hc08_freeReg (reg); + if (needpullreg2) + pullReg (reg); + offset++; + } + pullOrFreeReg (reg, needpullreg); + + /* release the aops */ +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + bool needpula; + + D (emitcode ("; genUminusFloat", "")); + + /* for this we just copy and then flip the bit */ + + size = AOP_SIZE (op) - 1; + + while (size--) + { + transferAopAop (AOP (op), offset, AOP (result), offset); + offset++; + } + + needpula = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (op), offset); + emitcode ("eor", "#0x80"); + regalloc_dry_run_cost += 2; + hc08_useReg (hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), offset); + pullOrFreeReg (hc08_reg_a, needpula); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype; + char *sub; + bool needpula; + asmop *result; + + D (emitcode ("; genUminus", "")); + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) + { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero */ + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + + if (size == 1) + { + needpula = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), 0); + rmwWithReg ("neg", hc08_reg_a); + hc08_freeReg (hc08_reg_a); + storeRegToFullAop (hc08_reg_a, AOP (IC_RESULT (ic)), SPEC_USIGN (operandType (IC_LEFT (ic)))); + pullOrFreeReg (hc08_reg_a, needpula); + goto release; + } + + /* If either left or result are in registers, handle this carefully to */ + /* avoid prematurely overwriting register values. The 1 byte case was */ + /* handled above and there aren't enough registers to handle 4 byte values */ + /* so this case only needs to deal with 2 byte values. */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + { + reg_info *result0 = NULL; + reg_info *left0 = NULL; + reg_info *left1 = NULL; + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + { + left0 = AOP (IC_LEFT (ic))->aopu.aop_reg[0]; + left1 = AOP (IC_LEFT (ic))->aopu.aop_reg[1]; + } + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG) + { + result0 = AOP (IC_RESULT (ic))->aopu.aop_reg[0]; + } + needpula = pushRegIfSurv (hc08_reg_a); + if (left1 == hc08_reg_a) + pushReg (left1, TRUE); + + if (left0 == hc08_reg_a) + rmwWithReg ("neg", hc08_reg_a); + else + { + loadRegFromConst (hc08_reg_a, 0); + accopWithAop ("sub", AOP (IC_LEFT (ic)), 0); + } + if (result0 == hc08_reg_a || (result0 && result0 == left1)) + pushReg (hc08_reg_a, TRUE); + else + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); + loadRegFromConst (hc08_reg_a, 0); + if (left1 == hc08_reg_a) + { + emitcode ("sbc","%d,s", (result0 == hc08_reg_a || (result0 && result0 == left1)) ? 2 : 1); + regalloc_dry_run_cost += 3; + hc08_dirtyReg (hc08_reg_a, FALSE); + } + else + { + accopWithAop ("sbc", AOP (IC_LEFT (ic)), 1); + } + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 1); + if (result0 == hc08_reg_a || (result0 && result0 == left1)) + pullReg (result0); + if (left1 == hc08_reg_a) + pullNull (1); + pullOrFreeReg (hc08_reg_a, needpula); + goto release; + } + + result = AOP (IC_RESULT (ic)); + + needpula = pushRegIfSurv (hc08_reg_a); + sub = "sub"; + while (size--) + { + loadRegFromConst (hc08_reg_a, 0); + accopWithAop (sub, AOP (IC_LEFT (ic)), offset); + storeRegToAop (hc08_reg_a, result, offset++); + sub = "sbc"; + } + storeRegSignToUpperAop (hc08_reg_a, result, offset, SPEC_USIGN (operandType (IC_LEFT (ic)))); + pullOrFreeReg (hc08_reg_a, needpula); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode *lic) +{ + int i; + iCode *ic; + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already or don't need to be then + do nothing */ + if (ic->regsSaved) + return; + if (IS_SYMOP (IC_LEFT (ic)) && + (IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type) || IFFUNC_ISNAKED (OP_SYM_TYPE (IC_LEFT (ic))))) + return; + + if (!regalloc_dry_run) + ic->regsSaved = 1; + for (i = A_IDX; i <= H_IDX; i++) + { + if (bitVectBitValue (ic->rSurv, i)) + pushReg (hc08_regWithIdx (i), FALSE); + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode *ic) +{ + int i; + + for (i = H_IDX; i >= A_IDX; i--) + { + if (bitVectBitValue (ic->rSurv, i)) + pullReg (hc08_regWithIdx (i)); + } + +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand *oper, int size, iCode *ic) +{ + int offset = 0; + bool xIsFree = hc08_reg_x->isFree; + + hc08_useReg (hc08_reg_x); + aopOp (oper, ic, FALSE); + + if (AOP_TYPE (oper) == AOP_REG) + { + /* The operand is in registers; we can push them directly */ + while (size--) + { + pushReg (AOP (oper)->aopu.aop_reg[offset++], TRUE); + } + } + else if (hc08_reg_a->isFree) + { + /* A is free, so piecewise load operand into a and push A */ + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (oper), offset++); + pushReg (hc08_reg_a, TRUE); + } + } + else + { + /* A is not free. Adjust stack, preserve A, copy operand */ + /* into position on stack (using A), and restore original A */ + adjustStack (-size); + pushReg (hc08_reg_a, TRUE); + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (oper), offset++); + emitcode ("sta", "%d,s", 2+size); + regalloc_dry_run_cost += 3; + } + pullReg (hc08_reg_a); + } + + freeAsmop (oper, NULL, ic, TRUE); + if (xIsFree) + hc08_freeReg (hc08_reg_x); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - */ +/*-----------------------------------------------------------------*/ +static void +assignResultValue (operand * oper) +{ + int size = AOP_SIZE (oper); + int offset = 0; + bool delayed_x = FALSE; + while (size--) + { + if (!offset && AOP_TYPE (oper) == AOP_REG && AOP_SIZE (oper) > 1 && AOP (oper)->aopu.aop_reg[0]->rIdx == X_IDX) + { + pushReg (hc08_reg_a, TRUE); + delayed_x = TRUE; + } + else + transferAopAop (hc08_aop_pass[offset], 0, AOP (oper), offset); + if (hc08_aop_pass[offset]->type == AOP_REG) + hc08_freeReg (hc08_aop_pass[offset]->aopu.aop_reg[0]); + offset++; + } + if (delayed_x) + pullReg (hc08_reg_x); +} + + + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + + D (emitcode (";", "genIpush")); + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset++); + pushReg (hc08_reg_a, TRUE); + } + return; + } + + /* this is a parameter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + if (!regalloc_dry_run) /* Cost for saving registers is counted at CALL or PCALL */ + saveRegisters (ic); + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE); + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + +// l = aopGet (AOP (IC_LEFT (ic)), 0, FALSE, TRUE); + if (AOP_TYPE (IC_LEFT (ic)) == AOP_IMMD || AOP_TYPE (IC_LEFT (ic)) == AOP_LIT ||IS_AOP_HX (AOP (IC_LEFT (ic)))) + { + if ((size == 2) && hc08_reg_hx->isDead || IS_AOP_HX (AOP (IC_LEFT (ic)))) + { + loadRegFromAop (hc08_reg_hx, AOP (IC_LEFT (ic)), 0); + pushReg (hc08_reg_hx, TRUE); + goto release; + } + } + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + { + while (size--) + pushReg (AOP (IC_LEFT (ic))->aopu.aop_reg[offset++], TRUE); + goto release; + } + + while (size--) + { +// printf("loading %d\n", offset); + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset++); +// printf("pushing \n"); + pushReg (hc08_reg_a, TRUE); + } + +release: + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genIpop")); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = size - 1; + while (size--) + { + pullReg (hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (IC_LEFT (ic)), offset--); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSend - gen code for SEND */ +/*-----------------------------------------------------------------*/ +static void +genSend (set *sendSet) +{ + iCode *send1; + iCode *send2; + + D (emitcode (";", "genSend")); + + /* case 1: single parameter in A + * case 2: single parameter in XA + * case 3: first parameter in A, second parameter in X + */ + send1 = setFirstItem (sendSet); + send2 = setNextItem (sendSet); + + if (!send2) + { + int size; + /* case 1 or 2, this is fairly easy */ + aopOp (IC_LEFT (send1), send1, FALSE); + size = AOP_SIZE (IC_LEFT (send1)); + wassert (size <= 2); + if (size == 1) + { + loadRegFromAop (send1->argreg == 2 ? hc08_reg_x : hc08_reg_a, AOP (IC_LEFT (send1)), 0); + } + else if (AOP (IC_LEFT (send1))->type == AOP_REG) + loadRegFromAop (hc08_reg_xa, AOP (IC_LEFT (send1)), 0); + else if (isOperandVolatile (IC_LEFT (send1), FALSE)) + { + /* use msb to lsb order for volatile operands */ + loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send1)), 1); + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); + } + else + { + /* otherwise perfer to load x last (lsb to msb order) */ + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); + loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send1)), 1); + } + freeAsmop (IC_LEFT (send1), NULL, send1, TRUE); + } + else + { + /* case 3 */ + /* make sure send1 is the first argument and swap with send2 if not */ + if (send1->argreg > send2->argreg) + { + iCode * sic = send1; + send1 = send2; + send2 = sic; + } + aopOp (IC_LEFT (send1), send1, FALSE); + aopOp (IC_LEFT (send2), send2, FALSE); + if (IS_AOP_X (AOP (IC_LEFT (send1))) && IS_AOP_A (AOP (IC_LEFT (send2)))) + { + /* If the parameters' register assignment is exactly backwards */ + /* from what is needed, then swap the registers. */ + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + } + else if (IS_AOP_A (AOP (IC_LEFT (send2)))) + { + loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send2)), 0); + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (IC_LEFT (send1)), 0); + loadRegFromAop (hc08_reg_x, AOP (IC_LEFT (send2)), 0); + } + freeAsmop (IC_LEFT (send2), NULL, send2, TRUE); + freeAsmop (IC_LEFT (send1), NULL, send1, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; +// bool restoreBank = FALSE; +// bool swapBanks = FALSE; + + D (emitcode (";", "genCall")); + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + dtype = operandType (IC_LEFT (ic)); + etype = getSpec (dtype); + /* if send set is not empty then assign */ + if (_G.sendSet && !regalloc_dry_run) + { + if (IFFUNC_ISREENT (dtype)) /* need to reverse the send set */ + { + genSend (reverseSet (_G.sendSet)); + } + else + { + genSend (_G.sendSet); + } + _G.sendSet = NULL; + } + + /* make the call */ + if (IS_LITERAL (etype)) + { + emitcode ("jsr", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + regalloc_dry_run_cost += 3; + } + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); + + emitcode (jump ? "jmp" : "jsr", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + regalloc_dry_run_cost += 3; + } + + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_dirtyReg (hc08_reg_hx, FALSE); + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + hc08_useReg (hc08_reg_a); + if (operandSize (IC_RESULT (ic)) > 1) + hc08_useReg (hc08_reg_x); + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + pullNull (ic->parmBytes); + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + symbol *rlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + iCode * sendic; + + D (emitcode (";", "genPcall")); + + dtype = operandType (IC_LEFT (ic))->next; + etype = getSpec (dtype); + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* Go through the send set and mark any registers used by iTemps as */ + /* in use so we don't clobber them while setting up the return address */ + for (sendic = setFirstItem (_G.sendSet); sendic; sendic = setNextItem (_G.sendSet)) + { + updateiTempRegisterUse (IC_LEFT (sendic)); + } + + if (!IS_LITERAL (etype)) + { + /* push the return address on to the stack */ + emitBranch ("bsr", tlbl); + emitBranch ("bra", rlbl); + if (!regalloc_dry_run) + emitLabel (tlbl); + _G.stackPushes += 2; /* account for the bsr return address now on stack */ + updateCFA (); + + /* now push the function address */ + pushSide (IC_LEFT (ic), FARPTRSIZE, ic); + } + + /* if send set is not empty then assign */ + if (_G.sendSet && !regalloc_dry_run) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + /* make the call */ + if (!IS_LITERAL (etype)) + { + emitcode ("rts", ""); + regalloc_dry_run_cost++; + + if (!regalloc_dry_run) + emitLabel (rlbl); + _G.stackPushes -= 4; /* account for rts here & in called function */ + updateCFA (); + } + else + { + emitcode ("jsr", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + regalloc_dry_run_cost += 3; + } + + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_dirtyReg (hc08_reg_hx, FALSE); + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + hc08_useReg (hc08_reg_a); + if (operandSize (IC_RESULT (ic)) > 1) + hc08_useReg (hc08_reg_x); + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + pullNull (ic->parmBytes); + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static int +regsCmp (void *p1, void *p2) +{ + return (STRCASECMP ((char *) p1, (char *) (p2)) == 0); +} + +static bool +inExcludeList (char *s) +{ + const char *p = setFirstItem (options.excludeRegsSet); + + if (p == NULL || STRCASECMP (p, "none") == 0) + return FALSE; + + + return isinSetWith (options.excludeRegsSet, s, regsCmp); +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype; + iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL; + int stackAdjust = sym->stack; + int accIsFree = sym->recvSize == 0; + + _G.nRegsSaved = 0; + _G.stackPushes = 0; + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", sym->name); + emitcode (";", "-----------------------------------------"); + emitcode (";", hc08_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal."); + emitcode (";", "Stack space usage: %d bytes.", sym->stack); + + emitcode ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + ftype = operandType (IC_LEFT (ic)); + + _G.stackOfs = 0; + _G.stackPushes = 0; + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, hc08_reg_sp, 0); + + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no prologue."); + return; + } + + /* if this is an interrupt service routine then + save h */ + if (IFFUNC_ISISR (sym->type)) + { + + if (!inExcludeList ("h")) + pushReg (hc08_reg_h, FALSE); + } + + /* For some cases it is worthwhile to perform a RECEIVE iCode */ + /* before setting up the stack frame completely. */ + while (ric && ric->next && ric->next->op == RECEIVE) + ric = ric->next; + while (ric && IC_RESULT (ric)) + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ric)); + int rsymSize = rsym ? getSize (rsym->type) : 0; + + if (rsym->isitmp) + { + if (rsym && rsym->regType == REG_CND) + rsym = NULL; + if (rsym && (/*rsym->accuse ||*/ rsym->ruonly)) + rsym = NULL; + if (rsym && (rsym->isspilt || rsym->nRegs == 0) && rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + } + + /* If the RECEIVE operand immediately spills to the first entry on the */ + /* stack, we can push it directly rather than use an sp relative store. */ + if (rsym && rsym->onStack && rsym->stack == -_G.stackPushes - rsymSize) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode ("; genReceive", "")); + for (ofs = 0; ofs < rsymSize; ofs++) + { + reg_info *reg = hc08_aop_pass[ofs + (ric->argreg - 1)]->aopu.aop_reg[0]; + pushReg (reg, TRUE); + if (reg->rIdx == A_IDX) + accIsFree = 1; + stackAdjust--; + } + genLine.lineElement.ic = ic; + ric->generated = 1; + } + ric = (ric->prev && ric->prev->op == RECEIVE) ? ric->prev : NULL; + } + + /* adjust the stack for the function */ + if (stackAdjust) + { + adjustStack (-stackAdjust); + } + _G.stackOfs = sym->stack; + _G.stackPushes = 0; + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + if (!accIsFree) + { + /* Function was passed parameters, so make sure A is preserved */ + pushReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, FALSE); + emitcode ("tpa", ""); + emitcode ("sta", "2,s"); + emitcode ("sei", ""); + regalloc_dry_run_cost += 5; + pullReg (hc08_reg_a); + } + else + { + /* No passed parameters, so A can be freely modified */ + emitcode ("tpa", ""); + regalloc_dry_run_cost++; + pushReg (hc08_reg_a, TRUE); + emitcode ("sei", ""); + regalloc_dry_run_cost++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + + if (IFFUNC_ISNAKED (sym->type)) + { + emitcode (";", "naked function: no epilogue."); + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + if (IFFUNC_ISCRITICAL (sym->type)) + { + if (!IS_VOID (sym->type->next)) + { + /* Function has return value, so make sure A is preserved */ + pushReg (hc08_reg_a, FALSE); + emitcode ("lda", "2,s"); + emitcode ("tap", ""); + regalloc_dry_run_cost += 4; + pullReg (hc08_reg_a); + pullNull (1); + } + else + { + /* Function returns void, so A can be freely modified */ + pullReg (hc08_reg_a); + emitcode ("tap", ""); + regalloc_dry_run_cost++; + } + } + + if (IFFUNC_ISREENT (sym->type) || options.stackAuto) + { + } + + if (sym->stack) + { + _G.stackPushes += sym->stack; + adjustStack (sym->stack); + } + + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto)) + { + } + + if (IFFUNC_ISISR (sym->type)) + { + + if (!inExcludeList ("h")) + pullReg (hc08_reg_h); + + + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("rti", ""); + regalloc_dry_run_cost++; + } + else + { + if (IFFUNC_CALLEESAVES (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i) || (hc08_ptrRegReq && (i == HX_IDX || i == HX_IDX))) + emitcode ("pop", "%s", hc08_regWithIdx (i)->name); /* Todo: Cost. Can't find this instruction in manual! */ + } + } + } + + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("rts", ""); + regalloc_dry_run_cost++; + } +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0; +// int pushed = 0; + bool delayed_x = FALSE; + + D (emitcode ("; genRet", "")); + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) + { + /* If returning a literal, we can load the bytes of the return value */ + /* in any order. By loading A and X first, any other bytes that match */ + /* can use the shorter sta and stx instructions. */ + offset = 0; + while (size--) + { + transferAopAop (AOP (IC_LEFT (ic)), offset, hc08_aop_pass[offset], 0); + offset++; + } + } + else + { + /* Take care when swapping a and x */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && size > 1 && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == X_IDX) + { + delayed_x = TRUE; + pushReg (hc08_reg_x, TRUE); + } + + offset = size - 1; + while (size--) + { + if (!(delayed_x && !offset)) + transferAopAop (AOP (IC_LEFT (ic)), offset, hc08_aop_pass[offset], 0); + offset--; + } + + if (delayed_x) + pullReg (hc08_reg_a); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + emitcode ("jmp", "%05d$", labelKey2num (returnLabel->key)); + regalloc_dry_run_cost += 3; + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + int i; + reg_info *reg; + + /* For the high level labels we cannot depend on any */ + /* register's contents. Amnesia time. */ + for (i = A_IDX; i <= XA_IDX; i++) + { + reg = hc08_regWithIdx (i); + if (reg) + { + reg->aop = NULL; + reg->isLitConst = 0; + } + } + + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + if (options.debug && !regalloc_dry_run) + debugFile->writeLabel (IC_LABEL (ic), ic); + + emitLabel (IC_LABEL (ic)); + +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a jmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + emitcode ("jmp", "%05d$", labelKey2num (IC_LABEL (ic)->key)); + regalloc_dry_run_cost += 3; +} + + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode * ic) +{ + int icount; + operand *left; + operand *result; + bool needpulx; + bool needpulh; + bool needpula; + unsigned int size = getDataSize (IC_RESULT (ic)); + unsigned int offset; + symbol *tlbl = NULL; + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + DD (emitcode ("", "; IS_AOP_HX = %d", IS_AOP_HX (AOP (left)))); + + if ((IS_AOP_HX (AOP (left)) || IS_AOP_HX (AOP (result)) || + ((AOP_TYPE (left) == AOP_DIR || IS_S08 && AOP_TYPE (left) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT))) && + (icount >= -128) && (icount <= 127) && (size == 2)) + { + needpulx = pushRegIfSurv (hc08_reg_x); + needpulh = pushRegIfSurv (hc08_reg_h); + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + emitcode ("aix", "#%d", icount); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + storeRegToAop (hc08_reg_hx, AOP (result), 0); + pullOrFreeReg (hc08_reg_h, needpulh); + pullOrFreeReg (hc08_reg_x, needpulx); + return TRUE; + } + if (size == 1 && (IS_AOP_X (AOP (result)) && (!IS_AOP_A (AOP (left)) || hc08_reg_h->isDead) || IS_AOP_X (AOP (left)) && !IS_AOP_A (AOP (result)) && hc08_reg_x->isDead && hc08_reg_h->isDead)) + { + icount = (int)(signed char) icount; /* truncate to a signed single byte value */ + needpulh = pushRegIfSurv (hc08_reg_h); + loadRegFromAop (hc08_reg_x, AOP (left), 0); + emitcode ("aix", "#%d", icount); + regalloc_dry_run_cost += 2; + storeRegToAop (hc08_reg_x, AOP (result), 0); + pullOrFreeReg (hc08_reg_h, needpulh); + return TRUE; + } + + DD (emitcode ("", "; icount = %d, sameRegs=%d", icount, sameRegs (AOP (left), AOP (result)))); + + if ((icount > 255) || (icount < 0)) + return FALSE; + + if (!sameRegs (AOP (left), AOP (result))) + return FALSE; + + D (emitcode ("; genPlusIncr", "")); + + aopOpExtToIdx (AOP (result), AOP (left), NULL); + + if (size > 1) + tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); + + if (icount == 1) + { + needpula = FALSE; + rmwWithAop ("inc", AOP (result), 0); + if (1 < size) + emitBranch ("bne", tlbl); + } + else + { + if (!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result))) + needpula = pushRegIfUsed (hc08_reg_a); + else + needpula = FALSE; + loadRegFromAop (hc08_reg_a, AOP (result), 0); + accopWithAop ("add", AOP (IC_RIGHT (ic)), 0); + hc08_useReg (hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), 0); + hc08_freeReg (hc08_reg_a); + if (1 < size) + emitBranch ("bcc", tlbl); + } + for (offset = 1; offset < size; offset++) + { + rmwWithAop ("inc", AOP (result), offset); + if ((offset + 1) < size) + emitBranch ("bne", tlbl); + } + + if (size > 1 && !regalloc_dry_run) + emitLabel (tlbl); + + pullOrFreeReg (hc08_reg_a, needpula); + + return TRUE; +} + + + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + char *add; + asmop *leftOp, *rightOp; + bool needpulla; + bool earlystore = FALSE; + bool delayedstore = FALSE; + bool mayskip = TRUE; + bool skip = FALSE; + + /* special cases :- */ + + D (emitcode ("; genPlus", "")); + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* we want registers on the left and literals on the right */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && !IS_AOP_WITH_A (AOP (IC_LEFT (ic))))) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + + + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + DD (emitcode ("", "; left size = %d", getDataSize (IC_LEFT (ic)))); + DD (emitcode ("", "; right size = %d", getDataSize (IC_RIGHT (ic)))); + DD (emitcode ("", "; result size = %d", getDataSize (IC_RESULT (ic)))); + + aopOpExtToIdx (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic))); + + size = getDataSize (IC_RESULT (ic)); + + leftOp = AOP (IC_LEFT (ic)); + rightOp = AOP (IC_RIGHT (ic)); + add = "add"; + + offset = 0; + needpulla = pushRegIfSurv (hc08_reg_a); + + if(size > 1 && IS_AOP_AX (AOP (IC_LEFT (ic)))) + { + earlystore = TRUE; + pushReg (hc08_reg_a, TRUE); + } + + while (size--) + { + if (earlystore && offset == 1) + pullReg (hc08_reg_a); + else + loadRegFromAop (hc08_reg_a, leftOp, offset); + if (!mayskip || AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT || (byteOfVal (AOP (IC_RIGHT (ic))->aopu.aop_lit, offset) != 0x00) ) + { + accopWithAop (add, rightOp, offset); + mayskip = FALSE; + skip = FALSE; + } + else + skip = TRUE; + if (size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + delayedstore = TRUE; + } + else + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset); + offset++; + hc08_freeReg (hc08_reg_a); + if (!skip) + add = "adc"; /* further adds must propagate carry */ + } + if (delayedstore) + pullReg (hc08_reg_a); + pullOrFreeReg (hc08_reg_a, needpulla); + + wassert (!earlystore || !delayedstore); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + int icount; + operand *left; + operand *result; + bool needpulx; + bool needpulh; + unsigned int size = getDataSize (IC_RESULT (ic)); +// int offset; +// symbol *tlbl; + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + if ((IS_AOP_HX (AOP (left)) || IS_AOP_HX (AOP (result)) || + ((AOP_TYPE (left) == AOP_DIR || IS_S08 && AOP_TYPE (left) == AOP_EXT) && (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT))) && + (icount >= -127) && (icount <= 128) && (size == 2)) + { + needpulx = pushRegIfSurv (hc08_reg_x); + needpulh = pushRegIfSurv (hc08_reg_h); + + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + emitcode ("aix", "#%d", -(int) icount); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + storeRegToAop (hc08_reg_hx, AOP (result), 0); + pullOrFreeReg (hc08_reg_h, needpulh); + pullOrFreeReg (hc08_reg_x, needpulx); + return TRUE; + } + + if ((icount > 1) || (icount < 0)) + return FALSE; + + if (!sameRegs (AOP (left), AOP (result))) + return FALSE; + + if (size != 1) + return FALSE; + + D (emitcode ("; genMinusDec", "")); + + aopOpExtToIdx (AOP (result), AOP (left), NULL); + + rmwWithAop ("dec", AOP (result), 0); + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) + { + if (sign) + { + emitcode ("rola", ""); + emitcode ("clra", ""); + emitcode ("sbc", zero); + hc08_dirtyReg (hc08_reg_a, FALSE); + regalloc_dry_run_cost += 4; + while (size--) + storeRegToAop (hc08_reg_a, AOP (result), offset++); + } + else + while (size--) + storeConstToAop (0, AOP (result), offset++); + } +} + + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + char *sub; + int size, offset = 0; + bool needpulla; + bool earlystore = FALSE; + bool delayedstore = FALSE; + + asmop *leftOp, *rightOp; + + D (emitcode ("; genMinus", "")); + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special cases :- */ + /* if I can do an decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + aopOpExtToIdx (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic))); + + size = getDataSize (IC_RESULT (ic)); + + + leftOp = AOP (IC_LEFT (ic)); + rightOp = AOP (IC_RIGHT (ic)); + sub = "sub"; + offset = 0; + + needpulla = pushRegIfSurv (hc08_reg_a); + + if (IS_AOP_A (rightOp)) + { + loadRegFromAop (hc08_reg_a, rightOp, offset); + accopWithAop (sub, leftOp, offset); + accopWithMisc ("nega", ""); + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset++); + pullOrFreeReg (hc08_reg_a, needpulla); + goto release; + } + + if (size > 1 && (IS_AOP_AX (AOP (IC_LEFT (ic))) || IS_AOP_AX (AOP (IC_RIGHT (ic))))) + { + earlystore = TRUE; + pushReg (hc08_reg_a, TRUE); + } + + while (size--) + { + if (earlystore && + (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX || + AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX)) + pullReg (hc08_reg_a); + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + loadRegFromAop (hc08_reg_a, leftOp, offset); + emitcode (sub, "1, s"); + hc08_dirtyReg (hc08_reg_a, FALSE); + regalloc_dry_run_cost += 3; + pullNull (1); + } + else + { + loadRegFromAop (hc08_reg_a, leftOp, offset); + accopWithAop (sub, rightOp, offset); + } + if (size && AOP_TYPE (IC_RESULT (ic)) == AOP_REG && AOP (IC_RESULT (ic))->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + delayedstore = TRUE; + } + else + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), offset); + offset++; + sub = "sbc"; + } + if(delayedstore) + pullReg (hc08_reg_a); + pullOrFreeReg (hc08_reg_a, needpulla); + + wassert (!earlystore || !delayedstore); + +release: + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + + + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8*8=8/16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + /* sym_link *opetype = operandType (result); */ + symbol *tlbl1, *tlbl2, *tlbl3, *tlbl4; + int size = AOP_SIZE (result); + bool negLiteral = FALSE; + bool lUnsigned, rUnsigned; + bool needpulla, needpullx; + + D (emitcode ("; genMultOneByte", "")); + + if (size < 1 || size > 2) + { + // this should never happen + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", AOP_SIZE (result), __FILE__, lineno); + exit (1); + } + + /* (if two literals: the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + /* if an operand is in A, make sure it is on the left */ + if (IS_AOP_A (AOP (right))) + { + operand *t = right; + right = left; + left = t; + } + + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + /* lUnsigned rUnsigned negLiteral negate case */ + /* false false false odd 3 */ + /* false false true even 3 */ + /* false true false odd 3 */ + /* false true true impossible */ + /* true false false odd 3 */ + /* true false true always 2 */ + /* true true false never 1 */ + /* true true true impossible */ + + /* case 1 */ + if (size == 1 || (lUnsigned && rUnsigned)) + { + // just an unsigned 8*8=8/16 multiply + //DD(emitcode (";","unsigned")); + + loadRegFromAop (hc08_reg_a, AOP (left), 0); + loadRegFromAop (hc08_reg_x, AOP (right), 0); + emitcode ("mul", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_xa, FALSE); + storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); + hc08_freeReg (hc08_reg_xa); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); + + return; + } + + // we have to do a signed multiply + + /* case 2 */ + /* left unsigned, right signed literal -- literal determines sign handling */ + if (AOP_TYPE (right) == AOP_LIT && lUnsigned && !rUnsigned) + { + signed char val = (signed char) ulFromVal (AOP (right)->aopu.aop_lit); + + loadRegFromAop (hc08_reg_a, AOP (left), 0); + emitcode ("ldx", "#0x%02x", val < 0 ? -val : val); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_x, FALSE); + + emitcode ("mul", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_xa, FALSE); + + if (val < 0) + { + rmwWithReg ("neg", hc08_reg_a); + tlbl4 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitBranch ("bcc", tlbl4); + rmwWithReg ("inc", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl4); + rmwWithReg ("neg", hc08_reg_x); + } + + storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); + hc08_freeReg (hc08_reg_xa); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + + /* case 3 */ + adjustStack (-1); + emitcode ("clr", "1,s"); + regalloc_dry_run_cost += 3; + + loadRegFromAop (hc08_reg_a, AOP (left), 0); + if (!lUnsigned) + { + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl1); + emitcode ("inc", "1,s"); + regalloc_dry_run_cost += 3; + rmwWithReg ("neg", hc08_reg_a); + regalloc_dry_run_cost++; + if (!regalloc_dry_run) + emitLabel (tlbl1); + } + + if (AOP_TYPE (right) == AOP_LIT && !rUnsigned) + { + signed char val = (signed char) ulFromVal (AOP (right)->aopu.aop_lit); + /* AND literal negative */ + if (val < 0) + { + emitcode ("ldx", "#0x%02x", -val); + regalloc_dry_run_cost += 2; + negLiteral = TRUE; + } + else + { + emitcode ("ldx", "#0x%02x", val); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_x, FALSE); + hc08_useReg (hc08_reg_x); + } + else + { + loadRegFromAop (hc08_reg_x, AOP (right), 0); + if (!rUnsigned) + { + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tstx", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl2); + emitcode ("inc", "1,s"); + regalloc_dry_run_cost += 3; + rmwWithReg ("neg", hc08_reg_x); + regalloc_dry_run_cost++; + if (!regalloc_dry_run) + emitLabel (tlbl2); + } + } + + emitcode ("mul", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_xa, FALSE); + + tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("dec", "1,s"); + regalloc_dry_run_cost += 3; + if (!lUnsigned && !rUnsigned && negLiteral) + emitBranch ("beq", tlbl3); + else + emitBranch ("bne", tlbl3); + + rmwWithReg ("neg", hc08_reg_a); + tlbl4 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitBranch ("bcc", tlbl4); + rmwWithReg ("inc", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl4); + rmwWithReg ("neg", hc08_reg_x); + + if (!regalloc_dry_run) + emitLabel (tlbl3); + adjustStack (1); + storeRegToFullAop (hc08_reg_xa, AOP (result), TRUE); + + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode ("; genMult", "")); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* if both are of size == 1 */ +// if (getSize(operandType(left)) == 1 && +// getSize(operandType(right)) == 1) + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + fprintf (stderr, "left: %d right: %d\n", getSize (OP_SYMBOL (left)->type), getSize (OP_SYMBOL (right)->type)); + fprintf (stderr, "left: %d right: %d\n", AOP_SIZE (left), AOP_SIZE (right)); + assert (0); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + symbol *tlbl1, *tlbl2, *tlbl3; + int size; + bool lUnsigned, rUnsigned; + bool runtimeSign, compiletimeSign; + bool needpulla, needpullh; + bool needpullx = FALSE; + bool preload_a = FALSE; + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + D (emitcode ("; genDivOneByte", "")); + + needpulla = pushRegIfSurv (hc08_reg_a); + needpullh = pushRegIfSurv (hc08_reg_h); + needpullx = pushRegIfSurv (hc08_reg_x); + + /* If both left and right are in registers and backwards from what we need, */ + /* the swap the operands and the registers. */ + if (IS_AOP_A (AOP (right)) && IS_AOP_X (AOP (left))) + { + operand * t; + t = left; + left = right; + right = t; + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + } + + size = AOP_SIZE (result); + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + if (IS_AOP_A (AOP (right))) + { + loadRegFromAop (hc08_reg_x, AOP (right), 0); + loadRegFromAop (hc08_reg_a, AOP (left), 0); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), 0); + loadRegFromAop (hc08_reg_x, AOP (right), 0); + } + loadRegFromConst (hc08_reg_h, 0); + emitcode ("div", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_dirtyReg (hc08_reg_h, FALSE); + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + pushConst (1); /* set sign flag */ + else + pushConst (0); /* reset sign flag */ + } + + if (IS_AOP_X (AOP (left))) + { + loadRegFromAop (hc08_reg_a, AOP (left), 0); + preload_a = TRUE; + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + { + emitcode ("ldx", "#0x%02x", -val); + regalloc_dry_run_cost += 2; + } + else + { + emitcode ("ldx", "#0x%02x", (unsigned char) val); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_x, FALSE); + hc08_useReg (hc08_reg_x); + } + else /* ! literal */ + { + loadRegFromAop (hc08_reg_x, AOP (right), 0); + if (!rUnsigned) + { + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tstx", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl1); + emitcode ("inc", "1,s"); + regalloc_dry_run_cost += 3; + rmwWithReg ("neg", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl1); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + emitcode ("lda", "#0x%02x", -val); + regalloc_dry_run_cost += 2; + } + else + { + emitcode ("lda", "#0x%02x", (unsigned char) val); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_useReg (hc08_reg_a); + } + else /* ! literal */ + { + if (!preload_a) + loadRegFromAop (hc08_reg_a, AOP (left), 0); + if (!lUnsigned) + { + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl2); + emitcode ("inc", "1,s"); + regalloc_dry_run_cost += 3; + rmwWithReg ("neg", hc08_reg_a); + if (!regalloc_dry_run) + emitLabel (tlbl2); + } + } + + loadRegFromConst (hc08_reg_h, 0); + emitcode ("div", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_x, FALSE); + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_dirtyReg (hc08_reg_h, FALSE); + + if (runtimeSign || compiletimeSign) + { + if (runtimeSign) + { + tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + pullReg (hc08_reg_x); + rmwWithReg ("lsr", hc08_reg_x); + if (size > 1) + loadRegFromConst (hc08_reg_x, 0); + emitBranch ("bcc", tlbl3); + rmwWithReg ("neg", hc08_reg_a); + if (size > 1) + rmwWithReg ("dec", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl3); + /* signed result now in A or XA */ + if (size == 1) + storeRegToAop (hc08_reg_a, AOP (result), 0); + else + storeRegToAop (hc08_reg_xa, AOP (result), 0); + } + else /* must be compiletimeSign */ + { + hc08_freeReg (hc08_reg_x); /* in case we need a free reg for the 0xff */ + rmwWithReg ("neg", hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), 0); + if (size > 1) + storeConstToAop (0xff, AOP (result), 1); + } + } + else + { + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + } + + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode ("; genDiv", "")); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1 && AOP_SIZE (result) <= 2) + { + genDivOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + symbol *tlbl1, *tlbl2, *tlbl3; + int size; + bool lUnsigned, rUnsigned; + bool runtimeSign, compiletimeSign; + bool needpulla, needpullh; + bool needpullx = FALSE; + bool preload_a = FALSE; + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + D (emitcode ("; genModOneByte", "")); + + size = AOP_SIZE (result); + + needpulla = pushRegIfSurv (hc08_reg_a); + needpullh = pushRegIfSurv (hc08_reg_h); + needpullx = pushRegIfSurv (hc08_reg_x); + + /* If both left and right are in registers and backwards from what we need, */ + /* the swap the operands and the registers. */ + if (IS_AOP_A (AOP (right)) && IS_AOP_X (AOP (left))) + { + operand * t; + t = left; + left = right; + right = t; + pushReg (hc08_reg_a, FALSE); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + pullReg (hc08_reg_x); + } + + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + if (IS_AOP_A (AOP (right))) + { + loadRegFromAop (hc08_reg_x, AOP (right), 0); + loadRegFromAop (hc08_reg_a, AOP (left), 0); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), 0); + loadRegFromAop (hc08_reg_x, AOP (right), 0); + } + loadRegFromConst (hc08_reg_h, 0); + emitcode ("div", ""); + regalloc_dry_run_cost++; + hc08_freeReg (hc08_reg_x); + hc08_dirtyReg (hc08_reg_a, TRUE); + hc08_dirtyReg (hc08_reg_h, FALSE); + storeRegToFullAop (hc08_reg_h, AOP (result), FALSE); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + /* signed is a little bit more difficult */ + if (IS_AOP_X (AOP (left))) + { + loadRegFromAop (hc08_reg_a, AOP (left), 0); + preload_a = TRUE; + } + + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + { + emitcode ("ldx", "#0x%02x", -val); + regalloc_dry_run_cost += 2; + } + else + { + emitcode ("ldx", "#0x%02x", (unsigned char) val); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_x, FALSE); + hc08_useReg (hc08_reg_x); + } + else /* ! literal */ + { + loadRegFromAop (hc08_reg_x, AOP (right), 0); + if (!rUnsigned) + { + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tstx", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl1); + rmwWithReg ("neg", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl1); + } + } + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + /* sign adjust left side */ + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + compiletimeSign = TRUE; /* set sign flag */ + emitcode ("lda", "#0x%02x", -val); + regalloc_dry_run_cost += 2; + } + else + { + emitcode ("lda", "#0x%02x", (unsigned char) val); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_useReg (hc08_reg_a); + } + else /* ! literal */ + { + if (lUnsigned) + { + if (!preload_a) + loadRegFromAop (hc08_reg_a, AOP (left), 0); + } + else + { + runtimeSign = TRUE; + pushConst (0); + + if (!preload_a) + loadRegFromAop (hc08_reg_a, AOP (left), 0); + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + emitBranch ("bpl", tlbl2); + emitcode ("inc", "1,s"); + regalloc_dry_run_cost += 3; + rmwWithReg ("neg", hc08_reg_a); + if (!regalloc_dry_run) + emitLabel (tlbl2); + } + } + + loadRegFromConst (hc08_reg_h, 0); + emitcode ("div", ""); + regalloc_dry_run_cost++; + hc08_freeReg (hc08_reg_x); + hc08_dirtyReg (hc08_reg_a, TRUE); + hc08_dirtyReg (hc08_reg_h, FALSE); + + if (runtimeSign || compiletimeSign) + { + transferRegReg (hc08_reg_h, hc08_reg_a, TRUE); + if (runtimeSign) + { + tlbl3 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + pullReg (hc08_reg_x); + rmwWithReg ("lsr", hc08_reg_x); + if (size > 1) + loadRegFromConst (hc08_reg_x, 0); + emitBranch ("bcc", tlbl3); + rmwWithReg ("neg", hc08_reg_a); + if (size > 1) + rmwWithReg ("dec", hc08_reg_x); + if (!regalloc_dry_run) + emitLabel (tlbl3); + /* signed result now in A or XA */ + if (size == 1) + storeRegToAop (hc08_reg_a, AOP (result), 0); + else + storeRegToAop (hc08_reg_xa, AOP (result), 0); + } + else /* must be compiletimeSign */ + { + hc08_freeReg (hc08_reg_x); /* in case we need a free reg for the 0xff */ + rmwWithReg ("neg", hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), 0); + if (size > 1) + storeConstToAop (0xff, AOP (result), 1); + } + } + else + { + storeRegToFullAop (hc08_reg_h, AOP (result), FALSE); + } + + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode ("; genMod", "")); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1 && AOP_SIZE (result) <= 2) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, char *jval) +{ + symbol *jlbl; + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + char *inst; + + D (emitcode ("; genIfxJump", "")); + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + if (!strcmp (jval, "a")) + inst = "beq"; + else if (!strcmp (jval, "c")) + inst = "bcc"; + else if (!strcmp (jval, "n")) + inst = "bpl"; + else + inst = "bge"; + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + if (!strcmp (jval, "a")) + inst = "bne"; + else if (!strcmp (jval, "c")) + inst = "bcs"; + else if (!strcmp (jval, "n")) + inst = "bmi"; + else + inst = "blt"; + } + emitBranch (inst, tlbl); + emitBranch ("jmp", jlbl); + if (!regalloc_dry_run) + emitLabel (tlbl); + + /* mark the icode as generated */ + ic->generated = 1; +} + + +/*-----------------------------------------------------------------*/ +/* exchangedCmp : returns the opcode need if the two operands are */ +/* exchanged in a comparison */ +/*-----------------------------------------------------------------*/ +static int +exchangedCmp (int opcode) +{ + switch (opcode) + { + case '<': + return '>'; + case '>': + return '<'; + case LE_OP: + return GE_OP; + case GE_OP: + return LE_OP; + case NE_OP: + return NE_OP; + case EQ_OP: + return EQ_OP; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + return EQ_OP; /* shouldn't happen, but need to return something */ +} + +/*------------------------------------------------------------------*/ +/* negatedCmp : returns the equivalent opcode for when a comparison */ +/* if not true */ +/*------------------------------------------------------------------*/ +static int +negatedCmp (int opcode) +{ + switch (opcode) + { + case '<': + return GE_OP; + case '>': + return LE_OP; + case LE_OP: + return '>'; + case GE_OP: + return '<'; + case NE_OP: + return EQ_OP; + case EQ_OP: + return NE_OP; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + return EQ_OP; /* shouldn't happen, but need to return something */ +} + +/*------------------------------------------------------------------*/ +/* nameCmp : helper function for human readable debug output */ +/*------------------------------------------------------------------*/ +static char * +nameCmp (int opcode) +{ + switch (opcode) + { + case '<': + return "<"; + case '>': + return ">"; + case LE_OP: + return "<="; + case GE_OP: + return ">="; + case NE_OP: + return "!="; + case EQ_OP: + return "=="; + default: + return "invalid"; + } +} + +/*------------------------------------------------------------------*/ +/* branchInstCmp : returns the conditional branch instruction that */ +/* will branch if the comparison is true */ +/*------------------------------------------------------------------*/ +static char * +branchInstCmp (int opcode, int sign) +{ + switch (opcode) + { + case '<': + if (sign) + return "blt"; + else + return "bcs"; /* same as blo */ + case '>': + if (sign) + return "bgt"; + else + return "bhi"; + case LE_OP: + if (sign) + return "ble"; + else + return "bls"; + case GE_OP: + if (sign) + return "bge"; + else + return "bcc"; /* same as bhs */ + case NE_OP: + return "bne"; + case EQ_OP: + return "beq"; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + return "brn"; +} + + +/*------------------------------------------------------------------*/ +/* genCmp :- greater or less than (and maybe with equal) comparison */ +/*------------------------------------------------------------------*/ +static void +genCmp (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign, opcode; + int size, offset = 0; + unsigned long long lit = 0ull; + char *sub; + symbol *jlbl = NULL; + bool needpulla = FALSE; + + opcode = ic->op; + + D (emitcode ("; genCmp", "(%s)", nameCmp (opcode))); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* need register operand on left, prefer literal operand on right */ + if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT) + { + operand *temp = left; + left = right; + right = temp; + opcode = exchangedCmp (opcode); + } + + if (ifx) + { + if (IC_TRUE (ifx)) + { + jlbl = IC_TRUE (ifx); + opcode = negatedCmp (opcode); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ifx); + } + } + + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (size == 1 && IS_AOP_X (AOP (left))) + { + accopWithAop ("cpx", AOP (right), offset); + } + else if (size == 1 && IS_AOP_A (AOP (left))) + { + accopWithAop ("cmp", AOP (right), offset); + } + else if ((size == 2) + && ((AOP_TYPE (left) == AOP_DIR || IS_AOP_HX (AOP (left))) && (AOP_SIZE (left) == 2)) + && ((AOP_TYPE (right) == AOP_LIT) || ((AOP_TYPE (right) == AOP_DIR || IS_S08 && AOP_TYPE (right) == AOP_EXT) && (AOP_SIZE (right) == 2))) && (hc08_reg_h->isDead && hc08_reg_x->isDead || IS_AOP_HX (AOP (left)))) + { + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + emitcode ("cphx", "%s", aopAdrStr (AOP (right), 0, TRUE)); + regalloc_dry_run_cost += (AOP_TYPE (right) == AOP_DIR ? 2 : 3); + hc08_freeReg (hc08_reg_hx); + } + else + { + offset = 0; + if (size == 1) + sub = "cmp"; + else + { + sub = "sub"; + + /* These conditions depend on the Z flag bit, but Z is */ + /* only valid for the last byte of the comparison, not */ + /* the whole value. So exchange the operands to get a */ + /* comparison that doesn't depend on Z. (This is safe */ + /* to do here since ralloc won't assign multi-byte */ + /* operands to registers for comparisons) */ + if ((opcode == '>') || (opcode == LE_OP)) + { + operand *temp = left; + left = right; + right = temp; + opcode = exchangedCmp (opcode); + } + + if ((AOP_TYPE (right) == AOP_LIT) && !isOperandVolatile (left, FALSE)) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + while ((size > 1) && (((lit >> (8 * offset)) & 0xff) == 0)) + { + offset++; + size--; + } + } + } + needpulla = pushRegIfSurv (hc08_reg_a); + while (size--) + { + if (AOP_TYPE (right) == AOP_REG && AOP(right)->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + loadRegFromAop (hc08_reg_a, AOP (left), offset); + emitcode (sub, "1, s"); + regalloc_dry_run_cost += 3; + pullReg (hc08_reg_a); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop (sub, AOP (right), offset); + } + hc08_freeReg (hc08_reg_a); + offset++; + sub = "sbc"; + } + } + freeAsmop (right, NULL, ic, FALSE); + freeAsmop (left, NULL, ic, FALSE); + + if (ifx) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + char *inst; + + pullOrFreeReg (hc08_reg_a, needpulla); + + freeAsmop (result, NULL, ic, TRUE); + + inst = branchInstCmp (opcode, sign); + emitBranch (inst, tlbl); + emitBranch ("jmp", jlbl); + if (!regalloc_dry_run) + emitLabel (tlbl); + + /* mark the icode as generated */ + ifx->generated = 1; + } + else + { + symbol *tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (!needpulla) + needpulla = pushRegIfSurv (hc08_reg_a); + + emitBranch (branchInstCmp (opcode, sign), tlbl1); + loadRegFromConst (hc08_reg_a, 0); + emitBranch ("bra", tlbl2); + if (!regalloc_dry_run) + emitLabel (tlbl1); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 1); + if (!regalloc_dry_run) + emitLabel (tlbl2); + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + pullOrFreeReg (hc08_reg_a, needpulla); + freeAsmop (result, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCmpEQorNE - equal or not equal comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpEQorNE (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int opcode; + int size, offset = 0; + symbol *jlbl = NULL; + symbol *tlbl_NE = NULL; + symbol *tlbl_EQ = NULL; + bool needpulla = FALSE; + + opcode = ic->op; + + D (emitcode ("; genCmpEQorNE", "(%s)", nameCmp (opcode))); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* need register operand on left, prefer literal operand on right */ + if ((AOP_TYPE (right) == AOP_REG) || AOP_TYPE (left) == AOP_LIT) + { + operand *temp = left; + left = right; + right = temp; + opcode = exchangedCmp (opcode); + } + + if (ifx) + { + if (IC_TRUE (ifx)) + { + jlbl = IC_TRUE (ifx); + opcode = negatedCmp (opcode); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ifx); + } + } + + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + if ((size == 2) + && ((AOP_TYPE (left) == AOP_DIR || IS_AOP_HX (AOP (left))) && (AOP_SIZE (left) == 2)) + && ((AOP_TYPE (right) == AOP_LIT) || ((AOP_TYPE (right) == AOP_DIR || IS_S08 && AOP_TYPE (right) == AOP_EXT) && (AOP_SIZE (right) == 2))) && hc08_reg_h->isDead && hc08_reg_x->isDead) + { + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + emitcode ("cphx", "%s", aopAdrStr (AOP (right), 0, TRUE)); + regalloc_dry_run_cost += (AOP_TYPE (right) == AOP_DIR ? 2 : 3); + hc08_freeReg (hc08_reg_hx); + } + else + { + offset = 0; + while (size--) + { + if (AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == X_IDX) + accopWithAop ("cpx", AOP (right), offset); + else + { + if (!(AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX)) + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), offset); + } + accopWithAop ("cmp", AOP (right), offset); + if (!(AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx == A_IDX)) + pullOrFreeReg (hc08_reg_a, needpulla); + needpulla = FALSE; + } + if (size) + { + if (!needpulla && !ifx) + needpulla = pushRegIfSurv (hc08_reg_a); + if (!tlbl_NE && !regalloc_dry_run) + tlbl_NE = newiTempLabel (NULL); + emitBranch ("bne", tlbl_NE); + pullOrFreeReg (hc08_reg_a, needpulla); + needpulla = FALSE; + } + offset++; + } + } + freeAsmop (right, NULL, ic, FALSE); + freeAsmop (left, NULL, ic, FALSE); + + if (ifx) + { + freeAsmop (result, NULL, ic, TRUE); + + if (opcode == EQ_OP) + { + if (!tlbl_EQ && !regalloc_dry_run) + tlbl_EQ = newiTempLabel (NULL); + emitBranch ("beq", tlbl_EQ); + if (tlbl_NE) + emitLabel (tlbl_NE); + emitBranch ("jmp", jlbl); + if (!regalloc_dry_run) + emitLabel (tlbl_EQ); + } + else + { + if (!tlbl_NE && !regalloc_dry_run) + tlbl_NE = newiTempLabel (NULL); + emitBranch ("bne", tlbl_NE); + emitBranch ("jmp", jlbl); + if (!regalloc_dry_run) + emitLabel (tlbl_NE); + } + + /* mark the icode as generated */ + ifx->generated = 1; + } + else + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (!needpulla) + needpulla = pushRegIfSurv (hc08_reg_a); + if (opcode == EQ_OP) + { + if (!tlbl_EQ && !regalloc_dry_run) + tlbl_EQ = newiTempLabel (NULL); + emitBranch ("beq", tlbl_EQ); + if (tlbl_NE) + emitLabel (tlbl_NE); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 0); + emitBranch ("bra", tlbl); + if (!regalloc_dry_run) + emitLabel (tlbl_EQ); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 1); + } + else + { + if (!tlbl_NE && !regalloc_dry_run) + tlbl_NE = newiTempLabel (NULL); + emitBranch ("bne", tlbl_NE); + loadRegFromConst (hc08_reg_a, 0); + emitBranch ("bra", tlbl); + if (!regalloc_dry_run) + emitLabel (tlbl_NE); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 1); + } + + if (!regalloc_dry_run) + emitLabel (tlbl); + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + pullOrFreeReg (hc08_reg_a, needpulla); + freeAsmop (result, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* hasInchc08 - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +iCode * +hasInchc08 (operand *op, const iCode *ic, int osize) +{ + sym_link *type = operandType (op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize; + + /* this could from a cast, e.g.: "(char xdata *) 0x7654;" */ + if (!IS_SYMOP (op)) + return NULL; + + if (IS_BITVAR (retype) || !IS_PTR (type)) + return NULL; + if (IS_AGGREGATE (type->next)) + return NULL; + if (osize != (isize = getSize (type->next))) + return NULL; + + while (lic) + { + /* if operand of the form op = op + */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl, *tlbl0; + bool needpulla; + + D (emitcode ("; genAndOp", "")); + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + tlbl0 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + needpulla = pushRegIfSurv (hc08_reg_a); + asmopToBool (AOP (left), FALSE); + emitBranch ("beq", tlbl0); + asmopToBool (AOP (right), FALSE); + emitBranch ("beq", tlbl0); + loadRegFromConst (hc08_reg_a, 1); + emitBranch ("bra", tlbl); + if (!regalloc_dry_run) + emitLabel (tlbl0); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 0); + if (!regalloc_dry_run) + emitLabel (tlbl); + hc08_dirtyReg (hc08_reg_a, FALSE); + + hc08_useReg (hc08_reg_a); + hc08_freeReg (hc08_reg_a); + + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + pullOrFreeReg(hc08_reg_a, needpulla); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl, *tlbl0; + bool needpulla; + + D (emitcode ("; genOrOp", "")); + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + tlbl0 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + needpulla = pushRegIfSurv (hc08_reg_a); + asmopToBool (AOP (left), FALSE); + emitBranch ("bne", tlbl0); + asmopToBool (AOP (right), FALSE); + emitBranch ("bne", tlbl0); + loadRegFromConst (hc08_reg_a, 0); + emitBranch ("bra", tlbl); + if (!regalloc_dry_run) + emitLabel (tlbl0); + hc08_dirtyReg (hc08_reg_a, FALSE); + loadRegFromConst (hc08_reg_a, 1); + if (!regalloc_dry_run) + emitLabel (tlbl); + hc08_dirtyReg (hc08_reg_a, FALSE); + + hc08_useReg (hc08_reg_a); + hc08_freeReg (hc08_reg_a); + + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + pullOrFreeReg(hc08_reg_a, needpulla); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = + { + 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx + 1; + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0ll; + unsigned long long litinv; + int bitpos = -1; + unsigned char bytemask; + bool needpulla = FALSE; + bool earlystore = FALSE; + + D (emitcode ("; genAnd", "")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); + DD (emitcode ("", "; Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right))); +#endif + + /* if left is a literal & right is not then exchange them */ + if (AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is accumulator & left is not then exchange them */ + if (AOP_TYPE (right) == AOP_REG && ! IS_AOP_WITH_A (AOP (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); + + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + if (size == 1) + lit &= 0xff; + else if (size == 2) + lit &= 0xffff; + else if (size == 4) + lit &= 0xffffffff; + else if (size == 8) + lit &= 0xffffffffffffffff; + bitpos = isLiteralBit (lit) - 1; + } + + if (ifx && AOP_TYPE (result) == AOP_CRY && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) == AOP_DIR && bitpos >= 0) + { + symbol *tlbl = NULL; + if (!regalloc_dry_run) + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + if (!regalloc_dry_run) + emitcode ("brclr", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE), labelKey2num ((tlbl->key))); + regalloc_dry_run_cost += 3; + emitBranch ("jmp", IC_TRUE (ifx)); + if (!regalloc_dry_run) + emitLabel (tlbl); + if (IC_FALSE (ifx)) + emitBranch ("jmp", IC_FALSE (ifx)); + } + else + { + if (!regalloc_dry_run) + emitcode ("brset", "#%d,%s,%05d$", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE), labelKey2num ((tlbl->key))); + regalloc_dry_run_cost += 3; + emitBranch ("jmp", IC_FALSE (ifx)); + if (!regalloc_dry_run) + emitLabel (tlbl); + } + ifx->generated = TRUE; + goto release; + } + + if (AOP_TYPE (result) == AOP_CRY && size > 1 && (isOperandVolatile (left, FALSE) || isOperandVolatile (right, FALSE))) + { + needpulla = pushRegIfSurv (hc08_reg_a); + + /* this generates ugly code, but meets volatility requirements */ + loadRegFromConst (hc08_reg_a, 0); + pushReg (hc08_reg_a, TRUE); + + offset = 0; + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("and", AOP (right), offset); + emitcode ("ora", "1,s"); + emitcode ("sta", "1,s"); + regalloc_dry_run_cost += 6; + offset++; + } + + pullReg (hc08_reg_a); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + + pullOrFreeReg (hc08_reg_a, needpulla); + + genIfxJump (ifx, "a"); + + goto release; + } + + if (AOP_TYPE (result) == AOP_CRY && AOP_TYPE (right) == AOP_LIT) + { + if (bitpos >= 0 && (bitpos & 7) == 7) + { + rmwWithAop ("tst", AOP (left), bitpos >> 3); + genIfxJump (ifx, "n"); + goto release; + } + } + + if (AOP_TYPE (result) == AOP_CRY && size == 1 && (IS_AOP_A (AOP (left)) || IS_AOP_A (AOP (right)))) + { + if (IS_AOP_A (AOP (left))) + accopWithAop ("bit", AOP (right), 0); + else + accopWithAop ("bit", AOP (left), 0); + genIfxJump (ifx, "a"); + goto release; + } + + if (AOP_TYPE (result) == AOP_CRY) + { + symbol *tlbl = NULL; + + needpulla = pushRegIfSurv (hc08_reg_a); + + offset = 0; + while (size--) + { + bytemask = (lit >> (offset * 8)) & 0xff; + + if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) + { + /* do nothing */ + } + else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) + { + rmwWithAop ("tst", AOP (left), offset); + if (size) + { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (NULL); + emitBranch ("bne", tlbl); + } + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("bit", AOP (right), offset); + hc08_freeReg (hc08_reg_a); + if (size) + { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (NULL); + emitBranch ("bne", tlbl); + } + } + offset++; + } + if (tlbl) + emitLabel (tlbl); + + pullOrFreeReg (hc08_reg_a, needpulla); + + if (ifx) + genIfxJump (ifx, "a"); + goto release; + } + + size = AOP_SIZE (result); + + if (AOP_TYPE (right) == AOP_LIT) + { + litinv = (~lit) & (((unsigned int) 0xffffffff) >> (8 * (4 - size))); + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (AOP_TYPE (left) == AOP_DIR) && isLiteralBit (litinv)) + { + bitpos = isLiteralBit (litinv) - 1; + emitcode ("bclr", "#%d,%s", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE)); + regalloc_dry_run_cost += 2; + goto release; + } + } + + needpulla = pushRegIfSurv (hc08_reg_a); + + offset = 0; + if (size >= 2 && IS_AOP_AX (AOP (left))) + { + pushReg (hc08_reg_a, TRUE); + earlystore = TRUE; + } + while (size--) + { + bytemask = (lit >> (offset * 8)) & 0xff; + + if (earlystore && offset == 1) + pullReg (hc08_reg_a); + if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) + { + if (isOperandVolatile (left, FALSE)) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + hc08_freeReg (hc08_reg_a); + } + storeConstToAop (0, AOP (result), offset); + } + else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) + { + transferAopAop (AOP (left), offset, AOP (result), offset); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("and", AOP (right), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + hc08_freeReg (hc08_reg_a); + } + if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + needpulla = TRUE; + } + offset++; + } + + pullOrFreeReg (hc08_reg_a, needpulla); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0ull; + unsigned char bytemask; + bool needpulla = FALSE; + bool earlystore = FALSE; + + D (emitcode ("; genOr", "")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); + DD (emitcode ("", "; Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right))); +#endif + + /* if left is a literal & right is not then exchange them */ + if (AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if left is accumulator & right is not then exchange them */ + if (AOP_TYPE (right) == AOP_REG && !IS_AOP_WITH_A (AOP (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); + + if (AOP_TYPE (result) == AOP_CRY && size > 1 && (isOperandVolatile (left, FALSE) || isOperandVolatile (right, FALSE))) + { + needpulla = pushRegIfSurv (hc08_reg_a); + + /* this generates ugly code, but meets volatility requirements */ + loadRegFromConst (hc08_reg_a, 0); + pushReg (hc08_reg_a, TRUE); + + offset = 0; + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("ora", AOP (right), offset); + emitcode ("ora", "1,s"); + emitcode ("sta", "1,s"); + regalloc_dry_run_cost += 6; + offset++; + } + + pullReg (hc08_reg_a); + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + + pullOrFreeReg (hc08_reg_a, needpulla); + + genIfxJump (ifx, "a"); + + goto release; + } + + if (AOP_TYPE (result) == AOP_CRY) + { + symbol *tlbl = NULL; + + needpulla = pushRegIfSurv (hc08_reg_a); + + offset = 0; + while (size--) + { + bytemask = (lit >> (offset * 8)) & 0xff; + + if (AOP_TYPE (right) == AOP_LIT && bytemask == 0x00) + { + rmwWithAop ("tst", AOP (left), offset); + if (size) + { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (NULL); + emitBranch ("bne", tlbl); + } + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("ora", AOP (right), offset); + hc08_freeReg (hc08_reg_a); + if (size) + { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (NULL); + emitBranch ("bne", tlbl); + } + } + offset++; + } + if (tlbl) + emitLabel (tlbl); + + pullOrFreeReg (hc08_reg_a, needpulla); + + if (ifx) + genIfxJump (ifx, "a"); + + goto release; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && + (AOP_TYPE (right) == AOP_LIT) && isLiteralBit (lit) && (AOP_TYPE (left) == AOP_DIR)) + { + int bitpos = isLiteralBit (lit) - 1; + emitcode ("bset", "#%d,%s", bitpos & 7, aopAdrStr (AOP (left), bitpos >> 3, FALSE)); + regalloc_dry_run_cost += 2; + goto release; + } + + needpulla = pushRegIfSurv (hc08_reg_a); + + offset = 0; + if (size >= 2 && IS_AOP_AX (AOP (left))) + { + pushReg (hc08_reg_a, TRUE); + earlystore = TRUE; + } + while (size--) + { + bytemask = (lit >> (offset * 8)) & 0xff; + + if (earlystore && offset == 1) + pullReg (hc08_reg_a); + if (AOP_TYPE (right) == AOP_LIT && bytemask == 0xff) + { + if (isOperandVolatile (left, FALSE)) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + hc08_freeReg (hc08_reg_a); + } + transferAopAop (AOP (right), offset, AOP (result), offset); + } + else if (AOP_TYPE (right) == AOP_LIT && bytemask == 0) + { + transferAopAop (AOP (left), offset, AOP (result), offset); + } + else + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("ora", AOP (right), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + hc08_freeReg (hc08_reg_a); + } + if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + needpulla = TRUE; + } + offset++; + } + + pullOrFreeReg (hc08_reg_a, needpulla); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for Exclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + bool needpulla = FALSE; + bool earlystore = FALSE; + + D (emitcode ("; genXor", "")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + DD (emitcode ("", "; Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right))); + DD (emitcode ("", "; Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right))); +#endif + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if (AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if left is accumulator & right is not then exchange them */ + if (AOP_TYPE (right) == AOP_REG && !IS_AOP_WITH_A (AOP (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + needpulla = pushRegIfSurv (hc08_reg_a); + + if (AOP_TYPE (result) == AOP_CRY) + { + symbol *tlbl; + + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + size = (AOP_SIZE (left) >= AOP_SIZE (right)) ? AOP_SIZE (left) : AOP_SIZE (right); + offset = 0; + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + if (AOP_TYPE (right) == AOP_LIT && ((ullFromVal (AOP (right)->aopu.aop_lit) >> (offset * 8)) & 0xff) == 0) + { + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + } + else + accopWithAop ("eor", AOP (right), offset); + + hc08_freeReg (hc08_reg_a); + if (size) + emitBranch ("bne", tlbl); + else + { + /* + * I think this is all broken here, (see simulation mismatch in bug1875933.c) + * multiple calls to emitLabel() ?! + * and we can't genIfxJump, if there is none + */ + if (!regalloc_dry_run) + emitLabel (tlbl); + pullOrFreeReg (hc08_reg_a, needpulla); + if (ifx) + genIfxJump (ifx, "a"); + } + offset++; + } + goto release; + } + + size = AOP_SIZE (result); + offset = 0; + if (size >= 2 && IS_AOP_AX (AOP (left))) + { + pushReg (hc08_reg_a, TRUE); + earlystore = TRUE; + } + while (size--) + { + if (earlystore && offset == 1) + pullReg (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), offset); + accopWithAop ("eor", AOP (right), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + if (AOP_TYPE (result) == AOP_REG && size && AOP (result)->aopu.aop_reg[offset]->rIdx == A_IDX) + { + pushReg (hc08_reg_a, TRUE); + needpulla = TRUE; + } + hc08_freeReg (hc08_reg_a); + offset++; + } + + pullOrFreeReg (hc08_reg_a, needpulla); + +release: + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +static const char * +expand_symbols (iCode * ic, const char *inlin) +{ + const char *begin = NULL, *p = inlin; + bool inIdent = FALSE; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + + while (*p) + { + if (inIdent) + { + if ('_' == *p || isalnum (*p)) + /* in the middle of identifier */ + ++p; + else + { + /* end of identifier */ + symbol *sym, *tempsym; + char *symname = Safe_strndup (p + 1, p - begin - 1); + + inIdent = 0; + + tempsym = newSymbol (symname, ic->level); + tempsym->block = ic->block; + sym = (symbol *) findSymWithLevel (SymbolTab, tempsym); + if (!sym) + { + dbuf_append (&dbuf, begin, p - begin); + } + else + { + asmop *aop = aopForSym (ic, sym, FALSE); + const char *l = aopAdrStr (aop, aop->size - 1, TRUE); + + if ('#' == *l) + l++; + sym->isref = 1; + if (sym->level && !sym->allocreq && !sym->ismyparm) + { + werror (E_ID_UNDEF, sym->name); + werror (W_CONTINUE, + " Add 'volatile' to the variable declaration so that it\n" + " can be referenced within inline assembly"); + } + dbuf_append_str (&dbuf, l); + } + Safe_free (symname); + begin = p++; + } + } + else if ('_' == *p) + { + /* begin of identifier */ + inIdent = TRUE; + if (begin) + dbuf_append (&dbuf, begin, p - begin); + begin = p++; + } + else + { + if (!begin) + begin = p; + p++; + } + } + + if (begin) + dbuf_append (&dbuf, begin, p - begin); + + return dbuf_detach_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* genInline - write the inline code out */ +/*-----------------------------------------------------------------*/ +static void +hc08_genInline (iCode * ic) +{ + char *buf, *bp, *begin; + const char *expanded; + bool inComment = FALSE; + + D (emitcode (";", "genInline")); + + genLine.lineElement.isInline += (!options.asmpeep); + + buf = bp = begin = Safe_strdup (IC_INLINE (ic)); + + /* emit each line as a code */ + while (*bp) + { + switch (*bp) + { + case ';': + inComment = TRUE; + ++bp; + break; + + case '\x87': + case '\n': + inComment = FALSE; + *bp++ = '\0'; + expanded = expand_symbols (ic, begin); + emitcode (expanded, NULL); + dbuf_free (expanded); + begin = bp; + break; + + default: + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) + { + ++bp; + *bp = '\0'; + ++bp; + emitcode (begin, NULL); + begin = bp; + } + else + ++bp; + break; + } + } + if (begin != bp) + { + expanded = expand_symbols (ic, begin); + emitcode (expanded, NULL); + dbuf_free (expanded); + } + + Safe_free (buf); + + /* consumed; we can free it here */ + dbuf_free (IC_INLINE (ic)); + + genLine.lineElement.isInline -= (!options.asmpeep); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset = 0; + bool needpula = FALSE; + bool resultInA = FALSE; + char *shift; + + D (emitcode ("; genRRC", "")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + if ((AOP_TYPE (result) == AOP_REG) && (AOP (result)->aopu.aop_reg[0]->rIdx == A_IDX)) + resultInA = TRUE; + + size = AOP_SIZE (result); + offset = size - 1; + + shift = "lsr"; + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + while (size--) + { + rmwWithAop (shift, AOP (result), offset--); + shift = "ror"; + } + } + else + { + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + rmwWithReg (shift, hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), offset--); + hc08_freeReg (hc08_reg_a); + shift = "ror"; + } + } + + if ((!hc08_reg_a->isFree) || resultInA) + { + pushReg (hc08_reg_a, TRUE); + needpula = TRUE; + } + + /* now we need to put the carry into the + highest order byte of the result */ + offset = AOP_SIZE (result) - 1; + emitcode ("clra", ""); + emitcode ("rora", ""); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + if (resultInA) + { + emitcode ("ora", "1,s"); + pullNull (1); + regalloc_dry_run_cost += 3; + hc08_dirtyReg (hc08_reg_a, FALSE); + needpula = FALSE; + } + else + accopWithAop ("ora", AOP (result), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + + pullOrFreeReg (hc08_reg_a, needpula); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset = 0; + char *shift; + bool resultInA = FALSE; + bool needpula = FALSE; + + D (emitcode ("; genRLC", "")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + if ((AOP_TYPE (result) == AOP_REG) && (AOP (result)->aopu.aop_reg[0]->rIdx == A_IDX)) + resultInA = TRUE; + + size = AOP_SIZE (result); + offset = 0; + + shift = "lsl"; + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + while (size--) + { + rmwWithAop (shift, AOP (result), offset++); + shift = "rol"; + } + } + else + { + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (left), offset); + rmwWithReg (shift, hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), offset++); + hc08_freeReg (hc08_reg_a); + shift = "rol"; + } + } + + if ((!hc08_reg_a->isFree) || resultInA) + { + pushReg (hc08_reg_a, TRUE); + needpula = TRUE; + } + + /* now we need to put the carry into the + lowest order byte of the result */ + offset = 0; + emitcode ("clra", ""); + emitcode ("rola", ""); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + if (resultInA) + { + emitcode ("ora", "1,s"); + pullNull (1); + regalloc_dry_run_cost += 3; + hc08_dirtyReg (hc08_reg_a, FALSE); + needpula = FALSE; + } + else + accopWithAop ("ora", AOP (result), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + + pullOrFreeReg (hc08_reg_a, needpula); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetAbit - generates code get a single bit */ +/*-----------------------------------------------------------------*/ +static void +genGetAbit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + bool needpulla; + + D (emitcode ("; genGetAbit", "")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + shCount = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + needpulla = pushRegIfSurv (hc08_reg_a); + + /* get the needed byte into a */ + loadRegFromAop (hc08_reg_a, AOP (left), shCount / 8); + shCount %= 8; + if (AOP_TYPE (result) == AOP_CRY) + { + emitcode ("and", "#0x%02x", 1 << shCount); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + } + else + { + switch (shCount) + { + case 3: + emitcode ("lsra", ""); + regalloc_dry_run_cost++; + //fallthrough + case 2: + emitcode ("lsra", ""); + regalloc_dry_run_cost++; + //fallthrough + case 1: + emitcode ("lsra", ""); + regalloc_dry_run_cost++; + //fallthrough + case 0: + emitcode ("and", "#0x01"); + regalloc_dry_run_cost += 2; + break; + case 4: + emitcode ("nsa", ""); + emitcode ("and", "#0x01"); + regalloc_dry_run_cost += 3; + break; + case 5: + emitcode ("rola", ""); + regalloc_dry_run_cost++; + //fallthrough + case 6: + emitcode ("rola", ""); + regalloc_dry_run_cost++; + //fallthrough + case 7: + emitcode ("rola", ""); + emitcode ("clra", ""); + emitcode ("rola", ""); + regalloc_dry_run_cost += 3; + break; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToFullAop (hc08_reg_a, AOP (result), FALSE); + } + pullOrFreeReg (hc08_reg_a, needpulla); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetByte - generates code get a single byte */ +/*-----------------------------------------------------------------*/ +static void +genGetByte (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetByte")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + transferAopAop (AOP (left), offset, AOP (result), 0); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetWord - generates code get two bytes */ +/*-----------------------------------------------------------------*/ +static void +genGetWord (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetWord")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + transferAopAop (AOP (left), offset + 1, AOP (result), 1); + transferAopAop (AOP (left), offset, AOP (result), 0); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSwap - generates code to swap nibbles or bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwap (iCode * ic) +{ + operand *left, *result; + bool needpulla; + + D (emitcode ("; genSwap", "")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + switch (AOP_SIZE (left)) + { + case 1: /* swap nibbles in byte */ + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), 0); + emitcode ("nsa", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, AOP (result), 0); + pullOrFreeReg (hc08_reg_a, needpulla); + break; + case 2: /* swap bytes in a word */ + if (IS_AOP_XA (AOP (left)) && IS_AOP_AX (AOP (result)) || + IS_AOP_AX (AOP (left)) && IS_AOP_XA (AOP (result))) + break; + if (AOP_TYPE (result) == AOP_REG && AOP_TYPE (left) == AOP_REG) + { + if (AOP (result)->aopu.aop_reg[1] != AOP (left)->aopu.aop_reg[0]) + pushReg (AOP (left)->aopu.aop_reg[0], TRUE); + storeRegToAop (AOP (left)->aopu.aop_reg[1], AOP (result), 0); + if (AOP (result)->aopu.aop_reg[1] != AOP (left)->aopu.aop_reg[0]) + pullReg (AOP (result)->aopu.aop_reg[1]); + } + else if (operandsEqu (left, result) || sameRegs (AOP (left), AOP (result))) + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), 0); + hc08_useReg (hc08_reg_a); + transferAopAop (AOP (left), 1, AOP (result), 0); + storeRegToAop (hc08_reg_a, AOP (result), 1); + pullOrFreeReg (hc08_reg_a, needpulla); + } + else + { + transferAopAop (AOP (left), 0, AOP (result), 1); + transferAopAop (AOP (left), 1, AOP (result), 0); + } + break; + default: + wassertl (FALSE, "unsupported SWAP operand size"); + } + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("rola", ""); /* 1 cycle */ + break; + case 2: + emitcode ("rola", ""); /* 1 cycle */ + emitcode ("rola", ""); /* 1 cycle */ + break; + case 3: + emitcode ("nsa", ""); + emitcode ("rora", ""); + break; + case 4: + emitcode ("nsa", ""); /* 3 cycles */ + break; + case 5: + emitcode ("nsa", ""); /* 3 cycles */ + emitcode ("rola", ""); /* 1 cycle */ + break; + case 6: + emitcode ("nsa", ""); /* 3 cycles */ + emitcode ("rola", ""); /* 1 cycle */ + emitcode ("rola", ""); /* 1 cycle */ + break; + case 7: + emitcode ("nsa", ""); /* 3 cycles */ + emitcode ("rola", ""); /* 1 cycle */ + emitcode ("rola", ""); /* 1 cycle */ + emitcode ("rola", ""); /* 1 cycle */ + break; + } +} +#endif + + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount) +{ + int i; + + shCount &= 0x0007; // shCount : 0..7 + + /* Shift counts of 4 and 5 are currently optimized for code size. */ + /* Falling through to the unrolled loop would be optimal for code speed. */ + /* For shift counts of 6 and 7, the unrolled loop is never optimal. */ + switch (shCount) + { + case 4: + if (optimize.codeSpeed) + break; + accopWithMisc ("nsa", ""); + accopWithMisc ("and", "#0xf0"); + /* total: 5 cycles, 3 bytes */ + return; + case 5: + if (optimize.codeSpeed) + break; + accopWithMisc ("nsa", ""); + accopWithMisc ("and", "#0xf0"); + accopWithMisc ("lsla", ""); + /* total: 6 cycles, 4 bytes */ + return; + case 6: + accopWithMisc ("rora", ""); + accopWithMisc ("rora", ""); + accopWithMisc ("rora", ""); + accopWithMisc ("and", "#0xc0"); + /* total: 5 cycles, 5 bytes */ + return; + case 7: + accopWithMisc ("rora", ""); + accopWithMisc ("clra", ""); + accopWithMisc ("rora", ""); + /* total: 3 cycles, 3 bytes */ + return; + } + + /* lsla is only 1 cycle and byte, so an unrolled loop is often */ + /* the fastest (shCount<6) and shortest (shCount<4). */ + for (i = 0; i < shCount; i++) + accopWithMisc ("lsla", ""); +} + + +/*-----------------------------------------------------------------*/ +/* AccSRsh - signed right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccSRsh (int shCount) +{ + int i; + + shCount &= 0x0007; // shCount : 0..7 + + if (shCount == 7) + { + accopWithMisc ("rola", ""); + accopWithMisc ("clra", ""); + accopWithMisc ("sbc", zero); + /* total: 4 cycles, 4 bytes */ + return; + } + + for (i = 0; i < shCount; i++) + accopWithMisc ("asra", ""); +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount, bool sign) +{ + int i; + + if (sign) + { + AccSRsh (shCount); + return; + } + + shCount &= 0x0007; // shCount : 0..7 + + /* Shift counts of 4 and 5 are currently optimized for code size. */ + /* Falling through to the unrolled loop would be optimal for code speed. */ + /* For shift counts of 6 and 7, the unrolled loop is never optimal. */ + switch (shCount) + { + case 4: + if (optimize.codeSpeed) + break; + accopWithMisc ("nsa", ""); + accopWithMisc ("and", "#0x0f"); + /* total: 5 cycles, 3 bytes */ + return; + case 5: + if (optimize.codeSpeed) + break; + accopWithMisc ("nsa", ""); + accopWithMisc ("and", "#0x0f"); + accopWithMisc ("lsra", ""); + /* total: 6 cycles, 4 bytes */ + return; + case 6: + accopWithMisc ("rola", ""); + accopWithMisc ("rola", ""); + accopWithMisc ("rola", ""); + accopWithMisc ("and", "#0x03"); + /* total: 5 cycles, 5 bytes */ + return; + case 7: + accopWithMisc ("rola", ""); + accopWithMisc ("clra", ""); + accopWithMisc ("rola", ""); + /* total: 3 cycles, 3 bytes */ + return; + } + + /* lsra is only 1 cycle and byte, so an unrolled loop is often */ + /* the fastest (shCount<6) and shortest (shCount<4). */ + for (i = 0; i < shCount; i++) + accopWithMisc ("lsra", ""); +} + + +/*-----------------------------------------------------------------*/ +/* XAccLsh - left shift register pair XA by known count */ +/*-----------------------------------------------------------------*/ +static void +XAccLsh (int shCount) +{ + int i; + + shCount &= 0x000f; // shCount : 0..15 + + if (shCount >= 8) + { + AccLsh (shCount - 8); + transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); + loadRegFromConst (hc08_reg_a, 0); + return; + } + + /* if we can beat 2n cycles or bytes for some special case, do it here */ + switch (shCount) + { + case 7: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** lsrx 1 1 0abc defg ijkl mnop h + ** rora 1 1 0abc defg hijk lmno p + ** tax 1 1 hijk lmno hijk lmno p + ** clra 1 1 hijk lmno 0000 0000 p + ** rora 1 1 hijk lmno p000 0000 0 + ** total: 5 cycles, 5 bytes (beats 14 cycles, 14 bytes) + */ + rmwWithReg ("lsr", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); + loadRegFromConst (hc08_reg_a, 0); + rmwWithReg ("ror", hc08_reg_a); + return; + + default: + ; + } + + /* lsla/rolx is only 2 cycles and bytes, so an unrolled loop is often */ + /* the fastest and shortest. */ + for (i = 0; i < shCount; i++) + { + rmwWithReg ("lsl", hc08_reg_a); + rmwWithReg ("rol", hc08_reg_x); + } +} + +/*-----------------------------------------------------------------*/ +/* XAccSRsh - signed right shift register pair XA by known count */ +/*-----------------------------------------------------------------*/ +static void +XAccSRsh (int shCount) +{ + int i; + + shCount &= 0x000f; // shCount : 0..7 + + /* if we can beat 2n cycles or bytes for some special case, do it here */ + switch (shCount) + { + case 15: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** lslx 1 1 bcde fgh0 ijkl mnop a + ** clra 1 1 bcde fgh0 0000 0000 a + ** rola 1 1 bcde fgh0 0000 000a 0 + ** nega 1 1 bcde fgh0 aaaa aaaa a + ** tax 1 1 aaaa aaaa aaaa aaaa a + ** total: 5 cycles, 5 bytes + */ + rmwWithReg ("lsl", hc08_reg_x); + loadRegFromConst (hc08_reg_a, 0); + rmwWithReg ("rol", hc08_reg_a); + rmwWithReg ("neg", hc08_reg_a); + transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); + return; + + case 14: + case 13: + case 12: + case 11: + case 10: + case 9: + case 8: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** txa 1 1 abcd efgh abcd efgh ? + ** (AccSRsh) <8 <8 abcd efgh LSBresult ? + ** lsla 1 1 abcd efgh ???? ???? a + ** clrx 1 1 0000 0000 ???? ???? a + ** rolx 1 1 0000 000a ???? ???? 0 + ** negx 1 1 aaaa aaaa ???? ???? a + ** rora 1 1 aaaa aaaa LSBresult 0 + ** total: n-2 cycles, n-2 bytes (beats 2n cycles, 2n bytes (for n>=8)) + */ + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + AccSRsh (shCount - 8); + rmwWithReg ("lsl", hc08_reg_a); + loadRegFromConst (hc08_reg_x, 0); + rmwWithReg ("rol", hc08_reg_x); + rmwWithReg ("neg", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + return; + + default: + ; + } + + /* asrx/rora is only 2 cycles and bytes, so an unrolled loop is often */ + /* the fastest and shortest. */ + for (i = 0; i < shCount; i++) + { + rmwWithReg ("asr", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + } +} + +/*-----------------------------------------------------------------*/ +/* XAccRsh - right shift register pair XA by known count */ +/*-----------------------------------------------------------------*/ +static void +XAccRsh (int shCount, bool sign) +{ + int i; + + if (sign) + { + XAccSRsh (shCount); + return; + } + + shCount &= 0x000f; // shCount : 0..f + + /* if we can beat 2n cycles or bytes for some special case, do it here */ + switch (shCount) + { + case 15: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** clra 1 1 abcd efgh 0000 0000 a + ** lslx 1 1 bcde fgh0 0000 0000 a + ** rola 1 1 bcde fgh0 0000 000a 0 + ** clrx 1 1 0000 0000 0000 000a 0 + ** total: 4 cycles, 4 bytes + */ + loadRegFromConst (hc08_reg_x, 0); + rmwWithReg ("lsl", hc08_reg_x); + rmwWithReg ("rol", hc08_reg_a); + loadRegFromConst (hc08_reg_a, 0); + return; + + case 14: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** clra 1 1 abcd efgh 0000 0000 a + ** lslx 1 1 bcde fgh0 0000 0000 a + ** rola 1 1 bcde fgh0 0000 000a 0 + ** lslx 1 1 cdef gh00 0000 000a b + ** rola 1 1 cdef gh00 0000 00ab 0 + ** clrx 1 1 0000 0000 0000 00ab 0 + ** total: 6 cycles, 6 bytes + */ + loadRegFromConst (hc08_reg_x, 0); + rmwWithReg ("lsl", hc08_reg_x); + rmwWithReg ("rol", hc08_reg_a); + rmwWithReg ("lsl", hc08_reg_x); + rmwWithReg ("rol", hc08_reg_a); + loadRegFromConst (hc08_reg_a, 0); + return; + + case 13: + case 12: + case 11: + case 10: + case 9: + case 8: + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + AccRsh (shCount - 8, FALSE); + loadRegFromConst (hc08_reg_x, 0); + return; + + case 7: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** lsla 1 1 abcd efgh jklm nop0 i + ** txa 1 1 abcd efgh abcd efgh i + ** rola 1 1 abcd efgh bcde fghi a + ** clrx 1 1 0000 0000 bcde fghi a + ** rolx 1 1 0000 000a bcde fghi 0 + ** total: 5 cycles, 5 bytes (beats 14 cycles, 14 bytes) + */ + rmwWithReg ("lsl", hc08_reg_a); + transferRegReg (hc08_reg_x, hc08_reg_a, FALSE); + rmwWithReg ("rol", hc08_reg_a); + loadRegFromConst (hc08_reg_x, 0); + rmwWithReg ("rol", hc08_reg_x); + return; + case 6: + /* bytes cycles reg x reg a carry + ** abcd efgh ijkl mnop ? + ** lsla 1 1 abcd efgh jklm nop0 i + ** rolx 1 1 bcde fghi jklm nop0 a + ** rola 1 1 bcde fghi klmn op0a j + ** rolx 1 1 cdef ghij klmn op0a b + ** rola 1 1 cdef ghij lmno p0ab k + ** and #3 2 2 cdef ghij 0000 00ab k + ** psha 1 2 cdef ghij 0000 00ab k + ** txa 1 1 cdef ghij cdef ghij k + ** pula 1 2 0000 00ab cdef ghij k + ** total: 12 cycles, 10 bytes (beats 12 bytes) + */ + default: + ; + } + + /* lsrx/rora is only 2 cycles and bytes, so an unrolled loop is often */ + /* the fastest and shortest. */ + for (i = 0; i < shCount; i++) + { + rmwWithReg ("lsr", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + } + +} + + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + bool needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), offl); + /* shift left accumulator */ + AccLsh (shCount); + storeRegToAop (hc08_reg_a, AOP (result), offr); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) +{ + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + transferAopAop (AOP (left), offl, AOP (result), offr); + } +} + + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + int i; + bool needpula = FALSE; + bool needpulx = FALSE; + + if (!IS_AOP_XA (AOP (left)) && !IS_AOP_A (AOP (left))) + needpula = pushRegIfUsed (hc08_reg_a); + else + needpula = FALSE; + if (!IS_AOP_XA (AOP (left))) + needpulx = pushRegIfUsed (hc08_reg_x); + else + needpulx = FALSE; + + loadRegFromAop (hc08_reg_xa, AOP (left), offl); + + switch (shCount) + { + case 7: + rmwWithReg ("lsr", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + transferRegReg (hc08_reg_a, hc08_reg_x, FALSE); + rmwWithReg ("clr", hc08_reg_a); + rmwWithReg ("ror", hc08_reg_a); + break; + default: + for (i = 0; i < shCount; i++) + { + rmwWithReg ("lsl", hc08_reg_a); + rmwWithReg ("rol", hc08_reg_x); + } + } + storeRegToAop (hc08_reg_xa, AOP (result), offr); + + pullOrFreeReg (hc08_reg_x, needpulx); + pullOrFreeReg (hc08_reg_a, needpula); + +} + + + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result */ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + loadRegFromAop (hc08_reg_a, AOP (left), offl); + /* shift left accumulator */ + AccRsh (shCount, FALSE); + /* or with result */ + accopWithAop ("ora", AOP (result), offr); + /* back to result */ + storeRegToAop (hc08_reg_a, AOP (result), offr); + hc08_freeReg (hc08_reg_a); +} + +/*-----------------------------------------------------------------*/ +/* genlshOne - left shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genlshOne (operand * result, operand * left, int shCount) +{ + D (emitcode ("; genlshOne", "")); + + shiftL1Left2Result (left, LSB, result, LSB, shCount); +} + +/*-----------------------------------------------------------------*/ +/* genlshTwo - left shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshTwo (operand * result, operand * left, int shCount) +{ + int size; + bool needpulla, needpullx; + + D (emitcode ("; genlshTwo", "")); + + + size = getDataSize (result); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + + needpulla = pushRegIfSurv (hc08_reg_a); + if (size > 1) + { + loadRegFromAop (hc08_reg_a, AOP (left), 0); + AccLsh (shCount); + storeRegToAop (hc08_reg_a, AOP (result), 1); + } + storeConstToAop (0, AOP (result), LSB); + pullOrFreeReg (hc08_reg_a, needpulla); + } + + /* 1 <= shCount <= 7 */ + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + loadRegFromAop (hc08_reg_xa, AOP (left), 0); + XAccLsh (shCount); + storeRegToFullAop (hc08_reg_xa, AOP (result), 0); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offr = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ +// char *l; +// int size = AOP_SIZE (result); + + bool needpula = FALSE; + bool needpulx = FALSE; + + needpula = pushRegIfUsed (hc08_reg_a); + needpulx = pushRegIfUsed (hc08_reg_x); + + loadRegFromAop (hc08_reg_xa, AOP (left), LSB); + rmwWithReg ("lsl", hc08_reg_a); + rmwWithReg ("rol", hc08_reg_x); + + if (offr == LSB) + { + storeRegToAop (hc08_reg_xa, AOP (result), offr); + loadRegFromAop (hc08_reg_xa, AOP (left), MSB24); + rmwWithReg ("rol", hc08_reg_a); + rmwWithReg ("rol", hc08_reg_x); + storeRegToAop (hc08_reg_xa, AOP (result), offr + 2); + } + else if (offr == MSB16) + { + storeRegToAop (hc08_reg_a, AOP (result), offr); + loadRegFromAop (hc08_reg_a, AOP (left), MSB24); + storeRegToAop (hc08_reg_x, AOP (result), offr + 1); + rmwWithReg ("rol", hc08_reg_a); + storeRegToAop (hc08_reg_a, AOP (result), offr + 2); + storeConstToAop (0, AOP (result), 0); + } + + pullOrFreeReg (hc08_reg_x, needpulx); + pullOrFreeReg (hc08_reg_a, needpula); +} + +/*-----------------------------------------------------------------*/ +/* genlshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFour (operand * result, operand * left, int shCount) +{ + int size; + + D (emitcode ("; genlshFour", "")); + + size = AOP_SIZE (result); + + /* TODO: deal with the &result == &left case */ + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + /* lowest order of left goes to the highest + order of the destination */ + shiftL1Left2Result (left, LSB, result, MSB32, shCount); + else + movLeft2Result (left, LSB, result, MSB32, 0); + storeConstToAop (0, AOP (result), LSB); + storeConstToAop (0, AOP (result), MSB16); + storeConstToAop (0, AOP (result), MSB24); + return; + } + + /* more than two bytes */ + else if (shCount >= 16) + { + /* lower order two bytes goes to higher order two bytes */ + shCount -= 16; + /* if some more remaining */ + if (shCount) + shiftL2Left2Result (left, LSB, result, MSB24, shCount); + else + { + movLeft2Result (left, MSB16, result, MSB32, 0); + movLeft2Result (left, LSB, result, MSB24, 0); + } + storeConstToAop (0, AOP (result), LSB); + storeConstToAop (0, AOP (result), MSB16); + return; + } + + /* if more than 1 byte */ + else if (shCount >= 8) + { + /* lower order three bytes goes to higher order three bytes */ + shCount -= 8; + if (size == 2) + { + if (shCount) + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + else + movLeft2Result (left, LSB, result, MSB16, 0); + } + else + { + /* size = 4 */ + if (shCount == 0) + { + movLeft2Result (left, MSB24, result, MSB32, 0); + movLeft2Result (left, MSB16, result, MSB24, 0); + movLeft2Result (left, LSB, result, MSB16, 0); + storeConstToAop (0, AOP (result), LSB); + } + else if (shCount == 1) + shiftLLong (left, result, MSB16); + else + { + shiftL2Left2Result (left, MSB16, result, MSB24, shCount); + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + shiftRLeftOrResult (left, LSB, result, MSB24, 8 - shCount); + storeConstToAop (0, AOP (result), LSB); + } + } + } + + /* 1 <= shCount <= 7 */ + else if (shCount <= 2) + { + shiftLLong (left, result, LSB); + if (shCount == 2) + shiftLLong (result, result, LSB); + } + /* 3 <= shCount <= 7, optimize */ + else + { + shiftL2Left2Result (left, MSB24, result, MSB24, shCount); + shiftRLeftOrResult (left, MSB16, result, MSB24, 8 - shCount); + shiftL2Left2Result (left, LSB, result, LSB, shCount); + } +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + D (emitcode ("; genLeftShiftLiteral", "")); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +// size = getSize (operandType (result)); + size = AOP_SIZE (result); + +#if VIEW_SIZE + DD (emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left))); +#endif + + if (shCount == 0) + { + genCopy (result, left); + } + else if (shCount >= (size * 8)) + { + while (size--) + storeConstToAop (0, AOP (result), size); + } + else + { + switch (size) + { + case 1: + genlshOne (result, left, shCount); + break; + + case 2: + genlshTwo (result, left, shCount); + break; + + case 4: + genlshFour (result, left, shCount); + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "*** ack! mystery literal shift!\n"); + break; + } + } + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl, *tlbl1; + char *shift; + asmop *aopResult; + bool needpullcountreg; + reg_info *countreg = NULL; + + D (emitcode ("; genLeftShift", "")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genLeftShiftLiteral (left, right, result, ic); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in X : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + aopOp (result, ic, FALSE); + aopOp (left, ic, FALSE); + aopResult = AOP (result); + + if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, FALSE)) + aopResult = forceStackedAop (AOP (result), sameRegs (AOP (left), AOP (result))); + + /* now move the left to the result if they are not the + same */ + if (IS_AOP_HX (AOP (result))) + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + else if (IS_AOP_AX (AOP (result)) && IS_AOP_XA (AOP (left)) || IS_AOP_XA (AOP (result)) && IS_AOP_AX (AOP (left))) + { + pushReg (hc08_reg_x, TRUE); + emitcode("tax", ""); + regalloc_dry_run_cost++; + pullReg (hc08_reg_a); + } + else if (!sameRegs (AOP (left), aopResult)) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + transferAopAop (AOP (left), offset, aopResult, offset); + offset++; + } + } + freeAsmop (left, NULL, ic, TRUE); + AOP (result) = aopResult; + + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (hc08_reg_x->isDead && !IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) + countreg = hc08_reg_x; + else if (hc08_reg_a->isDead && !IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) + countreg = hc08_reg_a; + else if (!IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) + countreg = hc08_reg_x; + else if(!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) + countreg = hc08_reg_a; + needpullcountreg = (countreg && pushRegIfSurv (countreg)); + if(countreg) + { + countreg->isFree = FALSE; + loadRegFromAop (countreg, AOP (right), 0); + } + else + { + pushReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + loadRegFromAop (hc08_reg_a, AOP (right), 0); + emitcode ("sta", "2, s"); + regalloc_dry_run_cost += 3; + pullReg (hc08_reg_a); + } + emitcode (countreg == hc08_reg_a ? "tsta" : (countreg ? "tstx" : "tst 1, s"), ""); + regalloc_dry_run_cost += (countreg ? 1 : 3); + + emitBranch ("beq", tlbl1); + if (!regalloc_dry_run) + emitLabel (tlbl); + + shift = "lsl"; + for (offset = 0; offset < size; offset++) + { + rmwWithAop (shift, AOP (result), offset); + shift = "rol"; + } + if (!regalloc_dry_run) + emitcode (countreg == hc08_reg_a ? "dbnza" : (countreg ? "dbnzx" : "dbnz 1, s"), "%05d$", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += (countreg ? 2 : 4); + + if (!regalloc_dry_run) + emitLabel (tlbl1); + + if (!countreg) + pullNull (1); + else + pullOrFreeReg (countreg, needpullcountreg); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + bool needpulla; + D (emitcode ("; genrshOne", "")); + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), 0); + AccRsh (shCount, sign); + storeRegToFullAop (hc08_reg_a, AOP (result), sign); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + bool needpulla, needpullx; + D (emitcode ("; genrshTwo", "")); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + if (shCount != 8 || sign) + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), 1); + AccRsh (shCount - 8, sign); + storeRegToFullAop (hc08_reg_a, AOP (result), sign); + pullOrFreeReg (hc08_reg_a, needpulla); + } + else + { + transferAopAop (AOP (left), 1, AOP (result), 0); + storeConstToAop (0, AOP (result), 1); + } + } + + /* 1 <= shCount <= 7 */ + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + loadRegFromAop (hc08_reg_xa, AOP (left), 0); + XAccRsh (shCount, sign); + storeRegToAop (hc08_reg_xa, AOP (result), 0); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + bool needpula = pushRegIfSurv (hc08_reg_a); + bool needpulx = pushRegIfSurv (hc08_reg_x); + + if (offl == LSB) + { + loadRegFromAop (hc08_reg_xa, AOP (left), MSB24); + if (sign) + rmwWithReg ("asr", hc08_reg_x); + else + rmwWithReg ("lsr", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + storeRegToAop (hc08_reg_xa, AOP (result), MSB24); + loadRegFromAop (hc08_reg_xa, AOP (left), LSB); + } + else if (offl == MSB16) + { + loadRegFromAop (hc08_reg_a, AOP (left), MSB32); + if (sign) + rmwWithReg ("asr", hc08_reg_a); + else + rmwWithReg ("lsr", hc08_reg_a); + loadRegFromAop (hc08_reg_x, AOP (left), MSB24); + storeRegToAop (hc08_reg_a, AOP (result), MSB24); + loadRegFromAop (hc08_reg_a, AOP (left), MSB16); + } + + rmwWithReg ("ror", hc08_reg_x); + rmwWithReg ("ror", hc08_reg_a); + storeRegToAop (hc08_reg_xa, AOP (result), LSB); + + if (offl == MSB16) + { + if (sign) + { + loadRegFromAop (hc08_reg_a, AOP (left), MSB24); + storeRegSignToUpperAop (hc08_reg_a, AOP (result), MSB32, sign); + } + else + { + storeConstToAop (0, AOP (result), MSB32); + } + } + + pullOrFreeReg (hc08_reg_x, needpulx); + pullOrFreeReg (hc08_reg_a, needpula); +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand * result, operand * left, int shCount, int sign) +{ + bool needpulla = FALSE; + bool needpullx = FALSE; + + /* TODO: handle cases where left == result */ + + D (emitcode ("; genrshFour", "")); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (left), 3); + AccRsh (shCount - 24, sign); + storeRegToFullAop (hc08_reg_a, AOP (result), sign); + } + else if (shCount >= 16) + { + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + loadRegFromAop (hc08_reg_xa, AOP (left), 2); + XAccRsh (shCount - 16, sign); + storeRegToFullAop (hc08_reg_xa, AOP (result), sign); + } + else if (shCount >= 8) + { + if (shCount == 1) + { + shiftRLong (left, MSB16, result, sign); + return; + } + else if (shCount == 8) + { + needpulla = pushRegIfSurv (hc08_reg_a); + transferAopAop (AOP (left), 1, AOP (result), 0); + transferAopAop (AOP (left), 2, AOP (result), 1); + loadRegFromAop (hc08_reg_a, AOP (left), 3); + storeRegToAop (hc08_reg_a, AOP (result), 2); + storeRegSignToUpperAop (hc08_reg_a, AOP (result), 3, sign); + } + else if (shCount == 9) + { + shiftRLong (left, MSB16, result, sign); + return; + } + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + loadRegFromAop (hc08_reg_xa, AOP (left), 1); + XAccRsh (shCount - 8, FALSE); + storeRegToAop (hc08_reg_xa, AOP (result), 0); + loadRegFromAop (hc08_reg_x, AOP (left), 3); + loadRegFromConst (hc08_reg_a, 0); + XAccRsh (shCount - 8, sign); + accopWithAop ("ora", AOP (result), 1); + storeRegToAop (hc08_reg_xa, AOP (result), 1); + storeRegSignToUpperAop (hc08_reg_x, AOP (result), 3, sign); + } + } + else + { + /* 1 <= shCount <= 7 */ + if (shCount == 1) + { + shiftRLong (left, LSB, result, sign); + return; + } + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + needpullx = pushRegIfSurv (hc08_reg_x); + loadRegFromAop (hc08_reg_xa, AOP (left), 0); + XAccRsh (shCount, FALSE); + storeRegToAop (hc08_reg_xa, AOP (result), 0); + loadRegFromAop (hc08_reg_a, AOP (left), 2); + AccLsh (8 - shCount); + accopWithAop ("ora", AOP (result), 1); + storeRegToAop (hc08_reg_a, AOP (result), 1); + loadRegFromAop (hc08_reg_xa, AOP (left), 2); + XAccRsh (shCount, sign); + storeRegToAop (hc08_reg_xa, AOP (result), 2); + } + } + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + D (emitcode ("; genRightShiftLiteral", "")); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + DD (emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left))); +#endif + + size = getDataSize (left); + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + if (shCount == 0) + { + genCopy (result, left); + } + else if (shCount >= (size * 8)) + { + bool needpulla = pushRegIfSurv (hc08_reg_a); + if (sign) + { + + /* get sign in acc.7 */ + loadRegFromAop (hc08_reg_a, AOP (left), size - 1); + } + addSign (result, LSB, sign); + pullOrFreeReg (hc08_reg_a, needpulla); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + default: + wassertl (0, "Invalid operand size in right shift."); + break; + } + } + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *retype; + int size, offset; + symbol *tlbl, *tlbl1; + char *shift; + bool sign; + asmop *aopResult; + bool needpullcountreg; + reg_info *countreg = NULL; + + D (emitcode ("; genRightShift", "")); + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + retype = getSpec (operandType (IC_RESULT (ic))); + sign = !SPEC_USIGN (retype); + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, sign); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in X : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + aopOp (result, ic, FALSE); + aopOp (left, ic, FALSE); + aopResult = AOP (result); + + if (sameRegs (AOP (right), AOP (result)) || regsInCommon (right, result) || IS_AOP_XA (AOP (result)) || isOperandVolatile (result, FALSE)) + aopResult = forceStackedAop (AOP (result), sameRegs (AOP (left), AOP (result))); + + /* now move the left to the result if they are not the + same */ + if (IS_AOP_HX (AOP (result))) + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + else if (IS_AOP_AX (AOP (result)) && IS_AOP_XA (AOP (left)) || IS_AOP_XA (AOP (result)) && IS_AOP_AX (AOP (left))) + { + pushReg (hc08_reg_x, TRUE); + emitcode("tax", ""); + regalloc_dry_run_cost++; + pullReg (hc08_reg_a); + } + else if (!sameRegs (AOP (left), aopResult)) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + transferAopAop (AOP (left), offset, aopResult, offset); + offset++; + } + } + freeAsmop (left, NULL, ic, TRUE); + AOP (result) = aopResult; + + tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (hc08_reg_x->isDead && !IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) + countreg = hc08_reg_x; + else if (hc08_reg_a->isDead && !IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) + countreg = hc08_reg_a; + else if (!IS_AOP_X (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result)) && !IS_AOP_HX (AOP (result))) + countreg = hc08_reg_x; + else if(!IS_AOP_A (AOP (result)) && !IS_AOP_XA (AOP (result)) && !IS_AOP_AX (AOP (result))) + countreg = hc08_reg_a; + needpullcountreg = (countreg && pushRegIfSurv (countreg)); + if(countreg) + { + countreg->isFree = FALSE; + loadRegFromAop (countreg, AOP (right), 0); + } + else + { + pushReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + loadRegFromAop (hc08_reg_a, AOP (right), 0); + emitcode ("sta", "2, s"); + regalloc_dry_run_cost += 3; + pullReg (hc08_reg_a); + } + emitcode (countreg == hc08_reg_a ? "tsta" : (countreg ? "tstx" : "tst 1, s"), ""); + regalloc_dry_run_cost += (countreg ? 1 : 3); + + emitBranch ("beq", tlbl1); + if (!regalloc_dry_run) + emitLabel (tlbl); + + shift = sign ? "asr" : "lsr"; + for (offset = size - 1; offset >= 0; offset--) + { + rmwWithAop (shift, AOP (result), offset); + shift = "ror"; + } + + if (!regalloc_dry_run) + emitcode (countreg == hc08_reg_a ? "dbnza" : (countreg ? "dbnzx" : "dbnz 1, s"), "%05d$", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += (countreg ? 2 : 4); + + if (!regalloc_dry_run) + emitLabel (tlbl1); + + if (!countreg) + pullNull (1); + else + pullOrFreeReg (countreg, needpullcountreg); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* decodePointerOffset - decode a pointer offset operand into a */ +/* literal offset and a rematerializable offset */ +/*-----------------------------------------------------------------*/ +static void +decodePointerOffset (operand * opOffset, int * litOffset, char ** rematOffset) +{ + *litOffset = 0; + *rematOffset = NULL; + + if (!opOffset) + return; + + if (IS_OP_LITERAL (opOffset)) + { + *litOffset = (int)operandLitValue (opOffset); + } + else if (IS_ITEMP (opOffset) && OP_SYMBOL (opOffset)->remat) + { + asmop * aop = aopForRemat (OP_SYMBOL (opOffset)); + + if (aop->type == AOP_LIT) + *litOffset = (int) floatFromVal (aop->aopu.aop_lit); + else if (aop->type == AOP_IMMD) + *rematOffset = aop->aopu.aop_immd.aop_immd1; + } + else + wassertl (0, "Pointer get/set with non-constant offset"); +} + + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, operand * left, operand * right, iCode * ifx) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + bool needpulla = FALSE; + bool needpullh = FALSE; + bool needpullx = FALSE; + int litOffset = 0; + char * rematOffset = NULL; + + D (emitcode ("; genUnpackBits", "")); + + decodePointerOffset (right, &litOffset, &rematOffset); + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + needpulla = pushRegIfSurv (hc08_reg_a); + + if (!IS_AOP_HX (AOP (left))) + { + needpullx = pushRegIfSurv (hc08_reg_x); + needpullh = pushRegIfSurv (hc08_reg_h); + } + + /* if the operand is already in hx + then we do nothing else we move the value to hx */ + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + /* so hx now contains the address */ + + if (ifx && blen <= 8) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + if (blen < 8) + { + emitcode ("and", "#0x%02x", (((unsigned char) - 1) >> (8 - blen)) << bstr); + regalloc_dry_run_cost += 2; + } + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); + genIfxJump (ifx, "a"); + return; + } + wassert (!ifx); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + AccRsh (bstr, FALSE); + emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - blen)); + regalloc_dry_run_cost += 2; + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + emitcode ("bit", "#0x%02x", 1 << (blen - 1)); + if (!regalloc_dry_run) + emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); + emitcode ("ora", "#0x%02x", (unsigned char) (0xff << blen)); + regalloc_dry_run_cost += 6; + if (!regalloc_dry_run) + emitLabel (tlbl); + } + storeRegToAop (hc08_reg_a, AOP (result), offset++); + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + if (rlen > 8 && AOP_TYPE (result) == AOP_REG) + pushReg (hc08_reg_a, TRUE); + else + storeRegToAop (hc08_reg_a, AOP (result), offset); + offset++; + if (rlen > 8) + { + litOffset++; + } + } + + /* Handle the partial byte at the end */ + if (rlen) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - rlen)); + regalloc_dry_run_cost += 3; + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + emitcode ("bit", "#0x%02x", 1 << (rlen - 1)); + if (!regalloc_dry_run) + emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); + emitcode ("ora", "#0x%02x", (unsigned char) (0xff << rlen)); + regalloc_dry_run_cost += 6; + if (!regalloc_dry_run) + emitLabel (tlbl); + } + storeRegToAop (hc08_reg_a, AOP (result), offset++); + } + if (blen > 8 && AOP_TYPE (result) == AOP_REG) + { + pullReg (AOP (result)->aopu.aop_reg[0]); + } + +finish: + if (offset < rsize) + { + rsize -= offset; + if (SPEC_USIGN (etype)) + { + while (rsize--) + storeConstToAop (0, AOP (result), offset++); + } + else + { + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rola", ""); + emitcode ("clra", ""); + emitcode ("sbc", zero); + regalloc_dry_run_cost += 4; + + while (rsize--) + storeRegToAop (hc08_reg_a, AOP (result), offset++); + } + } + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_x, needpullx); + pullOrFreeReg (hc08_reg_a, needpulla); +} + + +/*-----------------------------------------------------------------*/ +/* genUnpackBitsImmed - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBitsImmed (operand * left, operand *right, operand * result, iCode * ic, iCode * ifx) +{ + int size; + int offset = 0; /* result byte offset */ + int litOffset = 0; + char * rematOffset = NULL; + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + asmop *derefaop; + bool delayed_a = FALSE; + bool assigned_a = FALSE; + bool needpulla = FALSE; + + D (emitcode ("; genUnpackBitsImmed", "")); + + decodePointerOffset (right, &litOffset, &rematOffset); + wassert (rematOffset==NULL); + + aopOp (result, ic, TRUE); + size = AOP_SIZE (result); + + derefaop = aopDerefAop (AOP (left), litOffset); + freeAsmop (left, NULL, ic, TRUE); + derefaop->size = size; + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + needpulla = pushRegIfSurv (hc08_reg_a); + + /* if the bitfield is a single bit in the direct page */ + if (blen == 1 && derefaop->type == AOP_DIR) + { + if (!ifx && bstr) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + loadRegFromConst (hc08_reg_a, 0); + if (!regalloc_dry_run) + emitcode ("brclr", "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, FALSE), labelKey2num ((tlbl->key))); + regalloc_dry_run_cost += 3; + if (SPEC_USIGN (etype)) + rmwWithReg ("inc", hc08_reg_a); + else + rmwWithReg ("dec", hc08_reg_a); + if (!regalloc_dry_run) + emitLabel (tlbl); + storeRegToAop (hc08_reg_a, AOP (result), offset); + if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) + assigned_a = TRUE; + hc08_freeReg (hc08_reg_a); + offset++; + goto finish; + } + else if (ifx) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *jlbl; + char *inst; + + if (IC_TRUE (ifx)) + { + jlbl = IC_TRUE (ifx); + inst = "brclr"; + } + else + { + jlbl = IC_FALSE (ifx); + inst = "brset"; + } + if (!regalloc_dry_run) + emitcode (inst, "#%d,%s,%05d$", bstr, aopAdrStr (derefaop, 0, FALSE), labelKey2num ((tlbl->key))); + regalloc_dry_run_cost += 3; + emitBranch ("jmp", jlbl); + if (!regalloc_dry_run) + emitLabel (tlbl); + ifx->generated = 1; + offset++; + goto finish; + } + } + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + loadRegFromAop (hc08_reg_a, derefaop, 0); + if (!ifx) + { + AccRsh (bstr, FALSE); + emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - blen)); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + emitcode ("bit", "#0x%02x", 1 << (blen - 1)); + if (!regalloc_dry_run) + emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); + emitcode ("ora", "#0x%02x", (unsigned char) (0xff << blen)); + regalloc_dry_run_cost += 6; + if (!regalloc_dry_run) + emitLabel (tlbl); + } + storeRegToAop (hc08_reg_a, AOP (result), offset); + if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) + assigned_a = TRUE; + } + else + { + emitcode ("and", "#0x%02x", (((unsigned char) - 1) >> (8 - blen)) << bstr); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + } + offset++; + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + if (assigned_a && !delayed_a) + { + pushReg (hc08_reg_a, TRUE); + delayed_a = TRUE; + } + loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); + if (!ifx) + { + storeRegToAop (hc08_reg_a, AOP (result), offset); + if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) + assigned_a = TRUE; + } + else + { + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + } + offset++; + } + + /* Handle the partial byte at the end */ + if (rlen) + { + if (assigned_a && !delayed_a) + { + pushReg (hc08_reg_a, TRUE); + delayed_a = TRUE; + } + loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); + emitcode ("and", "#0x%02x", ((unsigned char) - 1) >> (8 - rlen)); + regalloc_dry_run_cost += 2; + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + emitcode ("bit", "#0x%02x", 1 << (rlen - 1)); + if (!regalloc_dry_run) + emitcode ("beq", "%05d$", labelKey2num (tlbl->key)); + emitcode ("ora", "#0x%02x", (unsigned char) (0xff << rlen)); + regalloc_dry_run_cost += 6; + if (!regalloc_dry_run) + emitLabel (tlbl); + } + storeRegToAop (hc08_reg_a, AOP (result), offset); + if (AOP_TYPE (result) == AOP_REG && AOP(result)->aopu.aop_reg[offset]->rIdx == A_IDX) + assigned_a = TRUE; + offset++; + } + +finish: + if (offset < rsize) + { + rsize -= offset; + if (SPEC_USIGN (etype)) + { + while (rsize--) + storeConstToAop (0, AOP (result), offset++); + } + else + { + if (assigned_a && !delayed_a) + { + pushReg (hc08_reg_a, TRUE); + delayed_a = TRUE; + } + + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rola", ""); + emitcode ("clra", ""); + emitcode ("sbc", zero); + regalloc_dry_run_cost += 4; + + while (rsize--) + storeRegToAop (hc08_reg_a, AOP (result), offset++); + } + } + + freeAsmop (NULL, derefaop, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, "a"); + } + if (delayed_a) + pullReg (hc08_reg_a); + + pullOrFreeReg (hc08_reg_a, needpulla); +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * right, operand * result, iCode * ic, iCode * ifx) +{ + int size; + int litOffset = 0; + char * rematOffset = NULL; + asmop *derefaop; + bool needpulla = FALSE; + + D (emitcode ("; genDataPointerGet", "")); + + decodePointerOffset (right, &litOffset, &rematOffset); + wassert (rematOffset==NULL); + + aopOp (result, ic, TRUE); + size = AOP_SIZE (result); + + derefaop = aopDerefAop (AOP (left), litOffset); + freeAsmop (left, NULL, ic, TRUE); + derefaop->size = size; + + if (ifx) + needpulla = pushRegIfSurv (hc08_reg_a); + + if (IS_AOP_HX (AOP (result))) + loadRegFromAop (hc08_reg_hx, derefaop, 0); + else + while (size--) + { + if (!ifx) + transferAopAop (derefaop, size, AOP (result), size); + else + loadRegFromAop (hc08_reg_a, derefaop, size); + } + + freeAsmop (NULL, derefaop, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + + pullOrFreeReg (hc08_reg_a, needpulla); + if (ifx && !ifx->generated) + { + genIfxJump (ifx, "a"); + } +} + + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode * pi, iCode * ifx) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + int size, offset, xoffset; + int litOffset = 0; + char * rematOffset = NULL; + sym_link *retype = getSpec (operandType (result)); + bool needpulla = FALSE; + bool needpullh = FALSE; + bool needpullx = FALSE; + bool vol = FALSE; + + D (emitcode ("; genPointerGet", "")); + + if ((size = getSize (operandType (result))) > 1) + ifx = NULL; + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable */ + if (AOP_TYPE (left) == AOP_IMMD || AOP_TYPE (left) == AOP_LIT) + { + /* if result is not bit variable type */ + if (!IS_BITVAR (retype)) + { + genDataPointerGet (left, right, result, ic, ifx); + return; + } + else + { + genUnpackBitsImmed (left, right, result, ic, ifx); + return; + } + } + + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITVAR (retype)) + { + /* hasInchc08() will be false for bitfields, so no need */ + /* to consider post-increment in this case. */ + genUnpackBits (result, left, right, ifx); + goto release; + } + + /* This was marking the result registers dead, but I think */ + /* it should be marking the pointer (left op) registers dead. */ + /* EEP - 5 Jan 2013 */ + if (AOP_TYPE (left) == AOP_REG && pi) + { + int i; + for (i = 0; i < AOP_SIZE (left); i++) + AOP (left)->aopu.aop_reg[i]->isDead = TRUE; + } + + if (!IS_AOP_HX (AOP (left))) + { + needpullx = pushRegIfSurv (hc08_reg_x); + needpullh = pushRegIfSurv (hc08_reg_h); + } + + /* if the operand is already in hx + then we do nothing else we move the value to hx */ + loadRegFromAop (hc08_reg_hx, AOP (left), 0); + /* so hx now contains the address */ + + decodePointerOffset (right, &litOffset, &rematOffset); + + if (AOP_TYPE (result) == AOP_REG) + { + if (pi) + aopOp (IC_RESULT (pi), pi, FALSE); + + if (AOP_SIZE (result) == 1) + { + if (!pi) + { + if (IS_AOP_H (AOP (result))) + hc08_freeReg (hc08_reg_x); + loadRegIndexed (AOP (result)->aopu.aop_reg[0], litOffset, rematOffset); + } + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + hc08_useReg (hc08_reg_a); + emitcode ("aix", "#%d", size); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + hc08_freeReg (hc08_reg_x); + storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); + pi->generated = 1; + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); + } + } + else + { + if (pi || (IS_AOP_XA (AOP (IC_RESULT (ic))) && vol)) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + pushReg (hc08_reg_a, FALSE); + loadRegIndexed (hc08_reg_a, litOffset+1, rematOffset); + hc08_useReg (hc08_reg_a); + if (pi) + { + emitcode ("aix", "#%d", size); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + hc08_freeReg (hc08_reg_x); + storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); + pi->generated = 1; + } + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); + pullReg (AOP (IC_RESULT (ic))->aopu.aop_reg[1]); + } + else + { + if (IS_AOP_HX (AOP (IC_RESULT (ic)))) + { + hc08_freeReg (hc08_reg_x); + loadRegIndexed (hc08_reg_hx, litOffset, rematOffset); + } + else if (IS_AOP_XA (AOP (IC_RESULT (ic)))) + { + loadRegIndexed (hc08_reg_a, litOffset+1, rematOffset); + loadRegIndexed (hc08_reg_x, litOffset, rematOffset); + } + else if (IS_AOP_AX (AOP (IC_RESULT (ic)))) + { + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + loadRegIndexed (hc08_reg_x, litOffset+1, rematOffset); + } + } + } + + if (pi) + { + freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); + } + } + else if (!pi && !ifx && size == 2 && IS_S08 && hc08_reg_x->isDead && hc08_reg_h->isDead && AOP_TYPE (result) == AOP_EXT) /* Todo: Use this for bigger sizes, too */ + { + loadRegIndexed (hc08_reg_hx, litOffset, rematOffset); + storeRegToAop (hc08_reg_hx, AOP (result), 0); + } + else if (!pi && !ifx && size == 1 && hc08_reg_x->isDead && !hc08_reg_a->isDead) + { + loadRegIndexed (hc08_reg_x, litOffset, rematOffset); + storeRegToAop (hc08_reg_x, AOP (result), 0); + } + else + { + offset = size - 1; + needpulla = pushRegIfSurv (hc08_reg_a); + + while (size--) + { + xoffset = litOffset + (AOP_SIZE (result) - offset - 1); + loadRegIndexed (hc08_reg_a, xoffset, rematOffset); + if (!ifx) + storeRegToAop (hc08_reg_a, AOP (result), offset); + offset--; + } + } + +release: + size = AOP_SIZE (result); + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + + if (pi && !pi->generated) + { + emitcode ("aix", "#%d", size); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + aopOp (IC_RESULT (pi), pi, FALSE); + storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); + if (ifx && AOP_TYPE (IC_RESULT (pi)) != AOP_REG) + { + /* Ensure Z/NZ flag is correct since storeRegToAop() */ + /* disrupts the flag bits when storing to memory. */ + emitcode ("tsta", ""); + regalloc_dry_run_cost++; + } + freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); + pi->generated = 1; + } + + pullOrFreeReg (hc08_reg_a, needpulla); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_x, needpullx); + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, "a"); + } +} + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (operand * result, operand * left, sym_link * etype, operand * right) +{ + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + int litOffset = 0; + char *rematOffset = NULL; + bool needpulla; + + D (emitcode ("; genPackBits", "")); + + decodePointerOffset (left, &litOffset, &rematOffset); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + needpulla = pushRegIfSurv (hc08_reg_a); + if (AOP_TYPE (right) == AOP_REG) + { + /* Not optimal, but works for any register sources. */ + /* Just push the source values onto the stack and */ + /* pull them off any needed. Better optimzed would */ + /* be to do some of the shifting/masking now and */ + /* push the intermediate result. */ + if (blen > 8) + pushReg (AOP (right)->aopu.aop_reg[1], TRUE); + pushReg (AOP (right)->aopu.aop_reg[0], TRUE); + } + loadRegFromAop (hc08_reg_hx, AOP (result), 0); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + if ((mask | litval) != 0xff) + { + emitcode ("and", "#0x%02x", mask); + regalloc_dry_run_cost += 2; + } + if (litval) + { + emitcode ("ora", "#0x%02x", litval); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + /* Case with a bitfield length < 8 and arbitrary source + */ + if (AOP_TYPE (right) == AOP_REG) + pullReg (hc08_reg_a); + else + loadRegFromAop (hc08_reg_a, AOP (right), 0); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("and", "#0x%02x", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + + loadRegIndexed (hc08_reg_a, litOffset, rematOffset); + emitcode ("and", "#0x%02x", mask); + emitcode ("ora", "1,s"); + regalloc_dry_run_cost += 5; + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + pullReg (hc08_reg_a); + + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + if (!litOffset && !rematOffset && AOP (right)->type == AOP_DIR) + { + emitcode ("mov", "%s,x+", aopAdrStr (AOP (right), offset, FALSE)); + regalloc_dry_run_cost += 2; + litOffset--; + } + else + { + if (AOP_TYPE (right) == AOP_REG) + pullReg (hc08_reg_a); + else + loadRegFromAop (hc08_reg_a, AOP (right), offset); + storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); + } + offset++; + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + loadRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); + if ((mask | litval) != 0xff) + { + emitcode ("and", "#0x%02x", mask); + regalloc_dry_run_cost += 2; + } + if (litval) + { + emitcode ("ora", "#0x%02x", litval); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); + pullOrFreeReg (hc08_reg_a, needpulla); + return; + } + + /* Case with partial byte and arbitrary source + */ + if (AOP_TYPE (right) == AOP_REG) + pullReg (hc08_reg_a); + else + loadRegFromAop (hc08_reg_a, AOP (right), offset); + emitcode ("and", "#0x%02x", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + + loadRegIndexed(hc08_reg_a, litOffset+offset, rematOffset); + emitcode ("and", "#0x%02x", mask); + emitcode ("ora", "1,s"); + regalloc_dry_run_cost += 5; + storeRegIndexed (hc08_reg_a, litOffset+offset, rematOffset); + pullReg (hc08_reg_a); + } + + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genPackBitsImmed - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBitsImmed (operand * result, operand * left, sym_link * etype, operand * right, iCode * ic) +{ + asmop *derefaop; + int size; + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + bool needpulla; + int litOffset = 0; + char *rematOffset = NULL; + + D (emitcode ("; genPackBitsImmed", "")); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + aopOp (right, ic, FALSE); + size = AOP_SIZE (right); + decodePointerOffset (left, &litOffset, &rematOffset); + wassert (!rematOffset); + + derefaop = aopDerefAop (AOP (result), litOffset); + freeAsmop (result, NULL, ic, TRUE); + derefaop->size = size; + + /* if the bitfield is a single bit in the direct page */ + if (blen == 1 && derefaop->type == AOP_DIR) + { + if (AOP_TYPE (right) == AOP_LIT) + { + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + + emitcode ((litval & 1) ? "bset" : "bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); + regalloc_dry_run_cost += 2; + } + else + { + symbol *tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (right), 0); + emitcode ("lsra", ""); + regalloc_dry_run_cost++; + emitBranch ("bcs", tlbl1); + emitcode ("bclr", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); + regalloc_dry_run_cost += 2; + emitBranch ("bra", tlbl2); + if (!regalloc_dry_run) + emitLabel (tlbl1); + emitcode ("bset", "#%d,%s", bstr, aopAdrStr (derefaop, 0, FALSE)); + regalloc_dry_run_cost += 2; + if (!regalloc_dry_run) + emitLabel (tlbl2); + pullOrFreeReg (hc08_reg_a, needpulla); + } + goto release; + } + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, derefaop, 0); + if ((mask | litval) != 0xff) + { + emitcode ("and", "#0x%02x", mask); + regalloc_dry_run_cost += 2; + } + if (litval) + { + emitcode ("ora", "#0x%02x", litval); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, derefaop, 0); + + pullOrFreeReg (hc08_reg_a, needpulla); + goto release; + } + + /* Case with a bitfield length < 8 and arbitrary source + */ + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (right), 0); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("and", "#0x%02x", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + + loadRegFromAop (hc08_reg_a, derefaop, 0); + emitcode ("and", "#0x%02x", mask); + emitcode ("ora", "1,s"); + regalloc_dry_run_cost += 5; + storeRegToAop (hc08_reg_a, derefaop, 0); + pullReg (hc08_reg_a); + + pullOrFreeReg (hc08_reg_a, needpulla); + goto release; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + transferAopAop (AOP (right), offset, derefaop, size - offset - 1); + offset++; + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); + if ((mask | litval) != 0xff) + { + emitcode ("and", "#0x%02x", mask); + regalloc_dry_run_cost += 2; + } + if (litval) + { + emitcode ("ora", "#0x%02x", litval); + regalloc_dry_run_cost += 2; + } + hc08_dirtyReg (hc08_reg_a, FALSE); + storeRegToAop (hc08_reg_a, derefaop, size - offset - 1); + hc08_dirtyReg (hc08_reg_a, FALSE); + pullOrFreeReg (hc08_reg_a, needpulla); + goto release; + } + + /* Case with partial byte and arbitrary source + */ + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (right), offset); + emitcode ("and", "#0x%02x", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_a, FALSE); + pushReg (hc08_reg_a, TRUE); + + loadRegFromAop (hc08_reg_a, derefaop, size - offset - 1); + emitcode ("and", "#0x%02x", mask); + emitcode ("ora", "1,s"); + regalloc_dry_run_cost += 5; + storeRegToAop (hc08_reg_a, derefaop, size - offset - 1); + pullReg (hc08_reg_a); + pullOrFreeReg (hc08_reg_a, needpulla); + } + + hc08_freeReg (hc08_reg_a); + +release: + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (NULL, derefaop, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * left, operand * right, operand * result, iCode * ic) +{ + int size; + asmop *derefaop; + int litOffset = 0; + char *rematOffset = NULL; + + D (emitcode ("; genDataPointerSet", "")); + + aopOp (right, ic, FALSE); + size = AOP_SIZE (right); + decodePointerOffset (left, &litOffset, &rematOffset); + wassert (!rematOffset); + + derefaop = aopDerefAop (AOP (result), litOffset); + freeAsmop (result, NULL, ic, TRUE); + derefaop->size = size; + + if (IS_AOP_HX (AOP (right))) + { + storeRegToAop (hc08_reg_hx, derefaop, 0); + } + else + { + while (size--) + { + transferAopAop (AOP (right), size, derefaop, size); + } + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (NULL, derefaop, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode * pi) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + bool needpulla = FALSE; + bool needpullx = FALSE; + bool needpullh = FALSE; + bool vol = FALSE; + int litOffset = 0; + char *rematOffset = NULL; + + D (emitcode ("; genPointerSet", "")); + + aopOp (result, ic, FALSE); + + /* if the result is rematerializable */ + if (AOP_TYPE (result) == AOP_IMMD || AOP_TYPE (result) == AOP_LIT) + { + if (!IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (left, right, result, ic); + return; + } + else + { + genPackBitsImmed (result, left, (IS_BITVAR (retype) ? retype : letype), right, ic); + return; + } + } + if (AOP_TYPE (result) == AOP_REG && pi) + { + int i; + for (i = 0; i < AOP_SIZE (result); i++) + AOP (result)->aopu.aop_reg[i]->isDead = TRUE; + } + + needpullx = pushRegIfSurv (hc08_reg_x); + needpullh = pushRegIfSurv (hc08_reg_h); + + aopOp (right, ic, FALSE); + size = AOP_SIZE (right); + + /* if bit then pack */ + if (IS_BITVAR (retype) || IS_BITVAR (letype)) + { + genPackBits (result, left, (IS_BITVAR (retype) ? retype : letype), right); + } + else if (AOP_TYPE (right) == AOP_REG) + { + decodePointerOffset (left, &litOffset, &rematOffset); + if (size == 1) + { + if (!IS_AOP_A (AOP (right))) + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegHXAfromAop(AOP (result), 1, AOP (result), 0, AOP (right), 0 ); + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + } + else if (IS_AOP_XA (AOP (right)) || IS_AOP_HX (AOP (right))) + { + if (vol) + { + if (AOP (right)->aopu.aop_reg[0]->rIdx != A_IDX) + needpulla = pushRegIfSurv (hc08_reg_a); + pushReg (AOP (right)->aopu.aop_reg[0], TRUE); + pushReg (AOP (right)->aopu.aop_reg[1], TRUE); + loadRegFromAop (hc08_reg_hx, AOP (result), 0); + pullReg (hc08_reg_a); + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + pullReg (hc08_reg_a); + storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); + } + else + { + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_a, AOP (right), 0); + pushReg (AOP (right)->aopu.aop_reg[1], TRUE); + loadRegFromAop (hc08_reg_hx, AOP (result), 0); + storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); + pullReg (hc08_reg_a); + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + hc08_freeReg (hc08_reg_a); + } + } + else if (IS_AOP_AX (AOP (right))) + { + needpulla = pushRegIfSurv (hc08_reg_a); + pushReg (hc08_reg_x, TRUE); + loadRegFromAop (hc08_reg_hx, AOP (result), 0); + storeRegIndexed (hc08_reg_a, litOffset, rematOffset); + pullReg (hc08_reg_a); /* original X value */ + storeRegIndexed (hc08_reg_a, litOffset+1, rematOffset); + hc08_freeReg (hc08_reg_a); + } + else + { + wassertl (0, "bad source regs in genPointerSet()"); + } + } + else + { + decodePointerOffset (left, &litOffset, &rematOffset); + needpulla = pushRegIfSurv (hc08_reg_a); + loadRegFromAop (hc08_reg_hx, AOP (result), 0); + offset = size; + + while (offset--) + { + loadRegFromAop (hc08_reg_a, AOP (right), offset); + storeRegIndexed (hc08_reg_a, litOffset + size - offset - 1, rematOffset); + hc08_freeReg (hc08_reg_a); + } + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + + if (pi) + { + aopOp (IC_RESULT (pi), pi, FALSE); + emitcode ("aix", "#%d", size); + regalloc_dry_run_cost += 2; + hc08_dirtyReg (hc08_reg_hx, FALSE); + storeRegToAop (hc08_reg_hx, AOP (IC_RESULT (pi)), 0); + freeAsmop (IC_RESULT (pi), NULL, pi, TRUE); + pi->generated = 1; + } + + pullOrFreeReg (hc08_reg_a, needpulla); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_x, needpullx); +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + + D (emitcode ("; genIfx", "")); + + aopOp (cond, ic, FALSE); + + /* If the condition is a literal, we can just do an unconditional */ + /* branch or no branch */ + if (AOP_TYPE (cond) == AOP_LIT) + { + unsigned long long lit = ullFromVal (AOP (cond)->aopu.aop_lit); + freeAsmop (cond, NULL, ic, TRUE); + + /* if there was something to be popped then do it */ + if (popIc) + genIpop (popIc); + if (lit) + { + if (IC_TRUE (ic)) + emitBranch ("jmp", IC_TRUE (ic)); + } + else + { + if (IC_FALSE (ic)) + emitBranch ("jmp", IC_FALSE (ic)); + } + ic->generated = 1; + return; + } + + /* evaluate the operand */ + if (AOP_TYPE (cond) != AOP_CRY) + asmopToBool (AOP (cond), FALSE); + /* the result is now in the z flag bit */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if there was something to be popped then do it */ + if (popIc) + genIpop (popIc); + + genIfxJump (ic, "a"); + + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + bool needpullx, needpullh; + + D (emitcode ("; genAddrOf", "")); + + aopOp (IC_RESULT (ic), ic, FALSE); + + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) + { + needpullx = pushRegIfSurv (hc08_reg_x); + needpullh = pushRegIfSurv (hc08_reg_h); + /* if it has an offset then we need to compute it */ + offset = _G.stackOfs + _G.stackPushes + sym->stack; + hc08_useReg (hc08_reg_hx); + emitcode ("tsx", ""); + hc08_dirtyReg (hc08_reg_hx, FALSE); + regalloc_dry_run_cost++; + while (offset > 127) + { + emitcode ("aix", "#127"); + regalloc_dry_run_cost += 2; + offset -= 127; + } + while (offset < -128) + { + emitcode ("aix", "#-128"); + regalloc_dry_run_cost += 2; + offset += 128; + } + if (offset) + { + emitcode ("aix", "#%d", offset); + regalloc_dry_run_cost += 2; + } + storeRegToFullAop (hc08_reg_hx, AOP (IC_RESULT (ic)), FALSE); + pullOrFreeReg (hc08_reg_h, needpullh); + pullOrFreeReg (hc08_reg_x, needpullx); + goto release; + } + + /* object not on stack then we need the name */ + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + + while (size--) + { + char s[SDCC_NAME_MAX + 10]; + switch (offset) + { + case 0: + sprintf (s, "#%s", sym->rname); + break; + case 1: + sprintf (s, "#>%s", sym->rname); + break; + default: + sprintf (s, "#(%s >> %d)", sym->rname, offset * 8); + } + storeImmToAop (s, AOP (IC_RESULT (ic)), offset++); + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genAssignLit - Try to generate code for literal assignment. */ +/* result and right should already be asmOped */ +/*-----------------------------------------------------------------*/ +static bool +genAssignLit (operand * result, operand * right) +{ + char assigned[8]; + unsigned char value[sizeof(assigned)]; + char dup[sizeof(assigned)]; + int size; + int offset,offset2; + int dups,multiples; + bool needpula = FALSE; + bool canUseHX = TRUE; + int remaining; + + /* Make sure this is a literal assignment */ + if (AOP_TYPE (right) != AOP_LIT) + return FALSE; + + /* The general case already handles register assignment well */ + if (AOP_TYPE (result) == AOP_REG) + return FALSE; + + /* Some hardware registers require MSB to LSB assignment order */ + /* so don't optimize the assignment order if volatile */ + if (isOperandVolatile (result, FALSE)) + return FALSE; + + /* Make sure the assignment is not larger than we can handle */ + size = AOP_SIZE (result); + if (size > sizeof(assigned)) + return FALSE; + + for (offset=0; offsetaopu.aop_lit, offset); + } + + if ((AOP_TYPE (result) != AOP_DIR ) && IS_HC08) + canUseHX = FALSE; + + if (canUseHX) + { + /* Assign words that are already in HX */ + for (offset=size-2; offset>=0; offset -= 2) + { + if (assigned[offset] || assigned[offset+1]) + continue; + if (hc08_reg_hx->isLitConst && hc08_reg_hx->litConst == ((value[offset+1] << 8) + value[offset])) + { + storeRegToAop (hc08_reg_hx, AOP (result), offset); + assigned[offset] = 1; + assigned[offset+1] = 1; + } + } + } + + if (!(hc08_reg_h->isDead && hc08_reg_x->isDead)) + canUseHX = FALSE; + + if (canUseHX && (size>=2)) + { + /* Assign whatever reamains to be assigned */ + for (offset=size-2; offset>=0; offset -= 2) + { + if (assigned[offset] && assigned[offset+1]) + continue; + loadRegFromConst (hc08_reg_hx, (value[offset+1] << 8) + value[offset]); + storeRegToAop (hc08_reg_hx, AOP (result), offset); + assigned[offset] = 1; + assigned[offset+1] = 1; + } + return TRUE; + } + + remaining = size; + for (offset=0; offset 2) + aopOpExtToIdx (AOP (result), NULL, NULL); + + /* Assign bytes that are already in A and/or X */ + for (offset=size-1; offset>=0; offset--) + { + if (assigned[offset]) + continue; + if ((hc08_reg_a->isLitConst && hc08_reg_a->litConst == value[offset]) || + (hc08_reg_x->isLitConst && hc08_reg_x->litConst == value[offset])) + { + storeConstToAop (value[offset], AOP (result), offset); + assigned[offset] = 1; + } + } + + /* Consider bytes that appear multiple times */ + multiples = 0; + for (offset=size-1; offset>=0; offset--) + { + if (assigned[offset] || dup[offset]) + continue; + dups = 0; + for (offset2=offset-1; offset2>=0; offset2--) + if (value[offset2] == value[offset]) + { + dup[offset] = 1; + dups++; + } + if (dups) + multiples += (dups+1); + } + + /* Assign bytes that appear multiple times if the register cost */ + /* isn't too high. */ + if (multiples > 2 || (multiples && !hc08_reg_a->isDead)) + { + needpula = pushRegIfSurv (hc08_reg_a); + for (offset=size-1; offset>=0; offset--) + { + if (assigned[offset]) + continue; + if (dup[offset]) + { + loadRegFromConst (hc08_reg_a, value[offset]); + for (offset2=offset; offset2>=0; offset2--) + if (!assigned[offset2] && value[offset2] == value[offset]) + { + storeRegToAop (hc08_reg_a, AOP (result), offset2); + assigned[offset2] = 1; + } + hc08_freeReg (hc08_reg_a); + } + } + } + + /* Assign whatever remains to be assigned */ + for (offset=size-1; offset>=0; offset--) + { + if (assigned[offset]) + continue; + storeConstToAop (value[offset], AOP (result), offset); + } + + if (needpula) + pullReg (hc08_reg_a); + return TRUE; +} + + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + + D (emitcode ("; genAssign", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + if (!genAssignLit (result, right)) + genCopy (result, right); + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + symbol *jtab; + symbol *jtablo = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *jtabhi = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + D (emitcode ("; genJumpTab", "")); + + aopOp (IC_JTCOND (ic), ic, FALSE); + + if (hc08_reg_x->isFree && hc08_reg_h->isFree) + { + /* get the condition into x */ + loadRegFromAop (hc08_reg_x, AOP (IC_JTCOND (ic)), 0); + freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); + loadRegFromConst (hc08_reg_h, 0); + + if (!regalloc_dry_run) + { + emitcode ("lda", "%05d$,x", labelKey2num (jtabhi->key)); + emitcode ("ldx", "%05d$,x", labelKey2num (jtablo->key)); + } + regalloc_dry_run_cost += 6; + transferRegReg (hc08_reg_a, hc08_reg_h, TRUE); + emitcode ("jmp", ",x"); + regalloc_dry_run_cost++; + + hc08_dirtyReg (hc08_reg_a, TRUE); + hc08_dirtyReg (hc08_reg_hx, TRUE); + } + else + { + adjustStack (-2); + pushReg (hc08_reg_hx, TRUE); + + /* get the condition into x */ + loadRegFromAop (hc08_reg_x, AOP (IC_JTCOND (ic)), 0); + freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); + loadRegFromConst (hc08_reg_h, 0); + + if (!regalloc_dry_run) + emitcode ("lda", "%05d$,x", labelKey2num (jtabhi->key)); + emitcode ("sta", "3,s"); + if (!regalloc_dry_run) + emitcode ("lda", "%05d$,x", labelKey2num (jtablo->key)); + emitcode ("sta", "4,s"); + regalloc_dry_run_cost += 12; + + pullReg (hc08_reg_hx); + emitcode ("rts", ""); + regalloc_dry_run_cost++; + _G.stackPushes -= 2; + updateCFA (); + } + + /* now generate the jump labels */ + if (!regalloc_dry_run) + emitLabel (jtablo); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + emitcode (".db", "%05d$", labelKey2num (jtab->key)); + regalloc_dry_run_cost++; + } + if (!regalloc_dry_run) + emitLabel (jtabhi); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + emitcode (".db", ">%05d$", labelKey2num (jtab->key)); + regalloc_dry_run_cost++; + } +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + bool signExtend; + bool save_a; + + D (emitcode ("; genCast", "")); + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + if (IS_BOOL (operandType (result))) + { + bool needpulla = pushRegIfSurv (hc08_reg_a); + asmopToBool (AOP (right), TRUE); + storeRegToAop (hc08_reg_a, AOP (result), 0); + pullOrFreeReg (hc08_reg_a, needpulla); + goto release; + } + + /* If the result is 1 byte, then just copy the one byte; there is */ + /* nothing special required. */ + if (AOP_SIZE (result) == 1) + { + transferAopAop (AOP (right), 0, AOP (result), 0); + goto release; + } + + signExtend = AOP_SIZE (result) > AOP_SIZE (right) && !IS_BOOL (rtype) && IS_SPEC (rtype) && !SPEC_USIGN (rtype); + + /* If the result is 2 bytes and in registers, we have to be careful */ + /* to make sure the registers are not overwritten prematurely. */ + if (AOP_SIZE (result) == 2 && AOP (result)->type == AOP_REG) + { + if (IS_AOP_HX (AOP (result)) && (AOP_SIZE (right) == 2)) + { + loadRegFromAop (hc08_reg_hx, AOP (right), 0); + goto release; + } + + if (AOP_SIZE (right) == 1) + { + transferAopAop (AOP (right), 0, AOP (result), 0); + if (!signExtend) + storeConstToAop (0, AOP (result), 1); + else + { + save_a = (AOP (result)->aopu.aop_reg[0] == hc08_reg_a || !hc08_reg_a->isDead); + + /* we need to extend the sign :{ */ + if (save_a) + pushReg(hc08_reg_a, FALSE); + if (AOP (result)->aopu.aop_reg[0] != hc08_reg_a) + loadRegFromAop (hc08_reg_a, AOP (right), 0); + accopWithMisc ("rola", ""); + accopWithMisc ("clra", ""); + accopWithMisc ("sbc", zero); + storeRegToAop (hc08_reg_a, AOP (result), 1); + if (save_a) + pullReg(hc08_reg_a); + } + goto release; + } + + if (AOP (right)->type == AOP_REG) + { + wassert (AOP_SIZE (right) == 2); + /* Source and destination are the same size; no need for sign */ + /* extension or zero padding. Just copy in the order that */ + /* won't prematurely overwrite the source. */ + if (AOP (result)->aopu.aop_reg[0] == AOP (right)->aopu.aop_reg[1]) + { + transferAopAop (AOP (right), 1, AOP (result), 1); + transferAopAop (AOP (right), 0, AOP (result), 0); + } + else + { + transferAopAop (AOP (right), 0, AOP (result), 0); + transferAopAop (AOP (right), 1, AOP (result), 1); + } + goto release; + } + else + { + /* Source is at least 2 bytes and not in registers; no need */ + /* for sign extension or zero padding. Just copy. */ + transferAopAop (AOP (right), 0, AOP (result), 0); + transferAopAop (AOP (right), 1, AOP (result), 1); + goto release; + } + } + + wassert (AOP (result)->type != AOP_REG); + + save_a = !hc08_reg_a->isDead && signExtend; + if (save_a) + pushReg(hc08_reg_a, TRUE); + + offset = 0; + size = AOP_SIZE (right); + if (AOP_SIZE (result) < size) + size = AOP_SIZE (result); + while (size) + { + if (size == 1 && signExtend) + { + loadRegFromAop (hc08_reg_a, AOP (right), offset); + storeRegToAop (hc08_reg_a, AOP (result), offset); + offset++; + size--; + } + else if ((size > 2 || size >= 2 && !signExtend) && hc08_reg_h->isDead && hc08_reg_x->isDead && + (AOP_TYPE (right) == AOP_IMMD || IS_S08 && AOP_TYPE (right) == AOP_EXT) && + (AOP_TYPE (result) == AOP_DIR || IS_S08 && AOP_TYPE (result) == AOP_EXT)) + { + loadRegFromAop (hc08_reg_hx, AOP (right), offset); + storeRegToAop (hc08_reg_hx, AOP (result), offset); + offset += 2; + size -= 2; + } + else + { + transferAopAop (AOP (right), offset, AOP (result), offset); + offset++; + size--; + } + } + + size = AOP_SIZE (result) - offset; + if (size && !signExtend) + { + while (size--) + storeConstToAop (0, AOP (result), offset++); + } + else if (size) + { + accopWithMisc ("rola", ""); + accopWithMisc ("clra", ""); + accopWithMisc ("sbc", zero); + while (size--) + storeRegToAop (hc08_reg_a, AOP (result), offset++); + } + + if (save_a) + pullReg(hc08_reg_a); + + /* we are done hurray !!!! */ + +release: + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instrucion */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + if (!ifx) + return 0; + + D (emitcode ("; genDjnz", "")); + + /* if the minus is not of the form + a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no + saving, unless it's already in HX */ + aopOp (IC_RESULT (ic), ic, FALSE); + if (AOP_SIZE (IC_RESULT (ic)) > 1 && !IS_AOP_HX (AOP (IC_RESULT (ic)))) + { + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 0; + } + + /* Trying to use dbnz directly requires some convoluted branch/jumps */ + /* to handle the cases where the target is far away. The peepholer */ + /* is left to clean it up for the simple cases. However, this leaves */ + /* a needlessly high dry run cost. So we do not use dbnz and instead */ + /* generate simpler (and less constly) code that the peepholer can */ + /* easily transform to dbnz if the target is close enough. Thus the */ + /* register allocator gets a better idea of the true cost. */ + if (IS_AOP_HX (AOP (IC_RESULT (ic)))) + { + emitcode ("aix", "#-1"); + hc08_dirtyReg (hc08_reg_hx, FALSE); + emitcode ("cphx", "#0"); + regalloc_dry_run_cost += 5; + } + else + rmwWithAop ("dec", AOP (IC_RESULT (ic)), 0); + genIfxJump (ifx, "a"); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size; + int offset; + bool delayed_x = FALSE; + + D (emitcode (";", "genReceive")); + + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + + if (ic->argreg && IS_AOP_HX (AOP (IC_RESULT (ic))) && (offset + (ic->argreg - 1)) == 0) + { + pushReg (hc08_reg_x, TRUE); + emitcode ("tax", ""); + regalloc_dry_run_cost++; + pullReg (hc08_reg_h); + } + else if (ic->argreg) + { + while (size--) + { + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG && !(offset + (ic->argreg - 1)) && AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == X_IDX && size) + { + pushReg (hc08_reg_a, TRUE); + delayed_x = TRUE; + } + else + transferAopAop (hc08_aop_pass[offset + (ic->argreg - 1)], 0, AOP (IC_RESULT (ic)), offset); + if (hc08_aop_pass[offset]->type == AOP_REG) + hc08_freeReg (hc08_aop_pass[offset]->aopu.aop_reg[0]); + offset++; + } + } + + if (delayed_x) + pullReg (hc08_reg_x); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + int size, offset; + bool needpulla; + + D (emitcode ("; genDummyRead", "")); + + op = IC_RIGHT (ic); + + needpulla = pushRegIfSurv (hc08_reg_a); + if (op && IS_SYMOP (op)) + { + + aopOp (op, ic, FALSE); + + size = AOP_SIZE (op); + offset = size - 1; + + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (op), offset); + hc08_freeReg (hc08_reg_a); + offset--; + } + + freeAsmop (op, NULL, ic, TRUE); + } + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + + aopOp (op, ic, FALSE); + + size = AOP_SIZE (op); + offset = size - 1; + + while (size--) + { + loadRegFromAop (hc08_reg_a, AOP (op), offset); + hc08_freeReg (hc08_reg_a); + offset--; + } + + freeAsmop (op, NULL, ic, TRUE); + } + pullOrFreeReg (hc08_reg_a, needpulla); +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (iCode * ic) +{ + D (emitcode ("; genCritical", "")); + + if (IC_RESULT (ic)) + aopOp (IC_RESULT (ic), ic, TRUE); + + emitcode ("tpa", ""); + regalloc_dry_run_cost++; + hc08_dirtyReg (hc08_reg_a, FALSE); + emitcode ("sei", ""); + regalloc_dry_run_cost++; + + if (IC_RESULT (ic)) + storeRegToAop (hc08_reg_a, AOP (IC_RESULT (ic)), 0); + else + pushReg (hc08_reg_a, FALSE); + + hc08_freeReg (hc08_reg_a); + if (IC_RESULT (ic)) + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (iCode * ic) +{ + D (emitcode ("; genEndCritical", "")); + + if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE); + loadRegFromAop (hc08_reg_a, AOP (IC_RIGHT (ic)), 0); + emitcode ("tap", ""); + regalloc_dry_run_cost++; + hc08_freeReg (hc08_reg_a); + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + } + else + { + pullReg (hc08_reg_a); + emitcode ("tap", ""); + regalloc_dry_run_cost++; + } +} + +static void +updateiTempRegisterUse (operand * op) +{ + symbol *sym; + + if (IS_ITEMP (op)) + { + sym = OP_SYMBOL (op); + if (!sym->isspilt) + { + /* If only used by IFX, there might not be any register assigned */ + int i; + for(i = 0; i < sym->nRegs; i++) + if (sym->regs[i]) + hc08_useReg (sym->regs[i]); + } + } +} + +/*---------------------------------------------------------------------------------------*/ +/* genhc08iode - generate code for HC08 based controllers for a single iCode instruction */ +/*---------------------------------------------------------------------------------------*/ +static void +genhc08iCode (iCode *ic) +{ + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + return; + + { + int i; + reg_info *reg; + + initGenLineElement (); + genLine.lineElement.ic = ic; + + for (i = A_IDX; i <= XA_IDX; i++) + { + reg = hc08_regWithIdx (i); + //if (reg->aop) + // emitcode ("", "; %s = %s offset %d", reg->name, aopName (reg->aop), reg->aopofs); + reg->isFree = TRUE; + if (regalloc_dry_run) + reg->isLitConst = 0; + } + + if (ic->op == IFX) + updateiTempRegisterUse (IC_COND (ic)); + else if (ic->op == JUMPTABLE) + updateiTempRegisterUse (IC_JTCOND (ic)); + else if (ic->op == RECEIVE) + { + hc08_useReg (hc08_reg_a); + hc08_useReg (hc08_reg_x); // TODO: x really is free if function only receives 1 byte + } + else + { + if (POINTER_SET (ic)) + updateiTempRegisterUse (IC_RESULT (ic)); + updateiTempRegisterUse (IC_LEFT (ic)); + updateiTempRegisterUse (IC_RIGHT (ic)); + } + + for (i = A_IDX; i <= H_IDX; i++) + { + if (bitVectBitValue (ic->rSurv, i)) + { + hc08_regWithIdx (i)->isDead = FALSE; + hc08_regWithIdx (i)->isFree = FALSE; + } + else + hc08_regWithIdx (i)->isDead = TRUE; + } + } + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop then the if statement might + be using some of the registers being popped which + would destory the contents of the register so + we need to check for this condition and handle it */ + if (ic->next && ic->next->op == IFX && regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) + genIfx (ic->next, ic); + else + genIpop (ic); + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + case '<': + case LE_OP: + case GE_OP: + genCmp (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case NE_OP: + case EQ_OP: + genCmpEQorNE (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + hc08_genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + wassertl (0, "Unimplemented iCode"); + break; + + case GETABIT: + genGetAbit (ic); + break; + + case GETBYTE: + genGetByte (ic); + break; + + case GETWORD: + genGetWord (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInchc08 (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic)))), ifxForOp (IC_RESULT (ic), ic)); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInchc08 (IC_RESULT (ic), ic, getSize (operandType (IC_RIGHT (ic))))); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + if (!regalloc_dry_run) + addSet (&_G.sendSet, ic); + else + { + set * sendSet = NULL; + addSet (&sendSet, ic); + genSend (sendSet); + deleteSet (&sendSet); + } + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + case SWAP: + genSwap (ic); + break; + + default: + wassertl (0, "Unknown iCode"); + } +} + +static void +init_aop_pass(void) +{ + if (hc08_aop_pass[0]) + return; + + hc08_aop_pass[0] = newAsmop (AOP_REG); + hc08_aop_pass[0]->size = 1; + hc08_aop_pass[0]->aopu.aop_reg[0] = hc08_reg_a; + hc08_aop_pass[1] = newAsmop (AOP_REG); + hc08_aop_pass[1]->size = 1; + hc08_aop_pass[1]->aopu.aop_reg[0] = hc08_reg_x; + hc08_aop_pass[2] = newAsmop (AOP_DIR); + hc08_aop_pass[2]->size = 1; + hc08_aop_pass[2]->aopu.aop_dir = "___SDCC_hc08_ret2"; + hc08_aop_pass[3] = newAsmop (AOP_DIR); + hc08_aop_pass[3]->size = 1; + hc08_aop_pass[3]->aopu.aop_dir = "___SDCC_hc08_ret3"; + hc08_aop_pass[4] = newAsmop (AOP_DIR); + hc08_aop_pass[4]->size = 1; + hc08_aop_pass[4]->aopu.aop_dir = "___SDCC_hc08_ret4"; + hc08_aop_pass[5] = newAsmop (AOP_DIR); + hc08_aop_pass[5]->size = 1; + hc08_aop_pass[5]->aopu.aop_dir = "___SDCC_hc08_ret5"; + hc08_aop_pass[6] = newAsmop (AOP_DIR); + hc08_aop_pass[6]->size = 1; + hc08_aop_pass[6]->aopu.aop_dir = "___SDCC_hc08_ret6"; + hc08_aop_pass[7] = newAsmop (AOP_DIR); + hc08_aop_pass[7]->size = 1; + hc08_aop_pass[7]->aopu.aop_dir = "___SDCC_hc08_ret7"; +} + +unsigned char +dryhc08iCode (iCode *ic) +{ + regalloc_dry_run = TRUE; + regalloc_dry_run_cost = 0; + + init_aop_pass(); + + genhc08iCode (ic); + + destroy_line_list (); + /*freeTrace (&_G.trace.aops);*/ + + return (regalloc_dry_run_cost); +} + +/*-----------------------------------------------------------------*/ +/* genhc08Code - generate code for HC08 based controllers */ +/*-----------------------------------------------------------------*/ +void +genhc08Code (iCode *lic) +{ + iCode *ic; + int cln = 0; + int clevel = 0; + int cblock = 0; + + regalloc_dry_run = FALSE; + + hc08_dirtyReg (hc08_reg_a, FALSE); + hc08_dirtyReg (hc08_reg_h, FALSE); + hc08_dirtyReg (hc08_reg_x, FALSE); + + /* print the allocation information */ + if (allocInfo && currFunc) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc && !regalloc_dry_run) + { + debugFile->writeFunction (currFunc, lic); + } + + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + init_aop_pass(); + + for (ic = lic; ic; ic = ic->next) + ic->generated = FALSE; + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->level != clevel || ic->block != cblock) + { + if (options.debug) + { + debugFile->writeScope (ic); + } + clevel = ic->level; + cblock = ic->block; + } + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + if (!options.noCcodeInAsm) + { + emitcode ("", ";%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + } + cln = ic->lineno; + } + if (options.iCodeInAsm) + { + char regsSurv[4]; + const char *iLine; + + regsSurv[0] = (bitVectBitValue (ic->rSurv, A_IDX)) ? 'a' : '-'; + regsSurv[1] = (bitVectBitValue (ic->rSurv, H_IDX)) ? 'h' : '-'; + regsSurv[2] = (bitVectBitValue (ic->rSurv, X_IDX)) ? 'x' : '-'; + regsSurv[3] = 0; + iLine = printILine (ic); + emitcode ("", "; [%s] ic:%d: %s", regsSurv, ic->seq, printILine (ic)); + dbuf_free (iLine); + } + + regalloc_dry_run_cost = 0; + genhc08iCode(ic); + /*if (options.verboseAsm) + emitcode (";", "iCode %d (key %d) total cost: %d\n", ic->seq, ic->key, (int) regalloc_dry_run_cost);*/ + + if (!hc08_reg_a->isFree) + DD (emitcode ("", "; forgot to free a")); + if (!hc08_reg_x->isFree) + DD (emitcode ("", "; forgot to free x")); + if (!hc08_reg_h->isFree) + DD (emitcode ("", "; forgot to free h")); + if (!hc08_reg_hx->isFree) + DD (emitcode ("", "; forgot to free hx")); + if (!hc08_reg_xa->isFree) + DD (emitcode ("", "; forgot to free xa")); + } + + if (options.debug) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} + diff --git a/src/hc08/gen.h b/src/hc08/gen.h new file mode 100644 index 0000000..f946f23 --- /dev/null +++ b/src/hc08/gen.h @@ -0,0 +1,95 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for hc(s)08 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGENHC08_H +#define SDCCGENHC08_H + +enum + { + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, + AOP_EXT, AOP_SOF, AOP_DUMMY, AOP_IDX + }; + +enum + { + ACCUSE_XA = 1, + ACCUSE_HX + }; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop + { + + short type; + /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - operand using direct addressing mode + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_SOF - operand at an offset on the stack + AOP_EXT - operand using extended addressing mode + AOP_IDX - operand using indexed addressing mode + */ + short regmask; /* register mask if AOP_REG */ + short coff; /* current offset */ + short size; /* total size */ + operand *op; /* originating operand */ + unsigned code:1; /* is in Code space */ + unsigned freed:1; /* already freed */ + unsigned stacked:1; /* partial results stored on stack */ + struct asmop *stk_aop[4]; /* asmops for the results on the stack */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + struct { + char *aop_immd1; /* if immediate others are implied */ + char *aop_immd2; /* cast remat will generate this */ + } aop_immd; + int aop_stk; /* stack offset when AOP_STK */ + } + aopu; + } +asmop; + +void genhc08Code (iCode *); +void hc08_emitDebuggerSymbol (const char *); + +extern unsigned fReturnSizeHC08; + +iCode *hasInchc08 (operand *op, const iCode *ic, int osize); +extern bool hc08_assignment_optimal; + +#endif + diff --git a/src/hc08/hc08.h b/src/hc08/hc08.h new file mode 100644 index 0000000..aa0f440 --- /dev/null +++ b/src/hc08/hc08.h @@ -0,0 +1,18 @@ +typedef enum + { + SUB_HC08, + SUB_S08 + } +HC08_SUB_PORT; + +typedef struct + { + HC08_SUB_PORT sub; + } +HC08_OPTS; + +extern HC08_OPTS hc08_opts; + +#define IS_HC08 (hc08_opts.sub == SUB_HC08) +#define IS_S08 (hc08_opts.sub == SUB_S08) + diff --git a/src/hc08/hc08.vcxproj b/src/hc08/hc08.vcxproj new file mode 100644 index 0000000..1825393 --- /dev/null +++ b/src/hc08/hc08.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {6FA87476-0FAB-4518-9845-12EFEBBCE03F} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/hc08.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level2 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/hc08.bsc + + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/hc08.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/hc08.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + + \ No newline at end of file diff --git a/src/hc08/hc08.vcxproj.filters b/src/hc08/hc08.vcxproj.filters new file mode 100644 index 0000000..ec3612f --- /dev/null +++ b/src/hc08/hc08.vcxproj.filters @@ -0,0 +1,49 @@ + + + + + {36ed1d0f-18b0-4751-99cf-8f3e3c6c6af9} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {63c1a458-3e87-4ca3-bd80-b0fe3ce009fc} + h;hpp;hxx;hm;inl + + + {efbf5b89-aca8-4e12-ae6a-f2173da55e89} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/hc08/main.c b/src/hc08/main.c new file mode 100644 index 0000000..85656f5 --- /dev/null +++ b/src/hc08/main.c @@ -0,0 +1,1058 @@ +/*------------------------------------------------------------------------- + main.h - hc08 specific general function + + Copyright (C) 2003, Erik Petrich + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ +/* + Note that mlh prepended _hc08_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "hc08.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +extern char * iComments2; +extern DEBUGFILE dwarf2DebugFile; +extern int dwarf2FinalizeFile(FILE *); + +static char _hc08_defaultRules[] = +{ +#include "peeph.rul" +}; + +static char _s08_defaultRules[] = +{ +#include "peeph.rul" +}; + +HC08_OPTS hc08_opts; + +/* list of key words used by msc51 */ +static char *_hc08_keywords[] = +{ + "at", + //"bit", + "code", + "critical", + "data", + "far", + //"idata", + "interrupt", + "near", + //"pdata", + "reentrant", + //"sfr", + //"sbit", + //"using", + "xdata", + "_data", + "_code", + "_generic", + "_near", + "_xdata", + //"_pdata", + //"_idata", + "_naked", + "_overlay", + NULL +}; + + +void hc08_assignRegisters (ebbIndex *); + +static int regParmFlg = 0; /* determine if we can register a parameter */ + +static void +_hc08_init (void) +{ + hc08_opts.sub = SUB_HC08; + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_s08_init (void) +{ + hc08_opts.sub = SUB_S08; + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_hc08_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; +} + +static int +_hc08_regparm (sym_link * l, bool reentrant) +{ + int size = getSize(l); + + /* If they fit completely, the first two bytes of parameters can go */ + /* into A and X, otherwise, they go on the stack. Examples: */ + /* foo(char p1) A <- p1 */ + /* foo(char p1, char p2) A <- p1, X <- p2 */ + /* foo(char p1, char p2, char p3) A <- p1, X <- p2, stack <- p3 */ + /* foo(int p1) XA <- p1 */ + /* foo(long p1) stack <- p1 */ + /* foo(char p1, int p2) A <- p1, stack <- p2 */ + /* foo(int p1, char p2) XA <- p1, stack <- p2 */ + + if (regParmFlg>=2) + return 0; + + if ((regParmFlg+size)>2) + { + regParmFlg = 2; + return 0; + } + + regParmFlg += size; + return 1+regParmFlg-size; +} + +static bool +_hc08_parseOptions (int *pargc, char **argv, int *i) +{ + if (!strcmp (argv[*i], "--out-fmt-elf")) + { + options.out_fmt = 'E'; + debugFile = &dwarf2DebugFile; + return TRUE; + } + + if (!strcmp (argv[*i], "--oldralloc")) + { + options.oldralloc = TRUE; + return TRUE; + } + + return FALSE; +} + +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_LARGE_MODEL "--model-large" + +static OPTION _hc08_options[] = + { + {0, OPTION_SMALL_MODEL, NULL, "8-bit address space for data"}, + {0, OPTION_LARGE_MODEL, NULL, "16-bit address space for data (default)"}, + {0, "--out-fmt-elf", NULL, "Output executable in ELF format" }, + {0, "--oldralloc", NULL, "Use old register allocator"}, + {0, NULL } + }; + +static void +_hc08_finaliseOptions (void) +{ + if (options.noXinitOpt) + port->genXINIT = 0; + + if (options.model == MODEL_LARGE) { + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + } + else + { + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + } + + istack->ptrType = FPOINTER; +} + +static void +_hc08_setDefaultOptions (void) +{ + options.code_loc = 0x8000; + options.data_loc = 0x80; + options.xdata_loc = 0; /* 0 means immediately following data */ + options.stack_loc = 0x7fff; + options.out_fmt = 's'; /* use motorola S19 output */ + + options.omitFramePtr = 1; /* no frame pointer (we use SP */ + /* offsets instead) */ +} + +static const char * +_hc08_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_hc08_genAssemblerPreamble (FILE * of) +{ + int i; + int needOrg = 1; + symbol *mainExists=newSymbol("main", 0); + mainExists->block=0; + + fprintf (of, "\t.area %s\n",HOME_NAME); + fprintf (of, "\t.area GSINIT0 (CODE)\n"); + fprintf (of, "\t.area %s\n",port->mem.static_name); + fprintf (of, "\t.area %s\n",port->mem.post_static_name); + fprintf (of, "\t.area %s\n",CODE_NAME); + fprintf (of, "\t.area %s\n",port->mem.xinit_name); + fprintf (of, "\t.area %s\n",port->mem.const_name); + fprintf (of, "\t.area %s\n",port->mem.data_name); + fprintf (of, "\t.area %s\n",port->mem.overlay_name); + fprintf (of, "\t.area %s\n",port->mem.xdata_name); + fprintf (of, "\t.area %s\n",port->mem.xidata_name); + + if ((mainExists=findSymWithLevel(SymbolTab, mainExists))) + { + // generate interrupt vector table + fprintf (of, "\t.area\tCODEIVT (ABS)\n"); + + for (i=maxInterrupts;i>0;i--) + { + if (interrupts[i]) + { + if (needOrg) + { + fprintf (of, "\t.org\t0x%04x\n", (0xfffe - (i * 2))); + needOrg = 0; + } + fprintf (of, "\t.dw\t%s\n", interrupts[i]->rname); + } + else + needOrg = 1; + } + if (needOrg) + fprintf (of, "\t.org\t0xfffe\n"); + fprintf (of, "\t.dw\t%s", "__sdcc_gs_init_startup\n\n"); + + fprintf (of, "\t.area GSINIT0\n"); + fprintf (of, "__sdcc_gs_init_startup:\n"); + if (options.stack_loc) + { + fprintf (of, "\tldhx\t#0x%04x\n", options.stack_loc+1); + fprintf (of, "\ttxs\n"); + } + else + fprintf (of, "\trsp\n"); + fprintf (of, "\tjsr\t__sdcc_external_startup\n"); + fprintf (of, "\tbeq\t__sdcc_init_data\n"); + fprintf (of, "\tjmp\t__sdcc_program_startup\n"); + fprintf (of, "__sdcc_init_data:\n"); + + fprintf (of, "; _hc08_genXINIT() start\n"); + fprintf (of, " ldhx #0\n"); + fprintf (of, "00001$:\n"); + fprintf (of, " cphx #l_XINIT\n"); + fprintf (of, " beq 00002$\n"); + fprintf (of, " lda s_XINIT,x\n"); + fprintf (of, " sta s_XISEG,x\n"); + fprintf (of, " aix #1\n"); + fprintf (of, " bra 00001$\n"); + fprintf (of, "00002$:\n"); + fprintf (of, "; _hc08_genXINIT() end\n"); + + fprintf (of, "\t.area GSFINAL\n"); + fprintf (of, "\tjmp\t__sdcc_program_startup\n\n"); + + fprintf (of, "\t.area CSEG\n"); + fprintf (of, "__sdcc_program_startup:\n"); + fprintf (of, "\tjsr\t_main\n"); + fprintf (of, "\tbra\t.\n"); + + } +} + +static void +_hc08_genAssemblerEnd (FILE * of) +{ + if (options.out_fmt == 'E' && options.debug) + { + dwarf2FinalizeFile (of); + } +} + +static void +_hc08_genExtraAreas (FILE * asmFile, bool mainExists) +{ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; extended address mode data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&xdata->oBuf, asmFile); +} + +/* Generate interrupt vector table. */ +static int +_hc08_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + int i; + + dbuf_printf (oBuf, "\t.area\tCODEIVT (ABS)\n"); + dbuf_printf (oBuf, "\t.org\t0x%04x\n", + (0xfffe - (maxInterrupts * 2))); + + for (i=maxInterrupts;i>0;i--) + { + if (interrupts[i]) + dbuf_printf (oBuf, "\t.dw\t%s\n", interrupts[i]->rname); + else + dbuf_printf (oBuf, "\t.dw\t0xffff\n"); + } + dbuf_printf (oBuf, "\t.dw\t%s", "__sdcc_gs_init_startup\n"); + + return TRUE; +} + +/* Generate code to copy XINIT to XISEG */ +static void _hc08_genXINIT (FILE * of) { + fprintf (of, "; _hc08_genXINIT() start\n"); + fprintf (of, "; _hc08_genXINIT() end\n"); +} + + +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ + operand *result = IC_RESULT(ic); + sym_link *result_type = operandType(result); + + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + if (ic->op == ADDRESS_OF) + return 0; + + /* if bitwise | add & subtract then no since hc08 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } + + /* for others it is cheaper to do the cse */ + return 1; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || (op == SWAP && size <= 2) + || op == GETABIT + || op == GETBYTE + || op == GETWORD + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + /* The hc08's addressing modes allow access to all storage classes */ + /* inexpensively (<=0) */ + + if (IN_DIRSPACE (oclass)) /* direct addressing mode is fastest */ + return -2; + if (IN_FARSPACE (oclass)) /* extended addressing mode is almost at fast */ + return -1; + if (oclass == istack) /* stack is the slowest, but still faster than */ + return 0; /* trying to copy to a temp location elsewhere */ + + return 0; /* anything we missed */ +} + +/*----------------------------------------------------------------------*/ +/* hc08_dwarfRegNum - return the DWARF register number for a register. */ +/* These are defined for the HC08 in "Motorola 8- and 16-bit Embedded */ +/* Application Binary Interface (M8/16EABI)" */ +/*----------------------------------------------------------------------*/ +static int +hc08_dwarfRegNum (const struct reg_info *reg) +{ + switch (reg->rIdx) + { + case A_IDX: return 0; + case H_IDX: return 1; + case X_IDX: return 2; + case CND_IDX: return 17; + case SP_IDX: return 15; + } + return -1; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + return getSize (left) == 1 && getSize (right) == 1; +} + +typedef struct asmLineNode + { + int size; + } +asmLineNode; + +static asmLineNode * +newAsmLineNode (void) +{ + asmLineNode *aln; + + aln = Safe_alloc ( sizeof (asmLineNode)); + aln->size = 0; + + return aln; +} + +typedef struct hc08opcodedata + { + char name[6]; + char adrmode; + /* info for registers used and/or modified by an instruction will be added here */ + } +hc08opcodedata; + +#define HC08OP_STD 1 +#define HC08OP_RMW 2 +#define HC08OP_INH 3 +#define HC08OP_IM1 4 +#define HC08OP_BR 5 +#define HC08OP_BTB 6 +#define HC08OP_BSC 7 +#define HC08OP_MOV 8 +#define HC08OP_CBEQ 9 +#define HC08OP_CPHX 10 +#define HC08OP_LDHX 11 +#define HC08OP_STHX 12 +#define HC08OP_DBNZ 13 + +/* These must be kept sorted by opcode name */ +static hc08opcodedata hc08opcodeDataTable[] = + { + {".db", HC08OP_INH}, /* used by the code generator only in the jump table */ + {"adc", HC08OP_STD}, + {"add", HC08OP_STD}, + {"ais", HC08OP_IM1}, + {"aix", HC08OP_IM1}, + {"and", HC08OP_STD}, + {"asl", HC08OP_RMW}, + {"asla", HC08OP_INH}, + {"aslx", HC08OP_INH}, + {"asr", HC08OP_RMW}, + {"asra", HC08OP_INH}, + {"asrx", HC08OP_INH}, + {"bcc", HC08OP_BR,}, + {"bclr", HC08OP_BSC}, + {"bcs", HC08OP_BR}, + {"beq", HC08OP_BR}, + {"bge", HC08OP_BR}, + {"bgnd", HC08OP_INH}, + {"bgt", HC08OP_BR}, + {"bhcc", HC08OP_BR}, + {"bhcs", HC08OP_BR}, + {"bhi", HC08OP_BR}, + {"bhs", HC08OP_BR}, + {"bih", HC08OP_BR}, + {"bil", HC08OP_BR}, + {"bit", HC08OP_STD}, + {"ble", HC08OP_BR}, + {"blo", HC08OP_BR}, + {"bls", HC08OP_BR}, + {"blt", HC08OP_BR}, + {"bmc", HC08OP_BR}, + {"bmi", HC08OP_BR}, + {"bms", HC08OP_BR}, + {"bne", HC08OP_BR}, + {"bpl", HC08OP_BR}, + {"bra", HC08OP_BR}, + {"brclr", HC08OP_BTB}, + {"brn", HC08OP_BR}, + {"brset", HC08OP_BTB}, + {"bset", HC08OP_BSC}, + {"bsr", HC08OP_BR}, + {"cbeq", HC08OP_CBEQ}, + {"cbeqa", HC08OP_CBEQ}, + {"cbeqx", HC08OP_CBEQ}, + {"clc", HC08OP_INH}, + {"cli", HC08OP_INH}, + {"clr", HC08OP_RMW}, + {"clra", HC08OP_INH}, + {"clrh", HC08OP_INH}, + {"clrx", HC08OP_INH}, + {"cmp", HC08OP_STD}, + {"com", HC08OP_RMW}, + {"coma", HC08OP_INH}, + {"comx", HC08OP_INH}, + {"cphx", HC08OP_CPHX}, + {"cpx", HC08OP_STD}, + {"daa", HC08OP_INH}, + {"dbnz", HC08OP_DBNZ}, + {"dbnza", HC08OP_BR}, + {"dbnzx", HC08OP_BR}, + {"dec", HC08OP_RMW}, + {"deca", HC08OP_INH}, + {"decx", HC08OP_INH}, + {"div", HC08OP_INH}, + {"eor", HC08OP_STD}, + {"inc", HC08OP_RMW}, + {"inca", HC08OP_INH}, + {"incx", HC08OP_INH}, + {"jmp", HC08OP_STD}, + {"jsr", HC08OP_STD}, + {"lda", HC08OP_STD}, + {"ldhx", HC08OP_LDHX}, + {"ldx", HC08OP_STD}, + {"lsl", HC08OP_RMW}, + {"lsla", HC08OP_INH}, + {"lslx", HC08OP_INH}, + {"lsr", HC08OP_RMW}, + {"lsra", HC08OP_INH}, + {"lsrx", HC08OP_INH}, + {"mov", HC08OP_MOV}, + {"mul", HC08OP_INH}, + {"neg", HC08OP_RMW}, + {"nega", HC08OP_INH}, + {"negx", HC08OP_INH}, + {"nop", HC08OP_INH}, + {"nsa", HC08OP_INH}, + {"ora", HC08OP_STD}, + {"psha", HC08OP_INH}, + {"pshh", HC08OP_INH}, + {"pshx", HC08OP_INH}, + {"pula", HC08OP_INH}, + {"pulh", HC08OP_INH}, + {"pulx", HC08OP_INH}, + {"rol", HC08OP_RMW}, + {"rola", HC08OP_INH}, + {"rolx", HC08OP_INH}, + {"ror", HC08OP_RMW}, + {"rora", HC08OP_INH}, + {"rorx", HC08OP_INH}, + {"rsp", HC08OP_INH}, + {"rti", HC08OP_INH}, + {"rts", HC08OP_INH}, + {"sbc", HC08OP_STD}, + {"sec", HC08OP_INH}, + {"sei", HC08OP_INH}, + {"sta", HC08OP_STD}, + {"sthx", HC08OP_STHX}, + {"stop", HC08OP_INH}, + {"stx", HC08OP_STD}, + {"sub", HC08OP_STD}, + {"swi", HC08OP_INH}, + {"tap", HC08OP_INH}, + {"tax", HC08OP_INH}, + {"tpa", HC08OP_INH}, + {"tst", HC08OP_RMW}, + {"tsta", HC08OP_INH}, + {"tstx", HC08OP_INH}, + {"tsx", HC08OP_INH}, + {"txa", HC08OP_INH}, + {"txs", HC08OP_INH}, + {"wait", HC08OP_INH} + }; + +static int +hc08_opcodeCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((hc08opcodedata *)member)->name); +} + +/*--------------------------------------------------------------------*/ +/* Given an instruction and its first two operands, compute the */ +/* instruction size. There are a few cases where it's too complicated */ +/* to distinguish between an 8-bit offset and 16-bit offset; in these */ +/* cases we conservatively assume the 16-bit offset size. */ +/*--------------------------------------------------------------------*/ +static int +hc08_instructionSize(const char *inst, const char *op1, const char *op2) +{ + hc08opcodedata *opcode; + int size; + long offset; + char * endnum = NULL; + + opcode = bsearch (inst, hc08opcodeDataTable, + sizeof(hc08opcodeDataTable)/sizeof(hc08opcodedata), + sizeof(hc08opcodedata), hc08_opcodeCompare); + + if (!opcode) + return 999; + switch (opcode->adrmode) + { + case HC08OP_INH: /* Inherent addressing mode */ + return 1; + + case HC08OP_BSC: /* Bit set/clear direct addressing mode */ + case HC08OP_BR: /* Branch (1 byte signed offset) */ + case HC08OP_IM1: /* 1 byte immediate addressing mode */ + return 2; + + case HC08OP_BTB: /* Bit test direct addressing mode and branch */ + return 3; + + case HC08OP_RMW: /* read/modify/write instructions */ + if (!op2[0]) /* if not ,x or ,sp must be direct addressing mode */ + return 2; + if (!op1[0]) /* if ,x with no offset */ + return 1; + if (op2[0] == 'x') /* if ,x with offset */ + return 2; + return 3; /* Otherwise, must be ,sp with offset */ + + case HC08OP_STD: /* standard instruction */ + if (!op2[0]) + { + if (op1[0] == '#') /* Immediate addressing mode */ + return 2; + if (op1[0] == '*') /* Direct addressing mode */ + return 2; + return 3; /* Otherwise, must be extended addressing mode */ + } + else + { + if (!op1[0]) /* if ,x with no offset */ + return 1; + size = 2; + if (op2[0] == 's') + size++; + offset = strtol (op1, &endnum, 0) & 0xffff; + if (endnum && *endnum) + size++; + else if (offset > 0xff) + size++; + return size; + } + case HC08OP_MOV: + if (op2[0] == 'x') + return 2; + return 3; + case HC08OP_CBEQ: + if (op2[0] == 'x' && !op1[0]) + return 2; /* cbeq ,x+,rel */ + if (op2[0] == 's') + return 4; /* cbeq oprx8,sp,rel */ + return 3; + case HC08OP_CPHX: + if (op1[0] == '*') + return 2; + return 3; + case HC08OP_DBNZ: + if (!op2[0]) + return 2; + if (!op1[0] && op2[0] == 'x') + return 2; + if (op2[0] == 's') + return 4; + return 3; + case HC08OP_LDHX: + case HC08OP_STHX: + if (op1[0] == '*') + return 2; + if (!op1[0] && op2[0] == 'x') + return 2; + if (op2[0] == 's' || op1[0] == '#' || !op2[0]) + return 3; + size = 3; + offset = strtol (op1, &endnum, 0) & 0xffff; + if (endnum && *endnum) + size++; + else if (offset > 0xff) + size++; + return size; + default: + return 4; + } +} + + +static asmLineNode * +hc08_asmLineNodeFromLineNode (lineNode *ln) +{ + asmLineNode *aln = newAsmLineNode(); + char *op, op1[256], op2[256]; + int opsize; + const char *p; + char inst[8]; + + p = ln->line; + + while (*p && isspace(*p)) p++; + for (op = inst, opsize=1; *p; p++) + { + if (isspace(*p) || *p == ';' || *p == ':' || *p == '=') + break; + else + if (opsize < sizeof(inst)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ';' || *p == ':' || *p == '=') + return aln; + + while (*p && isspace(*p)) p++; + if (*p == '=') + return aln; + + for (op = op1, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op1)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ',') p++; + for (op = op2, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op2)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + aln->size = hc08_instructionSize(inst, op1, op2); + + return aln; +} + +static int +hc08_getInstructionSize (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) hc08_asmLineNodeFromLineNode (line); + + return line->aln->size; +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdld6808", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = +{ + "sdas6808", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char * const _libs_hc08[] = { "hc08", NULL, }; +static const char * const _libs_s08[] = { "s08", NULL, }; + +/* Globals */ +PORT hc08_port = +{ + TARGET_ID_HC08, + "hc08", + "HC08", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE, + MODEL_LARGE, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_hc08, /* libs */ + }, + { /* Peephole optimizer */ + _hc08_defaultRules, + hc08_getInstructionSize, + }, + { + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + 1, 2, 2, 4, 8, 2, 2, 2, 2, 0, 1, 4 + }, + /* tags for generic pointers */ + { 0x00, 0x00, 0x00, 0x00 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CSEG (CODE)", + "DSEG (PAG)", + NULL, /* "ISEG" */ + NULL, /* "PSEG" */ + "XSEG", + NULL, /* "BSEG" */ + "RSEG (ABS)", + "GSINIT (CODE)", + "OSEG (PAG, OVR)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS)", // xabs_name - absolute xdata + "IABS (ABS)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _hc08_genExtraAreas, + NULL }, + { + -1, /* direction (-1 = stack grows down) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 2, /* call_overhead */ + 0, /* reent_overhead */ + 0, /* banked_overhead (switch between code banks) */ + 1 /* sp is offset by 1 from last item pushed */ + }, + { + 5, FALSE + }, + { + hc08_emitDebuggerSymbol, + { + hc08_dwarfRegNum, + NULL, + NULL, + 4, /* addressSize */ + 14, /* regNumRet */ + 15, /* regNumSP */ + -1, /* regNumBP */ + 1, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {8,16,32}, /* sizeofMatchJump[] */ + {8,16,32}, /* sizeofRangeCompare[] */ + 5, /* sizeofSubtract */ + 10, /* sizeofDispatch */ + }, + "_", + _hc08_init, + _hc08_parseOptions, + _hc08_options, + NULL, + _hc08_finaliseOptions, + _hc08_setDefaultOptions, + hc08_assignRegisters, + _hc08_getRegName, + 0, + NULL, + _hc08_keywords, + _hc08_genAssemblerPreamble, + _hc08_genAssemblerEnd, /* no genAssemblerEnd */ + _hc08_genIVT, + _hc08_genXINIT, + NULL, /* genInitStartup */ + _hc08_reset_regparm, + _hc08_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, /* use_dw_for_init */ + FALSE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 3, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT s08_port = +{ + TARGET_ID_S08, + "s08", + "S08", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, /* Emit glue around main */ + MODEL_SMALL | MODEL_LARGE, + MODEL_LARGE, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_s08, /* libs */ + }, + { /* Peephole optimizer */ + _s08_defaultRules, + hc08_getInstructionSize, + }, + { + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + 1, 2, 2, 4, 8, 2, 2, 2, 2, 0, 1, 4 + }, + /* tags for generic pointers */ + { 0x00, 0x00, 0x00, 0x00 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CSEG (CODE)", + "DSEG (PAG)", + NULL, /* "ISEG" */ + NULL, /* "PSEG" */ + "XSEG", + NULL, /* "BSEG" */ + "RSEG (ABS)", + "GSINIT (CODE)", + "OSEG (PAG, OVR)", + "GSFINAL (CODE)", + "HOME (CODE)", + "XISEG", // initialized xdata + "XINIT (CODE)", // a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS)", // xabs_name - absolute xdata + "IABS (ABS)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _hc08_genExtraAreas, + NULL }, + { + -1, /* direction (-1 = stack grows down) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 2, /* call_overhead */ + 0, /* reent_overhead */ + 0, /* banked_overhead (switch between code banks) */ + 1 /* sp is offset by 1 from last item pushed */ + }, + { + 5, FALSE + }, + { + hc08_emitDebuggerSymbol, + { + hc08_dwarfRegNum, + NULL, + NULL, + 4, /* addressSize */ + 14, /* regNumRet */ + 15, /* regNumSP */ + -1, /* regNumBP */ + 1, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {8,16,32}, /* sizeofMatchJump[] */ + {8,16,32}, /* sizeofRangeCompare[] */ + 5, /* sizeofSubtract */ + 10, /* sizeofDispatch */ + }, + "_", + _s08_init, + _hc08_parseOptions, + _hc08_options, + NULL, + _hc08_finaliseOptions, + _hc08_setDefaultOptions, + hc08_assignRegisters, + _hc08_getRegName, + 0, + NULL, + _hc08_keywords, + _hc08_genAssemblerPreamble, + _hc08_genAssemblerEnd, /* no genAssemblerEnd */ + _hc08_genIVT, + _hc08_genXINIT, + NULL, /* genInitStartup */ + _hc08_reset_regparm, + _hc08_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, /* use_dw_for_init */ + FALSE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 3, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/hc08/main.h b/src/hc08/main.h new file mode 100644 index 0000000..6555225 --- /dev/null +++ b/src/hc08/main.h @@ -0,0 +1,8 @@ +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + +#endif diff --git a/src/hc08/peeph.def b/src/hc08/peeph.def new file mode 100644 index 0000000..745c744 --- /dev/null +++ b/src/hc08/peeph.def @@ -0,0 +1,278 @@ +replace { + pula + psha + lda %1 +} by { + ; Peephole 1a - removed redundant pula/psha + lda %1 +} + +replace { + pula + psha + lda %1,s +} by { + ; Peephole 1b - removed redundant pula/psha + lda %1,s +} + +replace { + pula + psha + clra +} by { + ; Peephole 1c - removed redundant pula/psha + clra +} + + +replace { + bcs %1 + jmp %5 +%1: +} by { + ; Peephole 2a - eliminated jmp + bcc %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bcc %1 + jmp %5 +%1: +} by { + ; Peephole 2b - eliminated jmp + bcs %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + beq %1 + jmp %5 +%1: +} by { + ; Peephole 2c - eliminated jmp + bne %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bne %1 + jmp %5 +%1: +} by { + ; Peephole 2d - eliminated jmp + beq %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + brclr %3,%2,%1 + jmp %5 +%1: +} by { + ; Peephole 2e - eliminated jmp + brset %3,%2,%5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + brset %3,%2,%1 + jmp %5 +%1: +} by { + ; Peephole 2f - eliminated jmp + brclr %3,%2,%5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bls %1 + jmp %5 +%1: +} by { + ; Peephole 2g - eliminated jmp + bhi %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bhi %1 + jmp %5 +%1: +} by { + ; Peephole 2h - eliminated jmp + bls %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + ble %1 + jmp %5 +%1: +} by { + ; Peephole 2i - eliminated jmp + bgt %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bgt %1 + jmp %5 +%1: +} by { + ; Peephole 2j - eliminated jmp + ble %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bge %1 + jmp %5 +%1: +} by { + ; Peephole 2k - eliminated jmp + blt %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + blt %1 + jmp %5 +%1: +} by { + ; Peephole 2l - eliminated jmp + bge %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bmi %1 + jmp %5 +%1: +} by { + ; Peephole 2m - eliminated jmp + bpl %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + bpl %1 + jmp %5 +%1: +} by { + ; Peephole 2n - eliminated jmp + bmi %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace restart { + jmp %5 +} by { + ; Peephole 3 - shortened jmp to bra + bra %5 +} if labelInRange(%5) + +replace { + lda %1 + tsta +} by { + ; Peephole 4a - eliminated redundant tsta + lda %1 +} + +replace { + ldx %1 + tstx +} by { + ; Peephole 4b - eliminated redundant tstx + ldx %1 +} + +replace { + jmp %5 +} by { + ; Peephole 6a - replaced jmp to rts with rts + rts +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace { + bra %5 +} by { + ; Peephole 6b - replaced jmp to rts with rts + rts +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace { + bne %1 +%1: +} by { + ; Peephole 7a - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace { + beq %1 +%1: +} by { + ; Peephole 7b - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + bra %1 +%1: +} by { + ; Peephole 7c - eliminated branch +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + bra %1 +%2: +%1: +} by { + ; Peephole 7d - eliminated branch +%2: +%1: +} if labelRefCountChange(%1 -1) + +replace { + cmp #%2 + beq %1 +} by { + ; Peephole 10a - combined cmp/beq + cbeqa #%2,%1 +} + +replace { + cmp *%2 + beq %1 +} by { + ; Peephole 10b - combined cmp/beq + cbeq *%2,%1 +} + +replace { + dec %2,s + bne %1 +} by { + ; Peephole 10c - combined dec/bne + dbnz %2,s,%1 +} + +// should be one of the last peepholes +replace restart { +%1: +} by { + ; Peephole 500 removed redundant label %1 +} if labelRefCount(%1 0) + +replace restart { + jsr %1 + rts +} by { + ; Peephole 501 did tail call optimization. + jmp %1 +} if optimizeReturn() + diff --git a/src/hc08/ralloc.c b/src/hc08/ralloc.c new file mode 100644 index 0000000..d6f9573 --- /dev/null +++ b/src/hc08/ralloc.c @@ -0,0 +1,2423 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. 68HC08 specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +extern void genhc08Code (iCode *); + +#define D(x) + +// Build the old allocator. It can be used by command-line options +#define OLDRALLOC 1 + +/* Global data */ +static struct + { + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + } +_G; + +/* Shared with gen.c */ +int hc08_ptrRegReq; /* one byte pointer register required */ + +/* 6808 registers */ +reg_info regshc08[] = +{ + + {REG_GPR, A_IDX, "a", HC08MASK_A, NULL, 0, 1}, + {REG_GPR, X_IDX, "x", HC08MASK_X, NULL, 0, 1}, + {REG_GPR, H_IDX, "h", HC08MASK_H, NULL, 0, 1}, + {REG_PTR, HX_IDX, "hx", HC08MASK_HX, NULL, 0, 1}, + {REG_GPR, XA_IDX, "xa", HC08MASK_XA, NULL, 0, 1}, + + {REG_CND, CND_IDX, "C", 0, NULL, 0, 1}, + {0, SP_IDX, "sp", 0, NULL, 0, 1}, +}; +int hc08_nRegs = 7; + +reg_info *hc08_reg_a; +reg_info *hc08_reg_x; +reg_info *hc08_reg_h; +reg_info *hc08_reg_hx; +reg_info *hc08_reg_xa; +reg_info *hc08_reg_sp; + +static void spillThis (symbol *); +static void freeAllRegs (); + +/*-----------------------------------------------------------------*/ +/* hc08_regWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +hc08_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + if (regshc08[i].rIdx == idx) + return ®shc08[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* hc08_freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +void +hc08_freeReg (reg_info * reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "hc08_freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; + + switch (reg->rIdx) + { + case A_IDX: + if (hc08_reg_x->isFree) + hc08_reg_xa->isFree = 1; + break; + case X_IDX: + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + case H_IDX: + if (hc08_reg_x->isFree) + hc08_reg_hx->isFree = 1; + break; + case HX_IDX: + hc08_reg_h->isFree = 1; + hc08_reg_x->isFree = 1; + if (hc08_reg_a->isFree) + hc08_reg_xa->isFree = 1; + break; + case XA_IDX: + hc08_reg_x->isFree = 1; + hc08_reg_a->isFree = 1; + if (hc08_reg_h->isFree) + hc08_reg_hx->isFree = 1; + break; + default: + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* hc08_useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +void +hc08_useReg (reg_info * reg) +{ + reg->isFree = 0; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isFree = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isFree = 0; + break; + case HX_IDX: + hc08_reg_h->aop = NULL; + hc08_reg_h->isFree = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + break; + case XA_IDX: + hc08_reg_x->aop = NULL; + hc08_reg_x->isFree = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isFree = 0; + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* hc08_dirtyReg - marks a register as dirty */ +/*-----------------------------------------------------------------*/ +void +hc08_dirtyReg (reg_info * reg, bool freereg) +{ + reg->aop = NULL; + + switch (reg->rIdx) + { + case A_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + case X_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case H_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + break; + case HX_IDX: + hc08_reg_hx->aop = NULL; + hc08_reg_hx->isLitConst = 0; + hc08_reg_h->aop = NULL; + hc08_reg_h->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + break; + case XA_IDX: + hc08_reg_xa->aop = NULL; + hc08_reg_xa->isLitConst = 0; + hc08_reg_x->aop = NULL; + hc08_reg_x->isLitConst = 0; + hc08_reg_a->aop = NULL; + hc08_reg_a->isLitConst = 0; + break; + default: + break; + } + if (freereg) + hc08_freeReg(reg); +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) + { + if (bitVectBitValue(sym->clashes,fsym->key)) return 0; + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + struct dbuf_s dbuf; + int useXstack, model; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE(sloc->etype) = 0; + SPEC_ABSA(sloc->etype) = 0; + + /* we don't allow it to be allocated + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + */ + + useXstack = options.useXstack; + model = options.model; +/* noOverlay = options.noOverlay; */ +/* options.noOverlay = 1; */ + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; +/* options.noOverlay = noOverlay; */ + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it as spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + hc08_freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + // update the registers in use at the start of this icode + for (reg=0; regriu &= ~(regshc08[reg].mask); + } + else + { + ic->riu |= (regshc08[reg].mask); + } + } +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode * ic) +{ + symbol * sym; + + if (!op) return; + if (!IS_ITEMP (op)) return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) return; + if (!sym->nRegs) return; + if (sym->regs[0]) return; + + /* Don't warn for new allocator, since this is not used by default (until Thoruop is implemented for spillocation compaction). */ + /*if (z80_opts.oldralloc) + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name);*/ + + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + spillThis (sym); + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + { + continue; + } + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +hc08_rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (hc08_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (hc08_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_JTCOND (ic))); + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, hc08_rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rSurv = newBitVect(port->num_regs); + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (hc08_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "createRegMask cannot find live range"); + fprintf(stderr, "\tmissing live range: key=%d\n", j); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + { + if (!sym->regs[k]) + continue; + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + if (sym->liveTo != ic->key) + ic->rSurv = bitVectSetBit (ic->rSurv, sym->regs[k]->rIdx); + } + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (eBBlock *ebbs) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : + getSize (sym->type)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && IS_PTR (sym->type) && sym->uptr) + sym->regType = REG_PTR; + else + sym->regType = REG_GPR; + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +static void +freeAllRegs () +{ + int i; + + for (i = 0; i < hc08_nRegs; i++) + { + regshc08[i].isFree = 1; + regshc08[i].aop = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + + +#if 0 +static void +packRegsForLiteral (iCode * ic) +{ + int k; + iCode *uic; + + if (ic->op != '=') + return; + if (POINTER_SET (ic)) + return; + if (!IS_LITERAL (getSpec (operandType (IC_RIGHT (ic))))) + return; + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + for (k=0; k< OP_USES (IC_RESULT (ic))->size; k++) + if (bitVectBitValue (OP_USES (IC_RESULT (ic)), k)) + { + uic = hTabItemWithKey (iCodehTab, k); + if (!uic) continue; + + if (uic->op != IFX && uic->op != JUMPTABLE) + { + if (IC_LEFT (uic) && IC_LEFT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_LEFT(uic), IC_RIGHT(ic)); + if (IC_RIGHT (uic) && IC_RIGHT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RIGHT(uic), IC_RIGHT(ic)); + if (IC_RESULT (uic) && IC_RESULT (uic)->key == IC_RESULT (ic)->key) + ReplaceOpWithCheaperOp(&IC_RESULT(uic), IC_RIGHT(ic)); + } + } + +} +#endif + + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || + OP_SYMBOL (IC_RIGHT (ic))->isind || + OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ +#if 0 + if (isOperandInFarSpace(IC_RESULT(ic)) && !farSpacePackable(ic)) + { + return 0; + } +#endif + + /* find the definition of iTempNN scanning backwards if we find + a use of the true symbol before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + int crossedCall = 0; + + /* We can pack across a function call only if it's a local */ + /* variable or our parameter. Never pack global variables */ + /* or parameters to a function we call. */ + if ((dic->op == CALL || dic->op == PCALL)) + { + if (!OP_SYMBOL (IC_RESULT (ic))->ismyparm + && !OP_SYMBOL (IC_RESULT (ic))->islocal) + { + crossedCall = 1; + } + } + + /* Don't move an assignment out of a critical block */ + if (dic->op == CRITICAL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || + IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + + if (crossedCall) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + // TODO: and the otherway around? + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/*------------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*------------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + /* This routine is used to find sequences like + iTempAA = FOO; + ...; (intervening ops don't use iTempAA or modify FOO) + blah = blah + iTempAA; + + and eliminate the use of iTempAA, freeing up its register for + other uses. + */ + for (dic = ic->prev; dic; dic = dic->prev) + { + if (dic->op == '=' && + !POINTER_SET (dic) && + IC_RESULT (dic)->key == op->key + && IS_TRUE_SYMOP(IC_RIGHT(dic)) + ) + break; /* found where this temp was defined */ + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + if (!dic) + return NULL; /* didn't find any assignment to op */ + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then don't repack regs */ + if (!IS_SYMOP (IC_RIGHT (dic))) + return NULL; + + /* if the symbol's address has been taken, there might be a */ + /* non-obvious assignment to it, and so we should not */ + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken) + return NULL; + + /* if the symbol is volatile then we should not */ + if (isOperandVolatile (IC_RIGHT (dic), TRUE)) + return NULL; + /* XXX TODO --- should we be passing FALSE to isOperandVolatile()? + What does it mean for an iTemp to be volatile, anyway? Passing + TRUE is more cautious but may prevent possible optimizations */ + + /* if the symbol is in far space then we should not */ + /* if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; */ + + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; +} + +/*-----------------------------------------------------------------*/ +/* reassignAliasedSym - used by packRegsForSupport to replace */ +/* redundant iTemp with equivalent symbol */ +/*-----------------------------------------------------------------*/ +static void +reassignAliasedSym (eBBlock *ebp, iCode *assignment, iCode *use, operand *op) +{ + iCode *ic; + unsigned oldSymKey, newSymKey; + + oldSymKey = op->key; + newSymKey = IC_RIGHT(assignment)->key; + + /* only track live ranges of compiler-generated temporaries */ + if (!IS_ITEMP(IC_RIGHT(assignment))) + newSymKey = 0; + + /* update the live-value bitmaps */ + for (ic = assignment; ic != use; ic = ic->next) { + bitVectUnSetBit (ic->rlive, oldSymKey); + if (newSymKey != 0) + ic->rlive = bitVectSetBit (ic->rlive, newSymKey); + } + + /* update the sym of the used operand */ + OP_SYMBOL(op) = OP_SYMBOL(IC_RIGHT(assignment)); + op->key = OP_SYMBOL(op)->key; + + /* update the sym's liverange */ + if ( OP_LIVETO(op) < ic->seq ) + setToRange(op, ic->seq, FALSE); + + /* remove the assignment iCode now that its result is unused */ + remiCodeFromeBBlock (ebp, assignment); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(assignment))->defs, assignment->key); + hTabDeleteItem (&iCodehTab, assignment->key, assignment, DELETE_ITEM, NULL); +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + int changes = 0; + + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + dic = findAssignToSym (IC_LEFT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_LEFT(ic)); + changes++; + } + } + + /* do the same for the right operand */ + if (IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_RIGHT(ic)); + changes++; + } + } + + return changes; +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* only up to 2 bytes */ + if (getSize (operandType (op)) > (fReturnSizeHC08 - 2)) + return NULL; + + return NULL; + + if (ic->op != SEND && //RETURN + ic->op != SEND && + !POINTER_SET (ic) && + !POINTER_GET (ic)) + return NULL; + + if (ic->op == SEND && ic->argreg != 1) + return NULL; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* if that only usage is a cast */ + if (dic->op == CAST) + { + /* to a bigger type */ + if (getSize(OP_SYM_TYPE(IC_RESULT(dic))) > getSize(OP_SYM_TYPE(IC_RIGHT(dic)))) + { + /* then we can not, since we cannot predict the usage of b & acc */ + return NULL; + } + } + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + + +// /* otherwise check that the definition does +// not contain any symbols in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_LEFT (ic)) || +// IS_OP_RUONLY (IC_RIGHT (ic))) +// { +// return NULL; +// } + + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + + sic = dic; + + /* make sure the intervening instructions + don't have anything in far space */ + for (dic = dic->next; dic && dic != ic && sic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ +#if 0 + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; +#endif + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ +#if 0 + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 3) + return NULL; +#endif + + /* if left or right or result is in far space */ +// if (isOperandInFarSpace (IC_LEFT (dic)) || +// isOperandInFarSpace (IC_RIGHT (dic)) || +// isOperandInFarSpace (IC_RESULT (dic)) || +// IS_OP_RUONLY (IC_LEFT (dic)) || +// IS_OP_RUONLY (IC_RIGHT (dic)) || +// IS_OP_RUONLY (IC_RESULT (dic))) +// { +// return NULL; +// } +// /* if left or right or result is on stack */ +// if (isOperandOnStack(IC_LEFT(dic)) || +// isOperandOnStack(IC_RIGHT(dic)) || +// isOperandOnStack(IC_RESULT(dic))) { +// return NULL; +// } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} +#endif + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL(rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* isCommutativeOp - tests whether this op cares what order its */ +/* operands are in */ +/*-----------------------------------------------------------------*/ +bool isCommutativeOp2(unsigned int op) +{ + if (op == '+' || op == '*' || op == EQ_OP || + op == '^' || op == '|' || op == BITWISEAND) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandUsesAcc2 - determines whether the code generated for this */ +/* operand will have to use the accumulator */ +/*-----------------------------------------------------------------*/ +bool operandUsesAcc2(operand *op) +{ + if (!op) + return FALSE; + + if (IS_SYMOP(op)) { + symbol *sym = OP_SYMBOL(op); + memmap *symspace; + + if (sym->accuse) + return TRUE; /* duh! */ + + if (IS_ITEMP(op)) + { + if (SPIL_LOC(op)) { + sym = SPIL_LOC(op); /* if spilled, look at spill location */ + } else { + return FALSE; /* more checks? */ + } + } + + symspace = SPEC_OCLS(sym->etype); + + if (IN_BITSPACE(symspace)) + return TRUE; /* fetching bit vars uses the accumulator */ + + if (IN_FARSPACE(symspace) || IN_CODESPACE(symspace)) + return TRUE; /* fetched via accumulator and dptr */ + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* canDefAccResult - return 1 if the iCode can generate a result */ +/* in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canDefAccResult (iCode * ic) +{ + int size; + + if (ic->op == IFX || ic->op == JUMPTABLE) /* these iCodes have no result */ + return 0; + + if (POINTER_SET (ic)) + return 0; + + if (!IC_RESULT (ic)) + return 0; + + if (!IS_ITEMP (IC_RESULT (ic))) + return 0; + + /* I don't think an iTemp can be an aggregate, but just in case */ + if (IS_AGGREGATE(operandType(IC_RESULT(ic)))) + return 0; + + size = getSize (operandType (IC_RESULT (ic))); + + if (size == 1) + { + /* All 1 byte operations should safely generate an accumulator result */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)) + && SPEC_USIGN (operandType (IC_RESULT (ic))); + case CALL: + case PCALL: + case '*': + case RECEIVE: + case '=': /* assignment, since POINTER_SET is already ruled out */ + return 1; + + default: + return 0; + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* canUseAccOperand - return 1 if the iCode can use the operand */ +/* when passed in A or XA */ +/*-----------------------------------------------------------------*/ +static int +canUseAccOperand (iCode * ic, operand * op) +{ + int size; + + if (ic->op == IFX) + { + if (isOperandEqual (op, IC_COND (ic))) + return 1; + else + return 0; + } + + if (ic->op == JUMPTABLE) + { + if (isOperandEqual (op, IC_JTCOND (ic))) + return 1; + else + return 0; + } + + if (POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) + return 1; + + if (!isOperandEqual (op, IC_LEFT (ic)) && !isOperandEqual (op, IC_RIGHT (ic))) + return 0; + + /* Generation of SEND is deferred until CALL; not safe */ + /* if there are intermediate iCodes */ + if (ic->op == SEND && ic->next && ic->next->op != CALL) + return 0; + + size = getSize (operandType (op)); + if (size == 1) + { + /* All 1 byte operations should safely use an accumulator operand */ + return 1; + } + else if (size == 2) + { + switch (ic->op) + { + case LEFT_OP: + case RIGHT_OP: + return isOperandLiteral (IC_RIGHT (ic)); + case SEND: + return 1; + default: + return 0; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAccUse (iCode * ic) +{ + iCode * uic; + operand * op; + + if (!canDefAccResult (ic)) + return 0; + + op = IC_RESULT (ic); + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return 0; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (op)) > 1) + return 0; + + uic = ic->next; + if (!uic) + return 0; + + if (!canUseAccOperand (uic, op)) + return 0; + + #if 0 + if ((POINTER_GET(uic)) + || (ic->op == ADDRESS_OF && uic->op == '+' && IS_OP_LITERAL (IC_RIGHT (uic)))) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_HX; + return; + } + #endif + + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_XA; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* packForPush - heuristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock ** ebpp, int blockno) +{ + iCode *dic, *lic; + bitVect *dbv; + struct eBBlock * ebp=ebpp[blockno]; + int disallowHiddenAssignment = 0; + + if ((ic->op != IPUSH && ic->op != SEND) || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + if (dic->seq < ebp->fSeq) // Evelyn did this + { + int i; + for (i=0; iseq >= ebpp[i]->fSeq && dic->seq <= ebpp[i]->lSeq) + { + ebp=ebpp[i]; + break; + } + } + wassert (i!=blockno); // no way to recover from here + } + + if (IS_SYMOP(IC_RIGHT(dic))) + { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS(IC_RIGHT(dic)); + for (lic = ic; lic && lic != dic ; lic = lic->prev) + { + if (bitVectBitValue(dbv,lic->key)) + return ; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC(operandType(IC_LEFT(ic)))) + { + sym_link *itype=operandType(IC_LEFT(ic)); + sym_link *ditype=operandType(IC_RIGHT(dic)); + + if (SPEC_USIGN(itype)!=SPEC_USIGN(ditype) || + SPEC_LONG(itype)!=SPEC_LONG(ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL(IC_RIGHT(dic))->liveTo < ic->seq) + { + OP_SYMBOL(IC_RIGHT(dic))->liveTo = ic->seq; + } + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + ReplaceOpWithCheaperOp(&IC_LEFT (ic), IC_RIGHT (dic)); + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*------------------------------------------------------------------*/ +/* moveSendToCall - move SEND to immediately precede its CALL/PCALL */ +/*------------------------------------------------------------------*/ +static iCode * +moveSendToCall (iCode *sic, eBBlock *ebp) +{ + iCode * prev = sic->prev; + iCode * sic2 = NULL; + iCode * cic; + + /* Go find the CALL/PCALL */ + cic = sic; + while (cic && cic->op != CALL && cic->op != PCALL) + cic = cic->next; + if (!cic) + return sic; + + /* Is there a second SEND? If so, we'll need to move it too. */ + if (sic->next->op == SEND) + sic2 = sic->next; + + /* relocate the SEND(s) */ + remiCodeFromeBBlock (ebp, sic); + addiCodeToeBBlock (ebp, sic, cic); + if (sic2) + { + remiCodeFromeBBlock (ebp, sic2); + addiCodeToeBBlock (ebp, sic2, cic); + } + + /* Return the iCode to continue processing at. */ + if (prev) + return prev->next; + else + return ebp->sch; +} + + +/*---------------------------------------------------------------------*/ +/* packPointerOp - see if we can move an offset from addition iCode */ +/* to the pointer iCode to used indexed addr mode */ +/* The z80-related ports do a similar thing in SDCCopt.c, offsetFold() */ +/*---------------------------------------------------------------------*/ +static void +packPointerOp (iCode * ic, eBBlock ** ebpp) +{ + operand * pointer; + operand * offsetOp; + operand * nonOffsetOp; + iCode * dic; + iCode * uic; + int key; + + if (POINTER_SET (ic)) + { + pointer = IC_RESULT (ic); + offsetOp = IC_LEFT (ic); + } + else if (POINTER_GET (ic)) + { + pointer = IC_LEFT (ic); + offsetOp = IC_RIGHT (ic); + } + else + return; + + if (!IS_ITEMP (pointer)) + return; + + /* If the pointer is rematerializable, it's already fully optimized */ + if (OP_SYMBOL (pointer)->remat) + return; + + if (offsetOp && IS_OP_LITERAL (offsetOp) && operandLitValue (offsetOp) != 0) + return; + if (offsetOp && IS_SYMOP (offsetOp)) + return; + + /* There must be only one definition */ + if (bitVectnBitsOn (OP_DEFS (pointer)) != 1) + return; + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (pointer))))) + return; + + if (dic->op == '+' && (IS_OP_LITERAL (IC_RIGHT (dic)) || + (IS_ITEMP (IC_RIGHT (dic)) && OP_SYMBOL (IC_RIGHT (dic))->remat))) + { + nonOffsetOp = IC_LEFT (dic); + offsetOp = IC_RIGHT (dic); + } + else if (dic->op == '+' && IS_ITEMP (IC_LEFT (dic)) && OP_SYMBOL (IC_LEFT (dic))->remat) + { + nonOffsetOp = IC_RIGHT (dic); + offsetOp = IC_LEFT (dic); + } + else + return; + + + /* Now check all of the uses to make sure they are all get/set pointer */ + /* and don't already have a non-zero offset operand */ + for (key=0; keysize; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + if (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)) && operandLitValue (IC_RIGHT (uic)) != 0) + return; + if (IC_RIGHT (uic) && IS_SYMOP (IC_RIGHT (uic))) + return; + } + else if (POINTER_SET (uic)) + { + if (IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic)) && operandLitValue (IC_LEFT (uic)) != 0) + return; + if (IC_LEFT (uic) && IS_SYMOP (IC_LEFT (uic))) + return; + } + else + return; + } + } + + /* Everything checks out. Move the literal or rematerializable offset */ + /* to the pointer get/set iCodes */ + for (key=0; keysize; key++) + { + if (bitVectBitValue (OP_USES (pointer), key)) + { + uic = hTabItemWithKey (iCodehTab, key); + if (POINTER_GET (uic)) + { + IC_RIGHT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else if (POINTER_SET (uic)) + { + IC_LEFT (uic) = offsetOp; + if (IS_SYMOP (offsetOp)) + OP_USES (offsetOp) = bitVectSetBit (OP_USES (offsetOp), key); + } + else + return; + } + } + + /* Put the remaining operand on the right and convert to assignment */ + if (IS_SYMOP (offsetOp)) + bitVectUnSetBit (OP_USES (offsetOp), dic->key); + IC_RIGHT (dic) = nonOffsetOp; + IC_LEFT (dic) = NULL; + SET_ISADDR (IC_RESULT (dic), 0); + dic->op = '='; +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock ** ebpp, int blockno) +{ + iCode *ic; + int change = 0; + eBBlock *ebp = ebpp[blockno]; + + do + { + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TrueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + } + while (change); + + for (ic = ebp->sch; ic; ic = ic->next) + { + //packRegsForLiteral (ic); + + /* move SEND to immediately precede its CALL/PCALL */ + if (ic->op == SEND && ic->next && + ic->next->op != CALL && ic->next->op != PCALL) + { + ic = moveSendToCall (ic, ebp); + } + + /* if this is an itemp & result of an address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP(IC_RIGHT(ic)) && + OP_SYMBOL(IC_RIGHT(ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken && + !isOperandGlobal (IC_RESULT (ic))) + { + sym_link *to_type = operandType(IC_LEFT(ic)); + sym_link *from_type = operandType(IC_RIGHT(ic)); + if (IS_PTR(to_type) && IS_PTR(from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE(OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + /* reduce for support function calls */ + if (ic->supportRtn || (ic->op != IFX && ic->op != JUMPTABLE)) + packRegsForSupport (ic, ebp); + + /* if the condition of an if instruction + is defined in the previous instruction and + this is the only usage then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + (IS_BITWISE_OP(ic) && isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES(IC_RESULT(ic)))==1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH || ic->op == SEND) + { + packForPush (ic, ebpp, blockno); + } + + if (POINTER_SET (ic) || POINTER_GET (ic)) + packPointerOp (ic, ebpp); + + if (options.oldralloc) + packRegsForAccUse (ic); + } +} + +static void +RegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + deassignLRs (ic, ebbs[i]); + + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +static void +replaceAccuseOperand (operand * op) +{ + symbol * sym; + + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->remat || !sym->accuse) + return; + + sym->nRegs = getSize (operandType (op)); + wassert ((sym->accuse == ACCUSE_XA) || (sym->accuse == ACCUSE_HX)); + wassert (sym->nRegs <= 2); + + if (sym->accuse == ACCUSE_XA) + { + sym->regs[0] = hc08_reg_a; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_x; + } + else /* must be sym->accuse == ACCUSE_HX */ + { + sym->regs[0] = hc08_reg_x; + if (sym->nRegs > 1) + sym->regs[1] = hc08_reg_h; + } + sym->accuse = 0; +} + + +/* Replace all uses of the sym->accuse flag with initialized */ +/* sym->regs[] and sym->nRegs so the current code generator */ +/* can work with the old register allocator. */ +static void +replaceAccuse (eBBlock ** ebbs, int count) +{ + int i; + + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + replaceAccuseOperand (IC_COND (ic)); + continue; + } + + if (ic->op == JUMPTABLE) + { + replaceAccuseOperand (IC_JTCOND (ic)); + continue; + } + + replaceAccuseOperand (IC_RESULT (ic)); + replaceAccuseOperand (IC_LEFT (ic)); + replaceAccuseOperand (IC_RIGHT (ic)); + } + } +} + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* Old, obsolete register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_oldralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + freeAllRegs (); + //setToNull ((void *) &_G.regAssigned); + //setToNull ((void *) &_G.totRegAssigned); +#if 0 + fillGaps(); +#endif + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* Convert the old sym->accuse flag into normal register assignments */ + replaceAccuse (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} +#endif + +/** Serially allocate registers to the variables. + This was the main register allocation function. It is called after + packing. + In the new register allocator it only serves to mark variables for the new register allocator. + */ +static void +serialRegMark (eBBlock ** ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage(ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + { + continue; + } + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + + if (sym->remat) + { + spillThis (sym); + continue; + } + + if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + spillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} +/*-----------------------------------------------------------------*/ +/* New register allocator */ +/*-----------------------------------------------------------------*/ +void +hc08_ralloc (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + hc08_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + hc08_nRegs = 7; + hc08_reg_a = hc08_regWithIdx(A_IDX); + hc08_reg_x = hc08_regWithIdx(X_IDX); + hc08_reg_h = hc08_regWithIdx(H_IDX); + hc08_reg_hx = hc08_regWithIdx(HX_IDX); + hc08_reg_xa = hc08_regWithIdx(XA_IDX); + hc08_reg_sp = hc08_regWithIdx(SP_IDX); + hc08_nRegs = 5; + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegMark (ebbs, count); + + /* The new register allocator invokes its magic */ + ic = hc08_ralloc2_cc (ebbi); + + RegFix (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + genhc08Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +hc08_assignRegisters (ebbIndex * ebbi) +{ +#ifdef OLDRALLOC + if (options.oldralloc) + hc08_oldralloc (ebbi); + else +#endif + hc08_ralloc (ebbi); +} + diff --git a/src/hc08/ralloc.h b/src/hc08/ralloc.h new file mode 100644 index 0000000..2dfba64 --- /dev/null +++ b/src/hc08/ralloc.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum + { + A_IDX, + X_IDX, + H_IDX, + HX_IDX, + XA_IDX, + CND_IDX, + SP_IDX + }; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_CND 0x04 + +/* Must preserve the relation HC08MASK_H > HC08MASK_X > MC08MASK_A */ +/* so that HC08MASK_REV can be automatically applied when reversing */ +/* the usual register pair ordering. */ +#define HC08MASK_A 0x01 +#define HC08MASK_X 0x02 +#define HC08MASK_H 0x04 +#define HC08MASK_REV 0x08 +#define HC08MASK_XA (HC08MASK_X | HC08MASK_A) +#define HC08MASK_HX (HC08MASK_H | HC08MASK_X) +#define HC08MASK_AX (HC08MASK_REV | HC08MASK_X | HC08MASK_A) + +/* definition for the registers */ +typedef struct reg_info + { + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ + short mask; /* bitmask for pair allocation */ + struct asmop *aop; /* last operand */ + int aopofs; /* last operand offset */ + unsigned isFree:1; /* is currently unassigned */ + unsigned isDead:1; /* does not need to survive current instruction */ + unsigned isLitConst:1; /* has an literal constant loaded */ + int litConst; /* last literal constant */ + } +reg_info; +extern reg_info regshc08[]; +extern reg_info *hc08_reg_a; +extern reg_info *hc08_reg_x; +extern reg_info *hc08_reg_h; +extern reg_info *hc08_reg_hx; +extern reg_info *hc08_reg_xa; +extern reg_info *hc08_reg_sp; + +reg_info *hc08_regWithIdx (int); +void hc08_useReg (reg_info * reg); +void hc08_freeReg (reg_info * reg); +void hc08_dirtyReg (reg_info * reg, bool freereg); +bitVect *hc08_rUmaskForOp (operand * op); + +iCode *hc08_ralloc2_cc(ebbIndex *ebbi); + +#endif diff --git a/src/hc08/ralloc2.cc b/src/hc08/ralloc2.cc new file mode 100644 index 0000000..9c76417 --- /dev/null +++ b/src/hc08/ralloc2.cc @@ -0,0 +1,674 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2011 +// +// (c) 2012 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +//#define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +//#define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#define TD_SALLOC +#define CH_SALLOC + +#include "SDCCralloc.hpp" + +extern "C" +{ + #include "ralloc.h" + #include "gen.h" + unsigned char dryhc08iCode (iCode *ic); + bool hc08_assignment_optimal; +} + +#define REG_A 0 +#define REG_X 1 +#define REG_H 2 + +template +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + // Todo: Identify more operations that code generation can always handle and exclude them (as done for the z80-like ports). + if (ic->op == '=') + return; + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Check that the operand is either fully in registers or fully in memory. +template +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi2, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // Go to the second byte. If the operand is only a single byte, it cannot be + // an unsupported register combination or split between register and memory. + oi2 = oi; + oi2++; + if (oi2 == oi_end) + return(true); + + // Register combinations code generation cannot handle yet (AH, XH, HA). + if(std::binary_search(a.local.begin(), a.local.end(), oi->second) && std::binary_search(a.local.begin(), a.local.end(), oi2->second)) + { + const reg_t l = a.global[oi->second]; + const reg_t h = a.global[oi2->second]; + if(l == REG_A && h == REG_H || l == REG_H) + return(false); + } + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + { + while(++oi != oi_end) + if(!std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + else + { + while(++oi != oi_end) + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + + return(true); +} + +template +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +template +static bool operand_is_ax(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(false); + + oi2 = oi; + oi2++; + if (oi2 == oi_end) + return(false); + + // Register combinations code generation cannot handle yet (AX, AH, XH, HA). + if(std::binary_search(a.local.begin(), a.local.end(), oi->second) && std::binary_search(a.local.begin(), a.local.end(), oi2->second)) + { + const reg_t l = a.global[oi->second]; + const reg_t h = a.global[oi2->second]; + if(l == REG_X && h == REG_A) + return(true); + } + + return(false); +} + +template +static bool XAinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + // Instructions that can handle anything. + if(ic->op == '!' || + ic->op == '~' || + ic->op == UNARYMINUS || + ic->op == CALL || + ic->op == PCALL || + ic->op == FUNCTION || + ic->op == ENDFUNCTION || + ic->op == RETURN || + ic->op == LABEL || + ic->op == GOTO || + ic->op == IFX || + ic->op == '+' || + ic->op == '-' || + ic->op == '*' || + ic->op == '/' || + ic->op == '%' || + ic->op == '<' || ic->op == '>' || ic->op == LE_OP || ic->op == GE_OP || + ic->op == NE_OP || ic->op == EQ_OP || + ic->op == AND_OP || + ic->op == OR_OP || + ic->op == '^' || + ic->op == '|' || + ic->op == BITWISEAND || + ic->op == GETABIT || + ic->op == GETBYTE || + ic->op == GETWORD || + ic->op == LEFT_OP || + ic->op == RIGHT_OP || + ic->op == '=' || /* both regular assignment and POINTER_SET safe */ + ic->op == GET_VALUE_AT_ADDRESS || + ic->op == ADDRESS_OF || + ic->op == CAST || + ic->op == DUMMY_READ_VOLATILE || + ic->op == SWAP) + return(true); + + if(ic->op == IFX && ic->generated) + return(true); + + const i_assignment_t &ia = a.i_assignment; + + bool unused_A = (ia.registers[REG_A][1] < 0); + bool unused_H = (ia.registers[REG_H][1] < 0); + bool unused_X = (ia.registers[REG_X][1] < 0); + + if(unused_X && unused_A && unused_H) + return(true); + +#if 0 + std::cout << "XAinst_ok: at (" << i << ", " << ic->key << ")\nX = (" << ia.registers[REG_X][0] << ", " << ia.registers[REG_X][1] << "), A = (" << ia.registers[REG_A][0] << ", " << ia.registers[REG_A][1] << ")inst " << i << ", " << ic->key << "\n"; +#endif + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_H = operand_in_reg(result, REG_H, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_X = operand_in_reg(result, REG_X, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool left_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_X = operand_in_reg(result, REG_X, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_A = result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || dying.find(ia.registers[REG_A][0]) != dying.end(); + bool dying_H = result_in_H || dying.find(ia.registers[REG_H][1]) != dying.end() || dying.find(ia.registers[REG_H][0]) != dying.end(); + bool dying_X = result_in_X || dying.find(ia.registers[REG_X][1]) != dying.end() || dying.find(ia.registers[REG_X][0]) != dying.end(); + + bool result_only_XA = (result_in_X || unused_X || dying_X) && (result_in_A || unused_A || dying_A); + + if(ic->op == JUMPTABLE && (unused_A || dying_A)) + return(true); + + if(ic->op == IPUSH && (unused_A || dying_A || left_in_A || operand_in_reg(left, REG_H, ia, i, G) || left_in_X)) + return(true); + + if(ic->op == RECEIVE && (!ic->next || !(ic->next->op == RECEIVE) || !result_in_X || getSize(operandType(result)) >= 2)) + return(true); + + if(ic->op == SEND && ic->next && ic->next->op == SEND && ic->next->next && ic->next->next->op == SEND) + return(true); + + if(ic->op == SEND && ic->next && ic->next->op == SEND && (unused_X || dying_X)) + return(true); + + if(ic->op == SEND && (unused_X || dying_X) && (unused_A || dying_A)) + return(true); + + if(ic->op == SEND && ic->next && (ic->next->op == CALL || ic->next->op == PCALL)) // Might mess up A and X, but these would have been saved before if surviving, and will not be needed again before the call. + return(true); + + if((ic->op == CRITICAL || ic->op == ENDCRITICAL) && (unused_A || dying_A)) + return(true); + + return(false); +} + +template +static bool AXinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + const i_assignment_t &ia = a.i_assignment; + + if(ic->op == '!' || + ic->op == '~' || + ic->op == IPUSH || + ic->op == CALL || + ic->op == FUNCTION || + ic->op == ENDFUNCTION || + ic->op == RETURN || + ic->op == LABEL || + ic->op == GOTO || + ic->op == '+' || + ic->op == '-' || + ic->op == NE_OP || ic->op == EQ_OP || + ic->op == '^' || + ic->op == '|' || + ic->op == BITWISEAND || + ic->op == GETABIT || + ic->op == GETBYTE || + ic->op == GETWORD || + /*ic->op == LEFT_OP || + ic->op == RIGHT_OP ||*/ + ic->op == GET_VALUE_AT_ADDRESS || + ic->op == '=' || + ic->op == ADDRESS_OF || + ic->op == RECEIVE || + ic->op == SEND || + ic->op == DUMMY_READ_VOLATILE || + ic->op == CRITICAL || + ic->op == ENDCRITICAL || + ic->op == SWAP) + return(true); + + bool unused_A = (ia.registers[REG_A][1] < 0); + bool unused_X = (ia.registers[REG_X][1] < 0); + + if (unused_A || unused_X) + return(true); + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool result_in_X = operand_in_reg(result, REG_X, ia, i, G) && !(ic->op == '=' && POINTER_SET(ic)); + bool left_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_X = operand_in_reg(result, REG_X, ia, i, G); + bool right_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool right_in_X = operand_in_reg(result, REG_X, ia, i, G); + + bool result_is_ax = operand_is_ax (result, a, i, G, I); + bool left_is_ax = operand_is_ax (left, a, i, G, I); + bool right_is_ax = operand_is_ax (right, a, i, G, I); + + if (!result_is_ax && !left_is_ax && !right_is_ax) + return(true); + + return(false); +} + +template +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + if(G[i].dying.find(*v) == G[i].dying.end()) + if(!((IC_RESULT(ic) && !POINTER_SET(ic)) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = regshc08 + a.global[v]; + sym->isspilt = false; + sym->nRegs = I[v].size; + sym->accuse = 0; + } + else + { + for(int i = 0; i < I[v].size; i++) + sym->regs[i] = 0; + sym->accuse = 0; + sym->nRegs = I[v].size; + sym->isspilt = true; + } + } +} + +template +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + { + assign_operands_for_cost(a, *(adjacent_vertices(i, G).first), G, I); + } +} + +// Cost function. +template +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert (TARGET_IS_HC08 || TARGET_IS_S08); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(!XAinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(!AXinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + +#if 0 + std::cout << "Calculating at cost at ic " << ic->key << " for: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(a.global[i]) << ") "; + } + std::cout << "\n"; + std::cout.flush(); +#endif + + if(ic->generated) + return(0.0f); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: + return(0.0f); + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + case GETABIT: + case GETBYTE: + case GETWORD: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + case RECEIVE: + case SEND: + case DUMMY_READ_VOLATILE: + case CRITICAL: + case ENDCRITICAL: + case SWAP: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = dryhc08iCode(ic); + return(c); + default: + return(0.0f); + } +} + +// For early removal of assignments that cannot be extended to valid assignments. This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T) +{ + a = *T[t].assignments.begin(); + + std::set::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +// This is just a dummy for now, it probably isn't really needed for hc08 due to the low number of registers. +template +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + return(0.0f); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + if(ic->op == '>' || ic->op == '<' || ic->op == LE_OP || ic->op == GE_OP || ic->op == EQ_OP || ic->op == NE_OP || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) + { + iCode *ifx; + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } + if(ic->op == '-' && IS_VALOP (IC_RIGHT (ic)) && operandLitValue (IC_RIGHT (ic)) == 1 && getSize(operandType(IC_RESULT (ic))) == 1 && !isOperandInFarSpace (IC_RESULT (ic)) && isOperandEqual (IC_RESULT (ic), IC_LEFT (ic))) + { + iCode *ifx; + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } + if(ic->op == GET_VALUE_AT_ADDRESS) + { + iCode *inc; + if (inc = hasInchc08 (IC_LEFT (ic), ic, getSize (operandType (IC_RIGHT (ic))))) + inc->generated = true; + } +} + +template +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + const assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + if(winner.global[v] >= 0) + { + sym->regs[I[v].byte] = regshc08 + winner.global[v]; + sym->isspilt = false; + sym->nRegs = I[v].size; + sym->accuse = 0; + } + else + { + for(int i = 0; i < I[v].size; i++) + sym->regs[i] = 0; + sym->accuse = 0; + sym->nRegs = I[v].size; + wassert (sym->nRegs); + //spillThis(sym); Leave it to regFix, which can do some spillocation compaction. Todo: Use Thorup instead. + sym->isspilt = false; + } + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + return(!assignment_optimal); +} + +iCode *hc08_ralloc2_cc(ebbIndex *ebbi) +{ + iCode *ic; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + hc08_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph); + + return(ic); +} + diff --git a/src/mcs51/Makefile b/src/mcs51/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/mcs51/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/mcs51/Makefile.in b/src/mcs51/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/mcs51/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/mcs51/gen.c b/src/mcs51/gen.c new file mode 100644 index 0000000..b3ebf12 --- /dev/null +++ b/src/mcs51/gen.c @@ -0,0 +1,12371 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for 8051 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +#define D(x) do if (options.verboseAsm) {x;} while(0) + +#include +#include +#include +#include + +#include "common.h" +#include "ralloc.h" +#include "rtrack.h" +#include "gen.h" +#include "dbuf_string.h" + +char *aopLiteralGptr (const char *name, value * val); +extern int allocInfo; + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ + +static char *zero = "#0x00"; +static char *one = "#0x01"; +static char *spname; + +char *fReturn8051[] = { "dpl", "dph", "b", "a", "r4", "r5", "r6", "r7" }; + +unsigned fReturnSizeMCS51 = 4; /* shared with ralloc.c */ +char **fReturn = fReturn8051; +static char *accUse[] = { "a", "b" }; + +static short rbank = -1; + +#define REG_WITH_INDEX mcs51_regWithIdx + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define IS_AOP_PREG(x) (AOP(x) && (AOP_TYPE(x) == AOP_R1 || \ + AOP_TYPE(x) == AOP_R0)) + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || \ + AOP_TYPE(x) == AOP_DPTR || \ + AOP(x)->paged)) + +#define AOP_INPREG(x) (x && (x->type == AOP_REG && \ + (x->aopu.aop_reg[0] == REG_WITH_INDEX(R0_IDX) || \ + x->aopu.aop_reg[0] == REG_WITH_INDEX(R1_IDX) ))) + +#define IS_AOP_IMMEDIATE(x) (AOP(x) && (AOP_TYPE(x) == AOP_LIT || \ + AOP_TYPE(x) == AOP_IMMD || \ + AOP_TYPE(x) == AOP_STR)) + +#define SP_BP(sp, bp) (options.omitFramePtr ? sp : bp) +#define SYM_BP(sym) (SPEC_OCLS (sym->etype)->paged ? SP_BP("_spx", "_bpx") : SP_BP("sp", "_bp")) + +#define EQ(a, b) (strcmp (a, b) == 0) + +#define R0INB _G.bu.bs.r0InB +#define R1INB _G.bu.bs.r1InB +#define OPINB _G.bu.bs.OpInB +#define BITSINB _G.bu.bs.bitsInB +#define BINUSE _G.bu.BInUse + +static struct +{ + short r0Pushed; + short r1Pushed; + union + { + struct + { + short r0InB: 2; //2 so we can see it overflow + short r1InB: 2; //2 so we can see it overflow + short OpInB: 2; //2 so we can see it overflow + short bitsInB: 2; //2 so we can see it overflow + } bs; + short BInUse; + } bu; + short accInUse; + struct + { + int pushed; + int pushedregs; + int offset; + int param_offset; + int xpushed; + int xpushedregs; + int xoffset; + } stack; + set *sendSet; + symbol *currentFunc; +} +_G; + +static char *rb1regs[] = +{ + "b1_0", "b1_1", "b1_2", "b1_3", "b1_4", "b1_5", "b1_6", "b1_7", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7" +}; + +extern struct dbuf_s *codeOutBuf; + +#define RESULTONSTACK(x) \ + (IC_RESULT (x) && IC_RESULT (x)->aop && \ + IC_RESULT (x)->aop->type == AOP_STK ) + +#define MOVA(x) mova (x) /* use function to avoid multiple eval */ +#define MOVB(x) movb (x) + +#define CLRC emitcode ("clr","c") +#define SETC emitcode ("setb","c") + +static unsigned char SLMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00 + }; + +static unsigned char SRMask[] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x03, 0x01, 0x00 + }; + +#define MAX_REGISTER_BANKS 4 + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +/*-----------------------------------------------------------------*/ +/* mcs51_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +mcs51_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emitcode ("", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + +/*-----------------------------------------------------------------*/ +/* mova - moves specified value into accumulator */ +/*-----------------------------------------------------------------*/ +static void +mova (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "a", 2) || !strncmp (x, "acc", 4)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#' && rtrackMoveALit (x)) + return; + + /* another early peephole optimization */ + if (EQ (x, "#0x00")) + { + emitcode ("clr", "a"); + return; + } + + emitcode ("mov", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* movb - moves specified value into register b */ +/*-----------------------------------------------------------------*/ +static void +movb (const char *x) +{ + /* do some early peephole optimization */ + if (!strncmp (x, "b", 2)) + return; + + /* if it is a literal mov try to get it cheaper */ + if (*x == '#') + { + emitcode ("mov", "b,%s", rtrackGetLit (x)); + return; + } + + emitcode ("mov", "b,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* emitpush - push something on internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpush (const char *arg) +{ + char buf[] = "ar?"; + + _G.stack.pushed++; + if (!arg) + { + emitcode ("inc", "sp"); + return; + } + else if (EQ (arg, "a")) + { + arg = "acc"; + } + else if ((*arg == '@') || (*arg == '#')) + { + MOVA (arg); + arg = "acc"; + } + else if (EQ (arg, "r0") || EQ (arg, "r1") || EQ (arg, "r2") || EQ (arg, "r3") || + EQ (arg, "r4") || EQ (arg, "r5") || EQ (arg, "r6") || EQ (arg, "r7")) + { + buf[2] = arg[1]; + arg = buf; + } + emitcode ("push", arg); +} + +/*-----------------------------------------------------------------*/ +/* emitpop - pop something from internal stack */ +/*-----------------------------------------------------------------*/ +static void +emitpop (const char *arg) +{ + if (!arg) + emitcode ("dec", "sp"); + else + emitcode ("pop", arg); + _G.stack.pushed--; + wassertl (_G.stack.pushed >= 0, "stack underflow"); +} + +/*-----------------------------------------------------------------*/ +/* pushB - saves register B if necessary */ +/*-----------------------------------------------------------------*/ +static bool +pushB (void) +{ + bool pushedB = FALSE; + + if (BINUSE) + { + emitpush ("b"); +// printf("B was in use !\n"); + pushedB = TRUE; + } + else + { + OPINB++; + } + return pushedB; +} + +/*-----------------------------------------------------------------*/ +/* popB - restores value of register B if necessary */ +/*-----------------------------------------------------------------*/ +static void +popB (bool pushedB) +{ + if (pushedB) + { + emitpop ("b"); + } + else + { + OPINB--; + } +} + +/*-----------------------------------------------------------------*/ +/* pushReg - saves register */ +/*-----------------------------------------------------------------*/ +static bool +pushReg (int index, bool bits_pushed) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_pushed) + emitpush (reg->base); + return TRUE; + } + else + emitpush (reg->dname); + return bits_pushed; +} + +/*-----------------------------------------------------------------*/ +/* popReg - restores register */ +/*-----------------------------------------------------------------*/ +static bool +popReg (int index, bool bits_popped) +{ + const reg_info *reg = REG_WITH_INDEX (index); + if (reg->type == REG_BIT) + { + if (!bits_popped) + emitpop (reg->base); + return TRUE; + } + else + emitpop (reg->dname); + return bits_popped; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* showR0R1status - helper for debugging getFreePtr failures */ +/*-----------------------------------------------------------------*/ +static void +showR0R1status(iCode * ic) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + printf ("ic->rUsed = ["); + if (r0iu) + if (r1iu) + printf("r0,r1"); + else + printf("r0"); + else + if (r1iu) + printf("r1"); + printf("] "); + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + printf ("ic->rMask = ["); + if (r0ou) + if (r1ou) + printf("r0,r1"); + else + printf("r0"); + else + if (r1ou) + printf("r1"); + printf("]\n"); +} +#endif + +/*-----------------------------------------------------------------*/ +/* getFreePtr - returns r0 or r1 whichever is free or can be pushed */ +/*-----------------------------------------------------------------*/ +static reg_info * +getFreePtr (iCode * ic, asmop * aop, bool result) +{ + bool r0iu, r1iu; + bool r0ou, r1ou; + + /* the logic: if r0 & r1 used in the instruction + then we are in trouble otherwise */ + + /* first check if r0 & r1 are used by this + instruction, in which case we are in trouble */ + r0iu = bitVectBitValue (ic->rUsed, R0_IDX); + r1iu = bitVectBitValue (ic->rUsed, R1_IDX); + if (r0iu && r1iu) + { + goto endOfWorld; + } + + r0ou = bitVectBitValue (ic->rMask, R0_IDX); + r1ou = bitVectBitValue (ic->rMask, R1_IDX); + + /* if no usage of r0 then return it */ + if (!r0iu && !r0ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if no usage of r1 then return it */ + if (!r1iu && !r1ou) + { + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R1_IDX); + } + + /* now we know they both have usage */ + /* if r0 not used in this instruction */ + if (!r0iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R0_IDX)->dname); + R0INB++; + } + else if (!_G.r0Pushed) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + _G.r0Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R0_IDX); + aop->type = AOP_R0; + + return aop->aopu.aop_ptr = REG_WITH_INDEX (R0_IDX); + } + + /* if r1 not used then */ + + if (!r1iu) + { + /* push it if not already pushed */ + if ((ic->op == IPUSH) || (ic->op == PCALL)) + { + MOVB (REG_WITH_INDEX (R1_IDX)->dname); + R1INB++; + } + else if (!_G.r1Pushed) + { + emitpush (REG_WITH_INDEX (R1_IDX)->dname); + _G.r1Pushed++; + } + + ic->rUsed = bitVectSetBit (ic->rUsed, R1_IDX); + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + +endOfWorld: + /* I said end of world, but not quite end of world yet */ + /* if this is a result then we can push it on the stack */ + if (result) + { + aop->type = AOP_STK; + return NULL; + } + /* in the case that result AND left AND right needs a pointer reg + we can safely use the result's */ + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R0_IDX)) + { + aop->type = AOP_R0; + return REG_WITH_INDEX (R0_IDX); + } + if (bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R1_IDX) && + (!OP_SYMBOL (IC_RESULT (ic)) || OP_SYMBOL (IC_RESULT (ic))->regs[getSize (operandType (IC_RESULT (ic))) - 1]->rIdx == R1_IDX)) + { + aop->type = AOP_R1; + return REG_WITH_INDEX (R1_IDX); + } + + /* now this is REALLY the end of the world */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "getFreePtr should never reach here"); + exit (EXIT_FAILURE); +} + + +/*-----------------------------------------------------------------*/ +/* getTempRegs - initialize an array of pointers to GPR registers */ +/* that are not in use. Returns 1 if the requested */ +/* number of registers were available, 0 otherwise. */ +/*-----------------------------------------------------------------*/ +int +getTempRegs (reg_info ** tempRegs, int size, iCode * ic) +{ + bitVect *freeRegs; + int i; + int offset; + + if (!ic) + ic = genLine.lineElement.ic; + if (!ic) + return 0; + if (!_G.currentFunc) + return 0; + + freeRegs = newBitVect (8); + bitVectSetBit (freeRegs, R2_IDX); + bitVectSetBit (freeRegs, R3_IDX); + bitVectSetBit (freeRegs, R4_IDX); + bitVectSetBit (freeRegs, R5_IDX); + bitVectSetBit (freeRegs, R6_IDX); + bitVectSetBit (freeRegs, R7_IDX); + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + bitVect *newfreeRegs; + newfreeRegs = bitVectIntersect (freeRegs, _G.currentFunc->regsUsed); + freeBitVect (freeRegs); + freeRegs = newfreeRegs; + } + freeRegs = bitVectCplAnd (freeRegs, ic->rMask); + + offset = 0; + for (i = 0; i < freeRegs->size; i++) + { + if (bitVectBitValue (freeRegs, i)) + tempRegs[offset++] = REG_WITH_INDEX (i); + if (offset >= size) + { + freeBitVect (freeRegs); + return 1; + } + } + + freeBitVect (freeRegs); + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + aop->allocated = 1; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + return PTR_TYPE (SPEC_OCLS (etype)); +} + +/*-----------------------------------------------------------------*/ +/* leftRightUseAcc - returns size of accumulator use by operands */ +/*-----------------------------------------------------------------*/ +static int +leftRightUseAcc (iCode * ic) +{ + operand *op; + int size; + int accuseSize = 0; + int accuse = 0; + + if (!ic) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "null iCode pointer"); + return 0; + } + + if (ic->op == IFX) + { + op = IC_COND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else if (ic->op == JUMPTABLE) + { + op = IC_JTCOND (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + else + { + op = IC_LEFT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + op = IC_RIGHT (ic); + if (IS_OP_ACCUSE (op)) + { + accuse = 1; + size = getSize (OP_SYMBOL (op)->type); + if (size > accuseSize) + accuseSize = size; + } + } + + if (accuseSize) + return accuseSize; + else + return accuse; +} + +/*-----------------------------------------------------------------*/ +/* stackoffset - stack offset for symbol */ +/*-----------------------------------------------------------------*/ +static int +stackoffset (symbol * sym) +{ + int offset = sym->stack; + if (options.omitFramePtr) + { + if (SPEC_OCLS (sym->etype)->paged) + offset -= _G.stack.xoffset + _G.stack.xpushed; + else + offset -= _G.stack.offset + _G.stack.pushed; + } + if (sym->stack < 0) + offset -= _G.stack.param_offset; + return offset; +} + +/*-----------------------------------------------------------------*/ +/* aopPtrForSym - pointer for symbol */ +/*-----------------------------------------------------------------*/ +static void +aopPtrForSym (symbol * sym, bool accuse, int offset, asmop * aop, iCode * ic) +{ + char *base; + struct dbuf_s tmpBuf; + dbuf_init (&tmpBuf, 1024); + if (sym->onStack) + { + dbuf_printf (&tmpBuf, "%s", SYM_BP (sym)); + } + else + { + dbuf_printf (&tmpBuf, "#%s", sym->rname); + } + base = dbuf_detach_c_str (&tmpBuf); + + offset += stackoffset (sym); + + if (abs (offset) >= 248) + werrorfl (ic->filename, ic->lineno, W_LIT_OVERFLOW); + + if ((abs (offset) < 3) || (accuse && (abs (offset) < 4))) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_ptr->name, base); + while (offset < 0) + { + emitcode ("dec", aop->aopu.aop_ptr->name); + offset++; + } + while (offset > 0) + { + emitcode ("inc", aop->aopu.aop_ptr->name); + offset--; + } + } + else + { + if (accuse) + { + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("xch", "a,%s", aop->aopu.aop_ptr->name); + } + else + { + emitcode ("mov", "a,%s", base); + emitcode ("add", "a,#0x%02x", offset & 0xff); + emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + } + } + aop->paged = SPEC_OCLS (sym->etype)->paged; + dbuf_free (base); +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space; + bool accuse = leftRightUseAcc (ic) || _G.accInUse; + + wassertl (ic != NULL, "Got a null iCode"); + wassertl (sym != NULL, "Got a null symbol"); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + sym->aop->allocated++; + return sym->aop; + } + + /* assign depending on the storage class */ + /* if it is on the stack or indirectly addressable */ + /* space we need to assign either r0 or r1 to it */ + if (sym->onStack || sym->iaccess) + { + sym->aop = aop = newAsmop (0); + aop->aopu.aop_ptr = getFreePtr (ic, aop, result); + aop->size = getSize (sym->type); + + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) + { + aopPtrForSym (sym, accuse, 0, aop, ic); + } + else + { + aop->aopu.aop_sym = sym; + } + return aop; + } + + /* if in bit space */ + if (IN_BITSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + //printf("aopForSym, using AOP_DIR for %s (%x)\n", sym->name, sym); + //printTypeChainRaw(sym->type, NULL); + //printf("space = %s\n", space ? space->sname : "NULL"); + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd.aop_immd1 = Safe_strdup (sym->rname); + aop->size = getSize (sym->type); + return aop; + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + sym->aop = aop = newAsmop (AOP_DPTR); + + rtrackLoadDptrWithSym (sym->rname); + + aop->size = getSize (sym->type); + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol * sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int ptr_type = 0; + int val = 0; + sym_link *from_type = NULL; + const char *from_name = NULL; + struct dbuf_s dbuf; + + for (;;) + { + if (ic->op == '+') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + from_type = operandType (IC_RIGHT (ic)); + from_name = IS_SYMOP (IC_RIGHT (ic)) ? OP_SYMBOL (IC_RIGHT (ic))->name : NULL; + aop->aopu.aop_immd.from_cast_remat = 1; + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else + { + break; + } + } + + dbuf_init (&dbuf, 128); + if (val) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + + aop->aopu.aop_immd.aop_immd1 = dbuf_detach_c_str (&dbuf); + /* set immd2 field if required */ + if (aop->aopu.aop_immd.from_cast_remat) + { + ptr_type = pointerTypeToGPByte (DCL_TYPE (from_type), from_name, sym->name); + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "#!constbyte", ptr_type); + aop->aopu.aop_immd.aop_immd2 = dbuf_detach_c_str (&dbuf); + } + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they're not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + /* if they have the same rname */ + if (sym1->rname[0] && sym2->rname[0] && EQ (sym1->rname, sym2->rname) && !(IS_PARM (op2) && IS_ITEMP (op1))) + return TRUE; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameByte - two asmops have the same address at given offsets */ +/*-----------------------------------------------------------------*/ +static bool +sameByte (asmop * aop1, int off1, asmop * aop2, int off2) +{ + if (aop1 == aop2 && off1 == off2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->aopu.aop_reg[off1] != aop2->aopu.aop_reg[off2]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + if (aop1->type != AOP_REG && aop1->type != AOP_CRY) + return FALSE; + + if (aop1->type != aop2->type) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + { + op->aop->allocated++; + return; + } + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = OP_SYMBOL (op)->aop; + op->aop->allocated++; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, OP_SYMBOL (op), result); + return; + } + + /* this is a temporary : this has + only five choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->ruonly ? 1 : 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = operandSize (op); + return; + } + + if (sym->accuse) + { + int i; + sym->aop = op->aop = aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); + for (i = 0; i < 2; i++) + aop->aopu.aop_str[i] = accUse[i]; + return; + } + + if (sym->ruonly) + { + unsigned i; + + sym->aop = op->aop = aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < fReturnSizeMCS51; i++) + aop->aopu.aop_str[i] = fReturn[i]; + return; + } + + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, result); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + return; + } + + /* if the type is a bit register */ + if (sym->regType == REG_BIT && sym->regs[0]->type == REG_BIT) + { + sym->aop = op->aop = aop = newAsmop (AOP_CRY); + aop->size = sym->nRegs; //1??? + aop->aopu.aop_reg[0] = sym->regs[0]; + aop->aopu.aop_dir = sym->regs[0]->name; + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*-----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + int sz; + symbol *sym; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + aop->allocated--; + + if (aop->allocated) + goto dealloc; + + /* depending on the asmop type only three cases need work + AOP_R0, AOP_R1 & AOP_STK */ + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + R0INB--; + } + else if (_G.r0Pushed) + { + if (pop) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R0_IDX); + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + R1INB--; + } + else if (_G.r1Pushed) + { + if (pop) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + } + bitVectUnSetBit (ic->rUsed, R1_IDX); + break; + + case AOP_STK: + sz = aop->size; + sym = aop->aopu.aop_sym; + bitVectUnSetBit (ic->rUsed, R0_IDX); + bitVectUnSetBit (ic->rUsed, R1_IDX); + + getFreePtr (ic, aop, FALSE); + + aopPtrForSym (sym, FALSE, aop->size - 1, aop, ic); + + while (sz--) + { + emitpop ("acc"); + if (aop->paged) + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + else + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + if (!sz) + break; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + op->aop = aop; + freeAsmop (op, NULL, ic, TRUE); + if (_G.r1Pushed) + { + emitpop ("ar1"); + _G.r1Pushed--; + } + if (_G.r0Pushed) + { + emitpop ("ar0"); + _G.r0Pushed--; + } + break; + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmop - partial free up of Asmop for a branch; just */ +/* pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmop (operand * op, iCode * ic) +{ + asmop *aop; + + if (!op) + return; + + aop = op->aop; + + if (!aop) + return; + + if (!aop->allocated) + return; + + switch (aop->type) + { + case AOP_R0: + if (R0INB) + { + emitcode ("mov", "r0,b"); + } + else if (_G.r0Pushed) + { + emitcode ("pop", "ar0"); /* without pushed-- */ + } + break; + + case AOP_R1: + if (R1INB) + { + emitcode ("mov", "r1,b"); + } + else if (_G.r1Pushed) + { + emitcode ("pop", "ar1"); /* without pushed-- */ + } + break; + + case AOP_STK: + { + int sz = aop->size; + + emitcode ("mov", "b,r0"); + aopPtrForSym (aop->aopu.aop_sym, FALSE, 0, aop, ic); + + while (sz--) + { + emitcode ("pop", "acc"); /* without pushed-- */ + if (aop->paged) + emitcode ("movx", "@r0,a"); + else + emitcode ("mov", "@r0,a"); + if (!sz) + break; + emitcode ("dec", "r0"); + } + emitcode ("mov", "r0,b"); + } + } +} + +/*------------------------------------------------------------------*/ +/* freeForBranchAsmops - partial free up of 3 Asmops for a branch; */ +/* just pop r0 or r1 off stack if pushed */ +/*------------------------------------------------------------------*/ +static void +freeForBranchAsmops (operand * op1, operand * op2, operand * op3, iCode * ic) +{ + if (op1) + freeForBranchAsmop (op1, ic); + if (op2) + freeForBranchAsmop (op2, ic); + if (op3) + freeForBranchAsmop (op3, ic); +} + +/*-----------------------------------------------------------------*/ +/* opIsGptr: returns non-zero if the passed operand is */ +/* a generic pointer type. */ +/*-----------------------------------------------------------------*/ +static int +opIsGptr (operand * op) +{ + if (op && (AOP_SIZE (op) == GPTRSIZE) && (IS_GENPTR (operandType (op)) || IFFUNC_ISBANKEDCALL (operandType (op)))) + { + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/* swapOperands - swap two operands */ +/*-----------------------------------------------------------------*/ +static void +swapOperands (operand ** left, operand ** right) +{ + operand *t = *right; + *right = *left; + *left = t; +} + +/*-----------------------------------------------------------------*/ +/* aopGetUsesAcc - indicates ahead of time whether aopGet() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopGetUsesAcc (operand * oper, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + + case AOP_R0: + case AOP_R1: + if (aop->paged) + return TRUE; + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_IMMD: + return FALSE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_CRY: + return TRUE; + case AOP_ACC: + if (offset) + return FALSE; + return TRUE; + case AOP_LIT: + return FALSE; + case AOP_STR: + if (EQ (aop->aopu.aop_str[offset], "a")) + return TRUE; + return FALSE; + case AOP_DUMMY: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-------------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-------------------------------------------------------------------*/ +/* + * NOTE: function returns a pointer to a reusable dynamically allocated + * buffer, which should never be freed! + * Subsequent call to aopGet() will rewrite the result of the previous + * call, so the content of the result should be copied to an other + * location, usually using Safe_strdup(), in order to perserve it. + */ +static const char * +aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + asmop *aop = AOP (oper); + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + /* offset is greater than + size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + { + dbuf_append_str (&dbuf, zero); + } + else + { + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + dbuf_append_str (&dbuf, zero); + break; + + case AOP_R0: + case AOP_R1: + /* if we need to increment it */ + while (offset > aop->coff) + { + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + aop->coff--; + } + + aop->coff = offset; + if (aop->paged) + { + emitcode ("movx", "a,@%s", aop->aopu.aop_ptr->name); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + } + else + { + dbuf_printf (&dbuf, "@%s", aop->aopu.aop_ptr->name); + } + break; + + case AOP_DPTR: + if (aop->code && aop->coff == 0 && offset >= 1) + { + emitcode ("mov", "a,#0x%02x", offset); + emitcode ("movc", "a,@a+dptr"); + } + else + { + while (offset > aop->coff) + { + emitcode ("inc", "dptr"); + aop->coff++; + } + + while (offset < aop->coff) + { + emitcode ("lcall", "__decdptr"); + aop->coff--; + } + + aop->coff = offset; + if (aop->code) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + } + else + { + emitcode ("movx", "a,@dptr"); + } + } + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_IMMD: + if (aop->aopu.aop_immd.from_cast_remat && opIsGptr (oper) && offset == GPTRSIZE - 1) + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_immd.aop_immd2); + } + else if (bit16) + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + else if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", aop->aopu.aop_immd.aop_immd1, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", aop->aopu.aop_immd.aop_immd1); + } + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (oper))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_printf (&dbuf, "%s", aop->aopu.aop_dir); + } + break; + + case AOP_REG: + dbuf_append_str (&dbuf, dname ? aop->aopu.aop_reg[offset]->dname : aop->aopu.aop_reg[offset]->name); + break; + + case AOP_CRY: + if (!IS_OP_RUONLY (oper)) + emitcode ("mov", "c,%s", aop->aopu.aop_dir); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + dbuf_append_str (&dbuf, dname ? "acc" : "a"); + break; + + case AOP_ACC: + dbuf_append_str (&dbuf, (!offset && dname) ? "acc" : aop->aopu.aop_str[offset]); + break; + + case AOP_LIT: + if (opIsGptr (oper) && IS_FUNCPTR (operandType (oper)) && offset == GPTRSIZE - 1) + { + dbuf_append_str (&dbuf, aopLiteralGptr (NULL, aop->aopu.aop_lit)); + } + else + { + int size = 1 + (bit16 ? 1 : 0); + dbuf_append_str (&dbuf, aopLiteralLong (aop->aopu.aop_lit, offset, size)); + } + break; + + case AOP_STR: + aop->coff = offset; + if (EQ (aop->aopu.aop_str[offset], "a") && dname) + dbuf_append_str (&dbuf, "acc"); + else + dbuf_append_str (&dbuf, aop->aopu.aop_str[offset]); + break; + + default: + dbuf_destroy (&dbuf); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (EXIT_FAILURE); + } + } + return dbuf_c_str (&dbuf); +} + +/*-----------------------------------------------------------------*/ +/* aopPutUsesAcc - indicates ahead of time whether aopPut() will */ +/* clobber the accumulator */ +/*-----------------------------------------------------------------*/ +static bool +aopPutUsesAcc (operand * oper, const char *s, int offset) +{ + asmop *aop = AOP (oper); + + if (offset > (aop->size - 1)) + return FALSE; + + switch (aop->type) + { + case AOP_DUMMY: + return TRUE; + case AOP_DIR: + return FALSE; + case AOP_REG: + wassert (!EQ (aop->aopu.aop_reg[offset]->name, "a")); + return FALSE; + case AOP_DPTR: + return TRUE; + case AOP_R0: + case AOP_R1: + return ((aop->paged) || (*s == '@')); + case AOP_STK: + return (*s == '@'); + case AOP_CRY: + return (!aop->aopu.aop_dir || !EQ (s, aop->aopu.aop_dir)); + case AOP_STR: + return FALSE; + case AOP_IMMD: + return FALSE; + case AOP_ACC: + return FALSE; + default: + /* Error case --- will have been caught already */ + wassert (0); + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +aopPut (operand * result, const char *s, int offset) +{ + bool bvolatile = isOperandVolatile (result, FALSE); + bool accuse = FALSE; + asmop *aop = AOP (result); + const char *d = NULL; + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (EXIT_FAILURE); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DUMMY: + MOVA (s); /* read s in case it was volatile */ + accuse = TRUE; + break; + + case AOP_DIR: + if ((SPEC_SCLS (getSpec (operandType (result))) == S_SFR) && (aop->size > 1)) + { + dbuf_printf (&dbuf, "((%s >> %d) & 0xFF)", aop->aopu.aop_dir, offset * 8); + } + else if (offset) + { + dbuf_printf (&dbuf, "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + { + dbuf_append_str (&dbuf, aop->aopu.aop_dir); + } + + if (!EQ (dbuf_c_str (&dbuf), s) || bvolatile) + { + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), s); + } + if (EQ (dbuf_c_str (&dbuf), "acc")) + { + accuse = TRUE; + } + break; + + case AOP_REG: + if (!EQ (aop->aopu.aop_reg[offset]->name, s) && !EQ (aop->aopu.aop_reg[offset]->dname, s)) + { + if (*s == '@' || + EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->dname, s); + } + else + { + emitcode ("mov", "%s,%s", aop->aopu.aop_reg[offset]->name, s); + } + } + break; + + case AOP_DPTR: + if (aop->code) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut writing to code space"); + exit (EXIT_FAILURE); + } + + /* if not in accumulator */ + MOVA (s); + + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "dptr"); + } + + while (offset < aop->coff) + { + aop->coff--; + emitcode ("lcall", "__decdptr"); + } + + aop->coff = offset; + + emitcode ("movx", "@dptr,a"); + break; + + case AOP_R0: + case AOP_R1: + while (offset > aop->coff) + { + aop->coff++; + emitcode ("inc", "%s", aop->aopu.aop_ptr->name); + } + while (offset < aop->coff) + { + aop->coff--; + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + aop->coff = offset; + + if (aop->paged) + { + MOVA (s); + emitcode ("movx", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (*s == '@') + { + MOVA (s); + emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + } + else if (EQ (s, "r0") || EQ (s, "r1") || EQ (s, "r2") || EQ (s, "r3") || + EQ (s, "r4") || EQ (s, "r5") || EQ (s, "r6") || EQ (s, "r7")) + { + dbuf_printf (&dbuf, "a%s", s); + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, dbuf_c_str (&dbuf)); + } + else + { + emitcode ("mov", "@%s,%s", aop->aopu.aop_ptr->name, s); + } + break; + + case AOP_STK: + emitpush (s); + break; + + case AOP_CRY: + // destination is carry for return-use-only + d = (IS_OP_RUONLY (result)) ? "c" : aop->aopu.aop_dir; + + // source is no literal and not in carry + if (!EQ (s, zero) && !EQ (s, one) && !EQ (s, "c")) + { + MOVA (s); + /* set C, if a >= 1 */ + emitcode ("add", "a,#!constbyte", 0xff); + s = "c"; + } + // now source is zero, one or carry + + /* if result no bit variable */ + if (!d) + { + if (EQ (s, "c")) + { + /* inefficient: move carry into A and use jz/jnz */ + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + accuse = TRUE; + } + else + { + MOVA (s); + accuse = TRUE; + } + } + else if (EQ (s, zero)) + emitcode ("clr", "%s", d); + else if (EQ (s, one)) + emitcode ("setb", "%s", d); + else if (!EQ (s, d)) + emitcode ("mov", "%s,c", d); + break; + + case AOP_STR: + aop->coff = offset; + if (!EQ (aop->aopu.aop_str[offset], s) || bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + case AOP_ACC: + accuse = TRUE; + aop->coff = offset; + if (!offset && EQ (s, "acc") && !bvolatile) + break; + + if (!EQ (aop->aopu.aop_str[offset], s) && !bvolatile) + emitcode ("mov", "%s,%s", aop->aopu.aop_str[offset], s); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit (EXIT_FAILURE); + } + + return accuse; +} + +/*--------------------------------------------------------------------*/ +/* loadDptrFromOperand - load dptr (and optionally B) from operand op */ +/*--------------------------------------------------------------------*/ +static void +loadDptrFromOperand (operand *op, bool loadBToo) +{ + if (AOP_TYPE (op) != AOP_STR) + { + /* if this is rematerializable */ + if (AOP_TYPE (op) == AOP_IMMD) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + { + if (AOP (op)->aopu.aop_immd.from_cast_remat) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + else + { + wassertl (FALSE, "need pointerCode"); + emitcode (";", "mov b,???"); + /* genPointerGet and genPointerSet originally did different + ** things for this case. Both seem wrong. + ** from genPointerGet: + ** emitcode ("mov", "b,#%d", pointerCode (retype)); + ** from genPointerSet: + ** emitcode ("mov", "b,%s + 1", aopGet (result, 0, TRUE, FALSE)); + */ + } + } + } + else if (AOP_TYPE (op) == AOP_LIT) + { + emitcode ("mov", "dptr,%s", aopGet (op, 0, TRUE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + else if (AOP_TYPE (op) == AOP_DPTR) + { + emitpush (aopGet (op, 0, FALSE, FALSE)); + if (loadBToo) + { + emitpush (aopGet (op, 1, FALSE, FALSE)); + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + emitpop ("dph"); + } + else + { + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + } + emitpop ("dpl"); + } + else + { + /* we need to get it byte by byte */ + emitcode ("mov", "dpl,%s", aopGet (op, 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (op, 1, FALSE, FALSE)); + if (loadBToo) + emitcode ("mov", "b,%s", aopGet (op, AOP_SIZE (op) - 1, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* reAdjustPreg - points a register back to where it should */ +/*-----------------------------------------------------------------*/ +static void +reAdjustPreg (asmop * aop) +{ + if ((aop->coff == 0) || (aop->size <= 1)) + return; + + switch (aop->type) + { + case AOP_R0: + case AOP_R1: + while (aop->coff--) + emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + break; + case AOP_DPTR: + while (aop->coff--) + { + emitcode ("lcall", "__decdptr"); + } + break; + } + aop->coff = 0; +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size = AOP_SIZE (op); + + if (size == GPTRSIZE) + { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) + { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + } + } + return size; +} + +/*-----------------------------------------------------------------*/ +/* outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +static void +outAcc (operand * result) +{ + int size, offset; + size = getDataSize (result); + if (size) + { + aopPut (result, "a", 0); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + { + aopPut (result, zero, offset++); + } + } +} + +/*-----------------------------------------------------------------*/ +/* outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +outBitC (operand * result) +{ + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + if (!IS_OP_RUONLY (result) && !IS_OP_ACCUSE (result)) + aopPut (result, "c", 0); + } + else if (AOP_TYPE (result) != AOP_DUMMY) + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper) - 1; + int offset = 1; + bool AccUsed; + sym_link *type = operandType (oper); + bool pushedB; + + /* always need B for float */ + AccUsed = IS_FLOAT (type); + + while (!AccUsed && size--) + { + AccUsed |= aopGetUsesAcc (oper, offset++); + } + + if (opIsGptr (oper)) + { + /* assumes that banks never map to address 0x0000 + so it suffices to check dptr part only and ignore b */ + size = AOP_SIZE (oper) - 2; + } + else + { + size = AOP_SIZE (oper) - 1; + } + + offset = 0; + if (size && AccUsed && (AOP (oper)->type != AOP_ACC)) + { + pushedB = pushB (); + MOVB (aopGet (oper, offset++, FALSE, FALSE)); + while (--size) + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + emitcode ("orl", "b,a"); + } + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + if (IS_FLOAT (type)) + emitcode ("anl", "a,#0x7F"); //clear sign bit + emitcode ("orl", "a,b"); + popB (pushedB); + } + else + { + MOVA (aopGet (oper, offset++, FALSE, FALSE)); + while (size--) + { + emitcode ("orl", "a,%s", aopGet (oper, offset++, FALSE, FALSE)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* toCarry - make boolean and move into carry */ +/*-----------------------------------------------------------------*/ +static void +toCarry (operand *oper) +{ + /* if the operand is a literal then + we know what the value is */ + if (AOP_TYPE (oper) == AOP_LIT) + { + if ((int) operandLitValue (oper)) + SETC; + else + CLRC; + } + else if (AOP_TYPE (oper) == AOP_CRY) + { + if (!IS_OP_ACCUSE (oper)) + emitcode ("mov", "c,%s", oper->aop->aopu.aop_dir); + } + else if (IS_BOOL (operandType (oper)) || IS_BITFIELD (operandType (oper)) && SPEC_BLEN (getSpec (operandType (oper))) == 1) + { + MOVA (aopGet (oper, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + else + { + /* or the operand into a */ + toBoolean (oper); + /* set C, if a >= 1 */ + emitcode ("add", "a,#0xff"); + } +} + +/*-----------------------------------------------------------------*/ +/* assignBit - assign operand to bit operand */ +/*-----------------------------------------------------------------*/ +static void +assignBit (operand * result, operand * right) +{ + emitcode (";", "assignBit"); + /* if the right side is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + if ((int) operandLitValue (right)) + aopPut (result, one, 0); + else + aopPut (result, zero, 0); + } + else + { + toCarry (right); + outBitC (result); + } +} + +/*-------------------------------------------------------------------*/ +/* xch_a_aopGet - for exchanging acc with value of the aop */ +/*-------------------------------------------------------------------*/ +static const char * +xch_a_aopGet (operand * oper, int offset, bool bit16, bool dname) +{ + const char *l; + + if (aopGetUsesAcc (oper, offset)) + { + emitcode ("mov", "b,a"); + MOVA (aopGet (oper, offset, bit16, dname)); + emitcode ("xch", "a,b"); + aopPut (oper, "a", offset); + emitcode ("xch", "a,b"); + l = "b"; + } + else + { + l = aopGet (oper, offset, bit16, dname); + emitcode ("xch", "a,%s", l); + } + return l; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + symbol *tlbl; + + D (emitcode (";", "genNot")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if in bit space then a special case */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + /* if left==result then cpl bit */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + emitcode ("cpl", "%s", IC_LEFT (ic)->aop->aopu.aop_dir); + } + else + { + toCarry (IC_LEFT (ic)); + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + goto release; + } + + toBoolean (IC_LEFT (ic)); + + /* set C, if a == 0 */ + tlbl = newiTempLabel (NULL); + emitcode ("cjne", "a,#0x01,!tlabel", labelKey2num (tlbl->key)); + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + int offset = 0; + int size; + symbol *tlbl; + sym_link *letype = getSpec (operandType (IC_LEFT (ic))); + + D (emitcode (";", "genCpl")); + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special case if in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + const char *l; + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY || (SPEC_USIGN (letype) && IS_CHAR (letype))) + { + /* promotion rules are responsible for this strange result: + bit -> int -> ~int -> bit + uchar -> int -> ~int -> bit + */ + emitcode ("setb", "%s", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_LEFT (ic), offset++, FALSE, FALSE); + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_ACC && offset == 0) || + AOP_TYPE (IC_LEFT (ic)) == AOP_REG || IS_AOP_PREG (IC_LEFT (ic))) + { + emitcode ("cjne", "%s,#0xFF,!tlabel", l, labelKey2num (tlbl->key)); + } + else + { + MOVA (l); + emitcode ("cjne", "a,#0xFF,!tlabel", labelKey2num (tlbl->key)); + } + emitLabel (tlbl); + outBitC (IC_RESULT (ic)); + goto release; + } + + size = AOP_SIZE (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + + D (emitcode (";", "genUminusFloat")); + + /* for this we just copy and then flip the bit */ + + size = AOP_SIZE (op) - 1; + + while (size--) + { + aopPut (result, aopGet (op, offset, FALSE, FALSE), offset); + offset++; + } + + MOVA (aopGet (op, offset, FALSE, FALSE)); + + emitcode ("cpl", "acc.7"); + aopPut (result, "a", offset); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int offset, size; + sym_link *optype; + + D (emitcode (";", "genUminus")); + + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + emitcode ("mov", "c,%s", IC_LEFT (ic)->aop->aopu.aop_dir); + emitcode ("cpl", "c"); + emitcode ("mov", "%s,c", IC_RESULT (ic)->aop->aopu.aop_dir); + goto release; + } + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) + { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero */ + size = AOP_SIZE (IC_LEFT (ic)); + offset = 0; + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (EQ (l, "a")) + { + if (offset == 0) + SETC; + emitcode ("cpl", "a"); + emitcode ("addc", "a,#0x00"); + } + else + { + if (offset == 0) + CLRC; + emitcode ("clr", "a"); + emitcode ("subb", "a,%s", l); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + + /* if any remaining bytes in the result */ + /* we just need to propagate the sign */ + if ((size = (AOP_SIZE (IC_RESULT (ic)) - AOP_SIZE (IC_LEFT (ic))))) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (IC_RESULT (ic), "a", offset++); + } + +release: + /* release the aops */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); +} + +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static int +regsCmp (void *p1, void *p2) +{ + return (STRCASECMP ((char *) p1, (char *) (p2)) == 0); +} + +static bool +inExcludeList (char *s) +{ + const char *p = setFirstItem (options.excludeRegsSet); + + if (p == NULL || STRCASECMP (p, "none") == 0) + return FALSE; + + return isinSetWith (options.excludeRegsSet, s, regsCmp); +} + +/*-----------------------------------------------------------------*/ +/* xstackRegisters - create bitmask for registers on xstack */ +/*-----------------------------------------------------------------*/ +static int +xstackRegisters (bitVect * rsave, bool push, int count, char szRegs[32]) +{ + int i; + int mask = 0; + + szRegs[0] = '\0'; + + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (reg->type == REG_BIT) + { + mask |= 0x01; + strncat (szRegs, reg->base, 31); + } + else + { + if (i == R0_IDX) + { + mask |= 0x100; + } + else + { + //set bit(n) for Rn + mask |= (0x01 << reg->offset); + } + strncat (szRegs, reg->name, 31); + } + } + } + return mask ^ 0xFF; //invert all bits for jbc +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode * lic) +{ + int i; + iCode *ic; + bitVect *rsave; + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already or don't need to be then + do nothing */ + if (ic->regsSaved) + return; + if (IS_SYMOP (IC_LEFT (ic))) + { + sym_link *type = OP_SYM_TYPE (IC_LEFT (ic)); + if (IFFUNC_ISNAKED (type) && !IFFUNC_ISBANKEDCALL (type)) + return; + if (IFFUNC_CALLEESAVES (type)) + return; + } + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* save all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* save only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + ic->regsSaved = 1; + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are pushed all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); // allocate before use + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, TRUE, count, szRegs); + if (BINUSE) + emitpush ("b"); + emitcode ("mov", "a,#0x%02x", count); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpush_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpush_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + if (BINUSE) + emitpop ("b"); + _G.stack.xpushed += count; + } + else + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + emitcode ("mov", "r0,%s", spname); + if (count == 2) + { + emitcode ("inc", "%s", spname); + emitcode ("inc", "%s", spname); + } + else + { + MOVA ("r0"); + emitcode ("add", "a,#0x%02x", count); + emitcode ("mov", "%s,a", spname); + } + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + if (i == R0_IDX) + { + emitpop ("acc"); + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "a,%s", reg->base); + } + else + { + emitcode ("mov", "a,%s", reg->name); + } + emitcode ("movx", "@r0,a"); + _G.stack.xpushed++; + if (--count) + { + emitcode ("inc", "r0"); + } + } + } + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + else + { + bool bits_pushed = FALSE; + for (i = 0; i < mcs51_nRegs; i++) + { + if (bitVectBitValue (rsave, i)) + { + bits_pushed = pushReg (i, bits_pushed); + } + } + } + freeBitVect (rsave); +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode * ic) +{ + int i; + bitVect *rsave; + + if (IFFUNC_CALLEESAVES (_G.currentFunc->type)) + { + /* restore all registers if the caller is callee_saves and the callee is not */ + rsave = bitVectCopy (mcs51_allBankregs ()); + if (!inExcludeList ("bits")) + { + rsave = bitVectUnion (rsave, mcs51_allBitregs ()); + BitBankUsed = 1; + } + } + else + /* restore only the registers in use at this time */ + rsave = bitVectCopy (ic->rMask); + /* but skip the ones for the result */ + rsave = bitVectCplAnd (rsave, mcs51_rUmaskForOp (IC_RESULT (ic))); + + if (options.useXstack) + { + bitVect *rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), rsave); + int nBits = bitVectnBitsOn (rsavebits); + int count = bitVectnBitsOn (rsave); + + if (nBits != 0) + { + count = count - nBits + 1; + /* remove all but the first bits as they are popped all at once */ + rsave = bitVectCplAnd (rsave, rsavebits); + rsave = bitVectSetBit (rsave, bitVectFirstBit (rsavebits)); + } + freeBitVect (rsavebits); + + if (count == 1) + { + reg_info *reg = REG_WITH_INDEX (bitVectFirstBit (rsave)); + emitcode ("mov", "r0,%s", spname); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + emitcode ("dec", "%s", spname); + } + else if (count != 0) + { + if ((FUNC_REGBANK (currFunc->type) == 0) && optimize.codeSize) + { + char szRegs[32]; + int mask = xstackRegisters (rsave, FALSE, count, szRegs); + emitcode ("mov", "b,#0x%02x", mask & 0xFF); + if (mask & 0x100) + emitcode ("lcall", "___sdcc_xpop_regs_r0\t;(%s)", szRegs); + else + emitcode ("lcall", "___sdcc_xpop_regs\t;(%s)", szRegs); + genLine.lineCurr->isInline = 1; + _G.stack.xpushed -= count; + } + else + { + bool resultInR0 = bitVectBitValue (mcs51_rUmaskForOp (IC_RESULT (ic)), R0_IDX); + if (resultInR0) + { + emitpush (REG_WITH_INDEX (R0_IDX)->dname); + } + emitcode ("mov", "r0,%s", spname); + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + reg_info *reg = REG_WITH_INDEX (i); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + _G.stack.xpushed--; + if (i == R0_IDX) + { + emitpush ("acc"); + } + else if (reg->type == REG_BIT) + { + emitcode ("mov", "%s,a", reg->base); + } + else + { + emitcode ("mov", "%s,a", reg->name); + } + } + } + emitcode ("mov", "%s,r0", spname); + if (bitVectBitValue (rsave, R0_IDX) || resultInR0) + { + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + } + } + else + { + bool bits_popped = FALSE; + for (i = mcs51_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + bits_popped = popReg (i, bits_popped); + } + } + } + freeBitVect (rsave); +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand * oper, int size, iCode * ic) +{ + int offset = 0; + int nPushed = _G.r0Pushed + _G.r1Pushed; + + aopOp (oper, ic, FALSE); + + if (nPushed != _G.r0Pushed + _G.r1Pushed) + { + while (offset < size) + { + const char *l = aopGet (oper, offset, FALSE, TRUE); + emitcode ("mov", "%s,%s", fReturn[offset++], l); + } + freeAsmop (oper, NULL, ic, TRUE); + offset = 0; + while (offset < size) + { + emitpush (fReturn[offset++]); + } + return; + } + + while (size--) + { + const char *l = aopGet (oper, offset++, FALSE, TRUE); + if (AOP_TYPE (oper) != AOP_REG && AOP_TYPE (oper) != AOP_DIR) + { + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + } + + freeAsmop (oper, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - also indicates if acc is in use afterwards */ +/*-----------------------------------------------------------------*/ +static bool +assignResultValue (operand * oper, operand * func) +{ + int offset = 0; + int size = AOP_SIZE (oper); + bool accuse = FALSE; + bool pushedA = FALSE; + + if (func && IS_BIT (getSpec (operandType (func)))) + { + outBitC (oper); + return FALSE; + } + if ((size > 3) && aopPutUsesAcc (oper, fReturn[offset], offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + { + if ((offset == 3) && pushedA) + emitpop ("acc"); + accuse |= aopPut (oper, fReturn[offset], offset); + offset++; + } + return accuse; +} + + +/*-----------------------------------------------------------------*/ +/* genXpush - pushes onto the external stack */ +/*-----------------------------------------------------------------*/ +static void +genXpush (iCode * ic) +{ + asmop *aop = newAsmop (0); + reg_info *r; + int size, offset = 0; + + D (emitcode (";", "genXpush")); + + aopOp (IC_LEFT (ic), ic, FALSE); + r = getFreePtr (ic, aop, FALSE); + + size = AOP_SIZE (IC_LEFT (ic)); + emitcode ("mov", "%s,%s", r->name, spname); + + // allocate space first + if (size <= 2) + { + emitcode ("inc", "%s", spname); + if (size == 2) + emitcode ("inc", "%s", spname); + } + else + { + MOVA (r->name); + emitcode ("add", "a,#0x%02x", size); + emitcode ("mov", "%s,a", spname); + } + + while (offset < size) + { + MOVA (aopGet (IC_LEFT (ic), offset++, FALSE, FALSE)); + emitcode ("movx", "@%s,a", r->name); + emitcode ("inc", "%s", r->name); + } + _G.stack.xpushed += size; + + freeAsmop (NULL, aop, ic, TRUE); + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + int size, offset = 0; + char *prev; + + D (emitcode (";", "genIpush")); + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + } + return; + } + + /* this is a parameter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + saveRegisters (ic); + + /* if use external stack then call the external + stack pushing routine */ + if (options.useXstack) + { + genXpush (ic); + return; + } + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE); + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + + prev = Safe_strdup (""); + while (size--) + { + const char *l = aopGet (IC_LEFT (ic), offset++, FALSE, TRUE); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG && AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) + { + if (!EQ (l, prev) || *l == '@') + MOVA (l); + emitpush ("acc"); + } + else + { + emitpush (l); + } + Safe_free (prev); + prev = Safe_strdup (l); + } + Safe_free (prev); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genIpop")); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = size - 1; + while (size--) + { + emitpop (aopGet (IC_LEFT (ic), offset--, FALSE, TRUE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* popForBranch - recover the spilt registers for a branch */ +/*-----------------------------------------------------------------*/ +static void +popForBranch (iCode * ic, bool markGenerated) +{ + while (ic && ic->op == IPOP) + { + int pushed = _G.stack.pushed; + genIpop (ic); + if (markGenerated) + ic->generated = 1; /* mark the icode as generated */ + else + _G.stack.pushed = pushed; + ic = ic->next; + } +} + +/*-----------------------------------------------------------------*/ +/* emitDummyCall - emit a dummy call for --no-ret-without-call */ +/*-----------------------------------------------------------------*/ +static void +emitDummyCall(void) +{ + symbol *dummyLabel; + + if (!options.no_ret_without_call) + return; + dummyLabel = newiTempLabel (NULL); + emitcode ("lcall", "!tlabel", labelKey2num (dummyLabel->key)); + emitLabel (dummyLabel); + emitcode ("dec", "sp"); + emitcode ("dec", "sp"); +} + +/*-----------------------------------------------------------------*/ +/* saveRBank - saves an entire register bank on the stack */ +/*-----------------------------------------------------------------*/ +static void +saveRBank (int bank, iCode * ic, bool pushPsw) +{ + int i; + int count = 8 + (pushPsw ? 1 : 0); + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + // allocate space first + emitcode ("mov", "%s,%s", r->name, spname); + MOVA (r->name); + emitcode ("add", "a,#!constbyte", count); + emitcode ("mov", "%s,a", spname); + } + + for (i = 0; i < 8; i++) + { + if (options.useXstack) + { + emitcode ("mov", "a,(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + if (--count) + emitcode ("inc", "%s", r->name); + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpush (buf); + } + } + + if (pushPsw) + { + if (options.useXstack) + { + emitcode ("mov", "a,psw"); + emitcode ("movx", "@%s,a", r->name); + _G.stack.xpushed++; + } + else + { + emitpush ("psw"); + } + + emitcode ("mov", "psw,#!constbyte", (bank << 3) & 0x00ff); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + + if (ic) + { + ic->bankSaved = 1; + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRBank - restores the register bank from stack */ +/*-----------------------------------------------------------------*/ +static void +unsaveRBank (int bank, iCode * ic, bool popPsw) +{ + int i; + asmop *aop = NULL; + reg_info *r = NULL; + + if (options.useXstack) + { + if (!ic) + { + /* Assume r0 is available for use. */ + r = REG_WITH_INDEX (R0_IDX); + } + else + { + aop = newAsmop (0); + r = getFreePtr (ic, aop, FALSE); + } + emitcode ("mov", "%s,%s", r->name, spname); + } + + if (popPsw) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "psw,a"); + _G.stack.xpushed--; + } + else + { + emitpop ("psw"); + } + } + + for (i = 7; i >= 0; i--) + { + if (options.useXstack) + { + emitcode ("dec", "%s", r->name); + emitcode ("movx", "a,@%s", r->name); + emitcode ("mov", "(%s+%d),a", regs8051[i].base, 8 * bank + regs8051[i].offset); + _G.stack.xpushed--; + } + else + { + char buf[16] = ""; + SNPRINTF (buf, 16, "(%s+%d)", regs8051[i].base, 8 * bank + regs8051[i].offset); + emitpop (buf); + } + } + + if (options.useXstack) + { + emitcode ("mov", "%s,%s", spname, r->name); + } + + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genSend - gen code for SEND */ +/*-----------------------------------------------------------------*/ +static void +genSend (set * sendSet) +{ + iCode *sic; + int bit_count = 0; + + /* first we do all bit parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg > 12) + { + int bit = sic->argreg - 13; + + aopOp (IC_LEFT (sic), sic, FALSE); + + /* if left is a literal then + we know what the value is */ + if (AOP_TYPE (IC_LEFT (sic)) == AOP_LIT) + { + if (((int) operandLitValue (IC_LEFT (sic)))) + emitcode ("setb", "b[%d]", bit); + else + emitcode ("clr", "b[%d]", bit); + } + else + { + /* we need to or */ + toCarry (IC_LEFT (sic)); + emitcode ("mov", "b[%d],c", bit); + } + bit_count++; + BitBankUsed = 1; + + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } + + if (options.useXstack || bit_count || setFirstItem (sendSet) && operandSize (IC_LEFT ((iCode *)(setFirstItem (sendSet)))) >= 6) + { + if (bit_count) + BITSINB++; + saveRegisters (setFirstItem (sendSet)); + if (bit_count) + BITSINB--; + } + + if (bit_count) + { + emitcode ("mov", "bits,b"); + } + + /* then we do all other parameters */ + for (sic = setFirstItem (sendSet); sic; sic = setNextItem (sendSet)) + { + if (sic->argreg <= 12) + { + int size, offset = 0; + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + + if (sic->argreg == 1) + { + if (AOP_TYPE (IC_LEFT (sic)) != AOP_DPTR) + { + bool pushedA = FALSE; + while (size--) + { + const char *l = aopGet (IC_LEFT (sic), offset, FALSE, FALSE); + if (!EQ (l, fReturn[offset])) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (sic)) == AOP_REG || AOP_TYPE (IC_LEFT (sic)) == AOP_R0 || AOP_TYPE (IC_LEFT (sic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + else if (EQ (l, "a") && size != 0) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + if (pushedA) + emitpop ("acc"); + } + else /* need to load dpl, dph, etc from @dptr */ + { + while (size--) + { + MOVA (aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + emitpush ("acc"); + offset++; + } + size = AOP_SIZE (IC_LEFT (sic)); + while (size--) + { + offset--; + if (!EQ ("a", fReturn[offset])) + { + emitpop (fReturn[offset]); + } + else + { + emitpop ("acc"); + } + } + } + } + else + { + while (size--) + { + emitcode ("mov", "%s,%s", rb1regs[sic->argreg + offset - 5], aopGet (IC_LEFT (sic), offset, FALSE, FALSE)); + offset++; + } + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + } +} + +/*-----------------------------------------------------------------*/ +/* selectRegBank - emit code to select the register bank */ +/*-----------------------------------------------------------------*/ +static void +selectRegBank (short bank, bool keepFlags) +{ + /* if f.e. result is in carry */ + if (keepFlags) + { + emitcode ("anl", "psw,#0xE7"); + if (bank) + emitcode ("orl", "psw,#0x%02x", (bank << 3) & 0xff); + } + else + { + emitcode ("mov", "psw,#0x%02x", (bank << 3) & 0xff); + } +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool accuse = FALSE; + bool accPushed = FALSE; + bool resultInF0 = FALSE; + bool assignResultGenerated = FALSE; + + D (emitcode (";", "genCall")); + + dtype = operandType (IC_LEFT (ic)); + etype = getSpec (dtype); + /* if send set is not empty then assign */ + if (_G.sendSet) + { + if (IFFUNC_ISREENT (dtype)) + { + /* need to reverse the send set */ + genSend (reverseSet (_G.sendSet)); + } + else + { + genSend (_G.sendSet); + } + _G.sendSet = NULL; + } + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + } + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + if (swapBanks) + { + emitcode ("mov", "psw,#!constbyte", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + } + else + { + char *name = (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name); + emitcode ("mov", "r0,#%s", name); + emitcode ("mov", "r1,#(%s >> 8)", name); + emitcode ("mov", "r2,#(%s >> 16)", name); + } + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + if (IS_LITERAL (etype)) + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + else + { + emitcode ("lcall", "%s", (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? + OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + } + + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->accuse || + OP_SYMBOL (IC_RESULT (ic))->spildir || IS_BIT (etype))) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + accuse = assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + assignResultGenerated = TRUE; + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (accuse) + { + emitpush ("acc"); + accPushed = TRUE; + } + if (IS_BIT (etype) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + + /* unsaveRegisters from xstack needs acc, but */ + /* unsaveRegisters from stack needs this popped */ + if (accPushed && !options.useXstack) + { + emitpop ("acc"); + accPushed = FALSE; + } + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + { + if (accuse && !accPushed && options.useXstack) + { + /* xstack needs acc, but doesn't touch normal stack */ + emitpush ("acc"); + accPushed = TRUE; + } + unsaveRegisters (ic); + } + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && !assignResultGenerated) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + if (accPushed) + emitpop ("acc"); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + sym_link *etype; + bool swapBanks = FALSE; + bool resultInF0 = FALSE; + + D (emitcode (";", "genPcall")); + + dtype = operandType (IC_LEFT (ic))->next; + etype = getSpec (dtype); + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a not _naked function that is not using + the same register bank then we need to save the + destination registers on the stack */ + if (currFunc && dtype && !IFFUNC_ISNAKED (dtype) && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && !IFFUNC_ISISR (dtype)) + { + swapBanks = TRUE; + // need caution message to user here + } + + if (IS_LITERAL (etype)) + { + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + emitcode ("mov", "r0,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 0, 1)); + emitcode ("mov", "r1,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 1, 1)); + emitcode ("mov", "r2,#%s", aopLiteralLong (OP_VALUE (IC_LEFT (ic)), 2, 1)); + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else + { + emitcode ("lcall", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + } + } + else + { + if (IFFUNC_ISBANKEDCALL (dtype)) + { + if (IFFUNC_CALLEESAVES (dtype)) + { + werror (E_BANKED_WITH_CALLEESAVES); + } + else + { + aopOp (IC_LEFT (ic), ic, FALSE); + + emitpush (aopGet (IC_LEFT (ic), 0, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 1, FALSE, TRUE)); + emitpush (aopGet (IC_LEFT (ic), 2, FALSE, TRUE)); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + /* if send set is not empty then assign */ + if (_G.sendSet) + { + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + } + + if (swapBanks) + { + char buf[8] = ""; + int reg = ((FUNC_REGBANK (dtype)) << 3) & 0xff; + emitcode ("mov", "psw,#0x%02x", reg); + SNPRINTF (buf, 8, "0x%02x", reg + 2); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 1); + emitpop (buf); + SNPRINTF (buf, 8, "0x%02x", reg + 0); + emitpop (buf); + } + else + { + emitpop ("ar2"); + emitpop ("ar1"); + emitpop ("ar0"); + } + /* make the call */ + emitcode ("lcall", "__sdcc_banked_call"); + } + } + else if (_G.sendSet) /* the send set is not empty */ + { + symbol *callLabel = newiTempLabel (NULL); + symbol *returnLabel = newiTempLabel (NULL); + + /* create the return address on the stack */ + emitcode ("lcall", "!tlabel", labelKey2num (callLabel->key)); + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + emitLabel (callLabel); + _G.stack.pushed += 2; + + emitDummyCall(); + /* now push the function address */ + pushSide (IC_LEFT (ic), FARPTRSIZE, ic); + + /* send set is not empty: assign */ + genSend (reverseSet (_G.sendSet)); + _G.sendSet = NULL; + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("ret", ""); + _G.stack.pushed -= 4; + emitLabel (returnLabel); + } + else /* the send set is empty */ + { + /* now get the called address into dptr */ + aopOp (IC_LEFT (ic), ic, FALSE); + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + emitcode ("mov", "r0,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("mov", "dpl,r0"); + } + else + { + emitcode ("mov", "dpl,%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("mov", "dph,%s", aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + if (swapBanks) + { + emitcode ("mov", "psw,#0x%02x", ((FUNC_REGBANK (dtype)) << 3) & 0xff); + } + + /* make the call */ + emitcode ("lcall", "__sdcc_call_dptr"); + } + } + if (swapBanks) + { + selectRegBank (FUNC_REGBANK (currFunc->type), IS_BIT (etype)); + } + + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + !IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic))) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->accuse || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* adjust the stack for parameters if required */ + if (ic->parmBytes) + { + int i; + if (ic->parmBytes > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic))) && IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + emitcode ("mov", "F0,c"); + resultInF0 = TRUE; + } + + emitcode ("mov", "a,%s", spname); + emitcode ("add", "a,#0x%02x", (-ic->parmBytes) & 0xff); + emitcode ("mov", "%s,a", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + else + { + for (i = 0; i < ic->parmBytes; i++) + emitcode ("dec", "%s", spname); + if (options.useXstack) + _G.stack.xpushed -= ic->parmBytes; + else + _G.stack.pushed -= ic->parmBytes; + } + } + + /* if we had saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); + + if (IS_BIT (OP_SYM_ETYPE (IC_RESULT (ic)))) + { + if (resultInF0) + emitcode ("mov", "c,F0"); + + aopOp (IC_RESULT (ic), ic, FALSE); + assignResultValue (IC_RESULT (ic), IC_LEFT (ic)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool switchedPSW = FALSE; + int calleesaves_saved_register = -1; + int stackAdjust = sym->stack; + int accIsFree = sym->recvSize < 4; + char *freereg = NULL; + iCode *ric = (ic->next && ic->next->op == RECEIVE) ? ic->next : NULL; + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + + /* create the function header */ + emitcode (";", "-----------------------------------------"); + emitcode (";", " function %s", sym->name); + emitcode (";", "-----------------------------------------"); + + emitcode ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + _G.currentFunc = sym; + + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no prologue."); + return; + } + + /* here we need to generate the equates for the + register bank if required */ + if (FUNC_REGBANK (ftype) != rbank) + { + int i; + + rbank = FUNC_REGBANK (ftype); + for (i = 0; i < mcs51_nRegs; i++) + { + if (regs8051[i].type != REG_BIT) + { + if (EQ (regs8051[i].base, "0")) + emitcode ("", "%s !equ !constbyte", regs8051[i].dname, 8 * rbank + regs8051[i].offset); + else + emitcode ("", "%s !equ %s + !constbyte", regs8051[i].dname, regs8051[i].base, 8 * rbank + regs8051[i].offset); + } + } + } + + _G.stack.param_offset = 0; + _G.stack.offset = sym->stack; + _G.stack.xoffset = sym->xstack; + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + /* if this is an interrupt service routine then + save acc, b, dpl, dph */ + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("clr", "ea"); + } + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + { + emitpush ("bits"); + BitBankUsed = 1; + } + } + freeBitVect (rsavebits); + + if (!inExcludeList ("acc")) + emitpush ("acc"); + if (!inExcludeList ("b")) + emitpush ("b"); + if (!inExcludeList ("dpl")) + emitpush ("dpl"); + if (!inExcludeList ("dph")) + emitpush ("dph"); + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + pushReg (i, TRUE); + } + } + } + else + { + /* this function has a function call. We cannot + determine register usage so we will have to push the + entire bank */ + saveRBank (0, ic, FALSE); + if (options.parms_in_bank1) + { + for (i = 0; i < 8; i++) + { + emitpush (rb1regs[i]); + } + } + } + } + else + { + /* This ISR uses a non-zero bank. + * + * We assume that the bank is available for our + * exclusive use. + * + * However, if this ISR calls a function which uses some + * other bank, we must save that bank entirely. + */ + unsigned long banksToSave = 0; + + if (IFFUNC_HASFCALL (ftype)) + { + iCode *i; + int ix; + + for (i = ic; i; i = i->next) + { + sym_link *dtype = NULL; + + if (i->op == ENDFUNCTION) + { + /* we got to the end OK. */ + break; + } + + if (i->op == CALL) + { + dtype = operandType (IC_LEFT (i)); + } + if (i->op == PCALL) + { + /* This is a mess; we have no idea what + * register bank the called function might + * use. + * + * The only thing I can think of to do is + * throw a warning and hope. + */ +// werror (W_FUNCPTR_IN_USING_ISR); + dtype = operandType (IC_LEFT (i))->next; + } + if (dtype && FUNC_REGBANK (dtype) != FUNC_REGBANK (ftype)) + { + /* Mark this bank for saving. */ + if (FUNC_REGBANK (dtype) >= MAX_REGISTER_BANKS) + { + werror (E_NO_SUCH_BANK, FUNC_REGBANK (dtype)); + } + else + { + banksToSave |= (1 << FUNC_REGBANK (dtype)); + } + + /* And note that we don't need to do it in + * genCall. + */ + i->bankSaved = 1; + } + } + + if (banksToSave && options.useXstack) + { + /* Since we aren't passing it an ic, + * saveRBank will assume r0 is available to abuse. + * + * So switch to our (trashable) bank now, so + * the caller's R0 isn't trashed. + */ + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + switchedPSW = TRUE; + } + + for (ix = 0; ix < MAX_REGISTER_BANKS; ix++) + { + if (banksToSave & (1 << ix)) + { + saveRBank (ix, NULL, FALSE); + } + } + } + // TODO: this needs a closer look + SPEC_ISR_SAVED_BANKS (currFunc->etype) = banksToSave; + } + + /* Set the register bank to the desired value if nothing else */ + /* has done so yet. */ + if (!switchedPSW) + { + emitpush ("psw"); + emitcode ("mov", "psw,#!constbyte", (FUNC_REGBANK (ftype) << 3) & 0x00ff); + } + } + else + { + /* This is a non-ISR function. */ + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw */ + } + + /* The caller has already switched register banks if */ + /* necessary, so just handle the callee-saves option. */ + + /* if callee-save to be used for this function + then save the registers being used in this function */ + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + bool bits_pushed = FALSE; + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + /* remember one saved register for later usage */ + if (calleesaves_saved_register < 0) + calleesaves_saved_register = i; + bits_pushed = pushReg (i, bits_pushed); + _G.stack.param_offset--; + } + } + } + } + } + + if (fReentrant && !options.omitFramePtr) + { + if (options.useXstack) + { + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("mov", "r0,%s", spname); + emitcode ("inc", "%s", spname); + emitcode ("xch", "a,_bpx"); + emitcode ("movx", "@r0,a"); + emitcode ("inc", "r0"); + emitcode ("mov", "a,r0"); + emitcode ("xch", "a,_bpx"); + } + if (sym->stack) + { + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + else + { + if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + /* set up the stack */ + /* save the callers stack, but without pushed++ */ + emitcode ("push", "_bp"); + emitcode ("mov", "_bp,sp"); + } + } + } + + /* For some cases it is worthwhile to perform a RECEIVE iCode */ + /* before setting up the stack frame completely. */ + if (ric && ric->argreg == 1 && IC_RESULT (ric)) + { + symbol *rsym = OP_SYMBOL (IC_RESULT (ric)); + + if (rsym->isitmp) + { + if (rsym && rsym->regType == REG_CND) + rsym = NULL; + if (rsym && (rsym->accuse || rsym->ruonly)) + rsym = NULL; + if (rsym && (rsym->isspilt || rsym->nRegs == 0) && rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + } + + /* If the RECEIVE operand immediately spills to the first entry on the */ + /* stack, we can push it directly (since sp = _bp + 1 at this point) */ + /* rather than the usual @r0/r1 machinations. */ + if (!options.useXstack && rsym && rsym->onStack && rsym->stack == 1) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitpush (fReturn[ofs]); + _G.stack.pushed--; /* cancel out pushed++ from emitpush()*/ + } + stackAdjust -= sym->recvSize; + if (stackAdjust < 0) + { + assert (stackAdjust >= 0); + stackAdjust = 0; + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + /* If the RECEIVE operand is 4 registers, we can do the moves now */ + /* to free up the accumulator. */ + else if (rsym && rsym->nRegs && sym->recvSize == 4) + { + int ofs; + + genLine.lineElement.ic = ric; + D (emitcode (";", "genReceive")); + for (ofs = 0; ofs < sym->recvSize; ofs++) + { + emitcode ("mov", "%s,%s", rsym->regs[ofs]->name, fReturn[ofs]); + } + genLine.lineElement.ic = ic; + ric->generated = 1; + accIsFree = 1; + } + } + + /* If the accumulator is not free, we will need another register */ + /* to clobber. No need to worry about a possible conflict with */ + /* the above early RECEIVE optimizations since they would have */ + /* freed the accumulator if they were generated. */ + if (IFFUNC_CALLEESAVES (ftype)) + { + /* if it's a callee-saves function we need a saved register */ + if (calleesaves_saved_register >= 0) + { + freereg = REG_WITH_INDEX (calleesaves_saved_register)->dname; + } + } + else + { + /* not callee-saves, we can clobber r0 */ + freereg = "r0"; + } + + /* adjust the stack for the function */ + if (stackAdjust) + { + int i = stackAdjust & 0xff; + if (stackAdjust > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + } + else if (i > 4) + { + if (freereg) + { + emitcode ("xch", "a,%s", freereg); + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "sp,a"); + emitcode ("xch", "a,%s", freereg); + } + else + { + /* do it the hard way */ + while (i--) + emitcode ("inc", "sp"); + } + } + else + { + while (i--) + emitcode ("inc", "sp"); + } + } + + if (sym->xstack) + { + int i = sym->xstack & 0xff; + if (sym->xstack > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && accIsFree) + { + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", i & 0xff); + emitcode ("mov", "_spx,a"); + } + else if (i > 4) + { + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpush ("acc"); + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#0x%02x", i & 0xff); + emitcode ("mov", "_spx,a"); + if (freereg) + emitcode ("xch", "a,%s", freereg); + else + emitpop ("acc"); + } + else + { + while (i--) + emitcode ("inc", "_spx"); + } + } + + _G.stack.param_offset = options.useXstack ? _G.stack.xpushed : _G.stack.pushed; + _G.stack.pushedregs = _G.stack.pushed; + _G.stack.xpushedregs = _G.stack.xpushed; + _G.stack.pushed = 0; + _G.stack.xpushed = 0; +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool fReentrant = (IFFUNC_ISREENT (sym->type) || options.stackAuto); + lineNode *lineBodyEnd = genLine.lineCurr; + lineNode *linePrologueStart = NULL; + lineNode *lnp; + bitVect *regsUsed; + bitVect *regsUnneeded; + int idx; + + _G.currentFunc = NULL; + if (IFFUNC_ISNAKED (ftype)) + { + emitcode (";", "naked function: no epilogue."); + if (options.debug && currFunc) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + _G.stack.xpushed = _G.stack.xpushedregs; + _G.stack.pushed = _G.stack.pushedregs; + + if (fReentrant) + { + if (options.omitFramePtr) + { + bool cy_in_r0 = FALSE; + bool acc_in_r0 = FALSE; + + if (sym->stack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,sp"); + emitcode ("add", "a,#!constbyte", (-sym->stack) & 0xff); + emitcode ("mov", "sp,a"); + } + else + { + int i = sym->stack; + while (i--) + emitcode ("dec", "sp"); + } + if (sym->xstack > 3) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + if (!cy_in_r0) + emitcode ("mov", "r0,psw"); /* save cy in r0 */ + cy_in_r0 = TRUE; + } + if (getSize (OP_SYM_ETYPE (IC_LEFT (ic))) >= 4) + { + if (!acc_in_r0) + emitcode ("xch", "a,r0"); /* save a in r0 */ + acc_in_r0 = TRUE; + } + + emitcode ("mov", "a,_spx"); + emitcode ("add", "a,#!constbyte", (-sym->xstack) & 0xff); + emitcode ("mov", "_spx,a"); + } + else + { + int i = sym->xstack; + while (i--) + emitcode ("dec", "_spx"); + } + + if (acc_in_r0) + emitcode ("xch", "a,r0"); /* restore a from r0 */ + + if (cy_in_r0) + emitcode ("mov", "psw,r0"); /* restore c from r0 */ + } + else + { + if (options.useXstack) + { + if (sym->stack) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + if (sym->xstack || FUNC_HASSTACKPARM (ftype)) + { + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "r0,a"); + emitcode ("dec", "r0"); + emitcode ("movx", "a,@r0"); + emitcode ("xch", "a,_bpx"); + emitcode ("mov", "%s,r0", spname); //read before freeing stack space (interrupts) + } + } + else if (sym->stack || FUNC_HASSTACKPARM (ftype)) + { + if (sym->stack == 1) + emitcode ("dec", "sp"); + else if (sym->stack) + emitcode ("mov", "sp,_bp"); + emitcode ("pop", "_bp"); /* without pushed-- */ + } + } + } + + /* restore the register bank */ + if (IFFUNC_ISISR (ftype)) + { + if (!FUNC_REGBANK (ftype) || !options.useXstack) + { + /* Special case of ISR using non-zero bank with useXstack + * is handled below. + */ + emitpop ("psw"); + } + } + + if (IFFUNC_ISISR (ftype)) + { + bitVect *rsavebits; + + /* now we need to restore the registers */ + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (ftype)) + { + int i; + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (ftype)) + { + /* if any registers used */ + if (!bitVectIsZero (sym->regsUsed)) + { + /* restore the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + popReg (i, TRUE); + } + } + } + else + { + if (options.parms_in_bank1) + { + for (i = 7; i >= 0; i--) + { + emitpop (rb1regs[i]); + } + } + /* this function has a function call. We cannot + determine register usage so we will have to pop the + entire bank */ + unsaveRBank (0, ic, FALSE); + } + } + else + { + /* This ISR uses a non-zero bank. + * + * Restore any register banks saved by genFunction + * in reverse order. + */ + unsigned savedBanks = SPEC_ISR_SAVED_BANKS (currFunc->etype); + int ix; + + for (ix = MAX_REGISTER_BANKS - 1; ix >= 0; ix--) + { + if (savedBanks & (1 << ix)) + { + unsaveRBank (ix, NULL, FALSE); + } + } + + if (options.useXstack) + { + /* Restore bank AFTER calling unsaveRBank, + * since it can trash r0. + */ + emitpop ("psw"); + } + } + + if (!inExcludeList ("dph")) + emitpop ("dph"); + if (!inExcludeList ("dpl")) + emitpop ("dpl"); + if (!inExcludeList ("b")) + emitpop ("b"); + if (!inExcludeList ("acc")) + emitpop ("acc"); + + rsavebits = bitVectIntersect (bitVectCopy (mcs51_allBitregs ()), sym->regsUsed); + if (IFFUNC_HASFCALL (ftype) || !bitVectIsZero (rsavebits)) + { + if (!inExcludeList ("bits")) + emitpop ("bits"); + } + freeBitVect (rsavebits); + + /* weird but possible, one should better use a different priority */ + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + emitcode ("setb", "ea"); + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + emitcode ("reti", ""); + } + else + { + if (IFFUNC_CALLEESAVES (ftype)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i) || (mcs51_ptrRegReq && (i == R0_IDX || i == R1_IDX))) + emitpop (REG_WITH_INDEX (i)->dname); + } + } + else if (mcs51_ptrRegReq) + { + emitpop (REG_WITH_INDEX (R1_IDX)->dname); + emitpop (REG_WITH_INDEX (R0_IDX)->dname); + } + } + + if (IFFUNC_ISCRITICAL (ftype)) + { + if (IS_BIT (OP_SYM_ETYPE (IC_LEFT (ic)))) + { + emitcode ("rlc", "a"); /* save c in a */ + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + emitcode ("rrc", "a"); /* restore c from a */ + } + else + { + emitpop ("psw"); /* restore ea via c in psw */ + emitcode ("mov", "ea,c"); + } + } + + /* if debug then send end of function */ + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (IFFUNC_ISBANKEDCALL (ftype)) + { + emitcode ("ljmp", "__sdcc_banked_ret"); + } + else + { + emitcode ("ret", ""); + } + } + + wassertl (_G.stack.pushed == 0, "stack over/underflow"); + wassertl (_G.stack.xpushed == 0, "xstack over/underflow"); + + if (!port->peep.getRegsRead || !port->peep.getRegsWritten || options.nopeep) + return; + + /* If this was an interrupt handler using bank 0 that called another */ + /* function, then all registers must be saved; nothing to optimize. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype) && !FUNC_REGBANK (ftype)) + return; + + /* There are no push/pops to optimize if not callee-saves or ISR */ + if (!(FUNC_CALLEESAVES (ftype) || FUNC_ISISR (ftype))) + return; + + /* If there were stack parameters, we cannot optimize without also */ + /* fixing all of the stack offsets; this is too dificult to consider. */ + if (FUNC_HASSTACKPARM (ftype)) + return; + + /* Compute the registers actually used */ + regsUsed = newBitVect (mcs51_nRegs); + lnp = lineBodyEnd; + while (lnp) + { + /* Remove change of register bank if no registers used */ + if (lnp->ic && lnp->ic->op == FUNCTION && + !strncmp (lnp->line, "mov", 3) && + bitVectFirstBit (port->peep.getRegsWritten (lnp)) == CND_IDX && + !bitVectBitsInCommon (mcs51_allBankregs (), regsUsed) && + !IFFUNC_HASFCALL (ftype)) + { + emitcode (";", "eliminated unneeded mov psw,# (no regs used in bank)"); + connectLine (lnp->prev, lnp->next); + } + else + { + regsUsed = bitVectUnion (regsUsed, port->peep.getRegsWritten (lnp)); + } + + if (lnp->ic && lnp->ic->op == FUNCTION) + { + if (!lnp->prev || (lnp->prev->ic && lnp->prev->ic->op != FUNCTION)) + break; + } + lnp = lnp->prev; + } + linePrologueStart = lnp; + + /* If this was an interrupt handler that called another function */ + /* function, then assume A, B, DPH, & DPL may be modified by it. */ + if (IFFUNC_ISISR (ftype) && IFFUNC_HASFCALL (ftype)) + { + regsUsed = bitVectSetBit (regsUsed, DPL_IDX); + regsUsed = bitVectSetBit (regsUsed, DPH_IDX); + regsUsed = bitVectSetBit (regsUsed, B_IDX); + regsUsed = bitVectSetBit (regsUsed, A_IDX); + regsUsed = bitVectSetBit (regsUsed, CND_IDX); + } + + /* Remove the unneeded push/pops */ + regsUnneeded = newBitVect (mcs51_nRegs); + for (lnp = genLine.lineCurr; lnp != linePrologueStart; lnp = lnp->prev) + { + if (lnp->ic) + { + if (lnp->ic && (lnp->ic->op == FUNCTION) && !strncmp (lnp->line, "push", 4)) + { + idx = bitVectFirstBit (port->peep.getRegsRead (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + if (lnp->ic && (lnp->ic->op == ENDFUNCTION) && !strncmp (lnp->line, "pop", 3)) + { + idx = bitVectFirstBit (port->peep.getRegsWritten (lnp)); + if (idx >= 0 && !bitVectBitValue (regsUsed, idx)) + { + connectLine (lnp->prev, lnp->next); + regsUnneeded = bitVectSetBit (regsUnneeded, idx); + } + } + } + } + + for (idx = 0; idx < regsUnneeded->size; idx++) + if (bitVectBitValue (regsUnneeded, idx)) + emitcode (";", "eliminated unneeded push/pop %s", REG_WITH_INDEX (idx)->dname); + + freeBitVect (regsUnneeded); + freeBitVect (regsUsed); +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0, pushed = 0; + bool pushedA = FALSE; + + D (emitcode (";", "genRet")); + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + if (IS_BIT (_G.currentFunc->etype)) + { + if (!IS_OP_RUONLY (IC_LEFT (ic))) + toCarry (IC_LEFT (ic)); + } + else + { + while (size--) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_DPTR) + { + /* #NOCHANGE */ + emitpush (aopGet (IC_LEFT (ic), offset++, FALSE, TRUE)); + pushed++; + } + else + { + const char *l = aopGet (IC_LEFT (ic), offset, FALSE, FALSE); + if (!EQ (fReturn[offset], l)) + if (fReturn[offset][0] == 'r' && (AOP_TYPE (IC_LEFT (ic)) == AOP_REG || AOP_TYPE (IC_LEFT (ic)) == AOP_R0 || AOP_TYPE (IC_LEFT (ic)) == AOP_R1)) + emitcode ("mov", "a%s,%s", fReturn[offset], l); // use register's direct address instead of name + else + emitcode ("mov", "%s,%s", fReturn[offset], l); + if (size && !strcmp(fReturn[offset], "a") && aopGetUsesAcc (IC_LEFT (ic), offset+1)) + { + emitpush ("acc"); + pushedA = TRUE; + } + offset++; + } + } + if (pushedA) + { + emitpop ("acc"); + } + + while (pushed) + { + pushed--; + if (!EQ (fReturn[pushed], "a")) + emitpop (fReturn[pushed]); + else + emitpop ("acc"); + } + } + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + emitcode ("ljmp", "!tlabel", labelKey2num (returnLabel->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + emitLabel (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (iCode * ic) +{ + emitcode ("ljmp", "!tlabel", labelKey2num (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)), offset; + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + D (emitcode (";", "genPlusIncr")); + + /* if increment >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && + !isOperandVolatile (IC_RESULT (ic), FALSE) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("inc", "%s", l); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("clr", "a"); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("inc", "%s", l); + + for(offset = 2; size > 2; size--, offset++) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,%s,!tlabel", l, zero, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + + l = aopGet (IC_RESULT (ic), offset, FALSE, FALSE); + emitcode ("inc", "%s", l); + } + + emitLabel (tlbl); + return TRUE; + } + + /* if result is dptr */ + if ((AOP_TYPE (IC_RESULT (ic)) == AOP_STR) && + (AOP_SIZE (IC_RESULT (ic)) == 2) && + !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[0], "dpl", 4) && !strncmp (AOP (IC_RESULT (ic))->aopu.aop_str[1], "dph", 4)) + { + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + return FALSE; + + if (icount > 9) + return FALSE; + + if ((AOP_TYPE (IC_LEFT (ic)) != AOP_DIR) && (icount > 5)) + return FALSE; + + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 0, FALSE, FALSE), 0); + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), 1, FALSE, FALSE), 1); + while (icount--) + emitcode ("inc", "dptr"); + + return TRUE; + } + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if (icount > 4) + return FALSE; + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + if (icount > 3) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("add", "a,#!constbyte", ((char) icount) & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + else + { + while (icount--) + { + emitcode ("inc", "%s", aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + } + } + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("inc", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +static void +outBitAcc (operand * result) +{ + symbol *tlbl = newiTempLabel (NULL); + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + aopPut (result, "a", 0); + } + else + { + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + emitcode ("mov", "a,%s", one); + emitLabel (tlbl); + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlusBits - generates code for addition of two bits */ +/*-----------------------------------------------------------------*/ +static void +genPlusBits (iCode * ic) +{ + D (emitcode (";", "genPlusBits")); + + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("addc", "a,%s", zero); + outAcc (IC_RESULT (ic)); + } +} + +static void +adjustArithmeticResult (iCode * ic) +{ + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + if (opIsGptr (IC_LEFT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + if (opIsGptr (IC_RIGHT (ic))) + { + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_RIGHT (ic), GPTRSIZE - 1, FALSE, FALSE), GPTRSIZE - 1); + } + return; + } + + dbuf_init (&dbuf, 128); + if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && + IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && + !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic))) && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_LEFT (ic) && AOP_SIZE (IC_LEFT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + else if (IC_RIGHT (ic) && AOP_SIZE (IC_RIGHT (ic)) < GPTRSIZE && !sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (ic)))) + { + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_RIGHT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + } + dbuf_destroy (&dbuf); + } +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, offset = 0; + int skip_bytes = 0; + char *add = "add"; + bool swappedLR = FALSE; + operand *leftOp, *rightOp; + operand *op; + + D (emitcode (";", "genPlus")); + + /* special cases :- */ + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || (AOP_NEEDSACC (IC_LEFT (ic))) || AOP_TYPE (IC_RIGHT (ic)) == AOP_ACC) + { + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + swappedLR = TRUE; + } + + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genPlusBits (ic); + goto release; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + /* if result in bit space */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + if (ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) != 0L) + emitcode ("cpl", "c"); + outBitC (IC_RESULT (ic)); + } + else + { + size = getDataSize (IC_RESULT (ic)); + while (size--) + { + MOVA (aopGet (IC_RIGHT (ic), offset, FALSE, FALSE)); + emitcode ("addc", "a,%s", zero); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + goto release; + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + op = IC_LEFT (ic); + + /* if this is an add for an array access + at a 256 byte boundary */ + if (2 == size + && AOP_TYPE (op) == AOP_IMMD + && IS_SYMOP (op) + && IS_SPEC (OP_SYM_ETYPE (op)) && SPEC_ABSA (OP_SYM_ETYPE (op)) && (SPEC_ADDR (OP_SYM_ETYPE (op)) & 0xff) == 0) + { + D (emitcode (";", "genPlus aligned array")); + aopPut (IC_RESULT (ic), aopGet (rightOp, 0, FALSE, FALSE), 0); + + if (1 == getDataSize (IC_RIGHT (ic))) + { + aopPut (IC_RESULT (ic), aopGet (leftOp, 1, FALSE, FALSE), 1); + } + else + { + MOVA (aopGet (IC_LEFT (ic), 1, FALSE, FALSE)); + emitcode ("add", "a,%s", aopGet (rightOp, 1, FALSE, FALSE)); + aopPut (IC_RESULT (ic), "a", 1); + } + goto release; + } + + /* if the lower bytes of a literal are zero skip the addition */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + while ((0 == ((unsigned int) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit) & (0xff << skip_bytes * 8))) && + (skip_bytes + 1 < size)) + { + skip_bytes++; + } + if (skip_bytes) + D (emitcode (";", "genPlus shortcut")); + } + + while (size--) + { + if (offset >= skip_bytes) + { + if (aopGetUsesAcc (leftOp, offset) && aopGetUsesAcc (rightOp, offset)) + { + bool pushedB; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("xch", "a,b"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,b"); + popB (pushedB); + } + else if (aopGetUsesAcc (leftOp, offset)) + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode (add, "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + } + aopPut (IC_RESULT (ic), "a", offset); + add = "addc"; /* further adds must propagate carry */ + } + else + { + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) || isOperandVolatile (IC_RESULT (ic), FALSE)) + { + /* just move */ + aopPut (IC_RESULT (ic), aopGet (leftOp, offset, FALSE, FALSE), offset); + } + } + offset++; + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + if (swappedLR) + swapOperands (&IC_LEFT (ic), &IC_RIGHT (ic)); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genMinusDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + + /* will try to generate a decrement */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = (unsigned int) ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) > 4) + return FALSE; + + D (emitcode (";", "genMinusDec")); + + /* if decrement >=16 bits in register or direct space */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_REG || + AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || + (IS_AOP_PREG (IC_LEFT (ic)) && !AOP_NEEDSACC (IC_LEFT (ic)))) && + sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && (size > 1) && (icount == 1)) + { + symbol *tlbl; + const char *l; + + tlbl = newiTempLabel (NULL); + l = aopGet (IC_RESULT (ic), LSB, FALSE, FALSE); + emitcode ("dec", "%s", l); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("mov", "a,#!constbyte", 0xff); + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB16, FALSE, FALSE); + emitcode ("dec", "%s", l); + if (size > 2) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + l = aopGet (IC_RESULT (ic), MSB24, FALSE, FALSE); + emitcode ("dec", "%s", l); + } + if (size > 3) + { + if (EQ (l, "acc")) + { + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + else if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG || IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("cjne", "%s,#!constbyte,!tlabel", l, 0xff, labelKey2num (tlbl->key)); + } + else + { + emitcode ("cjne", "a,%s,!tlabel", l, labelKey2num (tlbl->key)); + } + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), MSB32, FALSE, FALSE)); + } + emitLabel (tlbl); + return TRUE; + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + const char *l; + + if (aopGetUsesAcc (IC_LEFT (ic), 0)) + { + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + l = "a"; + } + else + { + l = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + } + + while (icount--) + { + emitcode ("dec", "%s", l); + } + + if (AOP_NEEDSACC (IC_RESULT (ic))) + aopPut (IC_RESULT (ic), "a", 0); + + return TRUE; + } + + if (icount == 1) + { + MOVA (aopGet (IC_LEFT (ic), 0, FALSE, FALSE)); + emitcode ("dec", "a"); + aopPut (IC_RESULT (ic), "a", 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* addSign - complete with sign */ +/*-----------------------------------------------------------------*/ +static void +addSign (operand * result, int offset, int sign) +{ + int size = (getDataSize (result) - offset); + if (size > 0) + { + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + { + aopPut (result, "a", offset++); + } + } + else + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genMinusBits - generates code for subtraction of two bits */ +/*-----------------------------------------------------------------*/ +static void +genMinusBits (iCode * ic) +{ + symbol *lbl = newiTempLabel (NULL); + + D (emitcode (";", "genMinusBits")); + + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + emitcode ("jnb", "%s,!tlabel", AOP (IC_RIGHT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + outBitC (IC_RESULT (ic)); + } + else + { + emitcode ("mov", "c,%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + emitcode ("subb", "a,acc"); + emitcode ("jnb", "%s,!tlabel", AOP (IC_LEFT (ic))->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("inc", "a"); + emitLabel (lbl); + aopPut (IC_RESULT (ic), "a", 0); + addSign (IC_RESULT (ic), MSB16, SPEC_USIGN (getSpec (operandType (IC_RESULT (ic))))); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (iCode * ic) +{ + int size, offset = 0; + + D (emitcode (";", "genMinus")); + + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* special cases :- */ + /* if both left & right are in bit space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + genMinusBits (ic); + goto release; + } + + /* if I can do a decrement instead + of subtract then GOOD for ME */ + if (genMinusDec (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + + /* if literal, add a,#-lit, else normal subb */ + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + unsigned long long lit = 0L; + bool useCarry = FALSE; + + lit = ullFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + lit = -(long long) lit; + + while (size--) + { + if (useCarry || ((lit >> (offset * 8)) & 0x0ffll)) + { + MOVA (aopGet (IC_LEFT (ic), offset, FALSE, FALSE)); + if (!offset && !size && lit == (unsigned long long) - 1) + { + emitcode ("dec", "a"); + } + else if (!useCarry) + { + /* first add without previous c */ + emitcode ("add", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + useCarry = TRUE; + } + else + { + emitcode ("addc", "a,#!constbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + aopPut (IC_RESULT (ic), "a", offset++); + } + else + { + /* no need to add zeroes */ + if (!sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)))) + { + aopPut (IC_RESULT (ic), aopGet (IC_LEFT (ic), offset, FALSE, FALSE), offset); + } + offset++; + } + } + } + else + { + operand *leftOp, *rightOp; + + leftOp = IC_LEFT (ic); + rightOp = IC_RIGHT (ic); + + while (size--) + { + if (aopGetUsesAcc (rightOp, offset)) + { + if (aopGetUsesAcc (leftOp, offset)) + { + bool pushedB; + + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + pushedB = pushB (); + emitcode ("mov", "b,a"); + if (offset == 0) + CLRC; + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,b"); + popB (pushedB); + } + else + { + /* reverse subtraction with 2's complement */ + if (offset == 0) + emitcode ("setb", "c"); + else + emitcode ("cpl", "c"); + wassertl (!aopGetUsesAcc (leftOp, offset), "accumulator clash"); + MOVA (aopGet (rightOp, offset, FALSE, FALSE)); + emitcode ("subb", "a,%s", aopGet (leftOp, offset, FALSE, FALSE)); + emitcode ("cpl", "a"); + if (size) /* skip if last byte */ + emitcode ("cpl", "c"); + } + } + else + { + MOVA (aopGet (leftOp, offset, FALSE, FALSE)); + if (offset == 0) + CLRC; + emitcode ("subb", "a,%s", aopGet (rightOp, offset, FALSE, FALSE)); + } + + aopPut (IC_RESULT (ic), "a", offset++); + } + } + + adjustArithmeticResult (ic); + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + freeAsmop (IC_RIGHT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result) +{ + D (emitcode (";", "genMultbits")); + + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8*8=8/16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + symbol *lbl; + int size = AOP_SIZE (result); + bool runtimeSign, compiletimeSign; + bool lUnsigned, rUnsigned, pushedB; + + D (emitcode (";", "genMultOneByte")); + + if (size < 1 || size > 2) + { + /* this should never happen */ + fprintf (stderr, "size!=1||2 (%d) in %s at line:%d \n", AOP_SIZE (result), __FILE__, lineno); + exit (EXIT_FAILURE); + } + + /* (if two literals: the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT || AOP_TYPE (right) == AOP_ACC) + { + operand *t = right; + right = left; + left = t; + /* emitcode (";", "swapped left and right"); */ + } + /* if no literal, unsigned on the right: shorter code */ + if (AOP_TYPE (right) != AOP_LIT && SPEC_USIGN (getSpec (operandType (left)))) + { + operand *t = right; + right = left; + left = t; + } + + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + if (size == 1 /* no, this is not a bug; with a 1 byte result there's + no need to take care about the signedness! */ + || (lUnsigned && rUnsigned)) + { + /* just an unsigned 8 * 8 = 8 multiply + or 8u * 8u = 16u */ + /* emitcode (";","unsigned"); */ + /* TODO: check for accumulator clash between left & right aops? */ + + /*if (AOP_TYPE (right) == AOP_ACC) + MOVB (aopGet (left, 0, FALSE, FALSE)); + else*/ if (AOP_TYPE (right) == AOP_LIT) + { + /* moving to accumulator first helps peepholes */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + MOVB (aopGet (right, 0, FALSE, FALSE)); + } + else + { + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + } + + emitcode ("mul", "ab"); + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); + return; + } + + /* we have to do a signed multiply */ + /* emitcode (";", "signed"); */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#!constbyte", -val); + else + emitcode ("mov", "b,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) /* emitcode (";", "signed"); */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#!constbyte", -val); + else + emitcode ("mov", "a,#!constbyte", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the multiplication */ + emitcode ("mul", "ab"); + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + if (size != 2) + emitcode ("inc", "a"); /* inc doesn't set carry flag */ + else + { + emitcode ("add", "a,#0x01"); /* this sets carry flag */ + emitcode ("xch", "a,b"); + emitcode ("cpl", "a"); /* msb 2's complement */ + emitcode ("addc", "a,#0x00"); + emitcode ("xch", "a,b"); + } + emitLabel (lbl); + } + aopPut (result, "a", 0); + if (size == 2) + aopPut (result, "b", 1); + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMult")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ +#if 0 // one of them can be a sloc shared with the result + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) +#else + if (getSize (operandType (left)) == 1 && getSize (operandType (right)) == 1) +#endif + { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + fprintf (stderr, "left: %d right: %d\n", getSize (OP_SYMBOL (left)->type), getSize (OP_SYMBOL (right)->type)); + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genDivbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + bool accuse = FALSE; + bool pushedA = FALSE; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genDivOneByte")); + + /* Why is it necessary that genDivOneByte() can return an int result? + Have a look at: + + volatile unsigned char uc; + volatile signed char sc1, sc2; + volatile int i; + + uc = 255; + sc1 = -1; + i = uc / sc1; + + Or: + + sc1 = -128; + sc2 = -1; + i = sc1 / sc2; + + In all cases a one byte result would overflow, the following cast to int + would return the wrong result. + + Two possible solution: + a) cast operands to int, if ((unsigned) / (signed)) or + ((signed) / (signed)) + b) return an 16 bit signed int; this is what we're doing here! + */ + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + if (!lUnsigned) + { + if (AOP_TYPE (left) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + if (val < 0) + compiletimeSign = TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + if (!rUnsigned) + { + if (AOP_TYPE (right) == AOP_LIT) + { + /* signed literal */ + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + if (val < 0) + compiletimeSign ^= TRUE; + } + else + /* signed but not literal */ + runtimeSign = TRUE; + } + + /* initialize F0, which stores the runtime sign */ + if (runtimeSign) + { + if (compiletimeSign) + emitcode ("setb", "F0"); /* set sign flag */ + else + emitcode ("clr", "F0"); /* reset sign flag */ + } + + /* save the signs of the operands */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (right)->aopu.aop_lit); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + emitcode ("mov", "a,#0x%02x", -val); + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "F0"); /* complement sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the division */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* lsb 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + accuse = aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + if (accuse) + { + emitpush ("acc"); + pushedA = TRUE; + } + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + { + if (aopPutUsesAcc (result, "#0xff", offset)) + { + emitpush ("acc"); + pushedA = TRUE; + } + while (size--) + aopPut (result, "#0xff", offset++); + } + } + } + else + { + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + } + + if (pushedA) + emitpop ("acc"); + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genDiv")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genModbits :- modulus of bits */ +/*-----------------------------------------------------------------*/ +static void +genModbits (operand * left, operand * right, operand * result) +{ + bool pushedB; + + D (emitcode (";", "genModbits")); + + pushedB = pushB (); + + /* the result must be bit */ + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + + emitcode ("div", "ab"); + emitcode ("mov", "a,b"); + emitcode ("rrc", "a"); + + popB (pushedB); + + aopPut (result, "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + bool lUnsigned, rUnsigned, pushedB; + bool runtimeSign, compiletimeSign; + symbol *lbl; + int size, offset; + + D (emitcode (";", "genModOneByte")); + + size = AOP_SIZE (result) - 1; + offset = 1; + lUnsigned = SPEC_USIGN (getSpec (operandType (left))); + rUnsigned = SPEC_USIGN (getSpec (operandType (right))); + + /* if right is a literal, check it for 2^n */ + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned char val = abs ((int) operandLitValue (right)); + symbol *lbl2 = NULL; + + switch (val) + { + case 1: /* sometimes it makes sense (on tricky code and hardware)... */ + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + if (lUnsigned) + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "modulus of unsigned char by 2^n literal shouldn't be processed here"); + /* because iCode should have been changed to genAnd */ + /* see file "SDCCopt.c", function "convertToFcall()" */ + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("mov", "c,acc.7"); + emitcode ("anl", "a,#0x%02x", val - 1); + lbl = newiTempLabel (NULL); + emitcode ("jz", "!tlabel", labelKey2num (lbl->key)); + emitcode ("jnc", "!tlabel", labelKey2num (lbl->key)); + emitcode ("orl", "a,#0x%02x", 0xff ^ (val - 1)); + if (size) + { + int size2 = size; + int offs2 = offset; + + aopPut (result, "a", 0); + while (size2--) + aopPut (result, "#0xff", offs2++); + lbl2 = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl2->key)); + } + emitLabel (lbl); + aopPut (result, "a", 0); + while (size--) + aopPut (result, zero, offset++); + if (lbl2) + { + emitLabel (lbl2); + } + return; + + default: + break; + } + } + + pushedB = pushB (); + + /* signed or unsigned */ + if (lUnsigned && rUnsigned) + { + /* unsigned is easy */ + MOVB (aopGet (right, 0, FALSE, FALSE)); + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("div", "ab"); + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + + popB (pushedB); + return; + } + + /* signed is a little bit more difficult */ + + /* now sign adjust for both left & right */ + + /* modulus: sign of the right operand has no influence on the result! */ + if (AOP_TYPE (right) == AOP_LIT) + { + signed char val = (signed char) operandLitValue (right); + + if (!rUnsigned && val < 0) + emitcode ("mov", "b,#0x%02x", -val); + else + emitcode ("mov", "b,#0x%02x", (unsigned char) val); + } + else /* not literal */ + { + if (rUnsigned) + emitcode ("mov", "b,%s", aopGet (right, 0, FALSE, FALSE)); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + emitcode ("mov", "b,a"); + } + } + + /* let's see what's needed: */ + /* apply negative sign during runtime */ + runtimeSign = FALSE; + /* negative sign from literals */ + compiletimeSign = FALSE; + + /* sign adjust left side */ + if (AOP_TYPE (left) == AOP_LIT) + { + signed char val = (char) ulFromVal (AOP (left)->aopu.aop_lit); + + if (!lUnsigned && val < 0) + { + compiletimeSign = TRUE; /* set sign flag */ + emitcode ("mov", "a,#0x%02x", -val); + } + else + emitcode ("mov", "a,#0x%02x", (unsigned char) val); + } + else /* ! literal */ + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + + if (!lUnsigned) + { + runtimeSign = TRUE; + emitcode ("clr", "F0"); /* clear sign flag */ + + lbl = newiTempLabel (NULL); + emitcode ("jnb", "acc.7,!tlabel", labelKey2num (lbl->key)); + emitcode ("setb", "F0"); /* set sign flag */ + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + } + } + + /* now the modulus */ + emitcode ("div", "ab"); + + if (runtimeSign || compiletimeSign) + { + emitcode ("mov", "a,b"); + lbl = newiTempLabel (NULL); + if (runtimeSign) + emitcode ("jnb", "F0,!tlabel", labelKey2num (lbl->key)); + emitcode ("cpl", "a"); /* 2's complement */ + emitcode ("inc", "a"); + emitLabel (lbl); + + aopPut (result, "a", 0); + if (size > 0) + { + /* msb is 0x00 or 0xff depending on the sign */ + if (runtimeSign) + { + emitcode ("mov", "c,F0"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + else /* compiletimeSign */ + while (size--) + aopPut (result, "#0xff", offset++); + } + } + else + { + aopPut (result, "b", 0); + while (size--) + aopPut (result, zero, offset++); + } + + popB (pushedB); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + D (emitcode (";", "genMod")); + + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genModbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, const char *jval, operand * left, operand * right, operand * result, iCode * popIc) +{ + symbol *jlbl; + symbol *tlbl = newiTempLabel (NULL); + char *inst; + + /* if there is something to be popped then do it first */ + popForBranch (popIc, TRUE); + + D (emitcode (";", "genIfxJump")); + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + inst = ((EQ (jval, "a") ? "jz" : (EQ (jval, "c") ? "jnc" : "jnb"))); + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + inst = ((EQ (jval, "a") ? "jnz" : (EQ (jval, "c") ? "jc" : "jb"))); + } + if (EQ (inst, "jb") || EQ (inst, "jnb")) + emitcode (inst, "%s,!tlabel", jval, labelKey2num (tlbl->key)); + else + emitcode (inst, "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (jlbl->key)); + emitLabel (tlbl); + + /* mark the icode as generated */ + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign, iCode * ic) +{ + int size, offset = 0; + unsigned long long lit = 0L; + bool rightInB; + + D (emitcode (";", "genCmp")); + + /* if left & right are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + /* generic pointers require special handling since all NULL pointers must compare equal */ + else if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < GPTRSIZE) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + } + else + { + /* subtract right from left if at the + end the carry flag is set then we know that + left is greater than right */ + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + /* if unsigned char cmp with lit, do cjne left,#right,zz */ + if (size == 1 && !sign && AOP_TYPE (right) == AOP_LIT && AOP_TYPE (left) != AOP_DIR && AOP_TYPE (left) != AOP_STR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + symbol *lbl = newiTempLabel (NULL); + emitcode ("cjne", "%s,%s,!tlabel", l, aopGet (right, offset, FALSE, FALSE), labelKey2num (lbl->key)); + Safe_free (l); + emitLabel (lbl); + } + else + { + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + /* optimize if(x < 0) or if(x >= 0) */ + if (lit == 0ll) + { + if (!sign) + { + CLRC; + } + else + { + MOVA (aopGet (left, AOP_SIZE (left) - 1, FALSE, FALSE)); + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx) + { + genIfxJump (ifx, "acc.7", left, right, result, ic->next); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); + + return; + } + else + { + emitcode ("rlc", "a"); + } + } + goto release; + } + else + { + //nonzero literal + int bytelit = ((lit >> (offset * 8)) & 0x0ffll); + while (size && (bytelit == 0)) + { + offset++; + bytelit = ((lit >> (offset * 8)) & 0x0ffll); + size--; + } + CLRC; + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + emitcode ("subb", "a,#0x%02x", 0x80 ^ (unsigned int) ((lit >> (offset * 8)) & 0x0ffll)); + } + else + { + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + offset++; + } + goto release; + } + } + CLRC; + while (size--) + { + bool pushedB = FALSE; + rightInB = aopGetUsesAcc (right, offset); + if (rightInB) + { + pushedB = pushB (); + emitcode ("mov", "b,%s", aopGet (right, offset, FALSE, FALSE)); + } + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (sign && size == 0) + { + emitcode ("xrl", "a,#0x80"); + if (!rightInB) + { + pushedB = pushB (); + rightInB++; + MOVB (aopGet (right, offset, FALSE, FALSE)); + } + emitcode ("xrl", "b,#0x80"); + emitcode ("subb", "a,b"); + } + else + { + if (rightInB) + emitcode ("subb", "a,b"); + else + emitcode ("subb", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + if (rightInB) + popB (pushedB); + offset++; + } + } + } + +release: + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + } + else + { + /* if the result is used in the next + ifx conditional branch then generate + code a little differently */ + if (ifx) + { + genIfxJump (ifx, "c", NULL, NULL, result, ic->next); + } + else + { + outBitC (result); + } + /* leave the result in acc */ + } +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpGt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (result, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + + genCmp (right, left, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign = 0; + + D (emitcode (";", "genCmpLt")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !((SPEC_USIGN (letype) && !(IS_CHAR (letype) && IS_LITERAL (letype))) || + (SPEC_USIGN (retype) && !(IS_CHAR (retype) && IS_LITERAL (retype)))); + } + /* assign the asmops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + genCmp (left, right, result, ifx, sign, ic); + + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* gencjneshort - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjneshort (operand * left, operand * right, symbol * lbl) +{ + int size = max (AOP_SIZE (left), AOP_SIZE (right)); + int offset = 0; + + D (emitcode (";", "gencjneshort")); + + /* if the left side is a immediate/literal or + if the right is in a pointer register and left is not */ + if (IS_AOP_IMMEDIATE (left) || + (AOP_TYPE (left) == AOP_DIR && !IS_AOP_IMMEDIATE (right)) || + (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + operand *t = right; + right = left; + left = t; + } + + /* generic pointers require special handling since all NULL pointers must compare equal */ + if (opIsGptr (left) || opIsGptr (right)) + { + /* push right */ + while (offset < size) + { + emitpush (aopGet (right, offset++, FALSE, TRUE)); + } + loadDptrFromOperand (left, TRUE); + emitcode ("lcall", "___gptr_cmp"); + for (offset = 0; offset < GPTRSIZE; offset++) + emitpop (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + + /* if the right side is a literal then anything goes */ + else if (IS_AOP_IMMEDIATE (right) && + AOP_TYPE (left) != AOP_DIR && !IS_AOP_IMMEDIATE (left)) + { + while (size--) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + const char *r = aopGet (right, offset, FALSE, FALSE); + if (EQ (l, "a") && EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "%s,%s,!tlabel", l, r, labelKey2num (lbl->key)); + Safe_free (l); + offset++; + } + } + + /* if the right side is in a register or in direct space or + if the left is a pointer register & right is not */ + else if (AOP_TYPE (right) == AOP_REG || + AOP_TYPE (right) == AOP_DIR || + IS_AOP_IMMEDIATE (right) || + (IS_AOP_PREG (left) && !IS_AOP_PREG (right))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int val[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + bool chk[8] = {TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; + int rsize = AOP_SIZE (right); + int pidx = -1; + int lidx, cidx; + unsigned long long lit = ullFromVal (AOP (right)->aopu.aop_lit); + + switch (rsize) + { + case 8: + val[7] = (lit >> 56) & 0xff; + chk[7] = TRUE; + val[6] = (lit >> 48) & 0xff; + chk[6] = TRUE; + val[5] = (lit >> 40) & 0xff; + chk[5] = TRUE; + val[4] = (lit >> 32) & 0xff; + chk[4] = TRUE; + //fallthrough + case 4: + val[3] = (lit >> 24) & 0xff; + chk[3] = TRUE; + //fallthrough + case 3: + val[2] = (lit >> 16) & 0xff; + chk[2] = TRUE; + //fallthrough + case 2: + val[1] = (lit >> 8) & 0xff; + chk[1] = TRUE; + //fallthrough + default: + val[0] = (lit >> 0) & 0xff; + chk[0] = TRUE; + } + if (optimize.codeSize && (rsize > 1)) + { + if ((!chk[0] || val[0] == 0x00) && + (!chk[1] || val[1] == 0x00) && + (!chk[2] || val[2] == 0x00) && + (!chk[3] || val[3] == 0x00) && + (!chk[4] || val[4] == 0x00) && + (!chk[5] || val[5] == 0x00) && + (!chk[6] || val[6] == 0x00) && + (!chk[7] || val[7] == 0x00)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("orl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("jnz", "%05d$", lbl->key + 100); + return; + } + if ((!chk[0] || val[0] == 0xFF) && + (!chk[1] || val[1] == 0xFF) && + (!chk[2] || val[2] == 0xFF) && + (!chk[3] || val[3] == 0xFF) && + (!chk[4] || val[4] == 0xFF) && + (!chk[5] || val[5] == 0xFF) && + (!chk[6] || val[6] == 0xFF) && + (!chk[7] || val[7] == 0xFF)) + { + MOVA(aopGet(left, 0, FALSE, FALSE)); + for (cidx = 1; cidx < size; cidx++) + if (chk[cidx]) + emitcode ("anl", "a,%s", aopGet(left, cidx, FALSE, FALSE)); + emitcode ("cjne", "a,#0xFF,%05d$", lbl->key + 100); + return; + } + } + + for (lidx = 0; lidx < size; lidx++) + { + if (chk[lidx] && !aopGetUsesAcc(left, lidx)) + { + if (pidx >= 0) + { + if (val[pidx] == val[lidx]) + { + chk[lidx] = FALSE; + } + if ((~val[pidx] & 0xff) == val[lidx]) + { + emitcode ("cpl", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] + 1) & 0xff) == val[lidx]) + { + emitcode ("inc", "a"); + chk[lidx] = FALSE; + } + else if (((val[pidx] - 1) & 0xff) == val[lidx]) + { + emitcode ("dec", "a"); + chk[lidx] = FALSE; + } + } + pidx = lidx; + if (chk[lidx]) + { + MOVA(aopGet(right, lidx, FALSE, FALSE)); + chk[lidx] = FALSE; + } + emitcode ("cjne", "a,%s,%05d$", aopGet(left, lidx, FALSE, TRUE), lbl->key + 100); + + for (cidx = lidx + 1; cidx < size; cidx++) + { + if (chk[cidx] && val[lidx] == val[cidx] && !IS_AOP_PREG (left)) + { + chk[cidx] = FALSE; + emitcode ("cjne", "a,%s,%05d$", aopGet(left, cidx, FALSE, TRUE), lbl->key + 100); + } + } + } + } + return; + } + + while (size--) + { + const char *r; + MOVA (aopGet (left, offset, FALSE, FALSE)); + r = aopGet (right, offset, FALSE, TRUE); + if (EQ (r, zero)) + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + else + emitcode ("cjne", "a,%s,!tlabel", r, labelKey2num (lbl->key)); + offset++; + } + } + else + { + /* right is a pointer reg need both a & b */ + while (size--) + { + //if B in use: push B; mov B,left; mov A,right; clrc; subb A,B; pop B; jnz + wassertl (!BINUSE, "B was in use"); + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("cjne", "a,b,!tlabel", labelKey2num (lbl->key)); + offset++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* gencjne - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjne (operand * left, operand * right, symbol * lbl, bool useCarry) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "gencjne")); + + gencjneshort (left, right, lbl); + + if (useCarry) + SETC; + else + MOVA (one); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl->key)); + emitLabel (lbl); + if (useCarry) + CLRC; + else + MOVA (zero); + emitLabel (tlbl); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + bool swappedLR = FALSE; + operand *left, *right, *result; + iCode *popIc = ic->next; + + D (emitcode (";", "genCmpEq")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + /* if literal, literal on the right or + if the right is in a pointer register and left + is not */ + if ((AOP_TYPE (left) == AOP_LIT) || (IS_AOP_PREG (right) && !IS_AOP_PREG (left))) + { + swapOperands (&left, &right); + swappedLR = TRUE; + } + + if (ifx && !AOP_SIZE (result)) + { + symbol *tlbl; + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num ((lbl->key))); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* if true label then we jump if condition + supplied is true */ + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + emitcode ("jnc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + } + else + { + emitcode ("jc", "!tlabel", labelKey2num (tlbl->key)); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + } + emitLabel (tlbl); + } + else + { + tlbl = newiTempLabel (NULL); + gencjneshort (left, right, tlbl); + if (IC_TRUE (ifx)) + { + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (tlbl); + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (lbl->key)); + emitLabel (tlbl); + freeForBranchAsmops (result, right, left, ic); + popForBranch (popIc, FALSE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ifx)->key)); + emitLabel (lbl); + } + } + /* mark the icode as generated */ + ifx->generated = 1; + goto release; + } + + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("jb", "%s,!tlabel", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + emitcode ("cpl", "c"); + emitLabel (lbl); + } + /* c = 1 if egal */ + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + outBitC (result); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "c", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + outBitC (result); + } + else + { + bool useCarry = (AOP_TYPE (result) == AOP_CRY); + gencjne (left, right, newiTempLabel (NULL), useCarry); + if (useCarry && AOP_SIZE (result)) + { + aopPut (result, "c", 0); + goto release; + } + if (ifx) + { + genIfxJump (ifx, useCarry ? "c" : "a", left, right, result, popIc); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + if (!useCarry) + outAcc (result); + /* leave the result in acc */ + } + +release: + freeAsmop (result, NULL, ic, TRUE); + if (swappedLR) + swapOperands (&left, &right); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* hasInc - operand is incremented before any other use */ +/*-----------------------------------------------------------------*/ +static iCode * +hasInc (operand * op, iCode * ic, int osize) +{ + sym_link *type = operandType (op); + sym_link *retype = getSpec (type); + iCode *lic = ic->next; + int isize; + + /* this could from a cast, e.g.: "(char xdata *) 0x7654;" */ + if (!IS_SYMOP (op)) + return NULL; + + if (IS_BITVAR (retype) || !IS_PTR (type)) + return NULL; + if (IS_AGGREGATE (type->next)) + return NULL; + if (osize != (isize = getSize (type->next))) + return NULL; + + while (lic) + { + /* if operand of the form op = op + */ + if (lic->op == '+' && isOperandEqual (IC_LEFT (lic), op) && + isOperandEqual (IC_RESULT (lic), op) && + isOperandLiteral (IC_RIGHT (lic)) && operandLitValue (IC_RIGHT (lic)) == isize) + { + return lic; + } + /* if the operand used or deffed */ + if (bitVectBitValue (OP_USES (op), lic->key) || lic->defKey == op->key) + { + return NULL; + } + /* if GOTO or IFX */ + if (lic->op == IFX || lic->op == GOTO || lic->op == LABEL) + break; + lic = lic->next; + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genAndOp")); + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + D (emitcode (";", "genOrOp")); + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + outBitC (result); + } + else + { + tlbl = newiTempLabel (NULL); + toBoolean (left); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + toBoolean (right); + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long long lit) +{ + unsigned long long w = 1; + int idx; + + for (idx = 0; idx < 64; idx++, w<<=1) + if (lit == w) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic, iCode * popIc) +{ + if (IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic, iCode * popIc) +{ + if (!IC_TRUE (ic)) + { + popForBranch (popIc, TRUE); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl, operand * left, operand * right, operand * result, iCode * popIc) +{ + // ugly but optimized by peephole + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + emitcode ("sjmp", "!tlabel", labelKey2num (nlbl->key)); + emitLabel (tlbl); + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ic)->key)); + emitLabel (nlbl); + } + else + { + popForBranch (popIc, FALSE); + freeForBranchAsmops (result, right, left, ic); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_FALSE (ic)->key)); + emitLabel (tlbl); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0ull; + int bytelit = 0; + + D (emitcode (";", "genAnd")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit & 1) + { + if (size && sameRegs (AOP (result), AOP (left))) + // no change + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx, ic->next); + goto release; + } + emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("anl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else if (AOP_TYPE (right) == AOP_DIR && IS_BOOL (operandType (right)) && AOP_TYPE (left) == AOP_CRY) + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("anl", "c,acc.0"); + } + else + { + // c = bit & val; + MOVA (aopGet (right, 0, FALSE, FALSE)); + // c = lsb + emitcode ("rrc", "a"); + emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + int posbit = isLiteralBit (lit); + /* left & 2^n */ + if (posbit) + { + posbit--; + MOVA (aopGet (left, posbit >> 3, FALSE, FALSE)); + // bit = left & 2^n + if (size) + { + switch (posbit & 0x07) + { + case 0: + emitcode ("rrc", "a"); + break; + case 7: + emitcode ("rlc", "a"); + break; + default: + emitcode ("mov", "c,acc.%d", posbit & 0x07); + break; + } + } + // if(left & 2^n) + else + { + if (ifx) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "acc.%d", posbit & 0x07); + genIfxJump (ifx, dbuf_c_str (&dbuf), left, right, result, ic->next); + dbuf_destroy (&dbuf); + } + else + { + emitcode ("anl", "a,#!constbyte", 1 << (posbit & 0x07)); + } + goto release; + } + } + else + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + if (size) + emitcode ("setb", "c"); + while (sizel--) + { + if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + // byte == 2^n ? + if ((posbit = isLiteralBit (bytelit)) != 0) + emitcode ("jb", "acc.%d,!tlabel", (posbit - 1) & 0x07, labelKey2num (tlbl->key)); + else + { + if (bytelit != 0x0FFL) + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, TRUE)); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + } + } + offset++; + } + // bit = left & literal + if (size) + { + emitcode ("clr", "c"); + emitLabel (tlbl); + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0) + { + aopPut (result, zero, offset); + } + else if (IS_AOP_PREG (result)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (result)) + { + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("anl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0ffull); + if (bytelit == 0x0FF) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, zero, offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, anl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("anl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genOr")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit | literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == 0 => result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit | bit; + if (IS_OP_ACCUSE (left)) + { + emitcode ("orl", "c,%s", AOP (right)->aopu.aop_dir); + } + else if (IS_OP_ACCUSE (right)) + { + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + else + { + // c = bit | val; + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + toBoolean (right); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + goto release; + } + else + { + toCarry (right); + emitcode ("orl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + if (lit) + { + // result = 1 + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + else + { + // lit = 0, result = boolean(left) + if (size) + SETC; + toBoolean (left); + if (size) + { + symbol *tlbl = newiTempLabel (NULL); + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + CLRC; + emitLabel (tlbl); + } + else + { + /* FIXME, thats pretty fishy, check for ifx!=0, testcase .. */ + assert (ifx); + genIfxJump (ifx, "a", left, right, result, ic->next); + goto release; + } + } + outBitC (result); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("orl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left | right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (bytelit == 0x0FF) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + aopPut (result, "#0xff", offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, orl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("orl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + + D (emitcode (";", "genXor")); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + +#ifdef DEBUG_TYPE + emitcode (";", "Type res[%d] = l[%d]&r[%d]", AOP_TYPE (result), AOP_TYPE (left), AOP_TYPE (right)); + emitcode (";", "Size res[%d] = l[%d]&r[%d]", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); +#endif + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange left and right */ + if (sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit ^ literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx, ic->next); + goto release; + } + emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && sameRegs (AOP (result), AOP (left))) + goto release; + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && sameRegs (AOP (result), AOP (left))) + { + emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + emitcode ("cpl", "c"); + } + } + } + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + if (IS_OP_ACCUSE (left)) + { + // left already is in the carry + operand *tmp = right; + right = left; + left = tmp; + } + else + { + toCarry (right); + } + } + else + { + // c = bit ^ val + toCarry (right); + } + emitcode ("jnb", "%s,!tlabel", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + emitcode ("cpl", "c"); + emitLabel (tlbl); + } + // bit = c + // val = c + if (size) + outBitC (result); + // if(bit ^ ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c", left, right, result, ic->next); + goto release; + } + + /* if left is same as result */ + if (sameRegs (AOP (result), AOP (left))) + { + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + /* dummy read of volatile operand */ + if (isOperandVolatile (left, FALSE)) + MOVA (aopGet (left, offset, FALSE, FALSE)); + else + continue; + } + else if (IS_AOP_PREG (left)) + { + MOVA (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else if (AOP_TYPE (left) != AOP_DPTR) + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + } + else + { + char *l = Safe_strdup (aopGet (left, offset, FALSE, TRUE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + Safe_free (l); + aopPut (result, "a", offset); + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + if (offset) + emitcode ("mov", "b,a"); + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + aopPut (result, "a", offset); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + aopPut (result, "a", offset); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (IS_AOP_PREG (left)) + { + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, TRUE)); + aopPut (result, "a", offset); + } + else + { + emitcode ("xrl", "%s,a", aopGet (left, offset, FALSE, TRUE)); + } + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (size) + emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + } + else if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + + emitcode ("jnz", "!tlabel", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + emitLabel (tlbl); + outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl, left, right, result, ic->next); + else + emitLabel (tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left ^ right + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = (int) ((lit >> (offset * 8)) & 0x0FFL); + if (bytelit == 0) + { + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + continue; + } + else if (AOP_TYPE (left) == AOP_ACC) + { + // this should be the only use of left so A,B can be overwritten + char *l = Safe_strdup (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "%s,%s", l, aopGet (right, offset, FALSE, FALSE)); + aopPut (result, l, offset); + Safe_free (l); + continue; + } + } + // faster than result <- left, xrl result,right + // and better if result is SFR + if ((AOP_TYPE (right) == AOP_REG || IS_AOP_PREG (right) || AOP_TYPE (right) == AOP_DIR) + && AOP_TYPE (left) == AOP_ACC) + { + if (offset) + emitcode ("mov", "a,b"); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else if (AOP_TYPE (left) == AOP_ACC) + { + if (!offset) + { + //B contains high byte of left + emitpush ("b"); + emitcode ("mov", "b,a"); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + emitpop ("b"); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + } + else if (aopGetUsesAcc (left, offset) && aopGetUsesAcc (right, offset)) + { + MOVB (aopGet (left, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,b"); + } + else if (aopGetUsesAcc (left, offset)) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (right, offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + emitcode ("xrl", "a,%s", aopGet (left, offset, FALSE, FALSE)); + } + aopPut (result, "a", offset); + } + } + } + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRRC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = size - 1; + if (size == 1) + { + /* special case for 1 byte */ + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rr", "a"); + goto release; + } + /* no need to clear carry, bit7 will be written later */ + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset--); + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, AOP_SIZE (result) - 1, FALSE, FALSE)); + } + emitcode ("mov", "acc.7,c"); +release: + aopPut (result, "a", AOP_SIZE (result) - 1); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset; + + D (emitcode (";", "genRLC")); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* move it to the result */ + size = AOP_SIZE (result); + offset = 0; + if (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + if (size == 0) + { + /* special case for 1 byte */ + emitcode ("rl", "a"); + goto release; + } + emitcode ("rlc", "a"); /* bit0 will be written later */ + if (AOP_SIZE (result) > 1) + { + aopPut (result, "a", offset++); + } + + while (size--) + { + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (AOP_SIZE (result) > 1) + aopPut (result, "a", offset++); + } + } + /* now we need to put the carry into the + highest order byte of the result */ + if (AOP_SIZE (result) > 1) + { + MOVA (aopGet (result, 0, FALSE, FALSE)); + } + emitcode ("mov", "acc.0,c"); +release: + aopPut (result, "a", 0); + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetAbit - generates code get a single bit */ +/*-----------------------------------------------------------------*/ +static void +genGetAbit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + + D (emitcode (";", "genGetAbit")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + shCount = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + /* get the needed byte into a */ + MOVA (aopGet (left, shCount / 8, FALSE, FALSE)); + shCount %= 8; + if (AOP_TYPE (result) == AOP_CRY) + { + if ((shCount) == 7) + emitcode ("rlc", "a"); + else if ((shCount) == 0) + emitcode ("rrc", "a"); + else + emitcode ("mov", "c,acc[%d]", shCount); + outBitC (result); + } + else + { + switch (shCount) + { + case 2: + emitcode ("rr", "a"); + //fallthrough + case 1: + emitcode ("rr", "a"); + //fallthrough + case 0: + emitcode ("anl", "a,#0x01"); + break; + case 3: + case 5: + emitcode ("mov", "c,acc[%d]", shCount); + emitcode ("clr", "a"); + emitcode ("rlc", "a"); + break; + case 4: + emitcode ("swap", "a"); + emitcode ("anl", "a,#0x01"); + break; + case 6: + emitcode ("rl", "a"); + //fallthrough + case 7: + emitcode ("rl", "a"); + emitcode ("anl", "a,#0x01"); + break; + } + outAcc (result); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetByte - generates code get a single byte */ +/*-----------------------------------------------------------------*/ +static void +genGetByte (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetByte")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetWord - generates code get two bytes */ +/*-----------------------------------------------------------------*/ +static void +genGetWord (iCode * ic) +{ + operand *left, *right, *result; + int offset; + + D (emitcode (";", "genGetWord")); + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + offset = (int) ulFromVal (AOP (right)->aopu.aop_lit) / 8; + aopPut (result, aopGet (left, offset, FALSE, FALSE), 0); + aopPut (result, aopGet (left, offset + 1, FALSE, FALSE), 1); + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSwap - generates code to swap nibbles or bytes */ +/*-----------------------------------------------------------------*/ +static void +genSwap (iCode * ic) +{ + operand *left, *result; + + D (emitcode (";", "genSwap")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + switch (AOP_SIZE (left)) + { + case 1: /* swap nibbles in byte */ + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("swap", "a"); + aopPut (result, "a", 0); + break; + case 2: /* swap bytes in word */ + if (AOP_TYPE (left) == AOP_REG && sameRegs (AOP (left), AOP (result))) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, "a", 1); + } + else if (operandsEqu (left, result)) + { + char *reg = "a"; + bool pushedB = FALSE, leftInB = FALSE; + + MOVA (aopGet (left, 0, FALSE, FALSE)); + if (aopGetUsesAcc (left, 1) || aopGetUsesAcc (result, 0)) + { + pushedB = pushB (); + emitcode ("mov", "b,a"); + reg = "b"; + leftInB = TRUE; + } + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, reg, 1); + + if (leftInB) + popB (pushedB); + } + else + { + aopPut (result, aopGet (left, 1, FALSE, FALSE), 0); + aopPut (result, aopGet (left, 0, FALSE, FALSE), 1); + } + break; + default: + wassertl (FALSE, "unsupported SWAP operand size"); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("rl", "a"); + break; + case 2: + emitcode ("rl", "a"); + emitcode ("rl", "a"); + break; + case 3: + emitcode ("swap", "a"); + emitcode ("rr", "a"); + break; + case 4: + emitcode ("swap", "a"); + break; + case 5: + emitcode ("swap", "a"); + emitcode ("rl", "a"); + break; + case 6: + emitcode ("rr", "a"); + emitcode ("rr", "a"); + break; + case 7: + emitcode ("rr", "a"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + emitcode ("add", "a,acc"); + else if (shCount == 2) + { + emitcode ("add", "a,acc"); + emitcode ("add", "a,acc"); + } + else + { + /* rotate left accumulator */ + AccRol (shCount); + /* and kill the lower order bits */ + emitcode ("anl", "a,#!constbyte", SLMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount) +{ + if (shCount != 0) + { + if (shCount == 1) + { + CLRC; + emitcode ("rrc", "a"); + } + else + { + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccSRsh - signed right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccSRsh (int shCount) +{ + symbol *tlbl; + if (shCount != 0) + { + if (shCount == 1) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else if (shCount == 2) + { + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + } + else + { + tlbl = newiTempLabel (NULL); + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + emitcode ("anl", "a,#!constbyte", SRMask[shCount]); + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#!constbyte", (unsigned char) ~SRMask[shCount]); + emitLabel (tlbl); + } + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + if (sign) + AccSRsh (shCount); + else + AccRsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr, int sign) +{ + const char *l; + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + l = aopGet (left, offl, FALSE, FALSE); + + if (*l == '@' && (IS_AOP_PREG (result))) + { + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offr); + } + else + { + if (!sign) + { + aopPut (result, l, offr); + } + else + { + /* MSB sign in acc.7 ! */ + if (getDataSize (left) == offl + 1) + { + MOVA (l); + aopPut (result, "a", offr); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRrl1 - right rotate c->a:x->c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXRrl1 (const char *x) +{ + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLrl1 - left rotate c<-a:x<-c by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLrl1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh1 - left shift a:x<-0 by 1 */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh1 (const char *x) +{ + emitcode ("xch", "a,%s", x); + emitcode ("add", "a,acc"); + emitcode ("xch", "a,%s", x); + emitcode ("rlc", "a"); +} + +/*-----------------------------------------------------------------*/ +/* AccAXLsh - left shift a:x by known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXLsh (const char *x, int shCount) +{ + unsigned char mask; + + switch (shCount) + { + case 0: + break; + case 1: + AccAXLsh1 (x); + break; + case 2: + AccAXLsh1 (x); + AccAXLsh1 (x); + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD + mask = SLMask[shCount]; + AccRol (shCount); // BBBAAAAA:CCCCCDDD + emitcode ("anl", "a,#!constbyte", mask); // BBB00000:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBB00000 + AccRol (shCount); // DDDCCCCC:BBB00000 + emitcode ("xch", "a,%s", x); // BBB00000:DDDCCCCC + emitcode ("xrl", "a,%s", x); // (BBB^DDD)CCCCC:DDDCCCCC + emitcode ("xch", "a,%s", x); // DDDCCCCC:(BBB^DDD)CCCCC + emitcode ("anl", "a,#!constbyte", mask); // DDD00000:(BBB^DDD)CCCCC + emitcode ("xch", "a,%s", x); // (BBB^DDD)CCCCC:DDD00000 + emitcode ("xrl", "a,%s", x); // BBBCCCCC:DDD00000 + break; + case 6: // AAAAAABB:CCCCCCDD + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 000000BB:CCCCCCDD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCDD:000000BB + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("mov", "c,acc.0"); //<< get correct bit + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + emitcode ("rrc", "a"); + emitcode ("xch", "a,%s", x); + break; + case 7: // a:x <<= 7 + mask = SRMask[shCount]; + emitcode ("anl", "a,#!constbyte", mask); // 0000000B:CCCCCCCD + emitcode ("mov", "c,acc.0"); // c = B + emitcode ("xch", "a,%s", x); // CCCCCCCD:0000000B + AccAXRrl1 (x); // BCCCCCCC:D0000000 + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRsh - right shift a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRsh (const char *x, int shCount) +{ + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 2: + CLRC; + AccAXRrl1 (x); // 0->a:x + CLRC; + AccAXRrl1 (x); // 0->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + AccRol (8 - shCount); // BBBAAAAA:DDDCCCCC + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000AAAAA:BBBCCCCC + break; + case 6: // AABBBBBB:CCDDDDDD + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + break; + case 7: // ABBBBBBB:CDDDDDDD + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccAXRshS - right shift signed a:x known count (0..7) */ +/*-----------------------------------------------------------------*/ +static void +AccAXRshS (const char *x, int shCount) +{ + symbol *tlbl; + unsigned char mask = SRMask[shCount]; + + switch (shCount) + { + case 0: + break; + case 1: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 2: + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + emitcode ("mov", "c,acc.7"); + AccAXRrl1 (x); // s->a:x + break; + case 3: + case 4: + case 5: // AAAAABBB:CCCCCDDD = a:x + tlbl = newiTempLabel (NULL); + AccRol (8 - shCount); // BBBAAAAA:CCCCCDDD + emitcode ("xch", "a,%s", x); // CCCCCDDD:BBBAAAAA + AccRol (8 - shCount); // DDDCCCCC:BBBAAAAA + emitcode ("anl", "a,#!constbyte", mask); // 000CCCCC:BBBAAAAA + emitcode ("xrl", "a,%s", x); // BBB(CCCCC^AAAAA):BBBAAAAA + emitcode ("xch", "a,%s", x); // BBBAAAAA:BBB(CCCCC^AAAAA) + emitcode ("anl", "a,#!constbyte", mask); // 000AAAAA:BBB(CCCCC^AAAAA) + emitcode ("xch", "a,%s", x); // BBB(CCCCC^AAAAA):000AAAAA + emitcode ("xrl", "a,%s", x); // BBBCCCCC:000AAAAA + emitcode ("xch", "a,%s", x); // 000SAAAA:BBBCCCCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111AAAAA:BBBCCCCC + emitLabel (tlbl); + break; // SSSSAAAA:BBBCCCCC + case 6: // AABBBBBB:CCDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // ABBBBBBC:CDDDDDDA + emitcode ("mov", "c,acc.7"); + AccAXLrl1 (x); // BBBBBBCC:DDDDDDAA + emitcode ("xch", "a,%s", x); // DDDDDDAA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 000000AA:BBBBBBCC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 111111AA:BBBBBBCC + emitLabel (tlbl); + break; + case 7: // ABBBBBBB:CDDDDDDD + tlbl = newiTempLabel (NULL); + emitcode ("mov", "c,acc.7"); // c = A + AccAXLrl1 (x); // BBBBBBBC:DDDDDDDA + emitcode ("xch", "a,%s", x); // DDDDDDDA:BBBBBBCC + emitcode ("anl", "a,#!constbyte", mask); // 0000000A:BBBBBBBC + emitcode ("jnb", "acc.%d,!tlabel", 7 - shCount, labelKey2num (tlbl->key)); + mask = ~SRMask[shCount]; + emitcode ("orl", "a,#!constbyte", mask); // 1111111A:BBBBBBBC + emitLabel (tlbl); + break; + default: + break; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (left, offl + MSB16, FALSE, FALSE)); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("xch", "a,b"); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* ax << shCount (x = lsb(result)) */ + AccAXLsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + aopPut (result, "b", offr + MSB16); + popB (pushedB); + } + else + { + aopPut (result, "a", offr + MSB16); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + const char *x; + bool pushedB = FALSE; + bool usedB = FALSE; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + /* don't crash result[offr] */ + MOVA (aopGet (left, offl, FALSE, FALSE)); + x = xch_a_aopGet (left, offl + MSB16, FALSE, FALSE); + usedB = !strncmp (x, "b", 1); + } + else if (aopGetUsesAcc (result, offr)) + { + movLeft2Result (left, offl, result, offr, 0); + pushedB = pushB (); + usedB = TRUE; + emitcode ("mov", "b,%s", aopGet (result, offr, FALSE, FALSE)); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = "b"; + } + else + { + movLeft2Result (left, offl, result, offr, 0); + MOVA (aopGet (left, offl + MSB16, FALSE, FALSE)); + x = aopGet (result, offr, FALSE, FALSE); + } + /* a:x >> shCount (x = lsb(result)) */ + if (sign) + AccAXRshS (x, shCount); + else + AccAXRsh (x, shCount); + if (usedB) + { + emitcode ("xch", "a,b"); + aopPut (result, "a", offr); + emitcode ("xch", "a,b"); + popB (pushedB); + } + if (getDataSize (result) > 1) + aopPut (result, "a", offr + MSB16); +} + +/*------------------------------------------------------------------*/ +/* shiftLLeftOrResult - shift left one byte from left, or to result */ +/*------------------------------------------------------------------*/ +static void +shiftLLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift left accumulator */ + AccLsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result */ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + MOVA (aopGet (left, offl, FALSE, FALSE)); + /* shift right accumulator */ + AccRsh (shCount); + /* or with result */ + if (aopGetUsesAcc (result, offr)) + { + emitcode ("xch", "a,b"); + MOVA (aopGet (result, offr, FALSE, FALSE)); + emitcode ("orl", "a,b"); + } + else + { + emitcode ("orl", "a,%s", aopGet (result, offr, FALSE, FALSE)); + } + /* back to result */ + aopPut (result, "a", offr); +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ + int offl = LSB; + int size = AOP_SIZE (result); + int useXch = (sameRegs (AOP (left), AOP (result)) && size >= MSB16 + offr && offr != LSB); + + if (size > offl + offr) + { + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("add", "a,acc"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + + for (offl = LSB + 1; offl < LSB + 8; offl++) + { + if (size > offl + offr) + { + if (!useXch) + MOVA (aopGet (left, offl, FALSE, FALSE)); + emitcode ("rlc", "a"); + if (useXch) + xch_a_aopGet (left, offl + offr, FALSE, FALSE); + else + aopPut (result, "a", offl + offr); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genlshFixed - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFixed (operand *result, operand *left, int shCount) +{ + int size, b; + int full_bytes; + + D (emitcode (";", "genlshFixed")); + + size = AOP_SIZE (result); + + full_bytes = shCount / 8; + shCount -= full_bytes * 8; + if (shCount == 0) + { + for (b = size - 1; b > full_bytes - 1; b--) + movLeft2Result (left, b - full_bytes, result, b, 0); + } + else if ((shCount == 1) && (full_bytes < 2)) + { + shiftLLong (left, result, full_bytes); + } + else if ((shCount == 2) && (full_bytes == 0)) + { + shiftLLong (left, result, full_bytes); + shiftLLong (result, result, full_bytes); + } + else + { + int off; + for (off = size - 2; off - full_bytes >= 0; off -= 2) + { + shiftL2Left2Result (left, off - full_bytes, result, off, shCount); + if (off - full_bytes - 1 >= 0) + shiftRLeftOrResult (left, off - full_bytes - 1, result, off, 8 - shCount); + } + if (off - full_bytes == -1) + { + shiftL1Left2Result (left, LSB, result, full_bytes, shCount); + } + } + for (b = LSB; b < full_bytes; b++) + aopPut (result, zero, b); + return; +} + +/*-----------------------------------------------------------------*/ +/* genlshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshAny (operand *result, operand *left, int shCount) +{ + int size, size2, offset; + + D (emitcode (";", "genlshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, LSB, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", 0); + + for(size2 = size - 1, offset = 1; size2 > 0; size2--, offset++) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + unsigned int shCount = (unsigned int) ulFromVal (AOP (right)->aopu.aop_lit); + unsigned int size; + + size = getSize (operandType (result)); + + D (emitcode (";", "genLeftShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left)); +#endif + + switch (size) + { + case 1: + case 2: + case 4: + case 8: + genlshFixed (result, left, shCount); + break; + + default: + genlshAny (result, left, shCount); + break; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (iCode * ic) +{ + operand *left, *right, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genLeftShift")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genLeftShiftLiteral (left, right, result, ic); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits makes no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genLeftShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && (IS_AOP_PREG (result))) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = 0; + tlbl1 = newiTempLabel (NULL); + + /* if it is only one byte then */ + if (size == 1) + { + symbol *tlbl1 = newiTempLabel (NULL); + + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("add", "a,acc"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("add", "a,acc"); + aopPut (result, "a", offset++); + while (--size) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + aopPut (result, "a", offset++); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshOne")); + + shiftR1Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + D (emitcode (";", "genrshTwo")); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (shCount) + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB16, result, LSB, sign); + addSign (result, MSB16, sign); + } + + /* 1 <= shCount <= 7 */ + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, sign); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + bool overlapping = regsInCommon (left, result) || operandsEqu (left, result); + + if (overlapping && offl > 1) + { + // we are in big trouble, but this shouldn't happen + werror (E_INTERNAL_ERROR, __FILE__, __LINE__); + } + + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + + if (offl == MSB16) + { + // shift is > 8 + if (sign) + { + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + if (overlapping && sameByte (AOP (left), MSB32, AOP (result), MSB32)) + { + xch_a_aopGet (left, MSB32, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32); + MOVA (aopGet (left, MSB32, FALSE, FALSE)); + } + } + else + { + if (aopPutUsesAcc (result, zero, MSB32)) + { + emitcode ("xch", "a,b"); + aopPut (result, zero, MSB32); + emitcode ("xch", "a,b"); + } + else + { + aopPut (result, zero, MSB32); + } + } + } + + if (!sign) + { + emitcode ("clr", "c"); + } + else + { + emitcode ("mov", "c,acc.7"); + } + + emitcode ("rrc", "a"); + + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB24, AOP (result), MSB32 - offl)) + { + xch_a_aopGet (left, MSB24, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB32 - offl); + MOVA (aopGet (left, MSB24, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (overlapping && offl == MSB16 && sameByte (AOP (left), MSB16, AOP (result), MSB24 - offl)) + { + xch_a_aopGet (left, MSB16, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB24 - offl); + MOVA (aopGet (left, MSB16, FALSE, FALSE)); + } + + emitcode ("rrc", "a"); + if (offl != LSB) + { + aopPut (result, "a", MSB16 - offl); + } + else + { + if (overlapping && sameByte (AOP (left), LSB, AOP (result), MSB16 - offl)) + { + xch_a_aopGet (left, LSB, FALSE, FALSE); + } + else + { + aopPut (result, "a", MSB16 - offl); + MOVA (aopGet (left, LSB, FALSE, FALSE)); + } + emitcode ("rrc", "a"); + aopPut (result, "a", LSB); + } +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand *result, operand *left, int shCount, int sign) +{ + D (emitcode (";", "genrshFour")); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + shiftR1Left2Result (left, MSB32, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB32, result, LSB, sign); + addSign (result, MSB16, sign); + } + else if (shCount >= 16) + { + shCount -= 16; + if (shCount) + shiftR2Left2Result (left, MSB24, result, LSB, shCount, sign); + else + { + movLeft2Result (left, MSB24, result, LSB, 0); + movLeft2Result (left, MSB32, result, MSB16, sign); + } + addSign (result, MSB24, sign); + } + else if (shCount >= 8) + { + shCount -= 8; + if (shCount == 1) + { + shiftRLong (left, MSB16, result, sign); + } + else if (shCount == 0) + { + movLeft2Result (left, MSB16, result, LSB, 0); + movLeft2Result (left, MSB24, result, MSB16, 0); + movLeft2Result (left, MSB32, result, MSB24, sign); + addSign (result, MSB32, sign); + } + else + { + shiftR2Left2Result (left, MSB16, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB32, result, MSB16, 8 - shCount); + /* the last shift is signed */ + shiftR1Left2Result (left, MSB32, result, MSB24, shCount, sign); + addSign (result, MSB32, sign); + } + } + else + { + /* 1 <= shCount <= 7 */ + if (shCount <= 2) + { + shiftRLong (left, LSB, result, sign); + if (shCount == 2) + shiftRLong (result, LSB, result, sign); + } + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB24, result, MSB16, 8 - shCount); + shiftR2Left2Result (left, MSB24, result, MSB24, shCount, sign); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genrshAny - shift any number of bytes by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshAny (operand *result, operand *left, int shCount, int sign) +{ + int size, size2, offset; + + D (emitcode (";", "genrshAny")); + + size = AOP_SIZE (result); + + if (!operandsEqu (result, left)) + for (size2 = size, offset = 0; size2 > 0; size2--, offset++) + aopPut (result, aopGet (left, offset, FALSE, FALSE), offset); + + while (shCount--) + { + MOVA (aopGet (result, size - 1, FALSE, FALSE)); + if (!sign) + emitcode ("clr", "c"); + else + emitcode ("mov", "c,acc.7"); + emitcode ("rrc", "a"); + aopPut (result, "a", size - 1); + + for(size2 = size - 1, offset = size - 2; size2 > 0; size2--, offset--) + { + + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + int size; + + size = getSize (operandType (result)); //getDataSize (left); + + D (emitcode (";", "genRightShiftLiteral (%d), size %d", shCount, size)); + + freeAsmop (right, NULL, ic, TRUE); + + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + +#if VIEW_SIZE + emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left)); +#endif + + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + wassert ((int)getSize (operandType (left)) >= size); + + if (shCount == 0) + { + size = getDataSize (result); + while (size--) + movLeft2Result (left, size, result, size, 0); + } + else if (shCount >= (size * 8)) + { + if (sign) + { + /* get sign in acc.7 */ + MOVA (aopGet (left, size - 1, FALSE, FALSE)); + } + addSign (result, LSB, sign); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + + default: + genrshAny (result, left, shCount, sign); + break; + } + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genSignedRightShift - right shift of signed number */ +/*-----------------------------------------------------------------*/ +static void +genSignedRightShift (iCode * ic) +{ + operand *right, *left, *result; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genSignedRightShift")); + + /* we do it the hard way put the shift count in b + and loop thru preserving the sign */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 1); + return; + } + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + /* mov the highest order bit to OVR */ + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + + size = AOP_SIZE (result); + offset = size - 1; + MOVA (aopGet (left, offset, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("mov", "ov,c"); + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + emitcode ("mov", "c,ov"); + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (iCode * ic) +{ + operand *right, *left, *result; + sym_link *letype; + int size, offset; + symbol *tlbl, *tlbl1; + bool pushedB; + + D (emitcode (";", "genRightShift")); + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + letype = getSpec (operandType (IC_LEFT (ic))); + + if (!SPEC_USIGN (letype)) + { + genSignedRightShift (ic); + return; + } + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + genRightShiftLiteral (left, right, result, ic, 0); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + pushedB = pushB (); + if (AOP_TYPE (right) == AOP_LIT) + { + /* Really should be handled by genRightShiftLiteral, + * but since I'm too lazy to fix that today, at least we can make + * some small improvement. + */ + emitcode ("mov", "b,#!constbyte", ((int) ulFromVal (AOP (right)->aopu.aop_lit)) + 1); + } + else + { + MOVB (aopGet (right, 0, FALSE, FALSE)); + emitcode ("inc", "b"); + } + freeAsmop (right, NULL, ic, TRUE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result)) && AOP_SIZE (result) > 1) + { + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + const char *l = aopGet (left, offset, FALSE, TRUE); + if (*l == '@' && IS_AOP_PREG (result)) + { + + emitcode ("mov", "a,%s", l); + aopPut (result, "a", offset); + } + else + aopPut (result, l, offset); + offset++; + } + } + + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + size = AOP_SIZE (result); + offset = size - 1; + + /* if it is only one byte then */ + if (size == 1) + { + MOVA (aopGet (left, 0, FALSE, FALSE)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + emitcode ("rrc", "a"); + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + aopPut (result, "a", 0); + goto release; + } + + reAdjustPreg (AOP (result)); + emitcode ("sjmp", "!tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + CLRC; + while (size--) + { + MOVA (aopGet (result, offset, FALSE, FALSE)); + emitcode ("rrc", "a"); + aopPut (result, "a", offset--); + } + reAdjustPreg (AOP (result)); + + emitLabel (tlbl1); + emitcode ("djnz", "b,!tlabel", labelKey2num (tlbl->key)); + popB (pushedB); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteGet - emits code to get a byte into A through a */ +/* pointer register (R0, R1, or DPTR). The */ +/* original value of A can be preserved in B. */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteGet (const char *rname, int p_type, bool preserveAinB) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("mov", "a,@%s", rname); + break; + + case PPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@%s", rname); + break; + + case FPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("movx", "a,@dptr"); + break; + + case CPOINTER: + if (preserveAinB) + emitcode ("mov", "b,a"); + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + break; + + case GPOINTER: + if (preserveAinB) + { + emitpush ("b"); + emitpush ("acc"); + } + emitcode ("lcall", "__gptrget"); + if (preserveAinB) + emitpop ("b"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteSet - emits code to set a byte from src through a */ +/* pointer register (R0, R1, or DPTR). */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteSet (const char *rname, int p_type, const char *src) +{ + switch (p_type) + { + case IPOINTER: + case POINTER: + if (*src == '@') + { + MOVA (src); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, src); + break; + + case PPOINTER: + MOVA (src); + emitcode ("movx", "@%s,a", rname); + break; + + case FPOINTER: + MOVA (src); + emitcode ("movx", "@dptr,a"); + break; + + case GPOINTER: + MOVA (src); + emitcode ("lcall", "__gptrput"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static char * +genUnpackBits (operand * result, const char *rname, int ptype, iCode * ifx) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bitfield length */ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + static char *const accBits[] = { "acc.0", "acc.1", "acc.2", "acc.3", + "acc.4", "acc.5", "acc.6", "acc.7" + }; + + D (emitcode (";", "genUnpackBits")); + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + if (ifx && blen <= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + if (blen == 1) + { + return accBits[bstr];; + } + else + { + if (blen < 8) + emitcode ("anl", "a,#!constbyte", (((unsigned char) - 1) >> (8 - blen)) << bstr); + return "a"; + } + } + wassert (!ifx); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + emitPtrByteGet (rname, ptype, FALSE); + AccRol (8 - bstr); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - blen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", blen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << blen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + goto finish; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteGet (rname, ptype, FALSE); + aopPut (result, "a", offset++); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* Handle the partial byte at the end */ + if (rlen) + { + emitPtrByteGet (rname, ptype, FALSE); + emitcode ("anl", "a,#!constbyte", ((unsigned char) - 1) >> (8 - rlen)); + if (!SPEC_USIGN (etype)) + { + /* signed bitfield */ + symbol *tlbl = newiTempLabel (NULL); + + emitcode ("jnb", "acc.%d,!tlabel", rlen - 1, labelKey2num (tlbl->key)); + emitcode ("orl", "a,#0x%02x", (unsigned char) (0xff << rlen)); + emitLabel (tlbl); + } + aopPut (result, "a", offset++); + } + +finish: + if (offset < rsize) + { + char *source; + + if (SPEC_USIGN (etype)) + source = zero; + else + { + /* signed bitfield: sign extension with 0x00 or 0xff */ + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + + source = "a"; + } + rsize -= offset; + while (rsize--) + aopPut (result, source, offset++); + } + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + const char *l; + int size, offset = 0; + + D (emitcode (";", "genDataPointerGet")); + + aopOp (result, ic, TRUE); + + /* get the string representation of the name */ + l = aopGet (left, 0, FALSE, TRUE) + 1; // remove # + size = AOP_SIZE (result); + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (AOP_SIZE (result) > 1) + { + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + } + else + { + dbuf_append_str (&dbuf, l); + } + aopPut (result, dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + sym_link *ltype = operandType (left); + + D (emitcode (";", "genNearPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable and + result is not bitfield variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_IMMD && !IS_BITFIELD (retype) && DCL_TYPE (ltype) == POINTER) + { + genDataPointerGet (left, result, ic); + return; + } + + //aopOp (result, ic, FALSE); + aopOp (result, ic, result ? TRUE : FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + if (IS_AOP_PREG (left)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (left, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (left)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname '%s' @ %s:%d\n", rname, __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (left, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + rname = aopGet (left, 0, FALSE, FALSE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, rname, POINTER, ifx); + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + if (ifx || IS_AOP_PREG (result) || AOP_TYPE (result) == AOP_STK) + { + emitcode ("mov", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "@%s", rname); + aopPut (result, dbuf_c_str (&dbuf), offset); + dbuf_destroy (&dbuf); + } + offset++; + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + { + /* post increment present */ + aopPut (left, rname, 0); + } + freeAsmop (NULL, aop, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + /* done */ + freeAsmop (result, NULL, ic, RESULTONSTACK (ic) ? FALSE : TRUE); + freeAsmop (left, NULL, ic, TRUE); + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerGet - emitcode for paged pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + char *ifxCond = "a"; + sym_link *rtype, *retype; + + D (emitcode (";", "genPagedPointerGet")); + + rtype = operandType (result); + retype = getSpec (rtype); + + aopOp (left, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (left))) + { + const char *l; + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + l = aopGet (left, 0, FALSE, TRUE); + if (*l == '@') + emitcode ("mov", "a%s,%s", preg->name, l); + else + emitcode ("mov", "%s,%s", preg->name, l); + rname = preg->name; + } + else + { + rname = aopGet (left, 0, FALSE, FALSE); + } + + aopOp (result, ic, TRUE); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, rname, PPOINTER, ifx); + } + else + { + /* we can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + while (size--) + { + emitcode ("movx", "a,@%s", rname); + if (!ifx) + aopPut (result, "a", offset); + + offset++; + + if (size || pi) + emitcode ("inc", "%s", rname); + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (left, rname, 0); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (result) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + + /* done */ + freeAsmop (result, NULL, ic, TRUE); + if (aop) + { + freeAsmop (NULL, aop, ic, TRUE); + } + freeAsmop (left, NULL, ic, TRUE); + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, NULL, NULL, NULL, ic->next); + } + + if (pi) + pi->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerGet - get value from far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", FPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("movx", "a,@dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCodePointerGet - get value from code space */ +/*-----------------------------------------------------------------*/ +static void +genCodePointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genCodePointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, FALSE); + + /* so dptr now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + ifxCond = genUnpackBits (result, "dptr", CPOINTER, ifx); + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("clr", "a"); + emitcode ("movc", "a,@a+dptr"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - get value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic, iCode * pi, iCode * ifx) +{ + int size, offset; + char *ifxCond = "a"; + sym_link *retype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerGet")); + + aopOp (left, ic, FALSE); + loadDptrFromOperand (left, TRUE); + + /* so dptr-b now contains the address */ + aopOp (result, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + { + ifxCond = genUnpackBits (result, "dptr", GPOINTER, ifx); + } + else + { + size = AOP_SIZE (result); + offset = 0; + + while (size--) + { + emitcode ("lcall", "__gptrget"); + if (!ifx) + aopPut (result, "a", offset++); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (left) != AOP_IMMD && AOP_TYPE (left) != AOP_STR) + { + aopPut (left, "dpl", 0); + aopPut (left, "dph", 1); + pi->generated = 1; + } + + if (ifx && !ifx->generated) + { + genIfxJump (ifx, ifxCond, left, NULL, result, ic->next); + } + + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (left, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic, iCode * pi, iCode * ifx) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerGet")); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + if (getSize (operandType (result)) > 1) + ifx = NULL; + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (left) && OP_SYMBOL (left)->remat && IS_CAST_ICODE (OP_SYMBOL (left)->rematiCode)) + { + left = IC_RIGHT (OP_SYMBOL (left)->rematiCode); + type = operandType (left); + p_type = DCL_TYPE (type); + } + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerGet (left, result, ic, pi, ifx); + break; + + case PPOINTER: + genPagedPointerGet (left, result, ic, pi, ifx); + break; + + case FPOINTER: + genFarPointerGet (left, result, ic, pi, ifx); + break; + + case CPOINTER: + genCodePointerGet (left, result, ic, pi, ifx); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic, pi, ifx); + break; + } +} + + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * right, const char *rname, int p_type) +{ + int offset = 0; /* source byte offset */ + int rlen = 0; /* remaining bitfield length */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + + D (emitcode (";", "genPackBits")); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + if ((blen == 1) && (p_type != GPOINTER)) + { + /* Case with a bitfield length == 1 and no generic pointer + */ + if (AOP_TYPE (right) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + else + { + MOVA (aopGet (right, 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + } + emitPtrByteGet (rname, p_type, FALSE); + emitcode ("mov", "acc.%d,c", bstr); + } + else + { + bool pushedB; + /* Case with a bitfield length < 8 and arbitrary source + */ + MOVA (aopGet (right, 0, FALSE, FALSE)); + /* shift and mask source value */ + AccLsh (bstr); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + } + + emitPtrByteSet (rname, p_type, "a"); + return; + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emitPtrByteSet (rname, p_type, aopGet (right, offset++, FALSE, TRUE)); + if (rlen > 8) + emitcode ("inc", "%s", rname); + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source + */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + emitPtrByteGet (rname, p_type, FALSE); + if ((mask | litval) != 0xff) + emitcode ("anl", "a,#!constbyte", mask); + if (litval) + emitcode ("orl", "a,#!constbyte", litval); + } + else + { + bool pushedB; + /* Case with partial byte and arbitrary source + */ + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("anl", "a,#!constbyte", (~mask) & 0xff); + + pushedB = pushB (); + /* transfer A to B and get next byte */ + emitPtrByteGet (rname, p_type, TRUE); + + emitcode ("anl", "a,#!constbyte", mask); + emitcode ("orl", "a,b"); + if (p_type == GPOINTER) + emitpop ("b"); + + popB (pushedB); + } + emitPtrByteSet (rname, p_type, "a"); + } +} + +/*-----------------------------------------------------------------*/ +/* genLiteralAssign - assignment of literal */ +/*-----------------------------------------------------------------*/ +static void +genLiteralAssign (operand * result, operand * right, int size, bool (*output_fn) (operand * result, const char *s, int offset)) +{ + unsigned long long lit = 0LL; + int offset; + int accumulator_value = -1; /* -1 denotes: not yet set */ + + if (!IS_FLOAT (operandType (right))) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + } + else + { + union + { + float f; + unsigned char c[4]; + } fl; + + fl.f = (float) floatFromVal (AOP (right)->aopu.aop_lit); +#ifdef WORDS_BIGENDIAN + lit = (fl.c[3] << 0) | (fl.c[2] << 8) | (fl.c[1] << 16) | (fl.c[0] << 24); +#else + lit = (fl.c[0] << 0) | (fl.c[1] << 8) | (fl.c[2] << 16) | (fl.c[3] << 24); +#endif + } + + offset = 0; + while (size) + { + /* check whether preloading the accumulator pays off: + + mov direct,#something 3 byte, 2 cycle + mov direct,a 2 byte, 1 cycle + + mov @r0,#something 2 byte, 1 cycle + mov @r0,a 1 byte, 1 cycle + + mov rx,#something 2 byte, 1 cycle + mov rx,a 1 byte, 1 cycle + + clr a 1 byte, 1 cycle + mov a,#something 2 byte, 1 cycle + + (setting bytes in pdata and xdata need the accumulator anyway) + */ + + /* clr a needs an extra byte. If two bytes are zero it starts to pay off + to preload the accumulator */ + int clr_num_bytes_saved = -1 + /* size of clr a */ + (int) ((((lit >> 0) & 0xff) == 0) && (size >= 1)) + + (int) ((((lit >> 8) & 0xff) == 0) && (size >= 2)) + + (int) ((((lit >> 16) & 0xff) == 0) && (size >= 3)) + (int) ((((lit >> 24) & 0xff) == 0) && (size >= 4)); + + /* mov a,#something needs two extra bytes. If three bytes are identical it starts to pay off */ + int mov_num_bytes_saved = -2 + /* size of mov a,#something */ + (int) ((lit & 0xff) == ((lit >> 0) & 0xff) && (size >= 1)) + /* true */ + (int) ((lit & 0xff) == ((lit >> 8) & 0xff) && (size >= 2)) + + (int) ((lit & 0xff) == ((lit >> 16) & 0xff) && (size >= 3)) + + (int) ((lit & 0xff) == ((lit >> 24) & 0xff) && (size >= 4)); + + int num_bytes_to_save_before_using_acc_takes_effect = 1; + + if (optimize.codeSpeed && (AOP_TYPE (result) != AOP_DIR)) + { + /* require an extra byte being saved */ + num_bytes_to_save_before_using_acc_takes_effect++; + } + + /* eventually preload accumulator */ + if ((clr_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && + (clr_num_bytes_saved >= mov_num_bytes_saved) && (lit & 0xff) == 0) + { + if (0 != accumulator_value) + { + accumulator_value = 0; +// emitcode ("clr", "a"); + MOVA ("#0x00"); + } + } + else if ((mov_num_bytes_saved >= num_bytes_to_save_before_using_acc_takes_effect) && (mov_num_bytes_saved > clr_num_bytes_saved)) /* preferrably have 0 in acc */ + { + if ((lit & 0xff) != accumulator_value) + { + accumulator_value = lit & 0xff; +// emitcode ("mov", "a,%s", aopGet (right, offset, FALSE, FALSE)); + MOVA (aopGet (right, offset, FALSE, FALSE)); + } + } + + /* write byte */ + if ((lit & 0xff) == accumulator_value) + { + /* value in accumulator can be used */ + output_fn (result, "a", offset); + } + else + { + /* otherwise use the normal path that should always work */ + char *r = Safe_strdup (aopGet (right, offset, FALSE, FALSE)); + output_fn (result, r, offset); + Safe_free (r); + } + + /* advance */ + lit >>= 8; + offset++; + size--; + } +} + +/*-----------------------------------------------------------------*/ +/* dataPut - puts a string for a aop and indicates if acc is in use */ +/*-----------------------------------------------------------------*/ +static bool +litPut (operand * result, const char *s, int offset) +{ + emitcode ("mov", "(%s + %d),%s", aopGet (result, 0, FALSE, TRUE) + 1, offset, s); + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset; + + D (emitcode (";", "genDataPointerSet")); + + aopOp (right, ic, FALSE); + + size = max (AOP_SIZE (right), AOP_SIZE (result)); + if ((size > 1) && IS_OP_LITERAL (right)) + { + genLiteralAssign (result, right, size, litPut); + } + else + { + //remove # + char *l = Safe_strdup (aopGet (result, 0, FALSE, TRUE) + 1); //remove # + for (offset = 0; offset < size; offset++) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (size > 1) + dbuf_printf (&dbuf, "(%s + %d)", l, offset); + else + dbuf_append_str (&dbuf, l); + emitcode ("mov", "%s,%s", dbuf_c_str (&dbuf), aopGet (right, offset, FALSE, FALSE)); + dbuf_destroy (&dbuf); + } + Safe_free (l); + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + sym_link *ptype = operandType (result); + + D (emitcode (";", "genNearPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (ptype); + + aopOp (result, ic, FALSE); + + /* if the result is rematerializable & + in data space & not a bit variable */ + if (AOP_TYPE (result) == AOP_IMMD && DCL_TYPE (ptype) == POINTER && !IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (right, result, ic); + return; + } + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (EQ (rname, "a")) + { + // It's in pdata or on xstack + rname = AOP (result)->aopu.aop_ptr->name; + emitcode ("mov", "%s,a", rname); + } + else if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, POINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + const char *l = aopGet (right, offset, FALSE, TRUE); + if ((*l == '@') || (EQ (l, "acc"))) + { + MOVA (l); + emitcode ("mov", "@%s,a", rname); + } + else + emitcode ("mov", "@%s,%s", rname, l); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + if ((AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPagedPointerSet - emitcode for Paged pointer put */ +/*-----------------------------------------------------------------*/ +static void +genPagedPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + asmop *aop = NULL; + reg_info *preg = NULL; + const char *rname; + sym_link *retype, *letype; + + D (emitcode (";", "genPagedPointerSet")); + + retype = getSpec (operandType (right)); + letype = getSpec (operandType (result)); + + aopOp (result, ic, FALSE); + + /* if the value is already in a pointer register + then don't need anything more */ + if (!AOP_INPREG (AOP (result))) + { + if (IS_AOP_PREG (result)) + { + // Aha, it is a pointer, just in disguise. + rname = aopGet (result, 0, FALSE, FALSE); + if (*rname != '@') + { + fprintf (stderr, "probable internal error: unexpected rname @ %s:%d\n", __FILE__, __LINE__); + } + else + { + // Expected case. + emitcode ("mov", "a%s,%s", rname + 1, rname); + rname++; // skip the '@'. + } + } + else + { + /* otherwise get a free pointer register */ + aop = newAsmop (0); + preg = getFreePtr (ic, aop, FALSE); + emitcode ("mov", "%s,%s", preg->name, aopGet (result, 0, FALSE, TRUE)); + rname = preg->name; + } + } + else + { + rname = aopGet (result, 0, FALSE, FALSE); + } + + aopOp (right, ic, FALSE); + + rname = Safe_strdup (rname); + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, rname, PPOINTER); + else + { + /* we can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, TRUE)); + emitcode ("movx", "@%s,a", rname); + if (size || pi) + emitcode ("inc", "%s", rname); + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) /* we had to allocate for this iCode */ + { + if (pi) + aopPut (result, rname, 0); + freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 1 && this could be used again + we have to point it back to where it + belongs */ + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth) && !pi) + { + int size = AOP_SIZE (right) - 1; + while (size--) + emitcode ("dec", "%s", rname); + } + } + Safe_free ((void *) rname); + + /* done */ + if (pi) + pi->generated = 1; + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarPointerSet - set value in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genFarPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, FALSE); + + /* so dptr now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", FPOINTER); + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset, FALSE, FALSE)); + if (offset++ > 0) + emitcode ("inc", "dptr"); + emitcode ("movx", "@dptr,a"); + } + if (pi) + emitcode ("inc", "dptr"); + } + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic, iCode * pi) +{ + int size, offset; + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (operandType (result)); + + D (emitcode (";", "genGenPointerSet")); + + aopOp (result, ic, FALSE); + loadDptrFromOperand (result, TRUE); + + /* so dptr-b now contains the address */ + aopOp (right, ic, FALSE); + + /* if bit then unpack */ + if (IS_BITFIELD (retype) || IS_BITFIELD (letype)) + { + genPackBits ((IS_BITFIELD (retype) ? retype : letype), right, "dptr", GPOINTER); + } + else + { + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitcode ("lcall", "__gptrput"); + if (size || pi) + emitcode ("inc", "dptr"); + } + } + + if (pi && AOP_TYPE (result) != AOP_STR && AOP_TYPE (result) != AOP_IMMD) + { + aopPut (result, "dpl", 0); + aopPut (result, "dph", 1); + pi->generated = 1; + } + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic, iCode * pi) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + D (emitcode (";", "genPointerSet")); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + + /* special case when cast remat */ + while (IS_SYMOP (result) && OP_SYMBOL (result)->remat && + IS_CAST_ICODE (OP_SYMBOL (result)->rematiCode) && + !IS_BITFIELD (getSpec (operandType (result)))) + { + result = IC_RIGHT (OP_SYMBOL (result)->rematiCode); + type = operandType (result); + p_type = DCL_TYPE (type); + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case IPOINTER: + genNearPointerSet (right, result, ic, pi); + break; + + case PPOINTER: + genPagedPointerSet (right, result, ic, pi); + break; + + case FPOINTER: + genFarPointerSet (right, result, ic, pi); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic, pi); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + char *dup = NULL; + + D (emitcode (";", "genIfx")); + + aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + { + toBoolean (cond); + } + else + { + isbit = 1; + if (AOP (cond)->aopu.aop_dir) + dup = Safe_strdup (AOP (cond)->aopu.aop_dir); + } + + /* the result is now in the accumulator or a directly addressable bit */ + freeAsmop (cond, NULL, ic, TRUE); + + /* if the condition is a bit variable */ + if (isbit && dup) + genIfxJump (ic, dup, NULL, NULL, NULL, popIc); + else if (isbit && IS_OP_ACCUSE (cond)) + genIfxJump (ic, "c", NULL, NULL, NULL, popIc); + else if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + genIfxJump (ic, SPIL_LOC (cond)->rname, NULL, NULL, NULL, popIc); + else if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname, NULL, NULL, NULL, popIc); + else + genIfxJump (ic, "a", NULL, NULL, NULL, popIc); + + if (dup) + Safe_free (dup); + + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int size, offset; + + D (emitcode (";", "genAddrOf")); + + aopOp (IC_RESULT (ic), ic, FALSE); + + /* if the operand is on the stack then we + need to get the stack offset of this + variable */ + if (sym->onStack) + { + int stack_offset = stackoffset (sym); + + /* if it has an offset then we need to compute it */ + if (stack_offset) + { + if ((abs (stack_offset) == 1) && !AOP_NEEDSACC (IC_RESULT (ic)) && !isOperandVolatile (IC_RESULT (ic), FALSE)) + { + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + if (stack_offset > 0) + emitcode ("inc", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + else + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), LSB, FALSE, FALSE)); + } + else + { + emitcode ("mov", "a,%s", SYM_BP (sym)); + emitcode ("add", "a,#!constbyte", stack_offset & 0xff); + aopPut (IC_RESULT (ic), "a", 0); + } + } + else + { + /* we can just move _bp */ + aopPut (IC_RESULT (ic), SYM_BP (sym), 0); + } + /* fill the result with zero */ + size = AOP_SIZE (IC_RESULT (ic)) - 1; + + offset = 1; + while (size--) + { + aopPut (IC_RESULT (ic), zero, offset++); + } + goto release; + } + + /* object not on stack then we need the name */ + size = getDataSize (IC_RESULT (ic)); + offset = 0; + + while (size--) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "#(%s >> %d)", sym->rname, offset * 8); + } + else + { + dbuf_printf (&dbuf, "#%s", sym->rname); + } + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), offset++); + dbuf_destroy (&dbuf); + } + if (opIsGptr (IC_RESULT (ic))) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", pointerTypeToGPByte (pointerCode (getSpec (operandType (IC_LEFT (ic)))), NULL, NULL)); + aopPut (IC_RESULT (ic), dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genFarFarAssign - assignment when both are in far space */ +/*-----------------------------------------------------------------*/ +static void +genFarFarAssign (operand * result, operand * right, iCode * ic) +{ + int size = AOP_SIZE (right); + int offset = 0; + + D (emitcode (";", "genFarFarAssign")); + + /* first push the right side on to the stack */ + while (size--) + { + MOVA (aopGet (right, offset++, FALSE, FALSE)); + emitpush ("acc"); + } + + freeAsmop (right, NULL, ic, FALSE); + /* now assign DPTR to result */ + aopOp (result, ic, FALSE); + size = AOP_SIZE (result); + while (size--) + { + emitpop ("acc"); + aopPut (result, "a", --offset); + } + freeAsmop (result, NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset; + + D (emitcode (";", "genAssign")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* if they are the same */ + if (operandsEqu (result, right) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + return; + + aopOp (right, ic, FALSE); + + /* special case both in far space */ + if (AOP_TYPE (right) == AOP_DPTR && IS_TRUE_SYMOP (result) && isOperandInFarSpace (result)) + { + genFarFarAssign (result, right, ic); + return; + } + + aopOp (result, ic, TRUE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result)) && !isOperandVolatile (result, FALSE) && !isOperandVolatile (right, FALSE)) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + assignBit (result, right); + goto release; + } + + /* bit variables done */ + /* general case */ + + size = getDataSize (result); + + if ((size > 1) && (AOP_TYPE (result) != AOP_REG) && /* for registers too? (regression test passes) */ + (AOP_TYPE (right) == AOP_LIT) && !aopPutUsesAcc (result, aopGet (right, 0, FALSE, FALSE), 0)) + { + genLiteralAssign (result, right, size, aopPut); + } + else + { + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + } + adjustArithmeticResult (ic); + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + operand *cond = IC_JTCOND (ic); + symbol *jtab, *jtablo, *jtabhi; + unsigned int count; + const char *l; + bool useB = FALSE; + + D (emitcode (";", "genJumpTab")); + + count = elementsInSet (IC_JTLABELS (ic)); + + if ((count <= 7) || + (count <= 16 && optimize.codeSpeed) || + options.acall_ajmp) + { + /* This algorithm needs 9 cycles and 7 + 3*n bytes + if the switch argument is in a register. + (6 + 2*n bytes when options.acall_ajmp is set) */ + /* Peephole may not convert ljmp to sjmp or ret + labelIsReturnOnly & labelInRange must check + currPl->ic->op != JUMPTABLE */ + aopOp (cond, ic, FALSE); + /* get the condition into accumulator */ + l = aopGet (cond, 0, FALSE, FALSE); + MOVA (l); + /* multiply by three */ + if ((AOP_TYPE (cond) == AOP_REG) || (IS_AOP_PREG (cond) && !AOP (cond)->paged && !IS_VOLATILE (operandType (cond)))) + { + emitcode ("add", "a,%s", l); + if (options.acall_ajmp == 0) + emitcode ("add", "a,%s", l); + } + else + { + if (options.acall_ajmp == 0) + { + MOVB ("#0x03"); + emitcode ("mul", "ab"); + } + else + { + emitcode ("add", "a,acc"); + } + } + freeAsmop (cond, NULL, ic, TRUE); + + jtab = newiTempLabel (NULL); + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtab->key)); + emitcode ("jmp", "@a+dptr"); + emitLabel (jtab); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + if (options.acall_ajmp) + emitcode ("ajmp", "!tlabel", labelKey2num (jtab->key)); + else + emitcode ("ljmp", "!tlabel", labelKey2num (jtab->key)); + } + else + { + /* this algorithm needs 14 cycles and 14 + 2*n bytes + if the switch argument is in a register. + For n>7 this algorithm may be more compact */ + jtablo = newiTempLabel (NULL); + jtabhi = newiTempLabel (NULL); + + /* get the condition into accumulator. + Using b as temporary storage, if register push/pop is needed */ + aopOp (cond, ic, FALSE); + l = aopGet (cond, 0, FALSE, FALSE); + if ((AOP_TYPE (cond) == AOP_R0 && _G.r0Pushed) || + (AOP_TYPE (cond) == AOP_R1 && _G.r1Pushed) || + EQ (l, "a") || EQ (l, "acc") || + (count > 125 && EQ (l, "dpl")) || + IS_VOLATILE (operandType (cond))) + { + // (MB) what if B is in use??? + wassertl (!BINUSE, "B was in use"); + MOVB (l); + l = "b"; + useB = TRUE; + } + freeAsmop (cond, NULL, ic, TRUE); + MOVA (l); + if (count <= 125) + { + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+pc"); + if (EQ (l, "dpl")) + { + emitcode ("xch", "a,dpl"); + } + else + { + emitcode ("mov", "dpl,a"); + MOVA (l); + } + emitcode ("add", "a,#(!tlabel-3-.)", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+pc"); + emitcode ("mov", "dph,a"); + } + else + { + /* this scales up to n<=255, but needs four more bytes + and changes dptr */ + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtablo->key)); + emitcode ("movc", "a,@a+dptr"); + if (useB) + { + emitcode ("xch", "a,b"); + } + else + { + emitpush ("acc"); + MOVA (l); + } + emitcode ("mov", "dptr,#!tlabel", labelKey2num (jtabhi->key)); + emitcode ("movc", "a,@a+dptr"); + emitcode ("mov", "dph,a"); + if (useB) + emitcode ("mov", "dpl,b"); + else + emitpop ("dpl"); + } + + emitcode ("clr", "a"); + emitcode ("jmp", "@a+dptr"); + + /* now generate jump table, LSB */ + emitLabel (jtablo); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel", labelKey2num (jtab->key)); + + /* now generate jump table, MSB */ + emitLabel (jtabhi); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + emitcode (".db", "!tlabel>>8", labelKey2num (jtab->key)); + } +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + bool right_boolean; + + D (emitcode (";", "genCast")); + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + right_boolean = IS_BOOLEAN (rtype) || IS_BITFIELD (rtype) && SPEC_BLEN (getSpec (rtype)) == 1; + + /* if the result is a bit (and not a bitfield) */ + if (IS_BOOLEAN (OP_SYMBOL (result)->type) && !right_boolean) + { + assignBit (result, right); + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + /* if they are in the same place */ + if (sameRegs (AOP (right), AOP (result))) + goto release; + + /* if they are in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* if the either is of type pointer */ + if ((IS_PTR (ctype) || IS_PTR (rtype)) && !IS_INTEGRAL (rtype)) + { + int p_type; + sym_link *type = operandType (right); + sym_link *etype = getSpec (type); + + /* pointer to generic pointer or long */ + if (AOP_SIZE (result) >= GPTRSIZE) + { + if (IS_PTR (type)) + { + p_type = DCL_TYPE (type); + } + else + { + if (SPEC_SCLS (etype) == S_REGISTER) + { + // let's assume it is a generic pointer + p_type = GPOINTER; + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + } + } + + /* the first two bytes are known */ + size = GPTRSIZE - 1; + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + /* the third byte depending on type */ + { + int gpVal; + + /* If there will be no loss of precision, handle generic */ + /* pointer as special case to avoid generating warning */ + if (p_type == GPOINTER && AOP_SIZE (result) >= GPTRSIZE) + gpVal = -1; + else + gpVal = pointerTypeToGPByte (p_type, NULL, NULL); + + if (gpVal == -1) + { + // pointerTypeToGPByte will have warned, just copy. + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + } + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#0x%02x", gpVal); + aopPut (result, dbuf_c_str (&dbuf), GPTRSIZE - 1); + dbuf_destroy (&dbuf); + } + } + + /* 8051 uses unsigned address spaces, so no sign extension needed. */ + /* Pad the remaining bytes of the result (if any) with 0. */ + size = AOP_SIZE (result) - GPTRSIZE; + offset = GPTRSIZE; + while (size--) + { + aopPut (result, zero, offset++); + } + goto release; + } + + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source */ + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + while (size--) + { + aopPut (result, aopGet (right, offset, FALSE, FALSE), offset); + offset++; + } + + /* now depending on the sign of the source && destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + if (!IS_SPEC (rtype) || SPEC_USIGN (rtype) || AOP_TYPE (right) == AOP_CRY) + { + while (size--) + { + aopPut (result, zero, offset++); + } + } + else + { + /* we need to extend the sign :{ */ + MOVA (aopGet (right, AOP_SIZE (right) - 1, FALSE, FALSE)); + emitcode ("rlc", "a"); + emitcode ("subb", "a,acc"); + while (size--) + aopPut (result, "a", offset++); + } + + /* we are done hurray !!!! */ + +release: + freeAsmop (result, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instruction */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + symbol *lbl, *lbl1; + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + + popForBranch (ic->next, TRUE); + + D (emitcode (";", "genDjnz")); + + lbl = newiTempLabel (NULL); + lbl1 = newiTempLabel (NULL); + + aopOp (IC_RESULT (ic), ic, FALSE); + + if (AOP_NEEDSACC (IC_RESULT (ic))) + { + /* If the result is accessed indirectly via + * the accumulator, we must explicitly write + * it back after the decrement. + */ + const char *rByte = aopGet (IC_RESULT (ic), 0, FALSE, FALSE); + + if (!EQ (rByte, "a")) + { + /* Something is hopelessly wrong */ + fprintf (stderr, "*** warning: internal error at %s:%d\n", __FILE__, __LINE__); + /* We can just give up; the generated code will be inefficient, + * but what the hey. + */ + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + return 0; + } + emitcode ("dec", "%s", rByte); + aopPut (IC_RESULT (ic), rByte, 0); + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else if (IS_AOP_PREG (IC_RESULT (ic))) + { + emitcode ("dec", "%s", aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + MOVA (aopGet (IC_RESULT (ic), 0, FALSE, FALSE)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + emitcode ("jnz", "!tlabel", labelKey2num (lbl->key)); + } + else + { + emitcode ("djnz", "%s,!tlabel", aopGet (IC_RESULT (ic), 0, FALSE, FALSE), labelKey2num (lbl->key)); + } + emitcode ("sjmp", "!tlabel", labelKey2num (lbl1->key)); + emitLabel (lbl); + emitcode ("ljmp", "!tlabel", labelKey2num (IC_TRUE (ifx)->key)); + emitLabel (lbl1); + + if (!ifx->generated) + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + int size = getSize (operandType (IC_RESULT (ic))); + int offset = 0; + + D (emitcode (";", "genReceive")); + + if (ic->argreg == 1) + { + /* first parameter */ + if ((isOperandInFarSpace (IC_RESULT (ic)) || + isOperandInPagedSpace (IC_RESULT (ic))) && (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic)))) + { + reg_info *tempRegs[8]; + int receivingA = 0; + int roffset = 0; + + for (offset = 0; offset < size; offset++) + if (EQ (fReturn[offset], "a")) + receivingA = 1; + + if (!receivingA) + { + if (size == 1 || getTempRegs (tempRegs, size - 1, ic)) + { + for (offset = size - 1; offset > 0; offset--) + emitcode ("mov", "%s,%s", tempRegs[roffset++]->name, fReturn[offset]); + emitcode ("mov", "a,%s", fReturn[0]); + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + aopPut (IC_RESULT (ic), "a", offset); + for (offset = 1; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[--roffset]->name, offset); + goto release; + } + } + else + { + if (getTempRegs (tempRegs, size, ic)) + { + for (offset = 0; offset < size; offset++) + emitcode ("mov", "%s,%s", tempRegs[offset]->name, fReturn[offset]); + aopOp (IC_RESULT (ic), ic, FALSE); + for (offset = 0; offset < size; offset++) + aopPut (IC_RESULT (ic), tempRegs[offset]->name, offset); + goto release; + } + } + + offset = fReturnSizeMCS51 - size; + while (size--) + { + emitpush ((!EQ (fReturn[fReturnSizeMCS51 - offset - 1], "a") ? fReturn[fReturnSizeMCS51 - offset - 1] : "acc")); + offset++; + } + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + while (size--) + { + emitpop ("acc"); + aopPut (IC_RESULT (ic), "a", offset++); + } + } + else + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + assignResultValue (IC_RESULT (ic), NULL); + } + } + else if (ic->argreg > 12) + { + /* bit parameters */ + reg_info *reg = OP_SYMBOL (IC_RESULT (ic))->regs[0]; + + BitBankUsed = 1; + if (!reg || reg->rIdx != ic->argreg - 5) + { + aopOp (IC_RESULT (ic), ic, FALSE); + emitcode ("mov", "c,%s", rb1regs[ic->argreg - 5]); + outBitC (IC_RESULT (ic)); + } + } + else + { + /* other parameters */ + int rb1off; + aopOp (IC_RESULT (ic), ic, FALSE); + rb1off = ic->argreg; + while (size--) + { + aopPut (IC_RESULT (ic), rb1regs[rb1off++ - 5], offset++); + } + } + +release: + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + int size, offset; + + D (emitcode (";", "genDummyRead")); + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } + + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (op) == AOP_CRY) + emitcode ("mov", "c,%s", AOP (op)->aopu.aop_dir); + else + { + /* bit variables done */ + /* general case */ + size = AOP_SIZE (op); + offset = 0; + while (size--) + { + MOVA (aopGet (op, offset, FALSE, FALSE)); + offset++; + } + } + + freeAsmop (op, NULL, ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (iCode * ic) +{ + symbol *tlbl = newiTempLabel (NULL); + + D (emitcode (";", "genCritical")); + + if (IC_RESULT (ic)) + { + aopOp (IC_RESULT (ic), ic, TRUE); + aopPut (IC_RESULT (ic), one, 0); /* save old ea in an operand */ + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + aopPut (IC_RESULT (ic), zero, 0); + emitLabel (tlbl); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + else + { + emitcode ("setb", "c"); + emitcode ("jbc", "ea,!tlabel", labelKey2num (tlbl->key)); /* atomic test & clear */ + emitcode ("clr", "c"); + emitLabel (tlbl); + emitpush ("psw"); /* save old ea via c in psw on top of stack */ + } +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (iCode * ic) +{ + D (emitcode (";", "genEndCritical")); + + if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE); + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + emitcode ("mov", "c,%s", IC_RIGHT (ic)->aop->aopu.aop_dir); + emitcode ("mov", "ea,c"); + } + else + { + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_DUMMY) + MOVA (aopGet (IC_RIGHT (ic), 0, FALSE, FALSE)); + emitcode ("rrc", "a"); + emitcode ("mov", "ea,c"); + } + freeAsmop (IC_RIGHT (ic), NULL, ic, TRUE); + } + else + { + emitpop ("psw"); /* restore ea via c in psw on top of stack */ + emitcode ("mov", "ea,c"); + } +} + +/*-----------------------------------------------------------------*/ +/* gen51Code - generate code for 8051 based controllers */ +/*-----------------------------------------------------------------*/ +void +gen51Code (iCode * lic) +{ + iCode *ic; + int cln = 0; +#ifdef _DEBUG + int cseq = 0; +#endif + + _G.currentFunc = NULL; + + /* print the allocation information */ + if (allocInfo && currFunc) + printAllocInfo (currFunc, codeOutBuf); + /* if debug information required */ + if (options.debug && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + /* stack pointer name */ + if (options.useXstack) + spname = "_spx"; + else + spname = "sp"; + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + if (!options.noCcodeInAsm) + { + emitcode (";", "%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + } + cln = ic->lineno; + } +#ifdef _DEBUG + if (ic->seqPoint && ic->seqPoint != cseq) + { + emitcode (";", "sequence point %d", ic->seqPoint); + cseq = ic->seqPoint; + } +#endif + if (options.iCodeInAsm) + { + char regsInUse[80]; + int i; + const char *iLine; + +#if 0 + for (i = 0; i < 8; i++) + { + sprintf (®sInUse[i], "%c", ic->riu & (1 << i) ? i + '0' : '-'); /* show riu */ + regsInUse[i] = 0; + } +#else + strcpy (regsInUse, "--------"); + for (i = 0; i < 8; i++) + { + if (bitVectBitValue (ic->rMask, i)) + { + int offset = regs8051[i].offset; + regsInUse[offset] = offset + '0'; /* show rMask */ + } + } +#endif + iLine = printILine (ic); + emitcode (";", "[%s] ic:%d: %s", regsInUse, ic->seq, iLine); + dbuf_free (iLine); + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + { + iCode *ifxIc, *popIc; + bool CommonRegs = FALSE; + + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop (or several) then the if statement might + be using some of the registers being popped which + would destroy the contents of the register so + we need to check for this condition and handle it */ + for (ifxIc = ic->next; ifxIc && ifxIc->op == IPOP; ifxIc = ifxIc->next); + for (popIc = ic; popIc && popIc->op == IPOP; popIc = popIc->next) + CommonRegs |= (ifxIc && ifxIc->op == IFX && !ifxIc->generated && regsInCommon (IC_LEFT (popIc), IC_COND (ifxIc))); + if (CommonRegs) + genIfx (ifxIc, ic); + else + genIpop (ic); + } + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + in decorateType() in SDCCast.c */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETHBIT: + assert (0); + break; + + case GETABIT: + genGetAbit (ic); + break; + + case GETBYTE: + genGetByte (ic); + break; + + case GETWORD: + genGetWord (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic, hasInc (IC_LEFT (ic), ic, getSize (operandType (IC_RESULT (ic)))), ifxForOp (IC_RESULT (ic), ic)); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic, hasInc (IC_RESULT (ic), ic, getSize (operandType (IC_RIGHT (ic))))); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + case SWAP: + genSwap (ic); + break; + + default: + ic = ic; + } + } + + genLine.lineElement.ic = NULL; + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} diff --git a/src/mcs51/gen.h b/src/mcs51/gen.h new file mode 100644 index 0000000..66e3e06 --- /dev/null +++ b/src/mcs51/gen.h @@ -0,0 +1,88 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for 8051 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGEN51_H +#define SDCCGEN51_H + +enum +{ + AOP_LIT = 1, + AOP_REG, AOP_DIR, + AOP_DPTR, AOP_R0, AOP_R1, + AOP_STK, AOP_IMMD, AOP_STR, + AOP_CRY, AOP_ACC, AOP_DUMMY +}; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop +{ + short type; + /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_DPTR - dptr contains address of operand + AOP_R0/R1 - r0/r1 contains address of operand + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_ACC - result is in the acc:b pair + AOP_DUMMY - read as 0, discard writes + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned short allocated; /* number of times allocated */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[8]; /* array of registers */ + char *aop_dir; /* if direct */ + reg_info *aop_ptr; /* either -> to r0 or r1 */ + struct + { + int from_cast_remat; /* cast remat created this : immd2 field used for highest order */ + char *aop_immd1; /* if immediate others are implied */ + char *aop_immd2; /* cast remat will generate this */ + } aop_immd; + symbol *aop_sym; /* symbol when AOP_STK */ + char *aop_str[8]; /* just a string array containing the location */ + } + aopu; +} +asmop; + +void gen51Code (iCode *); +void mcs51_emitDebuggerSymbol (const char *); + +extern char *fReturn8051[]; +extern unsigned fReturnSizeMCS51; +//extern char **fReturn; + +#endif diff --git a/src/mcs51/main.c b/src/mcs51/main.c new file mode 100644 index 0000000..207356c --- /dev/null +++ b/src/mcs51/main.c @@ -0,0 +1,934 @@ +/*------------------------------------------------------------------------- + main.c - mcs51 specific general functions + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Copyright (C) 2000, Michael Hope + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------*/ +/* + Note that mlh prepended _mcs51_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "main.h" +#include "ralloc.h" +#include "gen.h" +#include "peep.h" +#include "rtrack.h" +#include "dbuf_string.h" +#include "../SDCCutil.h" + +static char _defaultRules[] = +{ +#include "peeph.rul" +}; + +#define OPTION_SMALL_MODEL "--model-small" +#define OPTION_MEDIUM_MODEL "--model-medium" +#define OPTION_LARGE_MODEL "--model-large" +#define OPTION_HUGE_MODEL "--model-huge" +#define OPTION_STACK_SIZE "--stack-size" + +static OPTION _mcs51_options[] = + { + { 0, OPTION_SMALL_MODEL, NULL, "internal data space is used (default)"}, + { 0, OPTION_MEDIUM_MODEL, NULL, "external paged data space is used"}, + { 0, OPTION_LARGE_MODEL, NULL, "external data space is used"}, + { 0, OPTION_HUGE_MODEL, NULL, "functions are banked, data in external space"}, + { 0, OPTION_STACK_SIZE, &options.stack_size, "Tells the linker to allocate this space for stack", CLAT_INTEGER }, + { 0, "--parms-in-bank1", &options.parms_in_bank1, "use Bank1 for parameter passing"}, + { 0, "--pack-iram", NULL, "Tells the linker to pack variables in internal ram (default)"}, + { 0, "--no-pack-iram", &options.no_pack_iram, "Deprecated: Tells the linker not to pack variables in internal ram"}, + { 0, "--acall-ajmp", &options.acall_ajmp, "Use acall/ajmp instead of lcall/ljmp" }, + { 0, "--no-ret-without-call", &options.no_ret_without_call, "Do not use ret independent of acall/lcall" }, + { 0, NULL } + }; + +/* list of key words used by msc51 */ +static char *_mcs51_keywords[] = +{ + "at", + "banked", + "bit", + "code", + "critical", + "data", + "far", + "generic", + "idata", + "interrupt", + "naked", + "near", + "nonbanked", + "overlay", + "pdata", + "reentrant", + "sbit", + "sfr", + "sfr16", + "sfr32", + "using", + "xdata", + NULL +}; + + + +void mcs51_assignRegisters (ebbIndex *); + +static int regParmFlg = 0; /* determine if we can register a parameter */ +static int regBitParmFlg = 0; /* determine if we can register a bit parameter */ + +static void +_mcs51_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); +} + +static void +_mcs51_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; + regBitParmFlg = 0; +} + +static int +_mcs51_regparm (sym_link * l, bool reentrant) +{ + if (IS_SPEC(l) && (SPEC_NOUN(l) == V_BIT)) + { + /* bit parameters go to b0 thru b7 */ + if (reentrant && (regBitParmFlg < 8)) + { + regBitParmFlg++; + return 12 + regBitParmFlg; + } + return 0; + } + if (options.parms_in_bank1 == 0) + { + /* simple can pass only the first parameter in a register */ + if (regParmFlg) + return 0; + + regParmFlg = 1; + return 1; + } + else + { + int size = getSize(l); + int remain; + + /* first one goes the usual way to DPTR */ + if (regParmFlg == 0) + { + regParmFlg += 4 ; + return 1; + } + /* second one onwards goes to RB1_0 thru RB1_7 */ + remain = regParmFlg - 4; + if (size > (8 - remain)) + { + regParmFlg = 12 ; + return 0; + } + regParmFlg += size ; + return regParmFlg - size + 1; + } +} + +static bool +_mcs51_parseOptions (int *pargc, char **argv, int *i) +{ + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + return FALSE; +} + +static void +_mcs51_finaliseOptions (void) +{ + if (options.noXinitOpt) + port->genXINIT=0; + + switch (options.model) + { + case MODEL_SMALL: + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + port->s.ptr_size = 3; + break; + case MODEL_MEDIUM: + port->mem.default_local_map = pdata; + port->mem.default_globl_map = pdata; + port->s.ptr_size = 3; + break; + case MODEL_LARGE: + case MODEL_HUGE: + port->mem.default_local_map = xdata; + port->mem.default_globl_map = xdata; + port->s.ptr_size = 3; + break; + default: + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + break; + } + + if (options.parms_in_bank1) + addSet(&preArgvSet, Safe_strdup("-DSDCC_PARMS_IN_BANK1")); + + /* mcs51 has an assembly coded float library that's almost always reentrant */ + if (!options.useXstack) + options.float_rent = 1; + + if (options.omitFramePtr) + port->stack.reent_overhead = 0; + + /* set up external stack location if not explicitly specified */ + if (!options.xstack_loc) + options.xstack_loc = options.xdata_loc; +} + +static void +_mcs51_setDefaultOptions (void) +{ +} + +static const char * +_mcs51_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_mcs51_genAssemblerPreamble (FILE * of) +{ + if (options.parms_in_bank1) + { + int i; + for (i=0; i < 8 ; i++ ) + fprintf (of, "\tb1_%d = 0x%x \n", i, 8+i); + } +} + +/* Generate interrupt vector table. */ +static int +_mcs51_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + int i; + + dbuf_printf (oBuf, "\t%cjmp\t__sdcc_gsinit_startup\n", options.acall_ajmp?'a':'l'); + if((options.acall_ajmp)&&(maxInterrupts)) dbuf_printf (oBuf, "\t.ds\t1\n"); + + /* now for the other interrupts */ + for (i = 0; i < maxInterrupts; i++) + { + if (interrupts[i]) + { + dbuf_printf (oBuf, "\t%cjmp\t%s\n", options.acall_ajmp?'a':'l', interrupts[i]->rname); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t%d\n", options.acall_ajmp?6:5); + } + else + { + dbuf_printf (oBuf, "\treti\n"); + if ( i != maxInterrupts - 1 ) + dbuf_printf (oBuf, "\t.ds\t7\n"); + } + } + return TRUE; +} + +static void +_mcs51_genExtraAreas(FILE *of, bool hasMain) +{ + tfprintf (of, "\t!area\n", HOME_NAME); + tfprintf (of, "\t!area\n", "GSINIT0 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT1 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT2 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT3 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT4 (CODE)"); + tfprintf (of, "\t!area\n", "GSINIT5 (CODE)"); + tfprintf (of, "\t!area\n", STATIC_NAME); + tfprintf (of, "\t!area\n", port->mem.post_static_name); + tfprintf (of, "\t!area\n", CODE_NAME); +} + +static void +_mcs51_genInitStartup (FILE *of) +{ + tfprintf (of, "\t!global\n", "__sdcc_gsinit_startup"); + tfprintf (of, "\t!global\n", "__sdcc_program_startup"); + tfprintf (of, "\t!global\n", "__start__stack"); + + if (options.useXstack) + { + tfprintf (of, "\t!global\n", "__sdcc_init_xstack"); + tfprintf (of, "\t!global\n", "__start__xstack"); + } + + // if the port can copy the XINIT segment to XISEG + if (port->genXINIT) + { + port->genXINIT(of); + } + + if (!getenv("SDCC_NOGENRAMCLEAR")) + tfprintf (of, "\t!global\n", "__mcs51_genRAMCLEAR"); +} + + +/* Generate code to copy XINIT to XISEG */ +static void _mcs51_genXINIT (FILE * of) +{ + tfprintf (of, "\t!global\n", "__mcs51_genXINIT"); + + if (!getenv("SDCC_NOGENRAMCLEAR")) + tfprintf (of, "\t!global\n", "__mcs51_genXRAMCLEAR"); +} + + +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ + operand *result = IC_RESULT(ic); + sym_link *result_type = operandType(result); + + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + /* if bitwise | add & subtract then no since mcs51 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') + { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } + + /* for others it is cheaper to do the cse */ + return 1; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETABIT + || op == GETBYTE + || op == GETWORD + || (op == SWAP && size <= 2) + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + return getSize (left) == 1 && getSize (right) == 1; +} + +static int +instructionSize(char *inst, char *op1, char *op2) +{ + #define ISINST(s) (strncmp(inst, (s), sizeof(s)-1) == 0) + #define IS_A(s) (*(s) == 'a' && *(s+1) == '\0') + #define IS_C(s) (*(s) == 'c' && *(s+1) == '\0') + #define IS_Rn(s) (*(s) == 'r' && *(s+1) >= '0' && *(s+1) <= '7') + #define IS_atRi(s) (*(s) == '@' && *(s+1) == 'r') + + /* Based on the current (2003-08-22) code generation for the + small library, the top instruction probability is: + + 57% mov/movx/movc + 6% push + 6% pop + 4% inc + 4% lcall + 4% add + 3% clr + 2% subb + */ + /* mov, push, & pop are the 69% of the cases. Check them first! */ + if (ISINST ("mov")) + { + if (*(inst+3)=='x') return 1; /* movx */ + if (*(inst+3)=='c') return 1; /* movc */ + if (IS_C (op1) || IS_C (op2)) return 2; + if (IS_A (op1)) + { + if (IS_Rn (op2) || IS_atRi (op2)) return 1; + return 2; + } + if (IS_Rn(op1) || IS_atRi(op1)) + { + if (IS_A(op2)) return 1; + return 2; + } + if (strcmp (op1, "dptr") == 0) return 3; + if (IS_A (op2) || IS_Rn (op2) || IS_atRi (op2)) return 2; + return 3; + } + + if (ISINST ("push")) return 2; + if (ISINST ("pop")) return 2; + + if (ISINST ("lcall")) return 3; + if (ISINST ("ret")) return 1; + if (ISINST ("ljmp")) return 3; + if (ISINST ("sjmp")) return 2; + if (ISINST ("rlc")) return 1; + if (ISINST ("rrc")) return 1; + if (ISINST ("rl")) return 1; + if (ISINST ("rr")) return 1; + if (ISINST ("swap")) return 1; + if (ISINST ("jc")) return 2; + if (ISINST ("jnc")) return 2; + if (ISINST ("jb")) return 3; + if (ISINST ("jnb")) return 3; + if (ISINST ("jbc")) return 3; + if (ISINST ("jmp")) return 1; // always jmp @a+dptr + if (ISINST ("jz")) return 2; + if (ISINST ("jnz")) return 2; + if (ISINST ("cjne")) return 3; + if (ISINST ("mul")) return 1; + if (ISINST ("div")) return 1; + if (ISINST ("da")) return 1; + if (ISINST ("xchd")) return 1; + if (ISINST ("reti")) return 1; + if (ISINST ("nop")) return 1; + if (ISINST ("acall")) return 2; + if (ISINST ("ajmp")) return 2; + + + if (ISINST ("add") || ISINST ("addc") || ISINST ("subb") || ISINST ("xch")) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + if (ISINST ("inc") || ISINST ("dec")) + { + if (IS_A(op1) || IS_Rn(op1) || IS_atRi(op1)) return 1; + if (strcmp(op1, "dptr") == 0) return 1; + return 2; + } + if (ISINST ("anl") || ISINST ("orl") || ISINST ("xrl")) + { + if (IS_C(op1)) return 2; + if (IS_A(op1)) + { + if (IS_Rn(op2) || IS_atRi(op2)) return 1; + return 2; + } + else + { + if (IS_A(op2)) return 2; + return 3; + } + } + if (ISINST ("clr") || ISINST ("setb") || ISINST ("cpl")) + { + if (IS_A(op1) || IS_C(op1)) return 1; + return 2; + } + if (ISINST ("djnz")) + { + if (IS_Rn(op1)) return 2; + return 3; + } + + /* If the instruction is unrecognized, we shouldn't try to optimize. */ + /* Return a large value to discourage optimization. */ + return 999; +} + +static asmLineNode * +newAsmLineNode (void) +{ + asmLineNode *aln; + + aln = Safe_alloc ( sizeof (asmLineNode)); + aln->size = 0; + aln->regsRead = NULL; + aln->regsWritten = NULL; + + return aln; +} + + +typedef struct mcs51operanddata + { + char name[6]; + int regIdx1; + int regIdx2; + } +mcs51operanddata; + +static mcs51operanddata mcs51operandDataTable[] = + { + {"a", A_IDX, -1}, + {"ab", A_IDX, B_IDX}, + {"ac", CND_IDX, -1}, + {"acc", A_IDX, -1}, + {"ar0", R0_IDX, -1}, + {"ar1", R1_IDX, -1}, + {"ar2", R2_IDX, -1}, + {"ar3", R3_IDX, -1}, + {"ar4", R4_IDX, -1}, + {"ar5", R5_IDX, -1}, + {"ar6", R6_IDX, -1}, + {"ar7", R7_IDX, -1}, + {"b", B_IDX, -1}, + {"c", CND_IDX, -1}, + {"cy", CND_IDX, -1}, + {"dph", DPH_IDX, -1}, + {"dpl", DPL_IDX, -1}, + {"dptr", DPL_IDX, DPH_IDX}, + {"f0", CND_IDX, -1}, + {"f1", CND_IDX, -1}, + {"ov", CND_IDX, -1}, + {"p", CND_IDX, -1}, + {"psw", CND_IDX, -1}, + {"r0", R0_IDX, -1}, + {"r1", R1_IDX, -1}, + {"r2", R2_IDX, -1}, + {"r3", R3_IDX, -1}, + {"r4", R4_IDX, -1}, + {"r5", R5_IDX, -1}, + {"r6", R6_IDX, -1}, + {"r7", R7_IDX, -1}, + }; + +static int +mcs51operandCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((mcs51operanddata *)member)->name); +} + +static void +updateOpRW (asmLineNode *aln, char *op, char *optype) +{ + mcs51operanddata *opdat; + char *dot; + + dot = strchr(op, '.'); + if (dot) + *dot = '\0'; + + opdat = bsearch (op, mcs51operandDataTable, + sizeof(mcs51operandDataTable)/sizeof(mcs51operanddata), + sizeof(mcs51operanddata), mcs51operandCompare); + + if (opdat && strchr(optype,'r')) + { + if (opdat->regIdx1 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx1); + if (opdat->regIdx2 >= 0) + aln->regsRead = bitVectSetBit (aln->regsRead, opdat->regIdx2); + } + if (opdat && strchr(optype,'w')) + { + if (opdat->regIdx1 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx1); + if (opdat->regIdx2 >= 0) + aln->regsWritten = bitVectSetBit (aln->regsWritten, opdat->regIdx2); + } + if (op[0] == '@') + { + if (!strcmp(op, "@r0")) + aln->regsRead = bitVectSetBit (aln->regsRead, R0_IDX); + if (!strcmp(op, "@r1")) + aln->regsRead = bitVectSetBit (aln->regsRead, R1_IDX); + if (strstr(op, "dptr")) + { + aln->regsRead = bitVectSetBit (aln->regsRead, DPL_IDX); + aln->regsRead = bitVectSetBit (aln->regsRead, DPH_IDX); + } + if (strstr(op, "a+")) + aln->regsRead = bitVectSetBit (aln->regsRead, A_IDX); + } +} + +typedef struct mcs51opcodedata + { + char name[6]; + char class[3]; + char pswtype[3]; + char op1type[3]; + char op2type[3]; + } +mcs51opcodedata; + +static mcs51opcodedata mcs51opcodeDataTable[] = + { + {"acall","j", "", "", ""}, + {"add", "", "w", "rw", "r"}, + {"addc", "", "rw", "rw", "r"}, + {"ajmp", "j", "", "", ""}, + {"anl", "", "", "rw", "r"}, + {"cjne", "j", "w", "r", "r"}, + {"clr", "", "", "w", ""}, + {"cpl", "", "", "rw", ""}, + {"da", "", "rw", "rw", ""}, + {"dec", "", "", "rw", ""}, + {"div", "", "w", "rw", ""}, + {"djnz", "j", "", "rw", ""}, + {"inc", "", "", "rw", ""}, + {"jb", "j", "", "r", ""}, + {"jbc", "j", "", "rw", ""}, + {"jc", "j", "", "", ""}, + {"jmp", "j", "", "", ""}, + {"jnb", "j", "", "r", ""}, + {"jnc", "j", "", "", ""}, + {"jnz", "j", "", "", ""}, + {"jz", "j", "", "", ""}, + {"lcall","j", "", "", ""}, + {"ljmp", "j", "", "", ""}, + {"mov", "", "", "w", "r"}, + {"movc", "", "", "w", "r"}, + {"movx", "", "", "w", "r"}, + {"mul", "", "w", "rw", ""}, + {"nop", "", "", "", ""}, + {"orl", "", "", "rw", "r"}, + {"pop", "", "", "w", ""}, + {"push", "", "", "r", ""}, + {"ret", "j", "", "", ""}, + {"reti", "j", "", "", ""}, + {"rl", "", "", "rw", ""}, + {"rlc", "", "rw", "rw", ""}, + {"rr", "", "", "rw", ""}, + {"rrc", "", "rw", "rw", ""}, + {"setb", "", "", "w", ""}, + {"sjmp", "j", "", "", ""}, + {"subb", "", "rw", "rw", "r"}, + {"swap", "", "", "rw", ""}, + {"xch", "", "", "rw", "rw"}, + {"xchd", "", "", "rw", "rw"}, + {"xrl", "", "", "rw", "r"}, + }; + +static int +mcs51opcodeCompare (const void *key, const void *member) +{ + return strcmp((const char *)key, ((mcs51opcodedata *)member)->name); +} + +static asmLineNode * +asmLineNodeFromLineNode (lineNode *ln) +{ + asmLineNode *aln = newAsmLineNode(); + char *op, op1[256], op2[256]; + int opsize; + const char *p; + char inst[8]; + mcs51opcodedata *opdat; + + p = ln->line; + + while (*p && isspace(*p)) p++; + for (op = inst, opsize=1; *p; p++) + { + if (isspace(*p) || *p == ';' || *p == ':' || *p == '=') + break; + else + if (opsize < sizeof(inst)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ';' || *p == ':' || *p == '=') + return aln; + + while (*p && isspace(*p)) p++; + if (*p == '=') + return aln; + + for (op = op1, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op1)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + if (*p == ',') p++; + for (op = op2, opsize=1; *p && *p != ','; p++) + { + if (!isspace(*p) && opsize < sizeof(op2)) + *op++ = tolower(*p), opsize++; + } + *op = '\0'; + + aln->size = instructionSize(inst, op1, op2); + + aln->regsRead = newBitVect (END_IDX); + aln->regsWritten = newBitVect (END_IDX); + + opdat = bsearch (inst, mcs51opcodeDataTable, + sizeof(mcs51opcodeDataTable)/sizeof(mcs51opcodedata), + sizeof(mcs51opcodedata), mcs51opcodeCompare); + + if (opdat) + { + updateOpRW (aln, op1, opdat->op1type); + updateOpRW (aln, op2, opdat->op2type); + if (strchr(opdat->pswtype,'r')) + aln->regsRead = bitVectSetBit (aln->regsRead, CND_IDX); + if (strchr(opdat->pswtype,'w')) + aln->regsWritten = bitVectSetBit (aln->regsWritten, CND_IDX); + } + + return aln; +} + +static int +getInstructionSize (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->size; +} + +static bitVect * +getRegsRead (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->regsRead; +} + +static bitVect * +getRegsWritten (lineNode *line) +{ + if (!line->aln) + line->aln = (asmLineNodeBase *) asmLineNodeFromLineNode (line); + + return line->aln->regsWritten; +} + +static const char * models[] = +{ + "small", "small-xstack", "small-stack-auto", "small-xstack-auto", + "medium", "medium-xstack", "medium-stack-auto", "medium-xstack-auto", + "large", "large-xstack", "large-stack-auto", "large-xstack-auto", + "huge", "huge-xstack", "huge-stack-auto", "huge-xstack-auto", +}; + +static const char * +get_model (void) +{ + int index; + + switch (options.model) + { + case MODEL_SMALL: + index = 0; + break; + case MODEL_MEDIUM: + index = 4; + break; + case MODEL_LARGE: + index = 8; + break; + case MODEL_HUGE: + index = 12; + break; + default: + werror (W_UNKNOWN_MODEL, __FILE__, __LINE__); + return "unknown"; + } + if (options.stackAuto) + index += 2; + if (options.useXstack) + index += 1; + return models[index]; +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdld", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_asmCmd[] = +{ + "sdas8051", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char * const _libs[] = { "mcs51", STD_LIB, STD_INT_LIB, STD_LONG_LIB, STD_FP_LIB, NULL, }; + +/* Globals */ +PORT mcs51_port = +{ + TARGET_ID_MCS51, + "mcs51", + "MCU 8051", /* Target name */ + NULL, /* Processor name */ + { + glue, + TRUE, /* glue_up_main: Emit glue around main */ + MODEL_SMALL | MODEL_MEDIUM | MODEL_LARGE | MODEL_HUGE, + MODEL_SMALL, + get_model, + }, + { /* Assembler */ + _asmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _linkCmd, + NULL, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs, /* libs */ + }, + { /* Peephole optimizer */ + _defaultRules, + getInstructionSize, + getRegsRead, + getRegsWritten, + mcs51DeadMove, + NULL, + NULL, + NULL, + NULL, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 1, 2, 3, 2, 3, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSTK (PAG,XDATA)", // xstack_name + "STACK (DATA)", // istack_name + "CSEG (CODE)", // code_name + "DSEG (DATA)", // data_name + "ISEG (DATA)", // idata_name + "PSEG (PAG,XDATA)", // pdata_name + "XSEG (XDATA)", // xdata_name + "BSEG (BIT)", // bit_name + "RSEG (ABS,DATA)", // reg_name + "GSINIT (CODE)", // static_name + "OSEG (OVR,DATA)", // overlay_name + "GSFINAL (CODE)", // post_static_name + "HOME (CODE)", // home_name + "XISEG (XDATA)", // xidata_name - initialized xdata + "XINIT (CODE)", // xinit_name - a code copy of xiseg + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata/pdata + "IABS (ABS,DATA)", // iabs_name - absolute idata/data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, + 1 // No fancy alignments supported. + }, + { _mcs51_genExtraAreas, NULL }, + { + +1, /* direction (+1 = stack grows up) */ + 0, /* bank_overhead (switch between register banks) */ + 4, /* isr_overhead */ + 1, /* call_overhead (2 for return address - 1 for pre-incrementing push */ + 1, /* reent_overhead */ + 1, /* banked_overhead (switch between code banks) */ + 0 /* sp points directly at last item pushed */ + }, + { -1, FALSE }, + { mcs51_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 2, /* sizeofElement */ + {6,9,15}, /* sizeofMatchJump[] */ + {9,18,36}, /* sizeofRangeCompare[] */ + 4, /* sizeofSubtract */ + 6, /* sizeofDispatch */ + }, + "_", + _mcs51_init, + _mcs51_parseOptions, + _mcs51_options, + NULL, + _mcs51_finaliseOptions, + _mcs51_setDefaultOptions, + mcs51_assignRegisters, + _mcs51_getRegName, + 0, + _mcs51_rtrackUpdate, + _mcs51_keywords, + _mcs51_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _mcs51_genIVT, + _mcs51_genXINIT, + _mcs51_genInitStartup, + _mcs51_reset_regparm, + _mcs51_regparm, + NULL, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, /* use_dw_for_init */ + TRUE, /* little_endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + cseCostEstimation, + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; diff --git a/src/mcs51/main.h b/src/mcs51/main.h new file mode 100644 index 0000000..e34fc20 --- /dev/null +++ b/src/mcs51/main.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + main.c - mcs51 specific general header file + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Copyright (C) 2000, Michael Hope + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------*/ + +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +#include "SDCCgen.h" + +typedef struct asmLineNode + { +#ifdef UNNAMED_STRUCT_TAG + struct asmLineNodeBase; +#else + /* exactly the same members as of struct asmLineNodeBase from SDCCgen.h */ + int size; + bitVect *regsRead; + bitVect *regsWritten; +#endif + } +asmLineNode; + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + +#endif diff --git a/src/mcs51/mcs51.vcxproj b/src/mcs51/mcs51.vcxproj new file mode 100644 index 0000000..fa9833a --- /dev/null +++ b/src/mcs51/mcs51.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9FACDB81-BE66-42D0-95F5-EA2FA3B09065} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/mcs51.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/mcs51.bsc + + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/mcs51.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/mcs51.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + {2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1} + false + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + + \ No newline at end of file diff --git a/src/mcs51/mcs51.vcxproj.filters b/src/mcs51/mcs51.vcxproj.filters new file mode 100644 index 0000000..aa1f754 --- /dev/null +++ b/src/mcs51/mcs51.vcxproj.filters @@ -0,0 +1,55 @@ + + + + + {bf0fcc72-c97b-453e-9ddb-0df03c205510} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {11a5d289-5894-4486-bee0-4d6dce084246} + h;hpp;hxx;hm;inl + + + {fa0a3371-90ba-4525-8964-2e8712494a31} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/mcs51/peep.c b/src/mcs51/peep.c new file mode 100644 index 0000000..0a8db59 --- /dev/null +++ b/src/mcs51/peep.c @@ -0,0 +1,773 @@ +/*------------------------------------------------------------------------- + peep.c - source file for peephole optimizer helper functions + + Written By - Bernhard Held + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include "common.h" +#include "ralloc.h" +#include "gen.h" + +#define D(x) x +#define DEADMOVEERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in deadmove");} while(0) + +typedef enum +{ + S4O_FOUNDOPCODE, + S4O_PUSHPOP, + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = FALSE; +} + +/*-----------------------------------------------------------------*/ +/* cleanLabelRef - clear label jump-counter and pass-flag */ +/*-----------------------------------------------------------------*/ +static void +cleanLabelRef (void) +{ + int key; + labelHashEntry *entry; + + if (!labelHash) + return; + for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key); + entry; + entry = (labelHashEntry *) hTabNextItem (labelHash, &key)) + { + entry->passedLabel = FALSE; + entry->jmpToCount = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* checkLabelRef - check all entries in labelHash */ +/* The path from 'pop' to 'push' must be the only possible path. */ +/* There must not be any paths in or out of this path. */ +/* This is checked by counting the label references. */ +/*-----------------------------------------------------------------*/ +static bool +checkLabelRef (void) +{ + int key; + labelHashEntry *entry; + + if (!labelHash) + { + /* no labels at all: no problems ;-) */ + return TRUE; + } + + for (entry = (labelHashEntry *) hTabFirstItem (labelHash, &key); + entry; + entry = (labelHashEntry *) hTabNextItem (labelHash, &key)) + { + + /* In our path we passed a label, + but we didn't meet all references (jumps) to this label. + This means that the code jumps from outside into this path. */ + if (entry->passedLabel && + entry->jmpToCount != entry->refCount) + { + return FALSE; + } + + /* In our path we jumped to (referenced) a label, + but we we didn't pass it. + This means that there's a code path into our path. */ + if (!entry->passedLabel && + entry->jmpToCount != 0) + { + return FALSE; + } + } + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* setLabelRefPassedLabel - set flag "passedLabel" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +setLabelRefPassedLabel (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->passedLabel = TRUE; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each mcs51 jumping opcode the label is at the end of the opcode */ + p = strlen (pl->line) - 1 + pl->line; + + /* scan backward until ',' or '\t' */ + for (; p > pl->line; p--) + if (*p == ',' || *p == '\t') + break; + + /* sanity check */ + if (p == pl->line) + { + DEADMOVEERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if (cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* isFunc - returns TRUE if it's a CALL or PCALL (not _gptrget()) */ +/*-----------------------------------------------------------------*/ +static bool +isFunc (const lineNode *pl) +{ + if (pl && pl->ic) + { + if ( pl->ic->op == CALL + || pl->ic->op == PCALL) + return TRUE; + } + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* termScanAtFunc - returns S4O_TERM if it's a 'normal' function */ +/* call and it's a 'caller save'. returns S4O_CONTINUE if it's */ +/* 'callee save' or 'naked'. returns S4O_ABORT if it's 'banked' */ +/* and uses the register for the destination. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +termScanAtFunc (const lineNode *pl, int rIdx) +{ + sym_link *ftype; + bool banked_reg = (rIdx == R0_IDX) || (rIdx == R1_IDX) || (rIdx == R2_IDX); + + if (!isFunc (pl)) + return S4O_CONTINUE; + // let's assume calls to literally given locations use the default + // most notably : (*(void (*)()) 0) (); see bug 1749275 + if (IS_VALOP (IC_LEFT (pl->ic))) + return (options.model == MODEL_HUGE) && banked_reg ? S4O_ABORT : options.all_callee_saves ? S4O_CONTINUE : S4O_TERM; + ftype = OP_SYM_TYPE(IC_LEFT(pl->ic)); + if (IS_FUNCPTR (ftype)) + ftype = ftype->next; + if (IFFUNC_ISBANKEDCALL(ftype) && banked_reg) + return S4O_ABORT; + if (FUNC_ARGS (ftype) && getSize (FUNC_ARGS (ftype)->type) > 4) + return S4O_ABORT; + if (FUNC_CALLEESAVES(ftype)) + return S4O_CONTINUE; + if (FUNC_ISNAKED(ftype)) + return S4O_CONTINUE; + return S4O_TERM; +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or an opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_PUSHPOP */ +/* hit a "push" or "pop" opcode */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *pReg, const char *untilOp, + lineNode **plCond) +{ + char *p; + int len; + bool isConditionalJump; + int rIdx; + S4O_RET ret; + bool findPushPop; + + findPushPop = untilOp && (strcmp (untilOp, "push") == 0 || strcmp (untilOp, "pop") == 0); + + /* pReg points to e.g. "ar0"..."ar7" */ + len = strlen (pReg); + + /* get index into pReg table */ + for (rIdx = 0; rIdx < mcs51_nRegs; ++rIdx) + if (strcmp (regs8051[rIdx].name, pReg + 1) == 0) + break; + + /* sanity check */ + if (rIdx >= mcs51_nRegs) + { + DEADMOVEERROR(); + return S4O_ABORT; + } + + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment) + continue; + + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + return S4O_ABORT; + + if ((*pl)->visited) + return S4O_VISITED; + (*pl)->visited = TRUE; + + /* found untilOp? */ + if (untilOp && strncmp ((*pl)->line, untilOp, strlen (untilOp)) == 0) + { + p = (*pl)->line + strlen (untilOp); + if (*p == '\t' && strncmp (p + 1, pReg, len) == 0) + return S4O_FOUNDOPCODE; + else + { + /* found untilOp but without our pReg */ + return S4O_ABORT; + } + } + + /* found pReg? */ + p = strchr ((*pl)->line, '\t'); + if (p) + { + /* skip '\t' */ + p++; + + /* when looking for push or pop and we find a direct access of sp: abort */ + if (findPushPop && strstr (p, "sp")) + return S4O_ABORT; + + /* course search */ + if (strstr (p, pReg + 1)) + { + /* ok, let's have a closer look */ + + /* does opcode read from pReg? */ + if (bitVectBitValue (port->peep.getRegsRead ((*pl)), rIdx)) + return S4O_RD_OP; + /* does opcode write to pReg? */ + if (bitVectBitValue (port->peep.getRegsWritten ((*pl)), rIdx)) + return S4O_WR_OP; + + /* we can get here, if the register name is + part of a variable name: ignore it */ + } + } + + /* found label? */ + if ((*pl)->isLabel) + { + const char *start; + char label[SDCC_NAME_MAX + 1]; + int len; + + if (!isLabelDefinition ((*pl)->line, &start, &len, FALSE)) + return S4O_ABORT; + memcpy (label, start, len); + label[len] = '\0'; + /* register passing this label */ + if (!setLabelRefPassedLabel (label)) + { + DEADMOVEERROR(); + return S4O_ABORT; + } + continue; + } + + /* branch or terminate? */ + isConditionalJump = FALSE; + switch ((*pl)->line[0]) + { + case 'a': + if (strncmp ("acall", (*pl)->line, 5) == 0) + { + /* for comments see 'lcall' */ + ret = termScanAtFunc (*pl, rIdx); + if (ret != S4O_CONTINUE) + return ret; + break; + } + if (strncmp ("ajmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + case 'c': + if (strncmp ("cjne", (*pl)->line, 4) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'd': + if (strncmp ("djnz", (*pl)->line, 4) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'j': + if (strncmp ("jmp", (*pl)->line, 3) == 0) + /* "jmp @a+dptr": no chance to trace execution */ + return S4O_ABORT; + if (strncmp ("jc", (*pl)->line, 2) == 0 || + strncmp ("jnc", (*pl)->line, 3) == 0 || + strncmp ("jz", (*pl)->line, 2) == 0 || + strncmp ("jnz", (*pl)->line, 3) == 0) + { + isConditionalJump = TRUE; + break; + } + if (strncmp ("jbc", (*pl)->line, 3) == 0 || + strncmp ("jb", (*pl)->line, 2) == 0 || + strncmp ("jnb", (*pl)->line, 3) == 0) + { + isConditionalJump = TRUE; + break; + } + break; + case 'l': + if (strncmp ("lcall", (*pl)->line, 5) == 0) + { + const char *p = (*pl)->line+5; + while (*p == ' ' || *p == '\t') + p++; + while (isdigit (*p)) + p++; + if (isdigit(p[-1]) && *p == '$') /* at least one digit */ + { + /* this is a temp label for a pcall */ + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + break; + } + + ret = termScanAtFunc (*pl, rIdx); + /* If it's a 'normal' 'caller save' function call, all + registers have been saved until the 'lcall'. The + 'life range' of all registers end at the lcall, + and we can terminate our search. + * If the function is 'banked', the registers r0, r1 and r2 + are used to tell the trampoline the destination. After + that their 'life range' ends just like the other registers. + * If it's a 'callee save' function call, registers are saved + by the callee. We've got no information, if the register + might live beyond the lcall. Therefore we've to continue + the search. + */ + if (ret != S4O_CONTINUE) + return ret; + break; + } + if (strncmp ("ljmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + case 'p': + if (strncmp ("pop", (*pl)->line, 3) == 0 || + strncmp ("push", (*pl)->line, 4) == 0) + return S4O_PUSHPOP; + break; + case 'r': + if (strncmp ("reti", (*pl)->line, 4) == 0) + return S4O_TERM; + + if (strncmp ("ret", (*pl)->line, 3) == 0) + { + /* pcall uses 'ret' */ + if (isFunc (*pl)) + { + /* for comments see 'lcall' */ + ret = termScanAtFunc (*pl, rIdx); + if (ret != S4O_CONTINUE) + return ret; + break; + } + + /* it's a normal function return */ + if (!((*pl)->ic)) + return S4O_ABORT; /* but no ic? */ + if (!currFunc->type) + return S4O_ABORT; /* not a function? */ + if (FUNC_CALLEESAVES (currFunc->type)) + return S4O_ABORT; /* returning from callee saves function */ + if (getSize(currFunc->etype) > 4) + { + for (unsigned i = 0; i < getSize(currFunc->etype); i++) + if (strstr (pReg, fReturn8051[i])) + return S4O_ABORT; /* return value is partially in r4-r7 */ + } + return S4O_TERM; + } + break; + case 's': + if (strncmp ("sjmp", (*pl)->line, 4) == 0) + { + *pl = findLabel (*pl); + if (!*pl) + return S4O_ABORT; + } + break; + default: + break; + } /* switch ((*pl)->line[0]) */ + + if (isConditionalJump) + { + *plCond = findLabel (*pl); + if (!*plCond) + return S4O_ABORT; + return S4O_CONDJMP; + } + } /* for (; *pl; *pl = (*pl)->next) */ + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doPushScan - scan through area 1. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doPushScan (lineNode **pl, const char *pReg) +{ + lineNode *plConditional, *pushPl = NULL; + + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, pReg, "push", &plConditional)) + { + case S4O_FOUNDOPCODE: + /* this is what we're looking for */ + return TRUE; + case S4O_VISITED: +#if 0 + if (!pushPl) + { + DEADMOVEERROR(); + return FALSE; + } + *pl = pushPl; + /* already checked */ + return TRUE; +#else + return FALSE; +#endif + case S4O_CONDJMP: +#if 0 + /* two possible destinations: recurse */ + { + lineNode *pushPl2 = plConditional; + + if (!doPushScan (&pushPl2, pReg)) + return FALSE; + pushPl = pushPl2; + } + continue; +#else + /* two possible destinations: give up */ + return FALSE; +#endif + default: + return FALSE; + } + } +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *pReg) +{ + lineNode *plConditional; + + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, pReg, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return TRUE; + case S4O_PUSHPOP: + /* don't care, go on */ + continue; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + + if (!doTermScan (&pl2, pReg)) + return FALSE; + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return FALSE; + } + } +} + +/*-----------------------------------------------------------------*/ +/* removeDeadPopPush - remove pop/push pair if possible */ +/*-----------------------------------------------------------------*/ +static bool +removeDeadPopPush (const char *pReg, lineNode *currPl, lineNode *head) +{ + lineNode *pushPl, *pl; + + /* A pop/push pair can be removed, if these criteria are met + (ar0 is just an example here, ar0...ar7 are possible): + + pop ar0 + + ; area 1 + + ; There must not be in area 1: + ; - read or write access of ar0 + ; - "acall", "lcall", "pop", "ret", "reti" or "jmp @a+dptr" opcodes + ; - "push" opcode, which doesn't push ar0 + ; - inline assembly + ; - a jump in or out of area 1 (see checkLabelRef()) + + ; area 1 must be terminated by a: + push ar0 + + ; area 2 + + ; There must not be: + ; - read access of ar0 + ; - "jmp @a+dptr" opcode + ; - inline assembly + ; - a jump in or out of area 2 (see checkLabelRef()) + + ; An "acall", "lcall" (not callee save), "ret" (not PCALL with + ; callee save), "reti" or write access of r0 terminate + ; the search, and the "pop/push ar0" can safely be removed. + */ + + /* area 1 */ + pushPl = currPl->next; + if (!doPushScan (&pushPl, pReg)) + return FALSE; + + if (!checkLabelRef()) + return FALSE; + + /* area 2 */ + pl = pushPl->next; + if (!doTermScan (&pl, pReg)) + return FALSE; + if (!checkLabelRef()) + return FALSE; + + /* Success! */ + if (options.noPeepComments) + { + /* remove pushPl from list */ + pushPl->prev->next = pushPl->next; + pushPl->next->prev = pushPl->prev; + } + else + { + /* replace 'push ar0' by comment */ + #define STR ";\tPeephole\tpush %s removed" + int size = sizeof(STR) + 2; + + pushPl->line = Safe_alloc (size); + SNPRINTF (pushPl->line, size, STR, pReg); + pushPl->isComment = TRUE; + } + + /* 'pop ar0' will be removed by peephole framework after returning TRUE */ + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* removeDeadMove - remove superflous 'mov r%1,%2' */ +/*-----------------------------------------------------------------*/ +static bool +removeDeadMove (const char *pReg, lineNode *currPl) +{ + lineNode *pl; + + /* "mov r0,a" can be removed, if these criteria are met + (r0 is just an example here, r0...r7 are possible): + + ; There must not be: + ; - read access of r0 + ; - "jmp @a+dptr" opcode + ; - inline assembly + ; - a jump in or out of this area (see checkLabelRef()) + + ; An "acall", "lcall" (not callee save), "ret" (not PCALL with + ; callee save), "reti" or write access of r0 terminate + ; the search, and the "mov r0,a" can safely be removed. + */ + pl = currPl->next; + if (!doTermScan (&pl, pReg)) + return FALSE; + + if (!checkLabelRef()) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* mcs51DeadMove - dispatch condition deadmove between */ +/* - remove pop/push */ +/* - remove mov r%1,%2 */ +/*-----------------------------------------------------------------*/ +bool +mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head) +{ + char pReg[5] = "ar"; + + _G.head = head; + strcat (pReg, reg); + + unvisitLines (_G.head); + cleanLabelRef(); + + if (strncmp (currPl->line, "pop", 3) == 0) + return removeDeadPopPush (pReg, currPl, head); + else if ( strncmp (currPl->line, "mov", 3) == 0 + && (currPl->line[3] == ' ' || currPl->line[3] == '\t')) + return removeDeadMove (pReg, currPl); + else + { + fprintf (stderr, "Error: " + "peephole rule with condition deadMove " + "used with unknown opocde:\n" + "\t%s\n", currPl->line); + return FALSE; + } +} diff --git a/src/mcs51/peep.h b/src/mcs51/peep.h new file mode 100644 index 0000000..fec1dd8 --- /dev/null +++ b/src/mcs51/peep.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + peep.h - header file for peephole optimizer helper functions + + Written By - Bernhard Held + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +bool mcs51DeadMove (const char *reg, lineNode *currPl, lineNode *head); diff --git a/src/mcs51/peeph.def b/src/mcs51/peeph.def new file mode 100644 index 0000000..67d46d0 --- /dev/null +++ b/src/mcs51/peeph.def @@ -0,0 +1,5092 @@ +// added by Jean Louis VERN for +// his shift stuff +replace { + xch a,%1 + xch a,%1 +} by { + ; Peephole 2.a removed redundant xch xch +} + +replace { +// saving 2 byte + mov %1,#0x00 + mov a,#0x00 +} by { + ; Peephole 3.a changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte + mov %1,#0x00 + clr a +} by { + ; Peephole 3.b changed mov to clr + clr a + mov %1,a +} + +replace restart { +// saving 1 byte, loosing 1 cycle but maybe allowing peephole 3.b to start + mov %1,#0x00 + mov %2,#0x00 + mov a,%3 +} by { + ; Peephole 3.c changed mov to clr + clr a + mov %1,a + mov %2,a + mov a,%3 +} + +replace { + clr a + mov %1,a + mov %2,a + clr a +} by { + clr a + mov %1,a + mov %2,a + ; Peephole 3.d removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + ; Peephole 3.e removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + ; Peephole 3.f removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + clr a +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + ; Peephole 3.g removed redundant clr +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,#0x00 +} by { + clr a + mov %1,a + mov %2,a + ; Peephole 3.h changed mov %3,#0x00 to ...,a + mov %3,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + ; Peephole 3.i changed mov %4,#0x00 to ...,a + mov %4,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + ; Peephole 3.j changed mov %5,#0x00 to ...,a + mov %5,a +} + +replace { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + mov %6,#0x00 +} by { + clr a + mov %1,a + mov %2,a + mov %3,a + mov %4,a + mov %5,a + ; Peephole 3.k changed mov %6,#0x00 to ...,a + mov %6,a +} + +replace { + mov %1,a + mov dptr,#%2 + mov a,%1 + movx @dptr,a +} by { + mov %1,a + mov dptr,#%2 + ; Peephole 100 removed redundant mov + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. lib/src/time.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.a removed redundant moves + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. support/regression/tests/literalop.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.b removed redundant moves + movx @dptr,a + inc dptr + movx @dptr,a +} if notVolatile %1 + +// applies to f.e. support/regression/tests/onebyte.c (--model-large) +replace { + mov a,%1 + movx @dptr,a + inc dptr + mov a,%1 + movx @dptr,a +} by { + mov a,%1 + movx @dptr,a + inc dptr + ; Peephole 101.c removed redundant mov + movx @dptr,a +} if notVolatile %1 + +replace { + mov %1,%2 + ljmp %3 +%4: + mov %1,%5 +%3: + mov dpl,%1 +%7: + mov sp,_bp + pop _bp +} by { + ; Peephole 102 removed redundant mov to %1 + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,_bp + pop _bp +} if notVolatile(%1), labelRefCount(%3 1) + +replace { + mov %1,%2 + ljmp %3 +%4: + mov a%1,%5 +%3: + mov dpl,%1 +%7: + mov sp,_bp + pop _bp +} by { + ; Peephole 103 removed redundant mov to %1 + mov dpl,%2 + ljmp %3 +%4: + mov dpl,%5 +%3: +%7: + mov sp,_bp + pop _bp +} if labelRefCount(%3 1) + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// clr c +// add a,#0x01 +// mov r%1,a +//} by { +// ; Peephole 104 optimized increment (acc not set to r%1, flags undefined) +// mov r%1,_bp +// inc r%1 +//} + +replace { + mov %1,a + mov a,%1 +} by { + mov %1,a + ; Peephole 105.a removed redundant mov +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr +} by { + mov dptr,#%1 + movx @dptr,a + ; Peephole 105.b removed redundant movx +} if notVolatile %1 + + +replace { + mov %1,a + clr c + mov a,%1 +} by { + mov %1,a + clr c + ; Peephole 106 removed redundant mov +} if notVolatile %1 + +replace { + ljmp %1 +%1: +} by { + ; Peephole 107 removed redundant ljmp +%1: +} if labelRefCountChange(%1 -1) + +replace { + jc %1 + ljmp %5 +%1: +} by { + ; Peephole 108.a removed ljmp by inverse jump logic + jnc %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jz %1 + ljmp %5 +%1: +} by { + ; Peephole 108.b removed ljmp by inverse jump logic + jnz %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jnz %1 + ljmp %5 +%1: +} by { + ; Peephole 108.c removed ljmp by inverse jump logic + jz %5 +%1: +} if labelInRange(%5), labelRefCountChange(%1 -1) + +replace { + jb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 108.d removed ljmp by inverse jump logic + jnb %1,%5 +%2: +} if labelInRange(%5), labelRefCountChange(%2 -1) + +replace { + jnb %1,%2 + ljmp %5 +%2: +} by { + ; Peephole 108.e removed ljmp by inverse jump logic + jb %1,%5 +%2: +} if labelInRange(%5), labelRefCountChange(%2 -1) + +replace { + ljmp %5 +%1: +} by { + ; Peephole 112.b changed ljmp to sjmp + sjmp %5 +%1: +} if labelInRange(%5) + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113.a optimized misc sequence + clr %4 + cjne %1,%2,%3 + setb %4 +%3: +} if labelRefCount %3 1 + +replace { + clr a + cjne %1,%2,%3 + cjne %10,%11,%3 + cpl a +%3: + rrc a + mov %4,c +} by { + ; Peephole 113.b optimized misc sequence + clr %4 + cjne %1,%2,%3 + cjne %10,%11,%3 + setb %4 +%3: +} if labelRefCount %3 2 + +// parameter passing for model-large and model-huge +replace { + mov r%2,dph + mov a,dpl + mov dptr,#%1 + movx @dptr,a + inc dptr + mov a,r%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + mov r%2,a + inc dptr + movx a,@dptr +} by { + mov r%2,dph + mov a,dpl + mov dptr,#%1 + movx @dptr,a + inc dptr + ; Peephole 114.a optimized 16-bit parameter passing + xch a,r%2 + movx @dptr,a +} if notVolatile(%1) + +replace { + mov r%2,dpl + mov r%3,dph + mov r%4,b + mov r%5,a + mov dptr,#%1 + mov a,r%2 + movx @dptr,a + inc dptr + mov a,r%3 + movx @dptr,a + inc dptr + mov a,r%4 + movx @dptr,a + inc dptr + mov a,r%5 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + mov r%2,a + inc dptr + movx a,@dptr + mov r%3,a + inc dptr + movx a,@dptr + mov r%4,a + inc dptr + movx a,@dptr + mov r%5,a +} by { + mov r%2,dpl + mov r%3,dph + mov r%4,b + mov r%5,a + mov dptr,#%1 + mov a,r%2 + movx @dptr,a + inc dptr + mov a,r%3 + movx @dptr,a + inc dptr + mov a,r%4 + movx @dptr,a + inc dptr + mov a,r%5 + movx @dptr,a +; Peephole 114.b optimized 32-bit parameter passing +} if notVolatile(%1) + + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.a jump optimization (acc not set) + cjne %1,%2,%3 + sjmp %4 +%3: +} if labelRefCount %3 1 + +replace { + mov %1,a + cjne %1,#0x00,%2 + sjmp %3 +%2: +} by { + mov %1,a + ; Peephole 115.b jump optimization + jz %3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.c jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.d jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jnz %4 +} by { + ; Peephole 115.e jump optimization (acc not set) + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.f jump optimization (acc not set) + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.g jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 +%3: +} if labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%4 1) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.h jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 +%3: +} if labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%4 2) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jnz %4 +} by { + ; Peephole 115.i jump optimization (acc not set) + cjne %1,%2,%4 + cjne %10,%11,%4 + cjne %12,%13,%4 + cjne %14,%15,%4 +%3: +} if labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%4 3) + +replace { + mov a,#0x01 + cjne %1,%2,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.j jump optimization (acc not set) + cjne %1,%2,%3 + sjmp %4 +%3: +} if labelRefCount %3 1 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.k jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + sjmp %4 +%3: +} if labelRefCount %3 2 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.l jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + sjmp %4 +%3: +} if labelRefCount %3 3 + +replace { + mov a,#0x01 + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + clr a +%3: + jz %4 +} by { + ; Peephole 115.m jump optimization (acc not set) + cjne %1,%2,%3 + cjne %10,%11,%3 + cjne %12,%13,%3 + cjne %14,%15,%3 + sjmp %4 +%3: +} if labelRefCount %3 4 + +replace { + push psw + mov psw,%1 + push _bp + mov _bp,%2 +%3: + mov %2,_bp + pop _bp + pop psw + ret +} by { + ; Peephole 127 removed misc sequence + ret +} if labelRefCount %3 0 + +replace { + clr a + rlc a + jz %1 +} by { + ; Peephole 128 jump optimization + jnc %1 +} + +// applies to: bug-524691.c --model-large: while (uRight - uLeft > 1) +replace { + clr a + rlc a + jnz %0 +} by { + ; Peephole 129.a jump optimization + jc %0 +} + +// applies to: _fsdiv.c --xstack: if (mant1 < mant2) +replace { + clr a + rlc a + pop %1 + jnz %0 +} by { + ; Peephole 129.b optimized condition + pop %1 + jc %0 +} if notVolatile %1 + +// applies to: time.c --xstack: while((days += (LEAP_YEAR(year) ? 366 : 365)) <= epoch) +replace { + clr a + rlc a + pop %1 + pop %2 + jnz %0 +} by { + ; Peephole 129.c optimized condition + pop %1 + pop %2 + jc %0 +} if notVolatile %1 %2 + +// applies to: _memmove.c --xstack: if (((int)src < (int)dst) && ((((int)src)+acount) > (int)dst)) +replace { + clr a + rlc a + pop %1 + pop %2 + pop %3 + jnz %0 +} by { + ; Peephole 129.d optimized condition + pop %1 + pop %2 + pop %3 + jc %0 +} if notVolatile %1 %2 %3 + +replace { + mov r%1,@r%2 +} by { + ; Peephole 130 changed target address mode r%1 to ar%1 + mov ar%1,@r%2 +} + +replace { + mov a,%1 + subb a,#0x01 + mov %2,a + mov %1,%2 +} by { + ; Peephole 131 optimized decrement (not caring for c) + dec %1 + mov %2,%1 +} + +// ideally the optimizations of rules 132.x should be done in genCmpXX +replace { + clr c + mov a,#%1 + subb a,%2 + mov %3,c +} by { + ; Peephole 132.a optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + mov %3,c +} if operandsLiteral(%1) + +replace { + clr c + mov a,#%1 + subb a,%2 + jnc %5 +} by { + ; Peephole 132.b optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + jnc %5 +} if operandsLiteral(%1) + +replace { + clr c + mov a,#%1 + subb a,%2 + jc %5 +} by { + ; Peephole 132.c optimized genCmpGt by inverse logic (acc differs) + mov a,%2 + add a,#0xff - %1 + jc %5 +} if operandsLiteral(%1) + +// disabled. See bug1734654.c +//replace { +// clr c +// mov a,%1 +// subb a,#%2 +// mov %3,c +//} by { +// ; Peephole 132.d optimized genCmpGt by inverse logic +// mov a,#0x100 - %2 +// add a,%1 +// mov %3,c +//} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + +replace { + clr c + mov a,%1 + subb a,#%2 + jnc %5 +} by { + ; Peephole 132.e optimized genCmpLt by inverse logic (carry differs) + mov a,#0x100 - %2 + add a,%1 + jc %5 +} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + +replace { + clr c + mov a,%1 + subb a,#%2 + jc %5 +} by { + ; Peephole 132.f optimized genCmpLt by inverse logic (carry differs) + mov a,#0x100 - %2 + add a,%1 + jnc %5 +} if operandsNotRelated('0x00' %2), operandsLiteral(%2) + + +replace { + mov r%1,%2 + mov ar%3,@r%1 + inc r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + mov r%1,%2 + ; Peephole 133 removed redundant moves + inc @r%1 + mov ar%3,@r%1 +} if notVolatile + +replace { + mov r%1,%2 + mov ar%3,@r%1 + dec r%3 + mov r%4,%2 + mov @r%4,ar%3 +} by { + mov r%1,%2 + ; Peephole 134 removed redundant moves + dec @r%1 + mov ar%3,@r%1 +} if notVolatile + +replace { + mov r%1,a + mov a,r%2 + orl a,r%1 +} by { + mov r%1,a + ; Peephole 135 removed redundant mov + orl a,r%2 +} + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov a,%1 +} by { + mov %1,a + mov dpl,%2 + mov dph,%3 + ; Peephole 136 removed redundant mov +} if notVolatile %1 + +// WTF? Doesn't look sensible to me... +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jz %5 +//} by { +// ; Peephole 137 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%5 +//%4: +//} if labelRefCount %4 1 +// +//replace { +// mov b,#0x00 +// mov a,%1 +// cjne %2,%3,%4 +// mov b,#0x01 +//%4: +// mov a,b +// jnz %5 +//} by { +// ; Peephole 138 optimized misc jump sequence +// mov a,%1 +// cjne %2,%3,%4 +// sjmp %5 +//%4: +//} if labelRefCount %4 1 + +replace { + mov r%1,a + anl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.a removed redundant mov + anl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + orl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.b removed redundant mov + orl a,%2 + mov r%1,a +} + +replace { + mov r%1,a + xrl ar%1,%2 + mov a,r%1 +} by { + ; Peephole 139.c removed redundant mov + xrl a,%2 + mov r%1,a +} + +// applies to genlshOne +replace { + mov ar%1,@%2 + mov a,r%1 + add a,acc + mov r%1,a +} by { + ; Peephole 140 removed redundant mov + mov a,@%2 + add a,@%2 + mov r%1,a +} + +replace { + mov r%1,a + mov r%2,ar%1 + mov ar%1,@r%2 +} by { + ; Peephole 142 removed redundant mov + mov r%2,a + mov ar%1,@r%2 +} + +replace { + rlc a + mov acc.0,c +} by { + ; Peephole 143.a converted rlc to rl + rl a +} + +replace { + rrc a + mov acc.7,c +} by { + ; Peephole 143.b converted rrc to rc + rr a +} + +replace { + clr c + addc a,%1 +} by { + ; Peephole 145.a changed to add without carry + add a,%1 +} + +replace { + clr c + mov a,%1 + addc a,%2 +} by { + ; Peephole 145.b changed to add without carry + mov a,%1 + add a,%2 +} + +// 147: Fix compiler output to comply with 8051 instruction set. +replace { + orl r%1,a +} by { + ; Peephole 147.a changed target address mode r%1 to ar%1 + orl ar%1,a +} + +replace { + anl r%1,a +} by { + ; Peephole 147.b changed target address mode r%1 to ar%1 + anl ar%1,a +} + +replace { + xrl r%1,a +} by { + ; Peephole 147.c changed target address mode r%1 to ar%1 + xrl ar%1,a +} + +replace { + mov r%1,dpl + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.a removed misc moves via dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.b removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.c removed misc moves via dph, dpl before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 + mov dph,r%2 + mov b,r%3 +%9: + ret +} by { + ; Peephole 150.d removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.e removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.f removed misc moves via dph, dpl, b before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 + mov dph,r%2 + mov b,r%3 + mov a,r%4 +%9: + ret +} by { + ; Peephole 150.g removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 + mov dph,r%2 +%9: + ret +} by { + ; Peephole 150.h removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +replace { + mov r%1,dpl + mov r%2,dph + mov r%3,b + mov r%4,a + mov dpl,r%1 +%9: + ret +} by { + ; Peephole 150.i removed misc moves via dph, dpl, b, a before return +%9: + ret +} + +// peephole 213.a might revert this +replace { + mov %1,#%2 + xrl %1,#0x80 +} by { + ; Peephole 159 avoided xrl during execution + mov %1,#(%2 ^ 0x80) +} + +replace { + jnc %1 + sjmp %2 +%1: +} by { + ; Peephole 160.a removed sjmp by inverse jump logic + jc %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jc %1 + sjmp %2 +%1: +} by { + ; Peephole 160.b removed sjmp by inverse jump logic + jnc %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jnz %1 + sjmp %2 +%1: +} by { + ; Peephole 160.c removed sjmp by inverse jump logic + jz %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jz %1 + sjmp %2 +%1: +} by { + ; Peephole 160.d removed sjmp by inverse jump logic + jnz %2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jnb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 160.e removed sjmp by inverse jump logic + jb %3,%2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + jb %3,%1 + sjmp %2 +%1: +} by { + ; Peephole 160.f removed sjmp by inverse jump logic + jnb %3,%2 +%1: +} if labelRefCountChange(%1 -1) + +replace { + mov %1,%2 + mov %3,%1 + mov %2,%1 +} by { + mov %1,%2 + mov %3,%1 + ; Peephole 166 removed redundant mov +} if notVolatile %1 %2 + +replace { + mov c,%1 + cpl c + mov %1,c +} by { + ; Peephole 167 removed redundant bit moves (c not set to %1) + cpl %1 +} + +replace { + jnb %1,%2 + sjmp %3 +%2: +} by { + ; Peephole 168 jump optimization + jb %1,%3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + jb %1,%2 + sjmp %3 +%2: +} by { + ; Peephole 169 jump optimization + jnb %1,%3 +%2: +} if labelRefCountChange(%2 -1) + +replace { + clr a + cjne %1,%2,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 170 jump optimization + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 171 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 +%3: +} if labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%4 1) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 172 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 +%3: +} if labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%4 2) + +replace { + clr a + cjne %1,%2,%3 + cjne %9,%10,%3 + cjne %11,%12,%3 + cjne %13,%14,%3 + cpl a +%3: + jz %4 +} by { + ; Peephole 173 jump optimization + cjne %1,%2,%4 + cjne %9,%10,%4 + cjne %11,%12,%4 + cjne %13,%14,%4 +%3: +} if labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%4 3) + +replace { + mov r%1,%2 + clr c + mov a,r%1 + subb a,#0x01 + mov %2,a +} by { + mov r%1,%2 + ; Peephole 174.a optimized decrement (acc not set to %2, flags undefined) + dec %2 +} + +replace { + mov r%1,%2 + mov a,r%1 + add a,#0x01 + mov %2,a +} by { + mov r%1,%2 + ; Peephole 174.b optimized increment (acc not set to %2, flags undefined) + inc %2 +} + +replace { + mov %1,@r%2 + inc %1 + mov @r%2,%1 +} by { + ; Peephole 174.c optimized increment, removed redundant mov + inc @r%2 + mov %1,@r%2 +} if notVolatile + +// this one will screw assignes to volatile/sfr's +replace { + mov %1,%2 + mov %2,%1 +} by { + mov %1,%2 + ; Peephole 177.a removed redundant mov +} if notVolatile %1 %2 + +// applies to f.e. scott-add.asm (--model-large) +replace { + mov r%1,a + mov a,ar%1 +} by { + mov r%1,a + ; Peephole 177.b removed redundant mov +} + +// applies to f.e. bug-408972.c +replace { + mov %1,%2 + mov %1,%3 +} by { + ; Peephole 177.c removed redundant mov + mov %1,%3 +} if notVolatile(%1 %2),operandsNotRelated(%1 %3) + +// applies to f.e. bug-408972.c +// not before peephole 177.c +replace restart { + mov %1,%2 + mov %3,%4 + mov %2,%1 +} by { + mov %1,%2 + mov %3,%4 + ; Peephole 177.d removed redundant mov +} if notVolatile(%1 %2),operandsNotRelated(%1 %2 %3) + +// applies to f.e. bug-607243.c +replace { + mov %1,%2 + mov a%1,%3 +} by { + ; peephole 177.e removed redundant mov %1,%2 + mov a%1,%3 +} if notVolatile(%2), operandsNotRelated(%1 %3) + +replace { + mov ar%1,%2 + mov r%1,%3 +} by { + ; peephole 177.f removed redundant mov + mov r%1,%3 +} if notVolatile %2 + +replace { + mov %1,%2 + mov a,%1 +} by { + ; peephole 177.g optimized mov sequence + mov a,%2 + mov %1,a +} if notVolatile %1 + +replace { + mov %1,%2 + mov a,%2 +} by { + ; peephole 177.h optimized mov sequence + mov a,%2 + mov %1,a +} if notVolatile(%2), notSame(%1 'dptr'), operandsNotRelated(%1 %2) + +// applies to f.e. testfwk.c +replace { + mov r%1,a + mov ar%2,r%1 +} by { + mov r%1,a + ; peephole 177.i optimized mov sequence + mov r%2,a +} + +replace { + mov r%1,%2 + mov ar%3,r%1 + mov r%1,%4 +} by { + ; peephole 177.j optimized mov sequence + mov r%3,%2 + mov r%1,%4 +} + +replace { + mov a,%1 + mov b,a + mov a,%2 +} by { + ; Peephole 178 removed redundant mov + mov b,%1 + mov a,%2 +} + +// rules 179-182 provided by : Frieder +// saving 2 byte, 1 cycle +replace { + mov b,#0x00 + mov a,#0x00 +} by { + ; Peephole 179 changed mov to clr + clr a + mov b,a +} + +// applies to: +// volatile xdata char t; t=0x01; t=0x03; +replace { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov dptr,%1 +} by { + mov dptr,%1 + mov a,%2 + movx @dptr,a + ; Peephole 180.a removed redundant mov to dptr +} + +// volatile xdata char t; t=0x01; t=0x03; t=0x01; +replace { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov a,%3 + movx @dptr,a + mov dptr,%1 +} by { + mov dptr,%1 + mov a,%2 + movx @dptr,a + mov a,%3 + movx @dptr,a + ; Peephole 180.b removed redundant mov to dptr +} + +// saving 1 byte, 0 cycles +replace { + mov a,#0x00 +} by { + ; Peephole 181 changed mov to clr + clr a +} + +// saving 3 bytes, 2 cycles +// provided by Bernhard Held +replace { + mov dpl,#%1 + mov dph,#(%1 >> 8) +} by { + ; Peephole 182.a used 16 bit load of DPTR + mov dptr,#%1 +} + +// saving 3 byte, 2 cycles, return(NULL) profits here +replace { + mov dpl,#0x%1 + mov dph,#0x%2 +} by { + ; Peephole 182.b used 16 bit load of dptr + mov dptr,#0x%2%1 +} + +// saving 3 byte, 2 cycles. Probably obsoleted by 182.b +replace { + mov dpl,#%1 + mov dph,#%2 +} by { + ; Peephole 182.c used 16 bit load of dptr + mov dptr,#(((%2)<<8) + %1) +} + +// applies to return 0.0; in f.e. sincosf.c +replace { + mov dpl,#%1 + clr a + mov dph,a +} by { + ; Peephole 182.d used 16 bit load of dptr + mov dptr,#(%1&0x00ff) + clr a +} + +replace { + anl %1,#%2 + anl %1,#%3 +} by { + ; Peephole 183.a avoided anl during execution + anl %1,#(%2&%3) +} if notVolatile %1 + +replace { + orl %1,#%2 + orl %1,#%3 +} by { + ; Peephole 183.b avoided orl during execution + orl %1,#(%2|%3) +} if notVolatile %1 + +replace { + mov %1,a + cpl a + mov %1,a +} by { + ; Peephole 184 removed redundant mov + cpl a + mov %1,a +} if notVolatile %1 + +//replace { +// acc being incremented might cause problems with register tracking +// mov %1,a +// inc %1 +//} by { +// ; Peephole 185 changed order of increment (acc incremented also!) +// inc a +// mov %1,a +//} if notVolatile %1 + +// char indexed access to: long code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr + mov %4,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.a optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr + mov %4,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: void* code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr + mov %3,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.b optimized movc sequence + mov b,a + mov dptr,#%1 + movc a,@a+dptr + mov %2,a + inc dptr + mov a,b + movc a,@a+dptr + mov %3,a + inc dptr + mov a,b + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace restart { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr + mov %2,a + inc dptr + clr a + movc a,@a+dptr +} by { + ; Peephole 186.c optimized movc sequence + mov %2,a + mov dptr,#%1 + movc a,@a+dptr + xch a,%2 + inc dptr + movc a,@a+dptr +} + +// char indexed access to: char code table[] = {4,3,2,1}; +replace { + add a,#%1 + mov dpl,a + clr a + addc a,#(%1 >> 8) + mov dph,a + clr a + movc a,@a+dptr +} by { + ; Peephole 186.d optimized movc sequence + mov dptr,#%1 + movc a,@a+dptr +} + +// char indexed access to: int code table[] = {4,3,2,1}; +replace { + mov b,#0x02 + mul ab + add a,#%2 + mov dpl,a + mov a,#(%2 >> 8) + addc a,b + mov dph,a + clr a + movc a,@a+dptr + mov %3,a + mov a,#0x01 + movc a,@a+dptr +} by { + ; Peephole 186.e optimized movc sequence (b, dptr differ) + add a,acc + mov b,a + mov dptr,#%2 + jnc .+3 + inc dph + movc a,@a+dptr + mov %3,a + mov a,b + inc a + movc a,@a+dptr +} + +replace { + mov r%1,%2 + anl ar%1,#%3 + mov a,r%1 +} by { + ; Peephole 187 used a instead of ar%1 for anl + mov a,%2 + anl a,#%3 + mov r%1,a +} + +replace { + mov %1,a + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} by { + ; Peephole 188 removed redundant mov + mov dptr,%2 + movc a,@a+dptr + mov %1,a +} if notVolatile %1 + +replace { + anl a,#0x0f + mov %1,a + mov a,#0x0f + anl a,%1 +} by { + anl a,#0x0f + mov %1,a + ; Peephole 189 removed redundant mov and anl +} if notVolatile %1 + +// rules 190 & 191 need to be in order +replace { + mov a,%1 + lcall __gptrput + mov a,%1 +} by { + mov a,%1 + lcall __gptrput + ; Peephole 190 removed redundant mov +} if notVolatile %1 + +replace { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + mov %1,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + ; Peephole 191 removed redundant mov +} if notVolatile %1 + +// applies to f.e. regression/ports/mcs51/support.c +replace { + mov r%1,a + mov @r%2,ar%1 +} by { + mov r%1,a + ; Peephole 192.a used a instead of ar%1 as source + mov @r%2,a +} + +// applies to f.e. printf_large.c +replace { + mov ar%1,@r%2 + mov a,r%1 +} by { + ; Peephole 192.b used a instead of ar%1 as destination + mov a,@r%2 + mov r%1,a +} + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + jnz %3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + jnz %8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + mov a,%12 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + mov a,%12 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + inc %1 + cjne @%1,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 193.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + inc %1 + cjne @%1,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + cjne %13,%14,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 194 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + cjne %13,%14,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 4), labelRefCountChange(%3 -4), labelRefCountChange(%8 3) + +replace { + jnz %3 + mov a,%4 + jnz %3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.a optimized misc jump sequence + jnz %8 + mov a,%4 + jnz %8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + mov a,%9 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + mov a,%9 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + inc %1 + cjne @%1,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 195.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + inc %1 + cjne @%1,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + cjne %10,%11,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 196 optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + cjne %10,%11,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 3), labelRefCountChange(%3 -3), labelRefCountChange(%8 2) + +replace { + jnz %3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.a optimized misc jump sequence + jnz %8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + mov a,%4 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.b optimized misc jump sequence + cjne %1,%2,%8 + mov a,%4 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne @%1,%2,%3 + inc %1 + cjne @%1,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 197.c optimized misc jump sequence + cjne @%1,%2,%8 + inc %1 + cjne @%1,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + cjne %5,%6,%3 + sjmp %7 +%3: + sjmp %8 +} by { + ; Peephole 198.a optimized misc jump sequence + cjne %1,%2,%8 + cjne %5,%6,%8 + sjmp %7 +%3: +} if labelInRange(%8), labelRefCount(%3 2), labelRefCountChange(%3 -2), labelRefCountChange(%8 1) + +replace { + cjne %1,%2,%3 + sjmp %4 +%3: + sjmp %5 +} by { + ; Peephole 198.b optimized misc jump sequence + cjne %1,%2,%5 + sjmp %4 +%3: +} if labelInRange(%5), labelRefCount(%3 1), labelRefCountChange(%3 -1) + +replace { + sjmp %1 +%1: +} by { + ; Peephole 200.a removed redundant sjmp +%1: +} if labelRefCountChange(%1 -1) + +replace { + sjmp %1 +%2: +%1: +} by { + ; Peephole 200.b removed redundant sjmp +%2: +%1: +} if labelRefCountChange(%1 -1) + +replace { + push acc + mov dptr,%1 + pop acc +} by { + ; Peephole 202 removed redundant push pop + mov dptr,%1 +} + +replace { + mov r%1,_spx + lcall %2 + mov r%1,_spx +} by { + ; Peephole 203 removed mov r%1,_spx + lcall %2 +} + +replace { + mov %1,a + add a,acc + mov %1,a +} by { + ; Peephole 204 removed redundant mov + add a,acc + mov %1,a +} if notVolatile %1 + +replace { + djnz %1,%2 + sjmp %3 +%2: + sjmp %4 +%3: +} by { + ; Peephole 205 optimized misc jump sequence + djnz %1,%4 +%2: +%3: +} if labelRefCount(%2 1), labelRefCountChange(%2 -1), labelRefCountChange(%3 -1) + +replace { + mov %1,%1 +} by { + ; Peephole 206 removed redundant mov %1,%1 +} if notVolatile %1 + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x00 +// mov %1,a +//} by { +// ; Peephole 207 removed zero add (acc not set to %1, flags undefined) +// mov %1,_bp +//} + +replace { + push acc + mov r%1,_bp + pop acc +} by { + ; Peephole 208 removed redundant push pop + mov r%1,_bp +} + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x00 +// inc a +// mov %1,a +//} by { +// ; Peephole 209 optimized increment (acc not set to %1, flags undefined) +// mov %1,_bp +// inc %1 +//} + +replace { + mov dptr,#((((%1 >> 8)) <<8) + %1) +} by { + ; Peephole 210 simplified expression + mov dptr,#%1 +} + +replace { + push %1 + pop %1 +} by { + ; Peephole 211 removed redundant push %1 pop %1 +} + +// Does not seem to be triggered anymore +//replace { +// mov a,_bp +// add a,#0x01 +// mov r%1,a +//} by { +// ; Peephole 212 reduced add sequence to inc +// mov r%1,_bp +// inc r%1 +//} + +// reverts peephole 159? asx8051 cannot handle, too complex? +replace { + mov %1,#(( %2 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.a inserted fix + mov %1,#(%2 >> 8) + xrl %1,#0x80 +} + +replace { + mov %1,#(( %2 + %3 >> 8 ) ^ 0x80) +} by { + ; Peephole 213.b inserted fix + mov %1,#((%2 + %3) >> 8) + xrl %1,#0x80 +} + + +replace { + mov %1,a + mov a,%2 + add a,%1 +} by { + mov %1,a + ; Peephole 214.a removed redundant mov + add a,%2 +} if notSame(%1 %2) + +replace { + mov %1,a + add a,%2 + mov %1,a +} by { + ; Peephole 214.b removed redundant mov + add a,%2 + mov %1,a +} if notSame(%1 %2) + +replace { + mov r%1,%2 + clr a + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.a simplified clear (2 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.b simplified clear (3 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + mov r%1,%2 + clr a + inc r%1 + inc r%1 + inc r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a + dec r%1 + mov @r%1,a +} by { + mov r%1,%2 + clr a + ; Peephole 216.c simplified clear (4 bytes) + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a + inc r%1 + mov @r%1,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + clr a + movx @dptr,a +} by { + ; Peephole 219.a removed redundant clear + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a +} + +replace { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + clr a + movx @dptr,a +} by { + clr a + movx @dptr,a + mov dptr,%1 + movx @dptr,a + mov dptr,%2 + ; Peephole 219.b removed redundant clear + movx @dptr,a +} + +replace { + mov dps,#0x00 + mov dps,#0x01 +} by { + ; Peephole 220.a removed bogus DPS set + mov dps,#0x01 +} + +replace { + mov dps,#0x01 + mov dps,#0x00 +} by { + ; Peephole 220.b removed bogus DPS set + mov dps,#0x00 +} + +replace { + mov %1 + %2,(%2 + %1) +} by { + ; Peephole 221.a remove redundant mov +} if notVolatile + +replace { + mov (%1 + %2 + %3),((%2 + %1) + %3) +} by { + ; Peephole 221.b remove redundant mov +} if notVolatile + +replace { + dec r%1 + inc r%1 +} by { + ; Peephole 222 removed dec/inc pair +} + +replace { + mov %1,dpl + mov %2,dph + mov dpl,%1 + mov dph,%2 +} by { + mov %1,dpl + mov %2,dph + ; Peephole 223.a removed redundant dph/dpl moves +} if notVolatile %1 %2 + +replace { + mov %1,dpl + mov (%1 + 1),dph + mov dpl,%1 + mov dph,(%1 + 1) +} by { + mov %1,dpl + mov (%1 + 1),dph + ; Peephole 223.b removed redundant dph/dpl moves +} if notVolatile %1 + +replace { + mov a,%1 + movx @dptr,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + mov a,%1 +} by { + mov a,%1 + movx @dptr,a + mov dpl,%2 + mov dph,%3 + mov b,%4 + ; Peephole 225 removed redundant move to acc +} if notVolatile %1 + +replace { + clr a + movx @%1,a + inc %1 + clr a +} by { + clr a + movx @%1,a + inc %1 + ; Peephole 226.a removed unnecessary clr +} + +replace { + clr a + movx @%1,a + inc %1 + movx @%1,a + inc %1 + clr a +} by { + clr a + movx @%1,a + inc %1 + movx @%1,a + inc %1 + ; Peephole 226.b removed unnecessary clr +} + +replace { + mov dptr,#%1 + clr a + inc dptr + inc dptr + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a +} by { + mov dptr,#%1 + clr a + ; Peephole 227.a replaced inefficient 32 bit clear + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + inc dptr + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + movx @dptr,a + lcall __decdptr + mov a,#%2 + movx @dptr,a +} by { + mov dptr,#%1 + ; Peephole 227.b replaced inefficient 32 constant + mov a,#%2 + movx @dptr,a + inc dptr + clr a + movx @dptr,a + inc dptr + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + movx @dptr,a + lcall __decdptr + movx @dptr,a +} by { + mov dptr,#%1 + clr a + ; Peephole 227.c replaced inefficient 16 bit clear + movx @dptr,a + inc dptr + movx @dptr,a + mov dptr,#%1 +} + +replace { + mov dptr,#%1 + clr a + inc dptr + movx @dptr,a + lcall __decdptr + mov a,#%2 + movx @dptr,a +} by { + mov dptr,#%1 + ; Peephole 227.d replaced inefficient 16 bit constant + mov a,#%2 + movx @dptr,a + inc dptr + clr a + movx @dptr,a + mov dptr,#%1 +} + +// this last peephole often removes the last mov from 227.a - 227.d +replace { + mov dptr,#%1 + mov dptr,#%2 +} by { + ; Peephole 227.e removed redundant mov to dptr + mov dptr,#%2 +} + +replace { + movx a,@dptr +} by { + ; Peephole 232 using movc to read xdata (--xram-movc) + clr a + movc a,@a+dptr +} if xramMovcOption + +replace { + lcall _gptrget +} by { + ; Peephole 233 using _gptrgetc instead of _gptrget (--xram-movc) + lcall _gptrgetc +} if xramMovcOption + +replace { + mov r%1,a + mov dpl,r%1 +%2: + ret +} by { + ; Peephole 234.a loading dpl directly from a(ccumulator), r%1 not set + mov dpl,a +%2: + ret +} + +replace { + mov r%1,a + mov dpl,r%2 + mov dph,r%1 +%3: + ret +} by { + ; Peephole 234.b loading dph directly from a(ccumulator), r%1 not set + mov dpl,r%2 + mov dph,a +%3: + ret +} + +// 14 rules by Fiorenzo D. Ramaglia + +replace { + add a,ar%1 +} by { + ; Peephole 236.a used r%1 instead of ar%1 + add a,r%1 +} + +replace { + addc a,ar%1 +} by { + ; Peephole 236.b used r%1 instead of ar%1 + addc a,r%1 +} + +replace { + anl a,ar%1 +} by { + ; Peephole 236.c used r%1 instead of ar%1 + anl a,r%1 +} + +replace { + dec ar%1 +} by { + ; Peephole 236.d used r%1 instead of ar%1 + dec r%1 +} + +replace { + djnz ar%1,%2 +} by { + ; Peephole 236.e used r%1 instead of ar%1 + djnz r%1,%2 +} + +replace { + inc ar%1 +} by { + ; Peephole 236.f used r%1 instead of ar%1 + inc r%1 +} + +replace { + mov a,ar%1 +} by { + ; Peephole 236.g used r%1 instead of ar%1 + mov a,r%1 +} + +replace { + mov ar%1,#%2 +} by { + ; Peephole 236.h used r%1 instead of ar%1 + mov r%1,#%2 +} + +replace { + mov ar%1,a +} by { + ; Peephole 236.i used r%1 instead of ar%1 + mov r%1,a +} + +replace { + mov ar%1,ar%2 +} by { + ; Peephole 236.j used r%1 instead of ar%1 + mov r%1,ar%2 +} + +replace { + orl a,ar%1 +} by { + ; Peephole 236.k used r%1 instead of ar%1 + orl a,r%1 +} + +replace { + subb a,ar%1 +} by { + ; Peephole 236.l used r%1 instead of ar%1 + subb a,r%1 +} + +replace { + xch a,ar%1 +} by { + ; Peephole 236.m used r%1 instead of ar%1 + xch a,r%1 +} + +replace { + xrl a,ar%1 +} by { + ; Peephole 236.n used r%1 instead of ar%1 + xrl a,r%1 +} + +// obsoleted by 251.b +//replace { +// sjmp %1 +//%2: +// mov %3,%4 +//%1: +// ret +//} by { +// ; Peephole 237.a removed sjmp to ret +// ret +//%2: +// mov %3,%4 +//%1: +// ret +//} if labelRefCountChange(%1 -1) + +// obsoleted by 251.b +//replace { +// sjmp %1 +//%2: +// mov %3,%4 +// mov dpl,%5 +// mov dph,%6 +//%1: +// ret +//} by { +// ; Peephole 237.b removed sjmp to ret +// ret +//%2: +// mov %3,%4 +// mov dpl,%5 +// mov dph,%6 +//%1: +// ret +//} if labelRefCountChange(%1 -1) + +// applies to f.e. device/lib/log10f.c +replace { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + + mov %9,%1 + mov %10,%2 + mov %11,%3 + mov %12,%4 +} by { + mov %1,%9 + mov %2,%10 + mov %3,%11 + mov %4,%12 + + mov %5,%13 + mov %6,%14 + mov %7,%15 + mov %8,%16 + ; Peephole 238.a removed 4 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7 %8), notVolatile(%1 %2 %3 %4 %9 %10 %11 %12) + +// applies to device/lib/log10f.c +replace { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 + mov %7,%3 +} by { + mov %1,%5 + mov %2,%6 + mov %3,%7 + mov %4,%8 + ; Peephole 238.b removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6 %7), notVolatile(%1 %2 %3 %5 %6 %7) + +// applies to f.e. device/lib/time.c +replace { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + + mov %5,%1 + mov %6,%2 +} by { + mov %1,%5 + mov %2,%6 + + mov %3,%7 + mov %4,%8 + ; Peephole 238.c removed 2 redundant moves +} if notSame(%1 %2 %3 %4), notVolatile(%1 %2 %5 %6) + +// applies to f.e. support/regression/tests/bug-524209.c +replace { + mov %1,%4 + mov %2,%5 + mov %3,%6 + + mov %4,%1 + mov %5,%2 + mov %6,%3 +} by { + mov %1,%4 + mov %2,%5 + mov %3,%6 + ; Peephole 238.d removed 3 redundant moves +} if notSame(%1 %2 %3 %4 %5 %6), notVolatile(%1 %2 %3 %4 %5 %6) + +// applies to f.e. ser_ir.asm +replace { + mov r%1,acc +} by { + ; Peephole 239 used a instead of acc + mov r%1,a +} + +replace restart { + mov a,%1 + addc a,#0x00 +} by { + ; Peephole 240 use clr instead of addc a,#0 + clr a + addc a,%1 +} + +// peepholes 241.a0 to 241.d and 241.e0 to 241.h need to be in order +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + cjne r%10,#%11,%0 + cjne r%12,#%13,%0 + cjne r%14,#%15,%0 + cjne r%16,#%17,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.a0 optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + cjne r%10,#%11,%0 + cjne r%12,#%13,%0 + cjne r%14,#%15,%0 + cjne r%16,#%17,%0 + inc a +%0: +%1: +} if labelRefCount(%0 8), labelRefCountChange(%1 -1) + +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.a optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + cjne r%8,#%9,%0 + inc a +%0: +%1: +} if labelRefCount(%0 4), labelRefCountChange(%1 -1) + +// applies to generic pointer compare +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.b optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + cjne r%6,#%7,%0 + inc a +%0: +%1: +} if labelRefCount(%0 3), labelRefCountChange(%1 -1) + +// applies to f.e. time.c +replace { + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.c optimized compare + clr a + cjne r%2,#%3,%0 + cjne r%4,#%5,%0 + inc a +%0: +%1: +} if labelRefCount(%0 2), labelRefCountChange(%1 -1) + +// applies to f.e. malloc.c +replace { + cjne r%2,#%3,%0 + mov a,#0x01 + sjmp %1 +%0: + clr a +%1: +} by { + ; Peephole 241.d optimized compare + clr a + cjne r%2,#%3,%0 + inc a +%0: +%1: +} if labelRefCount(%0 1), labelRefCountChange(%1 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata long long k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc r%0 + cjne @r%0,#%7,%1 + inc r%0 + cjne @r%0,#%8,%1 + inc r%0 + cjne @r%0,#%9,%1 + inc r%0 + cjne @r%0,#%10,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.e0 optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc r%0 + cjne @r%0,#%7,%1 + inc r%0 + cjne @r%0,#%8,%1 + inc r%0 + cjne @r%0,#%9,%1 + inc r%0 + cjne @r%0,#%10,%1 + inc a +%1: +%2: +} if labelRefCount(%1 8), labelRefCountChange(%2 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata long k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.e optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc r%0 + cjne @r%0,#%6,%1 + inc a +%1: +%2: +} if labelRefCount(%1 4), labelRefCountChange(%2 -1) + +// applies to f.e. j = (p!=NULL); +// with volatile idata char *p; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.f optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc r%0 + cjne @r%0,#%5,%1 + inc a +%1: +%2: +} if labelRefCount(%1 3), labelRefCountChange(%2 -1) + +// applies to f.e. j = (k!=0x1000); +// with volatile idata int k; +replace { + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.g optimized compare + clr a + cjne @r%0,#%3,%1 + inc r%0 + cjne @r%0,#%4,%1 + inc a +%1: +%2: +} if labelRefCount(%1 2), labelRefCountChange(%2 -1) + +// applies to f.e. vprintf.asm (--stack-auto) +replace { + cjne @r%0,#%3,%1 + mov a,#0x01 + sjmp %2 +%1: + clr a +%2: +} by { + ; Peephole 241.h optimized compare + clr a + cjne @r%0,#%3,%1 + inc a +%1: +%2: +} if labelRefCount(%1 1), labelRefCountChange(%2 -1) + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + ; Peephole 242.a avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. scott-bool1.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + orl a,%5 + ; Peephole 242.b avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. logic.c +replace { + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 +%1: + jz %4 +} by { + jnz %1 + mov %2,%3 + orl a,%5 + orl a,%6 + orl a,%7 + ; Peephole 242.c avoided branch jnz to jz + jz %4 +%1: +} if labelRefCount %1 1 + +// applies to f.e. vprintf.c +// this is a rare case, usually the "tail increment" is noticed earlier +replace { + cjne %1,%2,%3 + inc %4 +%3: + sjmp %5 +} by { + ; Peephole 243 avoided branch to sjmp + cjne %1,%2,%5 + inc %4 +%3: + sjmp %5 +} if labelInRange(%5), labelRefCountChange(%3 -1), labelRefCountChange(%5 1) + +// applies to f.e. simplefloat.c (saving 1 cycle) +replace { + mov r%1,dpl + mov a,r%1 +} by { + ; Peephole 244.a moving first to a instead of r%1 + mov a,dpl + mov r%1,a +} + +// applies to f.e. _itoa.c (saving 1 cycle) +replace { + mov r%1,dph + mov a,r%1 +} by { + ; Peephole 244.b moving first to a instead of r%1 + mov a,dph + mov r%1,a +} + + +// applies to f.e. bug-460010.c (saving 1 cycle) +replace { + mov r%1,a + mov dpl,r%1 +} by { + mov r%1,a + ; Peephole 244.c loading dpl from a instead of r%1 + mov dpl,a +} + +replace { + mov r%1,a + mov dph,r%1 +} by { + mov r%1,a + ; Peephole 244.d loading dph from a instead of r%1 + mov dph,a +} + +// this one is safe but disables 245.a 245.b +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. scott-compare.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a +} by { + ; Peephole 245 optimized complement (r%1 and acc set needed?) + cpl c + clr a + rlc a + mov r%1,a +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + +// this one will not be triggered if 245 is present +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. vprintf.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a + jz %3 +} by { + ; Peephole 245.a optimized conditional jump (r%1 and acc not set!) + jc %3 +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + +// this one will not be triggered if 245 is present +// please remove 245 if 245.a 245.b are found to be safe +// applies to f.e. scott-compare.c +replace { + clr a + rlc a + mov r%1,a + cjne a,#0x01,%2 +%2: + clr a + rlc a + mov r%1,a + jnz %3 +} by { + ; Peephole 245.b optimized conditional jump (r%1 and acc not set!) + jnc %3 +} if labelRefCount(%2 1), labelRefCountChange(%2 -1) + + +// rules 246.x apply to f.e. bitfields.c +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + ; Peephole 246.a combined clr/clr + anl a,#%2&%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + ; Peephole 246.b combined set/set + orl a,#%2|%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + ; Peephole 246.c combined set/clr + anl a,#%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%3 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + ; Peephole 246.d combined clr/set + orl a,#%3 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + ; Peephole 246.e combined set/clr/clr + anl a,#%3&%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + orl a,#%2 + anl a,#%3 + ; Peephole 246.f combined set/clr/set + orl a,#%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + anl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + ; Peephole 246.g combined clr/set/clr + anl a,#%4 + movx @dptr,a +} if notVolatile %1 + +replace { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + orl a,#%3 + movx @dptr,a + mov dptr,#%1 + movx a,@dptr + orl a,#%4 + movx @dptr,a +} by { + mov dptr,#%1 + movx a,@dptr + anl a,#%2 + ; Peephole 246.h combined clr/set/set + orl a,#%3|%4 + movx @dptr,a +} if notVolatile %1 + + +// rules 247.x apply to f.e. bitfields.c +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + ; Peephole 247.a combined clr/clr + anl a,#%2&%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + ; Peephole 247.b combined set/set + orl a,#%2|%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + ; Peephole 247.c combined set/clr + anl a,#%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%3 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + ; Peephole 247.d combined clr/set + orl a,#%3 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + ; Peephole 247.e combined set/clr/clr + anl a,#%3&%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + orl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + orl a,#%2 + anl a,#%3 + ; Peephole 247.f combined set/clr/set + orl a,#%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%5 + anl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + ; Peephole 247.g combined clr/set/clr + anl a,#%4 + mov @r%5,a +} if notVolatile %1 + +replace { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + orl a,#%3 + mov @r%5,a + mov r%5,#%1 + mov a,@r%4 + orl a,#%4 + mov @r%5,a +} by { + mov r%5,#%1 + mov a,@r%5 + anl a,#%2 + ; Peephole 247.h combined clr/set/set + orl a,#%3|%4 + mov @r%5,a +} if notVolatile %1 + + +// Peepholes 248.x have to be compatible with the keyword volatile. +// They optimize typical accesses to memory mapped I/O devices: +// volatile xdata char t; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + orl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.a optimized or to xdata + orl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t&=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + anl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.b optimized and to xdata + anl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t^=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + mov dptr,%1 + mov a,%3 + xrl a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + mov r%2,a + ; Peephole 248.c optimized xor to xdata + xrl a,%3 + movx @dptr,a +} + +// volatile xdata char t; t|=0x01; t&=~0x01; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.d optimized or/and/or to volatile xdata + orl a,%3 + movx @dptr,a + movx a,@dptr + anl a,%4 + movx @dptr,a + movx a,@dptr + mov r%2,a + orl a,%5 + movx @dptr,a +} + +// volatile xdata char t; t&=~0x01; t|=0x01; t&=~0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.e optimized and/or/and to volatile xdata + anl a,%3 + movx @dptr,a + movx a,@dptr + orl a,%4 + movx @dptr,a + movx a,@dptr + mov r%2,a + anl a,%5 + movx @dptr,a +} + +// volatile xdata char t; t|=0x01; t&=~0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.f optimized or/and to volatile xdata + orl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + anl a,%4 + movx @dptr,a +} + +// volatile xdata char t; t&=~0x01; t|=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.g optimized and/or to volatile xdata + anl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + orl a,%4 + movx @dptr,a +} + +// volatile xdata char t; t^=0x01; t^=0x01; +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.h optimized xor/xor to volatile xdata + xrl a,%3 + movx @dptr,a + movx a,@dptr + mov r%2,a + xrl a,%4 + movx @dptr,a +} + +// Peeepholes 248.i to 248.m are like 248.d to 248.h except they apply to bitfields: +// xdata struct { unsigned b0:1; unsigned b1:1; unsigned b2:1; } xport; +// xport.b0=1; xport.b0=0; xport.b0=1; +replace { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + ; Peephole 248.i optimized or/and/or to xdata bitfield + movx a,@dptr + anl a,%4 + movx @dptr,a + movx a,@dptr + orl a,%5 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%4 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%5 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + ; Peephole 248.j optimized and/or/and to xdata bitfield + movx a,@dptr + orl a,%4 + movx @dptr,a + movx a,@dptr + anl a,%5 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + anl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + orl a,%3 + movx @dptr,a + ; Peephole 248.k optimized or/and to xdata bitfield + movx a,@dptr + anl a,%4 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + orl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + anl a,%3 + movx @dptr,a + ; Peephole 248.l optimized and/or to xdata bitfield + movx a,@dptr + orl a,%4 + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + xrl a,%3 + movx @dptr,a + + mov dptr,%1 + movx a,@dptr + xrl a,%4 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + xrl a,%3 + movx @dptr,a + ; Peephole 248.m optimized xor/xor to xdata bitfield + movx a,@dptr + xrl a,%4 + movx @dptr,a +} + +// Peeepholes 248.n to 248.p are like previous peepholes but apply to arrays +// applies to f.e. bug2686159.c +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + orl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.n optimized or to xdata array + orl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + anl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.o optimized and to xdata array + anl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + mov dptr,%1 + movx a,@dptr + mov r%2,a + xrl ar%2,%3 + mov dptr,%1 + mov a,r%2 + movx @dptr,a +} by { + mov dptr,%1 + movx a,@dptr + ; Peephole 248.p optimized xor to xdata array + xrl a,%3 + mov r%2,a + movx @dptr,a +} + +replace { + jnz %1 +%1: +} by { + ; Peephole 249.a jump optimization +} if labelRefCount(%1 1), labelRefCountChange(%1 -1) + +replace { + jz %1 +%1: +} by { + ; Peephole 249.b jump optimization +} if labelRefCount(%1 1), labelRefCountChange(%1 -1) + + +// This allows non-interrupt and interrupt code to safely compete +// for a resource without the non-interrupt code having to disable +// interrupts: +// volatile bit resource_is_free; +// if( resource_is_free ) { +// resource_is_free=0; do_something; resource_is_free=1; +// } +replace { + jnb %1,%2 +%3: + clr %1 +} by { + ; Peephole 250.a using atomic test and clear + jbc %1,%3 + sjmp %2 +%3: +} if labelRefCount(%3 0), labelRefCountChange(%3 1) + +replace { + jb %1,%2 + ljmp %3 +%2: + clr %1 +} by { + ; Peephole 250.b using atomic test and clear + jbc %1,%2 + ljmp %3 +%2: +} if labelRefCount %2 1 + + +// not before peephole 250.b +replace { + ljmp %5 +} by { + ; Peephole 251.a replaced ljmp %5 to ret with ret + ret +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// not before peephole 250.b +replace { + sjmp %5 +} by { + ; Peephole 251.b replaced sjmp %5 to ret with ret + ret +} if optimizeReturn(), labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// applies to shifts.c and when accessing arrays with an unsigned integer index +// saving 1 byte, 2 cycles +replace { + mov r%1,%2 + mov a,(%2 + 1) + xch a,r%1 + add a,acc + xch a,r%1 + rlc a + mov r%3,a +} by { + ; Peephole 252 optimized left shift + mov a,%2 + add a,acc + mov r%1,a + mov a,(%2 + 1) + rlc a + mov r%3,a +} + +// unsigned char i=8; do{ } while(--i != 0); +// this applies if i is kept in a register +replace { + dec %1 + cjne %1,#0x00,%2 +} by { + ; Peephole 253.a optimized decrement with compare + djnz %1,%2 +} if notVolatile(%1) + +// unsigned char i=8; do{ } while(--i != 0); +// this applies if i is kept in data memory +// must come before 256, see bug 1721024 +replace { + dec %1 + mov a,%1 + jnz %2 +} by { + ; Peephole 253.b optimized decrement with compare + djnz %1,%2 +} if notVolatile(%1), operandsNotRelated(%1 '@r0' '@r1') + + +// applies to f.e. funptrs.c +// saves one byte if %1 is a register or @register +replace { + mov a,%1 + add a,acc +} by { + mov a,%1 + ; Peephole 254 optimized left shift + add a,%1 +} if notVolatile %1 + +// applies to f.e. switch.c +replace { + clr c + mov a,#%1 + subb a,%2 + jc %3 +%4: + mov a,%2 + add a,%2 + add a,%2 + mov dptr,%5 + jmp @a+dptr +} by { + ; Peephole 255 optimized jump table index calculation + mov a,%2 + cjne a,#(%1+0x01),.+1 + jnc %3 +%4: + add a,%2 + add a,%2 + mov dptr,%5 + jmp @a+dptr +} + +// applies to f.e. jump tables and scott-bool1.c. +// similar peepholes can be constructed for other instructions +// after which a flag or a register is known (like: djnz, cjne, jnc) +replace { + jc %1 +%2: + clr c +} by { + jc %1 +%2: + ; Peephole 256.a removed redundant clr c +} if labelRefCount %2 0 + +// applies to f.e. logf.c +replace { + jnz %1 +%2: + clr a +} by { + jnz %1 +%2: + ; Peephole 256.b removed redundant clr a +} if labelRefCount %2 0 + +// applies to f.e. bug-905492.c +replace { + jnz %1 +%2: + mov %3,#0x00 +} by { + jnz %1 +%2: + ; Peephole 256.c loading %3 with zero from a + mov %3,a +} if labelRefCount %2 0 + +// applies to f.e. malloc.c +replace { + jnz %1 +%2: + mov %4,%5 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + ; Peephole 256.d loading %3 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4) + +replace { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + ; Peephole 256.e loading %3 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4 %6) + +replace { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %8,%9 + mov %3,#0x00 +} by { + jnz %1 +%2: + mov %4,%5 + mov %6,%7 + mov %8,%9 + ; Peephole 256.f loading %2 with zero from a + mov %3,a +} if labelRefCount(%2 0),operandsNotRelated('a' %4 %6 %8) + + +replace restart { + ljmp %5 +} by { + ljmp %6 + ; peephole 257.a jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + sjmp %5 +} by { + sjmp %6 + ; peephole 257.b jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jz %5 +} by { + jz %6 + ; peephole 257.c jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnz %5 +} by { + jnz %6 + ; peephole 257.d jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jc %5 +} by { + jc %6 + ; peephole 257.e jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnc %5 +} by { + jnc %6 + ; peephole 257.f jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jb %1,%5 +} by { + jb %1,%6 + ; peephole 257.g jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jnb %1,%5 +} by { + jnb %1,%6 + ; peephole 257.h jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jbc %1,%5 +} by { + jbc %1,%6 + ; peephole 257.i jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), labelInRange(%6), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + + +// in_byte<<=1; if(in_bit) in_byte|=1; +// helps f.e. reading data on a 3-wire (SPI) bus +replace { + mov a,%1 + add a,%1 + mov %1,a + jnb %2,%3 +%4: + orl %1,#0x01 +%3: +} by { + mov a,%1 + ; Peephole 258.a optimized bitbanging + mov c,%2 + addc a,%1 + mov %1,a +%4: +%3: +} if notVolatile(%1), labelRefCountChange(%3 -1) + +// in_byte<<=1; if(in_bit) in_byte|=1; +replace { + mov a,r%1 + add a,r%1 + mov r%1,a + jnb %2,%3 +%4: + orl ar%1,#0x01 +%3: +} by { + mov a,r%1 + ; Peephole 258.b optimized bitbanging + mov c,%2 + addc a,r%1 + mov r%1,a +%4: +%3: +} if labelRefCountChange(%3 -1) + +// in_byte>>=1; if(in_bit) in_byte|=0x80; +replace { + mov a,%1 + clr c + rrc a + mov %1,a + jnb %2,%3 +%4: + orl %1,#0x80 +%3: +} by { + mov a,%1 + ; Peephole 258.c optimized bitbanging + mov c,%2 + rrc a + mov %1,a +%4: +%3: +} if notVolatile(%1), labelRefCountChange(%3 -1) + +// in_byte>>=1; if(in_bit) in_byte|=0x80; +replace { + mov a,r%1 + clr c + rrc a + mov r%1,a + jnb %2,%3 +%4: + orl ar%1,#0x80 +%3: +} by { + mov a,r%1 + ; Peephole 258.d optimized bitbanging + mov c,%2 + rrc a + mov r%1,a +%4: +%3: +} if labelRefCountChange(%3 -1) + +// out_bit=out_byte&0x80; out_byte<<=1; +// helps f.e. writing data on a 3-wire (SPI) bus +replace { + mov a,%1 + rlc a + mov %2,c + mov a,%1 + add a,%1 + mov %1,a +} by { + mov a,%1 + ; Peephole 258.e optimized bitbanging + add a,%1 + mov %2,c + mov %1,a +} if notVolatile %1 + +// out_bit=out_byte&0x01; out_byte>>=1; +replace { + mov a,%1 + rrc a + mov %2,c + mov a,%1 + clr c + rrc a + mov %1,a +} by { + mov a,%1 + ; Peephole 258.f optimized bitbanging + clr c + rrc a + mov %2,c + mov %1,a +} if notVolatile %1 + +// Peepholes 259.x rely on the correct labelRefCount. Otherwise they are +// not compatible with peepholes 250.x +// Peepholes 250.x add jumps to a previously unused label. If the +// labelRefCount is not increased, peepholes 259.x are (mistakenly) applied. +// (Mail on sdcc-devel 2004-10-25) +// +// applies to f.e. vprintf.c +replace { + sjmp %1 +%2: + ret +} by { + sjmp %1 + ; Peephole 259.a removed redundant label %2 and ret +} if optimizeReturn(), labelRefCount(%2 0) + +// applies to f.e. gets.c +replace { + ljmp %1 +%2: + ret +} by { + ljmp %1 + ; Peephole 259.b removed redundant label %2 and ret +} if optimizeReturn(), labelRefCount(%2 0) + +replace { + sjmp %1 +%2: + sjmp %3 +} by { + sjmp %1 + ; Peephole 259.c removed redundant label %2 and sjmp %3 +} if labelRefCount(%2 0) + +replace { + ljmp %1 +%2: + sjmp %3 +} by { + ljmp %1 + ; Peephole 259.d removed redundant label %2 and sjmp %3 +} if labelRefCount(%2 0) + +replace { + sjmp %1 +%2: + ljmp %3 +} by { + sjmp %1 + ; Peephole 259.e removed redundant label %2 and ljmp %3 +} if labelRefCount(%2 0) + +replace { + ljmp %1 +%2: + ljmp %3 +} by { + ljmp %1 + ; Peephole 259.f removed redundant label %2 and ljmp %3 +} if labelRefCount(%2 0) + + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 +%3: +} by { + ; Peephole 260.a used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 +%3: +} by { + ; Peephole 260.b used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 +%3: +} by { + ; Peephole 260.c used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 +%3: +} by { + ; Peephole 260.d used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 +%3: +} by { + ; Peephole 260.e used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 +%3: +} by { + ; Peephole 260.f used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 +%3: +} by { + ; Peephole 260.g used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 +%3: +} by { + ; Peephole 260.h used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 +%3: +} by { + ; Peephole 260.i used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 +%3: +} by { + ; Peephole 260.j used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 +%3: +} by { + ; Peephole 260.k used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 + ljmp %19 +%3: +} by { + ; Peephole 260.l used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 + sjmp %19 +%3: +} if labelJTInRange + +// optimizing jumptables +replace { + add a,%1 + mov dptr,#%2 + jmp @a+dptr +%2: + ljmp %5 + ljmp %6 + ljmp %7 + ljmp %8 + ljmp %9 + ljmp %10 + ljmp %11 + ljmp %12 + ljmp %13 + ljmp %14 + ljmp %15 + ljmp %16 + ljmp %17 + ljmp %18 + ljmp %19 + ljmp %20 +%3: +} by { + ; Peephole 260.m used sjmp in jumptable + mov dptr,#%2 + jmp @a+dptr +%2: + sjmp %5 + sjmp %6 + sjmp %7 + sjmp %8 + sjmp %9 + sjmp %10 + sjmp %11 + sjmp %12 + sjmp %13 + sjmp %14 + sjmp %15 + sjmp %16 + sjmp %17 + sjmp %18 + sjmp %19 + sjmp %20 +%3: +} if labelJTInRange + +// applies to: a = (a << 1) | (a >> 15); +replace { + mov a,%1 + rlc a + mov %1,a + mov a,%2 + rlc a + mov %2,a + mov a,%1 + mov acc.0,c + mov %1,a +} by { + mov a,%1 + rlc a + ; Peephole 261.a optimized left rol + xch a,%2 + rlc a + xch a,%2 + mov acc.0,c + mov %1,a +} + +// applies to: a = (a << 15) | (a >> 1); +replace { + mov a,%1 + rrc a + mov %1,a + mov a,%2 + rrc a + mov %2,a + mov a,%1 + mov acc.7,c + mov %1,a +} by { + mov a,%1 + rrc a + ; Peephole 261.b optimized right rol + xch a,%2 + rrc a + xch a,%2 + mov acc.7,c + mov %1,a +} + +replace { + cpl c + cpl c +} by { + ; Peephole 262 removed redundant cpl c +} + +replace { + mov %1,#%2 + inc %1 + inc %1 + inc %1 +} by { + ; Peephole 263.a optimized loading const + mov %1,#(%2 + 3) +} if notVolatile(%1) + +replace { + mov %1,#%2 + inc %1 + inc %1 +} by { + ; Peephole 263.b optimized loading const + mov %1,#(%2 + 2) +} if notVolatile(%1) + +replace { + mov %1,#%2 + inc %1 +} by { + ; Peephole 263.c optimized loading const + mov %1,#(%2 + 1) +} if notVolatile(%1) + + +replace { + clr a + cjne %1,%2,%3 + inc a +%3: + jz %4 +} by { + ; Peephole 264 jump optimization (acc not set) + cjne %1,%2,%4 +%3: +} if labelRefCount(%3 1), labelRefCountChange(%3 -1) + + +replace { + mov %1,c + cpl %1 +} by { + ; Peephole 265 optimized mov/cpl sequence (carry differs) + cpl c + mov %1,c +} if notVolatile(%1) + +replace { + mov %1,c + jb %1,%2 +} by { + ; Peephole 266.a optimized mov/jump sequence + mov %1,c + jc %2 +} if notVolatile(%1) + +replace { + mov %1,c + jnb %1,%2 +} by { + ; Peephole 266.b optimized mov/jump sequence + mov %1,c + jnc %2 +} if notVolatile(%1) + +replace { + jnc %1 + setb %2 + sjmp %3 +%1: + clr %2 +%3: +} by { + ; Peephole 267.a optimized mov bit sequence + mov %2,c +%1: +%3: +} if labelRefCount(%1 1), labelRefCountChange(%1 -1), labelRefCountChange(%3 -1) + +replace { + jc %1 + clr %2 + sjmp %3 +%1: + setb %2 +%3: +} by { + ; Peephole 267.b optimized mov bit sequence + mov %2,c +%1: +%3: +} if labelRefCount(%1 1), labelRefCountChange(%1 -1), labelRefCountChange(%3 -1) + +replace { + mov %1,c + mov %1,c +} by { + ; Peephole 268 removed redundant mov + mov %1,c +} if notVolatile(%1) + +replace { + mov %1,c + mov c,%1 +} by { + ; Peephole 269 removed redundant mov + mov %1,c +} if notVolatile(%1) + +//accessing struct/array on stack +//replace { +// add a,#%1 +// add a,#%2 +//} by { +// ; Peephole 270 removed redundant add (carry might differ, bug 2736282) +// add a,#%1+%2 +//} + +replace { + jz %1 + mov %2,%4 + sjmp %3 +%1: + mov %2,#0x00 +%3: +} by { + jz %1 + ; Peephole 271 optimized ternary operation (acc different) + mov a,%4 +%1: + mov %2,a +%3: +} if operandsNotRelated('a' 'dptr' %2), labelRefCount(%1 1), labelRefCountChange(%3 -1) + + +// left-shifting 8 bit to 16 bit result +// http://sourceforge.net/projects/sdcc/forums/forum/1864/topic/3536144 +replace { + clr a + swap a +} by { + clr a + ; Peephole 272.a removed swap operation on zero +} + +replace { + clr a + anl a,%1 +} by { + clr a + ; Peephole 272.b removed anl operation on zero +} if notVolatile(%1) + +replace { + clr a + mov c,acc.0 +} by { + clr a + ; Peephole 272.c clearing carry directly + clr c +} + + +replace restart { + pop ar%1 +} by { + ; Peephole 300 pop ar%1 removed +} if deadMove(%1) + +replace { + mov r%1,%2 +} by { + ; Peephole 301 mov r%1,%2 removed +} if notVolatile(%2), deadMove(%1) + + +// applies to: void test( char c ) { if( c ) func1(); else func2(); } +replace { + lcall %1 + ret +} by { + ; Peephole 400.a replaced lcall/ret with ljmp + ljmp %1 +} if optimizeReturn(), notSame(%1 '_longjmp') + +// applies to: void test( char c ) { if( c ) func1(); else func2(); } +replace { + lcall %1 +%2: + ret +} by { + ; Peephole 400.b replaced lcall/ret with ljmp + ljmp %1 + ; +} if optimizeReturn(), notSame(%1 '_longjmp'), labelRefCount(%2 0) + +// applies to f.e. scott-bool1.c +replace { + lcall %1 +%2: + ret +} by { + ; Peephole 400.c replaced lcall with ljmp + ljmp %1 +%2: + ret +} if optimizeReturn(), notSame(%1 '_longjmp') + +// for programs less than 2k +replace { + lcall %1 +} by { + ; Peephole 401.a replaced lcall with acall + acall %1 +} if useAcallAjmp + +// for programs less than 2k +replace { + ljmp %1 +} by { + ; Peephole 401.b replaced ljmp with ajmp + ajmp %1 +} if useAcallAjmp + + +// should be one of the last peepholes +replace{ +%1: +} by { + ; Peephole 500 removed redundant label %1 +} if labelRefCount(%1 0) diff --git a/src/mcs51/ralloc.c b/src/mcs51/ralloc.c new file mode 100644 index 0000000..1d900ee --- /dev/null +++ b/src/mcs51/ralloc.c @@ -0,0 +1,3466 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. (8051) specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +extern void gen51Code (iCode *); +#define D(x) + +/* Global data */ +static struct +{ + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + bitVect *allBitregs; /* all bit registers */ + bitVect *allBankregs; /* all bank registers */ +} +_G; + +/* Shared with gen.c */ +int mcs51_ptrRegReq; /* one byte pointer register required */ + +/* 8051 registers */ +reg_info regs8051[] = { + {REG_GPR, R7_IDX, REG_GPR, "r7", "ar7", "0", 7, 1}, + {REG_GPR, R6_IDX, REG_GPR, "r6", "ar6", "0", 6, 1}, + {REG_GPR, R5_IDX, REG_GPR, "r5", "ar5", "0", 5, 1}, + {REG_GPR, R4_IDX, REG_GPR, "r4", "ar4", "0", 4, 1}, + {REG_GPR, R3_IDX, REG_GPR, "r3", "ar3", "0", 3, 1}, + {REG_GPR, R2_IDX, REG_GPR, "r2", "ar2", "0", 2, 1}, + {REG_PTR, R1_IDX, REG_PTR, "r1", "ar1", "0", 1, 1}, + {REG_PTR, R0_IDX, REG_PTR, "r0", "ar0", "0", 0, 1}, + {REG_BIT, B0_IDX, REG_BIT, "b0", "b0", "bits", 0, 1}, + {REG_BIT, B1_IDX, REG_BIT, "b1", "b1", "bits", 1, 1}, + {REG_BIT, B2_IDX, REG_BIT, "b2", "b2", "bits", 2, 1}, + {REG_BIT, B3_IDX, REG_BIT, "b3", "b3", "bits", 3, 1}, + {REG_BIT, B4_IDX, REG_BIT, "b4", "b4", "bits", 4, 1}, + {REG_BIT, B5_IDX, REG_BIT, "b5", "b5", "bits", 5, 1}, + {REG_BIT, B6_IDX, REG_BIT, "b6", "b6", "bits", 6, 1}, + {REG_BIT, B7_IDX, REG_BIT, "b7", "b7", "bits", 7, 1}, + {REG_GPR, X8_IDX, REG_GPR, "x8", "x8", "xreg", 0, 1}, + {REG_GPR, X9_IDX, REG_GPR, "x9", "x9", "xreg", 1, 1}, + {REG_GPR, X10_IDX, REG_GPR, "x10", "x10", "xreg", 2, 1}, + {REG_GPR, X11_IDX, REG_GPR, "x11", "x11", "xreg", 3, 1}, + {REG_GPR, X12_IDX, REG_GPR, "x12", "x12", "xreg", 4, 1}, + {REG_CND, CND_IDX, REG_CND, "C", "psw", "0xd0", 0, 1}, + {0, DPL_IDX, 0, "dpl", "dpl", "0x82", 0, 0}, + {0, DPH_IDX, 0, "dph", "dph", "0x83", 0, 0}, + {0, B_IDX, 0, "b", "b", "0xf0", 0, 0}, + {0, A_IDX, 0, "a", "acc", "0xe0", 0, 0}, +}; + +int mcs51_nRegs = 16; +static void spillThis (symbol *); +static void freeAllRegs (); + +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocReg (short type) +{ + int i; + + for (i = 0; i < mcs51_nRegs; i++) + { + /* if type is given as 0 then any + free register will do */ + if (!type && regs8051[i].isFree) + { + regs8051[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s8051[i]; + } + /* otherwise look for specific type + of register */ + if (regs8051[i].isFree && regs8051[i].type == type) + { + regs8051[i].isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + return ®s8051[i]; + } + } + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* allocThisReg - allocates a particular register (if free) */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocThisReg (reg_info *reg) +{ + if (!reg->isFree) + return NULL; + + reg->isFree = 0; + if (currFunc) + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, reg->rIdx); + + return reg; +} + + +/*-----------------------------------------------------------------*/ +/* mcs51_regWithIdx - returns pointer to register with index number*/ +/*-----------------------------------------------------------------*/ +reg_info * +mcs51_regWithIdx (int idx) +{ + int i; + + for (i = 0; i < sizeof (regs8051) / sizeof (reg_info); i++) + if (regs8051[i].rIdx == idx) + return ®s8051[i]; + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "regWithIdx not found"); + exit (1); +} + +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (reg_info *reg) +{ + if (!reg) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "freeReg - Freeing NULL register"); + exit (1); + } + + reg->isFree = 1; +} + + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ + int i; + int nfr = 0; + + for (i = 0; i < mcs51_nRegs; i++) + if (regs8051[i].isFree && regs8051[i].type == type) + nfr++; + return nfr; +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} + +/*-----------------------------------------------------------------*/ +/* useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +static void +useReg (reg_info *reg) +{ + reg->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; +} + +/*-----------------------------------------------------------------*/ +/* bitType - will return 1 if the symbol has type REG_BIT */ +/*-----------------------------------------------------------------*/ +static int +bitType (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->regType == REG_BIT ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 0 : 1); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* directSpilLoc - will return 1 if the spillocation is in direct */ +/*-----------------------------------------------------------------*/ +static int +directSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + if (sym->usl.spillLoc && (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil */ +/* location but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((sym->usl.spillLoc && !sym->uptr) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + /* if usage is the same then prefer + to spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; sym = setNextItem (itmpStack)) + { + if (bitVectBitValue (sym->clashes, fsym->key)) + return 0; + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && noOverLap (sym->usl.itmpStack, fsym) && getSize (sym->type) >= getSize (fsym->type) + && (IS_BIT (sym->type) == IS_BIT (fsym->type))) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + reg_info *r0, *r1; + int k; + + if (!_G.regAssigned || bitVectIsZero (_G.regAssigned)) + return; + + r0 = mcs51_regWithIdx (R0_IDX); + r1 = mcs51_regWithIdx (R1_IDX); + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; lrsym = hTabNextItem (liveRanges, &k)) + { + int j; + + /* if no registers assigned to it or spilt */ + /* if it does not overlap this then + no need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || (lrsym->liveTo < forSym->liveFrom)) + continue; + + /* go thru the registers : if it is either + r0 or r1 then spill it */ + for (j = 0; j < lrsym->nRegs; j++) + if (lrsym->regs[j] == r0 || lrsym->regs[j] == r1) + { + spillThis (lrsym); + break; + } + } +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model; + + struct dbuf_s dbuf; + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + if (!IS_BIT (sloc->etype)) + { + SPEC_SCLS (sloc->etype) = S_DATA; + } + else if (SPEC_SCLS (sloc->etype) == S_SBIT) + { + SPEC_SCLS (sloc->etype) = S_BIT; + } + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE (sloc->etype) = 0; + SPEC_ABSA (sloc->etype) = 0; + + /* we don't allow it to be allocated + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + */ + + useXstack = options.useXstack; + model = options.model; +/* noOverlay = options.noOverlay; */ +/* options.noOverlay = 1; */ + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; +/* options.noOverlay = noOverlay; */ + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/* or otherwise needs a pointer register */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + +/* if (sym->_G.stackSpil) */ +/* return TRUE; */ + + if (!sym->usl.spillLoc) + return FALSE; + + if (sym->usl.spillLoc->onStack || sym->usl.spillLoc->iaccess) + return TRUE; + + etype = getSpec (sym->usl.spillLoc->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it as spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to some + LIVE ranges */ + if (!mcs51_ptrRegReq && isSpiltOnStack (sym)) + { + spillLRWithPtrReg (sym); + mcs51_ptrRegReq++; + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* remove incompatible registers */ + if ((forSym->regType == REG_PTR) || (forSym->regType == REG_GPR)) + { + selectS = liveRangesWith (lrcs, bitType, ebp, ic); + + for (sym = setFirstItem (selectS); sym; sym = setNextItem (selectS)) + { + bitVectUnSetBit (lrcs, sym->key); + } + } + + /* get all live ranges that are rematerializable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strncpyz (sym->rname, + sym->usl.spillLoc->rname[0] ? sym->usl.spillLoc->rname : sym->usl.spillLoc->name, sizeof (sym->rname)); + sym->spildir = 1; + /* mark it as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that are + not used in the remainder of the block */ + if (!_G.blockSpil && !isiCodeInFunctionCall (ic) && (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (sym != forSym) + { + if (!sym->remat) + { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack, for which one? + the least used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = ssym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + bitVectUnSetBit (_G.totRegAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!mcs51_ptrRegReq && isSpiltOnStack (ssym)) + { + spillLRWithPtrReg (ssym); + mcs51_ptrRegReq++; + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!mcs51_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in reality selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegBit - will try for Bit if not spill this */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBitTry (symbol * sym) +{ + reg_info *reg; + + /* try for a bit type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtrNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtrNoSpil () +{ + reg_info *reg; + + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegGprNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGprNoSpil () +{ + reg_info *reg; + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!mcs51_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* getRegBitNoSpil - get it cannot be spilt */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegBitNoSpil () +{ + reg_info *reg; + + /* try for a ptr type */ + if ((reg = allocReg (REG_BIT))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + assert (0); + + /* just to make the compiler happy */ + return 0; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol * sym, reg_info * reg) +{ + int i; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* updateRegUsage - update the registers in use at the start of */ +/* this icode */ +/*-----------------------------------------------------------------*/ +static void +updateRegUsage (iCode * ic) +{ + int reg; + + for (reg = 0; reg < mcs51_nRegs; reg++) + { + if (regs8051[reg].isFree) + { + ic->riu &= ~(1 << regs8051[reg].offset); + } + else + { + ic->riu |= (1 << regs8051[reg].offset); + BitBankUsed |= (reg >= 8); + } + } +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + symbol *result; + + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && ic->prev->op == IPOP && !ic->prev->parmPush && !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && + !( SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == RETURN || + POINTER_SET (ic) ) && + (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && + !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + sym->nRegs) >= result->nRegs)) + { + for (i = 0; i < result->nRegs; i++) + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, result->key); + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + /* first check if there are any available registers + of the type required */ + if (rt == REG_PTR) + { + /* special case for pointer type + if pointer type not available then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return 0; + if (nFreeRegs (REG_GPR) >= nr) + return 0; + } + else if (rt == REG_BIT) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (mcs51_ptrRegReq) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (nFreeRegs (REG_PTR) + nFreeRegs (REG_GPR) >= nr) + return 0; + } + } + + /* it will cause a spil */ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to */ +/* result and operand, if this happens make sure they are in the */ +/* same position as the operand otherwise chaos results */ +/*-----------------------------------------------------------------*/ +static int +positionRegs (symbol * result, symbol * opsym, int chOp) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + int change = 0; + + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return 0; + + for (;;) + { + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + for (j = 0; j < count; j++) + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = 1; + goto xchgPositions; + } +xchgPositions: + if (shared) + if (!chOp) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + change++; + } + else + { + reg_info *tmp = opsym->regs[i]; + opsym->regs[i] = opsym->regs[j]; + opsym->regs[j] = tmp; + change++; + } + else + return change; + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand * op, iCode * ic) +{ + symbol *sym; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) + return; + if (!sym->nRegs) + return; + if (sym->regs[0]) + return; + + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name); + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + updateRegUsage (ic); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j; + int ptrRegSet = 0; + + /* Make sure any spill location is definitely allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || sym->isspilt || bitVectBitValue (_G.regAssigned, sym->key) || sym->liveTo <= ic->seq) + { + continue; + } + + /* do not try to spil bit registers as it won't work */ + if (sym->regType != REG_BIT) + { + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + + willCS = willCauseSpill (sym->nRegs, sym->regType); + /* if this is a bit variable then don't use precious registers + along with expensive bit-to-char conversions but just spill + it */ + if (willCS && SPEC_NOUN (sym->etype) == V_BIT) + { + spillThis (sym); + continue; + } + + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + spillable = computeSpillable (ic); + if (sym->remat || (willCS && bitVectIsZero (spillable))) + { + spillThis (sym); + continue; + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) + { + if (sym->usl.spillLoc) + { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) + { + spillThis (sym); + continue; + } + } + else + { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith (spillable, noSpilLoc, ebbs[i], ic)) + { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) + { + spillThis (sym); + continue; + } + } + } + } + } + /* if we need ptr regs for the right side + then mark it */ + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) + && getSize (OP_SYMBOL (IC_LEFT (ic))->type) <= (unsigned int) NEARPTRSIZE) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype) == idata) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && SPEC_OCLS (OP_SYMBOL (IC_RIGHT (ic))->etype) == idata) + { + mcs51_ptrRegReq++; + ptrRegSet = 1; + } + + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + for (j = 0; j < sym->nRegs; j++) + { + sym->regs[j] = NULL; + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else + { + if (sym->regType == REG_BIT) /* Try to allocate to bit register if possible */ + sym->regs[j] = getRegBitTry (sym); + if (ic->op == CAST && IS_SYMOP (IC_RIGHT (ic))) + { + symbol *right = OP_SYMBOL (IC_RIGHT (ic)); + + if (right->regs[j] && (right->regType != REG_BIT)) + sym->regs[j] = allocThisReg (right->regs[j]); + } + if (!sym->regs[j]) + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + } + + /* if the allocation failed which means + this was spilt then break */ + if (!sym->regs[j]) + { + int i; + for (i = 0; i < sym->nRegs; i++) + sym->regs[i] = NULL; + break; + } + } + + /* for debugging prefer to keep the sym in ascending + registers so sort them by address */ + if (sym->regs[0]) + { + for (j = 0; j < sym->nRegs - 1; j++) + { + int k; + for (k=j+1; knRegs; k++) + { + if (sym->regs[j]->offset > sym->regs[k]->offset) + { + reg_info *tmp = sym->regs[j]; + sym->regs[j] = sym->regs[k]; + sym->regs[k] = tmp; + } + } + } + } + + if (!POINTER_SET (ic) && !POINTER_GET (ic)) + { + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 0); + } + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 0); + } + } + + if (ptrRegSet) + { + mcs51_ptrRegReq--; + ptrRegSet = 0; + } + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + { + continue; + } + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* fillGaps - Try to fill in the Gaps left by Pass1 */ +/*-----------------------------------------------------------------*/ +static void +fillGaps (void) +{ + symbol *sym = NULL; + int key = 0; + int pass; + iCode *ic = NULL; + + if (getenv ("DISABLE_FILL_GAPS")) + return; + + /* look for liveranges that were spilt by the allocator */ + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + int i; + int pdone = 0; + + if (!sym->spillA || !sym->clashes || sym->remat) + continue; + + /* if spilt in direct space the original rname is lost */ + if (sym->usl.spillLoc && (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + continue; + + /* find the liveRanges this one clashes with, that are + still assigned to registers & mark the registers as used */ + for (i = 0; i < sym->clashes->size; i++) + { + int k; + symbol *clr; + + if (bitVectBitValue (sym->clashes, i) == 0 || /* those that clash with this */ + bitVectBitValue (_G.totRegAssigned, i) == 0) /* and are still assigned to registers */ + continue; + + clr = hTabItemWithKey (liveRanges, i); + assert (clr); + + /* mark these registers as used */ + for (k = 0; k < clr->nRegs; k++) + useReg (clr->regs[k]); + } + + if (willCauseSpill (sym->nRegs, sym->regType)) + { + /* NOPE :( clear all registers & and continue */ + freeAllRegs (); + continue; + } + + ic = NULL; + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (ic->op == CAST) + break; + } + } + + D (printf ("Attempting fillGaps on %s: [", sym->name)); + /* THERE IS HOPE !!!! */ + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regType == REG_PTR) + sym->regs[i] = getRegPtrNoSpil (); + else if (sym->regType == REG_BIT) + sym->regs[i] = getRegBitNoSpil (); + else + { + sym->regs[i] = NULL; + if (ic && ic->op == CAST && IS_SYMOP (IC_RIGHT (ic))) + { + symbol *right = OP_SYMBOL (IC_RIGHT (ic)); + + if (right->regs[i]) + sym->regs[i] = allocThisReg (right->regs[i]); + } + if (!sym->regs[i]) + sym->regs[i] = getRegGprNoSpil (); + } + D (printf ("%s ", sym->regs[i]->name)); + } + D (printf ("]\n")); + + /* For all its definitions check if the registers + allocated needs positioning NOTE: we can position + only ONCE if more than One positioning required + then give up. + We may need to perform the checks twice; once to + position the registers as needed, the second to + verify any register repositioning is still + compatible. + */ + sym->isspilt = 0; + for (pass = 0; pass < 2; pass++) + { + D (printf (" checking definitions\n")); + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + D (printf (" ic->seq = %d\n", ic->seq)); + if (SKIP_IC (ic)) + continue; + assert (isSymbolEqual (sym, OP_SYMBOL (IC_RESULT (ic)))); /* just making sure */ + /* if left is assigned to registers */ + if (IS_SYMOP (IC_LEFT (ic))) + { + D (printf (" left = ")); + D (printOperand (IC_LEFT (ic), NULL)); + } + if (IS_SYMOP (IC_LEFT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_LEFT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_LEFT (ic)), 0) > 0); + } + if (IS_SYMOP (IC_RIGHT (ic))) + { + D (printf (" right = ")); + D (printOperand (IC_RIGHT (ic), NULL)); + } + if (IS_SYMOP (IC_RIGHT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RIGHT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RIGHT (ic)), 0) > 0); + } + D (printf (" pdone = %d\n", pdone)); + if (pdone > 1) + break; + } + } + D (printf (" checking uses\n")); + for (i = 0; i < sym->uses->size; i++) + { + if (bitVectBitValue (sym->uses, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + D (printf (" ic->seq = %d\n", ic->seq)); + if (SKIP_IC (ic)) + continue; + if (POINTER_SET (ic) || POINTER_GET (ic)) + continue; + + /* if result is assigned to registers */ + if (IS_SYMOP (IC_RESULT (ic))) + { + D (printf (" result = ")); + D (printOperand (IC_RESULT (ic), NULL)); + } + if (IS_SYMOP (IC_RESULT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RESULT (ic))->key)) + { + pdone += (positionRegs (sym, OP_SYMBOL (IC_RESULT (ic)), 0) > 0); + } + D (printf (" pdone = %d\n", pdone)); + if (pdone > 1) + break; + } + } + if (pdone == 0) + break; /* second pass only if regs repositioned */ + if (pdone > 1) + break; + } + D (printf (" sym->regs = [")); + for (i = 0; i < sym->nRegs; i++) + D (printf ("%s ", sym->regs[i]->name)); + D (printf ("]\n")); + /* had to position more than once GIVE UP */ + if (pdone > 1) + { + /* UNDO all the changes we made to try this */ + sym->isspilt = 1; + for (i = 0; i < sym->nRegs; i++) + { + sym->regs[i] = NULL; + } + freeAllRegs (); + D (printf + ("Fill Gap gave up due to positioning for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + continue; + } + D (printf ("FILLED GAP for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + sym->isspilt = sym->spillA = 0; + sym->usl.spillLoc->allocreq--; + freeAllRegs (); + } +} + +/*-----------------------------------------------------------------*/ +/* findAllBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +static bitVect * +findAllBitregs (void) +{ + bitVect *rmask = newBitVect (mcs51_nRegs); + int j; + + for (j = 0; j < mcs51_nRegs; j++) + { + if (regs8051[j].type == REG_BIT) + rmask = bitVectSetBit (rmask, regs8051[j].rIdx); + } + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* mcs51_allBitregs :- returns bit vector of all bit registers */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_allBitregs (void) +{ + return _G.allBitregs; +} + +/*-----------------------------------------------------------------*/ +/* findAllBankregs :- returns bit vector of all bank registers */ +/*-----------------------------------------------------------------*/ +static bitVect * +findAllBankregs (void) +{ + bitVect *rmask = newBitVect (mcs51_nRegs); + int j; + + for (j = 0; j < mcs51_nRegs; j++) + { + if ((regs8051[j].type == REG_GPR) || (regs8051[j].type == REG_PTR)) + rmask = bitVectSetBit (rmask, regs8051[j].rIdx); + } + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* mcs51_allBankregs :- returns bit vector of all bank registers */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_allBankregs (void) +{ + return _G.allBankregs; +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +mcs51_rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (mcs51_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + if (sym->regs[j]) /* EEP - debug */ + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (mcs51_nRegs); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_JTCOND (ic))); + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, mcs51_rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (mcs51_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "createRegMask cannot find live range"); + fprintf (stderr, "\tmissing live range: key=%d\n", j); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + if (sym->regs[k]) + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static char * +rematStr (symbol * sym) +{ + iCode *ic = sym->rematiCode; + int offset = 0; + struct dbuf_s dbuf; + + while (1) + { + /* if plus adjust offset to right hand side */ + if (ic->op == '+') + { + offset += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* if minus adjust offset to right hand side */ + if (ic->op == '-') + { + offset -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* cast then continue */ + if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + continue; + } + /* we reached the end */ + break; + } + + dbuf_init (&dbuf, 128); + if (offset) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, offset >= 0 ? '+' : '-', abs (offset) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + return dbuf_detach_c_str (&dbuf); +} + +/*------------------------------------------------------------------*/ +/* isBitVar - returns true if sym is a good candiate for allocation */ +/* to a bit */ +/*------------------------------------------------------------------*/ +static bool isFlagVar (symbol *sym) +{ + if (IS_BIT (sym->type)) + return (true); + + if (!(IS_SPEC(sym->type) && SPEC_NOUN (sym->type) == V_BOOL && !sym->addrtaken)) + return (false); + + bitVect *defs = bitVectCopy (sym->defs); + bitVect *uses = bitVectCopy (sym->uses); + int key; + unsigned int gooduses = 0; + unsigned int baduses = 0; + + for (key = bitVectFirstBit (defs); key >= 0; key = bitVectFirstBit (defs)) + { + bitVectUnSetBit (defs, key); + + iCode *ic = hTabItemWithKey (iCodehTab, key); + + if (ic->op == AND_OP || ic->op == OR_OP || ic->op == EQ_OP || ic->op == '<' || ic->op == '>' || ic->op == CAST || ic->op == '!') + gooduses++; + else if (ic->op == '=' && + (IS_OP_LITERAL (IC_RIGHT (ic)) || IS_SYMOP (IC_RIGHT (ic)) && IS_BIT (OP_SYMBOL (IC_RIGHT (ic))->type))) + gooduses++; + else + baduses++; + } + + for (key = bitVectFirstBit (uses); key >= 0; key = bitVectFirstBit (uses)) + { + bitVectUnSetBit (uses, key); + + iCode *ic = hTabItemWithKey (iCodehTab, key); + + if (!ic) /* Shouldn't happen, but does */ + continue; + + if (ic->op == IFX || ic->op == '!' || ic->op == AND_OP || ic->op == OR_OP) + gooduses++; + else if (ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') + gooduses++; + else if (ic->op == '=' && !POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) && IS_BIT (OP_SYMBOL (IC_RESULT (ic))->type)) + gooduses++; + else + baduses++; + } + + freeBitVect (defs); + freeBitVect (uses); + + return (gooduses >= baduses * 2); +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (eBBlock * ebbs) +{ + symbol *sym; + int k; + iCode *ic; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + else if (IS_BIT (sym->type)) + sym->regType = REG_CND; + continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer */ + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, bitVectFirstBit (sym->defs))) && + POINTER_GET (ic) && !IS_BITVAR (sym->etype) && (aggrToPtrDclType (operandType (IC_LEFT (ic)), FALSE) == POINTER)) + { + if (ptrPseudoSymSafe (sym, ic)) + { + char *s = rematStr (OP_SYMBOL (IC_LEFT (ic))); + ptrPseudoSymConvert (sym, ic, s); + Safe_free (s); + continue; + } + + /* if in data space or idata space then try to + allocate pointer register */ + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 or 0 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && IS_PTR (sym->type) && sym->uptr) + sym->regType = REG_PTR; + else if (isFlagVar (sym)) + sym->regType = REG_BIT; + else + sym->regType = REG_GPR; + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +static void +freeAllRegs () +{ + int i; + + for (i = 0; i < mcs51_nRegs; i++) + regs8051[i].isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > 1) + return NULL; + + if (POINTER_SET (dic) && getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) > 1) + return NULL; + + if (dic->op == IFX) + { + if (IC_COND (dic) && IS_TRUE_SYMOP (IC_COND (dic)) && isOperandInFarSpace (IC_COND (dic))) + return NULL; + } + else if (dic->op == JUMPTABLE) + { + if (IC_JTCOND (dic) && IS_TRUE_SYMOP (IC_JTCOND (dic)) && isOperandInFarSpace (IC_JTCOND (dic))) + return NULL; + } + else + { + /* if any tree is a true symbol in far space */ + if (IC_RESULT (dic) && IS_TRUE_SYMOP (IC_RESULT (dic)) && isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + } + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) + { + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP || dic->op == '-') && IS_OP_LITERAL (IC_RIGHT (dic))) + return NULL; + else + return dic; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ + if (isOperandInFarSpace (IC_RESULT (ic)) && !farSpacePackable (ic)) + { + return 0; + } + + /* find the definition of iTempNN scanning backwards if we find + a use of the true symbol before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + int crossedCall = 0; + + /* We can pack across a function call only if it's a local */ + /* variable or our parameter. Never pack global variables */ + /* or parameters to a function we call. */ + if ((dic->op == CALL || dic->op == PCALL)) + { + if (!OP_SYMBOL (IC_RESULT (ic))->ismyparm && !OP_SYMBOL (IC_RESULT (ic))->islocal) + { + crossedCall = 1; + } + } + + if (dic->op == INLINEASM) + { + dic = NULL; + break; + } + + /* Don't move an assignment out of a critical block */ + if (dic->op == CRITICAL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + + if (crossedCall) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + // TODO: and the otherway around? + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic)), ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/*------------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*------------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + /* This routine is used to find sequences like + iTempAA = FOO; + ...; (intervening ops don't use iTempAA or modify FOO) + blah = blah + iTempAA; + + and eliminate the use of iTempAA, freeing up its register for + other uses. + */ + + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if definition by assignment */ + if (dic->op == '=' && !POINTER_SET (dic) && IC_RESULT (dic)->key == op->key +/* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + ) + break; /* found where this temp was defined */ + + /* if we find an usage then we cannot delete it */ + + if (dic->op == IFX) + { + if (IC_COND (dic) && IC_COND (dic)->key == op->key) + return NULL; + } + else if (dic->op == JUMPTABLE) + { + if (IC_JTCOND (dic) && IC_JTCOND (dic)->key == op->key) + return NULL; + } + else + { + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + } + + if (!dic) + return NULL; /* didn't find any assignment to op */ + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then don't repack regs */ + if (!IS_SYMOP (IC_RIGHT (dic))) + return NULL; + + /* if the symbol is volatile then we should not */ + if (isOperandVolatile (IC_RIGHT (dic), TRUE)) + return NULL; + /* XXX TODO --- should we be passing FALSE to isOperandVolatile()? + What does it mean for an iTemp to be volatile, anyway? Passing + TRUE is more cautious but may prevent possible optimizations */ + + /* if the symbol is in far space then we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; +} + +/*-----------------------------------------------------------------*/ +/* reassignAliasedSym - used by packRegsForSupport to replace */ +/* redundant iTemp with equivalent symbol */ +/*-----------------------------------------------------------------*/ +static void +reassignAliasedSym (eBBlock * ebp, iCode * assignment, iCode * use, operand * op) +{ + iCode *ic; + unsigned oldSymKey, newSymKey; + + oldSymKey = op->key; + newSymKey = IC_RIGHT (assignment)->key; + + /* only track live ranges of compiler-generated temporaries */ + if (!IS_ITEMP (IC_RIGHT (assignment))) + newSymKey = 0; + + /* update the live-value bitmaps */ + for (ic = assignment; ic != use; ic = ic->next) + { + bitVectUnSetBit (ic->rlive, oldSymKey); + if (newSymKey != 0) + ic->rlive = bitVectSetBit (ic->rlive, newSymKey); + } + + /* update the sym of the used operand */ + OP_SYMBOL (op) = OP_SYMBOL (IC_RIGHT (assignment)); + op->key = OP_SYMBOL (op)->key; + OP_SYMBOL (op)->accuse = 0; + + /* update the sym's liverange */ + if (OP_LIVETO (op) < ic->seq) + setToRange (op, ic->seq, FALSE); + + /* remove the assignment iCode now that its result is unused */ + remiCodeFromeBBlock (ebp, assignment); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (assignment))->defs, assignment->key); + hTabDeleteItem (&iCodehTab, assignment->key, assignment, DELETE_ITEM, NULL); +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + + if (IS_ITEMP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + dic = findAssignToSym (IC_LEFT (ic), ic); + + if (dic) + { + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_LEFT (ic)); + return 1; + } + } + + /* do the same for the right operand */ + if (IS_ITEMP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + + if (dic) + { + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) + { + sym_link *etype = getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return 0; + } + /* found it we need to remove it from the block */ + reassignAliasedSym (ebp, dic, ic, IC_RIGHT (ic)); + + return 1; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + iCode *dic, *sic; + sym_link *type; + int usingCarry=0; + + /* if returning a literal then do nothing */ + if (!IS_ITEMP (op)) + return NULL; + + /* if rematerializable or already return use then do nothing */ + if (OP_SYMBOL (op)->remat || OP_SYMBOL (op)->ruonly) + return NULL; + + /* only upto 2 bytes since we cannot predict + the usage of b, & acc */ + type = operandType (op); + if (getSize (type) > (fReturnSizeMCS51 - 2)) + return NULL; + usingCarry = IS_BIT(type); + + if (ic->op != RETURN && ic->op != SEND && !POINTER_SET (ic) && !POINTER_GET (ic)) + return NULL; + + if (ic->op == SEND && ic->argreg != 1) + return NULL; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + if (bitVectnBitsOn (OP_USES (op)) > 1) + return NULL; + + /* if it has only one definition */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* if that only usage is a cast */ + if (dic->op == CAST) + { + /* to a bigger type */ + if (getSize (OP_SYM_TYPE (IC_RESULT (dic))) > getSize (OP_SYM_TYPE (IC_RIGHT (dic)))) + { + /* then we can not, since we cannot predict the usage of b & acc */ + return NULL; + } + } + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && !POINTER_SET (ic) && !POINTER_GET (ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + } + else + { + /* otherwise check that the definition does + not contain any symbols in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || IS_OP_RUONLY (IC_LEFT (ic)) || IS_OP_RUONLY (IC_RIGHT (ic))) + { + return NULL; + } + + /* if pointer set then make sure the pointer is one byte */ + if (POINTER_SET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + } + + /* Make sure no overlapping liverange is already assigned to DPTR */ + if (OP_SYMBOL (op)->clashes) + { + symbol *sym; + int i; + + for (i = 0; i < OP_SYMBOL (op)->clashes->size; i++) + { + if (bitVectBitValue (OP_SYMBOL (op)->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->ruonly) + return NULL; + } + } + } + + sic = dic; + + if (ic->op == SEND) + { + /* look for the call to extend following + far space search to include all parameters. + see bug 3004918 */ + for (; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + if (!ic) /* not found */ + return NULL; + } + + if (ic->op == PCALL && !IS_SMALL_PTR(aggrToPtr(operandType(IC_LEFT(ic)), FALSE))) + return NULL; + + /* make sure the intervening instructions + don't have anything in far space */ + for (dic = dic->next; dic && dic != ic && sic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && !IS_SMALL_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && getSize (operandType (op)) >= 3) + return NULL; + + /* if left or right or result is in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + isOperandInFarSpace (IC_RESULT (dic)) || + IS_OP_RUONLY (IC_LEFT (dic)) || IS_OP_RUONLY (IC_RIGHT (dic)) || IS_OP_RUONLY (IC_RESULT (dic))) + { + return NULL; + } + /* if left or right or result is on stack */ + if (isOperandOnStack (IC_LEFT (dic)) || isOperandOnStack (IC_RIGHT (dic)) || isOperandOnStack (IC_RESULT (dic))) + { + return NULL; + } + if (usingCarry) + { + if (isOperandInBitSpace (IC_LEFT (dic)) || + isOperandInBitSpace (IC_RIGHT (dic)) || + isOperandInBitSpace (IC_RESULT (dic))) + { + return NULL; + } + if (dic->op != SEND || dic->op != IPUSH || dic->op != '=') + { + return NULL; + } + } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype) || (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* isCommutativeOp - tests whether this op cares what order its */ +/* operands are in */ +/*-----------------------------------------------------------------*/ +bool +isCommutativeOp (unsigned int op) +{ + if (op == '+' || op == '*' || op == EQ_OP || op == '^' || op == '|' || op == BITWISEAND) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandUsesAcc - determines whether the code generated for this */ +/* operand will have to use the accumulator */ +/*-----------------------------------------------------------------*/ +bool +operandUsesAcc (operand * op, bool allowBitspace) +{ + if (!op) + return FALSE; + + if (IS_SYMOP (op)) + { + symbol *sym = OP_SYMBOL (op); + memmap *symspace; + + if (sym->accuse) + return TRUE; /* duh! */ + + if (IN_STACK (sym->etype) || sym->onStack || (SPIL_LOC (op) && SPIL_LOC (op)->onStack)) + return TRUE; /* acc is used to calc stack offset */ + + if (IS_ITEMP (op)) + { + if (SPIL_LOC (op)) + { + sym = SPIL_LOC (op); /* if spilled, look at spill location */ + } + else + { + return FALSE; /* more checks? */ + } + } + + symspace = SPEC_OCLS (sym->etype); + + if (sym->iaccess && symspace->paged) + return TRUE; /* must fetch paged indirect sym via accumulator */ + + if (!allowBitspace && IN_BITSPACE (symspace)) + return TRUE; /* fetching bit vars uses the accumulator */ + + if (IN_FARSPACE (symspace) || IN_CODESPACE (symspace)) + return TRUE; /* fetched via accumulator and dptr */ + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static void +packRegsForAccUse (iCode * ic) +{ + iCode *uic; + + /* if this is an aggregate, e.g. a one byte char array */ + if (IS_AGGREGATE (operandType (IC_RESULT (ic)))) + return; + + /* if we are calling a reentrant function that has stack parameters */ + if (ic->op == CALL && IFFUNC_ISREENT (operandType (IC_LEFT (ic))) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic)))) + return; + + if (ic->op == PCALL && + IFFUNC_ISREENT (operandType (IC_LEFT (ic))->next) && FUNC_HASSTACKPARM (operandType (IC_LEFT (ic))->next)) + return; + + /* if + or - then it has to be one byte result */ + if ((ic->op == '+' || ic->op == '-') && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* if shift operation make sure right side is not a literal */ + if (ic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (ic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (ic)) || getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (IS_BITWISE_OP (ic) && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) > 1) + return; + + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + return; + + if (ic->next != uic) + return; + + /* if it is a conditional branch then we definitely can */ + if (uic->op == IFX) + goto accuse; + + if (uic->op == JUMPTABLE) + return; + + if (POINTER_SET (uic) && getSize (aggrToPtr (operandType (IC_RESULT (uic)), FALSE)) > 1) + return; + + /* if the usage is not an assignment + or an arithmetic / bitwise / shift operation then not */ + if (uic->op != '=' && !IS_ARITHMETIC_OP (uic) && !IS_BITWISE_OP (uic) && uic->op != LEFT_OP && uic->op != RIGHT_OP) + return; + + /* if shift operation make sure right side is not a literal */ + /* WIML: Why is this? */ + if (uic->op == RIGHT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + if (uic->op == LEFT_OP && (isOperandLiteral (IC_RIGHT (uic)) || getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + /* make sure that the result of this icode is not on the + stack, since acc is used to compute stack offset */ +#if 0 + if (IS_TRUE_SYMOP (IC_RESULT (uic)) && OP_SYMBOL (IC_RESULT (uic))->onStack) + return; +#else + if (isOperandOnStack (IC_RESULT (uic))) + return; +#endif + + /* if the usage has only one operand then we can */ + if (IC_LEFT (uic) == NULL || IC_RIGHT (uic) == NULL) + goto accuse; + + /* if the other operand uses the accumulator then we cannot */ + if ((IC_LEFT (uic)->key == IC_RESULT (ic)->key && + operandUsesAcc (IC_RIGHT (uic), IS_BIT (operandType (IC_LEFT (uic))))) || + (IC_RIGHT (uic)->key == IC_RESULT (ic)->key && operandUsesAcc (IC_LEFT (uic), IS_BIT (operandType (IC_RIGHT (uic)))))) + return; + + /* make sure this is on the left side if not commutative */ + /* except for '-', which has been written to be able to + handle reversed operands */ + if (!(isCommutativeOp (ic->op) || ic->op == '-') && IC_LEFT (uic)->key != IC_RESULT (ic)->key) + return; + + /* Sign handling will overwrite a */ + if (uic->op == '*' && getSize (operandType (IC_RESULT (uic))) > 1 && + (!SPEC_USIGN (getSpec (operandType (IC_LEFT (uic)))) || !SPEC_USIGN (getSpec (operandType (IC_RIGHT (uic)))))) + return; + if ((uic->op == '/' || uic->op == '%') && + (!SPEC_USIGN (getSpec (operandType (IC_LEFT (uic)))) || !SPEC_USIGN (getSpec (operandType (IC_RIGHT (uic)))))) + return; + +#if 0 + // this is too dangerous and need further restrictions + // see bug #447547 + + /* if one of them is a literal then we can */ + if ((IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic))) || (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)))) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + return; + } +#endif + +accuse: + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; +} + +/*-----------------------------------------------------------------*/ +/* packForPush - heuristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock ** ebpp, int blockno) +{ + iCode *dic, *lic; + bitVect *dbv; + struct eBBlock *ebp = ebpp[blockno]; + int disallowHiddenAssignment = 0; + + if (ic->op != IPUSH || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* The changes in SDCCopt.c #7741 should correct the use info, making */ + /* this extra test redundant. */ + if (ic->parmPush) + {// find Send or other Push for this func call + for (lic = ic->next; lic && lic->op != CALL; lic = lic->next) + { + if ((lic->op == IPUSH || lic->op == SEND) && IS_ITEMP (IC_LEFT (lic))) + {// and check parameter is not passed again + symbol * parm = OP_SYMBOL (IC_LEFT (ic)); + symbol * other = OP_SYMBOL (IC_LEFT (lic)); + if (other == parm) + return; + } + } + } + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + if (dic->seq < ebp->fSeq) // Evelyn did this + { + int i; + for (i = 0; i < blockno; i++) + { + if (dic->seq >= ebpp[i]->fSeq && dic->seq <= ebpp[i]->lSeq) + { + ebp = ebpp[i]; + break; + } + } + wassert (i != blockno); // no way to recover from here + } + + if (IS_SYMOP (IC_RIGHT (dic))) + { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS (IC_RIGHT (dic)); + for (lic = ic; lic && lic != dic; lic = lic->prev) + { + if (bitVectBitValue (dbv, lic->key)) + return; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC (operandType (IC_LEFT (ic)))) + { + sym_link *itype = operandType (IC_LEFT (ic)); + sym_link *ditype = operandType (IC_RIGHT (dic)); + + if (SPEC_USIGN (itype) != SPEC_USIGN (ditype) || SPEC_LONG (itype) != SPEC_LONG (ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL (IC_RIGHT (dic))->liveTo < ic->seq) + { + OP_SYMBOL (IC_RIGHT (dic))->liveTo = ic->seq; + } + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + /* now we know that it has one & only one def & use + and the that the definition is an assignment */ + ReplaceOpWithCheaperOp (&IC_LEFT (ic), IC_RIGHT (dic)); + remiCodeFromeBBlock (ebp, dic); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock ** ebpp, int blockno) +{ + iCode *ic; + int change = 0; + eBBlock *ebp = ebpp[blockno]; + + do + { + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TrueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + } + while (change); + + for (ic = ebp->sch; ic; ic = ic->next) + { + /* Fix for bug #979599: */ + /* P0 &= ~1; */ + + /* Look for two subsequent iCodes with */ + /* iTemp := _c; */ + /* _c = iTemp & op; */ + /* and replace them by */ + /* iTemp := _c; */ + /* _c = _c & op; */ + if ((ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') && + ic->prev && + ic->prev->op == '=' && + (IS_ITEMP (IC_LEFT (ic)) && isOperandEqual (IC_LEFT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)) || + IS_ITEMP (IC_RIGHT (ic)) && isOperandEqual (IC_RIGHT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)))) + { + bool left = IS_ITEMP (IC_LEFT (ic)) && isOperandEqual (IC_LEFT (ic), IC_RESULT (ic->prev)) && isOperandEqual (IC_RESULT (ic), IC_RIGHT (ic->prev)); + + iCode *ic_prev = ic->prev; + symbol *prev_result_sym = OP_SYMBOL (IC_RESULT (ic_prev)); + + ReplaceOpWithCheaperOp (left ? &IC_LEFT (ic) : &IC_RIGHT (ic), IC_RESULT (ic)); + if (IC_RESULT (ic_prev) != (left ? IC_RIGHT (ic) : IC_LEFT (ic))) + { + bitVectUnSetBit (OP_USES (IC_RESULT (ic_prev)), ic->key); + if ( /*IS_ITEMP (IC_RESULT (ic_prev)) && */ + prev_result_sym->liveTo == ic->seq) + { + prev_result_sym->liveTo = ic_prev->seq; + } + } + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + + bitVectSetBit (ic->rlive, IC_RESULT (ic)->key); + + if (bitVectIsZero (OP_USES (IC_RESULT (ic_prev)))) + { + bitVectUnSetBit (ic->rlive, IC_RESULT (ic)->key); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic_prev)), ic_prev->key); + remiCodeFromeBBlock (ebp, ic_prev); + hTabDeleteItem (&iCodehTab, ic_prev->key, ic_prev, DELETE_ITEM, NULL); + } + } + + /* if this is an itemp & result of an address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode) && + !isOperandGlobal (IC_RESULT (ic)) && /* due to bug 1618050 */ + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + sym_link *to_type = operandType (IC_LEFT (ic)); + sym_link *from_type = operandType (IC_RIGHT (ic)); + if (IS_PTR (to_type) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerializable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic))) + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + + if (!SKIP_IC2 (ic)) + { + /* if we are using a symbol on the stack + then we should say mcs51_ptrRegReq */ + if (options.useXstack && ic->parmPush && (ic->op == IPUSH || ic->op == IPOP)) + mcs51_ptrRegReq++; + if (ic->op == IFX && IS_SYMOP (IC_COND (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_COND (ic))->onStack || + OP_SYMBOL (IC_COND (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_COND (ic))->etype) == idata) ? 1 : 0); + else if (ic->op == JUMPTABLE && IS_SYMOP (IC_JTCOND (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_JTCOND (ic))->onStack || + OP_SYMBOL (IC_JTCOND (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_JTCOND (ic))->etype) == idata) ? 1 : 0); + else + { + if (IS_SYMOP (IC_LEFT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_LEFT (ic))->onStack || + OP_SYMBOL (IC_LEFT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype) == idata) ? 1 : 0); + if (IS_SYMOP (IC_RIGHT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_RIGHT (ic))->onStack || + OP_SYMBOL (IC_RIGHT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_RIGHT (ic))->etype) == idata) ? 1 : 0); + if (IS_SYMOP (IC_RESULT (ic))) + mcs51_ptrRegReq += ((OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess || + SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype) == idata) ? 1 : 0); + if (POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) + && getSize (OP_SYMBOL (IC_LEFT (ic))->type) <= (unsigned int) NEARPTRSIZE) + mcs51_ptrRegReq++; + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) + && getSize (OP_SYMBOL (IC_RESULT (ic))->type) <= (unsigned int) NEARPTRSIZE) + mcs51_ptrRegReq++; + } + } + + /* if the condition of an if instruction + is defined in the previous instruction and + this is the only usage then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + (IS_BITWISE_OP (ic) && isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* if the condition of an if instruction + is defined in the previous GET_POINTER instruction and + this is the only usage then + mark the itemp as accumulator use */ + if ((POINTER_GET (ic) && getSize (operandType (IC_RESULT (ic))) <= 1) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + continue; + } + + /* reduce for support function calls */ + if (ic->supportRtn || ic->op == '+' || ic->op == '-') + packRegsForSupport (ic, ebp); + + /* some cases the redundant moves can + can be eliminated for return statements */ + if ((ic->op == RETURN || (ic->op == SEND && ic->argreg == 1)) && + !isOperandInFarSpace (IC_LEFT (ic)) && (options.model == MODEL_SMALL || options.model == MODEL_MEDIUM)) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* if pointer set & left has a size more than + one and right is not in far space */ + if (POINTER_SET (ic) && + IS_SYMOP (IC_RESULT (ic)) && + !isOperandInFarSpace (IC_RIGHT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_RESULT (ic), ebp); + } + + /* if pointer get */ + if (POINTER_GET (ic) && + IS_SYMOP (IC_LEFT (ic)) && + !isOperandInFarSpace (IC_RESULT (ic)) && + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && + getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* if this is a cast for integral promotion then + check if it's the only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) + { + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType) && SPEC_USIGN (fromType) == SPEC_USIGN (toType)) + { + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) + { + if (IS_ARITHMETIC_OP (dic)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + else + { + OP_SYMBOL (IC_RIGHT (ic))->ruonly = 0; + } + } + } + else + { + /* if the type from and type to are the same + then if this is the only use then pack it */ + if (compareType (operandType (IC_RIGHT (ic)), operandType (IC_LEFT (ic))) == 1) + { + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) + { + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + ReplaceOpWithCheaperOp (&IC_RESULT (dic), IC_RESULT (ic)); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + } + } + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH) + { + packForPush (ic, ebpp, blockno); + } + + /* pack registers for accumulator use, when the + result of an arithmetic or bit wise operation + has only one use, that use is immediately following + the definition and the using iCode has only one + operand or has two operands but one is literal & + the result of that operation is not on stack then + we can leave the result of this operation in acc:b + combination */ + if ((IS_ARITHMETIC_OP (ic) + || IS_CONDITIONAL (ic) + || IS_BITWISE_OP (ic) + || ic->op == LEFT_OP || ic->op == RIGHT_OP || ic->op == CALL + || ic->op == '=' && !POINTER_SET(ic) && getSize (operandType (IC_RESULT (ic))) < 2 + || (ic->op == ADDRESS_OF && isOperandOnStack (IC_LEFT (ic)))) && + IS_ITEMP (IC_RESULT (ic)) && getSize (operandType (IC_RESULT (ic))) <= 2) + { + packRegsForAccUse (ic); + } + } +} + +/*------------------------------------------------------------------------*/ +/* positionRegsReverse - positioning registers from end to begin to avoid */ +/* conflict among result, left and right operands in some extrem cases */ +/*------------------------------------------------------------------------*/ +static void +positionRegsReverse (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + + for (i = count - 1; i >= 0; i--) + for (ic = ebbs[i]->ech; ic; ic = ic->prev) + { + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic)), 1); + } + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs && + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_RESULT (ic))->nRegs) + { + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic)), 1); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +mcs51_assignRegisters (ebbIndex * ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.regAssigned); + setToNull ((void *) &_G.totRegAssigned); + mcs51_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + if ((currFunc && IFFUNC_ISREENT (currFunc->type)) || options.stackAuto) + { + mcs51_nRegs = 16; + } + else + { + mcs51_nRegs = 8; + } + _G.allBitregs = findAllBitregs (); + _G.allBankregs = findAllBankregs (); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + + for (i = 0; i < count; i++) + packRegisters (ebbs, i); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (*ebbs); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + freeAllRegs (); + //setToNull ((void *) &_G.regAssigned); + //setToNull ((void *) &_G.totRegAssigned); + fillGaps (); + + /* do positionRegs() for all ICs from end to begin */ + positionRegsReverse (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + /* make sure r0 & r1 are flagged as used if they might be used */ + /* as pointers */ + if (currFunc && mcs51_ptrRegReq) + { + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R0_IDX); + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, R1_IDX); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* do the overlaysegment stuff SDCCmem.c */ + doOverlays (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + gen51Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} diff --git a/src/mcs51/ralloc.h b/src/mcs51/ralloc.h new file mode 100644 index 0000000..10b86e1 --- /dev/null +++ b/src/mcs51/ralloc.h @@ -0,0 +1,81 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +enum +{ + R7_IDX = 0, R6_IDX, R5_IDX, R4_IDX, + R3_IDX, R2_IDX, R1_IDX, R0_IDX, + B0_IDX, B1_IDX, B2_IDX, B3_IDX, + B4_IDX, B5_IDX, B6_IDX, B7_IDX, + X8_IDX, X9_IDX, X10_IDX, X11_IDX, + X12_IDX, CND_IDX, + DPL_IDX, DPH_IDX, B_IDX, A_IDX, + END_IDX +}; + + +#define REG_PTR 0x01 +#define REG_GPR 0x02 +#define REG_CND 0x04 +#define REG_BIT 0x08 +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_BIT, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + short otype; + char *name; /* name */ + char *dname; /* name when direct access needed */ + char *base; /* base address */ + short offset; /* offset from the base */ + unsigned isFree:1; /* is currently unassigned */ + + struct + { + unsigned valueKnown:1; + unsigned char value; /* only valid when valueKnown is set */ + char *symbol; /* holds symbol if value is known by symbol */ + } + rtrack; +} +reg_info; + +extern reg_info regs8051[]; + +reg_info *mcs51_regWithIdx (int); + +bitVect *mcs51_rUmaskForOp (operand * op); +bitVect *mcs51_allBitregs (void); +bitVect *mcs51_allBankregs (void); + +extern int mcs51_ptrRegReq; +extern int mcs51_nRegs; + +#endif diff --git a/src/mcs51/rtrack.c b/src/mcs51/rtrack.c new file mode 100644 index 0000000..13fc5b2 --- /dev/null +++ b/src/mcs51/rtrack.c @@ -0,0 +1,1200 @@ +/*------------------------------------------------------------------------- + rtrack.c - tracking content of registers on an mcs51 + + Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +-------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------- + Status: + - passes regression test suite, still bugs are likely + - only active if environment variable SDCC_REGTRACK is set + + Missed opportunities: + - does not track offsets to symbols as in "mov dptr,#(_my_int + 2)" + - only used for moves to acc or dptr so chances to use: + "inc r2", "mov r2,a" would not be taken + - a label causes loss of tracking (no handling of information of blocks + known to follow/preceed the current block) + - not used in aopGet or genRet + - SFRX (__xdata volatile unsigned char __at(addr)) not handled as value + - does not track which registers are known to be unchanged within + a function (would not have to be saved when calling the function) +-------------------------------------------------------------------------*/ + + +#include +#include +#include "SDCCglobl.h" + +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "rtrack.h" + + +#define D(x) do if (options.verboseAsm) {x;} while(0) +#define DD(x) do if (options.verboseAsm && enableextraverbose) {x;} while(0) + + +/* move this (or rtrackGetLit() and rtrackMoveALit() + elsewhere? stealing emitcode from gen.c */ +void emitcode (const char *inst, const char *fmt,...); + + +static int enable = +1; +static int enableextraverbose = -1; + + +static unsigned int rx_num_to_idx (const unsigned int num) +{ + const unsigned int regidx[8] = + { R7_IDX, R6_IDX, R5_IDX, R4_IDX, R3_IDX, R2_IDX, R1_IDX, R0_IDX }; + + assert( 7 >= num ); + + return regidx [num & 0x7]; +} + + +static void rtrack_data_unset (const unsigned int idx) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + if (regs8051[idx].rtrack.symbol || regs8051[idx].rtrack.valueKnown) + { + DD(emitcode (";", "\t%s=?", regs8051[idx].name);); + } + + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + } + + memset (®s8051[idx].rtrack, 0, sizeof regs8051[idx].rtrack); +} + + +static void rtrack_data_set_val (const unsigned int idx, const unsigned char value) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + regs8051[idx].rtrack.value = value; + regs8051[idx].rtrack.valueKnown = 1; + + /* in case it was set by symbol, unset symbol */ + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + regs8051[idx].rtrack.symbol = NULL; + } + + DD(emitcode (";", "\t%s=#0x%02x", + regs8051[idx].name, + regs8051[idx].rtrack.value);); +} + + +static void rtrack_data_set_symbol (const unsigned int idx, const char * const symbol) +{ + assert (idx >= 0); + assert (idx < END_IDX); + + /* in case it was set by value, unset value */ + regs8051[idx].rtrack.value = 0; + regs8051[idx].rtrack.valueKnown = 0; + + /* eventually free a previous symbol */ + if (regs8051[idx].rtrack.symbol) + { + Safe_free (regs8051[idx].rtrack.symbol); + } + regs8051[idx].rtrack.symbol = Safe_strdup(symbol); + + DD(emitcode (";", "\t%s=#%s", + regs8051[idx].name, + regs8051[idx].rtrack.symbol);); +} + + +static int rtrack_data_is_same (const unsigned int idxdst, const unsigned int idxsrc) +{ + return ((regs8051[idxdst].rtrack.valueKnown && regs8051[idxsrc].rtrack.valueKnown) && + (regs8051[idxdst].rtrack.value == regs8051[idxsrc].rtrack.value)) || + ((regs8051[idxdst].rtrack.symbol && regs8051[idxsrc].rtrack.symbol) && + !strcmp (regs8051[idxdst].rtrack.symbol, regs8051[idxsrc].rtrack.symbol)); +} + + +static void rtrack_data_copy_dst_src (const unsigned int idxdst, const unsigned int idxsrc) +{ + assert (idxdst >= 0); + assert (idxdst < END_IDX); + assert (idxsrc >= 0); + assert (idxsrc < END_IDX); + + DD + ( + if ((NULL != regs8051[idxsrc].rtrack.symbol) || regs8051[idxsrc].rtrack.valueKnown) + { + emitcode (";", "\t%s=%s", regs8051[idxdst].name, regs8051[idxsrc].name); + } + else if (regs8051[idxdst].rtrack.symbol || regs8051[idxdst].rtrack.valueKnown) + { + emitcode (";", "\t%s=*",regs8051[idxdst].name); + } + + if (rtrack_data_is_same (idxdst, idxsrc)) + { + emitcode (";", "genFromRTrack redundant?"); + } + ); + + /* mov a, acc */ + if (idxsrc == idxdst) + return; + + regs8051[idxdst].rtrack.valueKnown = regs8051[idxsrc].rtrack.valueKnown; + regs8051[idxdst].rtrack.value = regs8051[idxsrc].rtrack.value; + + if (regs8051[idxdst].rtrack.symbol) + { + Safe_free (regs8051[idxdst].rtrack.symbol); + regs8051[idxdst].rtrack.symbol = NULL; + } + + memcpy (®s8051[idxdst].rtrack, ®s8051[idxsrc].rtrack, sizeof regs8051[idxdst].rtrack); + + if (regs8051[idxsrc].rtrack.symbol) + { + regs8051[idxdst].rtrack.symbol = Safe_strdup(regs8051[idxsrc].rtrack.symbol); + } +} + + +static void dumpAll() +{ + DD + ( + unsigned int i; + unsigned int column = 0; + char s[512]; + + s[0] = 0; + for (i = 0; i < END_IDX; i++) + { + if (regs8051[i].rtrack.valueKnown) + { + column += sprintf(s + column, "%s%s:#0x%02x", + column?" ":"", regs8051[i].name, regs8051[i].rtrack.value); + } + if (NULL != regs8051[i].rtrack.symbol) + { + column += sprintf(s + column, "%s%s:#%s", + column?" ":"", regs8051[i].name, regs8051[i].rtrack.symbol); + } + if (column>160) + { + strcpy (&s[157], "..."); + break; + } + } + emitcode (";", "\t%s", s); + ); +} + + +static void invalidateAllRx() +{ + unsigned int i; + for (i = 0; i <= 7; i++) + { + rtrack_data_unset (rx_num_to_idx (i)); + } +} + + +static void invalidateAll() +{ + invalidateAllRx(); + + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + rtrack_data_unset (B_IDX); + rtrack_data_unset (A_IDX); +} + + +static int regidxfromregname (const char* const s) +{ + unsigned int i; + + for (i = 0; i < END_IDX; i++) + { + if (regs8051[i].name) + if (!strncmp (s, regs8051[i].name, strlen(regs8051[i].name))) + return i; + + if (regs8051[i].dname) + if (!strncmp (s, regs8051[i].dname, strlen(regs8051[i].dname))) + return i; + } + + return -1; +} + + +static int valuefromliteral (const char* const s) +{ + char* tmp = NULL; + int value; + + if (strncmp (s, "0x", 2)) + return -1; + + value = strtol (s + 2, &tmp, 16); + if (s != tmp) + return value; + + return -1; +} + + +/* tracking values within registers by looking + at the line passed to the assembler. + Tries to keep regs8051[] up to date */ +bool _mcs51_rtrackUpdate (const char *line) +{ + bool modified = false; + + if (enable == -1) + enable = (NULL != getenv("SDCC_REGTRACK")); + + if (enableextraverbose == -1) + enableextraverbose = (NULL != getenv("SDCC_REGTRACK_VERBOSE")); + + if (!enable || + *line == ';' || /* comment */ + (NULL != strstr( line, "==."))) /* dirty check for _G.debugLine */ + return false; /* nothing to do */ + + dumpAll (); + + if (!strncmp (line, "mov", 3)) + { + if (!strncmp (line, "movc\ta", 6) || + !strncmp (line, "movx\ta", 6)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* mov to register (r0..r7, dpl, dph, a, b)*/ + if (!strncmp (line, "mov\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + + if (0 <= regIdx) + { + char *argument = strstr (line, ",") + 1; + char *s; + int value; + + value = strtol (argument + 1, &s, 16); + + /* check literal mov to register */ + if ((s != argument + 1) && !strncmp (argument, "#0x", 3)) + { + D + ( + if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value)) + { + emitcode (";", "genFromRTrack removed\t%s", line); + modified = true; + } + if (regs8051[A_IDX].rtrack.valueKnown && (value == regs8051[A_IDX].rtrack.value) && + (regIdx != A_IDX) && (regIdx != DPL_IDX) && (regIdx != DPH_IDX)) + /* ignore DPL/DPH for now as peephole rule for MOV DPTR is much better */ + { + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "%s,a", regs8051[regIdx].dname); + modified = true; + } + else if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value + 1) && + ((regIdx != A_IDX) || (0xff != regs8051[regIdx].rtrack.value))) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("inc", "%s", regs8051[regIdx].name); + modified = true; + } + else if (regs8051[regIdx].rtrack.valueKnown && (value == regs8051[regIdx].rtrack.value - 1) && + ((regIdx != A_IDX) || (0x01 != regs8051[regIdx].rtrack.value))) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("dec", "%s", regs8051[regIdx].name); + modified = true; + } + ); + + rtrack_data_set_val (regIdx, (unsigned char) value); + } + /* check literal mov of symbol to register */ + else if (!strncmp (argument, "#", 1)) + { + rtrack_data_set_symbol (regIdx, argument + 1); + } + /* check mov from register to register */ + else if (0 <= regidxfromregname (argument)) + { + rtrack_data_copy_dst_src (regIdx, regidxfromregname (argument)); + } + else + { + /* mov acc.7,c and the likes */ + rtrack_data_unset (regIdx); + } + return modified; + } + } + + /* mov to psw can change register bank */ + if (!strncmp (line, "mov\tpsw,", 8)) + { + invalidateAllRx (); + return false; + } + + /* tracking dptr */ + /* literal number 16 bit */ + if (!strncmp (line, "mov\tdptr,#0x", 12)) + { + char* s; + int value = strtol (line + 10, &s, 16); + if( s != line + 10 ) + { + if (options.verboseAsm) + { + bool foundshortcut = 0; + + if ( regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown && + (regs8051[DPH_IDX].rtrack.value == (value >> 8)) && + (regs8051[DPL_IDX].rtrack.value == (value & 0xff))) + { + emitcode (";", "genFromRTrack removed\t%s", line); + foundshortcut = 1; + modified = true; + } + + if (!foundshortcut && + regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + /* some instructions are shorter than mov dptr,#0xabcd */ + const struct + { + int offset; + const char* inst; + const char* parm; + } reachable[6] = + { + { 1, "inc", "dptr"}, + { 256, "inc", "dph"}, + {-256, "dec", "dph"}, + {-255, "inc", "dpl"}, /* if overflow */ + { -1, "dec", "dpl"}, /* if no overflow */ + { 255, "dec", "dpl"} /* if overflow */ + }; + + unsigned int dptr = (regs8051[DPH_IDX].rtrack.value << 8 ) | + regs8051[DPL_IDX].rtrack.value; + unsigned int i; + + for (i = 0; i < 6; i++) + { + if (dptr + reachable[i].offset == value) + { + /* check if an overflow would occur */ + if ((i == 3) && ((dptr & 0xff) != 0xff)) continue; + if ((i == 4) && ((dptr & 0xff) == 0x00)) continue; + if ((i == 5) && ((dptr & 0xff) != 0x00)) continue; + + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode (reachable[i].inst, "%s", reachable[i].parm); + modified = true; + foundshortcut = 1; + + break; + } + }; + } + + if (!foundshortcut && + regs8051[DPH_IDX].rtrack.valueKnown && + (regs8051[DPH_IDX].rtrack.value == (value >> 8))) + { + char s[32]; + sprintf (s, "#0x%02x", value & 0xff); + + if (s != rtrackGetLit(s)) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "dpl,%s", rtrackGetLit (s)); + modified = true; + foundshortcut = 1; + } + } + if (!foundshortcut && + regs8051[DPL_IDX].rtrack.valueKnown && + (regs8051[DPL_IDX].rtrack.value == (value & 0xff))) + { + char s[32]; + sprintf (s, "#0x%02x", value >> 8); + + if (s != rtrackGetLit (s)) + { + /* does not occur in regression test mcs51-small */ + emitcode (";", "genFromRTrack replaced\t%s", line); + emitcode ("mov", "dph,%s", rtrackGetLit (s)); + modified = true; + foundshortcut = 1; + } + } + } + + rtrack_data_set_val (DPH_IDX, (unsigned char) (value >> 8)); + rtrack_data_set_val (DPL_IDX, (unsigned char) value); + return modified; + } + } + /* literal symbol 16 bit */ + else if (!strncmp (line, "mov\tdptr,#", 10)) + { + char* s = Safe_alloc (strlen (line) + strlen ("( >> 8)")); + + strcat (s, "("); + strcat (s, &line[10]); + strcat (s, " >> 8)"); + + rtrack_data_set_symbol (DPH_IDX, s); + rtrack_data_set_symbol (DPL_IDX, &line[10]); + + Safe_free (s); + return false; + } + else if (!strncmp (line, "mov\tdptr", 8)) + { + /* unidentified */ + rtrack_data_unset (DPH_IDX); + rtrack_data_unset (DPL_IDX); + return false; + } + + /* move direct to symbol */ + if (!strncmp (line, "mov\t_", 5) || + !strncmp (line, "mov\t(", 5)) + { + char* argument = strstr (line, ",") + 1; + + if (argument && !strncmp (argument, "#0x", 3)) + { + char s[8] = {0}; + + strncpy ((void *)&s, argument, strlen ("#0xab")); + + /* could we get it from a, r0..r7? */ + if (s != rtrackGetLit (s)) + { + int lengthuptoargument = argument - (line + 4); + emitcode (";", "1-genFromRTrack replaced\t%s", line); + emitcode ("mov", "%.*s%s", + lengthuptoargument, + line + 4, + rtrackGetLit (s)); + modified = true; + } + } + return modified; + } + + /* no tracking of SP, so we do not care */ + if (!strncmp (line, "mov\tsp,", 7)) + return false; + + /* mov to xdata or pdata memory does not change registers */ + if (!strncmp (line, "movx\t@", 6)) + return false; + + /* mov to idata memory might change registers r0..r7 + but unless there is a stack problem + compiler generated code does not do idata + writes to 0x00..0x1f? */ + if (!strncmp (line, "mov\t@", 5)) + { + /* a little too paranoid? */ + invalidateAllRx (); + return false; + } + } + + /* no tracking of SP */ + if (!strncmp (line, "push", 4)) + return false; + + if (!strncmp (line, "pop\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + rtrack_data_unset (regIdx); + } + return false; + } + + if (!strncmp (line, "inc", 3)) + { + if (!strcmp (line, "inc\tdptr")) + { + if (regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + int val = (regs8051[DPH_IDX].rtrack.value << 8) | regs8051[DPL_IDX].rtrack.value; + val += 1; + rtrack_data_set_val (DPL_IDX, (unsigned char) val); + rtrack_data_set_val (DPH_IDX, (unsigned char) (val >> 8)); + } + else + { + /* not yet handling offset to a symbol. Invalidating. So no inc dptr for: + __xdata char array[4]; array[0] = 0; array[1] = 0; array[2] = 0; + (If an offset to the respective linker segment would be + available then additionally + __xdata int a = 123; __xdata int b = 456; __xdata c= 'a'; + could be 4 bytes shorter) */ + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + } + return false; + } + if (!strncmp (line, "inc\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + if (regs8051[regIdx].rtrack.valueKnown) + rtrack_data_set_val (regIdx, (unsigned char) (regs8051[regIdx].rtrack.value + 1)); + else + /* explicitely unsetting (could be known by symbol). + not yet handling offset to a symbol. (idata/pdata) */ + rtrack_data_unset (regIdx); + + return false; + } + } + return false; + } + + /* some bit in acc is cleared + MB: I'm too lazy to find out which right now */ + if (!strncmp (line, "jbc\tacc", 7)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* unfortunately the label typically following these + will cause loss of tracking */ + if (!strncmp (line, "jc\t", 3) || + !strncmp (line, "jnc\t", 4) || + !strncmp (line, "jb\t", 3) || + !strncmp (line, "jnb\t", 4) || + !strncmp (line, "jbc\t", 4)) + return false; + + /* if branch not taken in "cjne r2,#0x08,somewhere" + r2 is known to be 8 */ + if (!strncmp (line, "cjne\t", 5)) + { + int regIdx = regidxfromregname (line + 5); + if (0 <= regIdx) + { + char *argument = strstr (line, ",") + 1; + char *s; + int value; + + value = strtol (argument + 1, &s, 16); + + /* check literal compare to register */ + if ((s != argument + 1) && !strncmp (argument, "#0x", 3)) + { + rtrack_data_set_val (regIdx, (unsigned char) value); + return false; + } + rtrack_data_unset (regIdx); + } + return false; + } + + /* acc eventually known to be zero */ + if (!strncmp (line, "jz\t", 3)) + return false; + + /* acc eventually known to be zero */ + if (!strncmp (line, "jnz\t", 4)) + { + rtrack_data_set_val (A_IDX, 0x00); // branch not taken + return false; + } + + if (!strncmp (line, "djnz\t", 5)) + { + int regIdx = regidxfromregname (line + 5); + if (0 <= regIdx) + { + rtrack_data_set_val (regIdx, 0x00); // branch not taken + return false; + } + } + + /* only carry bit, so we do not care */ + if (!strncmp (line, "setb\tc", 6) || + !strncmp (line, "clr\tc", 5) || + !strncmp (line, "cpl\tc", 5)) + return false; + + /* operations on acc which depend on PSW */ + if (!strncmp (line, "addc\ta,", 7)|| + !strncmp (line, "subb\ta,", 7)|| + !strncmp (line, "da\ta", 4) || + !strncmp (line, "rlc\ta", 5) || + !strncmp (line, "rrc\ta", 5)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* bitwise operations on acc */ + if (!strncmp (line, "setb\ta", 6) || + !strncmp (line, "clrb\ta", 6)) + { + rtrack_data_unset (A_IDX); + return false; + } + + /* other operations on acc that can be tracked */ + if (!strncmp (line, "add\ta,", 6) || + !strncmp (line, "anl\ta,", 6) || + !strncmp (line, "orl\ta,", 6) || + !strncmp (line, "xrl\ta,", 6) || + !strcmp (line, "cpl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + { + if (!strncmp (line, "add\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value + regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value + valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "anl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value & regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value & valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "orl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value | regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value | valuefromliteral (line + 7))); + return false; + } + } + + if (!strncmp (line, "xrl\ta,", 6)) + { + int regIdx = regidxfromregname (line + 6); + + if (0 <= regIdx && regs8051[regIdx].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ regs8051[regIdx].rtrack.value)); + return false; + } + else if (('#' == line[6]) && (0 <= valuefromliteral (line + 7))) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ valuefromliteral (line + 7))); + return false; + } + } + + if (!strcmp (line, "cpl\ta")) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value ^ 0xff)); + return false; + } + + rtrack_data_unset (A_IDX); + return false; + } + else + { + rtrack_data_unset (A_IDX); + return false; + } + } + + if (!strncmp (line, "dec\t", 4)) + { + int regIdx = regidxfromregname (line + 4); + if (0 <= regIdx) + { + if (regs8051[regIdx].rtrack.valueKnown) + rtrack_data_set_val (regIdx, (unsigned char) (regs8051[regIdx].rtrack.value - 1)); + + /* not handling offset to a symbol. invalidating if needed */ + if (NULL != regs8051[regIdx].rtrack.symbol) + rtrack_data_unset (regIdx); + + return false; + } + return false; + } + + if (!strcmp (line, "clr\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown && (0 == regs8051[A_IDX].rtrack.value)) + { + emitcode (";", "genFromRTrack removed\t%s", line); + modified = true; + } + rtrack_data_set_val (A_IDX, 0); + return modified; + } + + if (!strcmp (line, "cpl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) (~regs8051[A_IDX].rtrack.value)); + else + /* in case a holds a symbol */ + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "rl\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value<<1) | + (regs8051[A_IDX].rtrack.value>>7))); + else + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "rr\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value>>1) | + (regs8051[A_IDX].rtrack.value<<7))); + else + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "swap\ta")) + { + if (regs8051[A_IDX].rtrack.valueKnown) + rtrack_data_set_val (A_IDX, (unsigned char) ((regs8051[A_IDX].rtrack.value>>4) | + (regs8051[A_IDX].rtrack.value<<4))); + else + rtrack_data_unset (A_IDX); + return false; + } + + if (!strncmp (line, "mul\t", 4)) + { + if (regs8051[A_IDX].rtrack.valueKnown && regs8051[B_IDX].rtrack.valueKnown) + { + unsigned int value = (unsigned int)regs8051[A_IDX].rtrack.value * + (unsigned int)regs8051[B_IDX].rtrack.value; + + rtrack_data_set_val (A_IDX, (unsigned char) value); + rtrack_data_set_val (B_IDX, (unsigned char) (value >> 8)); + } + else + { + rtrack_data_unset (A_IDX); + rtrack_data_unset (B_IDX); + } + return false; + } + + if (!strncmp (line, "div\t", 4)) + { + if (regs8051[A_IDX].rtrack.valueKnown && regs8051[B_IDX].rtrack.valueKnown) + { + rtrack_data_set_val (A_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value / regs8051[B_IDX].rtrack.value)); + rtrack_data_set_val (B_IDX, (unsigned char) (regs8051[A_IDX].rtrack.value % regs8051[B_IDX].rtrack.value)); + } + else + { + rtrack_data_unset (A_IDX); + rtrack_data_unset (B_IDX); + } + return false; + } + + /* assuming these library functions have no side-effects */ + if (!strncmp (line, "lcall", 5)) + { + if (!strcmp (line, "lcall\t__gptrput")) + { + /* invalidate R0..R7 because they might have been changed */ + /* MB: too paranoid ? */ + //invalidateAllRx(); + return false; + } + if (!strcmp (line, "lcall\t__gptrget")) + { + rtrack_data_unset (A_IDX); + return false; + } + if (!strcmp (line, "lcall\t__decdptr")) + { + if (regs8051[DPH_IDX].rtrack.valueKnown && + regs8051[DPL_IDX].rtrack.valueKnown) + { + int val = (regs8051[DPH_IDX].rtrack.value << 8) | regs8051[DPL_IDX].rtrack.value; + val -= 1; + rtrack_data_set_val (DPL_IDX, (unsigned char) val); + rtrack_data_set_val (DPH_IDX, (unsigned char) (val >> 8)); + } + else + { + rtrack_data_unset (DPL_IDX); + rtrack_data_unset (DPH_IDX); + } + return false; + } + /* if callee_saves */ + } + + if (!strncmp (line, "xch\ta,", 6)) + { + /* handle xch from register (r0..r7, dpl, dph, b) */ + int regIdx = regidxfromregname (line + 6); + if (0 <= regIdx) + { + void* swap = Safe_malloc (sizeof regs8051[A_IDX].rtrack); + + memcpy (swap, ®s8051[A_IDX].rtrack, sizeof regs8051[A_IDX].rtrack); + memcpy (®s8051[A_IDX ].rtrack, ®s8051[regIdx].rtrack, sizeof regs8051[A_IDX].rtrack); + memcpy (®s8051[regIdx].rtrack, swap, sizeof regs8051[A_IDX].rtrack); + + Safe_free (swap); + return false; + } + } + + /* all others unrecognized, invalidate */ + invalidateAll(); + return false; +} + + +/* expects f.e. "#0x01" and returns either "#0x01" + if the value is not known to be within registers + or "a" or "r0".."r7". + (mov a,r7 or add a,r7 need one byte whereas + mov a,#0x01 or add a,#0x01 would take two + */ +char * rtrackGetLit(const char *x) +{ + unsigned int i; + + char *s; + + if (enable != 1) + return (char *)x; + + /* was it a numerical literal? */ + if (*x == '#') + { + int val = strtol (x+1, &s, 16); + if (x+1 != s) + { + /* try to get from acc */ + reg_info *r = ®s8051[A_IDX]; + if (r->rtrack.valueKnown && + r->rtrack.value == val) + { + D(emitcode (";", "genFromRTrack 0x%02x==%s", val, r->name)); + return r->name; + } + /* try to get from register R0..R7 */ + for (i = 0; i < 8; i++) + { + reg_info *r = ®s8051[rx_num_to_idx(i)]; + if (r->rtrack.valueKnown && + r->rtrack.value == val) + { + D(emitcode (";", "genFromRTrack 0x%02x==%s", val, r->name)); + return r->name; + } + } + } + else + { + /* probably a symbolic literal as in "mov r3,#(_i+1)", + not handled... */ + } + } + + return (char *)x; +} + +/* Similar to the above function + As the destination is the accumulator try harder yet and + try to generate the result with arithmetic operations */ +int rtrackMoveALit (const char *x) +{ + + if (enable != 1) + return 0; + + /* if it is a literal mov try to get it cheaper */ + if ( *x == '#' ) + { + reg_info *a = ®s8051[A_IDX]; + + char *s; + int val = strtol (x+1, &s, 16); + + /* was it a numerical literal? */ + if (x+1 != s) + { + /* prefer mov a,#0x00 */ + if (val == 0 && + ((a->rtrack.valueKnown && a->rtrack.value != 0) || + !a->rtrack.valueKnown)) + { + /* peepholes convert to clr a */ + /* (regression test suite is slightly larger if "clr a" is used here) */ + emitcode ("mov", "a,#0x00"); + return 1; + } + + if (a->rtrack.valueKnown) + { + /* already there? */ + if (val == a->rtrack.value) + { + D(emitcode (";", "genFromRTrack acc==0x%02x", a->rtrack.value)); + return 1; + } + + /* can be calculated with an instruction + that does not change flags from acc itself? */ + if (val == ((a->rtrack.value+1) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x+1", val, a->rtrack.value)); + emitcode ("inc", "a"); + return 1; + } + if (val == ((a->rtrack.value-1) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x-1", val, a->rtrack.value)); + emitcode ("dec", "a"); + return 1; + } + if (val == ((~a->rtrack.value) & 0xff) ) + { + D(emitcode (";", "genFromRTrack 0x%02x==~0x%02x", val, a->rtrack.value)); + emitcode ("cpl", "a"); + return 1; + } + if (val == (((a->rtrack.value>>1) | + (a->rtrack.value<<7)) & 0xff)) + { + D(emitcode (";", "genFromRTrack 0x%02x==rr(0x%02x)", val, a->rtrack.value)); + emitcode ("rr", "a"); + return 1; + } + if (val == (((a->rtrack.value<<1) | + (a->rtrack.value>>7)) & 0xff )) + { + D(emitcode (";", "genFromRTrack 0x%02x==rl(0x%02x)", val, a->rtrack.value)); + emitcode ("rl", "a"); + return 1; + } + if (val == ( ((a->rtrack.value & 0x0f)<<4) | + ((a->rtrack.value & 0xf0)>>4) )) + { + D(emitcode (";", "genFromRTrack 0x%02x==swap(0x%02x)", val, a->rtrack.value)); + emitcode ("swap", "a"); + return 1; + } + /* Decimal Adjust Accumulator (da a) changes flags so not used */ + } + + + { + unsigned int i; + char *ptr= rtrackGetLit(x); + + if (x != ptr) + { + /* could get from register, fine */ + emitcode ("mov", "a,%s", ptr); + return 1; + } + + /* not yet giving up - try to calculate from register R0..R7 */ + for (i = 0; i < 8; i++) + { + reg_info *r = ®s8051[rx_num_to_idx(i)]; + + if (a->rtrack.valueKnown && r->rtrack.valueKnown) + { + /* calculate with a single byte instruction from R0..R7? */ + if (val == (a->rtrack.value | r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x|0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("orl", "a,%s",r->name); + return 1; + } + if (val == (a->rtrack.value & r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x&0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("anl", "a,%s", r->name); + return 1; + } + if (val == (a->rtrack.value ^ r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x==0x%02x^0x%02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("xrl", "a,%s", r->name); + return 1; + } + /* changes flags (does that matter?) + if (val == (a->rtrack.value + r->rtrack.value)) + { + D(emitcode (";", "genFromRTrack 0x%02x=0x%02x+%0x02x", + val, a->rtrack.value, r->rtrack.value)); + emitcode ("add", "a,%s",r->name); + return 1; + } + so not used */ + } + } + } + } + } + + return 0; +} + + +/* Loads dptr with symbol (if needed) + */ +void rtrackLoadDptrWithSym (const char *x) +{ + if (enable != 1) + { + emitcode ("mov", "dptr,#%s", x); + return; + } + + if (regs8051[DPL_IDX].rtrack.symbol && + regs8051[DPH_IDX].rtrack.symbol) + { + /* rtrack.symbol for dph should look like "(something >> 8)" */ + if ((!strcmp (x, regs8051[DPL_IDX].rtrack.symbol) && + !strncmp (x, regs8051[DPH_IDX].rtrack.symbol + 1, strlen (x) ) && + !strncmp (" >> 8)", regs8051[DPH_IDX].rtrack.symbol + 1 + strlen (x), 6))) + { + /* dptr already holds the symbol */ + D(emitcode (";", "genFromRTrack dptr==#%s",x)); + return; + } + } + + emitcode ("mov", "dptr,#%s", x); +} + + +#if 0 +/* Loads index registers R0, R1 with symbol (if needed) + * + * R0, R1 index registers are already handled in gen.c (see AOP_INPREG) + */ +void rtrackLoadR0R1WithSym (const char *reg, const char *x) +{ + int regNum, regIdx; + + if (enable != 1) + { + emitcode ("mov", "%s,#%s", reg, x ); + return; + } + + regNum = reg[1] - '0'; + if (regNum == 0 || regNum == 1) + { + regIdx = rx_num_to_idx(regNum); + if ((NULL != regs8051[regIdx].rtrack.symbol) && !strcmp (x, regs8051[regIdx].rtrack.symbol)) + { + /* register already holds the symbol */ + D(emitcode (";", "genFromRTrack %s=#%s",reg,x)); + return; + } + } + + emitcode ("mov", "%s,#%s", reg, x ); +} +#endif diff --git a/src/mcs51/rtrack.h b/src/mcs51/rtrack.h new file mode 100644 index 0000000..dfad2e0 --- /dev/null +++ b/src/mcs51/rtrack.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + rtrack.h - header file for tracking content of registers on an mcs51 + + Copyright 2007 Frieder Ferlemann (Frieder Ferlemann AT web.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +-------------------------------------------------------------------------*/ + +bool _mcs51_rtrackUpdate (const char *line); + +char * rtrackGetLit(const char *x); + +int rtrackMoveALit (const char *x); + +void rtrackLoadDptrWithSym (const char *x); +void rtrackLoadR0R1WithSym (const char *reg, const char *x); diff --git a/src/pdk/Makefile b/src/pdk/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/pdk/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pdk/Makefile.dep b/src/pdk/Makefile.dep new file mode 100644 index 0000000..8edde56 --- /dev/null +++ b/src/pdk/Makefile.dep @@ -0,0 +1,45 @@ +gen.o: gen.c ralloc.h ../common.h ../SDCCglobl.h ../SDCCset.h \ + ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h \ + ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h \ + ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h \ + ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h \ + ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h \ + ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h \ + ../port.h ../SDCCargs.h ../../support/util/newalloc.h gen.h +main.o: main.c ../common.h ../SDCCglobl.h ../SDCCset.h ../../sdccconf.h \ + ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h ../SDCCast.h \ + ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h ../SDCCbitv.h \ + ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h \ + ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h ../SDCCptropt.h \ + ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h \ + ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h ../port.h \ + ../SDCCargs.h ../../support/util/newalloc.h \ + ../../support/util/dbuf_string.h ../../support/util/dbuf.h ralloc.h \ + peep.h peeph.rul +peep.o: peep.c ../common.h ../SDCCglobl.h ../SDCCset.h ../../sdccconf.h \ + ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h ../SDCCast.h \ + ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h ../SDCCbitv.h \ + ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h \ + ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h ../SDCCptropt.h \ + ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h \ + ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h ../port.h \ + ../SDCCargs.h ../../support/util/newalloc.h ../SDCCgen.h peep.h +ralloc.o: ralloc.c ralloc.h ../common.h ../SDCCglobl.h ../SDCCset.h \ + ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h ../../support/util/dbuf.h \ + ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h ../SDCCval.h ../SDCCy.h \ + ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h ../SDCCBBlock.h \ + ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h ../SDCClrange.h \ + ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h ../SDCCpeeph.h ../SDCCgen.h \ + ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h ../SDCCasm.h ../SDCCsystem.h \ + ../port.h ../SDCCargs.h ../../support/util/newalloc.h gen.h \ + ../../support/util/dbuf_string.h ../../support/util/dbuf.h +ralloc2.o: ralloc2.cc ../SDCCralloc.hpp ../common.h ../SDCCglobl.h \ + ../SDCCset.h ../../sdccconf.h ../SDCCerr.h ../SDCCmem.h \ + ../../support/util/dbuf.h ../SDCCast.h ../SDCCsymt.h ../SDCChasht.h \ + ../SDCCval.h ../SDCCy.h ../SDCCbitv.h ../SDCCicode.h ../SDCClabel.h \ + ../SDCCBBlock.h ../SDCCloop.h ../SDCCcse.h ../SDCCcflow.h ../SDCCdflow.h \ + ../SDCClrange.h ../SDCCptropt.h ../SDCCopt.h ../SDCCglue.h \ + ../SDCCpeeph.h ../SDCCgen.h ../SDCCicode.h ../SDCCdebug.h ../SDCCutil.h \ + ../SDCCasm.h ../SDCCsystem.h ../port.h ../SDCCargs.h \ + ../../support/util/newalloc.h ../SDCCbtree.h ../SDCCtree_dec.hpp \ + ../SDCCsalloc.hpp ralloc.h ../common.h gen.h diff --git a/src/pdk/Makefile.in b/src/pdk/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/pdk/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pdk/gen.c b/src/pdk/gen.c new file mode 100644 index 0000000..576c69d --- /dev/null +++ b/src/pdk/gen.c @@ -0,0 +1,4244 @@ +/*------------------------------------------------------------------------- + gen.c - code generator for Padauk. + + Copyright (C) 2018, Philipp Klaus Krause pkk@spth.de + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "ralloc.h" +#include "gen.h" + +/* Use the D macro for basic (unobtrusive) debugging messages */ +#define D(x) do if (options.verboseAsm) { x; } while (0) + +static bool regalloc_dry_run; +static unsigned int regalloc_dry_run_cost_words; +static float regalloc_dry_run_cost_cycles; +static unsigned int regalloc_dry_run_cycle_scale = 1; + +static struct +{ + short debugLine; + struct + { + int pushed; + int size; + int param_offset; + } stack; + bool saved; + + /* Track content of p */ + struct + { + AOP_TYPE type; + const char *base; + int offset; + } p; +} +G; + +static struct asmop asmop_a, asmop_p, asmop_pa, asmop_ap, asmop_zero, asmop_one, asmop_sp; +static struct asmop *const ASMOP_A = &asmop_a; +static struct asmop *const ASMOP_P = &asmop_p; +static struct asmop *const ASMOP_PA = &asmop_pa; +static struct asmop *const ASMOP_AP = &asmop_ap; +static struct asmop *const ASMOP_ZERO = &asmop_zero; +static struct asmop *const ASMOP_ONE = &asmop_one; +static struct asmop *const ASMOP_SP = &asmop_sp; + +void +pdk_init_asmops (void) +{ + asmop_a.type = AOP_REG; + asmop_a.size = 1; + asmop_a.aopu.bytes[0].in_reg = true; + asmop_a.aopu.bytes[0].byteu.reg = pdk_regs + A_IDX; + + asmop_p.type = AOP_REG; + asmop_p.size = 1; + asmop_p.aopu.bytes[0].in_reg = true; + asmop_p.aopu.bytes[0].byteu.reg = pdk_regs + P_IDX; + + asmop_ap.type = AOP_REG; + asmop_ap.size = 2; + asmop_ap.aopu.bytes[0].in_reg = true; + asmop_ap.aopu.bytes[0].byteu.reg = pdk_regs + A_IDX; + asmop_ap.aopu.bytes[1].in_reg = true; + asmop_ap.aopu.bytes[1].byteu.reg = pdk_regs + P_IDX; + + asmop_pa.type = AOP_REG; + asmop_pa.size = 2; + asmop_pa.aopu.bytes[0].in_reg = true; + asmop_pa.aopu.bytes[0].byteu.reg = pdk_regs + P_IDX; + asmop_pa.aopu.bytes[1].in_reg = true; + asmop_pa.aopu.bytes[1].byteu.reg = pdk_regs + A_IDX; + + asmop_zero.type = AOP_LIT; + asmop_zero.size = 1; + asmop_zero.aopu.aop_lit = constVal ("0"); + + asmop_one.type = AOP_LIT; + asmop_one.size = 1; + asmop_one.aopu.aop_lit = constVal ("1"); + + asmop_sp.type = AOP_SFR; + asmop_sp.aopu.aop_dir = "sp"; + asmop_sp.size = 1; +} + +static void +emit2 (const char *inst, const char *fmt, ...) +{ + if (!regalloc_dry_run) + { + va_list ap; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + } +} + +static void +cost(unsigned int words, float cycles) +{ + regalloc_dry_run_cost_words += words; + regalloc_dry_run_cost_cycles += cycles * regalloc_dry_run_cycle_scale; +} + +static void +emitJP(const symbol *target, float probability) +{ + if (!regalloc_dry_run) + emit2 ("goto", "%05d$", labelKey2num (target->key)); + cost (1, 2 * probability); +} + +static bool +regDead (int idx, const iCode *ic) +{ + wassert (idx == A_IDX || idx == P_IDX); + + return (!bitVectBitValue (ic->rSurv, idx)); +} + +/*-----------------------------------------------------------------*/ +/* aopInReg - asmop from offset in the register */ +/*-----------------------------------------------------------------*/ +static bool +aopInReg (const asmop *aop, int offset, short rIdx) +{ + if (aop->type != AOP_REG) + return (false); + + return (aop->aopu.bytes[offset].in_reg && aop->aopu.bytes[offset].byteu.reg->rIdx == rIdx); +} + +/*-----------------------------------------------------------------*/ +/* aopSame - are two asmops in the same location? */ +/*-----------------------------------------------------------------*/ +static bool +aopSame (const asmop *aop1, int offset1, const asmop *aop2, int offset2, int size) +{ + for(; size; size--, offset1++, offset2++) + { + if (aop1->type == AOP_REG && aop2->type == AOP_REG && // Same register + aop1->aopu.bytes[offset1].in_reg && aop2->aopu.bytes[offset2].in_reg && + aop1->aopu.bytes[offset1].byteu.reg == aop2->aopu.bytes[offset2].byteu.reg) + continue; + + if (aop1->type == AOP_LIT && aop2->type == AOP_LIT && + byteOfVal (aop1->aopu.aop_lit, offset1) == byteOfVal (aop2->aopu.aop_lit, offset2)) + continue; + + if (aop1->type == AOP_DIR && aop2->type == AOP_DIR && aop1->aopu.immd_off + offset1 == aop2->aopu.immd_off + offset2 && + !strcmp(aop1->aopu.aop_dir, aop2->aopu.aop_dir)) + return (true); + + if (aop1->type == AOP_SFR && aop2->type == AOP_SFR && offset1 == offset2 && + aop1->aopu.aop_dir && aop2->aopu.aop_dir && !strcmp(aop1->aopu.aop_dir, aop2->aopu.aop_dir)) + return (true); + + return (false); + } + + return (true); +} + +/*-----------------------------------------------------------------*/ +/* aopIsLitVal - asmop from offset is val */ +/*-----------------------------------------------------------------*/ +static bool +aopIsLitVal (const asmop *aop, int offset, int size, unsigned long long int val) +{ + wassert_bt (size <= sizeof (unsigned long long int)); // Make sure we are not testing outside of argument val. + + for(; size; size--, offset++) + { + unsigned char b = val & 0xff; + val >>= 8; + + // Leading zeroes + if (aop->size <= offset && !b) + continue; + + if (aop->type == AOP_IMMD && offset > (aop->aopu.code ? 1 : 0) && !b) + continue; + + if (aop->size <= offset) + return (false); + + if (aop->type != AOP_LIT) + return (false); + + if (byteOfVal (aop->aopu.aop_lit, offset) != b) + return (false); + } + + return (true); +} + +static const char * +aopGet(const asmop *aop, int offset) +{ + static char buffer[256]; + + if (offset >= aop->size) + return ("#0x00"); + + if (aop->type == AOP_LIT) + { + SNPRINTF (buffer, sizeof(buffer), "#0x%02x", byteOfVal (aop->aopu.aop_lit, offset)); + return (buffer); + } + + if (aop->type == AOP_REG) + return (aop->aopu.bytes[offset].byteu.reg->name); + + if (aop->type == AOP_IMMD) + { + if (offset == 0 && aop->aopu.code) + SNPRINTF (buffer, sizeof(buffer), "#<(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 1 && aop->aopu.func) + SNPRINTF (buffer, sizeof(buffer), "#>(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 1 && aop->aopu.code) + SNPRINTF (buffer, sizeof(buffer), "#>(%s + 0x8000 + %d)", aop->aopu.immd, aop->aopu.immd_off); + else if (offset == 0) + SNPRINTF (buffer, sizeof(buffer), "#(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else + SNPRINTF (buffer, sizeof(buffer), "#0", aop->aopu.immd, aop->aopu.immd_off); + return (buffer); + } + + if (aop->type == AOP_DIR) + { + SNPRINTF (buffer, sizeof(buffer), "%s+%d", aop->aopu.aop_dir, offset); + return (buffer); + } + else if (aop->type == AOP_SFR) + { + wassert (aop->size == 1); + SNPRINTF (buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + return (buffer); + } + + wassert_bt (0); + return ("dummy"); +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand *op) +{ + asmop *aop; + + wassert_bt (op); + + aop = op->aop; + + if (!aop) + return; + + Safe_free (aop); + + op->aop = 0; + if (IS_SYMOP (op) && SPIL_LOC (op)) + SPIL_LOC (op)->aop = 0; +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (const iCode *ic, symbol *sym) +{ + asmop *aop; + + wassert_bt (ic); + wassert_bt (regalloc_dry_run || sym); + wassert_bt (regalloc_dry_run || sym->etype); + + // Unlike some other backends we really free asmops; to avoid a double-free, we need to support multiple asmops for the same symbol. + + if (sym && IS_FUNC (sym->type)) + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = sym->rname; + aop->aopu.immd_off = 0; + aop->aopu.code = IN_CODESPACE (SPEC_OCLS (sym->etype)); + aop->aopu.func = true; + aop->size = getSize (sym->type); + } + /* Assign depending on the storage class */ + else if (sym && sym->onStack || sym && sym->iaccess) + { + aop = newAsmop (AOP_STK); + aop->size = getSize (sym->type); + int base = sym->stack + (sym->stack < 0 ? G.stack.param_offset : 0); + for (int offset = 0; offset < aop->size; offset++) + aop->aopu.bytes[offset].byteu.stk = base + offset; + } + /* sfr */ + else if (sym && IN_REGSP (SPEC_OCLS (sym->etype))) + { + wassertl (getSize (sym->type) <= 2, "Unimplemented support for wide (> 16 bit) I/O register"); + + aop = newAsmop (AOP_SFR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + } + else + { + aop = newAsmop (sym && IN_CODESPACE (SPEC_OCLS (sym->etype)) ? AOP_CODE : AOP_DIR); + if (sym) + { + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + } + } + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol *sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop; + int val = 0; + + wassert_bt (ic); + + for (;;) + { + if (ic->op == '+') + { + if (isOperandLiteral (IC_RIGHT (ic))) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else + { + val += (int) operandLitValue (IC_LEFT (ic)); + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else if (ic->op == ADDRESS_OF) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + break; + } + else + wassert_bt (0); + } + + wassert (!OP_SYMBOL (IC_LEFT (ic))->onStack); +#if 0 // TODO: Enable for support for rematerialization of addresses on stack. + if (OP_SYMBOL (IC_LEFT (ic))->onStack) + { + aop = newAsmop (AOP_STL); + aop->aopu.stk_off = (long)(OP_SYMBOL (IC_LEFT (ic))->stack) + 1 + val; + } + else +#endif + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = OP_SYMBOL (IC_LEFT (ic))->rname; + aop->aopu.immd_off = val; + aop->aopu.code = IN_CODESPACE (SPEC_OCLS (OP_SYMBOL (IC_LEFT (ic))->etype)); + } + + aop->size = getSize (sym->type); + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand *op, const iCode *ic) +{ + wassert_bt (op); + + /* if already has an asmop */ + if (op->aop) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + asmop *aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + op->aop = aop; + return; + } + + symbol *sym = OP_SYMBOL (op); + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, sym); + return; + } + + /* Rematerialize symbols where all bytes are spilt. */ + if (sym->remat && (sym->isspilt || regalloc_dry_run)) + { + bool completely_spilt = TRUE; + for (int i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + if (completely_spilt) + { + op->aop = aopForRemat (sym); + return; + } + } + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + asmop *aop = newAsmop (AOP_CND); + op->aop = aop; + sym->aop = sym->aop; + return; + } + + /* None of the above, which only leaves temporaries. */ + if ((sym->isspilt || sym->nRegs == 0) && !(regalloc_dry_run && (options.stackAuto || reentrant))) + { + sym->aop = op->aop = aopForSym (ic, sym->usl.spillLoc); + op->aop->size = getSize (sym->type); + return; + } + + /* None of the above, which only leaves temporaries. */ + { + bool completely_in_regs = true; + bool completely_spilt = true; + asmop *aop = newAsmop (AOP_REGDIR); + + aop->size = getSize (operandType (op)); + op->aop = aop; + + for (int i = 0; i < aop->size; i++) + { + aop->aopu.bytes[i].in_reg = !!sym->regs[i]; + if (sym->regs[i]) + { + completely_spilt = false; + aop->aopu.bytes[i].byteu.reg = sym->regs[i]; + //aop->regs[sym->regs[i]->rIdx] = i; + } + else if (sym->isspilt && sym->usl.spillLoc || sym->nRegs && regalloc_dry_run) + { + completely_in_regs = false; + + if (!regalloc_dry_run) + { + /*aop->aopu.bytes[i].byteu.stk = (long int)(sym->usl.spillLoc->stack) + aop->size - i; + + if (sym->usl.spillLoc->stack + aop->size - (int)(i) <= -G.stack.pushed) + { + fprintf (stderr, "%s %d %d %d %d at ic %d\n", sym->name, (int)(sym->usl.spillLoc->stack), (int)(aop->size), (int)(i), (int)(G.stack.pushed), ic->key); + wassertl_bt (0, "Invalid stack offset."); + }*/ + } + else + { + static long int old_base = -10; + static const symbol *old_sym = 0; + if (sym != old_sym) + { + old_base -= aop->size; + if (old_base < -100) + old_base = -10; + old_sym = sym; + } + + // aop->aopu.bytes[i].byteu.stk = old_base + aop->size - i; + } + } + else // Dummy iTemp. + { + aop->type = AOP_DUMMY; + return; + } + + if (!completely_in_regs && (!currFunc || GcurMemmap == statsg)) + { + if (!regalloc_dry_run) + wassertl_bt (0, "Stack asmop outside of function."); + cost (180, 180); + } + } + + if (completely_in_regs) + aop->type = AOP_REG; + else if (completely_spilt && !(options.stackAuto || reentrant)) + { + aop->type = AOP_DIR; + aop->aopu.immd = sym->rname; + } + else if (completely_spilt) + aop->type = AOP_STK; + else + wassertl (0, "Unsupported partially spilt aop"); + } +} + +static void +cheapMove (const asmop *result, int roffset, const asmop *source, int soffset, bool a_dead, bool f_dead); + +/*-----------------------------------------------------------------*/ +/* pushAF - push af, adjusting stack tracking */ +/*-----------------------------------------------------------------*/ +static void pushAF (void) +{ + emit2 ("push", "af"); + cost (1, 1); + G.stack.pushed += 2; +} + +/*-----------------------------------------------------------------*/ +/* popAF - pop af, adjusting stack tracking */ +/*-----------------------------------------------------------------*/ +static void popAF (void) +{ + emit2 ("pop", "af"); + cost (1, 1); + G.stack.pushed -= 2; +} + +/*-----------------------------------------------------------------*/ +/* pointPStack - Make pseudo-register p point to stack location */ +/*-----------------------------------------------------------------*/ +static void pointPStack (int s, bool a_dead, bool f_dead) +{ + // Try to adjust p when doing so is cheaper. + if (G.p.type == AOP_STK) + { + if (G.p.offset == s) + return; + + if (!f_dead) + pushAF(); + + if (abs(G.p.offset - s) < 3) + { + while (G.p.offset < s) + { + emit2 ("inc", "p"); + cost (1, 1); + G.p.offset++; + } + + while (G.p.offset > s) + { + emit2 ("dec", "p"); + cost (1, 1); + G.p.offset--; + } + } + else if (a_dead || !f_dead) + { + emit2 ("mov", "a, #%d", s - G.p.offset); + emit2 ("add", "p, a"); + cost (2, 2); + G.p.offset = s; + } + else + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", s - G.p.offset); + emit2 ("xch", "a, p"); + cost (3, 3); + G.p.offset = s; + } + + if (!f_dead) + popAF(); + + return; + } + + if (!a_dead && f_dead) + { + int soffset = s - G.stack.pushed; + emit2 ("xch", "a, p"); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #0x%02x", soffset & 0xff); + emit2 ("xch", "a, p"); + cost (4, 4); + } + else + { + if (!(a_dead && f_dead)) + pushAF(); + + int soffset = s - G.stack.pushed; + cheapMove (ASMOP_A, 0, ASMOP_SP, 0, true, true); + emit2 ("add", "a, #0x%02x", soffset & 0xff); + cost (1, 1); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + + if (!(a_dead && f_dead)) + popAF(); + } + + G.p.type = AOP_STK; + G.p.offset = s; +} + +/*-----------------------------------------------------------------*/ +/* moveStackStack - Move a block of memory on the stack. */ +/*-----------------------------------------------------------------*/ +static void +moveStackStack (int d, int s, int size, bool a_dead) +{ + if (!a_dead) + pushAF (); + + bool up = (d <= s); + + for (int i = up ? 0 : size - 1; up ? i < size : i >= 0; up ? i++ : i--) + { + pointPStack (s + i, true, true); + emit2 ("idxm", "a, p"); + cost (1, 2); + pointPStack (d + i, false, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + } + + if (!a_dead) + popAF(); +} + +/*-----------------------------------------------------------------*/ +/* cheapMove - Copy a byte from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +cheapMove (const asmop *result, int roffset, const asmop *source, int soffset, bool a_dead, bool f_dead) +{ + bool dummy = (result->type == AOP_DUMMY || source->type == AOP_DUMMY); + + if (aopSame (result, roffset, source, soffset, 1)) + return; + else if (!dummy && (result->type == AOP_DIR || aopInReg (result, roffset, P_IDX)) && aopIsLitVal (source, soffset, 1, 0)) + { + emit2 ("clear", "%s", aopGet (result, roffset)); + cost (1, 1); + } + else if (source->type == AOP_CODE && aopInReg (result, roffset, A_IDX)) + { + emit2 ("call", "%s+%d", source->aopu.aop_dir, soffset); + cost (1, 4); + } + else if (source->type == AOP_STK && aopInReg (result, roffset, A_IDX) && !aopIsLitVal (source, soffset, 1, 0)) + { + pointPStack(source->aopu.bytes[soffset].byteu.stk, true, f_dead); + emit2 ("idxm", "a, p"); + cost (1, 2); + } + else if (result->type == AOP_STK && aopInReg (source, soffset, A_IDX)) + { + pointPStack(result->aopu.bytes[roffset].byteu.stk, false, f_dead); + emit2 ("idxm", "p, a"); + cost (1, 2); + } + else if (aopInReg (result, roffset, A_IDX)) + { + emit2 ("mov", "a, %s", aopGet (source, soffset)); + cost (1, 1); + } + else if (aopInReg (source, soffset, A_IDX)) + { + emit2 ("mov", "%s, a", aopGet (result, roffset)); + cost (1, 1); + } + else + { + if (!a_dead) + pushAF(); + cheapMove (ASMOP_A, 0, source, soffset, true, f_dead || !a_dead); + cheapMove (result, roffset, ASMOP_A, 0, true, f_dead || !a_dead); + if (!a_dead) + popAF(); + } + + if (aopInReg (result, roffset, P_IDX)) + G.p.type = AOP_INVALID; +} + +/*--------------------------------------------------------------------------*/ +/* adjustStack - Adjust the stack pointer by n bytes. */ +/*--------------------------------------------------------------------------*/ +static void +adjustStack (int n, bool a_free, bool p_free) +{ + wassertl_bt (!(n % 2), "Unsupported odd stack adjustment"); // The datasheets seem to require the stack pointer to be aligned to a 2-byte boundary. + + if (n >= 0 && (!(p_free || a_free) || n <= 4)) + for (int i = 0; i < n; i += 2) + { + emit2 ("push", "af"); + cost (1, 1); + } + else if (!a_free && p_free) + { + emit2 ("xch", "a, p"); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + emit2 ("xch", "a, p"); + cost (5, 5); + } + else if (!a_free && !p_free && n < 0) + { + pushAF(); + cheapMove (ASMOP_A, 0, ASMOP_P, 0, true, true); + pushAF(); + + moveStackStack (G.stack.pushed - 4 + n, G.stack.pushed - 4, 4, true); + + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + cost (3, 3); + + popAF(); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + popAF(); + } + else // Can't use pop af, since it might affect reserved flag bits. + { + wassert (a_free); + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #%d", n); + emit2 ("mov", "sp, a"); + cost (3, 3); + } + + G.stack.pushed += n; +} + +static void +push (const asmop *op, int offset, int size) +{ + wassertl (!(size % 2) && (op->type == AOP_DIR || op->type == AOP_LIT || op->type == AOP_IMMD || op->type == AOP_STK), "Unimplemented push operand"); + + if (op->type == AOP_STK) + { + int s = G.stack.pushed; + adjustStack (size, true, true); + moveStackStack (s, op->aopu.bytes[0].byteu.stk, size, true); + return; + } + else if (size == 2) + { + cheapMove (ASMOP_A, 0, op, 0, true, true); + pushAF (); + pointPStack (G.stack.pushed - 1, true, true); + cheapMove (ASMOP_A, 0, op, 1, true, true); + emit2 ("idxm", "p, a"); + cost (1, 1); + return; + } + + // Save old stack pointer + emit2 ("mov", "a, sp"); + emit2 ("mov", "p, a"); + G.p.type = AOP_INVALID; + cost (2, 2); + + adjustStack (size, true, false); + + // Write value onto stack + for (int i = offset; i < offset + size; i++) + { + cheapMove (ASMOP_A, 0, op, i, true, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + if (i + 1 < offset + size) + { + emit2 ("inc", "p"); + cost (1, 1); + } + } + G.p.type = AOP_INVALID; +} + +/*-----------------------------------------------------------------*/ +/* genMove_o - Copy part of one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove_o (asmop *result, int roffset, asmop *source, int soffset, int size, bool a_dead_global) +{ + // Handle I/O first. + wassert_bt ((result->type == AOP_SFR) + (source->type == AOP_SFR) <= 1); + if (result->type == AOP_SFR || source->type == AOP_SFR) + switch (size) + { + case 1: + cheapMove (result, roffset, source, soffset, a_dead_global, true); + return; + case 2: +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + if (result->type == AOP_SFR && source->type == AOP_DIR) + { + emit2 ("stt16", "%s", aopGet (source, soffset)); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } + else +#endif + if (result->type == AOP_SFR && source->type == AOP_LIT && aopIsLitVal (source, 1, 1, 0x00)) + { + cheapMove (ASMOP_P, 0, source, 0, true, true); + emit2 ("stt16", "p"); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + else if (result->type == AOP_DIR && source->type == AOP_SFR) + { + emit2 ("ldt16", "%s", aopGet (result, roffset)); + cost (1, 1); // TODO: Really just 1 cycle? Other 16-bit-transfer instructions use 2. + } +#endif + else if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemenetd operand in __sfr16 access"); + return; + default: + wassertl (0, "Unknown __sfr size"); + } + + wassert_bt (result->type == AOP_DIR || result->type == AOP_REG || result->type == AOP_STK); + wassert_bt (source->type == AOP_LIT || source->type == AOP_IMMD || source->type == AOP_DIR || source->type == AOP_REG || source->type == AOP_STK || source->type == AOP_CODE); + + if (size == 2 && aopInReg (result, roffset, P_IDX) && aopInReg (result, roffset + 1, A_IDX) && source->type == AOP_STK) + { + cheapMove (result, roffset + 1, source, soffset + 1, true, true); + cheapMove (result, roffset + 0, source, soffset + 0, false, true); + return; + } + else if (size == 2 && result->type == AOP_STK && aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX)) + { + pushAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + popAF (); + cheapMove (result, roffset + 0, ASMOP_A, 0, true, true); + return; + } + else if (size == 2 && result->type == AOP_DIR && !a_dead_global && // Using xch cheaper than push / pop. + aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX)) + { + cheapMove (result, roffset + 0, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size == 2 && result->type == AOP_DIR && !a_dead_global && // Using xch cheaper than push / pop. + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX)) + { + cheapMove (result, roffset + 1, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + cheapMove (result, roffset + 0, ASMOP_A, 0, false, true); + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size == 2 && // Assign upper byte first to avoid overwriting a. + (aopInReg (result, roffset, A_IDX) && aopInReg (result, roffset + 1, P_IDX) && source->type == AOP_DIR || + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX) && result->type == AOP_DIR)) + { + cheapMove (result, roffset + 1, source, soffset + 1, a_dead_global, true); + cheapMove (result, roffset + 0, source, soffset + 0, a_dead_global, true); + return; + } + else if (size == 2 && + (aopInReg (source, soffset, A_IDX) && aopInReg (source, soffset + 1, P_IDX) && aopInReg (result, roffset, P_IDX) && aopInReg (result, roffset + 1, A_IDX) || + aopInReg (source, soffset, P_IDX) && aopInReg (source, soffset + 1, A_IDX) && aopInReg (result, roffset, A_IDX) && aopInReg (result, roffset + 1, P_IDX))) + { + emit2 ("xch", "a, p"); + cost (1, 1); + return; + } + else if (size >= 2 && result->type == AOP_DIR && source->type == AOP_DIR && !strcmp (result->aopu.aop_dir, source->aopu.aop_dir) && soffset < roffset) + { + if (!a_dead_global) + pushAF(); + if (soffset + 1 == roffset) // Use xch via a. + { + emit2 ("mov", "a, %s", aopGet (source, soffset)); + for (int i = 0; i < size - 1; i++) + emit2 ("xch", "a, %s", aopGet (result, roffset + i)); + emit2 ("mov", "%s, a", aopGet (result, roffset + size - 1)); + cost (size + 1, size + 1); + } + else // Copy high-to-low to avoid overwriting of still-needed bytes. + { + for (int i = size - 1; i >= 0; i--) + cheapMove (result, roffset + i, source, soffset + i, true, true); + } + if (!a_dead_global) + popAF(); + return; + } + else if (size >= 2 && result->type == AOP_DIR && source->type == AOP_DIR && !strcmp (result->aopu.aop_dir, source->aopu.aop_dir) && soffset > roffset && roffset + 1 == soffset && a_dead_global) // Use xch via a. + { + emit2 ("mov", "a, %s", aopGet (source, soffset + size - 1)); + for (int i = size - 1; i > 0; i--) + emit2 ("xch", "a, %s", aopGet (result, roffset + i)); + emit2 ("mov", "%s, a", aopGet (result, roffset)); + cost (size + 1, size + 1); + return; + } + + bool a_dead = a_dead_global; + for (unsigned int i = 0; i < size; i++) + { + cheapMove (result, roffset + i, source, soffset + i, a_dead, true); + if (aopInReg (result, roffset + i, A_IDX)) + a_dead = false; + } +} + +/*-----------------------------------------------------------------*/ +/* genMove - Copy the value from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove (asmop *result, asmop *source, bool a_dead) +{ + genMove_o (result, 0, source, 0, result->size, a_dead); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = + { + 1l, 2l, 4l, 8l, 16l, 32l, 64l, 128l, + 0x100l, 0x200l, 0x400l, 0x800l, + 0x1000l, 0x2000l, 0x4000l, 0x8000l, + 0x10000l, 0x20000l, 0x40000l, 0x80000l, + 0x100000l, 0x200000l, 0x400000l, 0x800000l, + 0x1000000l, 0x2000000l, 0x4000000l, 0x8000000l, + 0x10000000l, 0x20000000l, 0x40000000l, 0x80000000l + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx; + return -1; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generates code for ! */ +/*-----------------------------------------------------------------*/ +static void +genNot (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genNot", "")); + + aopOp (left, ic); + aopOp (result, ic); + + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + for (int i = 1; i < left->aop->size; i++) + { + if (left->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic)) + { + cost (1000, 1000); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, left->aop, i, false, true); + emit2 ("or", "a, p"); + } + else + emit2 ("or", "a, %s", aopGet (left->aop, i)); + cost (1, 1); + } + emit2 ("sub", "a, #0x01"); + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + genMove_o (result->aop, 1, ASMOP_ZERO, 0, result->aop->size - 1, true); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genCpl", "")); + + aopOp (left, ic); + aopOp (result, ic); + + int size = result->aop->size; + + genMove (result->aop, left->aop, true); + + for(int i = 0; i < size; i++) + { + emit2("not", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genSub - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genSub (const iCode *ic, asmop *result_aop, asmop *left_aop, asmop *right_aop) +{ + int size = result_aop->size; + + bool started = false; + bool pushed_a = false; + for (int i = 0; i < size; i++) + { + if (!started && right_aop->type == AOP_LIT && aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (result_aop, i, left_aop, i, regDead (A_IDX, ic), true); + if (aopInReg (result_aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (left_aop, i, 1, 0x00) && + (right_aop->type == AOP_DIR || aopInReg (right_aop, i, P_IDX)) && aopSame (right_aop, i, result_aop, i, 1)) + { + emit2 ("neg", "%s", aopGet (right_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && aopIsLitVal (right_aop, i, 1, 0x01) && + (left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("dec", "%s", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (right_aop, i, 1, 0xff) && + (left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("inc", "%s", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (started && (left_aop->type == AOP_DIR || left_aop->type == AOP_REGDIR || left_aop->type == AOP_REG) && aopIsLitVal (right_aop, i, 1, 0x00) && aopSame (left_aop, i, result_aop, i, 1)) + { + emit2 ("subc", "%s", aopGet (left_aop, i)); + cost (1, 1); + continue; + } + + if (!(regDead (A_IDX, ic) || pushed_a)) + { + pushAF(); + pushed_a = true; + } + + if ((left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && right_aop->type != AOP_STK && aopSame (left_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 (started ? "subc" : "sub", "%s, a", aopGet (left_aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && i + 1 == size && aopIsLitVal (left_aop, i, 1, 0x00)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 ("neg", "a"); + cost (1, 1); + started = true; + } + else if (right_aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, !started); + cheapMove (ASMOP_P, 0, right_aop, i, false, !started); + emit2 (started ? "subc" : "sub", "a, p"); + cost (1, 1); + started = true; + } + else if (started && (right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD) && !aopIsLitVal (right_aop, i, 1, 0x00) && i + 1 == size) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("subc", "a"); + emit2 ("sub", "a, %s", aopGet (right_aop, i)); + cost (2, 2); + } + else if (started && (right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD) && !aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (ASMOP_P, 0, right_aop, i, !aopInReg (left_aop, i, A_IDX), false); + cheapMove (ASMOP_A, 0, left_aop, i, true, false); + emit2 ("subc", "a, p"); + cost (1, 1); + } + else + { + cheapMove (ASMOP_A, 0, left_aop, i, true, !started); + if (started || !aopIsLitVal (right_aop, i, 1, 0x00)) + { + if (aopInReg (right_aop, i, A_IDX)) + { + cost (1000, 1000); + if (!regalloc_dry_run) + wassertl (0, "Unimplemented operand in subtraction"); + } + if (started && aopIsLitVal (right_aop, i, 1, 0x00)) + emit2 ("subc", "a"); + else + emit2 (started ? "subc" : "sub", "a, %s", aopGet (right_aop, i)); + cost (1, 1); + started = true; + } + } + if (i + 1 < size && aopInReg (result_aop, i, P_IDX) && (left_aop->type == AOP_STK || right_aop->type == AOP_STK)) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented p result in subtraction with stack operand"); + if (i + 1 < size && result_aop->type == AOP_STK && (aopInReg (left_aop, i + 1, P_IDX) || aopInReg (right_aop, i + 1, P_IDX))) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented upper byte p operand in subtraction with stack result"); + + if (aopInReg (result_aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + else + cheapMove (result_aop, i, ASMOP_A, 0, true, i + 1 == size); + } + + if (pushed_a) + popAF(); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + D (emit2 ("; genUminusFloat", "")); + + genMove_o (result->aop, 0, left->aop, 0, result->aop->size - 1, true); + + cheapMove (ASMOP_A, 0, left->aop, result->aop->size - 1, true, true); + emit2 ("xor", "a, #0x80"); + cost (1, 1); + cheapMove (result->aop, result->aop->size - 1, ASMOP_A, 0, true, true); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + aopOp (result, ic); + + if (IS_FLOAT (operandType (left))) + { + genUminusFloat (ic); + return; + } + + D (emit2 ("; genUminus", "")); + + genSub (ic, result->aop, ASMOP_ZERO, left->aop); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (const iCode *ic) +{ + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + + D (emit2 ("; genIPush", "")); + + if (!ic->parmPush) + wassertl (0, "Encountered an unsupported spill push."); + + wassertl (left->aop->size == 1 || !(left->aop->size % 2), "Unimplemented operand size for parameter push"); + + if (left->aop->size == 1) + { + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + pushAF(); + } + else + push (left->aop, 0, left->aop->size); + + freeAsmop (IC_LEFT (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (const iCode *ic) +{ + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *etype = getSpec (dtype); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + bool bigreturn = (getSize (ftype->next) > 2) || IS_STRUCT (ftype->next); + + D (emit2 ("; genCall", "")); + + if (bigreturn) + { + wassertl (IC_RESULT (ic), "Unused return value in call to function returning large type."); + + const symbol *rsym = OP_SYMBOL_CONST (IC_RESULT (ic)); + if (rsym->usl.spillLoc) + rsym = rsym->usl.spillLoc; + + if (rsym->onStack || rsym->isspilt && regalloc_dry_run && (options.stackAuto || reentrant)) + { + emit2 ("mov", "a, sp"); + emit2 ("add", "a, #0x%02x", (rsym->stack + (rsym->stack < 0 ? G.stack.param_offset : 0) - G.stack.pushed) & 0xff); + } + else + { + emit2 ("mov", "a, #%s", rsym->rname); + cost (1, 1); + } + pushAF (); + } + + if (!regDead (A_IDX, ic) || !regDead (P_IDX, ic)) + { + cost (700, 700); + wassertl (regalloc_dry_run, "Register saving across call not yet implemented"); + } + + if (ic->op == PCALL) + { + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + + // Push return address + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (!regalloc_dry_run) + { + emit2 ("mov", "a, #<(!tlabel)", labelKey2num (tlbl->key)); + emit2 ("push", "af"); + emit2 ("mov", "a, sp"); + emit2 ("mov", "p, a"); + emit2 ("dec", "p"); + emit2 ("mov", "a, #>(!tlabel)", labelKey2num (tlbl->key)); + emit2 ("idxm", "p, a"); + G.p.type = AOP_INVALID; + } + G.stack.pushed += 2; + cost (7, 8); + + // Jump to function + push (left->aop, 0, 2); + emit2 ("ret", ""); + G.stack.pushed -= 4; + cost (2, 1); + + emitLabel (tlbl); + + freeAsmop (left); + } + else + { + if (IS_LITERAL (etype)) + emit2 ("call", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); + emit2 (jump ? "goto" : "call", "%s", + (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + } + cost (1, 2); + } + G.p.type = AOP_INVALID; + + bool SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) + || IS_TRUE_SYMOP (IC_RESULT (ic)); + + if (!SomethingReturned || bigreturn) + adjustStack (-ic->parmBytes - bigreturn * 2, true, true); + else + { + aopOp (IC_RESULT (ic), ic); + + genMove (IC_RESULT (ic)->aop, ASMOP_AP, true); + + adjustStack (-ic->parmBytes, !(aopInReg (IC_RESULT (ic)->aop, 0, A_IDX) || aopInReg (IC_RESULT (ic)->aop, 1, A_IDX)), !(aopInReg (IC_RESULT (ic)->aop, 0, P_IDX) || aopInReg (IC_RESULT (ic)->aop, 1, P_IDX))); + + freeAsmop (IC_RESULT (ic)); + } +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode *ic) +{ + const symbol *sym = OP_SYMBOL_CONST (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + + G.stack.pushed = 0; + G.stack.param_offset = 0; + + /* create the function header */ + emit2 (";", "-----------------------------------------"); + emit2 (";", " function %s", sym->name); + emit2 (";", "-----------------------------------------"); + + D (emit2 (";", pdk_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal.")); + + emit2 ("", "%s:", sym->rname); + if (!regalloc_dry_run) + genLine.lineCurr->isLabel = 1; + + if (IFFUNC_ISNAKED(ftype)) + { + emit2 (";", "naked function: no prologue."); + return; + } + + if (IFFUNC_ISISR (sym->type)) + { + pushAF (); + cheapMove (ASMOP_A, 0, ASMOP_P, 0, true, true); + pushAF (); + } + + G.stack.param_offset -= (getSize (ftype->next) > 2 || IS_STRUCT (ftype->next)) * 2; // Account for hidden parameter holding address of return value. + + G.p.type = AOP_INVALID; + + adjustStack (sym->stack, true, true); +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode *ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int retsize = getSize (sym->type->next); + + D (emit2 ("; genEndFunction", "")); + + if (IFFUNC_ISNAKED(sym->type)) + { + D (emit2 (";", "naked function: no epilogue.")); + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + /* adjust the stack for the function */ + if (sym->stack) + adjustStack (-sym->stack, retsize == 0 || retsize > 2, retsize != 2); + + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 1); + + if (IFFUNC_ISISR (sym->type)) + { + popAF (); + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + popAF (); + emit2 ("reti", ""); + cost (1, 2); + } + else + { + emit2 ("ret", ""); + cost (1, 1); + } + + wassertl (!G.stack.pushed, "Unbalanced stack."); +} + +/*-----------------------------------------------------------------*/ +/* genReturn - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genReturn (const iCode *ic) +{ + operand *left = IC_LEFT (ic); + + D (emit2 ("; genReturn", "")); + + /* if we have no return value then + just generate the "ret" */ + if (!left) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (left, ic); + + wassertl (currFunc, "return iCode outside of function"); + + if (left->aop->size > 2) + { + if (left->aop->type == AOP_STK) + { + for (int i = 0; i < left->aop->size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + pushAF (); + pointPStack (-4, true, true); + emit2 ("idxm", "a, p"); + emit2 ("mov", "p, a"); + cost (2, 3); + popAF (); + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + cost (1, 1); + } + emit2 ("idxm", "p, a"); + cost (1, 2); + G.p.type = AOP_INVALID; + } + } + else + { + pointPStack (-4, true, true); + emit2 ("idxm", "a, p"); + emit2 ("mov", "p, a"); + cost (2, 3); + for (int i = 0; i < left->aop->size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("idxm", "p, a"); + cost (1, 2); + if (i + 1 < left->aop->size) + { + emit2 ("inc", "p"); + cost (1, 1); + } + } + } + goto end; + } + + if (left->aop->size == 2 && aopInReg (left->aop, 0, P_IDX) && aopInReg (left->aop, 1, A_IDX)) + { + emit2 ("xch", "a, p"); + cost(1, 1); + goto end; + } + else if (left->aop->size == 2 && left->aop->type == AOP_STK) + { + genMove (ASMOP_AP, left->aop, true); + goto end; + } + + if (left->aop->size > 1) + cheapMove (ASMOP_P, 0, left->aop, 1, true, true); + if ((left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && !currFunc->stack) + { + emit2 ("ret", "%s", aopGet (left->aop, 0)); + cost (1, 2); + freeAsmop (left); + return; + } + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + +end: + freeAsmop (left); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + if (!currFunc->stack && !IFFUNC_ISISR (currFunc->type)) + { + emit2 ("ret", ""); + cost (2, 1); + } + else + emitJP(returnLabel, 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (const iCode *ic) +{ + D (emit2 ("; genLabel", "")); + + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + if (options.debug /*&& !regalloc_dry_run*/) + debugFile->writeLabel (IC_LABEL (ic), ic); + + emitLabel (IC_LABEL (ic)); + + G.p.type = AOP_INVALID; +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a jump */ +/*-----------------------------------------------------------------*/ +static void +genGoto (const iCode *ic) +{ + D (emit2 ("; genGoto", "")); + + emitJP (IC_LABEL (ic), 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPlus", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK && !aopInReg (left->aop, 0, A_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + bool started = false; + bool pushed_a = false; + bool moved_to_a = false; + + for (int i = 0; i < size; i++) + { + if (!started && !moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0x01) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("inc", "%s", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (!started && !moved_to_a && i + 1 == size && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0xff) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("dec", "%s", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (started && !moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && aopIsLitVal (right->aop, i, 1, 0x00) && aopSame (left->aop, i, result->aop, i, 1)) + { + emit2 ("addc", "%s", aopGet (left->aop, i)); + cost (1, 1); + continue; + } + else if (!started && !moved_to_a && right->aop->type == AOP_LIT && aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (result->aop, i, left->aop, i, regDead (A_IDX, ic), true); + if (aopInReg (result->aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + continue; + } + + if (!(regDead (A_IDX, ic) || pushed_a)) + { + pushAF(); + pushed_a = true; + } + + if (!moved_to_a && (left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX)) && right->aop->type != AOP_STK && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 (started ? "addc" : "add", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + started = true; + continue; + } + else if (right->aop->type == AOP_STK) + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, !started); + cheapMove (ASMOP_P, 0, right->aop, i, false, !started); + emit2 (started ? "addc" : "add", "a, p"); + cost (1, 1); + started = true; + } + else if (!moved_to_a && aopInReg (left->aop, i, P_IDX)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, !started); + emit2 (started ? "addc" : "add", "a, p"); + cost (1, 1); + } + else if (started && (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD) && !aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 == size) + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("addc", "a"); + emit2 ("add", "a, %s", aopGet (right->aop, i)); + cost (2, 2); + } + else if (started && (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD) && !aopIsLitVal (right->aop, i, 1, 0x00)) + { + if (!regDead (P_IDX, ic) || i > 0 && aopInReg (result->aop, i - 1, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, right->aop, i, !aopInReg (left->aop, i, A_IDX) && !moved_to_a, false); + cheapMove (ASMOP_A, 0, left->aop, i, true, false); + emit2 ("addc", "a, p"); + cost (1, 1); + } + else + { + if (!moved_to_a) + cheapMove (ASMOP_A, 0, left->aop, i, true, !started); + if (started || !aopIsLitVal (right->aop, i, 1, 0x00)) + { + if (started && aopIsLitVal (right->aop, i, 1, 0x00)) + emit2 ("addc", "a"); + else + emit2 (started ? "addc" : "add", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + started = true; + } + } + if (i + 1 < size && aopInReg (result->aop, i, P_IDX) && (left->aop->type == AOP_STK || right->aop->type == AOP_STK)) + if (regalloc_dry_run) + cost (1000, 1000); + else + wassertl (0, "Unimplemented p result in addition with stack operand"); + + if (aopInReg (result->aop, i, A_IDX) && i + 1 < size) + { + pushAF(); + pushed_a = true; + } + else if (aopInReg (result->aop, i, P_IDX) && regDead (P_IDX, ic) && aopInReg (left->aop, i + 1, P_IDX)) + { + emit2 ("xch", "a, p"); + cost (1, 1); + moved_to_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, i + 1 == size); + } + + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for minus */ +/*-----------------------------------------------------------------*/ +static void +genMinus (const iCode *ic, const iCode *ifx) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genMinus", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + if (ifx && ifx->generated) + { + wassert (IC_TRUE (ifx)); + wassert (left->aop->type == AOP_REG || left->aop->type == AOP_DIR); + wassert (aopIsLitVal (right->aop, 0, 2, 1)); + + emit2 ("dzsn", aopGet (left->aop, 0)); + cost (1, 1.8f); + emitJP (IC_TRUE (ifx), 0.2f); + + for(int i = 1; i < left->aop->size; i++) + { + emit2 ("subc", aopGet (left->aop, i)); + emit2 ("t1sn", "f, z"); + cost (2, 2.8f); + emitJP (IC_TRUE (ifx), 0.2f); + } + } + else + genSub (ic, result->aop, left->aop, right->aop); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultLit (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + /* Swap left and right such that right is a literal */ + if (left->aop->type == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + wassert (result->aop && result->aop->size == 1 && right->aop && right->aop->type == AOP_LIT); + + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + + asmop *add_aop; + if (aopInReg (left->aop, 0, P_IDX) || left->aop->type == AOP_DIR || left->aop->type == AOP_IMMD) + add_aop = left->aop; + else + { + add_aop = ASMOP_P; + cheapMove (ASMOP_P, 0, left->aop, 0, false, true); + } + + unsigned long long add, sub; + int topbit, nonzero; + + value *bval = valueFromLit (ulFromVal (right->aop->aopu.aop_lit) & 0xff); + wassert (!csdOfVal (&topbit, &nonzero, &add, &sub, bval)); + Safe_free (bval); + + // If the leading digits of the cse are 1 0 -1 we can use 0 1 1 instead to reduce the number of shifts. + if (topbit >= 2 && (add & (1ull << topbit)) && (sub & (1ull << (topbit - 2)))) + { + add = (add & ~(1u << topbit)) | (3u << (topbit - 2)); + sub &= ~(1u << (topbit - 1)); + topbit--; + } + + for (int bit = topbit - 1; bit >= 0; bit--) + { + emit2 ("sl", "a"); + cost (1, 1); + if ((add | sub) & (1ull << bit)) + { + emit2 (add & (1ull << bit) ? "add" : "sub" , "a, %s", aopGet (add_aop, 0)); + cost (1, 1); + } + } + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genMult", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + if (left->aop->size >= 2 || right->aop->size >= 2 || result->aop->size > 2) + wassertl (0, "Wide multiplication is to be handled via support function calls."); + + /* Swap left and right such that right is a literal */ + if (left->aop->type == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + if (right->aop->type == AOP_LIT && result->aop->size == 1) + { + genMultLit (ic); + goto release; + } + + wassert (0); + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmp (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + + D (emit2 ("; genCmp", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = max (left->aop->size, right->aop->size); + bool sign = false; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + sign = !(SPEC_USIGN (operandType (left)) | SPEC_USIGN (operandType (right))); + + // Non-destructive 1-byte unsigned comparison. + if (!sign && size == 1 && ifx && aopInReg (left->aop, 0, A_IDX)) + { + if (ic->op == '>' && right->aop->type == AOP_LIT) + { + wassert (!aopIsLitVal (right->aop, 0, 1, 0x00)); + + if (IC_TRUE (ifx)) + { + emit2 ("ceqsn", "a, #0x%02x", byteOfVal (right->aop->aopu.aop_lit, 0) + 1); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + else + { + emit2 ("ceqsn", "a, #0x%02x", byteOfVal (right->aop->aopu.aop_lit, 0) + 1); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + cost (3, 3.5); + } + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + goto release; + } + else if (ic->op == '<' && aopInReg (left->aop, 0, A_IDX) && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR) && (IC_TRUE (ifx) || !regDead (A_IDX, ic))) + { + if (IC_FALSE (ifx)) + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + else + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + cost (3, 3.5); + } + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + goto release; + } + } + + if (ic->op == '>') + { + operand *t = right; + right = left; + left = t; + } + + bool started = false; + for (int i = 0; i < size; i++) + { + if (!started && sign && aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 == size) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (ifx) + { + if (IC_FALSE (ifx)) + { + if (aopInReg (left->aop, i, A_IDX) && !regDead (A_IDX, ic)) + { + emit2 ("ceqsn", "a, #0x80"); + emit2 ("nop", ""); + cost (2, 2); + } + else + { + emit2 ("sub", "a, #0x80"); + cost (1, 1); + } + emit2 ("t0sn", "f, c"); + cost (1, 1.5); + } + else + { + emit2 ("ceqsn", "a, #0x80"); + emit2 ("t1sn", "f, c"); + cost (2, 2.5); + } + + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + } + else + { + emit2 ("sl", "a"); + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + goto release; + } + + if (!started && aopIsLitVal (right->aop, i, 1, 0x00) && i + 1 < size) + ; + else if (started && aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 ("subc", "a"); + cost (1, 1); + } + else if (started && (right->aop->type == AOP_LIT && !aopIsLitVal (right->aop, i, 1, 0x00) || right->aop->type == AOP_IMMD)) // Work around lack of subc a, #nn. + { + cheapMove (ASMOP_P, 0, right->aop, i, true, !i); + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 ("subc", "a, p"); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + cheapMove (ASMOP_P, 0, right->aop, i, false, !i); + emit2 (started ? "subc" : "sub", "a, p"); + cost (1, 1); + started = true; + } + else + { + if (aopInReg (right->aop, i, A_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, !i); + emit2 (started ? "subc" : "sub", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + started = true; + } + } + + if (sign) + { + emit2 ("t0sn", "f, ov"); + emit2 ("xor", "a, #0x80"); + emit2 ("sl", "a"); + cost (3, 3); + } + + if (ifx) + { + emit2 (IC_FALSE(ifx) ? "t1sn" : "t0sn", "f, c"); + cost (1, 1.5); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + } + else + { + emit2 ("mov", "a, #0x00"); + emit2 ("slc", "a"); + cost (2, 2); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEQorNE - equal or not equal comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpEQorNE (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + + D (emit2 ("; genCmpEQorNE", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + symbol *lbl_ne = 0; + symbol *endlbl = 0; + + if (ifx) + { + if ((ic->op == EQ_OP) ^ (bool)(IC_FALSE (ifx))) + lbl_ne = regalloc_dry_run ? 0 : newiTempLabel (NULL); + else + lbl_ne = IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx); + } + else if (!regalloc_dry_run) + { + lbl_ne = newiTempLabel (NULL); + endlbl = newiTempLabel (NULL); + } + + int size = max (left->aop->size, right->aop->size); + + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 1, A_IDX) || + !aopInReg (left->aop, 0, A_IDX) && right->aop->type == AOP_CODE) + { + operand *temp = left; + left = right; + right = temp; + } + + if (aopInReg (left->aop, 1, A_IDX) && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || right->aop->type == AOP_IMMD)) + { + wassert (regDead (A_IDX, ic)); + + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 1)); + cost (1, 1); + emitJP (lbl_ne, 0.0f); + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + if (ifx && ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx)))) + { + if (TARGET_IS_PDK13) // pdk13 does not have cneqsn + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 ("cneqsn", "a, %s", aopGet (right->aop, 0)); + cost (1, 1); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + } + } + else + { + emit2 ("ceqsn", "a, %s", aopGet (right->aop, 0)); + cost (1, 1); + emitJP (lbl_ne, 0.0f); + } + } + else + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + + if (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) + { + if (!regDead (P_IDX, ic) || i + 1 < size && aopInReg(left->aop, i + 1, P_IDX)) + { + cost (1000, 1000); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + } + + if (ifx && i + 1 == size && ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx)))) + { + if (TARGET_IS_PDK13) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 ("cneqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + cost (1, 1); + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.0f); + } + } + else + { + emit2 ("ceqsn", "a, %s", (right->aop->type == AOP_STK || right->aop->type == AOP_CODE) ? "p" : aopGet (right->aop, i)); + cost (1, 1); + emitJP(lbl_ne, 0.0f); + } + } + + if (ifx) // Jump condition only. + { + if ((ic->op == EQ_OP) ^ (bool)(IC_FALSE(ifx))) + emitLabel (lbl_ne); + } + else // Needs result + { + if (!regDead (A_IDX, ic)) + pushAF(); + cheapMove (result->aop, 0, ic->op == EQ_OP ? ASMOP_ONE : ASMOP_ZERO, 0, true, true); + emitJP(endlbl, 0.0f); + emitLabel (lbl_ne); + if (!regDead (A_IDX, ic)) + { + emit2 ("push", "af"); + cost (1, 1); + } + cheapMove (result->aop, 0, ic->op == NE_OP ? ASMOP_ONE : ASMOP_ZERO, 0, true, true); + emitLabel (endlbl); + if (!regDead (A_IDX, ic)) + popAF(); + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +static void +genXorByte (const asmop *result_aop, const asmop *left_aop, const asmop *right_aop, int i, bool *pushed_a, bool a_dead, bool p_dead) +{ + if ((aopInReg (left_aop, i, A_IDX) || aopInReg (left_aop, i, P_IDX) || left_aop->type == AOP_DIR) && + aopIsLitVal (right_aop, i, 1, 0xff) && aopSame (result_aop, i, left_aop, i, 1)) + { + emit2 ("not", "%s", aopGet (left_aop, i)); + cost (1, 1); + } + else if (aopIsLitVal (right_aop, i, 1, 0x00)) + { + cheapMove (result_aop, i, left_aop, i, a_dead, true); + } + else + { + if (!a_dead && !*pushed_a) + { + pushAF(); + *pushed_a = true; + } + + if ((left_aop->type == AOP_DIR || aopInReg (left_aop, i, P_IDX)) && aopSame (left_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right_aop, i, true, true); + emit2 ("xor", "%s, a", aopGet (left_aop, i)); + cost (1, 1); + } + else if ((right_aop->type == AOP_DIR || aopInReg (right_aop, i, P_IDX)) && aopSame (right_aop, i, result_aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("xor", "%s, a", aopGet (right_aop, i)); + cost (1, 1); + } + else if (right_aop->type == AOP_STK) + { + if (!p_dead || aopInReg (left_aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + cheapMove (ASMOP_P, 0, right_aop, i, false, true); + emit2 ("xor", "a, p"); + cost (1, 1); + cheapMove (result_aop, i, ASMOP_A, 0, true, true); + } + else + { + cheapMove (ASMOP_A, 0, left_aop, i, true, true); + emit2 ("xor", "a, %s", aopGet (right_aop, i)); + cost (1, 1); + cheapMove (result_aop, i, ASMOP_A, 0, true, true); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for or */ +/*-----------------------------------------------------------------*/ +static void +genXor (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genXor", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + int skip_byte = -1; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 1, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + bool a_free = regDead (A_IDX, ic); + bool pushed_a = false; + + for (int i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX)) + { + genXorByte (result->aop, left->aop, right->aop, i, &pushed_a, a_free, regDead (P_IDX, ic)); + skip_byte = i; + + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + } + + for (int i = 0; i < size; i++) + { + if (i == skip_byte) + continue; + + genXorByte (result->aop, left->aop, right->aop, i, &pushed_a, a_free, regDead (P_IDX, ic)); + + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + } + + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genOr", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + for (int i = 0; i < size; i++) + { + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : -1; + + if (aopIsLitVal (right->aop, i, 1, 0x00)) + { + cheapMove (result->aop, i, left->aop, i, true, true); + } + else if ((left->aop->type == AOP_SFR || aopInReg (left->aop, i, P_IDX) && !TARGET_IS_PDK13 /* set1 has weird encoding on pdk13, and is not yet supported by the assembler */) && aopSame (left->aop, i, result->aop, i, 1) && bit >= 0) + { + emit2 ("set1", "%s, #%d", aopGet (left->aop, i), bit); + cost (1, 1); + } + else if ((left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX) && right->aop->type != AOP_STK) && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("or", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + } + else if ((right->aop->type == AOP_DIR || aopInReg (right->aop, i, P_IDX) && left->aop->type != AOP_STK) && aopSame (right->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("or", "%s, a", aopGet (right->aop, i)); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic) || aopInReg (left->aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + emit2 ("or", "a, p"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("or", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (const iCode *ic, iCode *ifx) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genAnd", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || aopInReg (right->aop, 0, A_IDX) || right->aop->type == AOP_STK) + { + operand *t = right; + right = left; + left = t; + } + + if (ifx && IC_FALSE (ifx) && result->aop->type == AOP_CND) + { + int i, j, nonzero; + + // Find the non-zero byte. + if (right->aop->type != AOP_LIT) + { + wassert (right->aop->size == 1); + i = 0; + nonzero = 1; + } + else + for (j = 0, nonzero = 0, i = 0; j < left->aop->size; j++) + if (byteOfVal (right->aop->aopu.aop_lit, j)) + { + i = j; + nonzero++; + } + + wassertl (nonzero <= 1, "Code generation for jump on bitwise and can handle at most one nonzero byte"); + + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : - 1; + + if (aopInReg (left->aop, i, P_IDX) && bit >= 0) + { + emit2 (IC_FALSE (ifx) ? "t1sn" : "t0sn", "p, #%d", bit); + cost (1, 1.5); + } + else if (aopInReg (left->aop, i, P_IDX) && regDead (P_IDX, ic) && + (byteOfVal (right->aop->aopu.aop_lit, i) == 0x7f || byteOfVal (right->aop->aopu.aop_lit, i) == 0xfe)) + { + emit2 (byteOfVal (right->aop->aopu.aop_lit, 0) == 0x7f ? "sl" : "sr", "p"); + emit2 (IC_FALSE (ifx) ? "t0sn" : "t1sn", "f, z"); + cost (2, 2.5); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (!aopIsLitVal (right->aop, i, 1, 0xff)) + { + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + } + if (TARGET_IS_PDK13 && IC_FALSE (ic)) // pdk13 does not have cneqsn + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (0)); + emit2 ("ceqsn", "a, #0x00"); + cost (1, 1.5); + emitJP (tlbl, 0.5f); + emitJP (IC_FALSE (ifx), 0.5f); + emitLabel (tlbl); + goto release; + } + else + { + emit2 (IC_FALSE (ifx) ? "cneqsn" : "ceqsn", "a, #0x00"); + cost (1, 1.5); + } + } + + emitJP (IC_FALSE (ifx) ? IC_FALSE (ifx) : IC_TRUE (ifx), 0.5f); + + goto release; + } + else if (ifx && IC_TRUE (ifx) && result->aop->type == AOP_CND) + { + for (int i = 0; i < right->aop->size; i++) + { + int bit = right->aop->type == AOP_LIT ? isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) : - 1; + + if (aopIsLitVal (right->aop, i, 1, 0x00)) + continue; + else if (aopInReg (left->aop, i, P_IDX) && bit >= 0) + { + emit2 ("t0sn", "p, #%d", bit); + cost (1, 1.5); + } + else if (aopInReg (left->aop, i, P_IDX) && regDead (P_IDX, ic) && + (byteOfVal (right->aop->aopu.aop_lit, i) == 0x7f || byteOfVal (right->aop->aopu.aop_lit, i) == 0xfe)) + { + emit2 (byteOfVal (right->aop->aopu.aop_lit, 0) == 0x7f ? "sl" : "sr", "p"); + emit2 ("t1sn", "f, z"); + cost (2, 2.5); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + if (!aopIsLitVal (right->aop, i, 1, 0xff)) + { + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + } + emit2 ("ceqsn", "a, #0x00"); + cost (1, 1.5); + } + + emitJP (IC_TRUE (ifx), 0.5f); + } + + goto release; + } + + for (int i = 0; i < size; i++) + { + if (aopInReg (right->aop, i, A_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + int bit = right->aop->type == AOP_LIT ? isLiteralBit (~byteOfVal (right->aop->aopu.aop_lit, i) & 0xff) : -1; + + if (aopIsLitVal (right->aop, i, 1, 0xff)) + cheapMove (result->aop, i, left->aop, i, true, true); + else if (aopIsLitVal (right->aop, i, 1, 0x00)) + cheapMove (result->aop, i, ASMOP_ZERO, 0, true, true); + else if ((left->aop->type == AOP_SFR || aopInReg (left->aop, i, P_IDX) /* set1 has weird encoding on pdk13, and is not yet supported by the assembler */) && aopSame (left->aop, i, result->aop, i, 1) && bit >= 0) + { + emit2 ("set0", "%s, #%d", aopGet (left->aop, i), bit); + cost (1, 1); + } + else if ((left->aop->type == AOP_DIR || aopInReg (left->aop, i, P_IDX) && right->aop->type != AOP_STK) && aopSame (left->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("and", "%s, a", aopGet (left->aop, i)); + cost (1, 1); + } + else if ((right->aop->type == AOP_DIR || aopInReg (right->aop, i, P_IDX) && left->aop->type != AOP_STK) && aopSame (right->aop, i, result->aop, i, 1)) + { + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("and", "%s, a", aopGet (right->aop, i)); + cost (1, 1); + } + else if (right->aop->type == AOP_STK) + { + if (!regDead (P_IDX, ic) || aopInReg (left->aop, i, P_IDX)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + cheapMove (ASMOP_P, 0, right->aop, i, false, true); + emit2 ("and", "a, p"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + else + { + if (left->aop->type == AOP_STK && (!regDead (P_IDX, ic) || aopInReg (right->aop, i, P_IDX))) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, left->aop, i, true, true); + emit2 ("and", "a, %s", aopGet (right->aop, i)); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genLeftShift", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + int size = result->aop->size; + + if (right->aop->type == AOP_LIT) + { + int shCount = ulFromVal (right->aop->aopu.aop_lit); + int offset = shCount / 8; + + genMove_o (result->aop, offset, left->aop, 0, result->aop->size - shCount / 8, regDead (A_IDX, ic)); + genMove_o (result->aop, 0, ASMOP_ZERO, 0, offset, regDead (A_IDX, ic)); + shCount %= 8; + + if (!shCount) + goto release; + + bool loop = (shCount > 2 + ((size - offset) <= 1) * 2 + optimize.codeSpeed) && !(size == 1 && aopInReg (result->aop, 0, A_IDX)) && + regDead (A_IDX, ic) && !aopInReg (result->aop, 0, A_IDX) && !aopInReg (result->aop, 1, A_IDX); + symbol *tlbl = (!loop || regalloc_dry_run) ? 0 : newiTempLabel (0); + + if (loop) + { + emit2 ("mov", "a, #%d", shCount); + cost (1, 1); + emitLabel (tlbl); + regalloc_dry_run_cycle_scale = shCount; + shCount = 1; + if (result->aop->type == AOP_STK) + pushAF(); + } + + while (shCount) + { + if (shCount == 7 && (size - offset) == 1 && (result->aop->type == AOP_REG || result->aop->type == AOP_DIR)) + { + emit2 ("sr", "%s", aopGet (result->aop, offset)); + if (aopInReg (result->aop, 0, A_IDX)) + emit2("mov", "a, #0x00"); + else + emit2 ("clear", "%s", aopGet (result->aop, offset)); + emit2 ("src", "%s", aopGet (result->aop, offset)); + shCount = 0; + continue; + } + else if (shCount >= 4 && (size - offset) == 1 && aopInReg (result->aop, offset, A_IDX)) + { + emit2 ("swap", "a"); + emit2 ("and", "a, #0xf0"); + cost (2, 2); + shCount -= 4; + continue; + } + + for (int i = offset; i < size; i++) + { + if (result->aop->type == AOP_STK) + { + cheapMove (ASMOP_A, 0, result->aop, i, true, i <= offset); + emit2((i > offset) ? "slc" : "sl", "a"); + cost (1, 1); + cheapMove (result->aop, i, ASMOP_A, 0, true, i + 1 != size); + } + else + { + emit2((i > offset) ? "slc" : "sl", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + } + shCount--; + } + + if (loop) + { + if (result->aop->type == AOP_STK) + popAF(); + emit2 ("dzsn", "a"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); + } + + regalloc_dry_run_cycle_scale = 1; + } + else + { + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (0); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel (0); + + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + emitLabel (tlbl1); + emit2 ("sub", "a, #1"); + emit2 ("t0sn", "f, c"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl2->key)); + cost (3, 3); + + for(int i = 0; i < size; i++) + { + emit2(i ? "slc" : "sl", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl1->key)); + cost (1, 1); + + emitLabel (tlbl2); + } + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generates code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genRightShift", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + bool pushed_a = false; + + int size = result->aop->size; + + if (right->aop->type == AOP_LIT) + { + int shCount = ulFromVal (right->aop->aopu.aop_lit); + + if (SPEC_USIGN (getSpec (operandType (left)))) + { + genMove_o (result->aop, 0, left->aop, shCount / 8, result->aop->size, regDead (A_IDX, ic)); + size -= shCount / 8; + shCount %= 8; + } + else + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + if (!shCount) + goto release; + + bool loop = (shCount > 2 + (size == 1) * 2 + optimize.codeSpeed) && !(size == 1 && aopInReg (result->aop, 0, A_IDX)) && + regDead (A_IDX, ic) && !aopInReg (result->aop, 0, A_IDX) && !aopInReg (result->aop, 1, A_IDX); + + symbol *tlbl = (!loop || regalloc_dry_run) ? 0 : newiTempLabel (0); + + if (loop) + { + emit2 ("mov", "a, #%d", shCount); + cost (1, 1); + emitLabel (tlbl); + regalloc_dry_run_cycle_scale = shCount; + shCount = 1; + } + + while (shCount) + { + if (SPEC_USIGN (getSpec (operandType (left))) && shCount == 7 && size == 1 && (result->aop->type == AOP_REG || result->aop->type == AOP_DIR)) + { + emit2 ("sl", "%s", aopGet (result->aop, 0)); + if (aopInReg (result->aop, 0, A_IDX)) + emit2("mov", "a, #0x00"); + else + emit2 ("clear", "%s", aopGet (result->aop, 0)); + emit2 ("slc", "%s", aopGet (result->aop, 0)); + shCount = 0; + continue; + } + else if (SPEC_USIGN (getSpec (operandType (left))) && shCount >= 4 && size == 1 && aopInReg (result->aop, 0, A_IDX)) + { + emit2 ("swap", "a"); + emit2 ("and", "a, #0x0f"); + cost (2, 2); + shCount -= 4; + continue; + } + + if (!SPEC_USIGN (getSpec (operandType (left))) && (loop || !regDead (A_IDX, ic))) + { + pushAF(); + pushed_a = true; + } + + // Padauk has no arithmetic right shift instructions. + // So we need this emulation sequence here. + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + if (aopInReg (result->aop, size - 1, A_IDX) && + regDead (P_IDX, ic) && (size == 1 || !aopInReg (result->aop, 0, P_IDX))) + { + cheapMove (ASMOP_P, 0, ASMOP_A, 0, true, true); + emit2 ("sl", "p"); + emit2 ("src", "a"); + cost (2, 2); + } + else if (aopInReg (result->aop, size - 1, A_IDX)) + { + emit2 ("sl", "a"); + emit2 ("t0sn", "f, c"); + emit2 ("or", "a, #0x01", aopGet (result->aop, size - 1)); + emit2 ("src", "a"); + emit2 ("src", "a"); + cost (5, 5); + } + else + { + if (size > 1 && aopInReg (result->aop, 0, A_IDX) || !regDead (A_IDX, ic)) + { + wassert (regalloc_dry_run); + cost (500, 500); + } + cheapMove (ASMOP_A, 0, result->aop, size - 1, true, true); + emit2 ("sl", "a"); + emit2 ("src", aopGet (result->aop, size - 1)); + cost (2, 2); + } + } + else + { + emit2("sr", aopGet (result->aop, size - 1)); + cost (1, 1); + } + + for(int i = size - 2; i >= 0; i--) + { + if (pushed_a && aopInReg (result->aop, i, A_IDX)) + { + wassert (regalloc_dry_run); + cost (500, 500); + } + emit2 ("src", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + shCount--; + } + + if (loop) + { + if (pushed_a) + { + popAF(); + pushed_a = false; + } + emit2 ("dzsn", "a"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); + } + + regalloc_dry_run_cycle_scale = 1; + } + else + { + genMove (result->aop, left->aop, !aopInReg (right->aop, 0, A_IDX)); + + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (0); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel (0); + + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + emitLabel (tlbl1); + + emit2 ("sub", "a, #1"); + emit2 ("t0sn", "f, c"); + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl2->key)); + cost (3, 3); + + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + pushAF(); + pushed_a = true; + } + + // Padauk has no arithmetic right shift instructions. + // So we need this emulation sequence here. + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + emit2 ("mov", "a, #0x01"); + emit2 ("sl", aopGet (result->aop, size - 1)); + emit2 ("t0sn", "f, c"); + emit2 ("or", "%s, a", aopGet (result->aop, size - 1)); + emit2 ("src", aopGet (result->aop, size - 1)); + emit2 ("src", aopGet (result->aop, size - 1)); + cost (6, 6); + } + else + { + emit2("sr", aopGet (result->aop, size - 1)); + cost (1, 1); + } + + for(int i = size - 2; i >= 0; i--) + { + emit2 ("src", "%s", aopGet (result->aop, i)); + cost (1, 1); + } + + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + popAF(); + pushed_a = false; + } + + if (!regalloc_dry_run) + emit2 ("goto", "!tlabel", labelKey2num (tlbl1->key)); + cost (1, 1); + + emitLabel (tlbl2); + } + + if (pushed_a) + popAF(); + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + + +/*-----------------------------------------------------------------*/ +/* getBitFieldByte - process partial byte of bit-field */ +/*-----------------------------------------------------------------*/ +static void getBitFieldByte (int len, int str, bool sex) +{ + wassert (len >= 0 && len < 8); + + bool mask = len + str != 8; + + // Shift + if (len == 1 && str == 7) + { + emit2 ("sl", "a"); + emit2 ("slc", "a"); + str = 0; + mask = true; + } + if (str >= 4) + { + emit2 ("swap", "a"); + cost (1, 1); + str -= 4; + mask = true; + } + while (str--) + { + emit2 ("sr", "a"); + cost (1, 1); + } + + // Mask + if (mask) + { + emit2 ("and", "a, #0x%02x", 0xff >> (8 - len)); + cost (2, 1); + } + + // Sign-extend + if (sex) + { + symbol *const tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + emit2 ("ceqsn", "a, #0x%02x", 0x80 >> (8 - len)); + emit2 ("nop", ""); + emit2 ("t0sn", "f, c"); + if (tlbl) + emit2 ("goto", "!tlabel", labelKey2num (tlbl->key)); + emit2 ("or", "a, #0x%02x", (0xff00 >> (8 - len)) & 0xff); + cost (5, 5); + emitLabel (tlbl); + } +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPointerGet", "")); + + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + wassertl (right, "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (right), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + + bool pushed_a = false; + + bool bit_field = IS_BITVAR (getSpec (operandType (result))); + int size = result->aop->size; + int blen, bstr; + blen = bit_field ? (SPEC_BLEN (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + bstr = bit_field ? (SPEC_BSTR (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + + sym_link *type = operandType (left); + int ptype = (IS_PTR (type) && !IS_FUNC (type->next)) ? DCL_TYPE (type) : PTR_TYPE (SPEC_OCLS (getSpec (type))); + + if (left->aop->type == AOP_IMMD && ptype == GPOINTER && IS_SYMOP (left) && OP_SYMBOL (left)->remat) + ptype = left->aop->aopu.code ? CPOINTER : POINTER; + + wassertl (aopIsLitVal (right->aop, 0, 2, 0x0000), "Unimplemented nonzero right operand in pointer read"); + + if (left->aop->type == AOP_IMMD && (ptype == POINTER || ptype == CPOINTER)) + { + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (!regDead (A_IDX, ic) && !pushed_a) + { + pushAF(); + pushed_a = true; + } + + if (ptype == POINTER) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("call", "%s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 4); + } + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 > 0)) + { + wassert (!pushed_a); + pushAF(); + pushed_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } +#if 0 // TODO: Implement alignment requirements - ldt16 needs 16-bit-aligned operand + else if (TARGET_IS_PDK15 && ptype == CPOINTER && left->aop->type == AOP_DIR && size == 1) // pdk15 has ldtabl for efficient read from code space via a 12-bit address (top nibble of 16-bit value is ignored). + { + emit2 ("ldtabl", "a, %s", aopGet (left->aop, 0)); + cost (1, 2); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + goto release; + } +#endif + else if (ptype == POINTER) // Try to use efficient idxm when we know the target is in RAM. + { + const asmop *ptr_aop = (left->aop->type == AOP_DIR && TARGET_IS_PDK16) ? left->aop : ASMOP_P; + + cheapMove (ptr_aop, 0, left->aop, 0, true, true); + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (i != 0 && aopInReg (ptr_aop, 0, P_IDX) && aopInReg (result->aop, i - 1, P_IDX)) // Would have been overwritten on previous byte. + { + cost (500, 500); + wassert (regalloc_dry_run); + } + + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + pushAF(); + pushed_a = true; + } + else + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + + if (i + 1 != size) + { + emit2 ("inc", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + } + if (ptr_aop == left->aop && !(aopInReg (left->aop, 0, P_IDX) && regDead (P_IDX, ic))) + for (int i = 1; i < size; i++) + { + emit2 ("dec", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + goto release; + } + else // Generic, but also inefficient. + { + if (!regDead (A_IDX, ic)) + { + pushAF(); + pushed_a = true; + } + + if (!bit_field && size == 2) + { + genMove (ASMOP_PA, left->aop, true); + emit2 ("call", "__gptrget2"); + cost (1, (ptype == CPOINTER) ? 32 : 13); + genMove (result->aop, ASMOP_AP, true); + goto release; + } + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (i != 0 && (aopInReg (left->aop, 0, A_IDX) || aopInReg (left->aop, 1, A_IDX) || aopInReg (result->aop, i - 1, P_IDX))) // Would have been overwritten on previous byte. + { + cost (500, 500); + wassert (regalloc_dry_run); + } + + genMove (ASMOP_PA, left->aop, true); + if (i > 2) + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", i); + emit2 ("xch", "a, p"); + emit2 ("addc", "a"); + cost (4, 4); + } + else + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + emit2 ("addc", "a"); + cost (2, 2); + } + + emit2 ("call", "__gptrget"); + cost (1, (ptype == CPOINTER) ? 16 : 8); + + if (bit_field && blen < 8) + getBitFieldByte (blen, bstr, !SPEC_USIGN (getSpec (operandType (result)))); + + if (aopInReg (result->aop, i, P_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + wassert (regalloc_dry_run); + cost (200, 200); + } + else if (aopInReg (result->aop, i, A_IDX) && (!bit_field ? i + 1 < size : blen - 8 <= 0)) + { + pushAF(); + pushed_a = true; + } + else + { + cheapMove (result->aop, i, ASMOP_A, 0, true, true); + } + } + goto release; + } + +release: + if (pushed_a) + popAF(); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode *ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genPointerSet", "")); + + aopOp (left, ic); + aopOp (right, ic); + + bool pushed_p = false; + + bool bit_field = IS_BITVAR (getSpec (operandType (right))) || IS_BITVAR (getSpec (operandType (left))); + int size = right->aop->size; + + int blen, bstr; + blen = bit_field ? (SPEC_BLEN (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + bstr = bit_field ? (SPEC_BSTR (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + +#if 0 // TODO: Implement alignment requirements - idxm needs 16-bit-aligned operand + if (left->aop->type == AOP_DIR && TARGET_IS_PDK16 && !bit_field) + { + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("idxm", "%s, a", aopGet (left->aop, 0)); + cost (1, 2); + if (i + 1 != size) + { + emit2 ("inc", "%s", aopGet (left->aop, 0)); + cost (1, 1); + } + } + for (int i = 1; i < size; i++) + { + emit2 ("dec", "%s", aopGet (left->aop, 0)); + cost (1, 1); + } + } + else +#endif + if (right->aop->type == AOP_STK && !bit_field) + { + if (!regDead (P_IDX, ic)) + { + emit2 ("xch", "a, p"); + cost (1, 1); + pushAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + pushed_p = true; + } + for (int i = 0; i < size; i++) + { + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + cheapMove (ASMOP_P, 0, left->aop, 0, false, true); + if (i > 3) + { + emit2 ("xch", "a, p"); + emit2 ("add", "a, #%d", i); + emit2 ("xch", "a, p"); + cost (3, 3); + } + else + for (int j = 0; j < i; j++) + { + emit2 ("inc", "p"); + cost (1, 1); + } + emit2 ("idxm", "p, a"); + cost (1, 2); + } + } + else if (aopInReg (right->aop, 0, P_IDX) && aopInReg (right->aop, 1, A_IDX) && left->aop->type == AOP_IMMD) + { + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + 1); + cost (1, 1); + if (regDead (A_IDX, ic)) + { + emit2 ("mov", "a, p"); + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off); + cost (2, 2); + } + else + { + emit2 ("xch", "a, p"); + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off); + emit2 ("xch", "a, p"); + cost (3, 3); + } + } + else + { + const asmop *ptr_aop; + bool swapped = false; + + if (left->aop->type == AOP_IMMD) + ptr_aop = 0; +#if 0 // TODO: Implement alignment requirements - idxm needs 16-bit-aligned operand + else if (left->aop->type == AOP_DIR && size == 1 || aopInReg (left->aop, 0, P_IDX)) + ptr_aop = left->aop; +#endif + else if (aopInReg (right->aop, 0, P_IDX) && regDead (P_IDX, ic)) + { + if (left->aop->type == AOP_STK) + { + cost (250, 250); + wassert (regalloc_dry_run); + } + ptr_aop = ASMOP_P; + cheapMove (ASMOP_A, 0, left->aop, 0, true, true); + emit2 ("xch", "a, p"); + cost (1, 1); + G.p.type = AOP_INVALID; + swapped = true; + } + else + { + ptr_aop = ASMOP_P; + cheapMove (ptr_aop, 0, left->aop, 0, !aopInReg (right->aop, 0, A_IDX), true); + G.p.type = AOP_INVALID; + if (!regDead (P_IDX, ic) || aopInReg (right->aop, 0, P_IDX)) + { + wassert (regalloc_dry_run); + cost (1000, 1000); + } + } + + for (int i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (!ptr_aop && aopIsLitVal (right->aop, i, 1, 0) && !(bit_field || blen >= 8)) + { + emit2 ("clear", "%s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + continue; + } + + if (bit_field && blen < 8) + { + if (right->aop->type == AOP_LIT) + { + unsigned char mval = ~((0xff >> (8 - blen)) << bstr) & 0xff; + unsigned char bval = (byteOfVal (right->aop->aopu.aop_lit, i) << bstr) & ((0xff >> (8 - blen)) << bstr); + + if (!ptr_aop && (byteOfVal (right->aop->aopu.aop_lit, i) << bstr) == ((0xff >> (8 - blen)) << bstr)) + { + emit2 ("mov", "a, #0x%02x", bval); + emit2 ("or", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (2, 2); + continue; + } + else if (!ptr_aop && !bval) + { + emit2 ("mov", "a, #0x%02x", mval); + emit2 ("and", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (2, 2); + continue; + } + + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + + if ((byteOfVal (right->aop->aopu.aop_lit, i) << bstr) != ((0xff >> (8 - blen)) << bstr)) + { + emit2 ("and", "a, #0x%02x", mval); + cost (1, 1); + } + + if (bval) + { + emit2 ("or", "a, #0x%02x", bval); + cost (1, 1); + } + } + else if (bit_field && blen == 1) + { + if (!regDead (A_IDX, ic)) + { + cost (200, 200); + wassert (regalloc_dry_run); + } + + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + emit2 ("sr", "a"); + cost (1, 1); + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + emit2 ("and", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + emit2 ("t0sn", "f, c"); + emit2 ("or", "a, #0x%02x", 1 << bstr); + cost (3, 3); + } + else + { + if (aopInReg (right->aop, i, A_IDX) || !regDead (A_IDX, ic)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + if (!ptr_aop) + { + emit2 ("mov", "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "a, %s", aopGet (ptr_aop, 0)); + cost (1, 2); + } + emit2 ("and", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + cost (1, 1); + + emit2 ("xch", "a, p"); + cost (1, 1); + if (!regDead (P_IDX, ic) && !pushed_p) + { + pushAF (); + pushed_p = true; + } + if (ptr_aop && aopInReg (ptr_aop, 0, P_IDX)) + pushAF (); + if (!aopInReg (right->aop, i, P_IDX)) // xch above would already have brought it into a. + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + if (bstr >= 4) + { + emit2 ("swap", "a"); + cost (1, 1); + } + for (int j = (bstr >= 4 ? 4 : 0); j < bstr; j++) + { + emit2 ("sl", "a"); + cost (1, 1); + } + emit2 ("and", "a, #0x%02x", (0xff >> (8 - blen)) << bstr); + emit2 ("or", "a, p"); + cost (2, 2); + + if (ptr_aop && aopInReg (ptr_aop, 0, P_IDX)) + { + emit2 ("mov", "p, a"); + cost (1, 1); + popAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + } + } + } + else if (!swapped) + { + if (!aopInReg (right->aop, i, A_IDX) && !regDead (A_IDX, ic)) + { + cost (100, 100); + wassert (regalloc_dry_run); + } + cheapMove (ASMOP_A, 0, right->aop, i, true, true); + } + + if (!ptr_aop) + { + emit2 ("mov", "%s+%d, a", left->aop->aopu.immd, left->aop->aopu.immd_off + i); + cost (1, 1); + } + else + { + emit2 ("idxm", "%s, a", aopGet (ptr_aop, 0)); + cost (1, 2); + } + + if (i + 1 != size && ptr_aop) + { + emit2 ("inc", "%s", aopGet (ptr_aop, 0)); + cost (1, 1); + } + } + } + + if (pushed_p) + { + emit2 ("xch", "a, p"); + cost (1, 1); + popAF (); + emit2 ("xch", "a, p"); + cost (1, 1); + } + + freeAsmop (right); + freeAsmop (left); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (const iCode *ic) +{ + operand *result, *right; + + D (emit2 ("; genAssign", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + aopOp (right, ic); + aopOp (result, ic); + + wassert (result->aop->type != AOP_DUMMY || right->aop->type != AOP_DUMMY); + + if ((result->aop->type == AOP_STK || right->aop->type == AOP_STK)&& !regDead (P_IDX, ic)) + { + cost (100, 100); // Todo: Implement! + wassert (regalloc_dry_run); + } + + if (right->aop->type == AOP_DUMMY) + { + int i; + D (emit2 ("; Dummy write", "")); + for (i = 0; i < result->aop->size; i++) + cheapMove (result->aop, i, ASMOP_A, 0, false, true); + } + else if (result->aop->type == AOP_DUMMY) + { + wassert (0); +#if 0 + int i; + D (emit2 ("; Dummy read", "")); + + if (!regDead(A_IDX, ic) && right->aop->type == AOP_DIR) + for (i = 0; i < right->aop->size; i++) + emit3_o (A_TNZ, right->aop, i, 0, 0); + else + { + if (!regDead(A_IDX, ic)) + push (ASMOP_A, 0, 1); + for (i = 0; i < right->aop->size; i++) + cheapMove (ASMOP_A, 0, right->aop, i, FALSE); + if (!regDead(A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } +#endif + } + else + genMove(result->aop, right->aop, regDead (A_IDX, ic)); + + wassert (result->aop != right->aop); + + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (const iCode *ic) +{ + operand *const cond = IC_COND (ic); + + D (emit2 ("; genIfx", "")); + + aopOp (cond, ic); + + if ((aopInReg (cond->aop, 0, A_IDX) && aopInReg (cond->aop, 1, P_IDX) || + aopInReg (cond->aop, 0, P_IDX) && aopInReg (cond->aop, 1, A_IDX)) && + !regDead (A_IDX, ic)) + { + if (IC_TRUE (ic)) + { + emit2 ("ceqsn", "a, #0"); + cost (1, 1.5f); + emitJP (IC_TRUE (ic), 0.5f); + emit2 ("ceqsn", "a, p"); + cost (1, 0.75f); + emitJP (IC_TRUE (ic), 0.375f); + goto release; + } + else if (!TARGET_IS_PDK13) + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, #0"); + if (!regalloc_dry_run) + emit2 ("goto", "#!tlabel", labelKey2num (tlbl->key)); + emit2 ("cneqsn", "a, p"); + cost (3, 3.25f); + emitJP (IC_FALSE (ic), 0.375f); + emitLabel (tlbl); + goto release; + } + } + + int skip_byte; + if (IS_FLOAT (operandType (cond))) // Clear sign bit for float. + { + cheapMove (ASMOP_A, 0, cond->aop, cond->aop->size - 1, true, true); + emit2 ("and", "a, #0x7f"); + cost (1, 1); + skip_byte = cond->aop->size - 1; + } + else if (aopInReg (cond->aop, 1, A_IDX)) + { + skip_byte = 1; + } + else + { + cheapMove (ASMOP_A, 0, cond->aop, 0, true, true); + skip_byte = 0; + } + + for (int i = 0; i < cond->aop->size; i++) + { + if (i == skip_byte) + continue; + + if (cond->aop->type == AOP_STK) + { + pushAF (); + cheapMove (ASMOP_P, 0, cond->aop, i, true, true); + popAF (); + emit2 ("or", "a, p"); + cost (1, 1); + } + else + { + emit2 ("or", "a, %s", aopGet (cond->aop, i)); + cost (1, 1); + } + } + + if (TARGET_IS_PDK13 && IC_FALSE (ic)) // pdk13 does not have cneqsn. + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit2 ("ceqsn", "a, #0x00"); + emitJP (tlbl, 0.0f); + cost (2, 3); + emitJP (IC_FALSE (ic), 0.0f); + emitLabel (tlbl); + } + else + { + emit2 (IC_FALSE (ic) ? "cneqsn" : "ceqsn", "a, #0x00"); + cost (1, 1); + emitJP (IC_FALSE (ic) ? IC_FALSE (ic) : IC_TRUE (ic), 0.0f); + } + +release: + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (const iCode *ic) +{ + operand *result, *left, *right; + + D (emit2 ("; genAddrOf", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + wassert (result); + wassert (left); + wassert (IS_TRUE_SYMOP (left)); + wassert (right && IS_OP_LITERAL (IC_RIGHT (ic))); + + const symbol *sym = OP_SYMBOL_CONST (left); + wassert (sym); + + aopOp (result, ic); + + int size = result->aop->size; + + wassert (size == 1 || size == 2); + if (sym->onStack) + { + int s = sym->stack + (sym->stack < 0 ? G.stack.param_offset : 0) + operandLitValue (right); + + if (G.p.type == AOP_STK && s == G.p.offset) + cheapMove (result->aop, 0, ASMOP_P, 0, true, true); + else + { + cheapMove (ASMOP_A, 0, ASMOP_SP, 0, true, true); + emit2 ("add", "a, #0x%02x", (s - G.stack.pushed) & 0xff); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + + if (size == 2) + cheapMove (result->aop, 1, ASMOP_ZERO, 0, true, true); + } + else if (PTR_TYPE (SPEC_OCLS (getSpec (operandType (IC_LEFT (ic))))) == CPOINTER) // In ROM + { + wassert (size == 2); + + emit2 ("mov", "a, #<(%s + %d)", sym->rname, (int)operandLitValue (right)); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + emit2 ("mov", "a, #>(%s + 0x8000 + %d)", sym->rname, (int)operandLitValue (right)); + cheapMove (result->aop, 1, ASMOP_A, 0, true, true); + } + else // In RAM + { + emit2 ("mov", "a, #(%s + %d)", sym->rname, (int)operandLitValue (right)); + cost (1, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + if (size == 2) + cheapMove (result->aop, 1, ASMOP_ZERO, 0, true, true); + } + + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generate code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (const iCode *ic) +{ + operand *cond; + + D (emit2 ("; genJumpTab", "")); + + cond = IC_JTCOND (ic); + + aopOp (cond, ic); + + wassertl (cond->aop->size == 1, "Jump table not implemented for operands wider than 1 byte."); + + cheapMove (ASMOP_A, 0, cond->aop, 0, true, true); + + emit2 ("add", "a, #0x01"); + emit2 ("pcadd", "a"); + cost (2, 3); + + for (symbol *jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + if (!regalloc_dry_run) + emit2 ("goto", "#!tlabel", labelKey2num (jtab->key)); + cost (1, 0); + } + + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genCast - generate code for cast */ +/*-----------------------------------------------------------------*/ +static void +genCast (const iCode *ic) +{ + operand *result, *right; + int offset; + sym_link *resulttype, *righttype; + + D (emit2 ("; genCast", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + resulttype = operandType (result); + righttype = operandType (right); + + bool pushed_a = false; + + if ((getSize (resulttype) <= getSize (righttype) || !IS_SPEC (righttype) || (SPEC_USIGN (righttype) || IS_BOOLEAN (righttype))) && + (!IS_BOOLEAN (resulttype) || IS_BOOLEAN (righttype))) + { + genAssign (ic); + return; + } + + aopOp (right, ic); + aopOp (result, ic); + + if (!regDead (A_IDX, ic)) + { + pushAF (); + pushed_a = true; + } + + if (IS_BOOL (resulttype)) + { + int size = right->aop->size; + int skipbyte; + + if (aopInReg (right->aop, 1, A_IDX)) + skipbyte = 1; + else + { + cheapMove (ASMOP_A, 0, right->aop, 0, true, true); + skipbyte = 0; + } + + for(offset = 0; offset < size; offset++) + { + if (offset == skipbyte) + continue; + emit2 ("or", "a, %s", aopGet (right->aop, offset)); + cost (1, 1); + } + + emit2 ("ceqsn", "a, #0x00"); + emit2 ("mov", "a, #0x01"); + if (!regalloc_dry_run) // Dummy label as target for ceqsn to prevent peephole optimizer from optimizing out mov. + { + symbol *tlbl = newiTempLabel (0); + emitLabel (tlbl); + } + cost (2, 2); + + cheapMove (result->aop, 0, ASMOP_A, 0, true, true); + } + else // Cast to signed type + { + genMove_o (result->aop, 0, right->aop, 0, right->aop->size, regDead (A_IDX, ic)); + + int size = result->aop->size - right->aop->size; + offset = right->aop->size; + + cheapMove (ASMOP_A, 0, result->aop, right->aop->size - 1, true, true); + emit2 ("sl", "a"); + emit2 ("mov", "a, #0x00"); + emit2 ("subc", "a"); + cost (3, 3); + + while (size--) + cheapMove (result->aop, offset++, ASMOP_A, 0, true, true); + } + + if (pushed_a) + popAF (); + + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (const iCode *ic) +{ + operand *op; + + if ((op = IC_LEFT (ic)) && IS_SYMOP (op)) + ; + else if ((op = IC_RIGHT (ic)) && IS_SYMOP (op)) + ; + else + return; + + aopOp (op, ic); + + if (!regDead(A_IDX, ic) && op->aop->type == AOP_DIR && op->aop->size <= 2) + for (int i = 0; i < op->aop->size; i++) + { + emit2 ("ceqsn", "a, %s", aopGet (op->aop, i)); + emit2 ("nop", ""); + cost (2, 2); + } + else + { + if (!regDead(A_IDX, ic)) + { + emit2 ("push", "af"); + cost (1, 1); + } + + for (int i = 0; i < op->aop->size; i++) + cheapMove (ASMOP_A, 0, op->aop, i, true, true); + + if (!regDead(A_IDX, ic)) + { + emit2 ("pop", "af"); + cost (1, 1); + } + } + + freeAsmop (op); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is to be rematerialized */ +/*-----------------------------------------------------------------*/ +static bool +resultRemat (const iCode *ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + const symbol *sym = OP_SYMBOL_CONST (IC_RESULT (ic)); + + if (!sym->remat) + return(false); + + bool completely_spilt = TRUE; + for (unsigned int i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + + if (completely_spilt) + return(true); + } + + return (false); +} + +/*---------------------------------------------------------------------*/ +/* genSTM8Code - generate code for STM8 for a single iCode instruction */ +/*---------------------------------------------------------------------*/ +static void +genPdkiCode (iCode *ic) +{ + genLine.lineElement.ic = ic; + + if (resultRemat (ic)) + { + if (!regalloc_dry_run) + D (emit2 ("; skipping iCode since result will be rematerialized", "")); + return; + } + + if (ic->generated) + { + //if (!regalloc_dry_run) + D (emit2 ("; skipping generated iCode", "")); + return; + } + + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + wassertl (0, "Unimplemented iCode"); + break; + + case CALL: + case PCALL: + genCall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genReturn (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + genMinus (ic, ic->next && ic->next->op == IFX ? ic->next : 0); + break; + + case '*': + genMult (ic); + break; + + case '/': + case '%': + wassertl (0, "Unimplemented iCode"); + break; + + case '>': + case '<': + genCmp (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + wassertl (0, "Unimplemented iCode"); + break; + + case NE_OP: + case EQ_OP: + genCmpEQorNE (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + case OR_OP: + wassertl (0, "Unimplemented iCode"); + break; + + case '^': + genXor (ic); + break; + + case '|': + genOr (ic); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + case RLC: + wassertl (0, "Unimplemented iCode"); + break; + + case GETABIT: + wassertl (0, "Unimplemented iCode"); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic); + break; + + case SET_VALUE_AT_ADDRESS: + genPointerSet (ic); + break; + + case '=': + wassert (!POINTER_SET (ic)); + genAssign (ic); + break; + + case IFX: + genIfx (ic); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + case SEND: + wassertl (0, "Unimplemented iCode"); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + wassertl (0, "Unimplemented iCode: Critical section"); + break; + + case ENDCRITICAL: + wassertl (0, "Unimplemented iCode: Critical section"); + break; + + default: + fprintf (stderr, "iCode op %d:\n", ic->op); + wassertl (0, "Unknown iCode"); + } +} + +float +dryPdkiCode (iCode *ic) +{ + regalloc_dry_run = true; + regalloc_dry_run_cost_words = 0; + regalloc_dry_run_cost_cycles = 0; + + initGenLineElement (); + + genPdkiCode (ic); + + G.p.type = AOP_INVALID; + + destroy_line_list (); + + wassert (regalloc_dry_run); + + const unsigned int word_cost_weight = 2 << (optimize.codeSize * 3 + !optimize.codeSpeed * 3); + + return (regalloc_dry_run_cost_words * word_cost_weight + regalloc_dry_run_cost_cycles * ic->count); +} + +/*---------------------------------------------------------------------*/ +/* genPdkCode - generate code for Padauk for a block of intructions */ +/*---------------------------------------------------------------------*/ +void +genPdkCode (iCode *lic) +{ + int clevel = 0; + int cblock = 0; + int cln = 0; + regalloc_dry_run = false; + + for (iCode *ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->level != clevel || ic->block != cblock) + { + if (options.debug) + debugFile->writeScope (ic); + clevel = ic->level; + cblock = ic->block; + } + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + debugFile->writeCLine (ic); + + if (!options.noCcodeInAsm) + emit2 (";", "%s: %d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + cln = ic->lineno; + } + + regalloc_dry_run_cost_words = 0; + regalloc_dry_run_cost_cycles = 0; + + if (options.iCodeInAsm) + { + const char *iLine = printILine (ic); + emit2 ("; ic:", "%d: %s", ic->key, iLine); + dbuf_free (iLine); + } + + genPdkiCode(ic); + +#if 0 + D (emit2 (";", "Cost for generated ic %d : (%d, %f)", ic->key, regalloc_dry_run_cost_words, regalloc_dry_run_cost_cycles)); +#endif + } + + if (options.debug) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + /* now we are ready to call the + peephole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + G.p.type = AOP_INVALID; + + /* destroy the line list */ + destroy_line_list (); +} + diff --git a/src/pdk/gen.h b/src/pdk/gen.h new file mode 100644 index 0000000..e0eb838 --- /dev/null +++ b/src/pdk/gen.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for Padauk + + Written By - Philipp Krause . pkk@spth.de (2018) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef PDKGEN_H +#define PDKGEN_H 1 + +typedef enum +{ + AOP_INVALID, + /* Is a literal */ + AOP_LIT = 1, + /* Is in a register */ + AOP_REG, + /* Is partially in a register, partially in direct space */ + AOP_REGDIR, + /* Is on the stack */ + AOP_STK, + /* Is an immediate value */ + AOP_IMMD, + /* Is in direct space */ + AOP_DIR, + /* I/O register */ + AOP_SFR, + /* Is in code space */ + AOP_CODE, + /* Read undefined, discard writes */ + AOP_DUMMY, + /* Implicit condition operand */ + AOP_CND, +} +AOP_TYPE; + +/* asmop_byte: A type for the location a single byte + of an operand can be in */ +typedef struct asmop_byte +{ + bool in_reg; + union + { + reg_info *reg; /* Register this byte is in. */ + int stk; /* Stack offset for this byte. */ + } byteu; +} asmop_byte; + +/* asmop: A homogenised type for all the different + spaces an operand can be in */ +typedef struct asmop +{ + AOP_TYPE type; + short size; + union + { + value *aop_lit; + struct + { + char *immd; + int immd_off; + bool code; /* in code space */ + bool func; /* function address */ + }; + char *aop_dir; + asmop_byte bytes[8]; + } aopu; +} +asmop; + +void genPdkCode (iCode *); + +extern bool pdk_assignment_optimal; +void pdk_init_asmops (void); + +#endif + diff --git a/src/pdk/gen.o b/src/pdk/gen.o new file mode 100644 index 0000000..f9cc656 Binary files /dev/null and b/src/pdk/gen.o differ diff --git a/src/pdk/main.c b/src/pdk/main.c new file mode 100644 index 0000000..21b1fc2 --- /dev/null +++ b/src/pdk/main.c @@ -0,0 +1,714 @@ +/*------------------------------------------------------------------------- + main.c - Padauk specific definitions. + + Philipp Klaus Krause 2012-2018 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +#include "ralloc.h" +#include "peep.h" + +extern DEBUGFILE dwarf2DebugFile; +extern int dwarf2FinalizeFile(FILE *); + +static char pdk_defaultRules[] = { +#include "peeph.rul" + "" +}; + +static char *pdk_keywords[] = { + "at", + "code", + "data", + "interrupt", + "naked", + "near", + "reentrant", + "sfr", + "sfr16", + NULL +}; + +static void +pdk_genAssemblerStart (FILE *of) +{ + fprintf (of, "\n; default segment ordering in RAM for linker\n"); + tfprintf (of, "\t!area\n", DATA_NAME); + tfprintf (of, "\t!area\n", OVERLAY_NAME); + fprintf (of, "\n"); +} + +static void +pdk_genAssemblerEnd (FILE *of) +{ + if (options.out_fmt == 'E' && options.debug) + dwarf2FinalizeFile (of); +} + +int +pdk_genIVT(struct dbuf_s *oBuf, symbol **intTable, int intCount) +{ + dbuf_tprintf (oBuf, "\t.area\tHEADER (ABS)\n"); + dbuf_tprintf (oBuf, "\t.org\t 0x0020\n"); + if (interrupts[0]) + dbuf_tprintf (oBuf, "\tgoto\t%s\n", interrupts[0]->rname); + else + dbuf_tprintf (oBuf, "\treti\n"); + + return (true); +} + +static void +pdk_genInitStartup (FILE *of) +{ + fprintf (of, "\t.area\tPREG (ABS)\n"); + fprintf (of, "\t.org 0x00\n"); + fprintf (of, "p::\n"); + fprintf (of, "\t.ds 2\n"); + + + fprintf (of, "\t.area\tHEADER (ABS)\n"); // In the header we have 16 bytes. First should be nop. + fprintf (of, "\t.org 0x0000\n"); + fprintf (of, "\tnop\n"); // First word is a jump to self-test routine at end of ROM on some new devices. + + // Zero upper byte of pseudo-register p to make p usable for pointers. + fprintf (of, "\tclear\tp+1\n"); + + // Initialize stack pointer + if (options.stack_loc >= 0) + { + fprintf (of, "\tmov\ta, #0x%02x\n", options.stack_loc); + fprintf (of, "\tmov\tsp, a\n"); + } + else + { + fprintf (of, "\tmov\ta, #s_OSEG\n"); + fprintf (of, "\tadd\ta, #l_OSEG + 1\n"); + fprintf (of, "\tand\ta, #0xfe\n"); + fprintf (of, "\tmov\tsp, a\n"); + } + + fprintf (of, "\tcall\t__sdcc_external_startup\n"); + fprintf (of, "\tgoto\t__sdcc_gs_init_startup\n"); + + tfprintf (of, "\t!area\n", STATIC_NAME); + fprintf (of, "__sdcc_gs_init_startup:\n"); + + /* Init static & global variables */ + fprintf (of, "__sdcc_init_data:\n"); + + /* Zeroing memory (required by standard for static & global variables) */ + fprintf (of, "\tmov\ta, #s_DATA\n"); + fprintf (of, "\tmov\tp, a\n"); + fprintf (of, "\tgoto\t00002$\n"); + fprintf (of, "00001$:\n"); + fprintf (of, "\tmov\ta, #0x00\n"); + fprintf (of, "\tidxm\tp, a\n"); + fprintf (of, "\tinc\tp\n"); + fprintf (of, "\tmov\ta, #s_DATA\n"); + fprintf (of, "00002$:\n"); + fprintf (of, "\tadd\ta, #l_DATA\n"); + fprintf (of, "\tceqsn\ta, p\n"); + fprintf (of, "\tgoto\t00001$\n"); +} + +static void +pdk_init (void) +{ + asm_addTree (&asm_asxxxx_smallpdk_mapping); +} + +static void +pdk_reset_regparm (struct sym_link *funcType) +{ +} + +static int +pdk_reg_parm (sym_link *l, bool reentrant) +{ + return (0); +} + +static bool +pdk_parseOptions (int *pargc, char **argv, int *i) +{ + if (!strcmp (argv[*i], "--out-fmt-elf")) + { + options.out_fmt = 'E'; + debugFile = &dwarf2DebugFile; + return TRUE; + } + return FALSE; +} + +static void +pdk_finaliseOptions (void) +{ + port->mem.default_local_map = data; + port->mem.default_globl_map = data; +} + +static void +pdk_setDefaultOptions (void) +{ + options.out_fmt = 'i'; /* Default output format is ihx */ + options.data_loc = 0x02; /* First two bytes of RAM are used for the pseudo-register p */ + options.code_loc = 0x0022; + options.stack_loc = -1; +} + +static const char * +pdk_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + int result_size = IS_SYMOP (IC_RESULT(ic)) ? getSize (OP_SYM_TYPE (IC_RESULT(ic))) : 4; + + if (ic->op != '*') + return (false); + + return ((IS_LITERAL (left) || IS_LITERAL (right)) && result_size == 1); +} + +/* Indicate which extended bit operations this backend supports */ +static bool +hasExtBitOp (int op, int size) +{ + return (false); +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdldpdk", "-nf", "\"$1\"", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk13AsmCmd[] = +{ + "sdaspdk13", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk13[] = { "pdk13", NULL, }; + +PORT pdk13_port = +{ + TARGET_ID_PDK13, + "pdk13", + "PDK13", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk13AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk13, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk14AsmCmd[] = +{ + "sdaspdk14", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk14[] = { "pdk14", NULL, }; + +PORT pdk14_port = +{ + TARGET_ID_PDK14, + "pdk14", + "PDK14", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk14AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk14, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *pdk15AsmCmd[] = +{ + "sdaspdk15", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_pdk15[] = { "pdk15", NULL, }; + +PORT pdk15_port = +{ + TARGET_ID_PDK15, + "pdk15", + "PDK15", /* Target name */ + 0, /* Processor name */ + { + glue, + true, + NO_MODEL, + NO_MODEL, + 0, /* model == target */ + }, + { /* Assembler */ + pdk15AsmCmd, + 0, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + 0, //LINKCMD, + 0, + ".rel", + 1, + 0, /* crt */ + _libs_pdk15, /* libs */ + }, + { /* Peephole optimizer */ + pdk_defaultRules, + pdkinstructionSize, + 0, + 0, + 0, + pdknotUsed, + 0, + pdknotUsedFrom, + 0, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 1, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + "OSEG (OVR,DATA)", /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + 0, /* iabs_name */ + 0, /* name of segment for initialized variables */ + 0, /* name of segment for copies of initialized variables in code space */ + 0, + 0, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { 0, 0 }, + { /* stack information */ + +1, /* direction: stack grows up */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, false }, /* no int x int -> long multiplication support routine. */ + { 0, + { + 0, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 0, /* addressSize */ + 0, /* regNumRet */ + 0, /* regNumSP */ + 0, /* regNumBP */ + 0, /* offsetSP */ + }, + }, + { + 256, /* maxCount */ + 1, /* sizeofElement */ + {2, 0, 0}, /* sizeofMatchJump[] - the 0s here ensure that we only generate jump tables for 8-bit operands, which is all the backend can handle */ + {4, 0, 0}, /* sizeofRangeCompare[] */ + 1, /* sizeofSubtract */ + 2, /* sizeofDispatch */ + }, + "_", + pdk_init, + pdk_parseOptions, + 0, + 0, + pdk_finaliseOptions, /* finaliseOptions */ + pdk_setDefaultOptions, /* setDefaultOptions */ + pdk_assignRegisters, + pdk_getRegName, + 0, + 0, + pdk_keywords, + pdk_genAssemblerStart, + pdk_genAssemblerEnd, + pdk_genIVT, + 0, /* no genXINIT code */ + pdk_genInitStartup, /* genInitStartup */ + pdk_reset_regparm, + pdk_reg_parm, + 0, /* process_pragma */ + 0, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + 0, /* oclsExpense */ + false, /* data is represented in ROM using ret k instructions */ + true, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + false, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + 0, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 2, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/pdk/main.o b/src/pdk/main.o new file mode 100644 index 0000000..79372bb Binary files /dev/null and b/src/pdk/main.o differ diff --git a/src/pdk/peep.c b/src/pdk/peep.c new file mode 100644 index 0000000..153b090 --- /dev/null +++ b/src/pdk/peep.c @@ -0,0 +1,521 @@ +#include "common.h" +#include "SDCCgen.h" + +#include "peep.h" + +#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0) + +// #define D(_s) { printf _s; fflush(stdout); } +#define D(_s) + +#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) + +typedef enum +{ + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +static bool +isReturned(const char *what) +{ + symbol *sym; + sym_link *sym_lnk; + int size; + lineNode *l; + + l = _G.head; + do + { + l = l->next; + } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION); + + sym = OP_SYMBOL(IC_LEFT(l->ic)); + + if(sym && IS_DECL(sym->type)) + { + // Find size of return value. + specifier *spec; + if(sym->type->select.d.dcl_type != FUNCTION) + NOTUSEDERROR(); + spec = &(sym->etype->select.s); + if(spec->noun == V_VOID) + size = 0; + else if(spec->noun == V_CHAR || spec->noun == V_BOOL) + size = 1; + else if(spec->noun == V_INT && !(spec->b_long)) + size = 2; + else + size = 4; + + // Check for returned pointer. + sym_lnk = sym->type; + while (sym_lnk && !IS_PTR (sym_lnk)) + sym_lnk = sym_lnk->next; + if(IS_PTR(sym_lnk)) + size = 2; + } + else + { + NOTUSEDERROR(); + return TRUE; + } + + switch(*what) + { + case 'a': + return(size == 1 || size == 2); + case 'p': + return(size == 2); + default: + return false; + } +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each jump the label is at the end */ + p = strlen (pl->line) - 1 + pl->line; + + /* Skip trailing whitespace */ + while(isspace(*p)) + p--; + + /* scan backward until space or ',' */ + for (; p > pl->line; p--) + if (isspace(*p) || *p == ',') + break; + + /* sanity check */ + if (p == pl->line) + { + NOTUSEDERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if ( cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/* Check if reading arg implies reading what. */ +static bool argIs(const char *arg, const char *what) +{ + if (arg == NULL || strlen (arg) == 0) + return false; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + if (arg[0] == ',') + arg++; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + return !strncmp(arg, what, strlen(what)) && + (!arg[strlen(what)] || isspace((unsigned char)(arg[strlen(what)])) || arg[strlen(what)] == ','); +} + +static bool +stm8MightReadFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return true; + + if (ISINST (pl->line, "t0sn") || ISINST (pl->line, "t1sn")) + return argIs(strchr (pl->line, ','), what); + + if(ISINST (pl->line, "addc") || + ISINST (pl->line, "subc")) + return !strcmp(what, "c"); + + return false; +} + +static bool +pdkMightRead(const lineNode *pl, const char *what) +{ +//printf("pdkMightRead() for |%s|%s|\n", pl->line, what); + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8MightReadFlag(pl, what)); + else if (strcmp(what, "a") && strcmp(what, "p")) + return true; + + // Instructions that never read anything. + if (ISINST(pl->line, "engint") || + ISINST(pl->line, "disgint") || + ISINST(pl->line, "nop") || + ISINST(pl->line, "pop") || ISINST(pl->line, "popaf") || + ISINST(pl->line, "stopsys") || + ISINST(pl->line, "wdreset")) + return false; + + if (ISINST(pl->line, "ret") && strchr(pl->line + 2, '#') && !strcmp(what, "a")) + return false; + if (ISINST(pl->line, "ret")) + return isReturned(what); + + if (ISINST(pl->line, "mov")) + return argIs (strchr (pl->line, ','), what); + + if (ISINST (pl->line, "push") && argIs (pl->line + 4, "af") || ISINST (pl->line, "pushaf")) + return !strcmp(what, "a"); + + // Two-operand instructions that read both operands + if (ISINST (pl->line, "add") || + ISINST (pl->line, "and") || + ISINST (pl->line, "or") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "xor")) + return argIs (pl->line + 3, what) || argIs (strchr (pl->line, ','), what); + if (ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what) || argIs (strchr (pl->line, ','), what); + if (ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn")) + return argIs (pl->line + 6, what) || argIs (strchr (pl->line, ','), what); + + // One-operand instructions + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "pcadd") || + ISINST (pl->line, "stt16")) + return argIs (pl->line + 5, what); + + // Todo: addc, subc, xch + + return true; +} + +static bool +stm8SurelyWritesFlag(const lineNode *pl, const char *what) +{ + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return true; + + // Instructions that write all flags + if (ISINST (pl->line, "add") || + ISINST (pl->line, "addc") || + ISINST (pl->line, "ceqsn") || + ISINST (pl->line, "cneqsn") || + ISINST (pl->line, "dec") || + ISINST (pl->line, "dzsn") || + ISINST (pl->line, "inc") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "sub") || + ISINST (pl->line, "subc")) + return true; + + // Instructions that write c only + if (ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return !strcmp(what, "c"); + + // Instructions that write z only + if (ISINST (pl->line, "and") || + ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "or") || + ISINST (pl->line, "xor")) + return !strcmp(what, "z"); + + // mov writes z when the destination is a and hte source not an immediate only. + if (ISINST (pl->line, "mov") && !strcmp(what, "z") && pl->line[4] == 'a' && pl->line[5] == ',' && !strchr(pl->line, '#')) + return true; + + return false; +} + +static bool +pdkSurelyWrites(const lineNode *pl, const char *what) +{ + if (!strcmp(what, "z") || !strcmp(what, "c") || !strcmp(what, "ac") || !strcmp(what, "ov")) + return (stm8SurelyWritesFlag(pl, what)); + + if (ISINST(pl->line, "mov") || ISINST(pl->line, "idxm")) + return argIs (pl->line + 4, what); + + if (ISINST (pl->line, "pop") && argIs (pl->line + 4, "af") || ISINST (pl->line, "popaf")) + return !strcmp(what, "a"); + + // TODO: Other instructions + + // One-operand instructions that write their argument + if (ISINST (pl->line, "neg") || + ISINST (pl->line, "not") || + ISINST (pl->line, "sl") || + ISINST (pl->line, "slc") || + ISINST (pl->line, "sr") || + ISINST (pl->line, "src")) + return argIs (pl->line + 3, what); + if (ISINST (pl->line, "dzsn") || + ISINST (pl->line, "izsn") || + ISINST (pl->line, "ldt16")) + return argIs (pl->line + 5, what); + + return false; +} + + +static bool +pdkUncondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "goto") || ISINST(pl->line, "pcadd")); +} + +static bool +pdkCondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "ceqsn") || ISINST(pl->line, "cneqsn") || + ISINST(pl->line, "t0sn") || ISINST(pl->line, "t1sn") || + ISINST(pl->line, "izsn") || ISINST(pl->line, "dzsn")); +} + +static bool +pdkSurelyReturns(const lineNode *pl) +{ + return(ISINST(pl->line, "ret")); +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or a opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *what, const char *untilOp, + lineNode **plCond) +{ + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) + continue; + D(("Scanning %s for %s\n", (*pl)->line, what)); + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + { + D(("S4O_ABORT at inline asm\n")); + return S4O_ABORT; + } + + if ((*pl)->visited) + { + D(("S4O_VISITED\n")); + return S4O_VISITED; + } + + (*pl)->visited = TRUE; + + if(pdkMightRead(*pl, what)) + { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } + + // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c' + if(pdkSurelyWrites(*pl, what)) + { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } + + if(pdkUncondJump(*pl)) + { + *pl = findLabel (*pl); + if (!*pl) + { + D(("S4O_ABORT at unconditional jump\n")); + return S4O_ABORT; + } + } + if(pdkCondJump(*pl)) + { + *plCond = (*pl)->next->next; + if (!*plCond) + { + D(("S4O_ABORT at conditional jump\n")); + return S4O_ABORT; + } + D(("S4O_CONDJMP\n")); + return S4O_CONDJMP; + } + + /* Don't need to check for de, hl since stm8MightRead() does that */ + if(pdkSurelyReturns(*pl)) + { + D(("S4O_TERM\n")); + return S4O_TERM; + } + } + D(("S4O_ABORT\n")); + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *what) +{ + lineNode *plConditional; + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, what, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return true; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + D(("CONDJMP trying other branch first\n")); + if (!doTermScan (&pl2, what)) + return false; + D(("Other branch OK.\n")); + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return false; + } + } +} + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = false; +} + +bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head) +{ + lineNode *pl; + + _G.head = head; + + unvisitLines (_G.head); + + pl = endPl->next; + return (doTermScan (&pl, what)); +} + +bool pdknotUsedFrom(const char *what, const char *label, lineNode *head) +{ + lineNode *cpl; + + for (cpl = head; cpl; cpl = cpl->next) + if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label))) + return (pdknotUsed (what, cpl, head)); + + return false; +} + +int +pdkinstructionSize(lineNode *pl) +{ + return 1; +} + diff --git a/src/pdk/peep.h b/src/pdk/peep.h new file mode 100644 index 0000000..9ce3a3e --- /dev/null +++ b/src/pdk/peep.h @@ -0,0 +1,6 @@ +#include + +bool pdknotUsed(const char *what, lineNode *endPl, lineNode *head); +bool pdknotUsedFrom(const char *what, const char *label, lineNode *head); +int pdkinstructionSize(lineNode *node); + diff --git a/src/pdk/peep.o b/src/pdk/peep.o new file mode 100644 index 0000000..50fc2b6 Binary files /dev/null and b/src/pdk/peep.o differ diff --git a/src/pdk/peeph.def b/src/pdk/peeph.def new file mode 100644 index 0000000..bac385a --- /dev/null +++ b/src/pdk/peeph.def @@ -0,0 +1,56 @@ +// peeph.def - PDK peephole rules + +replace restart { + mov %1, %2 +} by { + ; peephole 0 removed dead load into %1 from %2. +} if notUsed(%1) + +replace restart { + mov %1, a + mov a, %1 +} by { + mov %1, a + ; peephole 1 removed redundant load from %1 into a. +} if notVolatile(%1) + + +replace restart { + mov a, #%1 + ret +} by { + ; peephole 2 moved load to ret. + ret #%1 +} + +replace restart { + xch a, %1 + xch a, %1 +} by { + ; peephole 3 removed redundant double exchange between a and %1. +} if notVolatile(%1) + +replace restart { + mov a, #0x00 + slc a + and a, #0x01 +} by { + mov a, #0x00 + slc a + ; peephole 4 removed redundant and. +} + +replace restart { +%1: +} by { + ; peephole j0 removed unused label %1. +} if labelRefCount(%1 0) + +replace restart { + ret %1 + ret +} by { + ret %1 + ; peephole j1 removed redundant ret after ret k. +} + diff --git a/src/pdk/peeph.rul b/src/pdk/peeph.rul new file mode 100644 index 0000000..fbaeb82 --- /dev/null +++ b/src/pdk/peeph.rul @@ -0,0 +1,58 @@ +/* Generated file, DO NOT Edit! */ +/* To Make changes to rules edit */ +/* /peeph.def instead. */ +"\n" +"replace restart {\n" +" mov %1, %2\n" +"} by {\n" +" ; peephole 0 removed dead load into %1 from %2.\n" +"} if notUsed(%1)\n" +"\n" +"replace restart {\n" +" mov %1, a\n" +" mov a, %1\n" +"} by {\n" +" mov %1, a\n" +" ; peephole 1 removed redundant load from %1 into a.\n" +"} if notVolatile(%1)\n" +"\n" +"\n" +"replace restart {\n" +" mov a, #%1\n" +" ret\n" +"} by {\n" +" ; peephole 2 moved load to ret.\n" +" ret #%1\n" +"}\n" +"\n" +"replace restart {\n" +" xch a, %1\n" +" xch a, %1\n" +"} by {\n" +" ; peephole 3 removed redundant double exchange between a and %1.\n" +"} if notVolatile(%1)\n" +"\n" +"replace restart {\n" +" mov a, #0x00\n" +" slc a\n" +" and a, #0x01\n" +"} by {\n" +" mov a, #0x00\n" +" slc a\n" +" ; peephole 4 removed redundant and.\n" +"}\n" +"\n" +"replace restart {\n" +"%1:\n" +"} by {\n" +" ; peephole j0 removed unused label %1.\n" +"} if labelRefCount(%1 0)\n" +"\n" +"replace restart {\n" +" ret %1\n" +" ret\n" +"} by {\n" +" ret %1\n" +" ; peephole j1 removed redundant ret after ret k.\n" +"}\n" +"\n" diff --git a/src/pdk/port.a b/src/pdk/port.a new file mode 100644 index 0000000..ac5f23a Binary files /dev/null and b/src/pdk/port.a differ diff --git a/src/pdk/ralloc.c b/src/pdk/ralloc.c new file mode 100644 index 0000000..735ae5c --- /dev/null +++ b/src/pdk/ralloc.c @@ -0,0 +1,747 @@ +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" + +reg_info pdk_regs[] = +{ + {REG_GPR, A_IDX, "a"}, + {REG_GPR, P_IDX, "p"}, + {REG_CND, C_IDX, "f.c"}, + {0, SP_IDX, "sp"}, +}; + +/* Flags to turn on debugging code. + */ +enum +{ + D_ALLOC = 0, +}; + +#if 1 +#define D(_a, _s) if (_a) { printf _s; fflush(stdout); } +#else +#define D(_a, _s) +#endif + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location somewhere to spill */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol *sym) +{ + static int slocNum; + symbol *sloc = 0; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE(sloc->etype) = 0; + SPEC_ABSA(sloc->etype) = 0; + + allocLocal (sloc); + + sloc->isref = 1; + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + currFunc->stack += getSize (sloc->type); + + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +void +pdkSpillThis (symbol * sym) +{ + int i; + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + sym->isspilt = sym->spillA = 1; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i]) + sym->regs[i] = 0; + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq++; + return; +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (void) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed. Exception: Variables larger than 2 bytes - these might need a spill location when they are return values */ + if ((sym->liveTo - sym->liveFrom) == 0 && getSize (sym->type) <= 2) + continue; + + D (D_ALLOC, ("regTypeNum: loop on sym %p\n", sym)); + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + D (D_ALLOC, + ("regTypeNum: isagg %u nRegs %u type %p\n", IS_AGGREGATE (sym->type) || sym->isptr, sym->nRegs, sym->type)); + sym->nRegs = + ((IS_AGGREGATE (sym->type) + || sym->isptr) ? getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + D (D_ALLOC, ("regTypeNum: setting nRegs of %s (%p) to %u\n", sym->name, sym, sym->nRegs)); + + D (D_ALLOC, ("regTypeNum: setup to assign regs sym %p\n", sym)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + /* Always general purpose */ + sym->regType = REG_GPR; + } + else + { + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + D (D_ALLOC, ("regTypeNum: #2 setting num of %p to 0\n", sym)); + sym->nRegs = 0; + } + } +} + +/** Transform weird SDCC handling of writes via pointers + into something more sensible. */ +static void +transformPointerSet (eBBlock **ebbs, int count) +{ + /* for all blocks */ + for (int i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (POINTER_SET (ic)) + { + IC_LEFT (ic) = IC_RESULT (ic); + IC_RESULT (ic) = 0; + ic->op = SET_VALUE_AT_ADDRESS; + } + } +} + +/** Register reduction for assignment. + */ +static int +packRegsForAssign (iCode *ic, eBBlock *ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + return 0; + + /* Avoid having multiple named address spaces in one iCode. */ + if (IS_SYMOP (IC_RESULT (ic)) && SPEC_ADDRSPACE (OP_SYMBOL (IC_RESULT (ic))->etype)) + return 0; + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* PENDING: Don't pack across function calls. */ + if (dic->op == CALL || dic->op == PCALL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* Avoid having a result in a sfr for shifts. */ + if (IS_SYMOP (IC_RESULT (ic)) && IN_REGSP (SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype)) && (dic->op == LEFT_OP || dic->op == RIGHT_OP)) + return 0; + + /* if assignment then check that right is not a bit */ + if (ic->op == '=') + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* if the result is on stack or iaccess then it must be + the same as at least one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + /* found the definition */ + + /* delete from liverange table also + delete from all the points in between and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + // PENDING: Check vs mcs51 + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/** Will reduce some registers for single use. + */ +static int +packRegsForOneuse (iCode *ic, operand **opp, eBBlock *ebp) +{ + iCode *dic; + + operand *op = *opp; + + /* if returning a literal then do nothing */ + if (!IS_ITEMP (op)) + return 0; + + /* if rematerializable do nothing */ + if (OP_SYMBOL (op)->remat) + return 0; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition */ + if (bitVectnBitsOn (OP_USES (op)) != 1 || bitVectnBitsOn (OP_DEFS (op)) != 1) + return 0; + + /* get the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return 0; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; /* non-local */ + + /* for now handle results from assignments from globals only */ + if (!(dic->op == '=' || dic->op == CAST && SPEC_USIGN (getSpec (operandType (IC_RIGHT (dic)))) && operandSize (op) > operandSize (IC_RIGHT (dic))) + || !isOperandGlobal (IC_RIGHT (dic))) + return 0; + + if (IS_OP_VOLATILE (IC_RESULT (ic)) && IS_OP_VOLATILE (IC_RIGHT (dic))) + return 0; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (iCode *nic = dic->next; nic && nic != ic; nic = nic->next) + { + /* if there is an intervening function call then no */ + if (nic->op == CALL || nic->op == PCALL) + return 0; + + if (nic->op == SET_VALUE_AT_ADDRESS) + return 0; + + /* if address of & the result is remat, then okay */ + if (nic->op == ADDRESS_OF && OP_SYMBOL (IC_RESULT (nic))->remat) + continue; + + if (IS_OP_VOLATILE (IC_LEFT (nic)) || + IS_OP_VOLATILE (IC_RIGHT (nic)) || + isOperandGlobal (IC_RESULT (nic))) + return 0; + } + + /* Optimize out the assignment */ + *opp = operandFromOperand (IC_RIGHT(dic)); + (*opp)->isaddr = true; + + bitVectUnSetBit (OP_SYMBOL (op)->defs, dic->key); + bitVectUnSetBit (OP_SYMBOL (op)->uses, ic->key); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + + /* delete from liverange table also + delete from all the points in between and the new + one */ + for (iCode *nic = dic; nic != ic; nic = nic->next) + bitVectUnSetBit (nic->rlive, op->key); + + remiCodeFromeBBlock (ebp, dic); + + hTabDeleteItem (&iCodehTab, dic->key, ic, DELETE_ITEM, NULL); + + return 1; +} + +/** Does some transformations to reduce register pressure. + */ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + D (D_ALLOC, ("packRegisters: entered.\n")); + + for(;;) + { + change = 0; + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=') + change += packRegsForAssign (ic, ebp); + } + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) + { + D (D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); + + /* Safe: address of a true sym is always constant. */ + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) /* The receiving of the parameter is not accounted for in DEFS */ && + IS_TRUE_SYMOP (IC_LEFT (ic)) && !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* Safe: just propagates the remat flag */ + /* if straight assignment then carry remat flag if this is the + only definition */ + if (ic->op == '=' && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the parameter is not accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter is not accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + sym_link *to_type = operandType (IC_LEFT (ic)); + sym_link *from_type = operandType (IC_RIGHT (ic)); + if ((IS_PTR (to_type) || IS_INT (to_type)) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* In some cases redundant moves can be eliminated */ + if (ic->op == GET_VALUE_AT_ADDRESS || ic->op == SET_VALUE_AT_ADDRESS || + ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS || + ic->op == '|' || ic->op == '&' || ic->op == '^' || + ic->op == EQ_OP || ic->op == NE_OP || + ic->op == IFX && operandSize (IC_COND (ic)) == 1 || + ic->op == IPUSH && operandSize (IC_LEFT (ic)) == 1 || + ic->op == LEFT_OP || ic->op == RIGHT_OP) + packRegsForOneuse (ic, &(IC_LEFT (ic)), ebp); + if (ic->op == '+' || ic->op == '-' || + ic->op == '|' || ic->op == '&' || ic->op == '^' || + ic->op == EQ_OP || ic->op == NE_OP || + ic->op == LEFT_OP || ic->op == RIGHT_OP) + packRegsForOneuse (ic, &(IC_RIGHT (ic)), ebp); + + // Optimize out some unsigned upcasts. + if (ic->op == CAST && IS_ITEMP (IC_RESULT (ic)) && !IS_OP_VOLATILE (IC_RIGHT (ic)) && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))) == ic->next && + SPEC_USIGN (getSpec (operandType (IC_RIGHT (ic)))) && operandSize (IC_RESULT (ic)) >= operandSize (IC_RIGHT (ic)) && !IS_BOOL (operandType (IC_RESULT (ic)))) + { + iCode *use = ic->next; + operand *op = IC_RESULT (ic); + if ((use->op == LEFT_OP || use->op == '+' || use->op == '-' || use->op == UNARYMINUS || + use->op == '&' || use->op == '|' || use->op == '^') && + IC_LEFT (use)->key == op->key && (!IC_RIGHT(use) || IC_RIGHT (use)->key != op->key)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + bitVectSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, use->key); + IC_LEFT (use) = operandFromOperand (IC_RIGHT(ic)); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + if(ic->prev) + ic = ic->prev; + } + else if (((use->op == SET_VALUE_AT_ADDRESS && !IS_BITVAR (getSpec (operandType (IC_LEFT (use)))) && !IS_BITVAR (getSpec (operandType (IC_RIGHT (use))))) || + use->op == CAST && (SPEC_USIGN (getSpec (operandType (IC_RIGHT (use)))) || operandSize (IC_RESULT (use)) <= operandSize (IC_RIGHT (use))) || + use->op == LEFT_OP || use->op == RIGHT_OP || use->op == '+' || use->op == '-' || + use->op == '&' || use->op == '|' || use->op == '^') && + IC_RIGHT (use)->key == op->key && (!IC_LEFT(use) || IC_LEFT (use)->key != op->key)) + { + bitVectUnSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, ic->key); + bitVectSetBit (OP_SYMBOL (IC_RIGHT (ic))->uses, use->key); + IC_RIGHT (use) = operandFromOperand (IC_RIGHT(ic)); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + if(ic->prev) + ic = ic->prev; + } + } + } +} + +/** + Mark variables for assignment by the register allocator. + */ +static void +serialRegMark (eBBlock **ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + D (D_ALLOC, ("serialRegMark for %s, currFunc->stack %d\n", currFunc->name, currFunc->stack)); + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (ic->op == IPOP) + wassert (0); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + + /* some don't need registers, since there is no result. */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || ic->op == SET_VALUE_AT_ADDRESS) + continue; + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + D (D_ALLOC, ("serialRegAssign: in loop on result %p %s\n", sym, sym->name)); + + if (sym->isspilt && sym->usl.spillLoc) // todo: Remove once remat is supported! + { + sym->usl.spillLoc->allocreq--; + sym->isspilt = FALSE; + } + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + sym->usl.spillLoc->allocreq++; + + /* if it does not need or is spilt + or is already marked for the new allocator + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || sym->for_newralloc || sym->liveTo <= ic->seq && (sym->nRegs <= 2 || ic->op != CALL && ic->op != PCALL)) + { + D (D_ALLOC, ("serialRegMark: won't live long enough.\n")); + continue; + } + + if (sym->usl.spillLoc && !sym->usl.spillLoc->_isparm) // I have no idea where these spill locations come from. Sometime two symbols even have the same spill location, whic tends to mess up stack allocation. THose that come from previous iterations in this loop would be okay, but those from outside are a problem. + { + sym->usl.spillLoc = 0; + sym->isspilt = false; + } + + if (sym->nRegs > 2 && ic->op == CALL) + { + sym->for_newralloc = 0; + pdkSpillThis (sym); + } + else if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + pdkSpillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have been spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand * op, iCode * ic) +{ + symbol *sym; + int i; + bool completely_in_regs; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + + if (sym->regType == REG_CND) + return; + + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + sym->usl.spillLoc->allocreq++; + + if (sym->isspilt) + return; + + for(i = 0, completely_in_regs = true; i < sym->nRegs; i++) + if (!sym->regs[i]) + completely_in_regs = false; + if (completely_in_regs) + return; + + pdkSpillThis (sym); +} + +void +pdkRegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +pdk_assignRegisters (ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + + pdk_init_asmops(); + + transformPointerSet (ebbs, count); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (int i = 0; i < count; i++) + packRegisters (ebbs[i]); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* Mark variables for assignment by the new allocator */ + serialRegMark (ebbs, count); + + /* Invoke optimal register allocator */ + ic = pdk_ralloc2_cc (ebbi); + + /* redo offsets for stacked automatic variables */ + if (currFunc) + { + redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + genPdkCode (ic); +} + diff --git a/src/pdk/ralloc.h b/src/pdk/ralloc.h new file mode 100644 index 0000000..bef02fa --- /dev/null +++ b/src/pdk/ralloc.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + + ralloc.h - header file register allocation + + Written By - Philipp Krause . pkk@spth.de (2018) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#include "common.h" + +enum +{ + A_IDX = 0, // The accumulator + P_IDX, // A memory location used as pseudoregister + C_IDX, // Implicit condition operand. + SP_IDX // SP - for use with debug info. +}; + +enum +{ + REG_GPR = 2, + REG_CND = 4, +}; + +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ +} reg_info; + +extern reg_info pdk_regs[]; + +void pdk_assignRegisters (ebbIndex *); + +void pdkSpillThis (symbol *sym); +iCode *pdk_ralloc2_cc(ebbIndex *ebbi); + +void pdkRegFix (eBBlock **ebbs, int count); + +#endif + diff --git a/src/pdk/ralloc.o b/src/pdk/ralloc.o new file mode 100644 index 0000000..c71d394 Binary files /dev/null and b/src/pdk/ralloc.o differ diff --git a/src/pdk/ralloc2.cc b/src/pdk/ralloc2.cc new file mode 100644 index 0000000..dac7386 --- /dev/null +++ b/src/pdk/ralloc2.cc @@ -0,0 +1,692 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2018 +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +// #define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +// #define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#include "SDCCralloc.hpp" +#include "SDCCsalloc.hpp" + +extern "C" +{ + #include "ralloc.h" + #include "gen.h" + float dryPdkiCode (iCode *ic); + bool pdk_assignment_optimal; +} + +#define REG_A 0 +#define REG_P 1 + +template +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + // Todo: More fine-grained control for these. + if (!(ic->op == '-' || ic->op == UNARYMINUS && !IS_FLOAT (operandType (left)) || ic->op == '~' || + ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND)) + return; + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + if(r >= port->num_regs) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Return true, iff the operand is placed in a reg. +template +static bool operand_byte_in_reg(const operand *o, int offset, reg_t r, const assignment &a, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi3, oi_end; + + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); offset && oi != oi_end; offset--, oi++); + + if(oi == oi_end) + return(false); + + return(a.global[oi->second] == r); +} + +template +static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const i_assignment_t &ia = a.i_assignment; + + if(ia.registers[REG_A][1] < 0) + return(true); // Register a not in use. + + if(ic->op == GOTO || ic->op == LABEL) + return(true); + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_A = operand_in_reg(result, REG_A, ia, i, G); + bool left_in_A = operand_in_reg(left, REG_A, ia, i, G); + bool right_in_A = operand_in_reg(right, REG_A, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_A = result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || dying.find(ia.registers[REG_A][0]) != dying.end(); + + bool result_dir = IS_TRUE_SYMOP (result) || IS_ITEMP (result) && !(options.stackAuto || reentrant) && !result_in_A; + bool left_dir = IS_TRUE_SYMOP (left) || IS_ITEMP (left) && !(options.stackAuto || reentrant) && !left_in_A; + bool right_dir = IS_TRUE_SYMOP (right) || IS_ITEMP (right) && !(options.stackAuto || reentrant) && !right_in_A; + + if(result && IS_ITEMP(result) && OP_SYMBOL_CONST(result)->remat && !operand_in_reg(result, REG_A, ia, i, G) && !operand_in_reg(result, REG_P, ia, i, G)) + return(true); + + if (ic->op == DUMMY_READ_VOLATILE || ic->op == '=') + return(true); + + if ((ic->op == EQ_OP || ic->op == NE_OP) && dying_A && + (left_in_A && (right_dir || IS_OP_LITERAL(right) || IS_ITEMP(right) && OP_SYMBOL_CONST(right)->remat) || + right_in_A && (left_dir || IS_OP_LITERAL(left) || IS_ITEMP(left) && OP_SYMBOL_CONST(left)->remat))) + return (true); + + if ((ic->op == EQ_OP || ic->op == NE_OP || (ic->op == '>' || ic->op == '<') && SPEC_USIGN(getSpec(operandType(left)))) && // Non-destructive comparison. + (left_in_A && getSize(operandType(left)) == 1 && (IS_OP_LITERAL(right) || right_dir) || right_in_A && getSize(operandType(right)) == 1 && (IS_OP_LITERAL(left) || left_dir))) + return (true); + + if (ic->op == IFX && (dying_A || left_in_A)) + return (true); + + if (ic->op == '<' && IS_OP_LITERAL(right) && !ullFromVal(OP_VALUE_CONST (right)) && + (operand_byte_in_reg(left, getSize(operandType(left)) - 1, REG_A, a, i, G) || operand_byte_in_reg(left, getSize(operandType(left)) - 1, REG_P, a, i, G))) + return (true); + + if (ic->op == SET_VALUE_AT_ADDRESS && getSize(operandType(right)) == 1 && left_dir && right_in_A) + return (true); + if (ic->op == SET_VALUE_AT_ADDRESS && IS_ITEMP(left) && OP_SYMBOL_CONST(left)->remat && !operand_in_reg(left, REG_A, ia, i, G)) + return (true); + + if ((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS) && (!left_in_A && !right_in_A || getSize(operandType(result)) == 1)) + return (true); + + if ((ic->op == CALL || ic->op == PCALL) && !left_in_A) + return(true); + + if (ic->op == RETURN || + (ic->op == GET_VALUE_AT_ADDRESS && getSize(operandType(result)) <= 2 || ic->op == SET_VALUE_AT_ADDRESS && getSize(operandType(right)) == 1) && dying_A) + return(true); + + if (ic->op == GET_VALUE_AT_ADDRESS && !left_in_A) + return(true); + + if (ic->op == CAST && + (getSize(operandType(result)) == 1 || getSize(operandType(result)) == 2 && SPEC_USIGN (getSpec(operandType(right))) && operand_byte_in_reg(result, 1, REG_P, a, i, G)) && + right_in_A && (result_dir || dying_A)) + return (true); + + if ((ic->op == CAST && (getSize(operandType(result)) <= getSize(operandType(right)) || SPEC_USIGN(getSpec(operandType(right))))) && + getSize(operandType(result)) <= 2 && + (result_dir && dying_A || result_in_A && right_dir || result_in_A && right_in_A)) + return (true); + + if ((ic->op == LEFT_OP || ic->op == RIGHT_OP)) + return(IS_OP_LITERAL(right) || right_in_A && !result_in_A); + + if (ic->op == '^' && + (operand_byte_in_reg(result, 0, REG_A, a, i, G) && (operand_byte_in_reg(left, 0, REG_A, a, i, G) || operand_byte_in_reg(right, 0, REG_A, a, i, G)) || + operand_byte_in_reg(result, 1, REG_A, a, i, G) && (operand_byte_in_reg(left, 1, REG_A, a, i, G) || operand_byte_in_reg(right, 1, REG_A, a, i, G)))) + return (true); + + // For most operations only allow lower byte in a for now (upper byte for result). + if (left_in_A && !operand_byte_in_reg(left, 0, REG_A, a, i, G) || right_in_A && !operand_byte_in_reg(right, 0, REG_A, a, i, G) || + ic->op != '+' && ic->op != '-' && ic->op != UNARYMINUS && result_in_A && !operand_byte_in_reg(result, getSize(operandType(result)) - 1, REG_A, a, i, G)) + return(false); + + if ((ic->op == GET_VALUE_AT_ADDRESS || ic->op == SET_VALUE_AT_ADDRESS) && (left_in_A || right_in_A)) + return(false); + + if(dying_A) + return(true); + + return(false); +} + +template +static bool Pinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const i_assignment_t &ia = a.i_assignment; + + if(ia.registers[REG_P][1] < 0) + return(true); // Pseudoregister p not in use. + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_P = operand_in_reg(result, REG_P, ia, i, G); + bool left_in_P = operand_in_reg(left, REG_P, ia, i, G); + bool right_in_P = operand_in_reg(right, REG_P, ia, i, G); + + bool left_in_A = operand_in_reg(left, REG_A, ia, i, G); + bool right_in_A = operand_in_reg(right, REG_A, ia, i, G); + + const cfg_dying_t &dying = G[i].dying; + + bool dying_P = result_in_P || dying.find(ia.registers[REG_P][1]) != dying.end() || dying.find(ia.registers[REG_P][0]) != dying.end(); + + bool left_stack = IS_ITEMP (left) && (options.stackAuto || reentrant) && !left_in_A && !left_in_P; + bool right_stack = IS_ITEMP (right) && (options.stackAuto || reentrant) && !right_in_A && !right_in_P; + + if(result && IS_ITEMP(result) && OP_SYMBOL_CONST(result)->remat && !operand_in_reg(result, REG_A, ia, i, G) && !operand_in_reg(result, REG_P, ia, i, G)) + return(true); + + // Arithmetic uses p internally for literal operands with multiple nonzero bytes. + if ((ic->op == '+' || ic->op == '-' || ic->op == '!' || ic->op == '<' || ic->op == '>') && (IS_OP_LITERAL(left) || IS_OP_LITERAL(right))) + { + const operand *const litop = IS_OP_LITERAL(left) ? left : right; + if ((ullFromVal(OP_VALUE_CONST (litop)) & 0x000000ffull) && (ullFromVal(OP_VALUE_CONST(litop)) & 0x0000ff00ull) && (ullFromVal(OP_VALUE_CONST (litop)) & 0x00ff0000ull) && (ullFromVal(OP_VALUE_CONST (litop)) & 0xff000000ull)) + return(false); + } + if (ic->op == PCALL || ic->op == IPUSH) + return(false); + + if (ic->op == CALL && !dying_P) + return(false); + + if (ic->op == GET_VALUE_AT_ADDRESS && !dying_P && !(left_in_P && getSize(operandType(result)) == 1)) + return(false); + + if ((ic->op == '+' || ic->op == '-' || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) && + (left_stack || right_stack) && (!dying_P || left_stack || right_stack)) + return(false); + + return(true); +} + +template +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + + if(!(IC_RESULT(ic) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + if(G[i].dying.find(*v) == G[i].dying.end()) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = pdk_regs + a.global[v]; + sym->nRegs = I[v].size; + } + else + { + sym->regs[I[v].byte] = 0; + sym->nRegs = I[v].size; + } + } +} + +template +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + assign_operands_for_cost(a, (unsigned short)*(adjacent_vertices(i, G).first), G, I); +} + +// Check that the operand is either fully in registers or fully in memory. Todo: Relax this once code generation can handle partially spilt variables! +template +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + { + while(++oi != oi_end) + if(!std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + else + { + while(++oi != oi_end) + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + + return(true); +} + +template +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +// Cost function. +template +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert(TARGET_PDK_LIKE); + wassert(ic); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits::infinity()); + +#if 0 + std::cout << "Calculating at cost at ic " << ic->key << ", op " << ic->op << " for: "; + print_assignment(a); + std::cout << "\n"; + std::cout.flush(); +#endif + + if(ic->generated) + { +#if 0 + std::cout << "Skipping, already generated.\n"; +#endif + return(0.0f); + } + + if(!Ainst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(!Pinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: +#if 0 + std::cout << "Skipping, indepent from assignment.\n"; +#endif + return(0.0f); + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + //case GETABIT: + //case GETBYTE: + //case GETWORD: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case SET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + /*case RECEIVE: + case SEND:*/ + case DUMMY_READ_VOLATILE: + /*case CRITICAL: + case ENDCRITICAL:*/ + case SWAP: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = dryPdkiCode(ic); + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT(ic)) && !OP_SYMBOL_CONST(IC_RESULT(ic))->remat && // Nudge towards saving RAM space. TODO: Do this in a better way, so it works for all backends! + !operand_in_reg(IC_RESULT(ic), REG_A, a.i_assignment, i, G) && !operand_in_reg(IC_RESULT(ic), REG_P, a.i_assignment, i, G)) + c += 0.0001; + + ic->generated = false; +#if 0 + std::cout << "Got cost " << c << "\n"; +#endif + return(c); + default: + return(0.0f); + } +} + +// For early removal of assignments that cannot be extended to valid assignments. This is just a dummy for now. +template +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. +template +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T) +{ + a = *T[t].assignments.begin(); + + std::set::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +// Suggest to honor register keyword. +template +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const i_assignment_t &ia = a.i_assignment; + float c = 0.0f; + + if(ia.registers[REG_A][1] < 0) + c += 0.05f; + + varset_t::const_iterator v, v_end; + for(v = a.local.begin(), v_end = a.local.end(); v != v_end; ++v) + { + const symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + if(a.global[*v] < 0 && !sym->remat) // Try to put non-rematerializeable variables into registers. + c += 0.1f; + if(a.global[*v] < 0 && IS_REGISTER(sym->type)) // Try to honour register keyword. + c += 4.0f; + } + + return(c); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + iCode *ifx; + + // - can only jump on nonzero result for decrement of register / direct variable. + if(ic->op == '-' && ic->next && ic->next->op == IFX && IC_TRUE(ic->next) && IC_COND (ic->next)->key == IC_RESULT(ic)->key) + { + ifx = ic->next; + + if ((!IS_ITEMP(IC_LEFT (ic)) || options.stackAuto || reentrant) && !isOperandGlobal (IC_LEFT (ic))) + return; + + if (!IS_OP_LITERAL(IC_RIGHT(ic))) + return; + + if (ullFromVal(OP_VALUE(IC_RIGHT(ic))) != 1) + return; + + if (!isOperandEqual (IC_RESULT(ic), IC_LEFT(ic))) + return; + + if (getSize(operandType(IC_RESULT(ic))) > 2) + return; + + ifx->generated = true; + return; + } + + if(ic->op != EQ_OP && ic->op != NE_OP && ic->op != '<' && ic->op != '>' && ic->op != BITWISEAND) + return; + + ifx = ifxForOp(IC_RESULT(ic), ic); + + if(!ifx) + return; + + // Bitwise and code generation can only do the jump if there is at most one nonzero byte. + if(ic->op == BITWISEAND) + { + int nonzero = 0; + operand *const litop = IS_OP_LITERAL(IC_LEFT(ic)) ? IC_LEFT(ic) : IC_RIGHT(ic); + + if (!IS_OP_LITERAL(litop)) + return; + + for(unsigned int i = 0; i < getSize(operandType(IC_LEFT (ic))) && i < getSize(operandType(IC_RIGHT(ic))) && i < getSize(operandType(IC_RESULT(ic))); i++) + if(byteOfVal(OP_VALUE(litop), i)) + nonzero++; + + if(nonzero > 1 && IC_FALSE (ifx)) + return; + } + +cnd: + OP_SYMBOL(IC_RESULT(ic))->for_newralloc = false; + OP_SYMBOL(IC_RESULT(ic))->regType = REG_CND; + ifx->generated = true; +} + +template +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I, SI_t &SI) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + /*const*/ assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + bool spilt = false; + + if(winner.global[v] >= 0) + sym->regs[I[v].byte] = pdk_regs + winner.global[v]; + else + { + sym->regs[I[v].byte] = 0; + spilt = true; + } + + if(spilt) + pdkSpillThis(sym); + + sym->nRegs = I[v].size; + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + set_spilt(G, I, SI); + + return(!assignment_optimal); +} + +iCode *pdk_ralloc2_cc(ebbIndex *ebbi) +{ + eBBlock **const ebbs = ebbi->bbOrder; + const int count = ebbi->count; + iCode *ic; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + scon_t spilt_conflict_graph; + + pdk_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph, spilt_conflict_graph); + + pdkRegFix (ebbs, count); + + doOverlays (ebbs, count); + + return(ic); +} + diff --git a/src/pdk/ralloc2.o b/src/pdk/ralloc2.o new file mode 100644 index 0000000..718d12c Binary files /dev/null and b/src/pdk/ralloc2.o differ diff --git a/src/pic14/Makefile b/src/pic14/Makefile new file mode 100644 index 0000000..1c6f129 --- /dev/null +++ b/src/pic14/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pic14/Makefile.in b/src/pic14/Makefile.in new file mode 100644 index 0000000..240faea --- /dev/null +++ b/src/pic14/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pic14/device.c b/src/pic14/device.c new file mode 100644 index 0000000..34c9874 --- /dev/null +++ b/src/pic14/device.c @@ -0,0 +1,821 @@ +/*------------------------------------------------------------------------- + + device.c - Accomodates subtle variations in PIC devices + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "device.h" +#include "pcode.h" + +/* + * Imports + */ +extern set *includeDirsSet; +extern set *userIncDirsSet; +extern set *libDirsSet; +extern set *libPathsSet; + +#define MAX_PICLIST 400 +static PIC_device *Pics[MAX_PICLIST]; +static PIC_device *pic = NULL; +static int num_of_supported_PICS = 0; +static int maxRAMaddress = 0; + +#define DEVICE_FILE_NAME "pic14devices.txt" +#define PIC14_STRING_LEN 256 +#define SPLIT_WORDS_MAX 16 + +/* Keep track of whether we found an assignment to the __config words. */ +static int pic14_hasSetConfigWord = 0; +static unsigned int config_word[MAX_NUM_CONFIGS]; +static memRange *rangeRAM = NULL; + + +/* parse a value from the configuration file */ +static int +parse_config_value (char *str) +{ + if (str[strlen (str) - 1] == 'K') + return atoi (str) * 1024; /* like "1K" */ + + else if (STRNCASECMP (str, "0x", 2) == 0) + return strtol (str+2, NULL, 16); /* like "0x400" */ + + else + return atoi (str); /* like "1024" */ +} + + +/* split a line into words */ +static int +split_words (char **result_word, char *str) +{ + static const char delim[] = " \f\n\r\t\v,"; + char *token; + int num_words; + + /* release previously allocated words */ + for (num_words = 0; num_words < SPLIT_WORDS_MAX; num_words++) + { + if (result_word[num_words]) + { + free (result_word[num_words]); + result_word[num_words] = NULL; + } // if + } // for + + /* split line */ + token = strtok (str, delim); + num_words = 0; + while (token && (num_words < SPLIT_WORDS_MAX)) + { + result_word[num_words] = Safe_strdup (token); + num_words++; + token = strtok (NULL, delim); + } // while + + return num_words; +} + + +/* remove annoying prefixes from the processor name */ +static char * +sanitise_processor_name (char *name) +{ + char *proc_pos = name; + + if (name == NULL) + return NULL; + + if (STRNCASECMP (proc_pos, "pic", 3) == 0) + proc_pos += 3; + + else if (tolower (*proc_pos) == 'p') + proc_pos += 1; + + return proc_pos; +} + + +/* create a structure for a pic processor */ +static PIC_device * +create_pic (char *pic_name, int maxram, int bankmsk, int confsiz, + int config[MAX_NUM_CONFIGS], int program, int data, int eeprom, + int io, int is_enhanced) +{ + PIC_device *new_pic; + char *simple_pic_name = sanitise_processor_name (pic_name); + + new_pic = Safe_alloc(sizeof(PIC_device)); + new_pic->name = Safe_strdup(simple_pic_name); + + new_pic->defMaxRAMaddrs = maxram; + new_pic->bankMask = bankmsk; + new_pic->num_configs = confsiz; + memcpy(new_pic->config, config, MAX_NUM_CONFIGS * sizeof(int)); + + new_pic->programMemSize = program; + new_pic->dataMemSize = data; + new_pic->eepromMemSize = eeprom; + new_pic->ioPins = io; + new_pic->isEnhancedCore = is_enhanced; + + new_pic->ram = rangeRAM; + + Pics[num_of_supported_PICS] = new_pic; + num_of_supported_PICS++; + + return new_pic; +} + + +/* mark some registers as being duplicated across banks */ +static void +register_map (int num_words, char **word) +{ + memRange *r; + int pcount; + + if (num_words < 3) + { + fprintf (stderr, "WARNING: not enough values in %s regmap directive\n", + DEVICE_FILE_NAME); + return; + } // if + + for (pcount = 2; pcount < num_words; pcount++) + { + r = Safe_alloc(sizeof(memRange)); + + r->start_address = parse_config_value (word[pcount]); + r->end_address = parse_config_value (word[pcount]); + r->alias = parse_config_value (word[1]); + r->bank = (r->start_address >> 7) & 3; + // add memRange to device entry for future lookup (sharebanks) + r->next = rangeRAM; + rangeRAM = r; + } // for +} + + +/* define ram areas - may be duplicated across banks */ +static void +ram_map (int num_words, char **word) +{ + memRange *r; + + if (num_words < 4) + { + fprintf (stderr, "WARNING: not enough values in %s memmap directive\n", + DEVICE_FILE_NAME); + return; + } // if + + r = Safe_alloc(sizeof(memRange)); + //fprintf (stderr, "%s: %s %s %s\n", __FUNCTION__, word[1], word[2], word[3]); + + r->start_address = parse_config_value (word[1]); + r->end_address = parse_config_value (word[2]); + r->alias = parse_config_value (word[3]); + r->bank = (r->start_address >> 7) & 3; + + // add memRange to device entry for future lookup (sharebanks) + r->next = rangeRAM; + rangeRAM = r; +} + +static void +setMaxRAM (int size) +{ + maxRAMaddress = size; + + if (maxRAMaddress < 0) + { + fprintf (stderr, "invalid maxram 0x%x setting in %s\n", + maxRAMaddress, DEVICE_FILE_NAME); + return; + } // if +} + +/* read the file with all the pic14 definitions and pick out the definition + * for a processor if specified. if pic_name is NULL reads everything */ +static PIC_device * +find_device (char *pic_name) +{ + FILE *pic_file; + char pic_buf[PIC14_STRING_LEN]; + int found_processor = FALSE; + int done = FALSE; + char **processor_name; + int num_processor_names = 0; + int pic_maxram = 0; + int pic_bankmsk = 0; + int pic_confsiz = 0; + int pic_config[MAX_NUM_CONFIGS]; + int pic_program = 0; + int pic_data = 0; + int pic_eeprom = 0; + int pic_io = 0; + int pic_is_enhanced = 0; + char *simple_pic_name; + char *dir; + char filename[512]; + int len = 512; + char **pic_word; + int num_pic_words; + int wcount; + int i; + + pic_word = Safe_calloc (sizeof (char *), SPLIT_WORDS_MAX); + processor_name = Safe_calloc (sizeof (char *), SPLIT_WORDS_MAX); + + for (i = 0; i < MAX_NUM_CONFIGS; i++) + { + config_word[i] = -1; + pic_config[i] = -1; + } // for + + /* allow abbreviations of the form "f877" - convert to "16f877" */ + simple_pic_name = sanitise_processor_name (pic_name); + num_of_supported_PICS = 0; + + /* open the piclist file */ + /* first scan all include directories */ + pic_file = NULL; + //fprintf (stderr, "%s: searching %s\n", __FUNCTION__, DEVICE_FILE_NAME); + for (dir = setFirstItem (userIncDirsSet); + !pic_file && dir; + dir = setNextItem (userIncDirsSet)) + { + //fprintf (stderr, "searching1 %s\n", dir); + SNPRINTF (&filename[0], len, "%s%s", dir, + DIR_SEPARATOR_STRING DEVICE_FILE_NAME); + pic_file = fopen (filename, "rt"); + if (pic_file) break; + } // for + + for (dir = setFirstItem (includeDirsSet); + !pic_file && dir; + dir = setNextItem (includeDirsSet)) + { + //fprintf (stderr, "searching2 %s\n", dir); + SNPRINTF (&filename[0], len, "%s%s", dir, + DIR_SEPARATOR_STRING DEVICE_FILE_NAME); + pic_file = fopen (filename, "rt"); + if (pic_file) break; + } // for + + for (dir = setFirstItem (libDirsSet); + !pic_file && dir; + dir = setNextItem (libDirsSet)) + { + //fprintf (stderr, "searching3 %s\n", dir); + SNPRINTF (&filename[0], len, "%s%s", dir, + DIR_SEPARATOR_STRING DEVICE_FILE_NAME); + pic_file = fopen (filename, "rt"); + if (pic_file) break; + } // for + + for (dir = setFirstItem (libPathsSet); + !pic_file && dir; + dir = setNextItem (libPathsSet)) + { + //fprintf (stderr, "searching4 %s\n", dir); + SNPRINTF (&filename[0], len, "%s%s", dir, + DIR_SEPARATOR_STRING DEVICE_FILE_NAME); + pic_file = fopen (filename, "rt"); + if (pic_file) break; + } // for + + if (!pic_file) + { + SNPRINTF (&filename[0], len, "%s", + DATADIR LIB_DIR_SUFFIX + DIR_SEPARATOR_STRING "pic" + DIR_SEPARATOR_STRING DEVICE_FILE_NAME); + pic_file = fopen (filename, "rt"); + } // if + + if (pic_file == NULL) + { + fprintf (stderr, "can't find %s\n", DEVICE_FILE_NAME); + return NULL; + } // if + + if (options.verbose) + printf ("Using devices from %s.\n", filename); + + /* read line by line */ + pic_buf[sizeof (pic_buf)-1] = '\0'; + while (fgets (pic_buf, sizeof (pic_buf)-1, pic_file) != NULL && !done) + { + /* strip comments */ + { + char *comment = strchr (pic_buf, '#'); + if (comment) + *comment = 0; + } + + /* split into fields */ + num_pic_words = split_words (pic_word, pic_buf); + + /* ignore comment / empty lines */ + if (num_pic_words > 0) + { + + if (STRCASECMP (pic_word[0], "processor") == 0) + { + if (pic_name == NULL) + { + int dcount; + + /* this is the mode where we read all the processors in - store the names for now */ + if (num_processor_names > 0) + { + /* store away all the previous processor definitions */ + for (dcount = 1; dcount < num_processor_names; dcount++) + { + create_pic (processor_name[dcount], pic_maxram, + pic_bankmsk, pic_confsiz, pic_config, + pic_program, pic_data, pic_eeprom, + pic_io, pic_is_enhanced); + } // for + } // if + + /* copy processor names */ + num_processor_names = num_pic_words; + for (dcount = 1; dcount < num_processor_names; dcount++) + { + processor_name[dcount] = pic_word[dcount]; + pic_word[dcount] = NULL; + } // for + } // if + else + { + /* if we've just completed reading a processor definition stop now */ + if (found_processor) + done = TRUE; + else + { + /* check if this processor name is a match */ + for (wcount = 1; wcount < num_pic_words; wcount++) + { + /* skip uninteresting prefixes */ + char *found_name = sanitise_processor_name (pic_word[wcount]); + + if (STRCASECMP (found_name, simple_pic_name) == 0) + found_processor = TRUE; + } // for + } // if + } // if + } // if + else + { + if (found_processor || pic_name == NULL) + { + /* only parse a processor section if we've found the one we want */ + if (STRCASECMP (pic_word[0], "maxram") == 0 && num_pic_words > 1) + { + pic_maxram = parse_config_value (pic_word[1]); + setMaxRAM (pic_maxram); + } // if + + else if (STRCASECMP (pic_word[0], "bankmsk") == 0 && num_pic_words > 1) + pic_bankmsk = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "config") == 0 && num_pic_words > 1) + { + pic_confsiz = 0; + for (i = 1; (i < num_pic_words) && (i < MAX_NUM_CONFIGS + 1); i++) + { + pic_config[i - 1] = parse_config_value (pic_word[i]); + pic_confsiz++; + } // for + } + + else if (STRCASECMP (pic_word[0], "program") == 0 && num_pic_words > 1) + pic_program = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "data") == 0 && num_pic_words > 1) + pic_data = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "eeprom") == 0 && num_pic_words > 1) + pic_eeprom = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "enhanced") == 0 && num_pic_words > 1) + pic_is_enhanced = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "io") == 0 && num_pic_words > 1) + pic_io = parse_config_value (pic_word[1]); + + else if (STRCASECMP (pic_word[0], "regmap") == 0 && num_pic_words > 2) + { + if (found_processor) + register_map (num_pic_words, pic_word); + } // if + + else if (STRCASECMP (pic_word[0], "memmap") == 0 && num_pic_words > 2) + { + if (found_processor) + ram_map (num_pic_words, pic_word); + } // if + + else + { + fprintf (stderr, "WARNING: %s: bad syntax `%s'\n", + DEVICE_FILE_NAME, pic_word[0]); + } // if + } // if + } // if + } // if + } // while + + fclose (pic_file); + + split_words (pic_word, NULL); + free (pic_word); + + /* if we're in read-the-lot mode then create the final processor definition */ + if (pic_name == NULL) + { + if (num_processor_names > 0) + { + /* store away all the previous processor definitions */ + int dcount; + + for (dcount = 1; dcount < num_processor_names; dcount++) + { + create_pic (processor_name[dcount], pic_maxram, pic_bankmsk, + pic_confsiz, pic_config, pic_program, pic_data, + pic_eeprom, pic_io, pic_is_enhanced); + } // for + } // if + } // if + else + { + /* in search mode */ + if (found_processor) + { + split_words (processor_name, NULL); + free (processor_name); + + /* create a new pic entry */ + return create_pic (pic_name, pic_maxram, pic_bankmsk, + pic_confsiz, pic_config, pic_program, + pic_data, pic_eeprom, pic_io, pic_is_enhanced); + } // if + } // if + + split_words (processor_name, NULL); + free (processor_name); + + return NULL; +} + +/*-----------------------------------------------------------------* + * void list_valid_pics(int ncols, int list_alias) + * + * Print out a formatted list of valid PIC devices + * + * ncols - number of columns in the list. + * + * list_alias - if non-zero, print all of the supported aliases + * for a device (e.g. F84, 16F84, etc...) + *-----------------------------------------------------------------*/ +static void +list_valid_pics(int ncols) +{ + int col=0,longest; + int i,k,l; + + if (num_of_supported_PICS == 0) + find_device(NULL); /* load all the definitions */ + + /* decrement the column number if it's greater than zero */ + ncols = (ncols > 1) ? ncols-1 : 4; + + /* Find the device with the longest name */ + for(i=0,longest=0; iname); + if(k>longest) + longest = k; + } + +#if 1 + /* heading */ + fprintf(stderr, "\nPIC14 processors and their characteristics:\n\n"); + fprintf(stderr, " processor"); + for(k=0; kname); + l = longest + 2 - strlen(Pics[i]->name); + for(k=0; kprogramMemSize % 1024 == 0) + fprintf(stderr, "%4dK", Pics[i]->programMemSize / 1024); + else + fprintf(stderr, "%5d", Pics[i]->programMemSize); + + fprintf(stderr, " %5d %5d %4d\n", + Pics[i]->dataMemSize, Pics[i]->eepromMemSize, Pics[i]->ioPins); + } + + col = 0; + + fprintf(stderr, "\nPIC14 processors supported:\n"); + for(i=0; i < num_of_supported_PICS; i++) { + + fprintf(stderr,"%s", Pics[i]->name); + if(colname); + for(k=0; kisEnhancedCore) + { + /* Hack: Fixup enhanced core's SFR(s). */ + pc_indf = &pc_indf0; + } + + return pic; +} + +/*-----------------------------------------------------------------* +* +*-----------------------------------------------------------------*/ +int picIsInitialized(void) +{ + if(pic && maxRAMaddress > 0) + return 1; + + return 0; + +} + +/*-----------------------------------------------------------------* +* char *processor_base_name(void) - Include file is derived from this. +*-----------------------------------------------------------------*/ +char *processor_base_name(void) +{ + + if(!pic) + return NULL; + + return pic->name; +} + +int IS_CONFIG_ADDRESS(int address) +{ + int i; + + for (i = 0; i < pic->num_configs; i++) + { + if ((-1 != address) && (address == pic->config[i])) + { + /* address is used for config words */ + return (1); + } // if + } // for + + return (0); +} + +/*-----------------------------------------------------------------* + * void pic14_assignConfigWordValue(int address, int value) + * + * Most midrange PICs have one config word at address 0x2007. + * Newer PIC14s have a second config word at address 0x2008. + * Even newer PICs have moved this address to 0x8007+... + * This routine will assign values to those addresses. + * + *-----------------------------------------------------------------*/ + +void +pic14_assignConfigWordValue(int address, int value) +{ + int i; + for (i = 0; i < pic->num_configs; i++) + { + if ((-1 != address) && (address == pic->config[i])) + { + config_word[i] = value; + pic14_hasSetConfigWord = 1; + } // if + } // for +} + +/*-----------------------------------------------------------------* + * int pic14_emitConfigWord (FILE * vFile) + * + * Emit the __config directives iff we found a previous assignment + * to the config word. + *-----------------------------------------------------------------*/ +int +pic14_emitConfigWord (FILE * vFile) +{ + int i; + + if (pic14_hasSetConfigWord) + { + fprintf (vFile, "%s", iComments2); + fprintf (vFile, "; config word(s)\n"); + fprintf (vFile, "%s", iComments2); + if (pic->num_configs > 1) + { + for (i = 0; i < pic->num_configs; i++) + { + if (-1 != config_word[i]) + { + fprintf (vFile, "\t__config _CONFIG%u, 0x%x\n", i + 1, config_word[i]); + } //if + } // for + } + else + { + if (-1 != config_word[0]) + { + fprintf (vFile, "\t__config 0x%x\n", config_word[0]); + } // if + } // if + + return 1; + } + return 0; +} + +/*-----------------------------------------------------------------* + * True iff the device has memory aliased in every bank. + * If true, low and high will be set to the low and high address + * occupied by the (last) sharebank found. + *-----------------------------------------------------------------*/ +static int pic14_hasSharebank(int *low, int *high, int *size) +{ + memRange *r; + + assert(pic); + r = pic->ram; + + while (r) { + //fprintf (stderr, "%s: region %x..%x, bank %x, alias %x, pic->bankmask %x, min_size %d\n", __FUNCTION__, r->start_address, r->end_address, r->bank, r->alias, pic->bankMask, size ? *size : 0); + // find sufficiently large shared region + if ((r->alias == pic->bankMask) + && (r->end_address != r->start_address) // ignore SFRs + && (!size || (*size <= (r->end_address - r->start_address + 1)))) + { + if (low) *low = r->start_address; + if (high) *high = r->end_address; + if (size) *size = r->end_address - r->start_address + 1; + return 1; + } // if + r = r->next; + } // while + + if (low) *low = 0x0; + if (high) *high = 0x0; + if (size) *size = 0x0; + //fprintf (stderr, "%s: no shared bank found\n", __FUNCTION__); + return 0; +} + +/* + * True iff the memory region [low, high] is aliased in all banks. + */ +static int pic14_isShared(int low, int high) +{ + memRange *r; + + assert(pic); + r = pic->ram; + + while (r) { + //fprintf (stderr, "%s: region %x..%x, bank %x, alias %x, pic->bankmask %x\n", __FUNCTION__, r->start_address, r->end_address, r->bank, r->alias, pic->bankMask); + if ((r->alias == pic->bankMask) && (r->start_address <= low) && (r->end_address >= high)) { + return 1; + } // if + r = r->next; + } // while + + return 0; +} + +/* + * True iff all RAM is aliased in all banks (no BANKSELs required except for + * SFRs). + */ +int pic14_allRAMShared(void) +{ + memRange *r; + + assert(pic); + r = pic->ram; + + while (r) { + if (r->alias != pic->bankMask) return 0; + r = r->next; + } // while + + return 1; +} + +/* + * True iff the pseudo stack is a sharebank --> let linker place it. + * [low, high] denotes a size byte long block of (shared or banked) + * memory to be used. + */ +int pic14_getSharedStack(int *low, int *high, int *size) +{ + int haveShared; + int l, h, s; + + s = options.stack_size ? options.stack_size : 0x10; + haveShared = pic14_hasSharebank(&l, &h, &s); + if ((options.stack_loc != 0) || !haveShared) + { + // sharebank not available or not to be used + s = options.stack_size ? options.stack_size : 0x10; + l = options.stack_loc ? options.stack_loc : 0x20; + h = l + s - 1; + if (low) *low = l; + if (high) *high = h; + if (size) *size = s; + // return 1 iff [low, high] is present in all banks + //fprintf(stderr, "%s: low %x, high %x, size %x, shared %d\n", __FUNCTION__, l, h, s, pic14_isShared(l, h)); + return (pic14_isShared(l, h)); + } else { + // sharebanks available for use by the stack + if (options.stack_size) s = options.stack_size; + else if (!s || s > 16) s = 16; // limit stack to 16 bytes in SHAREBANK + + // provide addresses for sharebank + if (low) *low = l; + if (high) *high = l + s - 1; + if (size) *size = s; + //fprintf(stderr, "%s: low %x, high %x, size %x, shared 1\n", __FUNCTION__, l, h, s); + return 1; + } +} + +PIC_device * pic14_getPIC(void) +{ + return pic; +} diff --git a/src/pic14/device.h b/src/pic14/device.h new file mode 100644 index 0000000..2ff58ff --- /dev/null +++ b/src/pic14/device.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + + device.c - Accomodates subtle variations in PIC devices + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* + PIC device abstraction + + There are dozens of variations of PIC microcontrollers. This include + file attempts to abstract those differences so that SDCC can easily + deal with them. +*/ + +#ifndef __DEVICE_H__ +#define __DEVICE_H__ + +#include "common.h" + +#define MAX_NUM_CONFIGS 16 + +/* + * Imports + */ +extern char *iComments2; + +/* memRange - a structure to define a range of valid memory addresses + * + * The Memory of most PICs (and other micros) is a collection of + * disjoint chunks. The memRange structure will define the start + * and end address of one of these chunks. The memory map of a + * particular device is a collection of memRange struct's. + */ + +typedef struct memRange { + int start_address; /* first address in range */ + int end_address; /* last */ + int alias; /* bit mask defining how/if memory range is aliased + * e.g. alias = 0x80 means start_address is identical + * to the memory location at (0x80 | start_address) */ + int bank; /* PIC memory bank this range occupies */ + struct memRange *next; /* linked list */ + +} memRange; + +/* Processor unique attributes */ +typedef struct PIC_device { + char *name; /* the processor name */ + + memRange *ram; /* RAM memory map */ + memRange *sfr; /* SFR memory map */ + + int maxRAMaddress; /* maximum value for a data address */ + int defMaxRAMaddrs; /* default maximum value for a data address */ + int bankMask; /* Bitmask that is ANDed with address to extract banking bits */ + // int hasAliasedRAM:1; /* True if there are bank independent registers */ + int num_configs; /* number of config words for this device */ + int config[MAX_NUM_CONFIGS]; /* addresses of config word(s) */ + + int programMemSize; /* program memory size in words - for device listing only */ + int dataMemSize; /* data (RAM) memory size in bytes - for device listing only */ + int eepromMemSize; /* EEPROM memory size in bytes - for device listing only */ + int ioPins; /* number of I/O pins - for device listing only */ + int isEnhancedCore; /* enhanced cores (19f1934) feature automatic context saving */ + +} PIC_device; + + +PIC_device *init_pic(char *pic_type); +int picIsInitialized(void); +char *processor_base_name(void); +int IS_CONFIG_ADDRESS(int addr); +void pic14_assignConfigWordValue(int address, int value); +int pic14_emitConfigWord(FILE *vFile); + +int pic14_allRAMShared(void); +int pic14_getSharedStack(int *low, int *high, int *size); +PIC_device * pic14_getPIC(void); + +#endif /* __DEVICE_H__ */ diff --git a/src/pic14/gen.c b/src/pic14/gen.c new file mode 100644 index 0000000..7dd7548 --- /dev/null +++ b/src/pic14/gen.c @@ -0,0 +1,7842 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for pic + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + PIC port: + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + Copyright (C) 2005, Raphael Neider + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +/* + * This is the down and dirty file with all kinds of + * kludgy & hacky stuff. This is what it is all about + * CODE GENERATION for a specific MCU . some of the + * routines may be reusable, will have to see. + */ + +#include "device.h" +#include "gen.h" +#include "glue.h" +#include "dbuf_string.h" + +/* + * Imports + */ +extern struct dbuf_s *codeOutBuf; +extern set *externs; + +static PIC_device *pic = NULL; + +static pCodeOp *popGetImmd (const char *name, unsigned int offset, int index, int is_func); +static pCodeOp *popRegFromString (const char *str, int size, int offset); +static int aop_isLitLike (asmop * aop); +static void genCritical (iCode * ic); +static void genEndCritical (iCode * ic); + +/* The PIC port(s) need not differentiate between POINTER and FPOINTER. */ +#define PIC_IS_DATA_PTR(x) (IS_DATA_PTR(x) || IS_FARPTR(x)) + +/* + * max_key keeps track of the largest label number used in + * a function. This is then used to adjust the label offset + * for the next function. + */ +static int max_key = 0; +static int labelOffset = 0; +static int GpseudoStkPtr = 0; +static int pic14_inISR = 0; + +static char *zero = "0x00"; +static char *one = "0x01"; +static char *spname = "sp"; + +unsigned fReturnSizePic = 4; /* shared with ralloc.c */ +static char *fReturnpic14[] = { "temp1", "temp2", "temp3", "temp4" }; + +static char **fReturn = fReturnpic14; + +static struct +{ + short accInUse; + short nRegsSaved; + set *sendSet; +} _G; + +/* + * Resolved ifx structure. This structure stores information + * about an iCode ifx that makes it easier to generate code. + */ +typedef struct resolvedIfx +{ + symbol *lbl; /* pointer to a label */ + int condition; /* true or false ifx */ + int generated; /* set true when the code associated with the ifx + * is generated */ +} resolvedIfx; + +static pBlock *pb; + +/*-----------------------------------------------------------------*/ +/* my_powof2(n) - If `n' is an integer power of 2, then the */ +/* exponent of 2 is returned, otherwise -1 is */ +/* returned. */ +/* note that this is similar to the function `powof2' in SDCCsymt */ +/* if(n == 2^y) */ +/* return y; */ +/* return -1; */ +/*-----------------------------------------------------------------*/ +static int +my_powof2 (unsigned long num) +{ + if (num) + { + if ((num & (num - 1)) == 0) + { + int nshifts = -1; + while (num) + { + num >>= 1; + nshifts++; + } + return nshifts; + } + } + + return -1; +} + +void +DEBUGpic14_AopType (int line_no, operand * left, operand * right, operand * result) +{ + + DEBUGpic14_emitcode ("; ", "line = %d result %s=%s, size=%d, left %s=%s, size=%d, right %s=%s, size=%d", + line_no, + ((result) ? AopType (AOP_TYPE (result)) : "-"), + ((result) ? aopGet (AOP (result), 0, TRUE, FALSE) : "-"), + ((result) ? AOP_SIZE (result) : 0), + ((left) ? AopType (AOP_TYPE (left)) : "-"), + ((left) ? aopGet (AOP (left), 0, TRUE, FALSE) : "-"), + ((left) ? AOP_SIZE (left) : 0), + ((right) ? AopType (AOP_TYPE (right)) : "-"), + ((right) ? aopGet (AOP (right), 0, FALSE, FALSE) : "-"), ((right) ? AOP_SIZE (right) : 0)); + +} + +static void +DEBUGpic14_AopTypeSign (int line_no, operand * left, operand * right, operand * result) +{ + + DEBUGpic14_emitcode ("; ", "line = %d, signs: result %s=%c, left %s=%c, right %s=%c", + line_no, + ((result) ? AopType (AOP_TYPE (result)) : "-"), + ((result) ? (SPEC_USIGN (operandType (result)) ? 'u' : 's') : '-'), + ((left) ? AopType (AOP_TYPE (left)) : "-"), + ((left) ? (SPEC_USIGN (operandType (left)) ? 'u' : 's') : '-'), + ((right) ? AopType (AOP_TYPE (right)) : "-"), + ((right) ? (SPEC_USIGN (operandType (right)) ? 'u' : 's') : '-')); + +} + +void +DEBUGpic14_emitcode (const char *inst, const char *fmt, ...) +{ + va_list ap; + + if (!debug_verbose && !options.debug) + return; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + + addpCode2pBlock (pb, newpCodeCharP (genLine.lineCurr->line)); +} + +void +emitpComment (const char *fmt, ...) +{ + va_list ap; + struct dbuf_s dbuf; + const char *line; + + dbuf_init (&dbuf, INITIAL_INLINEASM); + + dbuf_append_char (&dbuf, ';'); + va_start (ap, fmt); + dbuf_vprintf (&dbuf, fmt, ap); + va_end (ap); + + line = dbuf_detach_c_str (&dbuf); + emit_raw (line); + dbuf_free (line); + + addpCode2pBlock (pb, newpCodeCharP (genLine.lineCurr->line)); +} + +void +emitpLabel (int key) +{ + addpCode2pBlock (pb, newpCodeLabel (NULL, labelKey2num (key + labelOffset))); +} + +/* gen.h defines a macro emitpcode that should be used to call emitpcode + * as this allows for easy debugging (ever asked the question: where was + * this instruction geenrated? Here is the answer... */ +void +emitpcode_real (PIC_OPCODE poc, pCodeOp * pcop) +{ + if (pcop) + addpCode2pBlock (pb, newpCode (poc, pcop)); + else + { + static int has_warned = FALSE; + + DEBUGpic14_emitcode (";", "%s ignoring NULL pcop", __FUNCTION__); + if (!has_warned) + { + has_warned = TRUE; + fprintf (stderr, "WARNING: encountered NULL pcop--this is probably a compiler bug...\n"); + } + } +} + +static void +emitpcodeNULLop (PIC_OPCODE poc) +{ + addpCode2pBlock (pb, newpCode (poc, NULL)); +} + +/*-----------------------------------------------------------------*/ +/* pic14_emitcode - writes the code into a file : for now it is simple */ +/*-----------------------------------------------------------------*/ +void +pic14_emitcode (const char *inst, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + + if (debug_verbose) + addpCode2pBlock (pb, newpCodeCharP (genLine.lineCurr->line)); +} + +/*-----------------------------------------------------------------*/ +/* pic14_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +pic14_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = TRUE; + pic14_emitcode ("", ";%s ==.", debugSym); + genLine.lineElement.isDebug = FALSE; +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_alloc(sizeof(asmop)); + aop->type = type; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* resolveIfx - converts an iCode ifx into a form more useful for */ +/* generating code */ +/*-----------------------------------------------------------------*/ +static void +resolveIfx (resolvedIfx * resIfx, iCode * ifx) +{ + if (!resIfx) + return; + + // DEBUGpic14_emitcode("; ***","%s %d",__FUNCTION__,__LINE__); + + resIfx->condition = TRUE; /* assume that the ifx is true */ + resIfx->generated = FALSE; /* indicate that the ifx has not been used */ + + if (!ifx) + { + resIfx->lbl = NULL; /* this is wrong: newiTempLabel(NULL); / * oops, there is no ifx. so create a label */ + } + else + { + if (IC_TRUE (ifx)) + { + resIfx->lbl = IC_TRUE (ifx); + } + else + { + resIfx->lbl = IC_FALSE (ifx); + resIfx->condition = FALSE; + } + } + + // DEBUGpic14_emitcode("; ***","%s lbl->key=%d, (lab offset=%d)",__FUNCTION__,resIfx->lbl->key,labelOffset); + +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, symbol * sym, bool result) +{ + asmop *aop; + memmap *space = SPEC_OCLS (sym->etype); + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if already has one */ + if (sym->aop) + return sym->aop; + + //DEBUGpic14_emitcode(";","%d",__LINE__); + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + DEBUGpic14_emitcode (";", "%d sym->rname = %s, size = %d", __LINE__, sym->rname, aop->size); + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + + sym->aop = aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = popGetImmd (sym->rname, 0, 0, 1); + PCOI (aop->aopu.pcop)->_const = IN_CODESPACE (space); + PCOI (aop->aopu.pcop)->_function = TRUE; + PCOI (aop->aopu.pcop)->index = 0; + aop->size = FARPTRSIZE; + DEBUGpic14_emitcode (";", "%d size = %d, name =%s", __LINE__, aop->size, sym->rname); + return aop; + } + + if (IS_ARRAY (sym->type)) + { + sym->aop = aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = popGetImmd (sym->rname, 0, 0, 1); + PCOI (aop->aopu.pcop)->_const = IN_CODESPACE (space); + PCOI (aop->aopu.pcop)->_function = FALSE; + PCOI (aop->aopu.pcop)->index = 0; + aop->size = getSize (sym->etype) * DCL_ELEM (sym->type); + + DEBUGpic14_emitcode (";", "%d size = %d, name =%s", __LINE__, aop->size, sym->rname); + return aop; + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + sym->aop = aop = newAsmop (AOP_PCODE); + + aop->aopu.pcop = popGetImmd (sym->rname, 0, 0, 0); + PCOI (aop->aopu.pcop)->_const = IN_CODESPACE (space); + PCOI (aop->aopu.pcop)->index = 0; + + DEBUGpic14_emitcode (";", "%d: rname %s, val %d, const = %d", __LINE__, sym->rname, 0, PCOI (aop->aopu.pcop)->_const); + + allocDirReg (IC_LEFT (ic)); + + aop->size = FARPTRSIZE; + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = TRUE; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerialzes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (operand * op) // x symbol *sym) +{ + symbol *sym = OP_SYMBOL (op); + iCode *ic = NULL; + asmop *aop = newAsmop (AOP_PCODE); + int val = 0; + + ic = sym->rematiCode; + + DEBUGpic14_emitcode (";", "%s %d", __FUNCTION__, __LINE__); + if (IS_OP_POINTER (op)) + { + DEBUGpic14_emitcode (";", "%s %d IS_OP_POINTER", __FUNCTION__, __LINE__); + } + for (;;) + { + if (ic->op == '+') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + } + else + break; + + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + + aop->aopu.pcop = popGetImmd (OP_SYMBOL (IC_LEFT (ic))->rname, 0, val, 0); + PCOI (aop->aopu.pcop)->_const = IS_PTR_CONST (operandType (op)); + PCOI (aop->aopu.pcop)->index = val; + + DEBUGpic14_emitcode (";", "%d: rname %s, val %d, const = %d", + __LINE__, OP_SYMBOL (IC_LEFT (ic))->rname, val, IS_PTR_CONST (operandType (op))); + + // DEBUGpic14_emitcode(";","aop type %s",AopType(AOP_TYPE(IC_LEFT(ic)))); + + allocDirReg (IC_LEFT (ic)); + + return aop; +} + +static int +aopIdx (asmop * aop, int offset) +{ + if (!aop) + return -1; + + if (aop->type != AOP_REG) + return -2; + + return aop->aopu.aop_reg[offset]->rIdx; + +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + if (sym1->rname[0] && sym2->rname[0] && strcmp (sym1->rname, sym2->rname) == 0) + return TRUE; + + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* pic14_sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +bool +pic14_sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + if (aop1->type != AOP_REG || aop2->type != AOP_REG) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +void +aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + { + sym_link *type = operandType (op); + if (IS_PTR_CONST (type)) + DEBUGpic14_emitcode (";", "%d aop type is const pointer", __LINE__); + } + + /* if already has a asmop then continue */ + if (op->aop) + return; + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + DEBUGpic14_emitcode (";", "%d", __LINE__); + op->aop = OP_SYMBOL (op)->aop; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + //DEBUGpic14_emitcode(";","%d - true symop",__LINE__); + op->aop = aopForSym (ic, OP_SYMBOL (op), result); + return; + } + + /* this is a temporary : this has + only four choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + aop = op->aop = sym->aop = newAsmop (AOP_CRY); + aop->size = 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + + DEBUGpic14_emitcode (";", "%d", __LINE__); + /* rematerialize it NOW */ + if (sym->remat) + { + + sym->aop = op->aop = aop = aopForRemat (op); + aop->size = getSize (sym->type); + //DEBUGpic14_emitcode(";"," %d: size %d, %s\n",__LINE__,aop->size,aop->aopu.aop_immd); + return; + } + + if (sym->ruonly) + { + if (sym->isptr) // && sym->uptr + { + aop = op->aop = sym->aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = newpCodeOp (NULL, PO_GPR_POINTER); //popCopyReg(&pc_fsr); + + //PCOI(aop->aopu.pcop)->_const = 0; + //PCOI(aop->aopu.pcop)->index = 0; + /* + DEBUGpic14_emitcode(";","%d: rname %s, val %d, const = %d", + __LINE__,sym->rname, 0, PCOI(aop->aopu.pcop)->_const); + */ + //allocDirReg (IC_LEFT(ic)); + + aop->size = getSize (sym->type); + DEBUGpic14_emitcode (";", "%d", __LINE__); + return; + + } + else + { + + unsigned i; + + aop = op->aop = sym->aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < fReturnSizePic; i++) + aop->aopu.aop_str[i] = fReturn[i]; + + DEBUGpic14_emitcode (";", "%d", __LINE__); + return; + } + } + + /* else spill location */ + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + DEBUGpic14_emitcode (";", "%s %d %s sym->rname = %s, offset %d", + __FUNCTION__, __LINE__, sym->usl.spillLoc->rname, sym->rname, sym->usl.spillLoc->offset); + + sym->aop = op->aop = aop = newAsmop (AOP_PCODE); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + //aop->aopu.pcop = popGetImmd(sym->usl.spillLoc->rname,0,sym->usl.spillLoc->offset); + aop->aopu.pcop = popRegFromString (sym->usl.spillLoc->rname, getSize (sym->type), sym->usl.spillLoc->offset); + aop->size = getSize (sym->type); + + return; + } + } + + { + sym_link *type = operandType (op); + if (IS_PTR_CONST (type)) + DEBUGpic14_emitcode (";", "%d aop type is const pointer", __LINE__); + } + + /* must be in a register */ + DEBUGpic14_emitcode (";", "%d register type nRegs=%d", __LINE__, sym->nRegs); + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +void +freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + aop->freed = TRUE; + + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic14aopLiteral - string from a literal value */ +/*-----------------------------------------------------------------*/ +static unsigned int +pic14aopLiteral (value * val, int offset) +{ + union + { + float f; + unsigned char c[4]; + } fl; + + /* if it is a float then it gets tricky */ + /* otherwise it is fairly simple */ + if (!IS_FLOAT (val->type)) + { + unsigned long v = ulFromVal (val); + + return ((v >> (offset * 8)) & 0xff); + } + + /* it is type float */ + fl.f = (float) floatFromVal (val); +#ifdef WORDS_BIGENDIAN + return fl.c[3 - offset]; +#else + return fl.c[offset]; +#endif +} + +/*-----------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-----------------------------------------------------------------*/ +char * +aopGet (asmop * aop, int offset, bool bit16, bool dname) +{ + //DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + /* offset is greater than + size then zero */ + assert (aop); + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + return zero; + + /* depending on type */ + switch (aop->type) + { + case AOP_IMMD: + if (bit16) + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_immd); + else if (offset) + SNPRINTF(buffer, sizeof(buffer), "(%s >> %d)", aop->aopu.aop_immd, offset * 8); + else + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_immd); + DEBUGpic14_emitcode (";", "%d immd %s", __LINE__, buffer); + return Safe_strdup(buffer); + + case AOP_DIR: + if (offset) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", aop->aopu.aop_dir, offset); + DEBUGpic14_emitcode (";", "oops AOP_DIR did this %s\n", buffer); + } + else + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + return Safe_strdup(buffer); + + case AOP_REG: + //if (dname) + // return aop->aopu.aop_reg[offset]->dname; + //else + return aop->aopu.aop_reg[offset]->name; + + case AOP_CRY: + //pic14_emitcode(";","%d",__LINE__); + return aop->aopu.aop_dir; + + case AOP_LIT: + SNPRINTF(buffer, sizeof(buffer), "0x%02x", pic14aopLiteral (aop->aopu.aop_lit, offset)); + return Safe_strdup(buffer); + + case AOP_STR: + aop->coff = offset; + if (strcmp (aop->aopu.aop_str[offset], "a") == 0 && dname) + return "acc"; + DEBUGpic14_emitcode (";", "%d - %s", __LINE__, aop->aopu.aop_str[offset]); + return aop->aopu.aop_str[offset]; + + case AOP_PCODE: + { + pCodeOp *pcop = aop->aopu.pcop; + DEBUGpic14_emitcode (";", "%d: aopGet AOP_PCODE type %s", __LINE__, pCodeOpType (pcop)); + if (pcop->name) + { + if (pcop->type == PO_IMMEDIATE) + { + offset += PCOI (pcop)->index; + } + + if (offset) + { + DEBUGpic14_emitcode (";", "%s offset %d", pcop->name, offset); + SNPRINTF(buffer, sizeof(buffer), "(%s+%d)", pcop->name, offset); + } + else + { + DEBUGpic14_emitcode (";", "%s", pcop->name); + SNPRINTF(buffer, sizeof(buffer), "%s", pcop->name); + } + } + else + SNPRINTF(buffer, sizeof(buffer), "0x%02x", PCOI (aop->aopu.pcop)->offset); + } + + return Safe_strdup(buffer); + } + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (0); +} + +/*-----------------------------------------------------------------*/ +/* popGetTempReg - create a new temporary pCodeOp */ +/*-----------------------------------------------------------------*/ +static pCodeOp * +popGetTempReg (void) +{ + + pCodeOp *pcop; + + pcop = newpCodeOp (NULL, PO_GPR_TEMP); + if (pcop && pcop->type == PO_GPR_TEMP && PCOR (pcop)->r) + { + PCOR (pcop)->r->wasUsed = TRUE; + PCOR (pcop)->r->isFree = FALSE; + } + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* popReleaseTempReg - create a new temporary pCodeOp */ +/*-----------------------------------------------------------------*/ +static void +popReleaseTempReg (pCodeOp * pcop) +{ + + if (pcop && pcop->type == PO_GPR_TEMP && PCOR (pcop)->r) + PCOR (pcop)->r->isFree = TRUE; + +} + +/*-----------------------------------------------------------------*/ +/* popGetLabel - create a new pCodeOp of type PO_LABEL */ +/*-----------------------------------------------------------------*/ +pCodeOp * +popGetLabel (unsigned int key) +{ + + DEBUGpic14_emitcode ("; ***", "%s key=%d, label offset %d", __FUNCTION__, key, labelOffset); + + if (key > (unsigned int) max_key) + max_key = key; + + return newpCodeOpLabel (NULL, labelKey2num (key + labelOffset)); +} + +/*-------------------------------------------------------------------*/ +/* popGetHighLabel - create a new pCodeOp of type PO_LABEL with offset=1 */ +/*-------------------------------------------------------------------*/ +static pCodeOp * +popGetHighLabel (unsigned int key) +{ + pCodeOp *pcop; + pcop = popGetLabel (key); + PCOLAB (pcop)->offset = 1; + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* popGetLit - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +popGetLit (unsigned int lit) +{ + + return newpCodeOpLit ((unsigned char) lit); +} + +/*-----------------------------------------------------------------*/ +/* popGetImmd - asm operator to pcode immediate conversion */ +/*-----------------------------------------------------------------*/ +static pCodeOp * +popGetImmd (const char *name, unsigned int offset, int index, int is_func) +{ + + return newpCodeOpImmd (name, offset, index, 0, is_func); +} + +/*-----------------------------------------------------------------*/ +/* popGetWithString - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +static pCodeOp * +popGetWithString (const char *str, int isExtern) +{ + pCodeOp *pcop; + + + if (!str) + { + fprintf (stderr, "NULL string %s %d\n", __FILE__, __LINE__); + exit (1); + } + + pcop = newpCodeOp (str, PO_STR); + PCOS (pcop)->isPublic = isExtern ? 1 : 0; + + return pcop; +} + +pCodeOp * +popGetExternal (const char *str, int isReg) +{ + pCodeOp *pcop; + + if (isReg) + { + pcop = newpCodeOpRegFromStr (str); + } + else + { + pcop = popGetWithString (str, 1); + } + + if (str) + { + symbol *sym; + + for (sym = setFirstItem (externs); sym; sym = setNextItem (externs)) + { + if (!strcmp (str, sym->rname)) + break; + } + + if (!sym) + { + sym = newSymbol (str, 0); + strncpy (sym->rname, str, SDCC_NAME_MAX); + addSet (&externs, sym); + } // if + sym->used++; + } + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* popRegFromString - */ +/*-----------------------------------------------------------------*/ +static pCodeOp * +popRegFromString (const char *str, int size, int offset) +{ + + pCodeOp *pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->type = PO_DIR; + + DEBUGpic14_emitcode (";", "%d", __LINE__); + + if (!str) + str = "BAD_STRING"; + + pcop->name = Safe_strdup(str); + + //pcop->name = Safe_strdup( ( (str) ? str : "BAD STRING")); + + PCOR (pcop)->r = dirregWithName (pcop->name); + if (PCOR (pcop)->r == NULL) + { + //fprintf(stderr,"%d - couldn't find %s in allocated registers, size =%d\n",__LINE__,aop->aopu.aop_dir,aop->size); + PCOR (pcop)->r = allocRegByName (pcop->name, size); + DEBUGpic14_emitcode (";", "%d %s offset=%d - had to alloc by reg name", __LINE__, pcop->name, offset); + } + else + { + DEBUGpic14_emitcode (";", "%d %s offset=%d", __LINE__, pcop->name, offset); + } + PCOR (pcop)->instance = offset; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static pCodeOp * +popRegFromIdx (int rIdx) +{ + pCodeOp *pcop; + + DEBUGpic14_emitcode ("; ***", "%s,%d , rIdx=0x%x", __FUNCTION__, __LINE__, rIdx); + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + + PCOR (pcop)->rIdx = rIdx; + PCOR (pcop)->r = typeRegWithIdx (rIdx, REG_STK, 1); + PCOR (pcop)->r->isFree = FALSE; + PCOR (pcop)->r->wasUsed = TRUE; + + pcop->type = PCOR (pcop)->r->pc_type; + + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* popGet - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +popGet (asmop * aop, int offset) //, bool bit16, bool dname) +{ + //char *s = buffer ; + //char *rs; + + pCodeOp *pcop; + + //DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + /* offset is greater than + size then zero */ + + assert (aop); + + + /* XXX: still needed for BIT operands (AOP_CRY) */ + if ((offset >= aop->size) && (aop->type != AOP_LIT) && (aop->type != AOP_PCODE)) + { + printf ("%s: (offset[%d] >= AOP_SIZE(op)[%d]) && (AOP_TYPE(op)[%d] != { AOP_LIT, AOP_PCODE })\n", + __FUNCTION__, offset, aop->size, aop->type); + return NULL; //zero; + } + + /* depending on type */ + switch (aop->type) + { + + case AOP_IMMD: + DEBUGpic14_emitcode (";", "%d", __LINE__); + return popGetImmd (aop->aopu.aop_immd, offset, 0, 0); + + case AOP_DIR: + return popRegFromString (aop->aopu.aop_dir, aop->size, offset); + + case AOP_REG: + { + int rIdx; + assert (offset < aop->size); + rIdx = aop->aopu.aop_reg[offset]->rIdx; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR (pcop)->rIdx = rIdx; + PCOR (pcop)->r = pic14_regWithIdx (rIdx); + PCOR (pcop)->r->wasUsed = TRUE; + PCOR (pcop)->r->isFree = FALSE; + + PCOR (pcop)->instance = offset; + pcop->type = PCOR (pcop)->r->pc_type; + //rs = aop->aopu.aop_reg[offset]->name; + DEBUGpic14_emitcode (";", "%d rIdx = r0x%X ", __LINE__, rIdx); + return pcop; + } + + case AOP_CRY: + pcop = newpCodeOpBit (aop->aopu.aop_dir, -1, 1); + PCOR (pcop)->r = dirregWithName (aop->aopu.aop_dir); + //if(PCOR(pcop)->r == NULL) + //fprintf(stderr,"%d - couldn't find %s in allocated registers\n",__LINE__,aop->aopu.aop_dir); + return pcop; + + case AOP_LIT: + return newpCodeOpLit (pic14aopLiteral (aop->aopu.aop_lit, offset)); + + case AOP_STR: + DEBUGpic14_emitcode (";", "%d %s", __LINE__, aop->aopu.aop_str[offset]); + return newpCodeOpRegFromStr (aop->aopu.aop_str[offset]); + + case AOP_PCODE: + pcop = NULL; + DEBUGpic14_emitcode (";", "popGet AOP_PCODE (%s + %i) %d %s", pCodeOpType (aop->aopu.pcop), offset, + __LINE__, ((aop->aopu.pcop->name) ? (aop->aopu.pcop->name) : "no name")); + //emitpComment ("popGet; name %s, offset: %i, pcop-type: %s\n", aop->aopu.pcop->name, offset, pCodeOpType (aop->aopu.pcop)); + switch (aop->aopu.pcop->type) + { + case PO_IMMEDIATE: + pcop = pCodeOpCopy (aop->aopu.pcop); + /* usually we want to access the memory at " + offset" (using ->index), + * but sometimes we want to access the high byte of the symbol's address (using ->offset) */ + PCOI (pcop)->index += offset; + //PCOI(pcop)->offset = 0; + break; + case PO_DIR: + pcop = pCodeOpCopy (aop->aopu.pcop); + PCOR (pcop)->instance = offset; + break; + default: + assert (!"unhandled pCode type"); + break; + } // switch + return pcop; + } + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "popGet got unsupported aop->type"); + exit (0); +} + +/*-----------------------------------------------------------------*/ +/* popGetAddr - access the low/high word of a symbol (immediate) */ +/* (for non-PO_IMMEDIATEs this is the same as popGet) */ +/*-----------------------------------------------------------------*/ +pCodeOp * +popGetAddr (asmop * aop, int offset, int index) +{ + if (aop->type == AOP_PCODE && aop->aopu.pcop->type == PO_IMMEDIATE) + { + pCodeOp *pcop = aop->aopu.pcop; + assert (offset <= GPTRSIZE); + + /* special case: index >= 2 should return GPOINTER-style values */ + if (offset == 2) + { + pcop = popGetLit (aop->code ? GPTRTAG_CODE : GPTRTAG_DATA); + return pcop; + } + + pcop = pCodeOpCopy (pcop); + /* usually we want to access the memory at " + offset" (using ->index), + * but sometimes we want to access the high byte of the symbol's address (using ->offset) */ + PCOI (pcop)->offset += offset; + PCOI (pcop)->index += index; + //fprintf (stderr, "is PO_IMMEDIATE: %s+o%d+i%d (new o%d,i%d)\n", pcop->name,PCOI(pcop)->offset,PCOI(pcop)->index, offset, index); + return pcop; + } + else + { + return popGet (aop, offset + index); + } +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop */ +/*-----------------------------------------------------------------*/ +void +aopPut (asmop * aop, const char *s, int offset) +{ + symbol *lbl; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (0); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DIR: + if (offset) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", aop->aopu.aop_dir, offset); + fprintf (stderr, "oops aopPut:AOP_DIR did this %s\n", s); + } + else + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + + if (strcmp (buffer, s)) + { + DEBUGpic14_emitcode (";", "%d", __LINE__); + if (strcmp (s, "W")) + pic14_emitcode ("movf", "%s,w", s); + + pic14_emitcode ("movwf", "%s", buffer); + + if (strcmp (s, "W")) + { + pic14_emitcode (";BUG!? should have this:movf", "%s,w %d", s, __LINE__); + if (offset >= aop->size) + { + emitpcode (POC_CLRF, popGet (aop, offset)); + break; + } + else + { + emitpcode (POC_MOVFW, popGetImmd (s, 0, offset, 0)); + } + } + emitpcode (POC_MOVWF, popGet (aop, offset)); + } + break; + + case AOP_REG: + if (strcmp (aop->aopu.aop_reg[offset]->name, s) != 0) + { + if (strcmp (s, "W") == 0) + pic14_emitcode ("movf", "%s,w ; %d", s, __LINE__); + + pic14_emitcode ("movwf", "%s", aop->aopu.aop_reg[offset]->name); + + if (strcmp (s, zero) == 0) + { + emitpcode (POC_CLRF, popGet (aop, offset)); + + } + else if (strcmp (s, "W") == 0) + { + pCodeOp *pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->type = PO_GPR_REGISTER; + + PCOR (pcop)->rIdx = -1; + PCOR (pcop)->r = NULL; + + DEBUGpic14_emitcode (";", "%d", __LINE__); + pcop->name = Safe_strdup (s); + emitpcode (POC_MOVFW, pcop); + emitpcode (POC_MOVWF, popGet (aop, offset)); + } + else if (strcmp (s, one) == 0) + { + emitpcode (POC_CLRF, popGet (aop, offset)); + emitpcode (POC_INCF, popGet (aop, offset)); + } + else + { + emitpcode (POC_MOVWF, popGet (aop, offset)); + } + } + break; + + case AOP_STK: + if (strcmp (s, "a") == 0) + pic14_emitcode ("push", "acc"); + else + pic14_emitcode ("push", "%s", s); + break; + + case AOP_CRY: + /* if bit variable */ + if (!aop->aopu.aop_dir) + { + pic14_emitcode ("clr", "a"); + pic14_emitcode ("rlc", "a"); + } + else + { + if (s == zero) + pic14_emitcode ("clr", "%s", aop->aopu.aop_dir); + else if (s == one) + pic14_emitcode ("setb", "%s", aop->aopu.aop_dir); + else if (!strcmp (s, "c")) + pic14_emitcode ("mov", "%s,c", aop->aopu.aop_dir); + else + { + lbl = newiTempLabel (NULL); + + if (strcmp (s, "a")) + { + MOVA (s); + } + + pic14_emitcode ("clr", "c"); + pic14_emitcode ("jz", "%05d_DS_", labelKey2num (lbl->key)); + pic14_emitcode ("cpl", "c"); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + pic14_emitcode ("mov", "%s,c", aop->aopu.aop_dir); + } + } + break; + + case AOP_STR: + aop->coff = offset; + if (strcmp (aop->aopu.aop_str[offset], s)) + pic14_emitcode ("mov", "%s,%s ; %d", aop->aopu.aop_str[offset], s, __LINE__); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit(0); + } +} + +/*-----------------------------------------------------------------*/ +/* mov2w_op - generate either a MOVLW or MOVFW based operand type */ +/*-----------------------------------------------------------------*/ +static void +mov2w_op (operand * op, int offset) +{ + assert (op); + FENTRY; + + /* for PO_IMMEDIATEs: use address or value? */ + if (op_isLitLike (op)) + { + /* access address of op */ + if (AOP_TYPE (op) != AOP_LIT) + { + assert (offset < 3); + } + if (IS_SYMOP (op) && IS_GENPTR (OP_SYM_TYPE (op)) && AOP_SIZE (op) < offset) + { + if (offset == GPTRSIZE - 1) + emitpcode (POC_MOVLW, popGetLit (GPTRTAG_DATA)); + else + emitpcode (POC_MOVLW, popGetLit (0)); + } + else + emitpcode (POC_MOVLW, popGetAddr (AOP (op), offset, 0)); + } + else + { + /* access value stored in op */ + mov2w (AOP (op), offset); + } +} + + +/*-----------------------------------------------------------------*/ +/* mov2w - generate either a MOVLW or MOVFW based operand type */ +/*-----------------------------------------------------------------*/ +void +mov2w (asmop * aop, int offset) +{ + + if (!aop) + return; + + DEBUGpic14_emitcode ("; ***", "%s %d offset=%d", __FUNCTION__, __LINE__, offset); + + if (aop_isLitLike (aop)) + emitpcode (POC_MOVLW, popGetAddr (aop, offset, 0)); + else + emitpcode (POC_MOVFW, popGet (aop, offset)); + +} + +static void +movwf (asmop * op, int offset) +{ + emitpcode (POC_MOVWF, popGet (op, offset)); +} + +static pCodeOp * +get_argument_pcop (int idx) +{ + assert (idx > 0 && "the 0th (first) argument is passed via WREG"); + return popRegFromIdx (Gstack_base_addr - (idx - 1)); +} + +static pCodeOp * +get_return_val_pcop (int offset) +{ + assert (offset > 0 && "the most significant byte is returned via WREG"); + return popRegFromIdx (Gstack_base_addr - (offset - 1)); +} + +static void +pass_argument (operand * op, int offset, int idx) +{ + if (op) + mov2w_op (op, offset); + if (idx != 0) + emitpcode (POC_MOVWF, get_argument_pcop (idx)); +} + +static void +get_returnvalue (operand * op, int offset, int idx) +{ + if (idx != 0) + emitpcode (POC_MOVFW, get_return_val_pcop (idx)); + movwf (AOP (op), offset); +} + +static void +call_libraryfunc (char *name) +{ + symbol *sym; + + /* library code might reside in different page... */ + emitpcode (POC_PAGESEL, popGetWithString (name, 1)); + /* call the library function */ + emitpcode (POC_CALL, popGetExternal (name, 0)); + /* might return from different page... */ + emitpcode (POC_PAGESEL, popGetWithString ("$", 0)); + + /* create symbol, mark it as `extern' */ + sym = findSym (SymbolTab, NULL, name); + if (!sym) + { + sym = newSymbol (name, 0); + strncpy (sym->rname, name, SDCC_NAME_MAX); + addSym (SymbolTab, sym, sym->rname, 0, 0, 0); + addSet (&externs, sym); + } // if + sym->used++; +} + +/*-----------------------------------------------------------------*/ +/* pic14_getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +int +pic14_getDataSize (operand * op) +{ + int size; + + size = AOP_SIZE (op); + return size; +} + +/*-----------------------------------------------------------------*/ +/* pic14_outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +void +pic14_outAcc (operand * result) +{ + int size, offset; + DEBUGpic14_emitcode ("; ***", "%s %d - ", __FUNCTION__, __LINE__); + DEBUGpic14_AopType (__LINE__, NULL, NULL, result); + + + size = pic14_getDataSize (result); + if (size) + { + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + emitpcode (POC_CLRF, popGet (AOP (result), offset++)); + } + +} + +/*-----------------------------------------------------------------*/ +/* pic14_outBitC - output a bit C */ +/*-----------------------------------------------------------------*/ +static void +pic14_outBitC (operand * result) +{ + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + aopPut (AOP (result), "c", 0); + else + { + pic14_emitcode ("clr", "a ; %d", __LINE__); + pic14_emitcode ("rlc", "a"); + pic14_outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* pic14_toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +pic14_toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper); + int offset = 0; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + assert (size > 0); + + if (size == 1) + { + /* MOVFW does not load the flags... */ + emitpcode (POC_MOVLW, popGetLit (0)); + offset = 0; + } + else + { + emitpcode (POC_MOVFW, popGet (AOP (oper), 0)); + offset = 1; + } + + while (offset < size) + { + emitpcode (POC_IORFW, popGet (AOP (oper), offset++)); + } + /* Z is set iff (oper == 0) */ +} + + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (iCode * ic) +{ + //symbol *tlbl; + int size; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + DEBUGpic14_AopType (__LINE__, IC_LEFT (ic), NULL, IC_RESULT (ic)); + /* if in bit space then a special case */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY) + { + emitpcode (POC_MOVLW, popGet (AOP (IC_LEFT (ic)), 0)); + emitpcode (POC_XORWF, popGet (AOP (IC_RESULT (ic)), 0)); + } + else + { + emitpcode (POC_CLRF, popGet (AOP (IC_RESULT (ic)), 0)); + emitpcode (POC_BTFSS, popGet (AOP (IC_LEFT (ic)), 0)); + emitpcode (POC_INCF, popGet (AOP (IC_RESULT (ic)), 0)); + } + goto release; + } + + size = AOP_SIZE (IC_LEFT (ic)); + mov2w (AOP (IC_LEFT (ic)), 0); + while (--size > 0) + { + if (op_isLitLike (IC_LEFT (ic))) + emitpcode (POC_IORLW, popGetAddr (AOP (IC_LEFT (ic)), size, 0)); + else + emitpcode (POC_IORFW, popGet (AOP (IC_LEFT (ic)), size)); + } + emitpcode (POC_MOVLW, popGetLit (0)); + emitSKPNZ; + emitpcode (POC_MOVLW, popGetLit (1)); + movwf (AOP (IC_RESULT (ic)), 0); + + for (size = 1; size < AOP_SIZE (IC_RESULT (ic)); size++) + { + emitpcode (POC_CLRF, popGet (AOP (IC_RESULT (ic)), size)); + } + goto release; + +release: + /* release the aops */ + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (iCode * ic) +{ + operand *left, *result; + int size, offset = 0; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + /* if both are in bit space then + a special case */ + if (AOP_TYPE (result) == AOP_CRY && AOP_TYPE (left) == AOP_CRY) + { + + pic14_emitcode ("mov", "c,%s", left->aop->aopu.aop_dir); + pic14_emitcode ("cpl", "c"); + pic14_emitcode ("mov", "%s,c", result->aop->aopu.aop_dir); + goto release; + } + + size = AOP_SIZE (result); + if (AOP_SIZE (left) < size) + size = AOP_SIZE (left); + while (size--) + { + emitpcode (POC_COMFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + offset++; + } + addSign (result, AOP_SIZE (left), !SPEC_USIGN (operandType (result))); + + +release: + /* release the aops */ + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* for this we just need to flip the + first it then copy the rest in place */ + size = AOP_SIZE (op) - 1; + + mov2w_op (op, size); + emitpcode (POC_XORLW, popGetLit (0x80)); + movwf (AOP (result), size); + + while (size--) + { + mov2w_op (op, size); + movwf (AOP (result), size); + } // while +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int size, i; + sym_link *optype; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + emitpcode (POC_BCF, popGet (AOP (IC_RESULT (ic)), 0)); + emitpcode (POC_BTFSS, popGet (AOP (IC_LEFT (ic)), 0)); + emitpcode (POC_BSF, popGet (AOP (IC_RESULT (ic)), 0)); + + goto release; + } + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype)) + { + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero by taking the 2's complement */ + size = AOP_SIZE (IC_LEFT (ic)); + + for (i = 0; i < size; i++) + { + if (pic14_sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + emitpcode (POC_COMF, popGet (AOP (IC_LEFT (ic)), i)); + else + { + emitpcode (POC_COMFW, popGet (AOP (IC_LEFT (ic)), i)); + emitpcode (POC_MOVWF, popGet (AOP (IC_RESULT (ic)), i)); + } + } + + emitpcode (POC_INCF, popGet (AOP (IC_RESULT (ic)), 0)); + for (i = 1; i < size; i++) + { + emitSKPNZ; + emitpcode (POC_INCF, popGet (AOP (IC_RESULT (ic)), i)); + } + +release: + /* release the aops */ + freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* saverbank - saves an entire register bank on the stack */ +/*-----------------------------------------------------------------*/ +static void +saverbank (int bank, iCode * ic, bool pushPsw) +{ + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); +#if 0 + int i; + asmop *aop; + regs *r = NULL; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (options.useXstack) + { + + aop = newAsmop (0); + r = getFreePtr (ic, &aop, FALSE); + pic14_emitcode ("mov", "%s,_spx", r->name); + + } + + for (i = 0; i < pic14_nRegs; i++) + { + if (options.useXstack) + { + pic14_emitcode ("inc", "%s", r->name); + //pic14_emitcode("mov","a,(%s+%d)", + // regspic14[i].base,8*bank+regspic14[i].offset); + pic14_emitcode ("movx", "@%s,a", r->name); + } + else + pic14_emitcode ("push", ""); // "(%s+%d)", + //regspic14[i].base,8*bank+regspic14[i].offset); + } + + if (pushPsw) + { + if (options.useXstack) + { + pic14_emitcode ("mov", "a,psw"); + pic14_emitcode ("movx", "@%s,a", r->name); + pic14_emitcode ("inc", "%s", r->name); + pic14_emitcode ("mov", "_spx,%s", r->name); + freeAsmop (NULL, aop, ic, TRUE); + + } + else + pic14_emitcode ("push", "psw"); + + pic14_emitcode ("mov", "psw,#0x%02x", (bank << 3) & 0x00ff); + } + ic->bankSaved = 1; +#endif +} + +/*-----------------------------------------------------------------*/ +/* saveRegisters - will look for a call and save the registers */ +/*-----------------------------------------------------------------*/ +static void +saveRegisters (iCode * lic) +{ + iCode *ic; + sym_link *dtype; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* look for call */ + for (ic = lic; ic; ic = ic->next) + if (ic->op == CALL || ic->op == PCALL) + break; + + if (!ic) + { + fprintf (stderr, "found parameter push with no function call\n"); + return; + } + + /* if the registers have been saved already then do nothing */ + if (ic->regsSaved || IFFUNC_CALLEESAVES (OP_SYMBOL (IC_LEFT (ic))->type)) + return; + + /* find the registers in use at this time + and push them away to safety */ + bitVectCplAnd (bitVectCopy (ic->rMask), ic->rUsed); + + ic->regsSaved = 1; + + //fprintf(stderr, "ERROR: saveRegisters did not do anything to save registers, please report this as a bug.\n"); + + dtype = operandType (IC_LEFT (ic)); + if (currFunc && dtype && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && IFFUNC_ISISR (currFunc->type) && !ic->bankSaved) + { + saverbank (FUNC_REGBANK (dtype), ic, TRUE); + } +} + +/*-----------------------------------------------------------------*/ +/* unsaveRegisters - pop the pushed registers */ +/*-----------------------------------------------------------------*/ +static void +unsaveRegisters (iCode * ic) +{ + int i; + bitVect *rsave; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* find the registers in use at this time + and push them away to safety */ + rsave = bitVectCplAnd (bitVectCopy (ic->rMask), ic->rUsed); + + if (options.useXstack) + { + pic14_emitcode ("mov", "r0,%s", spname); + for (i = pic14_nRegs; i >= 0; i--) + { + if (bitVectBitValue (rsave, i)) + { + pic14_emitcode ("dec", "r0"); + pic14_emitcode ("movx", "a,@r0"); + pic14_emitcode ("mov", "%s,a", pic14_regWithIdx (i)->name); + } + + } + pic14_emitcode ("mov", "%s,r0", spname); + } //else + //for (i = pic14_nRegs ; i >= 0 ; i--) { + // if (bitVectBitValue(rsave,i)) + // pic14_emitcode("pop","%s",pic14_regWithIdx(i)->dname); + //} + +} + + +/*-----------------------------------------------------------------*/ +/* pushSide - */ +/*-----------------------------------------------------------------*/ +static void +pushSide (operand * oper, int size) +{ +#if 0 + int offset = 0; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + while (size--) + { + char *l = aopGet (AOP (oper), offset++, FALSE, TRUE); + if (AOP_TYPE (oper) != AOP_REG && AOP_TYPE (oper) != AOP_DIR && strcmp (l, "a")) + { + pic14_emitcode ("mov", "a,%s", l); + pic14_emitcode ("push", "acc"); + } + else + pic14_emitcode ("push", "%s", l); + } +#endif +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - */ +/*-----------------------------------------------------------------*/ +static void +assignResultValue (operand * oper) +{ + int size = AOP_SIZE (oper); + int offset = 0; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + DEBUGpic14_AopType (__LINE__, oper, NULL, NULL); + + /* assign MSB first (passed via WREG) */ + while (size--) + { + get_returnvalue (oper, size, offset + GpseudoStkPtr); + GpseudoStkPtr++; + } +} + + +/*-----------------------------------------------------------------*/ +/* genIpush - genrate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); +#if 0 + int size, offset = 0; + char *l; + + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + l = aopGet (AOP (IC_LEFT (ic)), offset++, FALSE, TRUE); + if (*l == '#') + { + MOVA (l); + l = "acc"; + } + pic14_emitcode ("push", "%s", l); + } + return; + } + + /* this is a paramter push: in this case we call + the routine to find the call and save those + registers that need to be saved */ + saveRegisters (ic); + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE); + + + // pushSide(IC_LEFT(ic), AOP_SIZE(IC_LEFT(ic))); + size = AOP_SIZE (IC_LEFT (ic)); + + while (size--) + { + l = aopGet (AOP (IC_LEFT (ic)), offset++, FALSE, TRUE); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG && AOP_TYPE (IC_LEFT (ic)) != AOP_DIR && strcmp (l, "a")) + { + pic14_emitcode ("mov", "a,%s", l); + pic14_emitcode ("push", "acc"); + } + else + pic14_emitcode ("push", "%s", l); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +#endif +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); + assert (!"genIpop -- unimplemented"); +#if 0 + int size, offset; + + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = (size - 1); + while (size--) + pic14_emitcode ("pop", "%s", aopGet (AOP (IC_LEFT (ic)), offset--, FALSE, TRUE)); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +#endif +} + +/*-----------------------------------------------------------------*/ +/* unsaverbank - restores the resgister bank from stack */ +/*-----------------------------------------------------------------*/ +static void +unsaverbank (int bank, iCode * ic, bool popPsw) +{ + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); +#if 0 + int i; + asmop *aop; + regs *r = NULL; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (popPsw) + { + if (options.useXstack) + { + aop = newAsmop (0); + r = getFreePtr (ic, &aop, FALSE); + + + pic14_emitcode ("mov", "%s,_spx", r->name); + pic14_emitcode ("movx", "a,@%s", r->name); + pic14_emitcode ("mov", "psw,a"); + pic14_emitcode ("dec", "%s", r->name); + + } + else + pic14_emitcode ("pop", "psw"); + } + + for (i = (pic14_nRegs - 1); i >= 0; i--) + { + if (options.useXstack) + { + pic14_emitcode ("movx", "a,@%s", r->name); + //pic14_emitcode("mov","(%s+%d),a", + // regspic14[i].base,8*bank+regspic14[i].offset); + pic14_emitcode ("dec", "%s", r->name); + + } + else + pic14_emitcode ("pop", ""); //"(%s+%d)", + //regspic14[i].base,8*bank); //+regspic14[i].offset); + } + + if (options.useXstack) + { + + pic14_emitcode ("mov", "_spx,%s", r->name); + freeAsmop (NULL, aop, ic, TRUE); + + } +#endif +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *dtype; + symbol *sym; + char *name; + int isExtern; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a function that is not using + the same register bank then we need to save the + destination registers on the stack */ + dtype = operandType (IC_LEFT (ic)); + if (currFunc && dtype && + (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype)) && IFFUNC_ISISR (currFunc->type) && !ic->bankSaved) + + saverbank (FUNC_REGBANK (dtype), ic, TRUE); + + /* if send set is not empty the assign */ + if (_G.sendSet) + { + iCode *sic; + /* For the Pic port, there is no data stack. + * So parameters passed to functions are stored + * in registers. (The pCode optimizer will get + * rid of most of these :). + */ + int pseudoStkPtr = -1; + int firstTimeThruLoop = 1; + + _G.sendSet = reverseSet (_G.sendSet); + + /* First figure how many parameters are getting passed */ + for (sic = setFirstItem (_G.sendSet); sic; sic = setNextItem (_G.sendSet)) + { + + aopOp (IC_LEFT (sic), sic, FALSE); + pseudoStkPtr += AOP_SIZE (IC_LEFT (sic)); + freeAsmop (IC_LEFT (sic), NULL, sic, FALSE); + } + + for (sic = setFirstItem (_G.sendSet); sic; sic = setNextItem (_G.sendSet)) + { + int size, offset = 0; + + aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + + while (size--) + { + DEBUGpic14_emitcode ("; ", "%d left %s", __LINE__, AopType (AOP_TYPE (IC_LEFT (sic)))); + + if (!firstTimeThruLoop) + { + /* If this is not the first time we've been through the loop + * then we need to save the parameter in a temporary + * register. The last byte of the last parameter is + * passed in W. */ + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - --pseudoStkPtr)); + + } + firstTimeThruLoop = 0; + + mov2w_op (IC_LEFT (sic), offset); + offset++; + } + freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + _G.sendSet = NULL; + } + /* make the call */ + sym = OP_SYMBOL (IC_LEFT (ic)); + name = sym->rname[0] ? sym->rname : sym->name; + /* + * As SDCC emits code as soon as it reaches the end of each + * function's definition, prototyped functions that are implemented + * after the current one are always considered EXTERN, which + * introduces many unneccessary PAGESEL instructions. + * XXX: Use a post pass to iterate over all `CALL _name' statements + * and insert `PAGESEL _name' and `PAGESEL $' around the CALL + * only iff there is no definition of the function in the whole + * file (might include this in the PAGESEL pass). + */ + isExtern = IS_EXTERN (sym->etype) || pic14_inISR; +#if 0 + if (isExtern) + { + /* Extern functions and ISRs maybe on a different page; + * must call pagesel */ +#endif + emitpcode (POC_PAGESEL, popGetWithString (name, 1)); +#if 0 + } +#endif + + emitpcode (POC_CALL, popGetWithString (name, isExtern)); +#if 0 + if (isExtern) + { + /* May have returned from a different page; + * must use pagesel to restore PCLATH before next + * goto or call instruction */ +#endif + emitpcode (POC_PAGESEL, popGetWithString ("$", 0)); +#if 0 + } +#endif + GpseudoStkPtr = 0; + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + assignResultValue (IC_RESULT (ic)); + + DEBUGpic14_emitcode ("; ", "%d left %s", __LINE__, AopType (AOP_TYPE (IC_RESULT (ic)))); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* if register bank was saved then pop them */ + if (ic->bankSaved) + unsaverbank (FUNC_REGBANK (dtype), ic, TRUE); + + /* if we hade saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *dtype; + symbol *albl = newiTempLabel (NULL); + symbol *blbl = newiTempLabel (NULL); + PIC_OPCODE poc; + pCodeOp *pcop; + operand *left; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if caller saves & we have not saved then */ + if (!ic->regsSaved) + saveRegisters (ic); + + /* if we are calling a function that is not using + the same register bank then we need to save the + destination registers on the stack */ + dtype = operandType (IC_LEFT (ic)); + if (currFunc && dtype && IFFUNC_ISISR (currFunc->type) && (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype))) + saverbank (FUNC_REGBANK (dtype), ic, TRUE); + + left = IC_LEFT (ic); + aopOp (left, ic, FALSE); + DEBUGpic14_AopType (__LINE__, left, NULL, NULL); + + poc = (op_isLitLike (IC_LEFT (ic)) ? POC_MOVLW : POC_MOVFW); + + pushSide (IC_LEFT (ic), FARPTRSIZE); + + /* if send set is not empty, assign parameters */ + if (_G.sendSet) + { + DEBUGpic14_emitcode ("; ***", "%s %d - WARNING arg-passing to indirect call not supported", __FUNCTION__, __LINE__); + /* no way to pass args - W always gets used to make the call */ + } + /* first idea - factor out a common helper function and call it. + But don't know how to get it generated only once in its own block + + if(AOP_TYPE(IC_LEFT(ic)) == AOP_DIR) { + char *rname; + char *buffer; + size_t len; + + rname = IC_LEFT(ic)->aop->aopu.aop_dir; + DEBUGpic14_emitcode ("; ***","%s %d AOP_DIR %s",__FUNCTION__,__LINE__,rname); + len = strlen(rname) + 16; + buffer = Safe_alloc(len); + SNPRINTF(buffer, len, "%s_goto_helper", rname); + addpCode2pBlock(pb,newpCode(POC_CALL,newpCodeOp(buffer,PO_STR))); + free(buffer); + } + */ + emitpcode (POC_CALL, popGetLabel (albl->key)); + pcop = popGetLabel (blbl->key); + emitpcode (POC_PAGESEL, pcop); /* Must restore PCLATH before goto, without destroying W */ + emitpcode (POC_GOTO, pcop); + emitpLabel (albl->key); + + emitpcode (poc, popGetAddr (AOP (left), 1, 0)); + emitpcode (POC_MOVWF, popCopyReg (&pc_pclath)); + emitpcode (poc, popGetAddr (AOP (left), 0, 0)); + emitpcode (POC_MOVWF, popCopyReg (&pc_pcl)); + + emitpLabel (blbl->key); + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + /* if we need to assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + GpseudoStkPtr = 0; + + assignResultValue (IC_RESULT (ic)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + /* if register bank was saved then unsave them */ + if (currFunc && dtype && (FUNC_REGBANK (currFunc->type) != FUNC_REGBANK (dtype))) + unsaverbank (FUNC_REGBANK (dtype), ic, TRUE); + + /* if we hade saved some registers then + unsave them */ + if (ic->regsSaved) + unsaveRegisters (ic); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + // DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + FENTRY; + + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym; + sym_link *ftype; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d curr label offset=%dprevious max_key=%d ", __FUNCTION__, __LINE__, labelOffset, + max_key); + + labelOffset += (max_key + 4); + max_key = 0; + GpseudoStkPtr = 0; + _G.nRegsSaved = 0; + /* create the function header */ + pic14_emitcode (";", "-----------------------------------------"); + pic14_emitcode (";", " function %s", (sym = OP_SYMBOL (IC_LEFT (ic)))->name); + pic14_emitcode (";", "-----------------------------------------"); + + /* prevent this symbol from being emitted as 'extern' */ + pic14_stringInSet (sym->rname, &pic14_localFunctions, 1); + + pic14_emitcode ("", "%s:", sym->rname); + addpCode2pBlock (pb, newpCodeFunction (moduleName, sym->rname, !IS_STATIC (sym->etype), IFFUNC_ISISR (sym->type))); + + /* mark symbol as NOT extern (even if it was declared so previously) */ + assert (IS_SPEC (sym->etype)); + SPEC_EXTR (sym->etype) = 0; + sym->cdef = 0; + if (!SPEC_OCLS (sym->etype)) + SPEC_OCLS (sym->etype) = code; + addSetIfnotP (&SPEC_OCLS (sym->etype)->syms, sym); + + ftype = operandType (IC_LEFT (ic)); + + /* here we need to generate the equates for the + register bank if required */ +#if 0 + if (FUNC_REGBANK (ftype) != rbank) + { + int i; + + rbank = FUNC_REGBANK (ftype); + for (i = 0; i < pic14_nRegs; i++) + { + if (strcmp (regspic14[i].base, "0") == 0) + pic14_emitcode ("", "%s = 0x%02x", regspic14[i].dname, 8 * rbank + regspic14[i].offset); + else + pic14_emitcode ("", "%s = %s + 0x%02x", regspic14[i].dname, regspic14[i].base, 8 * rbank + regspic14[i].offset); + } + } +#endif + + /* if this is an interrupt service routine */ + pic14_inISR = 0; + if (IFFUNC_ISISR (sym->type)) + { + pic14_inISR = 1; + + /* generate ISR prolog if and not naked */ + if (!IFFUNC_ISNAKED (sym->type)) + { + if (pic->isEnhancedCore) + { + /* + * Enhanced CPUs have automatic context saving for W, + * STATUS, BSR, FSRx, and PCLATH in shadow registers. + */ + emitpcode (POC_CLRF, popCopyReg (&pc_pclath)); + } + else + { + emitpcode (POC_MOVWF, popCopyReg (&pc_wsave)); + emitpcode (POC_SWAPFW, popCopyReg (&pc_status)); + /* XXX: Why? Does this assume that ssave and psave reside + * in a shared bank or bank0? We cannot guarantee the + * latter... + */ + emitpcode (POC_CLRF, popCopyReg (&pc_status)); + emitpcode (POC_MOVWF, popCopyReg (&pc_ssave)); + //emitpcode(POC_MOVWF, popGetExternal("___sdcc_saved_status",1 )); + emitpcode (POC_MOVFW, popCopyReg (&pc_pclath)); + /* during an interrupt PCLATH must be cleared before a goto or call statement */ + emitpcode (POC_CLRF, popCopyReg (&pc_pclath)); + emitpcode (POC_MOVWF, popCopyReg (&pc_psave)); + //emitpcode(POC_MOVWF, popGetExternal("___sdcc_saved_pclath", 1)); + emitpcode (POC_MOVFW, popCopyReg (&pc_fsr)); + emitpcode (POC_MOVWF, popGetExternal ("___sdcc_saved_fsr", 1)); + } + } + + pBlockConvert2ISR (pb); + pic14_hasInterrupt = 1; + } + else + { + /* if callee-save to be used for this function + then save the registers being used in this function */ + if (IFFUNC_CALLEESAVES (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + //pic14_emitcode("push","%s",pic14_regWithIdx(i)->dname); + _G.nRegsSaved++; + } + } + } + } + } + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + genCritical (NULL); + if (IFFUNC_ARGS (sym->type)) + { + fprintf (stderr, "PIC14: Functions with __critical (%s) must not have arguments for now.\n", sym->name); + exit (1); + } // if + } // if + + /* set the register bank to the desired value */ + if (FUNC_REGBANK (sym->type) || FUNC_ISISR (sym->type)) + { + pic14_emitcode ("push", "psw"); + pic14_emitcode ("mov", "psw,#0x%02x", (FUNC_REGBANK (sym->type) << 3) & 0x00ff); + } + + if (IFFUNC_ISREENT (sym->type) || options.stackAuto) + { + + if (options.useXstack) + { + pic14_emitcode ("mov", "r0,%s", spname); + pic14_emitcode ("mov", "a,_bp"); + pic14_emitcode ("movx", "@r0,a"); + pic14_emitcode ("inc", "%s", spname); + } + else + { + /* set up the stack */ + pic14_emitcode ("push", "_bp"); /* save the callers stack */ + } + pic14_emitcode ("mov", "_bp,%s", spname); + } + + /* adjust the stack for the function */ + if (sym->stack) + { + + int i = sym->stack; + if (i > 256) + werror (W_STACK_OVERFLOW, sym->name); + + if (i > 3 && sym->recvSize < 4) + { + + pic14_emitcode ("mov", "a,sp"); + pic14_emitcode ("add", "a,#0x%02x", ((char) sym->stack & 0xff)); + pic14_emitcode ("mov", "sp,a"); + + } + else + while (i--) + pic14_emitcode ("inc", "sp"); + } + + if (sym->xstack) + { + + pic14_emitcode ("mov", "a,_spx"); + pic14_emitcode ("add", "a,#0x%02x", ((char) sym->xstack & 0xff)); + pic14_emitcode ("mov", "_spx,a"); + } + +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (IFFUNC_ISREENT (sym->type) || options.stackAuto) + { + pic14_emitcode ("mov", "%s,_bp", spname); + } + + /* if use external stack but some variables were + added to the local stack then decrement the + local stack */ + if (options.useXstack && sym->stack) + { + pic14_emitcode ("mov", "a,sp"); + pic14_emitcode ("add", "a,#0x%02x", ((char) - sym->stack) & 0xff); + pic14_emitcode ("mov", "sp,a"); + } + + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto)) + { + if (options.useXstack) + { + pic14_emitcode ("mov", "r0,%s", spname); + pic14_emitcode ("movx", "a,@r0"); + pic14_emitcode ("mov", "_bp,a"); + pic14_emitcode ("dec", "%s", spname); + } + else + { + pic14_emitcode ("pop", "_bp"); + } + } + + /* restore the register bank */ + if (FUNC_REGBANK (sym->type) || FUNC_ISISR (sym->type)) + pic14_emitcode ("pop", "psw"); + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (sym->type)) + { + genEndCritical (NULL); + } // if + + if (IFFUNC_ISISR (sym->type)) + { + + /* now we need to restore the registers */ + /* if this isr has no bank i.e. is going to + run with bank 0 , then we need to save more + registers :-) */ + if (!FUNC_REGBANK (sym->type)) + { + + /* if this function does not call any other + function then we can be economical and + save only those registers that are used */ + if (!IFFUNC_HASFCALL (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + pic14_emitcode ("pop", "junk"); //"%s",pic14_regWithIdx(i)->dname); + } + } + } + + } + else + { + /* this function has a function call; cannot + determines register usage so we will have the + entire bank */ + unsaverbank (0, ic, FALSE); + } + } + + /* if debug then send end of function */ + if (options.debug && debugFile && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + /* generate ISR epilog if not enhanced core and not naked */ + if (!pic->isEnhancedCore && !IFFUNC_ISNAKED (sym->type)) + { + emitpcode (POC_MOVFW, popGetExternal ("___sdcc_saved_fsr", 1)); + emitpcode (POC_MOVWF, popCopyReg (&pc_fsr)); + //emitpcode(POC_MOVFW, popGetExternal("___sdcc_saved_pclath", 1)); + emitpcode (POC_MOVFW, popCopyReg (&pc_psave)); + emitpcode (POC_MOVWF, popCopyReg (&pc_pclath)); + emitpcode (POC_CLRF, popCopyReg (&pc_status)); // see genFunction + //emitpcode(POC_SWAPFW, popGetExternal("___sdcc_saved_status", 1)); + emitpcode (POC_SWAPFW, popCopyReg (&pc_ssave)); + emitpcode (POC_MOVWF, popCopyReg (&pc_status)); + emitpcode (POC_SWAPF, popCopyReg (&pc_wsave)); + emitpcode (POC_SWAPFW, popCopyReg (&pc_wsave)); + } + addpCode2pBlock (pb, newpCodeLabel ("END_OF_INTERRUPT", -1)); + emitpcodeNULLop (POC_RETFIE); + } + else + { + if (IFFUNC_ISCRITICAL (sym->type)) + pic14_emitcode ("setb", "ea"); + + if (IFFUNC_CALLEESAVES (sym->type)) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + /* save the registers used */ + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + pic14_emitcode ("pop", "junk"); //"%s",pic14_regWithIdx(i)->dname); + } + } + } + } + + /* if debug then send end of function */ + if (options.debug && debugFile && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + pic14_emitcode ("return", ""); + emitpcodeNULLop (POC_RETURN); + + /* Mark the end of a function */ + addpCode2pBlock (pb, newpCodeFunction (moduleName, NULL, 0, 0)); + } + +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size, offset = 0; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + for (offset = 0; offset < size; offset++) + { + pass_argument (IC_LEFT (ic), offset, size - 1 - offset); + } + + freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + + emitpcode (POC_GOTO, popGetLabel (returnLabel->key)); + } + +} + +static set *critical_temps = NULL; + +static void +genCritical (iCode * ic) +{ + pCodeOp *saved_intcon; + + (void) ic; + + if (!critical_temps) + critical_temps = newSet (); + + saved_intcon = popGetTempReg (); + addSetHead (&critical_temps, saved_intcon); + + /* This order saves one BANKSEL back to INTCON. */ + emitpcode (POC_MOVFW, popCopyReg (&pc_intcon)); + emitpcode (POC_BCF, popCopyGPR2Bit (popCopyReg (&pc_intcon), 7)); + emitpcode (POC_MOVWF, pCodeOpCopy (saved_intcon)); +} + +static void +genEndCritical (iCode * ic) +{ + pCodeOp *saved_intcon = NULL; + + (void) ic; + + saved_intcon = getSet (&critical_temps); + if (!saved_intcon) + { + fprintf (stderr, "Critical section left, but none entered -- ignoring for now.\n"); + return; + } // if + + emitpcode (POC_BTFSC, popCopyGPR2Bit (pCodeOpCopy (saved_intcon), 7)); + emitpcode (POC_BSF, popCopyGPR2Bit (popCopyReg (&pc_intcon), 7)); + popReleaseTempReg (saved_intcon); +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + FENTRY; + + /* special case never generate */ + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (IC_LABEL (ic) == entryLabel) + return; + + emitpLabel (IC_LABEL (ic)->key); + pic14_emitcode ("", "_%05d_DS_:", labelKey2num (IC_LABEL (ic)->key + labelOffset)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a goto */ +/*-----------------------------------------------------------------*/ +//tsd +static void +genGoto (iCode * ic) +{ + FENTRY; + + emitpcode (POC_GOTO, popGetLabel (IC_LABEL (ic)->key)); + pic14_emitcode ("goto", "_%05d_DS_", labelKey2num (IC_LABEL (ic)->key + labelOffset)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!pic14_sameRegs (AOP (result), AOP (right))) + emitpcode (POC_BSF, popGet (AOP (result), 0)); + + emitpcode (POC_BTFSC, popGet (AOP (right), 0)); + emitpcode (POC_BTFSS, popGet (AOP (left), 0)); + emitpcode (POC_BCF, popGet (AOP (result), 0)); + +} + + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8 bit multiplication & division */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + char *func[] = { NULL, "__mulchar", "__mulint", NULL, "__mullong" }; + + // symbol *lbl ; + int size, offset, i; + + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + DEBUGpic14_AopType (__LINE__, left, right, result); + DEBUGpic14_AopTypeSign (__LINE__, left, right, result); + + /* (if two literals, the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + assert (AOP_SIZE (left) == AOP_SIZE (right)); + + size = min (AOP_SIZE (result), AOP_SIZE (left)); + offset = Gstack_base_addr - (2 * size - 1); + + /* pass right operand as argument */ + for (i = 0; i < size; i++) + { + mov2w (AOP (right), i); + emitpcode (POC_MOVWF, popRegFromIdx (++offset)); + } // for + + /* pass left operand as argument */ + for (i = 0; i < size; i++) + { + mov2w (AOP (left), i); + if (i != size - 1) + emitpcode (POC_MOVWF, popRegFromIdx (++offset)); + } // for + assert (offset == Gstack_base_addr); + + /* call library routine */ + assert (size > 0 && size <= 4); + call_libraryfunc (func[size]); + + /* assign result */ + movwf (AOP (result), size - 1); + for (i = 0; i < size - 1; i++) + { + emitpcode (POC_MOVFW, popRegFromIdx (Gstack_base_addr - i)); + movwf (AOP (result), size - 2 - i); + } // for + + /* now (zero-/sign) extend the result to its size */ + addSign (result, AOP_SIZE (left), !SPEC_USIGN (operandType (result))); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genMultOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result) +{ + + char *l; + + FENTRY; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* the result must be bit */ + pic14_emitcode ("mov", "b,%s", aopGet (AOP (right), 0, FALSE, FALSE)); + l = aopGet (AOP (left), 0, FALSE, FALSE); + + MOVA (l); + + pic14_emitcode ("div", "ab"); + pic14_emitcode ("rrc", "a"); + aopPut (AOP (result), "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + int sign; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + assert (AOP_SIZE (right) == 1); + assert (AOP_SIZE (left) == 1); + + sign = !(SPEC_USIGN (operandType (left)) && SPEC_USIGN (operandType (right))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* XXX: might add specialized code */ + } + + if (!sign) + { + /* unsigned division */ +#if 1 + mov2w (AOP (right), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (left), 0); + call_libraryfunc ("__divuchar"); + movwf (AOP (result), 0); +#else + pCodeOp *temp; + symbol *lbl; + + temp = popGetTempReg (); + lbl = newiTempLabel (NULL); + + /* XXX: improve this naive approach: + [result] = [a] / [b] + ::= [result] = 0; while ([a] > [b]) do [a] -= [b]; [result]++ done + + In PIC assembler: + movf left,W + movwf temp // temp <-- left + movf right,W // W <-- right + clrf result + label1: + incf result + subwf temp,F // temp <-- temp - W + skipNC // subwf clears CARRY (i.e. sets BORROW) if temp < W + goto label1 + decf result // we just subtract once too often + */ + + /* XXX: This loops endlessly on DIVIDE BY ZERO */ + /* We need 1..128 iterations of the loop body (`4 / 5' .. `255 / 2'). */ + + mov2w (AOP (left), 0); + emitpcode (POC_MOVWF, temp); + mov2w (AOP (right), 0); + emitpcode (POC_CLRF, popGet (AOP (result), 0)); + + emitpLabel (lbl->key); + emitpcode (POC_INCF, popGet (AOP (result), 0)); + emitpcode (POC_SUBWF, temp); + emitSKPNC; + + if (pic->isEnhancedCore) + { + emitpcode (POC_BRA, popGetLabel (lbl->key)); + } + else + { + emitpcode (POC_GOTO, popGetLabel (lbl->key)); + } + + emitpcode (POC_DECF, popGet (AOP (result), 0)); + popReleaseTempReg (temp); +#endif + } + else + { + /* signed division */ + mov2w (AOP (right), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (left), 0); + call_libraryfunc ("__divschar"); + movwf (AOP (result), 0); + } + + /* now performed the signed/unsigned division -- extend result */ + addSign (result, 1, sign); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + int sign; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + assert (AOP_SIZE (right) == 1); + assert (AOP_SIZE (left) == 1); + + sign = !(SPEC_USIGN (operandType (left)) && SPEC_USIGN (operandType (right))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* XXX: might add specialized code */ + } + + if (!sign) + { + /* unsigned division */ +#if 1 + mov2w (AOP (right), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (left), 0); + call_libraryfunc ("__moduchar"); + movwf (AOP (result), 0); +#else + pCodeOp *temp; + symbol *lbl; + + lbl = newiTempLabel (NULL); + + assert (!pic14_sameRegs (AOP (right), AOP (result))); + + /* XXX: improve this naive approach: + [result] = [a] % [b] + ::= [result] = [a]; while ([result] > [b]) do [result] -= [b]; done + + In PIC assembler: + movf left,W + movwf result // result <-- left + movf right,W // W <-- right + label1: + subwf result,F // result <-- result - W + skipNC // subwf clears CARRY (i.e. sets BORROW) if result < W + goto label1 + addwf result, F // we just subtract once too often + */ + + /* XXX: This loops endlessly on DIVIDE BY ZERO */ + /* We need 1..128 iterations of the loop body (`4 % 5' .. `255 % 2'). */ + + if (!pic14_sameRegs (AOP (left), AOP (result))) + { + mov2w (AOP (left), 0); + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + } + mov2w (AOP (right), 0); + + emitpLabel (lbl->key); + emitpcode (POC_SUBWF, popGet (AOP (result), 0)); + emitSKPNC; + + if (pic->isEnhancedCore) + { + emitpcode (POC_BRA, popGetLabel (lbl->key)); + } + else + { + emitpcode (POC_GOTO, popGetLabel (lbl->key)); + } + + emitpcode (POC_ADDWF, popGet (AOP (result), 0)); +#endif + } + else + { + /* signed division */ + mov2w (AOP (right), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (left), 0); + call_libraryfunc ("__modschar"); + movwf (AOP (result), 0); + } + + /* now we performed the signed/unsigned modulus -- extend result */ + addSign (result, 1, sign); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +/* +note: May need to add parameter to indicate when a variable is in bit space. +*/ +static void +genIfxJump (iCode * ic, char *jval) +{ + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + + if (strcmp (jval, "a") == 0) + emitSKPZ; + else if (strcmp (jval, "c") == 0) + emitSKPC; + else + { + DEBUGpic14_emitcode ("; ***", "%d - assuming %s is in bit space", __LINE__, jval); + emitpcode (POC_BTFSC, newpCodeOpBit (jval, -1, 1)); + } + + emitpcode (POC_GOTO, popGetLabel (IC_TRUE (ic)->key)); + pic14_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_TRUE (ic)->key + labelOffset)); + + } + else + { + /* false label is present */ + if (strcmp (jval, "a") == 0) + emitSKPNZ; + else if (strcmp (jval, "c") == 0) + emitSKPNC; + else + { + DEBUGpic14_emitcode ("; ***", "%d - assuming %s is in bit space", __LINE__, jval); + emitpcode (POC_BTFSS, newpCodeOpBit (jval, -1, 1)); + } + + emitpcode (POC_GOTO, popGetLabel (IC_FALSE (ic)->key)); + pic14_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_FALSE (ic)->key + labelOffset)); + + } + + + /* mark the icode as generated */ + ic->generated = TRUE; +} + +/*-----------------------------------------------------------------*/ +/* genSkipc */ +/*-----------------------------------------------------------------*/ +static void +genSkipc (resolvedIfx * rifx) +{ + FENTRY; + if (!rifx) + return; + + if (rifx->condition) + emitSKPNC; + else + emitSKPC; + + emitpcode (POC_GOTO, popGetLabel (rifx->lbl->key)); + emitpComment ("%s:%u: created from rifx:%p", __FUNCTION__, __LINE__, rifx); + rifx->generated = TRUE; +} + +#define isAOP_REGlike(x) (AOP_TYPE(x) == AOP_REG || AOP_TYPE(x) == AOP_DIR || AOP_TYPE(x) == AOP_PCODE) +#define isAOP_LIT(x) (AOP_TYPE(x) == AOP_LIT) +#define DEBUGpc emitpComment + +/*-----------------------------------------------------------------*/ +/* mov2w_regOrLit :- move to WREG either the offset's byte from */ +/* aop (if it's NOT a literal) or from lit (if */ +/* aop is a literal) */ +/*-----------------------------------------------------------------*/ +static void +pic14_mov2w_regOrLit (asmop * aop, unsigned long lit, int offset) +{ + if (aop->type == AOP_LIT) + { + emitpcode (POC_MOVLW, popGetLit ((lit >> (offset * 8)) & 0x00FF)); + } + else + { + emitpcode (POC_MOVFW, popGet (aop, offset)); + } +} + +/* genCmp performs a left < right comparison, stores + * the outcome in result (if != NULL) and generates + * control flow code for the ifx (if != NULL). + * + * This version leaves in sequences like + * "B[CS]F STATUS,0; BTFS[CS] STATUS,0" + * which should be optmized by the peephole + * optimizer - RN 2005-01-01 */ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign) +{ + resolvedIfx rIfx; + int size; + int offs; + symbol *templbl; + operand *dummy; + unsigned long lit; + unsigned long mask; + int performedLt; + int invert_result = 0; + + FENTRY; + + assert (AOP_SIZE (left) == AOP_SIZE (right)); + assert (left && right); + + size = AOP_SIZE (right) - 1; + mask = (0x100UL << (size * 8)) - 1; + // in the end CARRY holds "left < right" (performedLt == 1) or "left >= right" (performedLt == 0) + performedLt = 1; + templbl = NULL; + lit = 0; + + resolveIfx (&rIfx, ifx); + + /********************************************************************** + * handle bits - bit compares are promoted to int compares seemingly! * + **********************************************************************/ +#if 0 + // THIS IS COMPLETELY UNTESTED! + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + pCodeOp *pcleft = pic16_popGet (AOP (left), 0); + pCodeOp *pcright = pic16_popGet (AOP (right), 0); + assert (pcleft->type == PO_GPR_BIT && pcright->type == PO_GPR_BIT); + + emitSETC; + // 1 < {0,1} is false --> clear C by skipping the next instruction + //pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (AOP (left),0), PCORB (pcleft)->bit); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (left), 0)); + // {0,1} < 0 is false --> clear C by NOT skipping the next instruction + pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (pic16_popGet (AOP (right), 0), PCORB (pcright)->bit)); + emitCLRC; // only skipped for left=0 && right=1 + + goto correct_result_in_carry; + } // if +#endif + + /************************************************* + * make sure that left is register (or the like) * + *************************************************/ + if (!isAOP_REGlike (left)) + { + DEBUGpc ("swapping arguments (AOP_TYPEs %d/%d)", AOP_TYPE (left), AOP_TYPE (right)); + assert (isAOP_LIT (left)); + assert (isAOP_REGlike (right)); + // swap left and right + // left < right <==> right > left <==> (right >= left + 1) + lit = ulFromVal (AOP (left)->aopu.aop_lit); + + if ((!sign && (lit & mask) == mask) || (sign && (lit & mask) == (mask >> 1))) + { + // MAXVALUE < right? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } // if + + // This fails for lit = 0xFF (unsigned) AND lit = 0x7F (signed), + // that's why we handled it above. + lit++; + + dummy = left; + left = right; + right = dummy; + + performedLt ^= 1; // instead of "left < right" we check for "right >= left+1, i.e. "right < left+1" + } + else if (isAOP_LIT (right)) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } // if + + assert (isAOP_REGlike (left)); // left must be register or the like + assert (isAOP_REGlike (right) || isAOP_LIT (right)); // right may be register-like or a literal + + /************************************************* + * special cases go here * + *************************************************/ + + if (isAOP_LIT (right)) + { + if (!sign) + { + // unsigned comparison to a literal + DEBUGpc ("unsigned compare: left %s lit(0x%X=%lu), size=%d", performedLt ? "<" : ">=", lit, lit, size + 1); + if (lit == 0) + { + // unsigned left < 0? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + } + else + { + // signed comparison to a literal + DEBUGpc ("signed compare: left %s lit(0x%X=%ld), size=%d, mask=%x", performedLt ? "<" : ">=", lit, lit, size + 1, + mask); + if ((lit & mask) == ((0x80 << (size * 8)) & mask)) + { + // signed left < 0x80000000? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + else if (lit == 0) + { + // compare left < 0; set CARRY if SIGNBIT(left) is set + if (performedLt) + emitSETC; + else + emitCLRC; + emitpcode (POC_BTFSS, newpCodeOpBit (aopGet (AOP (left), size, FALSE, FALSE), 7, 0)); + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + } // if (!sign) + } // right is literal + + /************************************************* + * perform a general case comparison * + * make sure we get CARRY==1 <==> left >= right * + *************************************************/ + // compare most significant bytes + //DEBUGpc ("comparing bytes at offset %d", size); + if (!sign) + { + // unsigned comparison + pic14_mov2w_regOrLit (AOP (right), lit, size); + emitpcode (POC_SUBFW, popGet (AOP (left), size)); + } + else + { + // signed comparison + // (add 2^n to both operands then perform an unsigned comparison) + if (isAOP_LIT (right)) + { + // left >= LIT <-> LIT-left <= 0 <-> LIT-left == 0 OR !(LIT-left >= 0) + unsigned char litbyte = (lit >> (8 * size)) & 0xFF; + + if (litbyte == 0x80) + { + // left >= 0x80 -- always true, but more bytes to come + mov2w (AOP (left), size); + emitpcode (POC_XORLW, popGetLit (0x80)); // set ZERO flag + emitSETC; + } + else + { + // left >= LIT <-> left + (-LIT) >= 0 <-> left + (0x100-LIT) >= 0x100 + mov2w (AOP (left), size); + emitpcode (POC_ADDLW, popGetLit (0x80)); + emitpcode (POC_ADDLW, popGetLit ((0x100 - (litbyte + 0x80)) & 0x00FF)); + } // if + } + else + { + pCodeOp *pctemp = popGetTempReg (); + mov2w (AOP (left), size); + emitpcode (POC_ADDLW, popGetLit (0x80)); + emitpcode (POC_MOVWF, pctemp); + mov2w (AOP (right), size); + emitpcode (POC_ADDLW, popGetLit (0x80)); + emitpcode (POC_SUBFW, pctemp); + popReleaseTempReg (pctemp); + } + } // if (!sign) + + // compare remaining bytes (treat as unsigned case from above) + templbl = newiTempLabel (NULL); + offs = size; + while (offs--) + { + //DEBUGpc ("comparing bytes at offset %d", offs); + emitSKPZ; + + if (pic->isEnhancedCore) + { + emitpcode (POC_BRA, popGetLabel (templbl->key)); + } + else + { + emitpcode (POC_GOTO, popGetLabel (templbl->key)); + } + + pic14_mov2w_regOrLit (AOP (right), lit, offs); + emitpcode (POC_SUBFW, popGet (AOP (left), offs)); + } // while (offs) + emitpLabel (templbl->key); + goto result_in_carry; + +result_in_carry: + + /**************************************************** + * now CARRY contains the result of the comparison: * + * SUBWF sets CARRY iff * + * F-W >= 0 <==> F >= W <==> !(F < W) * + * (F=left, W=right) * + ****************************************************/ + + if (performedLt) + { + invert_result = 1; + // value will be used in the following genSkipc () + rIfx.condition ^= TRUE; + } // if + +correct_result_in_carry: + + // assign result to variable (if neccessary), but keep CARRY intact to be used below + if (result && AOP_TYPE (result) != AOP_CRY) + { + //DEBUGpc ("assign result"); + size = AOP_SIZE (result); + while (size--) + { + emitpcode (POC_CLRF, popGet (AOP (result), size)); + } // while + if (invert_result) + { + emitSKPC; + emitpcode (POC_BSF, newpCodeOpBit (aopGet (AOP (result), 0, FALSE, FALSE), 0, 0)); + } + else + { + emitpcode (POC_RLF, popGet (AOP (result), 0)); + if (ifx) + { + /* Result is expected to be in CARRY by genSkipc () below. */ + emitpcode (POC_RRFW, popGet (AOP (result), 0)); + } // if + } // if + } // if (result) + + // perform conditional jump + if (ifx) + { + //DEBUGpc ("generate control flow"); + genSkipc (&rIfx); + ifx->generated = TRUE; + } // if +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + genCmp (right, left, result, ifx, sign); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the amsops */ + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + genCmp (left, right, result, ifx, sign); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size; + symbol *false_label; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (ifx) + DEBUGpic14_emitcode ("; ifx is non-null", ""); + else + DEBUGpic14_emitcode ("; ifx is null", ""); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + /* if literal, move literal to right */ + if (op_isLitLike (IC_LEFT (ic))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + false_label = NULL; + if (ifx && !IC_TRUE (ifx)) + { + assert (IC_FALSE (ifx)); + false_label = IC_FALSE (ifx); + } + + size = min (AOP_SIZE (left), AOP_SIZE (right)); + assert (!pic14_sameRegs (AOP (result), AOP (left))); + assert (!pic14_sameRegs (AOP (result), AOP (right))); + + /* assume left != right */ + { + int i; + for (i = 0; i < AOP_SIZE (result); i++) + { + emitpcode (POC_CLRF, popGet (AOP (result), i)); + } + } + + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + int i; + size = AOP_SIZE (left); + assert (!op_isLitLike (left)); + + switch (lit) + { + case 0: + mov2w (AOP (left), 0); + for (i = 1; i < size; i++) + emitpcode (POC_IORFW, popGet (AOP (left), i)); + /* now Z is set iff `left == right' */ + emitSKPZ; + if (!false_label) + false_label = newiTempLabel (NULL); + emitpcode (POC_GOTO, popGetLabel (false_label->key)); + break; + + default: + for (i = 0; i < size; i++) + { + mov2w (AOP (left), i); + emitpcode (POC_XORLW, popGetLit (lit >> (8 * i))); + /* now Z is cleared if `left != right' */ + emitSKPZ; + if (!false_label) + false_label = newiTempLabel (NULL); + emitpcode (POC_GOTO, popGetLabel (false_label->key)); + } // for i + break; + } // switch (lit) + } + else + { + /* right is no literal */ + int i; + + for (i = 0; i < size; i++) + { + mov2w (AOP (right), i); + emitpcode (POC_XORFW, popGet (AOP (left), i)); + /* now Z is cleared if `left != right' */ + emitSKPZ; + if (!false_label) + false_label = newiTempLabel (NULL); + emitpcode (POC_GOTO, popGetLabel (false_label->key)); + } // for i + } + + /* if we reach here, left == right */ + + if (AOP_SIZE (result) > 0) + { + emitpcode (POC_INCF, popGet (AOP (result), 0)); + } + + if (ifx && IC_TRUE (ifx)) + { + emitpcode (POC_GOTO, popGetLabel (IC_TRUE (ifx)->key)); + } + + if (false_label && (!ifx || IC_TRUE (ifx))) + emitpLabel (false_label->key); + + if (ifx) + ifx->generated = TRUE; + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + /* symbol *tlbl; */ + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + emitpcode (POC_MOVFW, popGet (AOP (left), 0)); + emitpcode (POC_ANDFW, popGet (AOP (right), 0)); + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + + /* if both are bit variables */ + /* if (AOP_TYPE(left) == AOP_CRY && */ + /* AOP_TYPE(right) == AOP_CRY ) { */ + /* pic14_emitcode("mov","c,%s",AOP(left)->aopu.aop_dir); */ + /* pic14_emitcode("anl","c,%s",AOP(right)->aopu.aop_dir); */ + /* pic14_outBitC(result); */ + /* } else { */ + /* tlbl = newiTempLabel(NULL); */ + /* pic14_toBoolean(left); */ + /* pic14_emitcode("jz","%05d_DS_",labelKey2num (tlbl->key)); */ + /* pic14_toBoolean(right); */ + /* pic14_emitcode("","%05d_DS_:",labelKey2num (tlbl->key)); */ + /* pic14_outBitAcc(result); */ + /* } */ + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +/* +tsd pic port - +modified this code, but it doesn't appear to ever get called +*/ + +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + int i; + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + for (i = 0; i < AOP_SIZE (result); i++) + { + emitpcode (POC_CLRF, popGet (AOP (result), i)); + } // for i + + tlbl = newiTempLabel (NULL); + pic14_toBoolean (left); + emitSKPZ; + emitpcode (POC_GOTO, popGetLabel (tlbl->key)); + pic14_toBoolean (right); + emitpLabel (tlbl->key); + /* here Z is clear IFF `left || right' */ + emitSKPZ; + emitpcode (POC_INCF, popGet (AOP (result), 0)); + + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = { 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (IC_TRUE (ic)) + { + emitpcode (POC_GOTO, popGetLabel (labelKey2num (IC_TRUE (ic)->key))); + pic14_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = TRUE; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (!IC_TRUE (ic)) + { + emitpcode (POC_GOTO, labelKey2num (popGetLabel (IC_TRUE (ic)->key))); + pic14_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ic)->key)); + } + ic->generated = TRUE; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl) +{ + FENTRY; + // ugly but optimized by peephole + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + pic14_emitcode ("sjmp", "%05d_DS_", labelKey2num (nlbl->key)); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic14_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_TRUE (ic)->key)); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (nlbl->key)); + } + else + { + pic14_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ic)->key)); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + ic->generated = TRUE; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + int bytelit = 0; + resolvedIfx rIfx; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + resolveIfx (&rIfx, ifx); + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic14_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + // c = bit & literal; + if (AOP_TYPE (right) == AOP_LIT) + { + if (lit & 1) + { + if (size && pic14_sameRegs (AOP (result), AOP (left))) + // no change + goto release; + pic14_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + pic14_emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx); + goto release; + } + pic14_emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + pic14_emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + pic14_emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // c = bit & val; + MOVA (aopGet (AOP (right), 0, FALSE, FALSE)); + // c = lsb + pic14_emitcode ("rrc", "a"); + pic14_emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + pic14_outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + int posbit = isLiteralBit (lit); + /* left & 2^n */ + if (posbit) + { + posbit--; + //MOVA(aopGet(AOP(left),posbit>>3,FALSE,FALSE)); + // bit = left & 2^n + if (size) + pic14_emitcode ("mov", "c,acc.%d", posbit & 0x07); + // if(left & 2^n) + else + { + if (ifx) + { + int offset = 0; + while (posbit > 7) + { + posbit -= 8; + offset++; + } + emitpcode (((rIfx.condition) ? POC_BTFSC : POC_BTFSS), + newpCodeOpBit (aopGet (AOP (left), offset, FALSE, FALSE), posbit, 0)); + emitpcode (POC_GOTO, popGetLabel (rIfx.lbl->key)); + + ifx->generated = TRUE; + } + goto release; + } + } + else + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + if (size) + pic14_emitcode ("setb", "c"); + while (sizel--) + { + if ((bytelit = ((lit >> (offset * 8)) & 0x0FFL)) != 0x0L) + { + // byte == 2^n ? + if ((posbit = isLiteralBit (bytelit)) != 0) + { + emitpcode (POC_BTFSC, + newpCodeOpBit (aopGet (AOP (left), offset, FALSE, FALSE), posbit - 1, 0)); + } + else + { + mov2w (AOP (left), offset); + emitpcode (POC_ANDLW, newpCodeOpLit (bytelit & 0x0ff)); + emitSKPZ; + } + emitpcode (POC_GOTO, popGetLabel (rIfx.condition ? + rIfx.lbl->key : tlbl->key)); + } + offset++; + } + if (!rIfx.condition) + { + emitpcode (POC_GOTO, popGetLabel (rIfx.lbl->key)); + } + emitpLabel(tlbl->key); + ifx->generated = TRUE; + // bit = left & literal + if (size) + { + pic14_emitcode ("clr", "c"); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl); + goto release; + } + } + pic14_outBitC (result); + goto release; + } + + /* if left is same as result */ + if (pic14_sameRegs (AOP (result), AOP (left))) + { + int know_W = -1; + for (; size--; offset++, lit >>= 8) + { + if (AOP_TYPE (right) == AOP_LIT) + { + switch (lit & 0xff) + { + case 0x00: + /* and'ing with 0 has clears the result */ + emitpcode (POC_CLRF, popGet (AOP (result), offset)); + break; + case 0xff: + /* and'ing with 0xff is a nop when the result and left are the same */ + break; + + default: + { + int p = my_powof2 ((~lit) & 0xff); + if (p >= 0) + { + /* only one bit is set in the literal, so use a bcf instruction */ + emitpcode (POC_BCF, newpCodeOpBit (aopGet (AOP (left), offset, FALSE, FALSE), p, 0)); + + } + else + { + if (know_W != (int) (lit & 0xff)) + emitpcode (POC_MOVLW, popGetLit (lit & 0xff)); + know_W = lit & 0xff; + emitpcode (POC_ANDWF, popGet (AOP (left), offset)); + } + } + } + } + else + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_ANDWF, popGet (AOP (left), offset)); + } + } + + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + pic14_emitcode ("setb", "c"); + while (sizer--) + { + MOVA (aopGet (AOP (right), offset, FALSE, FALSE)); + pic14_emitcode ("anl", "a,%s", aopGet (AOP (left), offset, FALSE, FALSE)); + pic14_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic14_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + emitpcode (POC_CLRF, popGet (AOP (result), offset)); + break; + case 0xff: + emitpcode (POC_MOVFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + break; + default: + emitpcode (POC_MOVLW, popGetLit (t)); + emitpcode (POC_ANDFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + continue; + } + + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_ANDFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + } + } + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic14_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + + DEBUGpic14_AopType (__LINE__, left, right, result); + + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + emitpcode (POC_BSF, popGet (AOP (result), 0)); + //pic14_emitcode("bsf","(%s >> 3), (%s & 7)", + // AOP(result)->aopu.aop_dir, + // AOP(result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx); + goto release; + } + } + else + { + // lit == 0 => result = left + if (size && pic14_sameRegs (AOP (result), AOP (left))) + goto release; + pic14_emitcode (";XXX mov", "c,%s %s,%d", AOP (left)->aopu.aop_dir, __FILE__, __LINE__); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + if (pic14_sameRegs (AOP (result), AOP (left))) + { + // c = bit | bit; + emitpcode (POC_BCF, popGet (AOP (result), 0)); + emitpcode (POC_BTFSC, popGet (AOP (right), 0)); + emitpcode (POC_BSF, popGet (AOP (result), 0)); + + pic14_emitcode ("bcf", "(%s >> 3), (%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + pic14_emitcode ("btfsc", "(%s >> 3), (%s & 7)", AOP (right)->aopu.aop_dir, AOP (right)->aopu.aop_dir); + pic14_emitcode ("bsf", "(%s >> 3), (%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + } + else + { + emitpcode (POC_BCF, popGet (AOP (result), 0)); + emitpcode (POC_BTFSS, popGet (AOP (right), 0)); + emitpcode (POC_BTFSC, popGet (AOP (left), 0)); + emitpcode (POC_BSF, popGet (AOP (result), 0)); + } + } + else + { + // c = bit | val; + symbol *tlbl = newiTempLabel (NULL); + pic14_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + + + emitpcode (POC_BCF, popGet (AOP (result), 0)); + + if (!((AOP_TYPE (result) == AOP_CRY) && ifx)) + pic14_emitcode (";XXX setb", "c"); + pic14_emitcode (";XXX jb", "%s,%05d_DS_", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + pic14_toBoolean (right); + pic14_emitcode (";XXX jnz", "%05d_DS_", labelKey2num (tlbl->key)); + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jmpTrueOrFalse (ifx, tlbl); + goto release; + } + else + { + CLRC; + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + } + } + // bit = c + // val = c + if (size) + pic14_outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + if (lit) + { + pic14_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + // result = 1 + if (size) + pic14_emitcode (";XXX setb", "%s", AOP (result)->aopu.aop_dir); + else + continueIfTrue (ifx); + goto release; + } + else + { + pic14_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + // lit = 0, result = boolean(left) + if (size) + pic14_emitcode (";XXX setb", "c"); + pic14_toBoolean (right); + if (size) + { + symbol *tlbl = newiTempLabel (NULL); + pic14_emitcode (";XXX jnz", "%05d_DS_", labelKey2num (tlbl->key)); + CLRC; + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + else + { + genIfxJump (ifx, "a"); + goto release; + } + } + pic14_outBitC (result); + goto release; + } + + /* if left is same as result */ + if (pic14_sameRegs (AOP (result), AOP (left))) + { + int know_W = -1; + for (; size--; offset++, lit >>= 8) + { + if (AOP_TYPE (right) == AOP_LIT) + { + if ((lit & 0xff) == 0) + /* or'ing with 0 has no effect */ + continue; + else + { + int p = my_powof2 (lit & 0xff); + if (p >= 0) + { + /* only one bit is set in the literal, so use a bsf instruction */ + emitpcode (POC_BSF, newpCodeOpBit (aopGet (AOP (left), offset, FALSE, FALSE), p, 0)); + } + else + { + if (know_W != (int) (lit & 0xff)) + emitpcode (POC_MOVLW, popGetLit (lit & 0xff)); + know_W = lit & 0xff; + emitpcode (POC_IORWF, popGet (AOP (left), offset)); + } + + } + } + else + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_IORWF, popGet (AOP (left), offset)); + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + pic14_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + + + if (size) + pic14_emitcode (";XXX setb", "c"); + while (sizer--) + { + MOVA (aopGet (AOP (right), offset, FALSE, FALSE)); + pic14_emitcode (";XXX orl", "a,%s", aopGet (AOP (left), offset, FALSE, FALSE)); + pic14_emitcode (";XXX jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic14_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + for (; (size--); offset++) + { + // normal case + // result = left | right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + emitpcode (POC_MOVFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + + break; + default: + emitpcode (POC_MOVLW, popGetLit (t)); + emitpcode (POC_IORFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + continue; + } + + // faster than result <- left, anl result,right + // and better if result is SFR + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_IORFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + } + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic14_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + { + emitpcode (POC_BSF, popGet (AOP (result), offset)); + pic14_emitcode ("setb", "%s", AOP (result)->aopu.aop_dir); + } + else if (ifx) + continueIfTrue (ifx); + goto release; + } + pic14_emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && pic14_sameRegs (AOP (result), AOP (left))) + goto release; + pic14_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && pic14_sameRegs (AOP (result), AOP (left))) + { + emitpcode (POC_MOVLW, popGet (AOP (result), offset)); + emitpcode (POC_XORWF, popGet (AOP (result), offset)); + pic14_emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + assert (!"incomplete genXor"); + pic14_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic14_emitcode ("cpl", "c"); + } + } + } + + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + pic14_emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + } + else + { + int sizer = AOP_SIZE (right); + // c = bit ^ val + // if val>>1 != 0, result = 1 + pic14_emitcode ("setb", "c"); + while (sizer) + { + MOVA (aopGet (AOP (right), sizer - 1, FALSE, FALSE)); + if (sizer == 1) + // test the msb of the lsb + pic14_emitcode ("anl", "a,#0xfe"); + pic14_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + sizer--; + } + // val = (0,1) + pic14_emitcode ("rrc", "a"); + } + pic14_emitcode ("jnb", "%s,%05d_DS_", AOP (left)->aopu.aop_dir, (labelKey2num (tlbl->key))); + pic14_emitcode ("cpl", "c"); + pic14_emitcode ("", "%05d_DS_:", (labelKey2num (tlbl->key))); + } + // bit = c + // val = c + if (size) + pic14_outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + if (pic14_sameRegs (AOP (result), AOP (left))) + { + /* if left is same as result */ + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + if (t == 0x00L) + continue; + else + { + emitpcode (POC_MOVLW, popGetLit (t)); + emitpcode (POC_XORWF, popGet (AOP (left), offset)); + } + } + else + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_XORWF, popGet (AOP (left), offset)); + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + pic14_emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (aopGet (AOP (left), offset, FALSE, FALSE)); + } + else + { + MOVA (aopGet (AOP (right), offset, FALSE, FALSE)); + pic14_emitcode ("xrl", "a,%s", aopGet (AOP (left), offset, FALSE, FALSE)); + } + pic14_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic14_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic14_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + emitpcode (POC_MOVFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + break; + case 0xff: + emitpcode (POC_COMFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + break; + default: + emitpcode (POC_MOVLW, popGetLit (t)); + emitpcode (POC_XORFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + continue; + } + + // faster than result <- left, anl result,right + // and better if result is SFR + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_XORFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + } + +release: + freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genInline - write the inline code out */ +/*-----------------------------------------------------------------*/ +static void +pic14_genInline (iCode * ic) +{ + char *buffer, *bp, *bp1; + bool inComment = FALSE; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + genLine.lineElement.isInline += (!options.asmpeep); + + buffer = bp = bp1 = Safe_strdup (IC_INLINE (ic)); + + while (*bp) + { + switch (*bp) + { + case ';': + inComment = TRUE; + ++bp; + break; + + case '\x87': + case '\n': + inComment = FALSE; + *bp++ = '\0'; + if (*bp1) + addpCode2pBlock (pb, newpCodeAsmDir (bp1, NULL)); // inline directly, no process + bp1 = bp; + break; + + default: + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) + { + ++bp; + *bp = '\0'; + ++bp; + /* print label, use this special format with NULL directive + * to denote that the argument should not be indented with tab */ + addpCode2pBlock (pb, newpCodeAsmDir (NULL, bp1)); // inline directly, no process + bp1 = bp; + } + else + ++bp; + break; + } + } + if ((bp1 != bp) && *bp1) + addpCode2pBlock (pb, newpCodeAsmDir (bp1, NULL)); // inline directly, no process + + Safe_free (buffer); + + /* consumed; we can free it here */ + dbuf_free (IC_INLINE (ic)); + + genLine.lineElement.isInline -= (!options.asmpeep); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, offset = 0, same; + + FENTRY; + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + DEBUGpic14_AopType (__LINE__, left, NULL, result); + + same = pic14_sameRegs (AOP (result), AOP (left)); + + size = AOP_SIZE (result); + + /* get the lsb and put it into the carry */ + emitpcode (POC_RRFW, popGet (AOP (left), size - 1)); + + offset = 0; + + while (size--) + { + if (same) + { + emitpcode (POC_RRF, popGet (AOP (left), offset)); + } + else + { + emitpcode (POC_RRFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + + offset++; + } + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset = 0; + int same; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + DEBUGpic14_AopType (__LINE__, left, NULL, result); + + same = pic14_sameRegs (AOP (result), AOP (left)); + + /* move it to the result */ + size = AOP_SIZE (result); + + /* get the msb and put it into the carry */ + emitpcode (POC_RLFW, popGet (AOP (left), size - 1)); + + offset = 0; + + while (size--) + { + if (same) + { + emitpcode (POC_RLF, popGet (AOP (left), offset)); + } + else + { + emitpcode (POC_RLFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + + offset++; + } + + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +static void +genGetABit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + int offset; + int i; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + aopOp (left, ic, FALSE); + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + offset = shCount / 8; + shCount %= 8; + + /* load and mask the source byte */ + mov2w (AOP (left), offset); + emitpcode (POC_ANDLW, popGetLit (1 << shCount)); + + /* move selected bit to bit 0 */ + switch (shCount) + { + case 0: + /* nothing more to do */ + break; + default: + /* keep W==0, force W=0x01 otherwise */ + emitSKPZ; + emitpcode (POC_MOVLW, popGetLit (1)); + break; + } // switch + + /* write result */ + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + + for (i = 1; i < AOP_SIZE (result); ++i) + { + emitpcode (POC_CLRF, popGet (AOP (result), i)); + } // for + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGetHbit - generates code get highest order bit */ +/*-----------------------------------------------------------------*/ +static void +genGetHbit (iCode * ic) +{ + operand *left, *result; + left = IC_LEFT (ic); + result = IC_RESULT (ic); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* get the highest order byte into a */ + MOVA (aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE, FALSE)); + if (AOP_TYPE (result) == AOP_CRY) + { + pic14_emitcode ("rlc", "a"); + pic14_outBitC (result); + } + else + { + pic14_emitcode ("rl", "a"); + pic14_emitcode ("anl", "a,#0x01"); + pic14_outAcc (result); + } + + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - shift left accumulator by known count */ +/* MARK: pic14 always rotates through CARRY! */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (pCodeOp * pcop, int shCount) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + shCount &= 0x0007; // shCount : 0..7 + switch (shCount) + { + case 0: + return; + break; + case 1: + emitCLRC; + emitpcode (POC_RLF, pcop); + return; + break; + case 2: + emitpcode (POC_RLF, pcop); + emitpcode (POC_RLF, pcop); + break; + case 3: + emitpcode (POC_RLF, pcop); + emitpcode (POC_RLF, pcop); + emitpcode (POC_RLF, pcop); + break; + case 4: + emitpcode (POC_SWAPF, pcop); + break; + case 5: + emitpcode (POC_SWAPF, pcop); + emitpcode (POC_RLF, pcop); + break; + case 6: + emitpcode (POC_SWAPF, pcop); + emitpcode (POC_RLF, pcop); + emitpcode (POC_RLF, pcop); + break; + case 7: + emitpcode (POC_RRFW, pcop); + emitpcode (POC_RRF, pcop); + break; + } + /* clear invalid bits */ + emitpcode (POC_MOVLW, popGetLit ((unsigned char) (~((1UL << shCount) - 1)))); + emitpcode (POC_ANDWF, pcop); +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - shift right accumulator by known count */ +/* MARK: pic14 always rotates through CARRY! */ +/* maskmode - 0: leave invalid bits undefined (caller should mask) */ +/* 1: mask out invalid bits (zero-extend) */ +/* 2: sign-extend result (pretty slow) */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (pCodeOp * pcop, int shCount, int mask_mode) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + shCount &= 0x0007; // shCount : 0..7 + switch (shCount) + { + case 0: + return; + break; + case 1: + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + else if (mask_mode == 1) + emitCLRC; + emitpcode (POC_RRF, pcop); + return; + break; + case 2: + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RRF, pcop); + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RRF, pcop); + if (mask_mode == 2) + return; + break; + case 3: + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RRF, pcop); + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RRF, pcop); + /* load sign if needed */ + if (mask_mode == 2) + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RRF, pcop); + if (mask_mode == 2) + return; + break; + case 4: + emitpcode (POC_SWAPF, pcop); + break; + case 5: + emitpcode (POC_SWAPF, pcop); + emitpcode (POC_RRF, pcop); + break; + case 6: + emitpcode (POC_SWAPF, pcop); + emitpcode (POC_RRF, pcop); + emitpcode (POC_RRF, pcop); + break; + case 7: + if (mask_mode == 2) + { + /* load sign */ + emitpcode (POC_RLFW, pcop); + emitpcode (POC_CLRF, pcop); + emitSKPNC; + emitpcode (POC_COMF, pcop); + return; + } + else + { + emitpcode (POC_RLFW, pcop); + emitpcode (POC_RLF, pcop); + } + break; + } + + if (mask_mode == 0) + { + /* leave invalid bits undefined */ + return; + } + + /* clear invalid bits -- zero-extend */ + emitpcode (POC_MOVLW, popGetLit (0x00ff >> shCount)); + emitpcode (POC_ANDWF, pcop); + + if (mask_mode == 2) + { + /* sign-extend */ + emitpcode (POC_MOVLW, popGetLit (0x00ff << (8 - shCount))); + emitpcode (POC_BTFSC, newpCodeOpBit (get_op (pcop, NULL, 0), 7 - shCount, 0)); + emitpcode (POC_IORWF, pcop); + } +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (!pic14_sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + aopGet (AOP (left), offl, FALSE, FALSE); + + emitpcode (POC_MOVFW, popGet (AOP (left), offl)); + emitpcode (POC_MOVWF, popGet (AOP (result), offr)); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftLeft_Left2ResultLit - shift left by known count */ +/*-----------------------------------------------------------------*/ + +static void +shiftLeft_Left2ResultLit (operand * left, operand * result, int shCount) +{ + int size, same, offr, i; + + size = AOP_SIZE (left); + if (AOP_SIZE (result) < size) + size = AOP_SIZE (result); + + same = pic14_sameRegs (AOP (left), AOP (result)); + + offr = shCount / 8; + shCount = shCount & 0x07; + + size -= offr; + + switch (shCount) + { + case 0: /* takes 0 or 2N cycles (for offr==0) */ + if (!same || offr) + { + for (i = size - 1; i >= 0; i--) + movLeft2Result (left, i, result, offr + i); + } // if + break; + + case 1: /* takes 1N+1 or 2N+1 cycles (or offr==0) */ + if (same && offr) + { + shiftLeft_Left2ResultLit (left, result, 8 * offr); + shiftLeft_Left2ResultLit (result, result, shCount); + return; /* prevent clearing result again */ + } + else + { + if (pic->isEnhancedCore) + { + for (i = 0; i < size; i++) + { + if (same && !offr) + { + if (i == 0) + { + emitpcode (POC_LSLF, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_RLF, popGet (AOP (left), i)); + } + } + else + { + if (i == 0) + { + emitpcode (POC_LSLFW, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_RLFW, popGet (AOP (left), i)); + } + + emitpcode (POC_MOVWF, popGet (AOP (result), i + offr)); + } + } + } + else + { + emitCLRC; + for (i = 0; i < size; i++) + { + if (same && !offr) + { + emitpcode (POC_RLF, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_RLFW, popGet (AOP (left), i)); + emitpcode (POC_MOVWF, popGet (AOP (result), i + offr)); + } // if + } // for + } // if (pic->isEnhancedCore) + } // if (same && offr) + break; + + case 4: /* takes 3+5(N-1) = 5N-2 cycles (for offr==0) */ + /* works in-place/with offr as well */ + emitpcode (POC_SWAPFW, popGet (AOP (left), size - 1)); + emitpcode (POC_ANDLW, popGetLit (0xF0)); + emitpcode (POC_MOVWF, popGet (AOP (result), size - 1 + offr)); + + for (i = size - 2; i >= 0; i--) + { + emitpcode (POC_SWAPFW, popGet (AOP (left), i)); + emitpcode (POC_MOVWF, popGet (AOP (result), i + offr)); + emitpcode (POC_ANDLW, popGetLit (0x0F)); + emitpcode (POC_IORWF, popGet (AOP (result), i + offr + 1)); + emitpcode (POC_XORWF, popGet (AOP (result), i + offr)); + } // for i + break; + + case 7: /* takes 2(N-1)+3 = 2N+1 cycles */ + /* works in-place/with offr as well */ + emitpcode (POC_RRFW, popGet (AOP (left), size - 1)); + for (i = size - 2; i >= 0; i--) + { + emitpcode (POC_RRFW, popGet (AOP (left), i)); + emitpcode (POC_MOVWF, popGet (AOP (result), offr + i + 1)); + } // for i + emitpcode (POC_CLRF, popGet (AOP (result), offr)); + emitpcode (POC_RRF, popGet (AOP (result), offr)); + break; + + default: + shiftLeft_Left2ResultLit (left, result, offr * 8 + shCount - 1); + shiftLeft_Left2ResultLit (result, result, 1); + return; /* prevent clearing result again */ + break; + } // switch + + while (0 < offr--) + { + emitpcode (POC_CLRF, popGet (AOP (result), offr)); + } // while +} + +/*-----------------------------------------------------------------*/ +/* shiftRight_Left2ResultLit - shift right by known count */ +/*-----------------------------------------------------------------*/ + +static void +shiftRight_Left2ResultLit (operand * left, operand * result, int shCount, int sign) +{ + int size, same, offr, i; + + size = AOP_SIZE (left); + if (AOP_SIZE (result) < size) + size = AOP_SIZE (result); + + same = pic14_sameRegs (AOP (left), AOP (result)); + + offr = shCount / 8; + shCount = shCount & 0x07; + + size -= offr; + + if (size) + { + switch (shCount) + { + case 0: /* takes 0 or 2N cycles (for offr==0) */ + if (!same || offr) + { + for (i = 0; i < size; i++) + movLeft2Result (left, i + offr, result, i); + } // if + break; + + case 1: /* takes 1N+1(3) or 2N+1(3) cycles (or offr==0) */ + emitpComment ("%s:%d: shCount=%d, size=%d, sign=%d, same=%d, offr=%d", __FUNCTION__, __LINE__, shCount, size, sign, + same, offr); + if (same && offr) + { + shiftRight_Left2ResultLit (left, result, 8 * offr, sign); + shiftRight_Left2ResultLit (result, result, shCount, sign); + return; /* prevent sign-extending result again */ + } + else + { + if (pic->isEnhancedCore) + { + for (i = size - 1; i >= 0; i--) + { + if (same && !offr) + { + if (i == (size - 1)) + { + if (sign) + { + emitpcode (POC_ASRF, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_LSRF, popGet (AOP (left), i)); + } + } + else + { + emitpcode (POC_RRF, popGet (AOP (left), i)); + } + } + else + { + if (i == (size - 1)) + { + if (sign) + { + emitpcode (POC_ASRFW, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_LSRFW, popGet (AOP (left), i)); + } + } + else + { + emitpcode (POC_RRFW, popGet (AOP (left), i + offr)); + } + + emitpcode (POC_MOVWF, popGet (AOP (result), i)); + } // if (same && !offr) + } // for i + } + else + { + emitCLRC; + if (sign) + { + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE, FALSE), 7, 0)); + emitSETC; + } + + for (i = size - 1; i >= 0; i--) + { + if (same && !offr) + { + emitpcode (POC_RRF, popGet (AOP (left), i)); + } + else + { + emitpcode (POC_RRFW, popGet (AOP (left), i + offr)); + emitpcode (POC_MOVWF, popGet (AOP (result), i)); + } + } // for i + } // if (pic->isEnhancedCore) + } // if (same && offr) + break; + + case 4: /* takes 3(6)+5(N-1) = 5N-2(+1) cycles (for offr==0) */ + /* works in-place/with offr as well */ + emitpcode (POC_SWAPFW, popGet (AOP (left), offr)); + emitpcode (POC_ANDLW, popGetLit (0x0F)); + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + + for (i = 1; i < size; i++) + { + emitpcode (POC_SWAPFW, popGet (AOP (left), i + offr)); + emitpcode (POC_MOVWF, popGet (AOP (result), i)); + emitpcode (POC_ANDLW, popGetLit (0xF0)); + emitpcode (POC_IORWF, popGet (AOP (result), i - 1)); + emitpcode (POC_XORWF, popGet (AOP (result), i)); + } // for i + + if (sign) + { + emitpcode (POC_MOVLW, popGetLit (0xF0)); + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (result), size - 1, FALSE, FALSE), 3, 0)); + emitpcode (POC_IORWF, popGet (AOP (result), size - 1)); + } // if + break; + + case 7: /* takes 2(N-1)+3(4) = 2N+1(2) cycles */ + /* works in-place/with offr as well */ + emitpcode (POC_RLFW, popGet (AOP (left), offr)); + for (i = 0; i < size - 1; i++) + { + emitpcode (POC_RLFW, popGet (AOP (left), offr + i + 1)); + emitpcode (POC_MOVWF, popGet (AOP (result), i)); + } // for i + emitpcode (POC_CLRF, popGet (AOP (result), size - 1)); + if (!sign) + { + emitpcode (POC_RLF, popGet (AOP (result), size - 1)); + } + else + { + emitSKPNC; + emitpcode (POC_DECF, popGet (AOP (result), size - 1)); + } + break; + + default: + shiftRight_Left2ResultLit (left, result, offr * 8 + shCount - 1, sign); + shiftRight_Left2ResultLit (result, result, 1, sign); + return; /* prevent sign extending result again */ + break; + } // switch + } // if + + addSign (result, size, sign); +} + +/*-----------------------------------------------------------------* +* genMultiAsm - repeat assembly instruction for size of register. +* if endian == 1, then the high byte (i.e base address + size of +* register) is used first else the low byte is used first; +*-----------------------------------------------------------------*/ +static void +genMultiAsm (PIC_OPCODE poc, operand * reg, int size, int endian) +{ + + int offset = 0; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!reg) + return; + + if (!endian) + { + endian = 1; + } + else + { + endian = -1; + offset = size - 1; + } + + while (size--) + { + emitpcode (poc, popGet (AOP (reg), offset)); + offset += endian; + } + +} + +/*-----------------------------------------------------------------*/ +/* loadSignToC - load the operand's sign bit into CARRY */ +/*-----------------------------------------------------------------*/ + +static void +loadSignToC (operand * op) +{ + FENTRY; + assert (op && AOP (op) && AOP_SIZE (op)); + + emitCLRC; + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (op), AOP_SIZE (op) - 1, FALSE, FALSE), 7, 0)); + emitSETC; +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genGenericShift (iCode * ic, int shiftRight) +{ + operand *right, *left, *result; + int size; + symbol *tlbl, *tlbl1, *inverselbl; + + FENTRY; + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* signed & unsigned types are treated the same : i.e. the + signed is NOT propagated inwards : quoting from the + ANSI - standard : "for E1 >> E2, is equivalent to division + by 2**E2 if unsigned or if it has a non-negative value, + otherwise the result is implementation defined ", MY definition + is that the sign does not get propagated */ + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + int lit = (int) ulFromVal (AOP (right)->aopu.aop_lit); + if (lit < 0) + { + lit = -lit; + shiftRight = !shiftRight; + } + + if (shiftRight) + shiftRight_Left2ResultLit (left, result, lit, !SPEC_USIGN (operandType (left))); + else + shiftLeft_Left2ResultLit (left, result, lit); + //genRightShiftLiteral (left,right,result,ic, 0); + return; + } + + /* shift count is unknown then we have to form + a loop get the loop count in B : Note: we take + only the lower order byte since shifting + more that 32 bits make no sense anyway, ( the + largest size of an object can be only 32 bits ) */ + + /* we must not overwrite the shift counter */ + assert (!pic14_sameRegs (AOP (right), AOP (result))); + + /* now move the left to the result if they are not the + same */ + if (!pic14_sameRegs (AOP (left), AOP (result))) + { + size = min (AOP_SIZE (result), AOP_SIZE (left)); + while (size--) + { + mov2w (AOP (left), size); + movwf (AOP (result), size); + } + addSign (result, AOP_SIZE (left), !SPEC_USIGN (operandType (left))); + } + + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + inverselbl = NULL; + size = AOP_SIZE (result); + + mov2w (AOP (right), 0); + if (!SPEC_USIGN (operandType (right))) + { + inverselbl = newiTempLabel (NULL); + /* signed shift count -- invert shift direction for c<0 */ + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (right), 0, FALSE, FALSE), 7, 0)); + emitpcode (POC_GOTO, popGetLabel (inverselbl->key)); + } // if + emitpcode (POC_SUBLW, popGetLit (0)); /* -count in WREG, 0-x > 0 --> BORROW = !CARRY --> CARRY is clear! */ + /* check for `a = b >> c' with `-c == 0' */ + emitSKPNZ; + emitpcode (POC_GOTO, popGetLabel (tlbl1->key)); + emitpLabel (tlbl->key); + /* propagate the sign bit inwards for SIGNED result */ + if (shiftRight && !SPEC_USIGN (operandType (result))) + loadSignToC (result); + genMultiAsm (shiftRight ? POC_RRF : POC_RLF, result, size, shiftRight); + emitpcode (POC_ADDLW, popGetLit (1)); /* clears CARRY (unless W==0 afterwards) */ + emitSKPC; + emitpcode (POC_GOTO, popGetLabel (tlbl->key)); + + if (!SPEC_USIGN (operandType (right))) + { + symbol *inv_loop = newiTempLabel (NULL); + + shiftRight = !shiftRight; /* invert shift direction */ + + /* we came here from the code above -- we are done */ + emitpcode (POC_GOTO, popGetLabel (tlbl1->key)); + + /* emit code for shifting N<0 steps, count is already in W */ + emitpLabel (inverselbl->key); + if (!shiftRight || SPEC_USIGN (operandType (result))) + emitCLRC; + emitpLabel (inv_loop->key); + /* propagate the sign bit inwards for SIGNED result */ + if (shiftRight && !SPEC_USIGN (operandType (result))) + loadSignToC (result); + genMultiAsm (shiftRight ? POC_RRF : POC_RLF, result, size, shiftRight); + emitpcode (POC_ADDLW, popGetLit (1)); + emitSKPC; + emitpcode (POC_GOTO, popGetLabel (inv_loop->key)); + } // if + + emitpLabel (tlbl1->key); + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +static void +genRightShift (iCode * ic) +{ + genGenericShift (ic, 1); +} + +static void +genLeftShift (iCode * ic) +{ + genGenericShift (ic, 0); +} + +/*-----------------------------------------------------------------*/ +/* SetIrp - Set IRP bit */ +/*-----------------------------------------------------------------*/ +static void +SetIrp (operand * result) +{ + FENTRY; + if (AOP_TYPE (result) == AOP_LIT) + { + unsigned lit = (unsigned) double2ul (operandLitValue (result)); + if (lit & 0x100) + emitSETIRP; + else + emitCLRIRP; + } + else if ((AOP_TYPE (result) == AOP_PCODE) && (AOP (result)->aopu.pcop->type == PO_LITERAL)) + { + int addrs = PCOL (AOP (result)->aopu.pcop)->lit; + if (addrs & 0x100) + emitSETIRP; + else + emitCLRIRP; + } + else if ((AOP_TYPE (result) == AOP_PCODE) && (AOP (result)->aopu.pcop->type == PO_IMMEDIATE)) + { + emitCLRIRP; /* always ensure this is clear as it may have previously been set */ + emitpcode (POC_MOVLW, popGetAddr (AOP (result), 1, 0)); + emitpcode (POC_ANDLW, popGetLit (0x01)); + emitSKPZ; + emitSETIRP; + } + else + { + emitCLRIRP; /* always ensure this is clear as it may have previouly been set */ + if (AOP_SIZE (result) > 1) + { + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (result), 1, FALSE, FALSE), 0, 0)); + emitSETIRP; + } + } +} + +static void +setup_fsr (operand * ptr) +{ + if (pic->isEnhancedCore) + { + mov2w_op (ptr, 0); + emitpcode (POC_MOVWF, popCopyReg (&pc_fsr0l)); + mov2w_op (ptr, 1); + emitpcode (POC_MOVWF, popCopyReg (&pc_fsr0h)); + } + else + { + mov2w_op (ptr, 0); + emitpcode (POC_MOVWF, popCopyReg (&pc_fsr)); + + /* also setup-up IRP */ + SetIrp (ptr); + } +} + +static void +inc_fsr (int delta) +{ + if (0 == delta) + { + /* Nothing to do. */ + return; + } // if + + if (pic->isEnhancedCore) + { + if (pic14_options.no_ext_instr) + { + /* + * Not sure if we may modify W here, so implement this without + * touching W. + * + * Efficiency is not too important here, as enhanced cores + * will most likely use extended instructions here. This is + * only a workaround for gputils 0.13.7, which supports the + * 16f1934 enhanced core, but fails to assemble ADDFSR. + */ + while (delta > 0) + { + emitpcode (POC_INCFSZ, popCopyReg (&pc_fsr0l)); + emitpcode (POC_DECF, popCopyReg (&pc_fsr0h)); + emitpcode (POC_INCF, popCopyReg (&pc_fsr0h)); + --delta; + } // while + while (delta < 0) + { + addpCode2pBlock (pb, newpCodeAsmDir("MOVF", "FSR0L, 1")); + emitSKPNZ; + emitpcode (POC_DECF, popCopyReg (&pc_fsr0h)); + emitpcode (POC_DECF, popCopyReg (&pc_fsr0l)); + ++delta; + } // while + } + else + { + assert (delta >= -32); + assert (delta < 32); + /* Hack: Turn this into a PCI (not that easy due to the argument structure). */ + addpCode2pBlock (pb, newpCodeAsmDir ("ADDFSR", "FSR0, %d", delta)); + } // if + } + else + { + while (delta > 0) + { + emitpcode (POC_INCF, popCopyReg (&pc_fsr)); + --delta; + } // while + while (delta < 0) + { + emitpcode (POC_DECF, popCopyReg (&pc_fsr)); + ++delta; + } // while + } // if +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteGet - emits code to get a byte into WREG from an */ +/* arbitrary pointer (__code, __data, generic) */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteGet (operand * src, int p_type, bool alreadyAddressed) +{ + FENTRY; + switch (p_type) + { + case POINTER: + case FPOINTER: + if (!alreadyAddressed) + setup_fsr (src); + emitpcode (POC_MOVFW, popCopyReg (pc_indf)); + break; + + case CPOINTER: + assert (AOP_SIZE (src) == 2); + mov2w_op (src, 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w_op (src, 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + emitpcode (POC_MOVLW, popGetLit (GPTRTAG_CODE)); /* GPOINTER tag for __code space */ + call_libraryfunc ("__gptrget1"); + break; + + case GPOINTER: + assert (AOP_SIZE (src) == 3); + mov2w_op (src, 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w_op (src, 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w_op (src, 2); + call_libraryfunc ("__gptrget1"); + break; + + default: + assert (!"unhandled pointer type"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* emitPtrByteSet - emits code to set a byte from src through a */ +/* pointer register INDF (legacy 8051 uses R0, R1, or DPTR). */ +/*-----------------------------------------------------------------*/ +static void +emitPtrByteSet (operand * dst, int p_type, bool alreadyAddressed) +{ + FENTRY; + switch (p_type) + { + case POINTER: + case FPOINTER: + if (!alreadyAddressed) + setup_fsr (dst); + emitpcode (POC_MOVWF, popCopyReg (pc_indf)); + break; + + case CPOINTER: + assert (!"trying to assign to __code pointer"); + break; + + case GPOINTER: + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 2)); + mov2w_op (dst, 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w_op (dst, 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w_op (dst, 2); + call_libraryfunc ("__gptrput1"); + break; + + default: + assert (!"unhandled pointer type"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, operand * left, int ptype, iCode * ifx) +{ + sym_link *etype; /* bitfield type information */ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + etype = getSpec (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* single bit field case */ + if (blen == 1) + { + if (ifx) + { + /* that is for an if statement */ + pCodeOp *pcop; + resolvedIfx rIfx; + + resolveIfx (&rIfx, ifx); + if (ptype == -1) /* direct */ + pcop = newpCodeOpBit (aopGet (AOP (left), 0, FALSE, FALSE), bstr, 0); + else + { + setup_fsr (left); + pcop = newpCodeOpBit (pc_indf->pcop.name, bstr, 0); + } + emitpcode ((rIfx.condition) ? POC_BTFSC : POC_BTFSS, pcop); + emitpcode (POC_GOTO, popGetLabel (rIfx.lbl->key)); + ifx->generated = TRUE; + } + else + { + /* + * In case of a volatile bitfield read such as + * (void)PORTCbits.RC3; + * we end up having no result ... + */ + int haveResult = !!AOP_SIZE(result); + + if (haveResult) + { + assert (!pic14_sameRegs (AOP (result), AOP (left))); + emitpcode (POC_CLRF, popGet (AOP (result), 0)); + } // if + + switch (ptype) + { + case -1: + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (left), 0, FALSE, FALSE), bstr, 0)); + /* If haveResult, adjust result below, otherwise: */ + if (!haveResult) + { + /* Dummy instruction to allow bit-test above (volatile dummy bitfield read). */ + emitpcode (POC_MOVLW, popGetLit (0)); + } // if + break; + + case POINTER: + case FPOINTER: + case GPOINTER: + case CPOINTER: + emitPtrByteGet (left, ptype, FALSE); + if (haveResult) + { + emitpcode (POC_ANDLW, popGetLit (1UL << bstr)); + emitSKPZ; + /* adjust result below */ + } // if + break; + + default: + assert (!"unhandled pointer type"); + } // switch + + /* move sign-/zero extended bit to result */ + if (haveResult) + { + if (SPEC_USIGN (OP_SYM_ETYPE (left))) + emitpcode (POC_INCF, popGet (AOP (result), 0)); + else + emitpcode (POC_DECF, popGet (AOP (result), 0)); + addSign (result, 1, !SPEC_USIGN (OP_SYM_ETYPE (left))); + } // if + } + return; + } + else if (blen <= 8 && ((blen + bstr) <= 8)) + { + /* blen > 1 */ + int i; + + for (i = 0; i < AOP_SIZE (result); i++) + emitpcode (POC_CLRF, popGet (AOP (result), i)); + + switch (ptype) + { + case -1: + mov2w (AOP (left), 0); + break; + + case POINTER: + case FPOINTER: + case GPOINTER: + case CPOINTER: + emitPtrByteGet (left, ptype, FALSE); + break; + + default: + assert (!"unhandled pointer type"); + } // switch + + if (blen < 8) + emitpcode (POC_ANDLW, popGetLit ((((1UL << blen) - 1) << bstr) & 0x00ff)); + movwf (AOP (result), 0); + AccRsh (popGet (AOP (result), 0), bstr, 1); /* zero extend the bitfield */ + + if (!SPEC_USIGN (OP_SYM_ETYPE (left)) && (bstr + blen != 8)) + { + /* signed bitfield */ + assert (bstr + blen > 0); + emitpcode (POC_MOVLW, popGetLit (0x00ff << (bstr + blen))); + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (result), 0, FALSE, FALSE), bstr + blen - 1, 0)); + emitpcode (POC_IORWF, popGet (AOP (result), 0)); + } + addSign (result, 1, !SPEC_USIGN (OP_SYM_ETYPE (left))); + return; + } + + assert (!"bitfields larger than 8 bits or crossing byte boundaries are not yet supported"); +} + +#if 1 +/*-----------------------------------------------------------------*/ +/* genDataPointerGet - generates code when ptr offset is known */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + unsigned int size; + int offset = 0; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + + /* optimization - most of the time, left and result are the same + * address, but different types. for the pic code, we could omit + * the following + */ + aopOp (result, ic, TRUE); + + if (pic14_sameRegs (AOP (left), AOP (result))) + return; + + DEBUGpic14_AopType (__LINE__, left, NULL, result); + + //emitpcode(POC_MOVFW, popGet(AOP(left),0)); + + size = AOP_SIZE (result); + if (size > getSize (OP_SYM_ETYPE (left))) + size = getSize (OP_SYM_ETYPE (left)); + + offset = 0; + while (size--) + { + emitpcode (POC_MOVFW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + offset++; + } + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} +#endif + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - pic14_emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic) +{ + asmop *aop = NULL; + sym_link *ltype = operandType (left); + sym_link *rtype = operandType (result); + sym_link *retype = getSpec (rtype); /* bitfield type information */ + int direct = 0; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + + aopOp (left, ic, FALSE); + + /* if left is rematerialisable and + result is not bit variable type and + the left is pointer to data space i.e + lower 128 bytes of space */ + if (AOP_TYPE (left) == AOP_PCODE && //AOP_TYPE(left) == AOP_IMMD && + !IS_BITVAR (retype) && PIC_IS_DATA_PTR (ltype)) + { + genDataPointerGet (left, result, ic); + return; + } + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (result, ic, FALSE); + + /* Check if can access directly instead of via a pointer */ + if ((AOP_TYPE (left) == AOP_PCODE) && (AOP (left)->aopu.pcop->type == PO_IMMEDIATE) && (AOP_SIZE (result) <= 1)) + { + direct = 1; + } + + if (IS_BITFIELD (getSpec (operandType (result)))) + { + genUnpackBits (result, left, direct ? -1 : POINTER, ifxForOp (IC_RESULT (ic), ic)); + goto release; + } + + /* If the pointer value is not in a the FSR then need to put it in */ + /* Must set/reset IRP bit for use with FSR. */ + if (!direct) + setup_fsr (left); + +// sym_link *etype; + /* if bitfield then unpack the bits */ + { + /* we have can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + while (size--) + { + if (direct) + emitpcode (POC_MOVWF, popGet (AOP (left), 0)); + else + emitpcode (POC_MOVFW, popCopyReg (pc_indf)); + if (AOP_TYPE (result) == AOP_LIT) + { + emitpcode (POC_MOVLW, popGet (AOP (result), offset)); + } + else + { + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + if (size && !direct) + { + inc_fsr (1); + } + offset++; + } + } + + /* now some housekeeping stuff */ + if (aop) + { + /* we had to allocate for this iCode */ + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + freeAsmop (NULL, aop, ic, TRUE); + } + else if (!direct) + { + /* nothing to do */ + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) + { + int size = AOP_SIZE (result) - 1; + inc_fsr (-size); + } + } + +release: + /* done */ + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - gget value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + + DEBUGpic14_AopType (__LINE__, left, NULL, result); + + if (IS_BITFIELD (getSpec (operandType (result)))) + { + genUnpackBits (result, left, GPOINTER, ifxForOp (IC_RESULT (ic), ic)); + return; + } + + { + /* emit call to __gptrget */ + char *func[] = { NULL, "__gptrget1", "__gptrget2", "__gptrget3", "__gptrget4" }; + int size = AOP_SIZE (result); + int idx = 0; + + assert (size > 0 && size <= 4); + + /* pass arguments */ + assert (AOP_SIZE (left) == 3); + mov2w (AOP (left), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w (AOP (left), 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (left), 2); + call_libraryfunc (func[size]); + + /* save result */ + movwf (AOP (result), --size); + while (size--) + { + emitpcode (POC_MOVFW, popRegFromIdx (Gstack_base_addr - idx++)); + movwf (AOP (result), size); + } // while + } + + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genConstPointerGet - get value from const generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genConstPointerGet (operand * left, operand * result, iCode * ic) +{ + //sym_link *retype = getSpec(operandType(result)); +#if 0 + symbol *albl, *blbl; //, *clbl; + pCodeOp *pcop; +#endif + int i, lit; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (left, ic, FALSE); + aopOp (result, ic, FALSE); + + DEBUGpic14_AopType (__LINE__, left, NULL, result); + + DEBUGpic14_emitcode ("; ", " %d getting const pointer", __LINE__); + + lit = op_isLitLike (left); + + if (IS_BITFIELD (getSpec (operandType (result)))) + { + genUnpackBits (result, left, lit ? -1 : CPOINTER, ifxForOp (IC_RESULT (ic), ic)); + goto release; + } + + { + char *func[] = { NULL, "__gptrget1", "__gptrget2", "__gptrget3", "__gptrget4" }; + int size = AOP_SIZE (result); + assert (size > 0 && size <= 4); + + mov2w_op (left, 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w_op (left, 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + emitpcode (POC_MOVLW, popGetLit (GPTRTAG_CODE)); /* GPOINTER tag for __code space */ + call_libraryfunc (func[size]); + + movwf (AOP (result), size - 1); + for (i = 1; i < size; i++) + { + emitpcode (POC_MOVFW, popRegFromIdx (Gstack_base_addr + 1 - i)); + movwf (AOP (result), size - 1 - i); + } // for + } + +release: + freeAsmop (left, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type = -1; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + + if (IS_PTR_CONST (type)) + DEBUGpic14_emitcode ("; ***", "%d - const pointer", __LINE__); + + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + p_type = DCL_TYPE (type); + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + DEBUGpic14_emitcode ("; ***", "%d - resolve pointer by storage class", __LINE__); + + if (SPEC_OCLS (etype)->codesp) + { + DEBUGpic14_emitcode ("; ***", "%d - cpointer", __LINE__); + //p_type = CPOINTER ; + } + else if (SPEC_OCLS (etype)->fmap && !SPEC_OCLS (etype)->paged) + DEBUGpic14_emitcode ("; ***", "%d - fpointer", __LINE__); + /*p_type = FPOINTER ; */ + else if (SPEC_OCLS (etype)->fmap && SPEC_OCLS (etype)->paged) + DEBUGpic14_emitcode ("; ***", "%d - ppointer", __LINE__); + /* p_type = PPOINTER; */ + else if (SPEC_OCLS (etype) == idata) + DEBUGpic14_emitcode ("; ***", "%d - ipointer", __LINE__); + /* p_type = IPOINTER; */ + else + DEBUGpic14_emitcode ("; ***", "%d - pointer", __LINE__); + /* p_type = POINTER ; */ + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + + case POINTER: + case FPOINTER: + //case IPOINTER: + genNearPointerGet (left, result, ic); + break; + /* + case PPOINTER: + genPagedPointerGet(left,result,ic); + break; + + case FPOINTER: + genFarPointerGet (left,result,ic); + break; + */ + case CPOINTER: + genConstPointerGet (left, result, ic); + break; + + case GPOINTER: + genGenPointerGet (left, result, ic); + break; + default: + assert (!"unhandled pointer type"); + break; + } + +} + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * result, operand * right, int p_type) +{ + unsigned blen; /* bitfield length */ + unsigned bstr; /* bitfield starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bitfield length is less than a byte and does not cross byte boundaries */ + if ((blen <= 8) && ((bstr + blen) <= 8)) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bitfield length <8 and literal source */ + int lit = (int) ulFromVal (AOP (right)->aopu.aop_lit); + if (blen == 1) + { + pCodeOp *pcop; + + switch (p_type) + { + case -1: + if (AOP (result)->type == AOP_PCODE) + pcop = newpCodeOpBit (aopGet (AOP (result), 0, FALSE, FALSE), bstr, 0); + else + pcop = popGet (AOP (result), 0); + emitpcode (lit ? POC_BSF : POC_BCF, pcop); + break; + + case POINTER: + case FPOINTER: + setup_fsr (result); + emitpcode (lit ? POC_BSF : POC_BCF, newpCodeOpBit (PCOP (pc_indf)->name, bstr, 0)); + break; + + case CPOINTER: + assert (!"trying to assign to bitfield via pointer to __code space"); + break; + + case GPOINTER: + emitPtrByteGet (result, p_type, FALSE); + if (lit) + { + emitpcode (POC_IORLW, newpCodeOpLit (1UL << bstr)); + } + else + { + emitpcode (POC_ANDLW, newpCodeOpLit ((~(1UL << bstr)) & 0x0ff)); + } + emitPtrByteSet (result, p_type, TRUE); + break; + + default: + assert (!"unhandled pointer type"); + break; + } // switch (p_type) + } + else + { + /* blen > 1 */ + litval = lit << bstr; + litval &= (~mask) & 0x00ff; + + switch (p_type) + { + case -1: + mov2w (AOP (result), 0); + if ((litval | mask) != 0x00ff) + emitpcode (POC_ANDLW, popGetLit (mask)); + if (litval != 0x00) + emitpcode (POC_IORLW, popGetLit (litval)); + movwf (AOP (result), 0); + break; + + case POINTER: + case FPOINTER: + case GPOINTER: + emitPtrByteGet (result, p_type, FALSE); + if ((litval | mask) != 0x00ff) + emitpcode (POC_ANDLW, popGetLit (mask)); + if (litval != 0x00) + emitpcode (POC_IORLW, popGetLit (litval)); + emitPtrByteSet (result, p_type, TRUE); + break; + + case CPOINTER: + assert (!"trying to assign to bitfield via pointer to __code space"); + break; + + default: + assert (!"unhandled pointer type"); + break; + } // switch + } // if (blen > 1) + } + else + { + /* right is no literal */ + if (blen == 1) + { + switch (p_type) + { + case -1: + /* Note more efficient code, of pre clearing bit then only setting it if required, + * can only be done if it is known that the result is not a SFR */ + emitpcode (POC_RRFW, popGet (AOP (right), 0)); + emitSKPC; + emitpcode (POC_BCF, newpCodeOpBit (aopGet (AOP (result), 0, FALSE, FALSE), bstr, 0)); + emitSKPNC; + emitpcode (POC_BSF, newpCodeOpBit (aopGet (AOP (result), 0, FALSE, FALSE), bstr, 0)); + break; + + case POINTER: + case FPOINTER: + case GPOINTER: + emitPtrByteGet (result, p_type, FALSE); + emitpcode (POC_BTFSS, newpCodeOpBit (aopGet (AOP (right), 0, FALSE, FALSE), bstr, 0)); + emitpcode (POC_ANDLW, newpCodeOpLit (~(1UL << bstr) & 0x0ff)); + emitpcode (POC_BTFSC, newpCodeOpBit (aopGet (AOP (right), 0, FALSE, FALSE), bstr, 0)); + emitpcode (POC_IORLW, newpCodeOpLit ((1UL << bstr) & 0x0ff)); + emitPtrByteSet (result, p_type, TRUE); + break; + + case CPOINTER: + assert (!"trying to assign to bitfield via pointer to __code space"); + break; + + default: + assert (!"unhandled pointer type"); + break; + } // switch + return; + } + else + { + /* Case with a bitfield 1 < length <= 8 and arbitrary source */ + pCodeOp *temp = popGetTempReg (); + + mov2w (AOP (right), 0); + if (blen < 8) + { + emitpcode (POC_ANDLW, popGetLit ((1UL << blen) - 1)); + } + emitpcode (POC_MOVWF, temp); + if (bstr) + { + AccLsh (temp, bstr); + } + + switch (p_type) + { + case -1: + mov2w (AOP (result), 0); + emitpcode (POC_ANDLW, popGetLit (mask)); + emitpcode (POC_IORFW, temp); + movwf (AOP (result), 0); + break; + + case POINTER: + case FPOINTER: + case GPOINTER: + emitPtrByteGet (result, p_type, FALSE); + emitpcode (POC_ANDLW, popGetLit (mask)); + emitpcode (POC_IORFW, temp); + emitPtrByteSet (result, p_type, TRUE); + break; + + case CPOINTER: + assert (!"trying to assign to bitfield via pointer to __code space"); + break; + + default: + assert (!"unhandled pointer type"); + break; + } // switch + + popReleaseTempReg (temp); + } // if (blen > 1) + } // if (AOP(right)->type != AOP_LIT) + return; + } // if (blen <= 8 && ((blen + bstr) <= 8)) + + assert (!"bitfields larger than 8 bits or crossing byte boundaries are not yet supported"); +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size = 0; + int offset = 0; + sym_link *rtype = operandType(right); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + assert (IS_SYMOP (result)); + assert (IS_PTR (OP_SYM_TYPE (result))); + + /* + * Determine size from right operand (not result): + * The result might be a rematerialized pointer to (the first field in) a struct, + * which then assumes the type (and size) of the struct rather than the first field. + */ + size = AOP_SIZE(right); + + /*test the right operand has a pointer value*/ + if ((AOP_TYPE(right) == AOP_PCODE) && PIC_IS_DATA_PTR(rtype)) + { + while (size--) + { + emitpcode(POC_MOVLW, popGetAddr(AOP(right), size, 0)); + emitpcode(POC_MOVWF, popGet(AOP(result), size)); + } + } + else + { + // tsd, was l+1 - the underline `_' prefix was being stripped + while (size--) + { + emitpComment ("%s:%u: size=%d, offset=%d, AOP_TYPE(res)=%d", __FILE__, __LINE__, size, offset, + AOP_TYPE (result)); + + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned int lit = pic14aopLiteral (AOP (IC_RIGHT (ic))->aopu.aop_lit, offset); + //fprintf (stderr, "%s:%u: lit %d 0x%x\n", __FUNCTION__,__LINE__, lit, lit); + if (lit & 0xff) + { + emitpcode (POC_MOVLW, popGetLit (lit & 0xff)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + else + { + emitpcode (POC_CLRF, popGet (AOP (result), offset)); + } + } + else + { + //fprintf (stderr, "%s:%u: no lit\n", __FUNCTION__,__LINE__); + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + offset++; + } + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - pic14_emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic) +{ + asmop *aop = NULL; + sym_link *ptype = operandType (result); + sym_link *retype = getSpec (operandType (right)); + sym_link *letype = getSpec (ptype); + int direct = 0; + + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (result, ic, FALSE); + +#if 1 + /* if the result is rematerializable & + in data space & not a bit variable */ + //if (AOP_TYPE(result) == AOP_IMMD && + if (AOP_TYPE (result) == AOP_PCODE && PIC_IS_DATA_PTR (ptype) && !IS_BITVAR (retype) && !IS_BITVAR (letype)) + { + genDataPointerSet (right, result, ic); + freeAsmop (result, NULL, ic, TRUE); + return; + } +#endif + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (right, ic, FALSE); + DEBUGpic14_AopType (__LINE__, NULL, right, result); + + /* Check if can access directly instead of via a pointer */ + if ((AOP_TYPE (result) == AOP_PCODE) && (AOP (result)->aopu.pcop->type == PO_IMMEDIATE) && (AOP_SIZE (right) == 1)) + { + direct = 1; + } + + if (IS_BITFIELD (letype)) + { + genPackBits (letype, result, right, direct ? -1 : POINTER); + return; + } + + /* If the pointer value is not in a the FSR then need to put it in */ + /* Must set/reset IRP bit for use with FSR. */ + /* Note only do this once - assuming that never need to cross a bank boundary at address 0x100. */ + if (!direct) + setup_fsr (result); + + { + /* we have can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + while (size--) + { + char *l = aopGet (AOP (right), offset, FALSE, TRUE); + if (*l == '@') + { + emitpcode (POC_MOVFW, popCopyReg (pc_indf)); + } + else + { + if (AOP_TYPE (right) == AOP_LIT) + { + emitpcode (POC_MOVLW, popGet (AOP (right), offset)); + } + else + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + } + if (direct) + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + else + emitpcode (POC_MOVWF, popCopyReg (pc_indf)); + } + if (size && !direct) + inc_fsr (1); + offset++; + } + } + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* now some housekeeping stuff */ + if (aop) + { + /* we had to allocate for this iCode */ + freeAsmop (NULL, aop, ic, TRUE); + } + else if (!direct) + { + /* nothing to do */ + } + else + { + /* we did not allocate which means left + already in a pointer register, then + if size > 0 && this could be used again + we have to point it back to where it + belongs */ + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) + { + int size = AOP_SIZE (right) - 1; + inc_fsr (-size); + } + } + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* done */ + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic) +{ + sym_link *retype = getSpec (operandType (result)); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + + DEBUGpic14_AopType (__LINE__, right, NULL, result); + + if (IS_BITFIELD (retype)) + { + genPackBits (retype, result, right, GPOINTER); + return; + } + + { + /* emit call to __gptrput */ + char *func[] = { NULL, "__gptrput1", "__gptrput2", "__gptrput3", "__gptrput4" }; + int size = AOP_SIZE (right); + int idx = 0; + + /* The following assertion fails for + * struct foo { char a; char b; } bar; + * void demo(struct foo *dst, char c) { dst->b = c; } + * as size will be 1 (sizeof(c)), whereas dst->b will be accessed + * using (((char *)dst)+1), whose OP_SYM_ETYPE still is struct foo + * of size 2. + * The frontend seems to guarantee that IC_LEFT has the correct size, + * it works fine both for larger and smaller types of `char c'. + * */ + //assert (size == getSize(OP_SYM_ETYPE(result))); + assert (size > 0 && size <= 4); + + /* pass arguments */ + /* - value (MSB in Gstack_base_addr-2, growing downwards) */ + { + int off = size; + idx = 2; + while (off--) + { + mov2w_op (right, off); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - idx++)); + } + idx = 0; + } + /* - address */ + assert (AOP_SIZE (result) == 3); + mov2w (AOP (result), 0); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr - 1)); + mov2w (AOP (result), 1); + emitpcode (POC_MOVWF, popRegFromIdx (Gstack_base_addr)); + mov2w (AOP (result), 2); + call_libraryfunc (func[size]); + } + + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + /* if (SPEC_OCLS(etype)->codesp ) { */ + /* p_type = CPOINTER ; */ + /* } */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && !SPEC_OCLS(etype)->paged) */ + /* p_type = FPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && SPEC_OCLS(etype)->paged) */ + /* p_type = PPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype) == idata ) */ + /* p_type = IPOINTER ; */ + /* else */ + /* p_type = POINTER ; */ + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case FPOINTER: + //case IPOINTER: + genNearPointerSet (right, result, ic); + break; + /* + case PPOINTER: + genPagedPointerSet (right,result,ic); + break; + + case FPOINTER: + genFarPointerSet (right,result,ic); + break; + */ + case GPOINTER: + genGenPointerSet (right, result, ic); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + pic14_toBoolean (cond); + else + isbit = 1; + + /* if there was something to be popped then do it */ + if (popIc) + genIpop (popIc); + + if (isbit) + { + /* This assumes that CARRY is set iff cond is true */ + if (IC_TRUE (ic)) + { + assert (!IC_FALSE (ic)); + emitpcode (POC_BTFSC, popGet (AOP (cond), 0)); + //emitSKPNC; + emitpcode (POC_GOTO, popGetLabel (IC_TRUE (ic)->key)); + } + else + { + assert (IC_FALSE (ic)); + emitpcode (POC_BTFSS, popGet (AOP (cond), 0)); + //emitSKPC; + emitpcode (POC_GOTO, popGetLabel (IC_FALSE (ic)->key)); + } + if (0) + { + static int hasWarned = 0; + if (!hasWarned) + { + fprintf (stderr, "WARNING: using untested code for %s:%u -- please check the .asm output and report bugs.\n", + ic->filename, ic->lineno); + hasWarned = 1; + } + } + } + else + { + /* now Z is set iff !cond */ + if (IC_TRUE (ic)) + { + assert (!IC_FALSE (ic)); + emitSKPZ; + emitpcode (POC_GOTO, popGetLabel (IC_TRUE (ic)->key)); + } + else + { + emitSKPNZ; + emitpcode (POC_GOTO, popGetLabel (IC_FALSE (ic)->key)); + } + } + + ic->generated = TRUE; + + /* the result is now in the accumulator */ + freeAsmop (cond, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + operand *right, *result, *left; + int size, offset; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + + //aopOp(IC_RESULT(ic),ic,FALSE); + + aopOp ((left = IC_LEFT (ic)), ic, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic14_AopType (__LINE__, left, right, result); + assert (IS_SYMOP (left)); + + /* sanity check: generic pointers to code space are not yet supported, + * pionters to codespace must not be assigned addresses of __data values. */ +#if 0 + fprintf (stderr, "result: %s, left: %s\n", OP_SYMBOL (result)->name, OP_SYMBOL (left)->name); + fprintf (stderr, "result->type : "); + printTypeChain (OP_SYM_TYPE (result), stderr); + fprintf (stderr, ", codesp:%d, codeptr:%d, constptr:%d\n", IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (result)))), + IS_CODEPTR (OP_SYM_TYPE (result)), IS_PTR_CONST (OP_SYM_TYPE (result))); + fprintf (stderr, "result->etype: "); + printTypeChain (OP_SYM_ETYPE (result), stderr); + fprintf (stderr, ", codesp:%d, codeptr:%d, constptr:%d\n", IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_ETYPE (result)))), + IS_CODEPTR (OP_SYM_ETYPE (result)), IS_PTR_CONST (OP_SYM_ETYPE (result))); + fprintf (stderr, "left->type : "); + printTypeChain (OP_SYM_TYPE (left), stderr); + fprintf (stderr, ", codesp:%d, codeptr:%d, constptr:%d\n", IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (left)))), + IS_CODEPTR (OP_SYM_TYPE (left)), IS_PTR_CONST (OP_SYM_TYPE (left))); + fprintf (stderr, "left->etype : "); + printTypeChain (OP_SYM_ETYPE (left), stderr); + fprintf (stderr, ", codesp:%d, codeptr:%d, constptr:%d\n", IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_ETYPE (left)))), + IS_CODEPTR (OP_SYM_ETYPE (left)), IS_PTR_CONST (OP_SYM_ETYPE (left))); +#endif + + if (IS_SYMOP (result) && IS_CODEPTR (OP_SYM_TYPE (result)) && !IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (left))))) + { + fprintf (stderr, "trying to assign __code pointer (%s) an address in __data space (&%s) -- expect trouble\n", + IS_SYMOP (result) ? OP_SYMBOL (result)->name : "unknown", OP_SYMBOL (left)->name); + } + else if (IS_SYMOP (result) && !IS_CODEPTR (OP_SYM_TYPE (result)) && IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (left))))) + { + fprintf (stderr, "trying to assign __data pointer (%s) an address in __code space (&%s) -- expect trouble\n", + IS_SYMOP (result) ? OP_SYMBOL (result)->name : "unknown", OP_SYMBOL (left)->name); + } + + size = AOP_SIZE (IC_RESULT (ic)); + if (IS_SYMOP (result) && IS_GENPTR (OP_SYM_TYPE (result))) + { + /* strip tag */ + if (size > GPTRSIZE - 1) + size = GPTRSIZE - 1; + } + offset = 0; + + while (size--) + { + /* fixing bug #863624, reported from (errolv) */ + emitpcode (POC_MOVLW, popGetImmd (OP_SYMBOL (left)->rname, offset, 0, IS_FUNC (OP_SYM_TYPE (left)))); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + +#if 0 + emitpcode (POC_MOVLW, popGet (AOP (left), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); +#endif + offset++; + } + + if (IS_SYMOP (result) && IS_GENPTR (OP_SYM_TYPE (result))) + { + /* provide correct tag */ + int isCode = IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (left)))); + emitpcode (POC_MOVLW, popGetLit (isCode ? GPTRTAG_CODE : GPTRTAG_DATA)); + movwf (AOP (result), 2); + } + + freeAsmop (left, NULL, ic, FALSE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + int size, offset, know_W; + unsigned long lit = 0L; + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* if they are the same */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, TRUE); + + DEBUGpic14_AopType (__LINE__, NULL, right, result); + + /* if they are the same registers */ + if (pic14_sameRegs (AOP (right), AOP (result))) + goto release; + + /* special case: assign from __code */ + if (!IS_ITEMP (right) /* --> iTemps never reside in __code */ + && IS_SYMOP (right) /* --> must be an immediate (otherwise we would be in genConstPointerGet) */ + && !IS_FUNC (OP_SYM_TYPE (right)) /* --> we would want its address instead of the first instruction */ + && !IS_CODEPTR (OP_SYM_TYPE (right)) /* --> get symbols address instread */ + && IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (right))))) + { + emitpComment ("genAssign from CODESPACE"); + genConstPointerGet (right, result, ic); + goto release; + } + + /* just for symmetry reasons... */ + if (!IS_ITEMP (result) && IS_SYMOP (result) && IN_CODESPACE (SPEC_OCLS (getSpec (OP_SYM_TYPE (result))))) + { + assert (!"cannot write to CODESPACE"); + } + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + + emitpcode ((((int) operandLitValue (right)) ? POC_BSF : POC_BCF), popGet (AOP (result), 0)); + + if (((int) operandLitValue (right))) + pic14_emitcode ("bsf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + else + pic14_emitcode ("bcf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) + { + emitpcode (POC_BCF, popGet (AOP (result), 0)); + emitpcode (POC_BTFSC, popGet (AOP (right), 0)); + emitpcode (POC_BSF, popGet (AOP (result), 0)); + + pic14_emitcode ("bcf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + pic14_emitcode ("btfsc", "(%s >> 3),(%s & 7)", AOP (right)->aopu.aop_dir, AOP (right)->aopu.aop_dir); + pic14_emitcode ("bsf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + goto release; + } + + /* we need to or */ + emitpcode (POC_BCF, popGet (AOP (result), 0)); + pic14_toBoolean (right); + emitSKPZ; + emitpcode (POC_BSF, popGet (AOP (result), 0)); + //aopPut(AOP(result),"a",0); + goto release; + } + + /* bit variables done */ + /* general case */ + size = AOP_SIZE (result); + offset = 0; + if (AOP_TYPE (right) == AOP_DIR && (AOP_TYPE (result) == AOP_REG) && size == 1) + { + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (aopIdx (AOP (result), 0) == 4) + { + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + goto release; + } + else + DEBUGpic14_emitcode ("; WARNING", "%s %d ignoring register storage", __FUNCTION__, __LINE__); + } + + know_W = -1; + while (size--) + { + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (AOP_TYPE (right) == AOP_LIT) + { + lit = (unsigned long) pic14aopLiteral (AOP (right)->aopu.aop_lit, offset) & 0x0ff; + if (lit & 0xff) + { + if (know_W != (int) (lit & 0xff)) + emitpcode (POC_MOVLW, popGetLit (lit & 0xff)); + know_W = lit & 0xff; + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + else + emitpcode (POC_CLRF, popGet (AOP (result), offset)); + + } + else if (AOP_TYPE (right) == AOP_CRY) + { + emitpcode (POC_CLRF, popGet (AOP (result), offset)); + if (offset == 0) + { + emitpcode (POC_BTFSS, popGet (AOP (right), 0)); + emitpcode (POC_INCF, popGet (AOP (result), 0)); + } + } + else + { + mov2w_op (right, offset); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + } + + offset++; + } + + +release: + freeAsmop (right, NULL, ic, FALSE); + freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + symbol *jtab; + char *l; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + aopOp (IC_JTCOND (ic), ic, FALSE); + /* get the condition into accumulator */ + l = aopGet (AOP (IC_JTCOND (ic)), 0, FALSE, FALSE); + MOVA (l); + /* multiply by three */ + pic14_emitcode ("add", "a,acc"); + pic14_emitcode ("add", "a,%s", aopGet (AOP (IC_JTCOND (ic)), 0, FALSE, FALSE)); + + jtab = newiTempLabel (NULL); + pic14_emitcode ("mov", "dptr,#%05d_DS_", labelKey2num (jtab->key)); + pic14_emitcode ("jmp", "@a+dptr"); + pic14_emitcode ("", "%05d_DS_:", labelKey2num (jtab->key)); + + emitpcode (POC_MOVLW, popGetHighLabel (jtab->key)); + emitpcode (POC_MOVWF, popCopyReg (&pc_pclath)); + emitpcode (POC_MOVLW, popGetLabel (jtab->key)); + emitpcode (POC_ADDFW, popGet (AOP (IC_JTCOND (ic)), 0)); + emitSKPNC; + emitpcode (POC_INCF, popCopyReg (&pc_pclath)); + emitpcode (POC_MOVWF, popCopyReg (&pc_pcl)); + emitpLabel (jtab->key); + + freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); + + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + pic14_emitcode ("ljmp", "%05d_DS_", labelKey2num (jtab->key)); + emitpcode (POC_GOTO, popGetLabel (jtab->key)); + + } + +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *restype = operandType (result); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE); + aopOp (result, ic, FALSE); + + DEBUGpic14_AopType (__LINE__, NULL, right, result); + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + assert (!"assigning to bit variables is not supported"); + } + + if ((AOP_TYPE (right) == AOP_CRY) && (AOP_TYPE (result) == AOP_REG)) + { + int offset = 1; + size = AOP_SIZE (result); + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + emitpcode (POC_CLRF, popGet (AOP (result), 0)); + emitpcode (POC_BTFSC, popGet (AOP (right), 0)); + emitpcode (POC_INCF, popGet (AOP (result), 0)); + + while (size--) + emitpcode (POC_CLRF, popGet (AOP (result), offset++)); + + goto release; + } + + if (IS_BOOL (operandType (result))) + { + pic14_toBoolean (right); + emitSKPNZ; + emitpcode (POC_MOVLW, popGetLit (1)); + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + goto release; + } + + if (IS_PTR (restype)) + { + operand *result = IC_RESULT (ic); + //operand *left = IC_LEFT(ic); + operand *right = IC_RIGHT (ic); + int tag = 0xff; + + /* copy common part */ + int max, size = AOP_SIZE (result); + if (size > AOP_SIZE (right)) + size = AOP_SIZE (right); + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* warn if we discard generic opinter tag */ + if (!IS_GENPTR (restype) && IS_GENPTR (rtype) && (AOP_SIZE (result) < AOP_SIZE (right))) + { + //fprintf (stderr, "%s:%u: discarding generic pointer type tag\n", __FUNCTION__, __LINE__); + } // if + + max = size; + while (size--) + { + mov2w_op (right, size); + movwf (AOP (result), size); + } // while + + /* upcast into generic pointer type? */ + if (IS_GENPTR (restype) && (size < AOP_SIZE (result)) && (!IS_GENPTR (rtype) || AOP_SIZE (right) < GPTRSIZE)) + { + //fprintf (stderr, "%s:%u: must determine pointer type\n", __FUNCTION__, __LINE__); + if (IS_PTR (rtype)) + { + switch (DCL_TYPE (rtype)) + { + case POINTER: /* __data */ + case FPOINTER: /* __data */ + assert (AOP_SIZE (right) == 2); + tag = GPTRTAG_DATA; + break; + + case CPOINTER: /* __code */ + assert (AOP_SIZE (right) == 2); + tag = GPTRTAG_CODE; + break; + + case GPOINTER: /* unknown destination, __data or __code */ + /* assume __data space (address of immediate) */ + assert (AOP_TYPE (right) == AOP_PCODE && AOP (right)->aopu.pcop->type == PO_IMMEDIATE); + if (AOP (right)->code) + tag = GPTRTAG_CODE; + else + tag = GPTRTAG_DATA; + break; + + default: + assert (!"unhandled pointer type"); + } // switch + } + else + { + /* convert other values into pointers to __data space */ + tag = GPTRTAG_DATA; + } + + assert (AOP_SIZE (result) == 3); + if (tag == 0) + { + emitpcode (POC_CLRF, popGet (AOP (result), 2)); + } + else + { + emitpcode (POC_MOVLW, popGetLit (tag)); + movwf (AOP (result), 2); + } + } + else + { + addSign (result, max, 0); + } // if + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + + /* if they are in the same place */ + if (pic14_sameRegs (AOP (right), AOP (result))) + goto release; + + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (IS_PTR_CONST (rtype)) + DEBUGpic14_emitcode ("; ***", "%d - right is const pointer", __LINE__); + if (IS_PTR_CONST (operandType (IC_RESULT (ic)))) + DEBUGpic14_emitcode ("; ***", "%d - result is const pointer", __LINE__); + + if ((AOP_TYPE (right) == AOP_PCODE) && AOP (right)->aopu.pcop->type == PO_IMMEDIATE) + { + emitpcode (POC_MOVLW, popGetAddr (AOP (right), 0, 0)); + emitpcode (POC_MOVWF, popGet (AOP (result), 0)); + emitpcode (POC_MOVLW, popGetAddr (AOP (right), 1, 0)); + emitpcode (POC_MOVWF, popGet (AOP (result), 1)); + if (AOP_SIZE (result) < 2) + fprintf (stderr, "%d -- result is not big enough to hold a ptr\n", __LINE__); + + } + else + { + + /* if they in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + + //aopPut(AOP(result), + // aopGet(AOP(right),offset,FALSE,FALSE), + // offset); + + offset++; + } + } + goto release; + } + + /* so we now know that the size of destination is greater + than the size of the source. */ + + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + while (size--) + { + emitpcode (POC_MOVFW, popGet (AOP (right), offset)); + emitpcode (POC_MOVWF, popGet (AOP (result), offset)); + offset++; + } + + addSign (result, AOP_SIZE (right), !SPEC_USIGN (rtype)); + +release: + freeAsmop (right, NULL, ic, TRUE); + freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instrucion */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form + a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no + saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + aopOp (IC_RESULT (ic), ic, FALSE); + + emitpcode (POC_DECFSZ, popGet (AOP (IC_RESULT (ic)), 0)); + emitpcode (POC_GOTO, popGetLabel (IC_TRUE (ifx)->key)); + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = TRUE; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + FENTRY; + DEBUGpic14_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (isOperandInFarSpace (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic)))) + { + + int size = getSize (operandType (IC_RESULT (ic))); + int offset = fReturnSizePic - size; + while (size--) + { + pic14_emitcode ("push", "%s", (strcmp (fReturn[fReturnSizePic - offset - 1], "a") ? + fReturn[fReturnSizePic - offset - 1] : "acc")); + offset++; + } + aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + while (size--) + { + pic14_emitcode ("pop", "acc"); + aopPut (AOP (IC_RESULT (ic)), "a", offset++); + } + + } + else + { + _G.accInUse++; + aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + GpseudoStkPtr = ic->parmBytes; // address used arg on stack + assignResultValue (IC_RESULT (ic)); + } + + freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + FENTRY; + pic14_emitcode ("; genDummyRead", ""); + pic14_emitcode ("; not implemented", ""); + + ic = ic; +} + +/*-----------------------------------------------------------------*/ +/* genpic14Code - generate code for pic14 based controllers */ +/*-----------------------------------------------------------------*/ +/* +* At this point, ralloc.c has gone through the iCode and attempted +* to optimize in a way suitable for a PIC. Now we've got to generate +* PIC instructions that correspond to the iCode. +* +* Once the instructions are generated, we'll pass through both the +* peep hole optimizer and the pCode optimizer. +*-----------------------------------------------------------------*/ + +void +genpic14Code (iCode * lic) +{ + iCode *ic; + int cln = 0; + const char *cline; + + FENTRY; + + pic = pic14_getPIC(); + + pb = newpCodeChain (GcurMemmap, 0, newpCodeCharP ("; Starting pCode block")); + addpBlock (pb); + + /* if debug information required */ + if (options.debug && debugFile && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + //DEBUGpic14_emitcode(";ic",""); + //fprintf (stderr, "in ic loop\n"); + //pic14_emitcode ("", ";\t%s:%d: %s", ic->filename, + //ic->lineno, printCLine(ic->filename, ic->lineno)); + + if (!options.noCcodeInAsm && (cln != ic->lineno)) + { + cln = ic->lineno; + //fprintf (stderr, "%s\n", printCLine (ic->filename, ic->lineno)); + cline = printCLine (ic->filename, ic->lineno); + if (!cline || strlen (cline) == 0) + cline = printCLine (ic->filename, ic->lineno); + addpCode2pBlock (pb, newpCodeCSource (ic->lineno, ic->filename, cline)); + //emitpComment ("[C-SRC] %s:%d: %s", ic->filename, cln, cline); + } + + if (options.iCodeInAsm) + { + const char *iLine = printILine (ic); + emitpComment ("[ICODE] %s:%d: %s", ic->filename, ic->lineno, printILine (ic)); + dbuf_free (iLine); + } + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop then the if statement might + be using some of the registers being popped which + would destory the contents of the register so + we need to check for this condition and handle it */ + if (ic->next && ic->next->op == IFX && regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) + genIfx (ic->next, ic); + else + genIpop (ic); + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + during parsing SDCC.y */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + pic14_genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETABIT: + genGetABit (ic); + break; + + case GETHBIT: + genGetHbit (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + default: + fprintf (stderr, "UNHANDLED iCode: "); + piCode (ic, stderr); + ic = ic; + break; + } + } + + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + { + peepHole (&genLine.lineHead); + } + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + +#ifdef PCODE_DEBUG + DFPRINTF ((stderr, "printing pBlock\n\n")); + printpBlock (stdout, pb); +#endif + + /* destroy the line list */ + destroy_line_list (); +} + +/* This is not safe, as a AOP_PCODE/PO_IMMEDIATE might be used both as literal + * (meaning: representing its own address) or not (referencing its contents). + * This can only be decided based on the operand's type. */ +static int +aop_isLitLike (asmop * aop) +{ + assert (aop); + if (aop->type == AOP_LIT) + return TRUE; + if (aop->type == AOP_IMMD) + return TRUE; + if ((aop->type == AOP_PCODE) && + ((aop->aopu.pcop->type == PO_LITERAL) || + (aop->aopu.pcop->type == PO_IMMEDIATE))) + { + /* this should be treated like a literal/immediate (use MOVLW/ADDLW/SUBLW + * instead of MOVFW/ADDFW/SUBFW, use popGetAddr instead of popGet) */ + return TRUE; + } + return FALSE; +} + +int +op_isLitLike (operand * op) +{ + assert (op); + if (aop_isLitLike (AOP (op))) + return TRUE; + if (IS_SYMOP (op) && IS_FUNC (OP_SYM_TYPE (op))) + return TRUE; + if (IS_SYMOP (op) && IS_PTR (OP_SYM_TYPE (op)) && (AOP_TYPE (op) == AOP_PCODE) && (AOP (op)->aopu.pcop->type == PO_IMMEDIATE)) + { + return TRUE; + } + + return FALSE; +} diff --git a/src/pic14/gen.h b/src/pic14/gen.h new file mode 100644 index 0000000..6143d30 --- /dev/null +++ b/src/pic14/gen.h @@ -0,0 +1,174 @@ +/*------------------------------------------------------------------------- + SDCCgen51.h - header file for code generation for 8051 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + PIC port - T. Scott Dattalo scott@dattalo.com (2000) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGENPIC14_H +#define SDCCGENPIC14_H + +#include "common.h" + +#include "main.h" +#include "pcode.h" +#include "ralloc.h" + +#define FENTRY do { \ + /*fprintf (stderr, "%s:%u:%s: *{*\n", __FILE__, __LINE__, __FUNCTION__);*/ \ + if (options.debug || debug_verbose) { \ + emitpComment ("; %s:%u:%s *{*", __FILE__, __LINE__, __FUNCTION__); \ + } \ +} while (0) +#define FEXIT do { \ + /*fprintf (stderr, "%s:%u:%s: *}*\n", __FILE__, __LINE__, __FUNCTION__);*/ \ + if (options.debug || debug.verbose) { \ + emitpComment ("; %s:%u:%s *}*", __FILE__, __LINE__, __FUNCTION__); \ + } \ +} while (0) + +enum +{ + AOP_LIT = 1, + AOP_REG, + AOP_DIR, + AOP_STK, + AOP_IMMD, + AOP_STR, + AOP_CRY, + AOP_PCODE + +}; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop +{ + + short type; /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_IMMD - immediate value for eg. remateriazable + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned freed:1; /* already freed */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + reg_info *aop_ptr; /* either -> to r0 or r1 */ + char *aop_immd; /* if immediate others are implied */ + int aop_stk; /* stack offset when AOP_STK */ + char *aop_str[4]; /* just a string array containing the location */ + pCodeOp *pcop; + } + aopu; +} +asmop; + +extern unsigned fReturnSizePic; + + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || AOP(x)->paged)) + +#define RESULTONSTACK(x) \ + (IC_RESULT(x) && IC_RESULT(x)->aop && \ + IC_RESULT(x)->aop->type == AOP_STK ) + +#define MOVA(x) if (strcmp(x,"a") && strcmp(x,"acc")) pic14_emitcode(";XXX mov","a,%s %s,%d",x,__FILE__,__LINE__); +#define CLRC pic14_emitcode(";XXX clr","c %s,%d",__FILE__,__LINE__); + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + +/*-----------------------------------------------------------------*/ +/* Macros for emitting skip instructions */ +/*-----------------------------------------------------------------*/ + +#define emitSKPC emitpcode(POC_BTFSS,popCopyGPR2Bit(PCOP(&pc_status),PIC_C_BIT)) +#define emitSKPNC emitpcode(POC_BTFSC,popCopyGPR2Bit(PCOP(&pc_status),PIC_C_BIT)) +#define emitSKPZ emitpcode(POC_BTFSS,popCopyGPR2Bit(PCOP(&pc_status),PIC_Z_BIT)) +#define emitSKPNZ emitpcode(POC_BTFSC,popCopyGPR2Bit(PCOP(&pc_status),PIC_Z_BIT)) +#define emitSKPDC emitpcode(POC_BTFSS,popCopyGPR2Bit(PCOP(&pc_status),PIC_DC_BIT)) +#define emitSKPNDC emitpcode(POC_BTFSC,popCopyGPR2Bit(PCOP(&pc_status),PIC_DC_BIT)) +#define emitCLRZ emitpcode(POC_BCF, popCopyGPR2Bit(PCOP(&pc_status),PIC_Z_BIT)) +#define emitCLRC emitpcode(POC_BCF, popCopyGPR2Bit(PCOP(&pc_status),PIC_C_BIT)) +#define emitCLRDC emitpcode(POC_BCF, popCopyGPR2Bit(PCOP(&pc_status),PIC_DC_BIT)) +#define emitCLRIRP emitpcode(POC_BCF, popCopyGPR2Bit(PCOP(&pc_status),PIC_IRP_BIT)) +#define emitSETZ emitpcode(POC_BSF, popCopyGPR2Bit(PCOP(&pc_status),PIC_Z_BIT)) +#define emitSETC emitpcode(POC_BSF, popCopyGPR2Bit(PCOP(&pc_status),PIC_C_BIT)) +#define emitSETDC emitpcode(POC_BSF, popCopyGPR2Bit(PCOP(&pc_status),PIC_DC_BIT)) +#define emitSETIRP emitpcode(POC_BSF, popCopyGPR2Bit(PCOP(&pc_status),PIC_IRP_BIT)) + +int pic14_getDataSize(operand *op); +void emitpcode_real(PIC_OPCODE poc, pCodeOp *pcop); +#define emitpcode(poc,pcop) do { if (options.debug || debug_verbose) { emitpComment (" >>> %s:%d:%s", __FILE__, __LINE__, __FUNCTION__); } emitpcode_real(poc,pcop); } while(0) +void emitpComment (const char *fmt, ...); +void emitpLabel(int key); +void pic14_emitcode (const char *inst, const char *fmt, ...); +void DEBUGpic14_emitcode (const char *inst, const char *fmt, ...); +void pic14_emitDebuggerSymbol (const char *); +bool pic14_sameRegs (asmop *aop1, asmop *aop2 ); +char *aopGet (asmop *aop, int offset, bool bit16, bool dname); +void DEBUGpic14_AopType(int line_no, operand *left, operand *right, operand *result); +void genpic14Code (iCode *lic); + + +pCodeOp *popGet (asmop *aop, int offset);//, bool bit16, bool dname); +pCodeOp *popGetAddr (asmop *aop, int offset, int index); +pCodeOp *popGetExternal (const char *str, int isReg); +pCodeOp *popGetLabel(unsigned int key); +pCodeOp *popGetLit(unsigned int lit); + + +void aopPut (asmop *aop, const char *s, int offset); +void pic14_outAcc(operand *result); +void aopOp (operand *op, iCode *ic, bool result); +void freeAsmop (operand *op, asmop *aaop, iCode *ic, bool pop); +void mov2w (asmop *aop, int offset); +int op_isLitLike (operand *op); + +/* + * From genarith.c: + */ +const char *AopType(short type); +const char *pCodeOpType(pCodeOp *pcop); +void genPlus (iCode *ic); +void addSign(operand *result, int offset, int sign); +void genMinus (iCode *ic); + +#endif diff --git a/src/pic14/genarith.c b/src/pic14/genarith.c new file mode 100644 index 0000000..2c9936e --- /dev/null +++ b/src/pic14/genarith.c @@ -0,0 +1,1289 @@ +/*------------------------------------------------------------------------- + genarith.c - source file for code generation - arithmetic + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + and - Jean-Louis VERN.jlvern@writeme.com (1999) + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + PIC port - Scott Dattalo scott@dattalo.com (2000) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! + + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "newalloc.h" +//#include "SDCCglobl.h" +//#include "SDCCpeeph.h" + +#include "gen.h" +#include "pcode.h" +#include "ralloc.h" + + +#define BYTEofLONG(l,b) ( (l>> (b<<3)) & 0xff) + +const char *AopType(short type) +{ + switch(type) { + case AOP_LIT: + return "AOP_LIT"; + break; + case AOP_REG: + return "AOP_REG"; + break; + case AOP_DIR: + return "AOP_DIR"; + break; + case AOP_STK: + return "AOP_STK"; + break; + case AOP_IMMD: + return "AOP_IMMD"; + break; + case AOP_STR: + return "AOP_STR"; + break; + case AOP_CRY: + return "AOP_CRY"; + break; + case AOP_PCODE: + return "AOP_PCODE"; + break; + } + + return "BAD TYPE"; +} + +const char *pCodeOpType(pCodeOp *pcop) +{ + + if(pcop) { + + switch(pcop->type) { + + case PO_NONE: + return "PO_NONE"; + case PO_W: + return "PO_W"; + case PO_STATUS: + return "PO_STATUS"; + case PO_FSR: + return "PO_FSR"; + case PO_INDF: + return "PO_INDF"; + case PO_INTCON: + return "PO_INTCON"; + case PO_GPR_REGISTER: + return "PO_GPR_REGISTER"; + case PO_GPR_POINTER: + return "PO_GPR_POINTER"; + case PO_GPR_BIT: + return "PO_GPR_BIT"; + case PO_GPR_TEMP: + return "PO_GPR_TEMP"; + case PO_SFR_REGISTER: + return "PO_SFR_REGISTER"; + case PO_PCL: + return "PO_PCL"; + case PO_PCLATH: + return "PO_PCLATH"; + case PO_LITERAL: + return "PO_LITERAL"; + case PO_IMMEDIATE: + return "PO_IMMEDIATE"; + case PO_DIR: + return "PO_DIR"; + case PO_CRY: + return "PO_CRY"; + case PO_BIT: + return "PO_BIT"; + case PO_STR: + return "PO_STR"; + case PO_LABEL: + return "PO_LABEL"; + case PO_WILD: + return "PO_WILD"; + } + } + + return "BAD PO_TYPE"; +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool genPlusIncr (iCode *ic) +{ + unsigned int icount ; + unsigned int size = pic14_getDataSize(IC_RESULT(ic)); + FENTRY; + + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + DEBUGpic14_emitcode ("; ","result %s, left %s, right %s", + AopType(AOP_TYPE(IC_RESULT(ic))), + AopType(AOP_TYPE(IC_LEFT(ic))), + AopType(AOP_TYPE(IC_RIGHT(ic)))); + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) + return FALSE ; + + DEBUGpic14_emitcode ("; ","%s %d",__FUNCTION__,__LINE__); + /* if the literal value of the right hand side + is greater than 1 then it is faster to add */ + if ((icount = (unsigned int) ulFromVal (AOP(IC_RIGHT(ic))->aopu.aop_lit)) > 2) + return FALSE ; + + /* if increment 16 bits in register */ + if (pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) && + (icount == 1)) { + + int offset = MSB16; + + emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),LSB)); + //pic14_emitcode("incf","%s,f",aopGet(AOP(IC_RESULT(ic)),LSB,FALSE,FALSE)); + + while(--size) { + emitSKPNZ; + emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),offset++)); + //pic14_emitcode(" incf","%s,f",aopGet(AOP(IC_RESULT(ic)),offset++,FALSE,FALSE)); + } + + return TRUE; + } + + DEBUGpic14_emitcode ("; ","%s %d",__FUNCTION__,__LINE__); + /* if left is in accumulator - probably a bit operation*/ + if( strcmp(aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE),"a") && + (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) ) { + + emitpcode(POC_BCF, popGet(AOP(IC_RESULT(ic)),0)); + pic14_emitcode("bcf","(%s >> 3), (%s & 7)", + AOP(IC_RESULT(ic))->aopu.aop_dir, + AOP(IC_RESULT(ic))->aopu.aop_dir); + if(icount) + emitpcode(POC_XORLW,popGetLit(1)); + //pic14_emitcode("xorlw","1"); + else + emitpcode(POC_ANDLW,popGetLit(1)); + //pic14_emitcode("andlw","1"); + + emitSKPZ; + emitpcode(POC_BSF, popGet(AOP(IC_RESULT(ic)),0)); + pic14_emitcode("bsf","(%s >> 3), (%s & 7)", + AOP(IC_RESULT(ic))->aopu.aop_dir, + AOP(IC_RESULT(ic))->aopu.aop_dir); + + return TRUE; + } + + + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE(IC_RESULT(ic)) > 1 || + AOP_SIZE(IC_LEFT(ic)) > 1 ) + return FALSE ; + + /* If we are incrementing the same register by two: */ + + if (pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + + while (icount--) + emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),0)); + //pic14_emitcode("incf","%s,f",aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + return TRUE ; + } + + DEBUGpic14_emitcode ("; ","couldn't increment "); + + return FALSE ; +} + +/*-----------------------------------------------------------------*/ +/* genAddlit - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void genAddLit2byte (operand *result, int offr, int lit) +{ + FENTRY; + + switch(lit & 0xff) { + case 0: + break; + case 1: + emitpcode(POC_INCF, popGet(AOP(result),offr)); + break; + case 0xff: + emitpcode(POC_DECF, popGet(AOP(result),offr)); + break; + default: + emitpcode(POC_MOVLW,popGetLit(lit&0xff)); + emitpcode(POC_ADDWF,popGet(AOP(result),offr)); + } + +} + +static void emitMOVWF(operand *reg, int offset) +{ + FENTRY; + if(!reg) + return; + + emitpcode(POC_MOVWF, popGet(AOP(reg),offset)); + +} + +static void genAddLit (iCode *ic, int lit) +{ + int size, same; + int lo; + + operand *result; + operand *left; + + FENTRY; + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + left = IC_LEFT(ic); + result = IC_RESULT(ic); + same = pic14_sameRegs(AOP(left), AOP(result)); + size = pic14_getDataSize(result); + if (size > pic14_getDataSize(left)) + { + size = pic14_getDataSize(left); + } + + /* + * Fix accessing libsdcc/<*>/idata.c:_cinit in __code space. + */ + if (AOP_PCODE == AOP_TYPE(IC_LEFT(ic))) + { + int u; + if (debug_verbose) + { + printf("%s:%u: CHECK: using address of '%s' instead of contents\n", + ic->filename, ic->lineno, + popGetAddr(AOP(IC_LEFT(ic)), 0, lit & 0xff)->name); + } // if + for (u = 0; u < size; ++u) + { + emitpcode(POC_MOVLW, popGetAddr(AOP(IC_LEFT(ic)), u, lit)); + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)), u)); + } // for + + if (size < pic14_getDataSize(result)) + { + for (u = size; u < pic14_getDataSize(result); ++u) + { + /* XXX: Might fail for u >= size?!? */ + emitpcode(POC_MOVLW, popGetAddr(AOP(IC_LEFT(ic)), u, lit)); + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)), u)); + } // for + } // if + + goto out; + } // if + + if (same) + { + /* Handle special cases first */ + if (size == 1) + { + genAddLit2byte (result, 0, lit); + } + else if (size == 2) + { + int hi = (lit >> 8) & 0xff; + lo = lit & 0xff; + + switch (hi) + { + case 0: + /* lit = 0x00LL */ + DEBUGpic14_emitcode ("; hi = 0","%s %d",__FUNCTION__,__LINE__); + switch (lo) + { + case 0: + break; + case 1: + emitpcode(POC_INCF, popGet(AOP(result),0)); + emitSKPNZ; + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + case 0xff: + emitpcode(POC_DECF, popGet(AOP(result),0)); + emitpcode(POC_INCFSZW, popGet(AOP(result),0)); + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + default: + emitpcode(POC_MOVLW,popGetLit(lit&0xff)); + emitpcode(POC_ADDWF,popGet(AOP(result),0)); + emitSKPNC; + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + } // switch + break; + + case 1: + /* lit = 0x01LL */ + DEBUGpic14_emitcode ("; hi = 1","%s %d",__FUNCTION__,__LINE__); + switch (lo) + { + case 0: /* 0x0100 */ + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + case 1: /* 0x0101 */ + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + emitpcode(POC_INCF, popGet(AOP(result),0)); + emitSKPNZ; + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + case 0xff: /* 0x01ff */ + emitpcode(POC_DECF, popGet(AOP(result),0)); + emitpcode(POC_INCFSZW, popGet(AOP(result),0)); + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + default: + emitpcode(POC_MOVLW, popGetLit(lo)); + emitpcode(POC_ADDWF, popGet(AOP(result),0)); + emitSKPNC; + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + emitpcode(POC_INCF, popGet(AOP(result),MSB16)); + break; + } // switch + break; + + case 0xff: + DEBUGpic14_emitcode ("; hi = ff","%s %d",__FUNCTION__,__LINE__); + /* lit = 0xffLL */ + switch (lo) + { + case 0: /* 0xff00 */ + emitpcode(POC_DECF, popGet(AOP(result),MSB16)); + break; + case 1: /*0xff01 */ + emitpcode(POC_INCFSZ, popGet(AOP(result),0)); + emitpcode(POC_DECF, popGet(AOP(result),MSB16)); + break; + default: + emitpcode(POC_MOVLW,popGetLit(lo)); + emitpcode(POC_ADDWF,popGet(AOP(result),0)); + emitSKPC; + emitpcode(POC_DECF, popGet(AOP(result),MSB16)); + break; + } // switch + break; + + default: + DEBUGpic14_emitcode ("; hi is generic","%d %s %d",hi,__FUNCTION__,__LINE__); + + /* lit = 0xHHLL */ + switch (lo) + { + case 0: /* 0xHH00 */ + genAddLit2byte (result, MSB16, hi); + break; + case 1: /* 0xHH01 */ + emitpcode(POC_MOVLW,popGetLit((hi+1)&0xff)); + emitpcode(POC_INCFSZ, popGet(AOP(result),0)); + emitpcode(POC_MOVLW,popGetLit(hi)); + emitpcode(POC_ADDWF,popGet(AOP(result),MSB16)); + break; + default: /* 0xHHLL */ + emitpcode(POC_MOVLW,popGetLit(lo)); + emitpcode(POC_ADDWF, popGet(AOP(result),0)); + emitpcode(POC_MOVLW,popGetLit(hi)); + emitSKPNC; + emitpcode(POC_MOVLW,popGetLit((hi+1) & 0xff)); + emitpcode(POC_ADDWF,popGet(AOP(result),MSB16)); + break; + } // switch + break; + } // switch + } + else + { + int carry_info = 0; + int offset = 0; + /* size > 2 */ + DEBUGpic14_emitcode ("; add lit to long","%s %d",__FUNCTION__,__LINE__); + + while (size--) + { + lo = BYTEofLONG(lit,0); + + if (carry_info) + { + switch (lo) + { + case 0: + switch (carry_info) + { + case 1: + emitSKPNZ; + emitpcode(POC_INCF, popGet(AOP(result),offset)); + break; + case 2: + emitpcode(POC_RLFW, popGet(AOP(result),offset)); + emitpcode(POC_ANDLW,popGetLit(1)); + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + break; + default: /* carry_info = 3 */ + emitSKPNC; + emitpcode(POC_INCF, popGet(AOP(result),offset)); + carry_info = 1; + break; + } // switch + break; + case 0xff: + emitpcode(POC_MOVLW,popGetLit(lo)); + if (carry_info==1) + emitSKPZ; + else + emitSKPC; + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + break; + default: + emitpcode(POC_MOVLW,popGetLit(lo)); + if (carry_info==1) + emitSKPNZ; + else + emitSKPNC; + emitpcode(POC_MOVLW,popGetLit(lo+1)); + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + carry_info=2; + break; + } // switch + } + else + { + /* no carry info from previous step */ + /* this means this is the first time to add */ + switch (lo) + { + case 0: + break; + case 1: + emitpcode(POC_INCF, popGet(AOP(result),offset)); + carry_info=1; + break; + default: + emitpcode(POC_MOVLW,popGetLit(lo)); + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + if (lit <0x100) + carry_info = 3; /* Were adding only one byte and propogating the carry */ + else + carry_info = 2; + break; + } // switch + } // if + offset++; + lit >>= 8; + } // while + } // if + } + else + { + int offset = 1; + DEBUGpic14_emitcode ("; left and result aren't same","%s %d",__FUNCTION__,__LINE__); + + if (size == 1) + { + /* left addend is in a register */ + switch (lit & 0xff) + { + case 0: + emitpcode(POC_MOVFW, popGet(AOP(left),0)); + emitMOVWF(result,0); + break; + case 1: + emitpcode(POC_INCFW, popGet(AOP(left),0)); + emitMOVWF(result,0); + break; + case 0xff: + emitpcode(POC_DECFW, popGet(AOP(left),0)); + emitMOVWF(result,0); + break; + default: + emitpcode(POC_MOVLW, popGetLit(lit & 0xff)); + emitpcode(POC_ADDFW, popGet(AOP(left),0)); + emitMOVWF(result,0); + } // switch + } + else + { + int clear_carry=0; + + /* left is not the accumulator */ + if (lit & 0xff) + { + emitpcode(POC_MOVLW, popGetLit(lit & 0xff)); + emitpcode(POC_ADDFW, popGet(AOP(left),0)); + } + else + { + emitpcode(POC_MOVFW, popGet(AOP(left),0)); + /* We don't know the state of the carry bit at this point */ + clear_carry = 1; + } // if + emitMOVWF(result,0); + while (--size) + { + lit >>= 8; + if (lit & 0xff) + { + if (clear_carry) + { + /* The ls byte of the lit must've been zero - that + means we don't have to deal with carry */ + + emitpcode(POC_MOVLW, popGetLit(lit & 0xff)); + emitpcode(POC_ADDFW, popGet(AOP(left),offset)); + emitpcode(POC_MOVWF, popGet(AOP(result),offset)); + + clear_carry = 0; + } + else + { + emitpcode(POC_MOVLW, popGetLit(lit & 0xff)); + emitMOVWF(result,offset); + emitpcode(POC_MOVFW, popGet(AOP(left),offset)); + emitSKPNC; + emitpcode(POC_INCFSZW,popGet(AOP(left),offset)); + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + } // if + } + else + { + emitpcode(POC_CLRF, popGet(AOP(result),offset)); + emitpcode(POC_RLF, popGet(AOP(result),offset)); + emitpcode(POC_MOVFW, popGet(AOP(left),offset)); + emitpcode(POC_ADDWF, popGet(AOP(result),offset)); + } // if + offset++; + } // while + } // if + } // if + +out: + size = pic14_getDataSize(result); + if (size > pic14_getDataSize(left)) + { + size = pic14_getDataSize(left); + } // if + addSign(result, size, 0); +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +void genPlus (iCode *ic) +{ + int size, offset = 0; + + /* special cases :- */ + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + FENTRY; + + aopOp (IC_LEFT(ic),ic,FALSE); + aopOp (IC_RIGHT(ic),ic,FALSE); + aopOp (IC_RESULT(ic),ic,TRUE); + + DEBUGpic14_AopType(__LINE__,IC_LEFT(ic),IC_RIGHT(ic),IC_RESULT(ic)); + + /* if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + + if (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) { + operand *t = IC_RIGHT(ic); + IC_RIGHT(ic) = IC_LEFT(ic); + IC_LEFT(ic) = t; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE(IC_LEFT(ic)) == AOP_CRY && + AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT) { + /* if result in bit space */ + if(AOP_TYPE(IC_RESULT(ic)) == AOP_CRY){ + if(ulFromVal (AOP(IC_RIGHT(ic))->aopu.aop_lit) != 0L) { + emitpcode(POC_MOVLW, popGet(AOP(IC_RESULT(ic)),0)); + if (!pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) + emitpcode(POC_BTFSC, popGet(AOP(IC_LEFT(ic)),0)); + emitpcode(POC_XORWF, popGet(AOP(IC_RESULT(ic)),0)); + } + } else { + size = pic14_getDataSize(IC_RESULT(ic)); + while (size--) { + MOVA(aopGet(AOP(IC_RIGHT(ic)),offset,FALSE,FALSE)); + pic14_emitcode("addc","a,#00 ;%d",__LINE__); + aopPut(AOP(IC_RESULT(ic)),"a",offset++); + } + } + goto release ; + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = pic14_getDataSize(IC_RESULT(ic)); + + if(AOP(IC_RIGHT(ic))->type == AOP_LIT) { + /* Add a literal to something else */ + unsigned lit = (unsigned) ulFromVal (AOP(IC_RIGHT(ic))->aopu.aop_lit); + DEBUGpic14_emitcode(";","adding lit to something. size %d",size); + + genAddLit (ic, lit); + goto release; + + } else if(AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { + + pic14_emitcode(";bitadd","right is bit: %s",aopGet(AOP(IC_RIGHT(ic)),0,FALSE,FALSE)); + pic14_emitcode(";bitadd","left is bit: %s",aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE)); + pic14_emitcode(";bitadd","result is bit: %s",aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + /* here we are adding a bit to a char or int */ + if(size == 1) { + if (pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + emitpcode(POC_BTFSC , popGet(AOP(IC_RIGHT(ic)),0)); + emitpcode(POC_INCF , popGet(AOP(IC_RESULT(ic)),0)); + } else { + + emitpcode(POC_MOVFW , popGet(AOP(IC_LEFT(ic)),0)); + emitpcode(POC_BTFSC , popGet(AOP(IC_RIGHT(ic)),0)); + emitpcode(POC_INCFW , popGet(AOP(IC_LEFT(ic)),0)); + + if(AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + emitpcode(POC_ANDLW , popGetLit(1)); + emitpcode(POC_BCF , popGet(AOP(IC_RESULT(ic)),0)); + emitSKPZ; + emitpcode(POC_BSF , popGet(AOP(IC_RESULT(ic)),0)); + } else { + emitpcode(POC_MOVWF , popGet(AOP(IC_RESULT(ic)),0)); + } + } + + } else { + int offset = 1; + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if (pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + emitCLRZ; + emitpcode(POC_BTFSC, popGet(AOP(IC_RIGHT(ic)),0)); + emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),0)); + } else { + + emitpcode(POC_MOVFW, popGet(AOP(IC_LEFT(ic)),0)); + emitpcode(POC_BTFSC, popGet(AOP(IC_RIGHT(ic)),0)); + emitpcode(POC_INCFW, popGet(AOP(IC_LEFT(ic)),0)); + emitMOVWF(IC_RIGHT(ic),0); + } + + while(--size){ + emitSKPZ; + emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),offset++)); + } + + } + + } else { + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + /* Add the first bytes */ + + if(strcmp(aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE),"a") == 0 ) { + emitpcode(POC_ADDFW, popGet(AOP(IC_RIGHT(ic)),0)); + emitpcode(POC_MOVWF,popGet(AOP(IC_RESULT(ic)),0)); + } else { + + emitpcode(POC_MOVFW,popGet(AOP(IC_RIGHT(ic)),0)); + + if (pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) + emitpcode(POC_ADDWF, popGet(AOP(IC_LEFT(ic)),0)); + else { + PIC_OPCODE poc = POC_ADDFW; + + if (op_isLitLike (IC_LEFT (ic))) + poc = POC_ADDLW; + emitpcode(poc, popGetAddr(AOP(IC_LEFT(ic)),0,0)); + emitpcode(POC_MOVWF,popGet(AOP(IC_RESULT(ic)),0)); + } + } + + size = min( AOP_SIZE(IC_RESULT(ic)), AOP_SIZE(IC_RIGHT(ic))) - 1; + offset = 1; + + + if(size){ + if (pic14_sameRegs(AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic)))) { + if (op_isLitLike (IC_LEFT(ic))) + { + while(size--){ + emitpcode(POC_MOVFW, popGet(AOP(IC_RIGHT(ic)),offset)); + emitSKPNC; + emitpcode(POC_INCFSZW, popGet(AOP(IC_RIGHT(ic)),offset)); + emitpcode(POC_ADDLW, popGetAddr(AOP(IC_LEFT(ic)),offset,0)); + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)),offset)); + offset++; + } + } else { + while(size--){ + emitpcode(POC_MOVFW, popGet(AOP(IC_LEFT(ic)),offset)); + emitSKPNC; + emitpcode(POC_INCFSZW, popGet(AOP(IC_LEFT(ic)),offset)); + emitpcode(POC_ADDWF, popGet(AOP(IC_RESULT(ic)),offset)); + offset++; + } + } + } else { + PIC_OPCODE poc = POC_MOVFW; + if (op_isLitLike (IC_LEFT(ic))) + poc = POC_MOVLW; + while(size--){ + if (!pic14_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + emitpcode(poc, popGetAddr(AOP(IC_LEFT(ic)),offset,0)); + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)),offset)); + } + emitpcode(POC_MOVFW, popGet(AOP(IC_RIGHT(ic)),offset)); + emitSKPNC; + emitpcode(POC_INCFSZW, popGet(AOP(IC_RIGHT(ic)),offset)); + emitpcode(POC_ADDWF, popGet(AOP(IC_RESULT(ic)),offset)); + offset++; + } + } + } + } + + if (AOP_SIZE(IC_RESULT(ic)) > AOP_SIZE(IC_RIGHT(ic))) { + int sign = !(SPEC_USIGN(getSpec(operandType(IC_LEFT(ic)))) | + SPEC_USIGN(getSpec(operandType(IC_RIGHT(ic)))) ); + + + /* Need to extend result to higher bytes */ + size = AOP_SIZE(IC_RESULT(ic)) - AOP_SIZE(IC_RIGHT(ic)) - 1; + + /* First grab the carry from the lower bytes */ + if (AOP_SIZE(IC_LEFT(ic)) > AOP_SIZE(IC_RIGHT(ic))) { + int leftsize = AOP_SIZE(IC_LEFT(ic)) - AOP_SIZE(IC_RIGHT(ic)); + PIC_OPCODE poc = POC_MOVFW; + if (op_isLitLike (IC_LEFT(ic))) + poc = POC_MOVLW; + while(leftsize-- > 0) { + emitpcode(poc, popGetAddr(AOP(IC_LEFT(ic)),offset,0)); + emitSKPNC; + emitpcode(POC_ADDLW, popGetLit(0x01)); + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)),offset)); + //emitSKPNC; + //emitpcode(POC_INCF, popGet(AOP(IC_RESULT(ic)),offset)); /* INCF does not update Carry! */ + offset++; + if (size) + size--; + else + break; + } + } else { + emitpcode(POC_CLRF, popGet(AOP(IC_RESULT(ic)),offset)); + emitpcode(POC_RLF, popGet(AOP(IC_RESULT(ic)),offset)); + } + + + if(sign && offset > 0 && offset < AOP_SIZE(IC_RESULT(ic))) { + /* Now this is really horrid. Gotta check the sign of the addends and propogate + * to the result */ + + emitpcode(POC_BTFSC, newpCodeOpBit(aopGet(AOP(IC_LEFT(ic)),offset-1,FALSE,FALSE),7,0)); + emitpcode(POC_DECF, popGet(AOP(IC_RESULT(ic)),offset)); + emitpcode(POC_BTFSC, newpCodeOpBit(aopGet(AOP(IC_RIGHT(ic)),offset-1,FALSE,FALSE),7,0)); + emitpcode(POC_DECF, popGet(AOP(IC_RESULT(ic)),offset)); + + /* if chars or ints or being signed extended to longs: */ + if(size) { + emitpcode(POC_MOVLW, popGetLit(0)); + emitpcode(POC_BTFSC, newpCodeOpBit(aopGet(AOP(IC_RESULT(ic)),offset,FALSE,FALSE),7,0)); + emitpcode(POC_MOVLW, popGetLit(0xff)); + } + } + + offset++; + while(size--) { + + if(sign) + emitpcode(POC_MOVWF, popGet(AOP(IC_RESULT(ic)),offset)); + else + emitpcode(POC_CLRF, popGet(AOP(IC_RESULT(ic)),offset)); + + offset++; + } + } + + + //adjustArithmeticResult(ic); + +release: + freeAsmop(IC_LEFT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + freeAsmop(IC_RIGHT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + freeAsmop(IC_RESULT(ic),NULL,ic,TRUE); +} + +/*-----------------------------------------------------------------*/ +/* addSign - propogate sign bit to higher bytes */ +/*-----------------------------------------------------------------*/ +void addSign(operand *result, int offset, int sign) +{ + int size = (pic14_getDataSize(result) - offset); + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + FENTRY; + + if(size > 0){ + if(sign && offset) { + + if(size == 1) { + emitpcode(POC_CLRF,popGet(AOP(result),offset)); + emitpcode(POC_BTFSC,newpCodeOpBit(aopGet(AOP(result),offset-1,FALSE,FALSE),7,0)); + emitpcode(POC_DECF, popGet(AOP(result),offset)); + } else { + + emitpcode(POC_MOVLW, popGetLit(0)); + emitpcode(POC_BTFSC, newpCodeOpBit(aopGet(AOP(result),offset-1,FALSE,FALSE),7,0)); + emitpcode(POC_MOVLW, popGetLit(0xff)); + while(size--) + emitpcode(POC_MOVWF, popGet(AOP(result),offset+size)); + } + } else + while(size--) + emitpcode(POC_CLRF,popGet(AOP(result),offset++)); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +void genMinus (iCode *ic) +{ + int size, offset = 0, same=0; + unsigned long lit = 0L; + int isLit; + symbol *lbl_comm, *lbl_next; + asmop *left, *right, *result; + + FENTRY; + DEBUGpic14_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + aopOp (IC_LEFT(ic),ic,FALSE); + aopOp (IC_RIGHT(ic),ic,FALSE); + aopOp (IC_RESULT(ic),ic,TRUE); + + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && + AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT) { + operand *t = IC_RIGHT(ic); + IC_RIGHT(ic) = IC_LEFT(ic); + IC_LEFT(ic) = t; + } + + DEBUGpic14_emitcode ("; ","result %s, left %s, right %s", + AopType(AOP_TYPE(IC_RESULT(ic))), + AopType(AOP_TYPE(IC_LEFT(ic))), + AopType(AOP_TYPE(IC_RIGHT(ic)))); + + left = AOP(IC_LEFT(ic)); + right = AOP(IC_RIGHT(ic)); + result = AOP(IC_RESULT(ic)); + + size = pic14_getDataSize(IC_RESULT(ic)); + same = pic14_sameRegs(right, result); + + if((AOP_TYPE(IC_LEFT(ic)) != AOP_LIT) + && (pic14_getDataSize(IC_LEFT(ic)) < size)) + { + fprintf(stderr, "%s:%d(%s):WARNING: left operand too short for result\n", + ic->filename, ic->lineno, __FUNCTION__); + } // if + if((AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) + && (pic14_getDataSize(IC_RIGHT(ic)) < size)) + { + fprintf(stderr, "%s:%d(%s):WARNING: right operand too short for result\n", + ic->filename, ic->lineno, __FUNCTION__ ); + } // if + + if(AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT) { + /* Add a literal to something else */ + + lit = ulFromVal(right->aopu.aop_lit); + lit = -(long)lit; + + genAddLit(ic, lit); + + } else if(AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { + // bit subtraction + + pic14_emitcode(";bitsub","right is bit: %s",aopGet(right,0,FALSE,FALSE)); + pic14_emitcode(";bitsub","left is bit: %s",aopGet(left,0,FALSE,FALSE)); + pic14_emitcode(";bitsub","result is bit: %s",aopGet(result,0,FALSE,FALSE)); + + /* here we are subtracting a bit from a char or int */ + if(size == 1) { + if(pic14_sameRegs(left, result)) { + + emitpcode(POC_BTFSC, popGet(right, 0)); + emitpcode(POC_DECF , popGet(result, 0)); + + } else { + + if( (AOP_TYPE(IC_LEFT(ic)) == AOP_IMMD) || + (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) ) { + /* + * result = literal - bit + * + * XXX: probably fails for AOP_IMMDs! + */ + + lit = ulFromVal (left->aopu.aop_lit); + + if(AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + if(pic14_sameRegs(right, result)) { + if(lit & 1) { + emitpcode(POC_MOVLW, popGet(right, 0)); + emitpcode(POC_XORWF, popGet(result, 0)); + } + } else { + emitpcode(POC_BCF, popGet(result, 0)); + if(lit & 1) + emitpcode(POC_BTFSS, popGet(right, 0)); + else + emitpcode(POC_BTFSC, popGet(right, 0)); + emitpcode(POC_BSF, popGet(result, 0)); + } + goto release; + } else { + emitpcode(POC_MOVLW, popGetLit(lit & 0xff)); + emitpcode(POC_BTFSC, popGet(right, 0)); + emitpcode(POC_MOVLW, popGetLit((lit-1) & 0xff)); + emitpcode(POC_MOVWF, popGet(result, 0)); + } + + } else { + // result = register - bit + // XXX: Fails for lit-like left operands + emitpcode(POC_MOVFW, popGet(left, 0)); + emitpcode(POC_BTFSC, popGet(right, 0)); + emitpcode(POC_DECFW, popGet(left, 0)); + emitpcode(POC_MOVWF, popGet(result, 0)); + } + } + } else { + fprintf(stderr, "WARNING: subtracting bit from multi-byte operands is incomplete.\n"); + //exit(EXIT_FAILURE); + } // if + } else { + /* + * RIGHT is not a literal and not a bit operand, + * LEFT is unknown (register, literal, bit, ...) + */ + lit = 0; + isLit = 0; + + if(AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) { + lit = ulFromVal (left->aopu.aop_lit); + isLit = 1; + DEBUGpic14_emitcode ("; left is lit","line %d result %s, left %s, right %s",__LINE__, + AopType(AOP_TYPE(IC_RESULT(ic))), + AopType(AOP_TYPE(IC_LEFT(ic))), + AopType(AOP_TYPE(IC_RIGHT(ic)))); + } // if + + + /* + * First byte, no carry-in. + * Any optimizations that are longer than 2 instructions are + * useless. + */ + if(same && isLit && ((lit & 0xff) == 0xff)) { + // right === res = 0xFF - right = ~right + emitpcode(POC_COMF, popGet(right, 0)); + if(size > 1) { + // setup CARRY/#BORROW + emitSETC; + } // if + } else if((size == 1) && isLit && ((lit & 0xff) == 0xff)) { + // res = 0xFF - right = ~right + emitpcode(POC_COMFW, popGet(right, 0)); + emitpcode(POC_MOVWF, popGet(result, 0)); + // CARRY/#BORROW is not setup correctly + } else if((size == 1) && same && isLit && ((lit & 0xff) == 0)) { + // right === res = 0 - right = ~right + 1 + emitpcode(POC_COMF, popGet(right, 0)); + emitpcode(POC_INCF, popGet(right, 0)); + // CARRY/#BORROW is not setup correctly + } else { + // general case, should always work + mov2w(right, 0); + if (pic14_sameRegs(left, result)) { + // result === left = left - right (in place) + emitpcode(POC_SUBWF, popGet(result, 0)); + } else { + // works always: result = left - right + emitpcode(op_isLitLike(IC_LEFT(ic)) + ? POC_SUBLW : POC_SUBFW, + popGetAddr(left, 0, 0)); + emitpcode(POC_MOVWF, popGet(result, 0)); + } // if + } // if + + /* + * Now for the remaining bytes with carry-in (and carry-out). + */ + offset = 0; + while(--size) { + lit >>= 8; + offset++; + + /* + * The following code generation templates are ordered + * according to increasing length; the first template + * that matches will thus be the shortest and produce + * the best code possible with thees templates. + */ + + if(pic14_sameRegs(left, right)) { + /* + * This case should not occur; however, the + * template works if LEFT, RIGHT, and RESULT are + * register operands and LEFT and RIGHT are the + * same register(s) and at least as long as the + * result. + * + * CLRF result + * + * 1 cycle + */ + emitpcode(POC_CLRF, popGet(result, offset)); + } else if(pic14_sameRegs(left, result)) { + /* + * This template works if LEFT, RIGHT, and + * RESULT are register operands and LEFT and + * RESULT are the same register(s). + * + * MOVF right, W ; W := right + * BTFSS STATUS, C + * INCFSZ right, W ; W := right + BORROW + * SUBWF result, F ; res === left := left - (right + BORROW) + * + * The SUBWF *must* be skipped if we have a + * BORROW bit and right == 0xFF in order to + * keep the BORROW bit intact! + * + * 4 cycles + */ + mov2w(right, offset); + emitSKPC; + emitpcode(POC_INCFSZW, popGet(right, offset)); + emitpcode(POC_SUBWF, popGet(result, offset)); + } else if((size == 1) && isLit && ((lit & 0xff) == 0xff)) { + /* + * This template works for the last byte (MSB) of + * the subtraction and ignores correct propagation + * of the outgoing BORROW bit. RIGHT and RESULT + * must be register operands, LEFT must be the + * literal 0xFF. + * + * (The case LEFT === RESULT is optimized above.) + * + * 0xFF - right - BORROW = ~right - BORROW + * + * COMF right, W ; W := 0xff - right + * BTFSS STATUS, C + * ADDLW 0xFF ; W := 0xff - right - BORROW + * MOVWF result + * + * 4 cycles + */ + emitpcode(POC_COMFW, popGet(right, offset)); + emitSKPC; + emitpcode(POC_ADDLW, popGetLit(0xff)); + emitpcode(POC_MOVWF, popGet(result, offset)); + } else if(size == 1) { + /* + * This template works for the last byte (MSB) of + * the subtraction and ignores correct propagation + * of the outgoing BORROW bit. RIGHT and RESULT + * must be register operands, LEFT can be a + * register or a literal operand. + * + * (The case LEFT === RESULT is optimized above.) + * + * MOVF right, W ; W := right + * BTFSS STATUS, C + * INCF right, W ; W := right + BORROW + * SUBxW left, W ; W := left - right - BORROW + * MOVWF result + * + * 5 cycles + */ + mov2w(right, offset); + emitSKPC; + emitpcode(POC_INCFW, popGet(right, offset)); + emitpcode(op_isLitLike(IC_LEFT(ic)) + ? POC_SUBLW : POC_SUBFW, + popGetAddr(left, offset, 0)); + emitpcode(POC_MOVWF, popGet(result, offset)); + } else if(IS_ITEMP(IC_RESULT(ic)) + && !pic14_sameRegs(right, result)) { + /* + * This code template works if RIGHT and RESULT + * are different register operands and RESULT + * is not volatile/an SFR (written twice). + * + * ######################################### + * Since we cannot determine that for sure, + * we approximate via IS_ITEMP() for now. + * ######################################### + * + * MOVxW left, W ; copy left to result + * MOVWF result + * MOVF right, W ; W := right + * BTFSS STATUS, C + * INCFSZ right, W ; W := right + BORROW + * SUBWF result, F ; res === left := left - (right + BORROW) + * + * 6 cycles, but fails for SFR RESULT operands + */ + mov2w(left, offset); + emitpcode(POC_MOVWF, popGet(result, offset)); + mov2w(right, offset); + emitSKPC; + emitpcode(POC_INCFSZW, popGet(right, offset)); + emitpcode(POC_SUBWF, popGet(result, offset)); + } else if(!optimize.codeSize && isLit && ((lit & 0xff) != 0)) { + /* + * This template works if RIGHT and RESULT are + * register operands and LEFT is a literal + * operand != 0. + * + * MOVxW right, W + * BTFSC STATUS, C + * GOTO next + * SUBLW left-1 + * GOTO common + * next: + * SUBLW left + * common: + * MOVWF result + * + * 6 cycles, 7 iff BORROW + * (9 instructions) + */ + lbl_comm = newiTempLabel(NULL); + lbl_next = newiTempLabel(NULL); + + mov2w(right, offset); + emitSKPNC; + emitpcode(POC_GOTO, popGetLabel(lbl_next->key)); + emitpcode(POC_SUBLW, popGetLit((lit - 1) & 0xff)); + emitpcode(POC_GOTO, popGetLabel(lbl_comm->key)); + emitpLabel(lbl_next->key); + emitpcode(POC_SUBLW, popGetLit(lit & 0xff)); + emitpLabel(lbl_comm->key); + emitpcode(POC_MOVWF, popGet(result, offset)); + } else { + /* + * This code template works if RIGHT and RESULT + * are register operands. + * + * MOVF right, W ; W := right + * BTFSS STATUS, C + * INCFSZ right, W ; W := right + BORROW + * GOTO common + * MOVxW left ; if we subtract 0x100 = 0xFF + 1, ... + * GOTO next ; res := left, but keep BORROW intact + * common: + * SUBxW left, W ; W := left - (right + BORROW) + * next: + * MOVW result ; res := left - (right + BORROW) + * + * 7 cycles, 8 iff BORROW and (right == 0xFF) + * (8 instructions) + * + * + * + * Alternative approach using -x = ~x + 1 ==> ~x = -x - 1 = -(x + 1) + * + * COMFW right, W ; W := -right - (assumed BORROW) + * BTFSC STATUS, C ; SKIP if we have a BORROW + * ADDLW 1 ; W := -right (no BORROW) + * BTFSC STATUS, C ; (***) + * MOVLW left ; (***) + * BTFSS STATUS, C ; (***) + * ADDFW left, W ; W := left - right - BORROW (if any) + * MOVWF result ; result := left - right - BORROW (if any) + * + * 8 cycles + * + * Case A: C=0 (BORROW) + * r=0x00, W=0xFF, W=left+0xFF, C iff left>0x00 + * r=0x01, W=0xFE, W=left+0xFE, C iff left>0x01 + * r=0xFE, W=0x01, W=left+0x01, C iff left>0xFE + * r=0xFF, W=0x00, W=left+0x00, C iff left>0xFF + * + * Case B: C=1 (no BORROW) + * r=0x00, W=0xFF, W=0x00/C=1, W=left+0x00, C iff left>=0x100 (***) + * r=0x01, W=0xFE, W=0xFF/C=0, W=left+0xFF, C iff left>=0x01 + * r=0xFE, W=0x01, W=0x02/C=0, W=left+0x02, C iff left>=0xFE + * r=0xFF, W=0x00, W=0x01/C=0, W=left+0x01, C iff left>=0xFF + */ + lbl_comm = newiTempLabel(NULL); + lbl_next = newiTempLabel(NULL); + + mov2w(right, offset); + emitSKPC; + emitpcode(POC_INCFSZW, popGet(right, offset)); + emitpcode(POC_GOTO, popGetLabel(lbl_comm->key)); + mov2w(left, offset); + emitpcode(POC_GOTO, popGetLabel(lbl_next->key)); + emitpLabel(lbl_comm->key); + emitpcode(op_isLitLike(IC_LEFT(ic)) + ? POC_SUBLW : POC_SUBFW, + popGetAddr(left, offset, 0)); + emitpLabel(lbl_next->key); + emitpcode(POC_MOVWF, popGet(result, offset)); + } // if + } // while + } // if + + if(AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + fprintf(stderr, "WARNING: AOP_CRY (bit-) results are probably broken. Please report this with source code as a bug.\n"); + mov2w(result, 0); // load Z flag + emitpcode(POC_BCF, popGet(result, 0)); + emitSKPZ; + emitpcode(POC_BSF, popGet(result, 0)); + } // if + + // adjustArithmeticResult(ic); + +release: + freeAsmop(IC_LEFT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + freeAsmop(IC_RIGHT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + freeAsmop(IC_RESULT(ic),NULL,ic,TRUE); +} + diff --git a/src/pic14/glue.c b/src/pic14/glue.c new file mode 100644 index 0000000..ee266e9 --- /dev/null +++ b/src/pic14/glue.c @@ -0,0 +1,1206 @@ +/*------------------------------------------------------------------------- + + glue.c - glues everything we have done together into one file. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "glue.h" +#include "dbuf_string.h" + +#include "device.h" +#include "gen.h" +#include "main.h" + +/* + * Imports + */ +extern set *publics; +extern set *externs; +extern symbol *mainf; +extern struct dbuf_s *codeOutBuf; + +extern void initialComments (FILE *afile); +extern operand *operandFromAst (ast *tree, int lvl); +extern value *initPointer (initList *ilist, sym_link *toType); + + +set *pic14_localFunctions = NULL; +int pic14_hasInterrupt = 0; // Indicates whether to emit interrupt handler or not + +int pic14_stringInSet(const char *str, set **world, int autoAdd); + + +#ifdef WORDS_BIGENDIAN +#define _ENDIAN(x) (3-x) +#else +#define _ENDIAN(x) (x) +#endif + +#define BYTE_IN_LONG(x,b) ((x>>(8*_ENDIAN(b)))&0xff) +#define IS_GLOBAL(sym) ((sym)->level == 0) +#define IS_DEFINED_HERE(sym) (!IS_EXTERN(sym->etype)) + +/* dbufs for initialized data (idata and code sections), + * extern, and global declarations */ +static struct dbuf_s *ivalBuf, *extBuf, *gloBuf, *gloDefBuf; + +static set *emitted = NULL; + +static void showAllMemmaps (FILE *of); // XXX: emits initialized symbols + +static void +emitPseudoStack(struct dbuf_s *oBuf, struct dbuf_s *oBufExt) +{ + int shared, low, high, size, i; + PIC_device *pic = pic14_getPIC(); + + /* also emit STK symbols + * XXX: This is ugly and fails as soon as devices start to get + * differently sized sharebanks, since STK12 will be + * required by larger devices but only up to STK03 might + * be defined using smaller devices. */ + shared = pic14_getSharedStack(&low, &high, &size); + if (!pic14_options.isLibrarySource) + { + dbuf_printf (oBuf, "\n"); + if (!pic->isEnhancedCore) { + size -= 3; + dbuf_printf (oBuf, "\tglobal PSAVE\n"); + dbuf_printf (oBuf, "\tglobal SSAVE\n"); + dbuf_printf (oBuf, "\tglobal WSAVE\n"); + } + for (i = size - 1; i >= 0; i--) { + dbuf_printf (oBuf, "\tglobal STK%02d\n", i); + } // for i + dbuf_printf (oBuf, "\n"); + + // 16f84 has no SHAREBANK (in linkerscript) but memory aliased in two + // banks, sigh... + if (1 || !shared) { + // for single banked devices: use normal, "banked" RAM + dbuf_printf (oBuf, "sharebank udata_ovr 0x%04X\n", low); + } else { + // for devices with at least two banks, require a sharebank section + dbuf_printf (oBuf, "sharebank udata_shr\n"); + } + if (!pic->isEnhancedCore) { + dbuf_printf (oBuf, "PSAVE\tres 1\n"); + dbuf_printf (oBuf, "SSAVE\tres 1\n"); + dbuf_printf (oBuf, "WSAVE\tres 1\n"); // WSAVE *must* be in sharebank (IRQ handlers) + } + /* fill rest of sharebank with stack STKxx .. STK00 */ + for (i = size - 1; i >= 0; i--) { + dbuf_printf (oBuf, "STK%02d\tres 1\n", i); + } // for i + } else { + /* declare STKxx as extern for all files + * except the one containing main() */ + dbuf_printf (oBufExt, "\n"); + if (!pic->isEnhancedCore) { + size -= 3; + dbuf_printf (oBufExt, "\textern PSAVE\n"); + dbuf_printf (oBufExt, "\textern SSAVE\n"); + dbuf_printf (oBufExt, "\textern WSAVE\n"); + } + for (i = size - 1; i >= 0; i--) { + char buffer[128]; + SNPRINTF(&buffer[0], 127, "STK%02d", i); + dbuf_printf (oBufExt, "\textern %s\n", &buffer[0]); + pic14_stringInSet(&buffer[0], &emitted, 1); + } // for i + } + dbuf_printf (oBuf, "\n"); +} + +static int +emitIfNew(struct dbuf_s *oBuf, set **emitted, const char *fmt, + const char *name) +{ + int wasPresent = pic14_stringInSet(name, emitted, 1); + + if (!wasPresent) { + dbuf_printf (oBuf, fmt, name); + } // if + return (!wasPresent); +} + +static void +pic14_constructAbsMap (struct dbuf_s *oBuf, struct dbuf_s *gloBuf) +{ + memmap *maps[] = { data, sfr, NULL }; + int i; + hTab *ht = NULL; + symbol *sym; + set *aliases; + int addr, min=-1, max=-1; + unsigned int size; + + for (i=0; maps[i] != NULL; i++) + { + for (sym = (symbol *)setFirstItem (maps[i]->syms); sym; sym = setNextItem (maps[i]->syms)) + { + if (IS_DEFINED_HERE(sym) && SPEC_ABSA(sym->etype)) + { + addr = SPEC_ADDR(sym->etype); + + /* handle CONFIG words here */ + if (IS_CONFIG_ADDRESS( addr )) + { + //fprintf( stderr, "%s: assignment to CONFIG@0x%x found\n", __FUNCTION__, addr ); + //fprintf( stderr, "ival: %p (0x%x)\n", sym->ival, (int)list2int( sym->ival ) ); + if (sym->ival) { + pic14_assignConfigWordValue( addr, (int)list2int( sym->ival ) ); + } else { + fprintf( stderr, "ERROR: Symbol %s, which is covering a __CONFIG word must be initialized!\n", sym->name ); + } + continue; + } + + if (max == -1 || addr > max) max = addr; + if (min == -1 || addr < min) min = addr; + //fprintf (stderr, "%s: sym %s @ 0x%x\n", __FUNCTION__, sym->name, addr); + aliases = hTabItemWithKey (ht, addr); + if (aliases) { + /* May not use addSetHead, as we cannot update the + * list's head in the hastable `ht'. */ + addSet (&aliases, sym); +#if 0 + fprintf( stderr, "%s: now %d aliases for %s @ 0x%x\n", + __FUNCTION__, elementsInSet(aliases), sym->name, addr); +#endif + } else { + addSet (&aliases, sym); + hTabAddItem (&ht, addr, aliases); + } // if + } // if + } // for sym + } // for i + + /* now emit definitions for all absolute symbols */ + dbuf_printf (oBuf, "%s", iComments2); + dbuf_printf (oBuf, "; absolute symbol definitions\n"); + dbuf_printf (oBuf, "%s", iComments2); + for (addr=min; addr <= max; addr++) + { + size = 1; + aliases = hTabItemWithKey (ht, addr); + if (aliases && elementsInSet(aliases)) { + /* Make sure there is no initialized value at this location! */ + for (sym = setFirstItem(aliases); sym; sym = setNextItem(aliases)) { + if (sym->ival) break; + } // for + if (sym) continue; + + dbuf_printf (oBuf, "UD_abs_%s_%x\tudata_ovr\t0x%04x\n", + moduleName, addr, addr); + for (sym = setFirstItem (aliases); sym; sym = setNextItem (aliases)) { + if (getSize(sym->type) > size) { + size = getSize(sym->type); + } + + /* initialized values are handled somewhere else */ + if (sym->ival) continue; + + /* emit STATUS as well as _STATUS, required for SFRs only */ + //dbuf_printf (oBuf, "%s\tres\t0\n", sym->name); + dbuf_printf (oBuf, "%s\n", sym->rname); + + if (IS_GLOBAL(sym) && !IS_STATIC(sym->etype)) { + //emitIfNew(gloBuf, &emitted, "\tglobal\t%s\n", sym->name); + emitIfNew(gloBuf, &emitted, "\tglobal\t%s\n", sym->rname); + } // if + } // for + dbuf_printf (oBuf, "\tres\t%d\n", size); + } // if + } // for i +} + +/*-----------------------------------------------------------------*/ +/* createInterruptVect - creates the interrupt vector */ +/*-----------------------------------------------------------------*/ +static void +pic14createInterruptVect (struct dbuf_s * vBuf) +{ + mainf = newSymbol ("main", 0); + mainf->block = 0; + + /* only if the main function exists */ + if (!(mainf = findSymWithLevel (SymbolTab, mainf))) + { + struct options *op = &options; + if (!(op->cc_only || noAssemble)) + // werror (E_NO_MAIN); + fprintf(stderr,"WARNING: function 'main' undefined\n"); + return; + } + + /* if the main is only a prototype ie. no body then do nothing */ + if (!IFFUNC_HASBODY(mainf->type)) + { + /* if ! compile only then main function should be present */ + if (!(options.cc_only || noAssemble)) + // werror (E_NO_MAIN); + fprintf(stderr,"WARNING: function 'main' undefined\n"); + return; + } + + dbuf_printf (vBuf, "%s", iComments2); + dbuf_printf (vBuf, "; reset vector \n"); + dbuf_printf (vBuf, "%s", iComments2); + // Lkr file should place section STARTUP at address 0x0, but does not ... + dbuf_printf (vBuf, "STARTUP\t%s 0x0000\n", CODE_NAME); + dbuf_printf (vBuf, "\tnop\n"); /* first location for used by incircuit debugger */ + dbuf_printf (vBuf, "\tpagesel __sdcc_gsinit_startup\n"); + dbuf_printf (vBuf, "\tgoto\t__sdcc_gsinit_startup\n"); + popGetExternal("__sdcc_gsinit_startup", 0); +} + + +/*-----------------------------------------------------------------*/ +/* initialComments - puts in some initial comments */ +/*-----------------------------------------------------------------*/ +static void +pic14initialComments (FILE * afile) +{ + initialComments (afile); + fprintf (afile, "; PIC port for the 14-bit core\n"); + fprintf (afile, "%s", iComments2); +} + +int +pic14_stringInSet(const char *str, set **world, int autoAdd) +{ + char *s; + + if (!str) return TRUE; + assert(world); + + for (s = setFirstItem(*world); s; s = setNextItem(*world)) + { + /* found in set */ + if (0 == strcmp(s, str)) return TRUE; + } + + /* not found */ + if (autoAdd) addSet(world, Safe_strdup(str)); + return FALSE; +} + +static void +pic14printLocals (struct dbuf_s *oBuf) +{ + set *allregs[6] = { dynAllocRegs/*, dynStackRegs, dynProcessorRegs*/, + dynDirectRegs, dynDirectBitRegs/*, dynInternalRegs */ }; + reg_info *reg; + int i, is_first = TRUE; + static unsigned sectionNr = 0; + const char *split_locals; + + split_locals = getenv("SDCC_PIC14_SPLIT_LOCALS"); + + /* emit all registers from all possible sets */ + for (i = 0; i < 6; i++) { + if (allregs[i] == NULL) continue; + + for (reg = setFirstItem(allregs[i]); reg; reg = setNextItem(allregs[i])) { + if (reg->isEmitted) continue; + + if (reg->wasUsed && !reg->isExtern) { + if (!pic14_stringInSet(reg->name, &emitted, TRUE)) { + if (reg->isFixed) { + // Should not happen, really... + assert ( !"Compiler-assigned variables should not be pinned... This is a bug." ); + dbuf_printf(oBuf, "UDL_%s_%u\tudata\t0x%04X\n%s\tres\t%d\n", + moduleName, sectionNr++, reg->address, reg->name, reg->size); + } else { + if (split_locals != NULL) { + // assign each local register into its own section + dbuf_printf(oBuf, "UDL_%s_%u\tudata\n%s\tres\t%d\n", + moduleName, sectionNr++, reg->name, reg->size); + } else { + // group all local registers into a single section + // This should greatly improve BANKSEL generation... + if (is_first) { + dbuf_printf(oBuf, "UDL_%s_%u\tudata\n", moduleName, sectionNr++); + is_first = FALSE; + } + dbuf_printf(oBuf, "%s\tres\t%d\n", reg->name, reg->size); + } + } + } + } + reg->isEmitted = TRUE; + } // for + } // for +} + +/*-----------------------------------------------------------------*/ +/* emitOverlay - will emit code for the overlay stuff */ +/*-----------------------------------------------------------------*/ +static void +pic14emitOverlay (struct dbuf_s * aBuf) +{ + set *ovrset; + + /* if (!elementsInSet (ovrSetSets))*/ + + /* the hack below, fixes translates for devices which + * only have udata_shr memory */ + dbuf_printf (aBuf, "%s\t%s\n", + (elementsInSet(ovrSetSets)?"":";"), + port->mem.overlay_name); + + /* for each of the sets in the overlay segment do */ + for (ovrset = setFirstItem (ovrSetSets); ovrset; + ovrset = setNextItem (ovrSetSets)) + { + + symbol *sym; + + if (elementsInSet (ovrset)) + { + /* this dummy area is used to fool the assembler + otherwise the assembler will append each of these + declarations into one chunk and will not overlay + sad but true */ + + /* I don't think this applies to us. We are using gpasm. CRF */ + + dbuf_printf (aBuf, ";\t.area _DUMMY\n"); + /* output the area informtion */ + dbuf_printf (aBuf, ";\t.area\t%s\n", port->mem.overlay_name); /* MOF */ + } + + for (sym = setFirstItem (ovrset); sym; + sym = setNextItem (ovrset)) + { + + /* if extern then do nothing */ + if (IS_EXTERN (sym->etype)) + continue; + + /* if allocation required check is needed + then check if the symbol really requires + allocation only for local variables */ + if (!IS_AGGREGATE (sym->type) && + !(sym->_isparm && !IS_REGPARM (sym->etype)) + && !sym->allocreq && sym->level) + continue; + + /* if global variable & not static or extern + and addPublics allowed then add it to the public set */ + if ((sym->_isparm && !IS_REGPARM (sym->etype)) + && !IS_STATIC (sym->etype)) + addSetHead (&publics, sym); + + /* if extern then do nothing or is a function + then do nothing */ + if (IS_FUNC (sym->type)) + continue; + + /* print extra debug info if required */ + if (options.debug || sym->level == 0) + { + if (!sym->level) + { /* global */ + if (IS_STATIC (sym->etype)) + dbuf_printf (aBuf, "F%s_", moduleName); /* scope is file */ + else + dbuf_printf (aBuf, "G_"); /* scope is global */ + } + else + /* symbol is local */ + dbuf_printf (aBuf, "L%s_", + (sym->localof ? sym->localof->name : "-null-")); + dbuf_printf (aBuf, "%s_%ld_%d", sym->name, sym->level, sym->block); + } + + /* if is has an absolute address then generate + an equate for this no need to allocate space */ + if (SPEC_ABSA (sym->etype)) + { + + if (options.debug || sym->level == 0) + dbuf_printf (aBuf, " == 0x%04x\n", SPEC_ADDR (sym->etype)); + + dbuf_printf (aBuf, "%s\t=\t0x%04x\n", + sym->rname, + SPEC_ADDR (sym->etype)); + } + else + { + if (options.debug || sym->level == 0) + dbuf_printf (aBuf, "==.\n"); + + /* allocate space */ + dbuf_printf (aBuf, "%s:\n", sym->rname); + dbuf_printf (aBuf, "\t.ds\t0x%04x\n", (unsigned int) getSize (sym->type) & 0xffff); + } + + } + } +} + + +static void +pic14_emitInterruptHandler (FILE * asmFile) +{ + if (pic14_hasInterrupt) + { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; interrupt and initialization code\n"); + fprintf (asmFile, "%s", iComments2); + // Note - for mplink may have to enlarge section vectors in .lnk file + // Note: Do NOT name this code_interrupt to avoid nameclashes with + // source files's code segment (interrupt.c -> code_interrupt) + fprintf (asmFile, "c_interrupt\t%s\t0x0004\n", CODE_NAME); + + /* interrupt service routine */ + fprintf (asmFile, "__sdcc_interrupt:\n"); + copypCode(asmFile, 'I'); + } +} + +/*-----------------------------------------------------------------*/ +/* glue - the final glue that hold the whole thing together */ +/*-----------------------------------------------------------------*/ +void +picglue (void) +{ + FILE *asmFile; + struct dbuf_s ovrBuf; + struct dbuf_s vBuf; + + dbuf_init(&ovrBuf, 4096); + dbuf_init(&vBuf, 4096); + + pCodeInitRegisters(); + + /* check for main() */ + mainf = newSymbol ("main", 0); + mainf->block = 0; + mainf = findSymWithLevel (SymbolTab, mainf); + + if (!mainf || !IFFUNC_HASBODY(mainf->type)) + { + /* main missing -- import stack from main module */ + //fprintf (stderr, "main() missing -- assuming we are NOT the main module\n"); + pic14_options.isLibrarySource = TRUE; + } + + /* At this point we've got all the code in the form of pCode structures */ + /* Now it needs to be rearranged into the order it should be placed in the */ + /* code space */ + + movepBlock2Head('P'); // Last + movepBlock2Head(code->dbName); + movepBlock2Head('X'); + movepBlock2Head(statsg->dbName); // First + + + /* print the global struct definitions */ + if (options.debug) + cdbStructBlock (0); + + /* do the overlay segments */ + pic14emitOverlay(&ovrBuf); + + /* PENDING: this isnt the best place but it will do */ + if (port->general.glue_up_main) { + /* create the interrupt vector table */ + pic14createInterruptVect (&vBuf); + } + + AnalyzepCode('*'); + + ReuseReg(); // ReuseReg where call tree permits + + InlinepCode(); + + AnalyzepCode('*'); + + if (options.debug) pcode_test(); + + + /* now put it all together into the assembler file */ + /* create the assembler file name */ + + if ((noAssemble || options.c1mode) && fullDstFileName) + { + SNPRINTF(buffer, sizeof(buffer), "%s", fullDstFileName); + } + else + { + SNPRINTF(buffer, sizeof(buffer), "%s.asm", dstFileName); + } + + if (!(asmFile = fopen (buffer, "w"))) { + werror (E_FILE_OPEN_ERR, buffer); + exit (1); + } + + /* prepare statistics */ + resetpCodeStatistics (); + + /* initial comments */ + pic14initialComments (asmFile); + + /* print module name */ + fprintf (asmFile, "%s\t.file\t\"%s\"\n", + options.debug ? "" : ";", fullSrcFileName); + + /* Let the port generate any global directives, etc. */ + if (port->genAssemblerPreamble) + { + port->genAssemblerPreamble(asmFile); + } + + /* Put all variables into a cblock */ + AnalyzeBanking(); + + /* emit initialized data */ + showAllMemmaps(asmFile); + + /* print the locally defined variables in this module */ + writeUsedRegs(asmFile); + + /* create the overlay segments */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; overlayable items in internal ram \n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&ovrBuf, asmFile); + + /* copy the interrupt vector table */ + if (mainf && IFFUNC_HASBODY(mainf->type)) + dbuf_write_and_destroy (&vBuf, asmFile); + else + dbuf_destroy(&vBuf); + + /* create interupt vector handler */ + pic14_emitInterruptHandler (asmFile); + + /* copy over code */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; code\n"); + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "code_%s\t%s\n", moduleName, port->mem.code_name); + + /* unknown */ + copypCode(asmFile, 'X'); + + /* _main function */ + copypCode(asmFile, 'M'); + + /* other functions */ + copypCode(asmFile, code->dbName); + + /* unknown */ + copypCode(asmFile, 'P'); + + dumppCodeStatistics (asmFile); + + fprintf (asmFile,"\tend\n"); + + fclose (asmFile); + pic14_debugLogClose(); +} + +/* + * Deal with initializers. + */ +#undef DEBUGprintf +#if 0 +// debugging output +#define DEBUGprintf printf +#else +// be quiet +#define DEBUGprintf 1 ? (void)0 : (void)printf +#endif + +static char * +parseIvalAst (ast *node, int *inCodeSpace) { +#define LEN 4096 + char *buffer = NULL; + char *left, *right; + + if (IS_AST_VALUE(node)) { + value *val = AST_VALUE(node); + symbol *sym = IS_AST_SYM_VALUE(node) ? AST_SYMBOL(node) : NULL; + if (inCodeSpace && val->type + && (IS_FUNC(val->type) || IS_CODE(getSpec(val->type)))) + { + *inCodeSpace = TRUE; + } + if (inCodeSpace && sym + && (IS_FUNC(sym->type) + || IS_CODE(getSpec(sym->type)))) + { + *inCodeSpace = TRUE; + } + + DEBUGprintf ("%s: AST_VALUE\n", __FUNCTION__); + if (IS_AST_LIT_VALUE(node)) { + buffer = Safe_alloc(LEN); + SNPRINTF(buffer, LEN, "0x%lx", AST_ULONG_VALUE (node)); + } else if (IS_AST_SYM_VALUE(node)) { + assert ( AST_SYMBOL(node) ); + /* + printf ("sym %s: ", AST_SYMBOL(node)->rname); + printTypeChain(AST_SYMBOL(node)->type, stdout); + printTypeChain(AST_SYMBOL(node)->etype, stdout); + printf ("\n---sym %s: done\n", AST_SYMBOL(node)->rname); + */ + buffer = Safe_strdup(AST_SYMBOL(node)->rname); + } else { + assert ( !"Invalid values type for initializers in AST." ); + } + } else if (IS_AST_OP(node)) { + DEBUGprintf ("%s: AST_OP\n", __FUNCTION__); + switch (node->opval.op) { + case CAST: + assert (node->right); + buffer = parseIvalAst(node->right, inCodeSpace); + DEBUGprintf ("%s: %s\n", __FUNCTION__, buffer); + break; + case '&': + assert ( node->left && !node->right ); + buffer = parseIvalAst(node->left, inCodeSpace); + DEBUGprintf ("%s: %s\n", __FUNCTION__, buffer); + break; + case '+': + assert (node->left && node->right ); + left = parseIvalAst(node->left, inCodeSpace); + right = parseIvalAst(node->right, inCodeSpace); + buffer = Safe_alloc(LEN); + SNPRINTF(buffer, LEN, "(%s + %s)", left, right); + DEBUGprintf ("%s: %s\n", __FUNCTION__, buffer); + Safe_free(left); + Safe_free(right); + break; + case '[': + assert ( node->left && node->right ); + assert ( IS_AST_VALUE(node->left) && AST_VALUE(node->left)->sym ); + right = parseIvalAst(node->right, inCodeSpace); + buffer = Safe_alloc(LEN); + SNPRINTF(buffer, LEN, "(%s + %u * %s)", + AST_VALUE(node->left)->sym->rname, getSize(AST_VALUE(node->left)->type), right); + Safe_free(right); + DEBUGprintf ("%s: %s\n", __FUNCTION__, &buffer[0]); + break; + default: + assert ( !"Unhandled operation in initializer." ); + break; + } + } else { + assert ( !"Invalid construct in initializer." ); + } + + return (buffer); +} + +/* + * Emit the section preamble, absolute location (if any) and + * symbol name(s) for intialized data. + */ +static int +emitIvalLabel(struct dbuf_s *oBuf, symbol *sym) +{ + char *segname; + static int in_code = FALSE; + static int sectionNr = 0; + + if (sym) { + // code or data space? + if (IS_CODE(getSpec(sym->type))) { + segname = "code"; + in_code = TRUE; + } else { + segname = "idata"; + in_code = FALSE; + } + dbuf_printf(oBuf, "\nID_%s_%d\t%s", moduleName, sectionNr++, segname); + if (SPEC_ABSA(getSpec(sym->type))) { + // specify address for absolute symbols + dbuf_printf(oBuf, "\t0x%04X", SPEC_ADDR(getSpec(sym->type))); + } // if + dbuf_printf(oBuf, "\n%s\n", sym->rname); + + addSet(&emitted, sym->rname); + } + return (in_code); +} + +/* + * Actually emit the initial values in .asm format. + */ +static void +emitIvals(struct dbuf_s *oBuf, symbol *sym, initList *list, long lit, int size) +{ + int i; + ast *node; + operand *op; + value *val = NULL; + int inCodeSpace = FALSE; + char *str = NULL; + int in_code; + + assert (size <= sizeof(long)); + assert (!list || (list->type == INIT_NODE)); + node = list ? list->init.node : NULL; + + in_code = emitIvalLabel(oBuf, sym); + if (!in_code) + dbuf_printf (oBuf, "\tdb\t"); + + if (!node) { + for (i = 0; i < size; i++) { + if (in_code) { + dbuf_printf (oBuf, "\tretlw 0x%02x\n", (int)(lit & 0xff)); + // dbuf_printf (oBuf, "\tretlw 0x00\n"); // conflict from merge of sf-patch-2991122 ? + } else { + dbuf_printf (oBuf, "%s0x%02x", (i == 0) ? "" : ", ", (int)(lit & 0xff)); + } + lit >>= 8; + } // for + if (!in_code) + dbuf_printf (oBuf, "\n"); + return; + } // if + + op = NULL; + if (constExprTree(node) && (val = constExprValue(node, 0))) { + op = operandFromValue(val); + DEBUGprintf ("%s: constExpr ", __FUNCTION__); + } else if (IS_AST_VALUE(node)) { + op = operandFromAst(node, 0); + } else if (IS_AST_OP(node)) { + str = parseIvalAst(node, &inCodeSpace); + DEBUGprintf("%s: AST_OP: %s\n", __FUNCTION__, str); + op = NULL; + } else { + assert ( !"Unhandled construct in intializer." ); + } + + if (op) { + aopOp(op, NULL, 1); + assert(AOP(op)); + //printOperand(op, of); + } + + for (i = 0; i < size; i++) { + char *text; + + /* + * FIXME: This is hacky and needs some more thought. + */ + if (op && IS_SYMOP(op) && IS_FUNC(OP_SYM_TYPE(op))) { + /* This branch is introduced to fix #1427663. */ + PCOI(AOP(op)->aopu.pcop)->offset+=i; + text = get_op(AOP(op)->aopu.pcop, NULL, 0); + PCOI(AOP(op)->aopu.pcop)->offset-=i; + } else { + text = op ? aopGet(AOP(op), i, 0, 0) + : get_op(newpCodeOpImmd(str, i, 0, inCodeSpace, 0), NULL, 0); + } // if + if (in_code) { + dbuf_printf (oBuf, "\tretlw %s\n", text); + } else { + dbuf_printf (oBuf, "%s%s", (i == 0) ? "" : ", ", text); + } + } // for + if (!in_code) + dbuf_printf (oBuf, "\n"); +} + +/* + * For UNIONs, we first have to find the correct alternative to map the + * initializer to. This function maps the structure of the initializer to + * the UNION members recursively. + * Returns the type of the first `fitting' member. + */ +static sym_link * +matchIvalToUnion (initList *list, sym_link *type, int size) +{ + symbol *sym; + + assert (type); + + if (IS_PTR(type) || IS_CHAR(type) || IS_INT(type) || IS_LONG(type) + || IS_FLOAT(type)) + { + if (!list || (list->type == INIT_NODE)) { + DEBUGprintf ("OK, simple type\n"); + return (type); + } else { + DEBUGprintf ("ERROR, simple type\n"); + return (NULL); + } + } else if (IS_BITFIELD(type)) { + if (!list || (list->type == INIT_NODE)) { + DEBUGprintf ("OK, bitfield\n"); + return (type); + } else { + DEBUGprintf ("ERROR, bitfield\n"); + return (NULL); + } + } else if (IS_STRUCT(type) && SPEC_STRUCT(getSpec(type))->type == STRUCT) { + if (!list || (list->type == INIT_DEEP)) { + if (list) list = list->init.deep; + sym = SPEC_STRUCT(type)->fields; + while (sym) { + DEBUGprintf ("Checking STRUCT member %s\n", sym->name); + if (!matchIvalToUnion(list, sym->type, 0)) { + DEBUGprintf ("ERROR, STRUCT member %s\n", sym->name); + return (NULL); + } + if (list) list = list->next; + sym = sym->next; + } // while + + // excess initializers? + if (list) { + DEBUGprintf ("ERROR, excess initializers\n"); + return (NULL); + } + + DEBUGprintf ("OK, struct\n"); + return (type); + } + return (NULL); + } else if (IS_STRUCT(type) && SPEC_STRUCT(getSpec(type))->type == UNION) { + if (!list || (list->type == INIT_DEEP)) { + if (list) list = list->init.deep; + sym = SPEC_STRUCT(type)->fields; + while (sym) { + while (list && list->type == INIT_HOLE) { + list = list->next; + sym = sym->next; + } + DEBUGprintf ("Checking UNION member %s.\n", sym->name); + if (((IS_STRUCT(sym->type) || getSize(sym->type) == size)) + && matchIvalToUnion(list, sym->type, size)) + { + DEBUGprintf ("Matched UNION member %s.\n", sym->name); + return (sym->type); + } + sym = sym->next; + } // while + } // if + // no match found + DEBUGprintf ("ERROR, no match found.\n"); + return (NULL); + } else { + assert ( !"Unhandled type in UNION." ); + } + + assert ( !"No match found in UNION for the given initializer structure." ); + return (NULL); +} + +/* + * Parse the type and its initializer and emit it (recursively). + */ +static void +emitInitVal(struct dbuf_s *oBuf, symbol *topsym, sym_link *my_type, initList *list) +{ + symbol *sym; + int size; + long lit; + unsigned char *str; + + /* Handle designated initializers */ + if (list) + list = reorderIlist (my_type, list); + + /* If this is a hole, substitute an appropriate initializer. */ + if (list && list->type == INIT_HOLE) + { + if (IS_AGGREGATE (my_type)) + { + list = newiList(INIT_DEEP, NULL); /* init w/ {} */ + } + else + { + ast *ast = newAst_VALUE (constVal("0")); + ast = decorateType (ast, RESULT_TYPE_NONE); + list = newiList(INIT_NODE, ast); + } + } + + size = getSize(my_type); + + if (IS_PTR(my_type)) { + DEBUGprintf ("(pointer, %d byte) 0x%x\n", size, list ? (unsigned int)list2int(list) : 0); + emitIvals(oBuf, topsym, list, 0, size); + return; + } + + if (IS_ARRAY(my_type) && topsym && topsym->isstrlit) { + str = (unsigned char *)SPEC_CVAL(topsym->etype).v_char; + emitIvalLabel(oBuf, topsym); + do { + dbuf_printf (oBuf, "\tretlw 0x%02x ; '%c'\n", str[0], (str[0] >= 0x20 && str[0] < 128) ? str[0] : '.'); + } while (*(str++)); + return; + } + + if (IS_ARRAY(my_type) && list && list->type == INIT_NODE) { + fprintf (stderr, "Unhandled initialized symbol: %s\n", topsym->name); + assert ( !"Initialized char-arrays are not yet supported, assign at runtime instead." ); + return; + } + + if (IS_ARRAY(my_type)) { + size_t i; + + DEBUGprintf ("(array, %d items, %ud byte) below\n", (unsigned int) DCL_ELEM(my_type), size); + assert (!list || list->type == INIT_DEEP); + if (list) list = list->init.deep; + for (i = 0; i < DCL_ELEM(my_type); i++) { + emitInitVal(oBuf, topsym, my_type->next, list); + topsym = NULL; + if (list) list = list->next; + } // for i + return; + } + + if (IS_FLOAT(my_type)) { + // float, 32 bit + DEBUGprintf ("(float, %d byte) %lf\n", size, list ? list2int(list) : 0.0); + emitIvals(oBuf, topsym, list, 0, size); + return; + } + + if (IS_CHAR(my_type) || IS_INT(my_type) || IS_LONG(my_type) || + IS_BOOL(my_type)) { + // integral type, 8, 16, or 32 bit + DEBUGprintf ("(integral, %d byte) 0x%lx/%ld\n", size, list ? (long)list2int(list) : 0, list ? (long)list2int(list) : 0); + emitIvals(oBuf, topsym, list, 0, size); + return; + + } else if (IS_STRUCT(my_type) && SPEC_STRUCT(my_type)->type == STRUCT) { + // struct + DEBUGprintf ("(struct, %d byte) handled below\n", size); + assert (!list || (list->type == INIT_DEEP)); + + // iterate over struct members and initList + if (list) list = list->init.deep; + sym = SPEC_STRUCT(my_type)->fields; + while (sym) { + long bitfield = 0; + int len = 0; + if (IS_BITFIELD(sym->type)) { + while (sym && IS_BITFIELD(sym->type)) { + int bitoff = SPEC_BSTR(getSpec(sym->type)) + 8 * sym->offset; + assert (!list || ((list->type == INIT_NODE) + && IS_AST_LIT_VALUE(list->init.node))); + lit = (long) (list ? list2int(list) : 0); + DEBUGprintf ( "(bitfield member) %02lx (%d bit, starting at %d, bitfield %02lx)\n", + lit, SPEC_BLEN(getSpec(sym->type)), + bitoff, bitfield); + bitfield |= (lit & ((1ul << SPEC_BLEN(getSpec(sym->type))) - 1)) << bitoff; + len += SPEC_BLEN(getSpec(sym->type)); + + sym = sym->next; + if (list) list = list->next; + } // while + assert (len < sizeof (long) * 8); // did we overflow our initializer?!? + len = (len + 7) & ~0x07; // round up to full bytes + emitIvals(oBuf, topsym, NULL, bitfield, len / 8); + topsym = NULL; + } // if + + if (sym) { + emitInitVal(oBuf, topsym, sym->type, list); + topsym = NULL; + sym = sym->next; + if (list) list = list->next; + } // if + } // while + if (list) { + assert ( !"Excess initializers." ); + } // if + return; + + } else if (IS_STRUCT(my_type) && SPEC_STRUCT(my_type)->type == UNION) { + // union + DEBUGprintf ("(union, %d byte) handled below\n", size); + assert (list && list->type == INIT_DEEP); + + // iterate over union members and initList, try to map number and type of fields and initializers + my_type = matchIvalToUnion(list, my_type, size); + if (my_type) { + emitInitVal(oBuf, topsym, my_type, list->init.deep); + topsym = NULL; + size -= getSize(my_type); + if (size > 0) { + // pad with (leading) zeros + emitIvals(oBuf, NULL, NULL, 0, size); + } + return; + } // if + + assert ( !"No UNION member matches the initializer structure."); + } else if (IS_BITFIELD(my_type)) { + assert ( !"bitfields should only occur in structs..." ); + + } else { + printf ("SPEC_NOUN: %d\n", SPEC_NOUN(my_type)); + assert( !"Unhandled initialized type."); + } +} + +/* + * Emit a set of symbols. + * type - 0: have symbol tell whether it is local, extern or global + * 1: assume all symbols in set to be global + * 2: assume all symbols in set to be extern + */ +static void +emitSymbolSet(set *s, int type) +{ + symbol *sym; + initList *list; + unsigned int sectionNr = 0; + + for (sym = setFirstItem(s); sym; sym = setNextItem(s)) { +#if 0 + fprintf (stdout, "; name %s, rname %s, level %d, block %d, key %d, local %d, ival %p, static %d, cdef %d, used %d\n", + sym->name, sym->rname, sym->level, sym->block, sym->key, sym->islocal, sym->ival, IS_STATIC(sym->etype), sym->cdef, sym->used); +#endif + + if (sym->etype && SPEC_ABSA(sym->etype) + && IS_CONFIG_ADDRESS(SPEC_ADDR(sym->etype)) + && sym->ival) + { + // handle config words + pic14_assignConfigWordValue(SPEC_ADDR(sym->etype), + (int)list2int(sym->ival)); + pic14_stringInSet(sym->rname, &emitted, 1); + continue; + } + + if (sym->isstrlit) { + // special case: string literals + emitInitVal(ivalBuf, sym, sym->type, NULL); + continue; + } + + if (type != 0 || sym->cdef || (!IS_STATIC(sym->etype) && IS_GLOBAL(sym))) + { + // bail out for ___fsadd and friends + if (sym->cdef && !sym->used) continue; + + /* export or import non-static globals */ + if (!pic14_stringInSet(sym->rname, &emitted, 0)) { + + if (type == 2 || IS_EXTERN(sym->etype) || sym->cdef) + { + /* do not add to emitted set, it might occur again! */ + //if (!sym->used) continue; + // declare symbol + emitIfNew (extBuf, &emitted, "\textern\t%s\n", sym->rname); + } else { + // declare symbol + emitIfNew (gloBuf, &emitted, "\tglobal\t%s\n", sym->rname); + if (!sym->ival && !IS_FUNC(sym->type)) { + // also define symbol + if (IS_ABSOLUTE(sym->etype)) { + // absolute location? + //dbuf_printf (gloDefBuf, "UD_%s_%u\tudata\t0x%04X\n", moduleName, sectionNr++, SPEC_ADDR(sym->etype)); + // deferred to pic14_constructAbsMap + } else { + dbuf_printf (gloDefBuf, "UD_%s_%u\tudata\n", moduleName, sectionNr++); + dbuf_printf (gloDefBuf, "%s\tres\t%d\n\n", sym->rname, getSize(sym->type)); + } + } // if + } // if + pic14_stringInSet(sym->rname, &emitted, 1); + } // if + } // if + list = sym->ival; + //if (list) showInitList(list, 0); + if (list) { + resolveIvalSym( list, sym->type ); + emitInitVal(ivalBuf, sym, sym->type, sym->ival); + dbuf_printf (ivalBuf, "\n"); + } + } // for sym +} + +/* + * Iterate over all memmaps and emit their contents (attributes, symbols). + */ +static void +showAllMemmaps(FILE *of) +{ + struct dbuf_s locBuf; + memmap *maps[] = { + xstack, istack, code, data, pdata, xdata, xidata, xinit, + idata, bit, statsg, c_abs, x_abs, i_abs, d_abs, + sfr, sfrbit, reg, generic, overlay, eeprom, home }; + memmap * map; + int i; + + DEBUGprintf ("---begin memmaps---\n"); + if (!extBuf) extBuf = dbuf_new(1024); + if (!gloBuf) gloBuf = dbuf_new(1024); + if (!gloDefBuf) gloDefBuf = dbuf_new(1024); + if (!ivalBuf) ivalBuf = dbuf_new(1024); + dbuf_init(&locBuf, 1024); + + dbuf_printf (extBuf, "%s; external declarations\n%s", iComments2, iComments2); + dbuf_printf (gloBuf, "%s; global declarations\n%s", iComments2, iComments2); + dbuf_printf (gloDefBuf, "%s; global definitions\n%s", iComments2, iComments2); + dbuf_printf (ivalBuf, "%s; initialized data\n%s", iComments2, iComments2); + dbuf_printf (&locBuf, "%s; compiler-defined variables\n%s", iComments2, iComments2); + + for (i = 0; i < sizeof(maps) / sizeof (memmap *); i++) { + map = maps[i]; + //DEBUGprintf ("memmap %i: %p\n", i, map); + if (map) { +#if 0 + fprintf (stdout, "; pageno %c, sname %s, dbName %c, ptrType %d, slbl %d, sloc %u, fmap %u, paged %u, direct %u, bitsp %u, codesp %u, regsp %u, syms %p\n", + map->pageno, map->sname, map->dbName, map->ptrType, map->slbl, + map->sloc, map->fmap, map->paged, map->direct, map->bitsp, + map->codesp, map->regsp, map->syms); +#endif + emitSymbolSet(map->syms, 0); + } // if (map) + } // for i + DEBUGprintf ("---end of memmaps---\n"); + + emitSymbolSet(publics, 1); + emitSymbolSet(externs, 2); + + emitPseudoStack(gloBuf, extBuf); + pic14_constructAbsMap(gloDefBuf, gloBuf); + pic14printLocals (&locBuf); + pic14_emitConfigWord(of); // must be done after all the rest + + dbuf_write_and_destroy(extBuf, of); + dbuf_write_and_destroy(gloBuf, of); + dbuf_write_and_destroy(gloDefBuf, of); + dbuf_write_and_destroy(&locBuf, of); + dbuf_write_and_destroy(ivalBuf, of); + + extBuf = gloBuf = gloDefBuf = ivalBuf = NULL; +} diff --git a/src/pic14/glue.h b/src/pic14/glue.h new file mode 100644 index 0000000..f4a9407 --- /dev/null +++ b/src/pic14/glue.h @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + + SDCCglue.h - glues everything we have done together into one file. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef PIC_GLUE_H +#define PIC_GLUE_H + +#include "common.h" + +extern int pic14_hasInterrupt; +extern set *pic14_localFunctions; + +void picglue (void); +int pic14_stringInSet(const char *str, set **world, int autoAdd); + +#endif diff --git a/src/pic14/main.c b/src/pic14/main.c new file mode 100644 index 0000000..ee90470 --- /dev/null +++ b/src/pic14/main.c @@ -0,0 +1,485 @@ +/** @file main.c + pic14 specific general functions. + + Note that mlh prepended _pic14_ on the static functions. Makes + it easier to set a breakpoint using the debugger. +*/ +#include "common.h" +#include "dbuf_string.h" +#include "SDCCmacro.h" + +#include "device.h" +#include "gen.h" +#include "glue.h" +#include "main.h" +#include "pcode.h" +#include "ralloc.h" + +/* + * Imports + */ +extern set *dataDirsSet; +extern set *includeDirsSet; +extern set *libDirsSet; +extern set *libPathsSet; +extern set *linkOptionsSet; + + +pic14_options_t pic14_options; +int debug_verbose = 0; + + +#define OPTION_STACK_SIZE "--stack-size" + +static char _defaultRules[] = +{ +#include "peeph.rul" +}; + +static OPTION _pic14_poptions[] = + { + { 0, "--debug-xtra", &debug_verbose, "show more debug info in assembly output" }, + { 0, "--no-pcode-opt", &pic14_options.disable_df, "disable (slightly faulty) optimization on pCode" }, + { 0, OPTION_STACK_SIZE, &options.stack_size, "sets the size if the argument passing stack (default: 16, minimum: 4)", CLAT_INTEGER }, + { 0, "--no-extended-instructions", &pic14_options.no_ext_instr, "forbid use of the extended instruction set (e.g., ADDFSR)" }, + { 0, "--no-warn-non-free", &pic14_options.no_warn_non_free, "suppress warning on absent --use-non-free option" }, + { 0, NULL, NULL, NULL } + }; + +/* list of key words used by pic14 */ +static char *_pic14_keywords[] = +{ + "at", + //"bit", + "code", + "critical", + "data", + "far", + "idata", + "interrupt", + "naked", + "near", + //"pdata", + "reentrant", + "sfr", + //"sbit", + "using", + "xdata", + NULL +}; + +static int regParmFlg = 0; /* determine if we can register a parameter */ + + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +/* +static const char *_linkCmd[] = +{ + "gplink", "$l", "-w", "-r", "-o \"$2\"", "\"$1\"", "$3", NULL +}; +*/ + +static const char *_asmCmd[] = +{ + "gpasm", "$l", "$3", "-o", "$2", "-c", "$1.asm", NULL +}; + +static void +_pic14_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); + memset (&pic14_options, 0, sizeof (pic14_options)); +} + +static void +_pic14_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; +} + +static int +_pic14_regparm (sym_link * l, bool reentrant) +{ +/* for this processor it is simple + can pass only the first parameter in a register */ + //if (regParmFlg) + // return 0; + + regParmFlg++;// = 1; + return 1; +} + +static bool +_pic14_parseOptions (int *pargc, char **argv, int *i) +{ + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + return FALSE; +} + +static void +_pic14_finaliseOptions (void) +{ + struct dbuf_s dbuf; + + pCodeInitRegisters(); + + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + + dbuf_init (&dbuf, 512); + dbuf_printf (&dbuf, "-D__SDCC_PROCESSOR=\"%s\"", port->processor); + addSet (&preArgvSet, Safe_strdup (dbuf_detach_c_str (&dbuf))); + + { + char *upperProc, *p1, *p2; + int len; + + dbuf_set_length (&dbuf, 0); + len = strlen (port->processor); + upperProc = Safe_malloc (len); + for (p1 = port->processor, p2 = upperProc; *p1; ++p1, ++p2) + { + *p2 = toupper (*p1); + } + dbuf_append (&dbuf, "-D__SDCC_PIC", sizeof ("-D__SDCC_PIC") - 1); + dbuf_append (&dbuf, upperProc, len); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + + /* This is valid after calling pCodeInitRegisters */ + if (pic14_getPIC()->isEnhancedCore) + { + dbuf_set_length (&dbuf, 0); + dbuf_printf (&dbuf, "-D__SDCC_PIC14_ENHANCED"); + addSet (&preArgvSet, Safe_strdup (dbuf_detach_c_str (&dbuf))); + } + + /* Report the actual stack size */ + { + int size; + pic14_getSharedStack(NULL, NULL, &size); + + /* non-enhanced cores take PSAVE, SSAVE and WSAVE from stack */ + if (!pic14_getPIC()->isEnhancedCore) + size -= 3; + + /* Actual stack size includes WREG and STK00, ... */ + dbuf_set_length (&dbuf, 0); + dbuf_printf (&dbuf, "-D__SDCC_PIC14_STACK_SIZE=%d", size + 1); + addSet (&preArgvSet, Safe_strdup (dbuf_detach_c_str (&dbuf))); + } + + if (!pic14_options.no_warn_non_free && !options.use_non_free) + { + fprintf(stderr, + "WARNING: Command line option --use-non-free not present.\n" + " When compiling for PIC14/PIC16, please provide --use-non-free\n" + " to get access to device headers and libraries.\n" + " If you do not use these, you may provide --no-warn-non-free\n" + " to suppress this warning (not recommended).\n"); + } // if + +} + +static void +_pic14_setDefaultOptions (void) +{ +} + +static const char * +_pic14_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +_pic14_genAssemblerPreamble (FILE * of) +{ + char * name = processor_base_name(); + + if(!name) { + + name = "16f877"; + fprintf(stderr,"WARNING: No Pic has been selected, defaulting to %s\n",name); + } + + fprintf (of, "\tlist\tp=%s\n",name); + fprintf (of, "\tradix dec\n"); + fprintf (of, "\tinclude \"p%s.inc\"\n",name); +} + +/* Generate interrupt vector table. */ +static int +_pic14_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ + /* Let the default code handle it. */ + return FALSE; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ +#if 0 + if ( ic->op != '*') + { + return FALSE; + } + + /* multiply chars in-place */ + if (getSize(left) == 1 && getSize(right) == 1) + return TRUE; +#endif + + /* use library functions for more complex maths */ + return FALSE; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETABIT + /* || op == GETHBIT */ /* GETHBIT doesn't look complete for PIC */ + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + /* The IN_FARSPACE test is compatible with historical behaviour, */ + /* but I don't think it is applicable to PIC. If so, please feel */ + /* free to remove this test -- EEP */ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +static void +_pic14_do_link (void) +{ + /* + * link command format: + * {linker} {incdirs} {lflags} -o {outfile} {spec_ofiles} {ofiles} {libs} + * + */ +#define LFRM "{linker} {incdirs} {sysincdirs} {lflags} -w -r -o {outfile} {user_ofile} {spec_ofiles} {ofiles} {libs}" + hTab *linkValues = NULL; + char *lcmd; + set *tSet = NULL; + int ret; + char * procName; + + shash_add (&linkValues, "linker", "gplink"); + + /* LIBRARY SEARCH DIRS */ + mergeSets (&tSet, libPathsSet); + mergeSets (&tSet, libDirsSet); + shash_add (&linkValues, "incdirs", joinStrSet (processStrSet (tSet, "-I", NULL, shell_escape))); + + joinStrSet (processStrSet (libDirsSet, "-I", NULL, shell_escape)); + shash_add (&linkValues, "sysincdirs", joinStrSet (processStrSet (libDirsSet, "-I", NULL, shell_escape))); + + shash_add (&linkValues, "lflags", joinStrSet (linkOptionsSet)); + + { + char *s = shell_escape (fullDstFileName ? fullDstFileName : dstFileName); + + shash_add (&linkValues, "outfile", s); + Safe_free (s); + } + + if (fullSrcFileName) + { + struct dbuf_s dbuf; + char *s; + + dbuf_init (&dbuf, 128); + + dbuf_append_str (&dbuf, fullDstFileName ? fullDstFileName : dstFileName); + dbuf_append (&dbuf, ".o", 2); + s = shell_escape (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + shash_add (&linkValues, "user_ofile", s); + Safe_free (s); + } + + shash_add (&linkValues, "ofiles", joinStrSet (processStrSet (relFilesSet, NULL, NULL, shell_escape))); + + /* LIBRARIES */ + procName = processor_base_name (); + if (!procName) + procName = "16f877"; + + addSet (&libFilesSet, Safe_strdup (pic14_getPIC()->isEnhancedCore ? + "libsdcce.lib" : "libsdcc.lib")); + + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_append (&dbuf, "pic", sizeof ("pic") - 1); + dbuf_append_str (&dbuf, procName); + dbuf_append (&dbuf, ".lib", sizeof (".lib") - 1); + addSet (&libFilesSet, dbuf_detach_c_str (&dbuf)); + } + + shash_add (&linkValues, "libs", joinStrSet (processStrSet (libFilesSet, NULL, NULL, shell_escape))); + + lcmd = msprintf(linkValues, LFRM); + ret = sdcc_system (lcmd); + Safe_free (lcmd); + + if (ret) + exit (1); +} + +/* Globals */ +PORT pic_port = +{ + TARGET_ID_PIC14, + "pic14", + "MCU pic", /* Target name */ + "", /* Processor */ + { + picglue, + TRUE, /* Emit glue around main */ + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { + _asmCmd, + NULL, + "-g", /* options with --debug */ + NULL, /* options without --debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { + NULL, + NULL, + _pic14_do_link, /* own do link function */ + ".o", + 0 + }, + { /* Peephole optimizer */ + _defaultRules + }, + { + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + 1, 2, 2, 4, 8, 2, 2, 3, 2, 3, 1, 4 + /* TSD - I changed the size of gptr from 3 to 1. However, it should be + 2 so that we can accomodate the PIC's with 4 register banks (like the + 16f877) + */ + }, + /* tags for generic pointers */ + { 0x00, 0x00, 0x00, 0x80 }, /* far, near, xstack, code */ + { + "XSEG (XDATA)", + "STACK (DATA)", + "code", + "DSEG (DATA)", + "ISEG (DATA)", + NULL, /* pdata */ + "XSEG (XDATA)", + "BSEG (BIT)", + "RSEG (DATA)", + "GSINIT (CODE)", + "udata_ovr", + "GSFINAL (CODE)", + "HOME (CODE)", + NULL, // xidata + NULL, // xinit + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata + "IABS (ABS,DATA)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, + NULL, + 1, // code is read only + 1 // No fancy alignments supported. + }, + { NULL, NULL }, + { + +1, 1, 4, 1, 1, 0, 0 + }, + /* pic14 has an 8 bit mul */ + { + -1, FALSE + }, + { + pic14_emitDebuggerSymbol + }, + { + 255/3, /* maxCount */ + 3, /* sizeofElement */ + /* The rest of these costs are bogus. They approximate */ + /* the behavior of src/SDCCicode.c 1.207 and earlier. */ + {4,4,4}, /* sizeofMatchJump[] */ + {0,0,0}, /* sizeofRangeCompare[] */ + 0, /* sizeofSubtract */ + 3, /* sizeofDispatch */ + }, + "_", + _pic14_init, + _pic14_parseOptions, + _pic14_poptions, + NULL, + _pic14_finaliseOptions, + _pic14_setDefaultOptions, + pic14_assignRegisters, + _pic14_getRegName, + 0, + NULL, + _pic14_keywords, + _pic14_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _pic14_genIVT, + NULL, // _pic14_genXINIT + NULL, /* genInitStartup */ + _pic14_reset_regparm, + _pic14_regparm, + NULL, /* process a pragma */ + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, +// TRUE, /* little endian */ + FALSE, /* little endian - PIC code enumlates big endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + 0, /* no CSE cost estimation yet */ + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local static allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/pic14/main.h b/src/pic14/main.h new file mode 100644 index 0000000..4275b65 --- /dev/null +++ b/src/pic14/main.h @@ -0,0 +1,15 @@ +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +typedef struct { + unsigned int isLibrarySource:1; + int disable_df; + int no_ext_instr; + int no_warn_non_free; +} pic14_options_t; + +extern pic14_options_t pic14_options; +extern int debug_verbose; + +#endif + diff --git a/src/pic14/pcode.c b/src/pic14/pcode.c new file mode 100644 index 0000000..5e010da --- /dev/null +++ b/src/pic14/pcode.c @@ -0,0 +1,5985 @@ +/*------------------------------------------------------------------------- + + pcode.c - post code generation + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "device.h" +#include "gen.h" +#include "pcode.h" +#include "pcodeflow.h" +#include "ralloc.h" + +/****************************************************************/ +/****************************************************************/ + +// Eventually this will go into device dependent files: +pCodeOpReg pc_status = {{PO_STATUS, "STATUS"}, -1, NULL,0,NULL}; +pCodeOpReg pc_fsr = {{PO_FSR, "FSR"}, -1, NULL,0,NULL}; +pCodeOpReg pc_fsr0l = {{PO_FSR, "FSR0L"}, -1, NULL,0,NULL}; +pCodeOpReg pc_fsr0h = {{PO_FSR, "FSR0H"}, -1, NULL,0,NULL}; +pCodeOpReg pc_indf_ = {{PO_INDF, "INDF"}, -1, NULL,0,NULL}; +pCodeOpReg pc_indf0 = {{PO_INDF, "INDF0"}, -1, NULL,0,NULL}; +pCodeOpReg pc_intcon = {{PO_INTCON, "INTCON"}, -1, NULL,0,NULL}; +pCodeOpReg pc_pcl = {{PO_PCL, "PCL"}, -1, NULL,0,NULL}; +pCodeOpReg pc_pclath = {{PO_PCLATH, "PCLATH"}, -1, NULL,0,NULL}; + +pCodeOpReg *pc_indf = &pc_indf_; + +pCodeOpReg pc_wsave = {{PO_GPR_REGISTER, "WSAVE"}, -1, NULL,0,NULL}; +pCodeOpReg pc_ssave = {{PO_GPR_REGISTER, "SSAVE"}, -1, NULL,0,NULL}; +pCodeOpReg pc_psave = {{PO_GPR_REGISTER, "PSAVE"}, -1, NULL,0,NULL}; + +pFile *the_pFile = NULL; + + +#define SET_BANK_BIT (1 << 16) +#define CLR_BANK_BIT 0 + +static peepCommand peepCommands[] = { + + {NOTBITSKIP, "_NOTBITSKIP_"}, + {BITSKIP, "_BITSKIP_"}, + {INVERTBITSKIP, "_INVERTBITSKIP_"}, + + {-1, NULL} +}; + +static int mnemonics_initialized = 0; + +static hTab *pic14MnemonicsHash = NULL; +static hTab *pic14pCodePeepCommandsHash = NULL; + +static pBlock *pb_dead_pcodes = NULL; + +/* Hardcoded flags to change the behavior of the PIC port */ +static int functionInlining = 1; /* inline functions if nonzero */ + +// static int GpCodeSequenceNumber = 1; +static int GpcFlowSeq = 1; + +/* statistics (code size estimation) */ +static unsigned int pcode_insns = 0; +static unsigned int pcode_doubles = 0; + +static unsigned peakIdx = 0; /* This keeps track of the peak register index for call tree register reuse */ + + +/****************************************************************/ +/* Forward declarations */ +/****************************************************************/ + +static void genericDestruct(pCode *pc); +static void genericPrint(FILE *of,pCode *pc); + +static void pBlockStats(FILE *of, pBlock *pb); +static pCode *findFunction(const char *fname); +static void pCodePrintLabel(FILE *of, pCode *pc); +static void pCodePrintFunction(FILE *of, pCode *pc); +static void pCodeOpPrint(FILE *of, pCodeOp *pcop); +static char *get_op_from_instruction( pCodeInstruction *pcc); +static pBlock *newpBlock(void); + + +/****************************************************************/ +/* PIC Instructions */ +/****************************************************************/ + +static pCodeInstruction pciADDWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDWF, + "ADDWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciADDFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDFW, + "ADDWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciADDLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDLW, + "ADDLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_C | PCC_DC) // outCond +}; + +static pCodeInstruction pciANDLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ANDLW, + "ANDLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciANDWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ANDWF, + "ANDWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciANDFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ANDFW, + "ANDWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciBCF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BCF, + "BCF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_BSF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP) // outCond +}; + +static pCodeInstruction pciBSF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BSF, + "BSF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_BCF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP) // outCond +}; + +static pCodeInstruction pciBTFSC = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BTFSC, + "BTFSC", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_BTFSS, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciBTFSS = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BTFSS, + "BTFSS", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_BTFSC, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciCALL = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_CALL, + "CALL", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond, reads argument from WREG + (PCC_NONE | PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond, flags are destroyed by called function +}; + +static pCodeInstruction pciCOMF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_COMF, + "COMF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciCOMFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_COMFW, + "COMF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciCLRF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_CLRF, + "CLRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_REGISTER | PCC_Z // outCond +}; + +static pCodeInstruction pciCLRW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_CLRW, + "CLRW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciCLRWDT = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_CLRWDT, + "CLRWDT", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciDECF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_DECF, + "DECF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciDECFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_DECFW, + "DECF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciDECFSZ = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_DECFSZ, + "DECFSZ", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_DECF, // followed by BTFSC STATUS, Z --> also kills STATUS + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciDECFSZW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_DECFSZW, + "DECFSZ", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_DECFW, // followed by BTFSC STATUS, Z --> also kills STATUS + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciGOTO = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_GOTO, + "GOTO", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciINCF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_INCF, + "INCF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciINCFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_INCFW, + "INCF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciINCFSZ = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_INCFSZ, + "INCFSZ", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_INCF, // followed by BTFSC STATUS, Z --> also kills STATUS + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciINCFSZW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_INCFSZW, + "INCFSZ", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + POC_INCFW, // followed by BTFSC STATUS, Z --> also kills STATUS + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciIORWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_IORWF, + "IORWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciIORFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_IORFW, + "IORWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciIORLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_IORLW, + "IORLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciMOVF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVF, + "MOVF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_Z // outCond +}; + +static pCodeInstruction pciMOVFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVFW, + "MOVF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciMOVWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVWF, + "MOVWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_W, // inCond + PCC_REGISTER // outCond +}; + +static pCodeInstruction pciMOVLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLW, + "MOVLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_NONE | PCC_LITERAL), // inCond + PCC_W // outCond +}; + +static pCodeInstruction pciNOP = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_NOP, + "NOP", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciRETFIE = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RETFIE, + "RETFIE", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_NONE | PCC_C | PCC_DC | PCC_Z) // outCond (not true... affects the GIE bit too), STATUS bit are retored +}; + +static pCodeInstruction pciRETLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RETLW, + "RETLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_LITERAL, // inCond + (PCC_W| PCC_C | PCC_DC | PCC_Z) // outCond, STATUS bits are irrelevant after RETLW +}; + +static pCodeInstruction pciRETURN = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RETURN, + "RETURN", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond, return value is possibly present in W + (PCC_NONE | PCC_C | PCC_DC | PCC_Z) // outCond, STATUS bits are irrelevant after RETURN +}; + +static pCodeInstruction pciRLF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RLF, + "RLF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C) // outCond +}; + +static pCodeInstruction pciRLFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RLFW, + "RLF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C) // outCond +}; + +static pCodeInstruction pciRRF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RRF, + "RRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C) // outCond +}; + +static pCodeInstruction pciRRFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RRFW, + "RRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C) // outCond +}; + +static pCodeInstruction pciSUBWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SUBWF, + "SUBWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciSUBFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SUBFW, + "SUBWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciSUBLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SUBLW, + "SUBLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_C | PCC_DC) // outCond +}; + +static pCodeInstruction pciSWAPF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SWAPF, + "SWAPF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER // outCond +}; + +static pCodeInstruction pciSWAPFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SWAPFW, + "SWAPF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_W // outCond +}; + +static pCodeInstruction pciTRIS = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_TRIS, + "TRIS", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond /* FIXME: what's TRIS doing? */ + PCC_REGISTER // outCond /* FIXME: what's TRIS doing */ +}; + +static pCodeInstruction pciXORWF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_XORWF, + "XORWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z) // outCond +}; + +static pCodeInstruction pciXORFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_XORFW, + "XORWF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciXORLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_XORLW, + "XORLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z) // outCond +}; + + +static pCodeInstruction pciBANKSEL = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BANKSEL, + "BANKSEL", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciPAGESEL = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_PAGESEL, + "PAGESEL", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +/****************************************************************/ +/* PIC Enhanced Instructions */ +/****************************************************************/ + +static pCodeInstruction pciADDFSR = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDFSR, + "ADDFSR", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciADDWFC = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDWFC, + "ADDWFC", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciADDFWC = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ADDFWC, + "ADDWFC", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciASRF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ASRF, + "ASRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciASRFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_ASRFW, + "ASRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciBRA = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BRA, + "BRA", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciBRW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_BRW, + "BRW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciCALLW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_CALLW, + "CALLW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond, reads lower bits of subroutine address from WREG + (PCC_NONE | PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond, flags are destroyed by called function +}; + +static pCodeInstruction pciLSLF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_LSLF, + "LSLF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciLSLFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_LSLFW, + "LSLF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciLSRF = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_LSRF, + "LSRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciLSRFW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_LSRFW, + "LSRF", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_C | PCC_Z) // outCond +}; + +static pCodeInstruction pciMOVIW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVIW, + "MOVIW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_NONE | PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciMOVIW_K = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVIW_K, + "MOVIW", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_NONE | PCC_W | PCC_Z) // outCond +}; + +static pCodeInstruction pciMOVLB = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLB, + "MOVLB", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciMOVLP = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLP, + "MOVLP", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciMOVWI = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVWI, + "MOVWI", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciMOVWI_K = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVWI_K, + "MOVWI", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + POC_NOP, + (PCC_NONE | PCC_W), // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciRESET = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_RESET, + "RESET", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE // outCond +}; + +static pCodeInstruction pciSUBWFB = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SUBWFB, + "SUBWFB", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +static pCodeInstruction pciSUBWFBW = { + {PC_OPCODE, NULL, NULL, 0, 0, NULL, + genericDestruct, + genericPrint}, + POC_SUBWFBW, + "SUBWFB", + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_C | PCC_DC | PCC_Z) // outCond +}; + +pCodeInstruction *pic14Mnemonics[MAX_PIC14MNEMONICS]; + + +/*-----------------------------------------------------------------*/ +/* return a unique ID number to assist pCodes debuging */ +/*-----------------------------------------------------------------*/ +static unsigned PCodeID(void) { + static unsigned int pcodeId = 1; /* unique ID number to be assigned to all pCodes */ + /* + static unsigned int stop; + if (pcodeId == 1448) + stop++; // Place break point here + */ + return pcodeId++; +} + +void pCodeInitRegisters(void) +{ + static int initialized=0; + int shareBankAddress, stkSize, haveShared; + PIC_device *pic; + + if(initialized) + return; + initialized = 1; + + pic = init_pic(port->processor); + haveShared = pic14_getSharedStack(NULL, &shareBankAddress, &stkSize); + /* Set pseudo stack size to SHAREBANKSIZE - 3. + * On multi memory bank ICs this leaves room for WSAVE/SSAVE/PSAVE + * (used for interrupts) to fit into the shared portion of the + * memory bank. This is not needed on enhanced processors. */ + if (!pic->isEnhancedCore) + stkSize = stkSize - 3; + assert(stkSize >= 0); + initStack(shareBankAddress, stkSize, haveShared); + + /* TODO: Read aliases for SFRs from regmap lines in device description. */ + pc_status.r = allocProcessorRegister(IDX_STATUS,"STATUS", PO_STATUS, 0xf80); + pc_pcl.r = allocProcessorRegister(IDX_PCL,"PCL", PO_PCL, 0xf80); + pc_pclath.r = allocProcessorRegister(IDX_PCLATH,"PCLATH", PO_PCLATH, 0xf80); + pc_indf_.r = allocProcessorRegister(IDX_INDF,"INDF", PO_INDF, 0xf80); + pc_indf0.r = allocProcessorRegister(IDX_INDF0,"INDF0", PO_INDF, 0xf80); + pc_fsr.r = allocProcessorRegister(IDX_FSR,"FSR", PO_FSR, 0xf80); + pc_fsr0l.r = allocProcessorRegister(IDX_FSR0L,"FSR0L", PO_FSR, 0xf80); + pc_fsr0h.r = allocProcessorRegister(IDX_FSR0H,"FSR0H", PO_FSR, 0xf80); + pc_intcon.r = allocProcessorRegister(IDX_INTCON,"INTCON", PO_INTCON, 0xf80); + + pc_status.rIdx = IDX_STATUS; + pc_fsr.rIdx = IDX_FSR; + pc_fsr0l.rIdx = IDX_FSR0L; + pc_fsr0h.rIdx = IDX_FSR0H; + pc_indf_.rIdx = IDX_INDF; + pc_indf0.rIdx = IDX_INDF0; + pc_intcon.rIdx = IDX_INTCON; + pc_pcl.rIdx = IDX_PCL; + pc_pclath.rIdx = IDX_PCLATH; + + if (!pic->isEnhancedCore) { + /* Interrupt storage for working register - must be same address in all banks ie section SHAREBANK. */ + pc_wsave.r = allocInternalRegister(IDX_WSAVE,pc_wsave.pcop.name,pc_wsave.pcop.type, pic ? pic->bankMask : 0xf80); + /* Interrupt storage for status register. */ + pc_ssave.r = allocInternalRegister(IDX_SSAVE,pc_ssave.pcop.name,pc_ssave.pcop.type, (pic && haveShared) ? pic->bankMask : 0); + /* Interrupt storage for pclath register. */ + pc_psave.r = allocInternalRegister(IDX_PSAVE,pc_psave.pcop.name,pc_psave.pcop.type, (pic && haveShared) ? pic->bankMask : 0); + + pc_wsave.rIdx = pc_wsave.r->rIdx; + pc_ssave.rIdx = pc_ssave.r->rIdx; + pc_psave.rIdx = pc_psave.r->rIdx; + + pc_wsave.r->isFixed = 1; /* Some PIC ICs do not have a sharebank - this register needs to be reserved across all banks. */ + pc_wsave.r->address = shareBankAddress-stkSize; + pc_ssave.r->isFixed = 1; /* This register must be in the first bank. */ + pc_ssave.r->address = shareBankAddress-stkSize-1; + pc_psave.r->isFixed = 1; /* This register must be in the first bank. */ + pc_psave.r->address = shareBankAddress-stkSize-2; + } + + /* probably should put this in a separate initialization routine */ + pb_dead_pcodes = newpBlock(); + +} + +/*-----------------------------------------------------------------*/ +/* mnem2key - convert a pic mnemonic into a hash key */ +/* (BTW - this spreads the mnemonics quite well) */ +/* */ +/*-----------------------------------------------------------------*/ + +static int mnem2key(unsigned char const *mnem) +{ + int key = 0; + + if(!mnem) + return 0; + + while(*mnem) { + + key += toupper(*mnem++) +1; + + } + + return (key & 0x1f); + +} + +static void pic14initMnemonics(void) +{ + int i = 0; + int key; + // char *str; + pCodeInstruction *pci; + + if(mnemonics_initialized) + return; + + //FIXME - probably should NULL out the array before making the assignments + //since we check the array contents below this initialization. + + pic14Mnemonics[POC_ADDLW] = &pciADDLW; + pic14Mnemonics[POC_ADDWF] = &pciADDWF; + pic14Mnemonics[POC_ADDFW] = &pciADDFW; + pic14Mnemonics[POC_ANDLW] = &pciANDLW; + pic14Mnemonics[POC_ANDWF] = &pciANDWF; + pic14Mnemonics[POC_ANDFW] = &pciANDFW; + pic14Mnemonics[POC_BCF] = &pciBCF; + pic14Mnemonics[POC_BSF] = &pciBSF; + pic14Mnemonics[POC_BTFSC] = &pciBTFSC; + pic14Mnemonics[POC_BTFSS] = &pciBTFSS; + pic14Mnemonics[POC_CALL] = &pciCALL; + pic14Mnemonics[POC_COMF] = &pciCOMF; + pic14Mnemonics[POC_COMFW] = &pciCOMFW; + pic14Mnemonics[POC_CLRF] = &pciCLRF; + pic14Mnemonics[POC_CLRW] = &pciCLRW; + pic14Mnemonics[POC_CLRWDT] = &pciCLRWDT; + pic14Mnemonics[POC_DECF] = &pciDECF; + pic14Mnemonics[POC_DECFW] = &pciDECFW; + pic14Mnemonics[POC_DECFSZ] = &pciDECFSZ; + pic14Mnemonics[POC_DECFSZW] = &pciDECFSZW; + pic14Mnemonics[POC_GOTO] = &pciGOTO; + pic14Mnemonics[POC_INCF] = &pciINCF; + pic14Mnemonics[POC_INCFW] = &pciINCFW; + pic14Mnemonics[POC_INCFSZ] = &pciINCFSZ; + pic14Mnemonics[POC_INCFSZW] = &pciINCFSZW; + pic14Mnemonics[POC_IORLW] = &pciIORLW; + pic14Mnemonics[POC_IORWF] = &pciIORWF; + pic14Mnemonics[POC_IORFW] = &pciIORFW; + pic14Mnemonics[POC_MOVF] = &pciMOVF; + pic14Mnemonics[POC_MOVFW] = &pciMOVFW; + pic14Mnemonics[POC_MOVLW] = &pciMOVLW; + pic14Mnemonics[POC_MOVWF] = &pciMOVWF; + pic14Mnemonics[POC_NOP] = &pciNOP; + pic14Mnemonics[POC_RETFIE] = &pciRETFIE; + pic14Mnemonics[POC_RETLW] = &pciRETLW; + pic14Mnemonics[POC_RETURN] = &pciRETURN; + pic14Mnemonics[POC_RLF] = &pciRLF; + pic14Mnemonics[POC_RLFW] = &pciRLFW; + pic14Mnemonics[POC_RRF] = &pciRRF; + pic14Mnemonics[POC_RRFW] = &pciRRFW; + pic14Mnemonics[POC_SUBLW] = &pciSUBLW; + pic14Mnemonics[POC_SUBWF] = &pciSUBWF; + pic14Mnemonics[POC_SUBFW] = &pciSUBFW; + pic14Mnemonics[POC_SWAPF] = &pciSWAPF; + pic14Mnemonics[POC_SWAPFW] = &pciSWAPFW; + pic14Mnemonics[POC_TRIS] = &pciTRIS; + pic14Mnemonics[POC_XORLW] = &pciXORLW; + pic14Mnemonics[POC_XORWF] = &pciXORWF; + pic14Mnemonics[POC_XORFW] = &pciXORFW; + pic14Mnemonics[POC_BANKSEL] = &pciBANKSEL; + pic14Mnemonics[POC_PAGESEL] = &pciPAGESEL; + + /* Enhanced instruction set. */ + + pic14Mnemonics[POC_ADDFSR] = &pciADDFSR; + pic14Mnemonics[POC_ADDWFC] = &pciADDWFC; + pic14Mnemonics[POC_ADDFWC] = &pciADDFWC; + pic14Mnemonics[POC_ASRF] = &pciASRF; + pic14Mnemonics[POC_ASRFW] = &pciASRFW; + pic14Mnemonics[POC_BRA] = &pciBRA; + pic14Mnemonics[POC_BRW] = &pciBRW; + pic14Mnemonics[POC_CALLW] = &pciCALLW; + pic14Mnemonics[POC_LSLF] = &pciLSLF; + pic14Mnemonics[POC_LSLFW] = &pciLSLFW; + pic14Mnemonics[POC_LSRF] = &pciLSRF; + pic14Mnemonics[POC_LSRFW] = &pciLSRFW; + pic14Mnemonics[POC_MOVIW] = &pciMOVIW; + pic14Mnemonics[POC_MOVIW_K] = &pciMOVIW_K; + pic14Mnemonics[POC_MOVLB] = &pciMOVLB; + pic14Mnemonics[POC_MOVLP] = &pciMOVLP; + pic14Mnemonics[POC_MOVWI] = &pciMOVWI; + pic14Mnemonics[POC_MOVWI_K] = &pciMOVWI_K; + pic14Mnemonics[POC_RESET] = &pciRESET; + pic14Mnemonics[POC_SUBWFB] = &pciSUBWFB; + pic14Mnemonics[POC_SUBWFBW] = &pciSUBWFBW; + + + for(i=0; imnemonic), pic14Mnemonics[i]); + pci = hTabFirstItem(pic14MnemonicsHash, &key); + + while(pci) { + DFPRINTF((stderr, "element %d key %d, mnem %s\n",i++,key,pci->mnemonic)); + pci = hTabNextItem(pic14MnemonicsHash, &key); + } + + mnemonics_initialized = 1; +} + +int getpCode(const char *mnem, unsigned dest) +{ + + pCodeInstruction *pci; + int key = mnem2key((const unsigned char *)mnem); + + if(!mnemonics_initialized) + pic14initMnemonics(); + + pci = hTabFirstItemWK(pic14MnemonicsHash, key); + + while(pci) { + + if(STRCASECMP(pci->mnemonic, mnem) == 0) { + if((pci->num_ops <= 1) || (pci->isModReg == dest) || (pci->isBitInst)) + return(pci->op); + } + + pci = hTabNextItemWK (pic14MnemonicsHash); + + } + + return -1; +} + +/*-----------------------------------------------------------------* +* pic14initpCodePeepCommands +* +*-----------------------------------------------------------------*/ +void pic14initpCodePeepCommands(void) +{ + + int key, i; + peepCommand *pcmd; + + i = 0; + do { + hTabAddItem(&pic14pCodePeepCommandsHash, + mnem2key((const unsigned char *)peepCommands[i].cmd), &peepCommands[i]); + i++; + } while (peepCommands[i].cmd); + + pcmd = hTabFirstItem(pic14pCodePeepCommandsHash, &key); + + while(pcmd) { + //fprintf(stderr, "peep command %s key %d\n",pcmd->cmd,pcmd->id); + pcmd = hTabNextItem(pic14pCodePeepCommandsHash, &key); + } + +} + +/*----------------------------------------------------------------- +* +* +*-----------------------------------------------------------------*/ + +int getpCodePeepCommand(const char *cmd) +{ + + peepCommand *pcmd; + int key = mnem2key((const unsigned char *)cmd); + + + pcmd = hTabFirstItemWK(pic14pCodePeepCommandsHash, key); + + while(pcmd) { + // fprintf(stderr," comparing %s to %s\n",pcmd->cmd,cmd); + if(STRCASECMP(pcmd->cmd, cmd) == 0) { + return pcmd->id; + } + + pcmd = hTabNextItemWK (pic14pCodePeepCommandsHash); + + } + + return -1; +} + +static char getpBlock_dbName(pBlock *pb) +{ + if(!pb) + return 0; + + if(pb->cmemmap) + return pb->cmemmap->dbName; + + return pb->dbName; +} + +void pBlockConvert2ISR(pBlock *pb) +{ + if(!pb) + return; + + if(pb->cmemmap) + pb->cmemmap = NULL; + + pb->dbName = 'I'; +} + +/*-----------------------------------------------------------------*/ +/* movepBlock2Head - given the dbname of a pBlock, move all */ +/* instances to the front of the doubly linked */ +/* list of pBlocks */ +/*-----------------------------------------------------------------*/ + +void movepBlock2Head(char dbName) +{ + pBlock *pb; + + if (!the_pFile) + return; + + pb = the_pFile->pbHead; + + while(pb) { + + if(getpBlock_dbName(pb) == dbName) { + pBlock *pbn = pb->next; + pb->next = the_pFile->pbHead; + the_pFile->pbHead->prev = pb; + the_pFile->pbHead = pb; + + if(pb->prev) + pb->prev->next = pbn; + + // If the pBlock that we just moved was the last + // one in the link of all of the pBlocks, then we + // need to point the tail to the block just before + // the one we moved. + // Note: if pb->next is NULL, then pb must have + // been the last pBlock in the chain. + + if(pbn) + pbn->prev = pb->prev; + else + the_pFile->pbTail = pb->prev; + + pb = pbn; + } else + pb = pb->next; + } +} + +void copypCode(FILE *of, char dbName) +{ + pBlock *pb; + + if(!of || !the_pFile) + return; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(getpBlock_dbName(pb) == dbName) { + pBlockStats(of,pb); + printpBlock(of,pb); + fprintf (of, "\n"); + } + } +} + +void resetpCodeStatistics (void) +{ + pcode_insns = pcode_doubles = 0; +} + +void dumppCodeStatistics (FILE *of) +{ + /* dump statistics */ + fprintf (of, "\n"); + fprintf (of, ";\tcode size estimation:\n"); + fprintf (of, ";\t%5u+%5u = %5u instructions (%5u byte)\n", pcode_insns, pcode_doubles, pcode_insns + pcode_doubles, 2*(pcode_insns + 2*pcode_doubles)); + fprintf (of, "\n"); +} + +void pcode_test(void) +{ + DFPRINTF((stderr,"pcode is alive!\n")); + + //initMnemonics(); + + if(the_pFile) { + + pBlock *pb; + FILE *pFile; + char buffer[100]; + + /* create the file name */ + SNPRINTF(buffer, sizeof(buffer), "%s.p", dstFileName); + + if( !(pFile = fopen(buffer, "w" ))) { + werror(E_FILE_OPEN_ERR,buffer); + exit(1); + } + + fprintf(pFile,"pcode dump\n\n"); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(pFile,"\n\tNew pBlock\n\n"); + if(pb->cmemmap) + fprintf(pFile,"%s",pb->cmemmap->sname); + else + fprintf(pFile,"internal pblock"); + + fprintf(pFile,", dbName =%c\n",getpBlock_dbName(pb)); + printpBlock(pFile,pb); + } + } +} + +/*-----------------------------------------------------------------*/ +/* int RegCond(pCodeOp *pcop) - if pcop points to the STATUS reg- */ +/* ister, RegCond will return the bit being referenced. */ +/* */ +/* fixme - why not just OR in the pcop bit field */ +/*-----------------------------------------------------------------*/ + +static int RegCond(pCodeOp *pcop) +{ + if(!pcop) + return 0; + + if (pcop->type == PO_GPR_BIT) { + const char *name = pcop->name; + if (!name) + name = PCOR(pcop)->r->name; + if (strcmp(name, pc_status.pcop.name) == 0) + { + switch(PCORB(pcop)->bit) { + case PIC_C_BIT: + return PCC_C; + case PIC_DC_BIT: + return PCC_DC; + case PIC_Z_BIT: + return PCC_Z; + } + } + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* newpCode - create and return a newly initialized pCode */ +/* */ +/* fixme - rename this */ +/* */ +/* The purpose of this routine is to create a new Instruction */ +/* pCode. This is called by gen.c while the assembly code is being */ +/* generated. */ +/* */ +/* Inouts: */ +/* PIC_OPCODE op - the assembly instruction we wish to create. */ +/* (note that the op is analogous to but not the */ +/* same thing as the opcode of the instruction.) */ +/* pCdoeOp *pcop - pointer to the operand of the instruction. */ +/* */ +/* Outputs: */ +/* a pointer to the new malloc'd pCode is returned. */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +pCode *newpCode (PIC_OPCODE op, pCodeOp *pcop) +{ + pCodeInstruction *pci ; + + if(!mnemonics_initialized) + pic14initMnemonics(); + + pci = Safe_alloc(sizeof(pCodeInstruction)); + + if((op>=0) && (op < MAX_PIC14MNEMONICS) && pic14Mnemonics[op]) { + memcpy(pci, pic14Mnemonics[op], sizeof(pCodeInstruction)); + pci->pc.id = PCodeID(); + pci->pcop = pcop; + + if(pci->inCond & PCC_EXAMINE_PCOP) + pci->inCond |= RegCond(pcop); + + if(pci->outCond & PCC_EXAMINE_PCOP) + pci->outCond |= RegCond(pcop); + + pci->pc.prev = pci->pc.next = NULL; + return (pCode *)pci; + } + + fprintf(stderr, "pCode mnemonic error %s,%d\n",__FUNCTION__,__LINE__); + exit(1); + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* newpCodeWild - create a "wild" as in wild card pCode */ +/* */ +/* Wild pcodes are used during the peep hole optimizer to serve */ +/* as place holders for any instruction. When a snippet of code is */ +/* compared to a peep hole rule, the wild card opcode will match */ +/* any instruction. However, the optional operand and label are */ +/* additional qualifiers that must also be matched before the */ +/* line (of assembly code) is declared matched. Note that the */ +/* operand may be wild too. */ +/* */ +/* Note, a wild instruction is specified just like a wild var: */ +/* %4 ; A wild instruction, */ +/* See the peeph.def file for additional examples */ +/* */ +/*-----------------------------------------------------------------*/ + +pCode *newpCodeWild(int pCodeID, pCodeOp *optional_operand, pCodeOp *optional_label) +{ + pCodeWild *pcw; + + pcw = Safe_alloc(sizeof(pCodeWild)); + + pcw->pci.pc.type = PC_WILD; + pcw->pci.pc.prev = pcw->pci.pc.next = NULL; + pcw->id = PCodeID(); + pcw->pci.from = pcw->pci.to = pcw->pci.label = NULL; + pcw->pci.pc.pb = NULL; + + pcw->pci.pc.destruct = genericDestruct; + pcw->pci.pc.print = genericPrint; + + pcw->id = pCodeID; // this is the 'n' in %n + pcw->operand = optional_operand; + pcw->label = optional_label; + + pcw->mustBeBitSkipInst = 0; + pcw->mustNotBeBitSkipInst = 0; + pcw->invertBitSkipInst = 0; + + return ((pCode *)pcw); +} + +/*-----------------------------------------------------------------*/ +/* newPcodeCharP - create a new pCode from a char string */ +/*-----------------------------------------------------------------*/ + +pCode *newpCodeCharP(const char *cP) +{ + pCodeComment *pcc; + + pcc = Safe_alloc(sizeof(pCodeComment)); + + pcc->pc.type = PC_COMMENT; + pcc->pc.prev = pcc->pc.next = NULL; + pcc->pc.id = PCodeID(); + //pcc->pc.from = pcc->pc.to = pcc->pc.label = NULL; + pcc->pc.pb = NULL; + + pcc->pc.destruct = genericDestruct; + pcc->pc.print = genericPrint; + + if(cP) + pcc->comment = Safe_strdup(cP); + else + pcc->comment = NULL; + + return ((pCode *)pcc); +} + +/*-----------------------------------------------------------------*/ +/* newpCodeFunction - */ +/*-----------------------------------------------------------------*/ + +pCode *newpCodeFunction(const char *mod, const char *f, int isPublic, int isInterrupt) +{ + pCodeFunction *pcf; + + pcf = Safe_alloc(sizeof(pCodeFunction)); + + pcf->pc.type = PC_FUNCTION; + pcf->pc.prev = pcf->pc.next = NULL; + pcf->pc.id = PCodeID(); + //pcf->pc.from = pcf->pc.to = pcf->pc.label = NULL; + pcf->pc.pb = NULL; + + pcf->pc.destruct = genericDestruct; + pcf->pc.print = pCodePrintFunction; + + pcf->ncalled = 0; + + pcf->modname = (mod != NULL) ? Safe_strdup(mod) : NULL; + pcf->fname = (f != NULL) ? Safe_strdup(f) : NULL; + + pcf->isPublic = (unsigned int)isPublic; + pcf->isInterrupt = (unsigned int)isInterrupt; + + return ((pCode *)pcf); +} + +/*-----------------------------------------------------------------*/ +/* newpCodeFlow */ +/*-----------------------------------------------------------------*/ +static void destructpCodeFlow(pCode *pc) +{ + if(!pc || !isPCFL(pc)) + return; + + /* + if(PCFL(pc)->from) + if(PCFL(pc)->to) + */ + unlinkpCode(pc); + + deleteSet(&PCFL(pc)->registers); + deleteSet(&PCFL(pc)->from); + deleteSet(&PCFL(pc)->to); + free(pc); +} + +static pCode *newpCodeFlow(void ) +{ + pCodeFlow *pcflow; + + pcflow = Safe_alloc(sizeof(pCodeFlow)); + + pcflow->pc.type = PC_FLOW; + pcflow->pc.prev = pcflow->pc.next = NULL; + pcflow->pc.pb = NULL; + + pcflow->pc.destruct = destructpCodeFlow; + pcflow->pc.print = genericPrint; + + pcflow->pc.seq = GpcFlowSeq++; + + pcflow->from = pcflow->to = NULL; + + pcflow->inCond = PCC_NONE; + pcflow->outCond = PCC_NONE; + + pcflow->firstBank = 'U'; /* Undetermined */ + pcflow->lastBank = 'U'; /* Undetermined */ + + pcflow->FromConflicts = 0; + pcflow->ToConflicts = 0; + + pcflow->end = NULL; + + pcflow->registers = newSet(); + + return ((pCode *)pcflow); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static pCodeFlowLink *newpCodeFlowLink(pCodeFlow *pcflow) +{ + pCodeFlowLink *pcflowLink; + + pcflowLink = Safe_alloc(sizeof(pCodeFlowLink)); + + pcflowLink->pcflow = pcflow; + pcflowLink->bank_conflict = 0; + + return pcflowLink; +} + +/*-----------------------------------------------------------------*/ +/* newpCodeCSource - create a new pCode Source Symbol */ +/*-----------------------------------------------------------------*/ + +pCode *newpCodeCSource(int ln, const char *f, const char *l) +{ + pCodeCSource *pccs; + + pccs = Safe_alloc(sizeof(pCodeCSource)); + + pccs->pc.type = PC_CSOURCE; + pccs->pc.prev = pccs->pc.next = NULL; + pccs->pc.id = PCodeID(); + pccs->pc.pb = NULL; + + pccs->pc.destruct = genericDestruct; + pccs->pc.print = genericPrint; + + pccs->line_number = ln; + if(l) + pccs->line = Safe_strdup(l); + else + pccs->line = NULL; + + if(f) + pccs->file_name = Safe_strdup(f); + else + pccs->file_name = NULL; + + return ( (pCode *)pccs); +} + +/*******************************************************************/ +/* pic16_newpCodeAsmDir - create a new pCode Assembler Directive */ +/* added by VR 6-Jun-2003 */ +/*******************************************************************/ + +pCode *newpCodeAsmDir(const char *asdir, const char *argfmt, ...) +{ + pCodeAsmDir *pcad; + va_list ap; + char buffer[512]; + char *lbp=buffer; + + pcad = Safe_alloc(sizeof(pCodeAsmDir)); + pcad->pci.pc.type = PC_ASMDIR; + pcad->pci.pc.prev = pcad->pci.pc.next = NULL; + pcad->pci.pc.pb = NULL; + pcad->pci.pc.destruct = genericDestruct; + pcad->pci.pc.print = genericPrint; + + if(asdir && *asdir) { + while(isspace((unsigned char)*asdir)) asdir++; // strip any white space from the beginning + + pcad->directive = Safe_strdup(asdir); + } + + va_start(ap, argfmt); + + memset(buffer, 0, sizeof(buffer)); + if(argfmt && *argfmt) + vsprintf(buffer, argfmt, ap); + + va_end(ap); + + while(isspace((unsigned char)*lbp)) lbp++; + + if(lbp && *lbp) + pcad->arg = Safe_strdup(lbp); + + return ((pCode *)pcad); +} + +/*-----------------------------------------------------------------*/ +/* pCodeLabelDestruct - free memory used by a label. */ +/*-----------------------------------------------------------------*/ +static void pCodeLabelDestruct(pCode *pc) +{ + if(!pc) + return; + + if((pc->type == PC_LABEL) && PCL(pc)->label) + free(PCL(pc)->label); + + free(pc); +} + +pCode *newpCodeLabel(const char *name, int key) +{ + const char *s; + pCodeLabel *pcl; + + pcl = Safe_alloc(sizeof(pCodeLabel)); + + pcl->pc.type = PC_LABEL; + pcl->pc.prev = pcl->pc.next = NULL; + pcl->pc.id = PCodeID(); + //pcl->pc.from = pcl->pc.to = pcl->pc.label = NULL; + pcl->pc.pb = NULL; + + pcl->pc.destruct = pCodeLabelDestruct; + pcl->pc.print = pCodePrintLabel; + + pcl->key = key; + + pcl->label = NULL; + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", key); + s = buffer; + } else { +/* SNPRINTF(buffer, sizeof(buffer), "%s:", name); + s = buffer;*/ + s = name; + } + + if(s) + pcl->label = Safe_strdup(s); + + //fprintf(stderr,"newpCodeLabel: key=%d, name=%s\n",key, ((s)?s:"")); + return ((pCode *)pcl); +} + + +/*-----------------------------------------------------------------*/ +/* newpBlock - create and return a pointer to a new pBlock */ +/*-----------------------------------------------------------------*/ +static pBlock *newpBlock(void) +{ + pBlock *PpB; + + PpB = Safe_alloc(sizeof(pBlock)); + PpB->next = PpB->prev = NULL; + + PpB->function_entries = PpB->function_exits = PpB->function_calls = NULL; + PpB->tregisters = NULL; + PpB->visited = 0; + PpB->FlowTree = NULL; + + return PpB; +} + +/*-----------------------------------------------------------------*/ +/* newpCodeChain - create a new chain of pCodes */ +/*-----------------------------------------------------------------* +* +* This function will create a new pBlock and the pointer to the +* pCode that is passed in will be the first pCode in the block. +*-----------------------------------------------------------------*/ + + +pBlock *newpCodeChain(memmap *cm,char c, pCode *pc) +{ + pBlock *pB = newpBlock(); + + pB->pcHead = pB->pcTail = pc; + pB->cmemmap = cm; + pB->dbName = c; + + return pB; +} + +/*-----------------------------------------------------------------*/ +/* newpCodeOpLabel - Create a new label given the key */ +/* Note, a negative key means that the label is part of wild card */ +/* (and hence a wild card label) used in the pCodePeep */ +/* optimizations). */ +/*-----------------------------------------------------------------*/ + +pCodeOp *newpCodeOpLabel(const char *name, int key) +{ + const char *s; + static int label_key = -1; + + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLabel)); + pcop->type = PO_LABEL; + + pcop->name = NULL; + + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", key); + s = buffer; + } + else { + s = name; + key = label_key--; + } + + PCOLAB(pcop)->offset = 0; + if(s) + pcop->name = Safe_strdup(s); + + ((pCodeOpLabel *)pcop)->key = key; + + //fprintf(stderr,"newpCodeOpLabel: key=%d, name=%s\n",key,((s)?s:"")); + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *newpCodeOpLit(int lit) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLit)); + pcop->type = PO_LITERAL; + + pcop->name = NULL; + if(lit>=0) { + SNPRINTF(buffer, sizeof(buffer),"0x%02x", (unsigned char)lit); + pcop->name = Safe_strdup(buffer); + } + + ((pCodeOpLit *)pcop)->lit = (unsigned char)lit; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *newpCodeOpImmd(const char *name, int offset, int index, int code_space, int is_func) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpImmd)); + pcop->type = PO_IMMEDIATE; + if(name) { + reg_info *r = NULL; + pcop->name = Safe_strdup(name); + + if(!is_func) + r = dirregWithName(name); + + PCOI(pcop)->r = r; + if(r) { + //fprintf(stderr, " newpCodeOpImmd reg %s exists\n",name); + PCOI(pcop)->rIdx = r->rIdx; + } else { + //fprintf(stderr, " newpCodeOpImmd reg %s doesn't exist\n",name); + PCOI(pcop)->rIdx = -1; + } + //fprintf(stderr,"%s %s %d\n",__FUNCTION__,name,offset); + } else { + pcop->name = NULL; + } + + PCOI(pcop)->index = index; + PCOI(pcop)->offset = offset; + PCOI(pcop)->_const = code_space; + PCOI(pcop)->_function = is_func; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *newpCodeOpWild(int id, pCodeWildBlock *pcwb, pCodeOp *subtype) +{ + pCodeOp *pcop; + + if(!pcwb || !subtype) { + fprintf(stderr, "Wild opcode declaration error: %s-%d\n",__FILE__,__LINE__); + exit(1); + } + + pcop = Safe_alloc(sizeof(pCodeOpWild)); + pcop->type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id); + pcop->name = Safe_strdup(buffer); + + PCOW(pcop)->id = id; + PCOW(pcop)->pcwb = pcwb; + PCOW(pcop)->subtype = subtype; + PCOW(pcop)->matched = NULL; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* Find a symbol with matching name */ +/*-----------------------------------------------------------------*/ +static symbol *symFindWithName(memmap * map, const char *name) +{ + symbol *sym; + + for (sym = setFirstItem(map->syms); sym; sym = setNextItem (map->syms)) { + if (sym->rname && (strcmp(sym->rname,name)==0)) + return sym; + } + return 0; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *newpCodeOpBit(const char *name, int ibit, int inBitSpace) +{ + pCodeOp *pcop; + struct reg_info *r = 0; + + pcop = Safe_alloc(sizeof(pCodeOpRegBit)); + pcop->type = PO_GPR_BIT; + + PCORB(pcop)->bit = ibit; + PCORB(pcop)->inBitSpace = inBitSpace; + + if (name) r = regFindWithName(name); + if (name && !r) { + // Register has not been allocated - check for symbol information + symbol *sym; + sym = symFindWithName(bit, name); + if (!sym) sym = symFindWithName(sfrbit, name); + if (!sym) sym = symFindWithName(sfr, name); + if (!sym) sym = symFindWithName(reg, name); + // Hack to fix accesses to _INTCON_bits (e.g. GIE=0), see #1579535. + // XXX: This ignores nesting levels, but works for globals... + if (!sym) sym = findSym(SymbolTab, NULL, name); + if (!sym && name && name[0] == '_') sym = findSym(SymbolTab, NULL, &name[1]); + if (sym) { + r = allocNewDirReg(sym->etype,name); + } + } + if (r) { + pcop->name = NULL; + PCOR(pcop)->r = r; + PCOR(pcop)->rIdx = r->rIdx; + } else if (name) { + pcop->name = Safe_strdup(name); + PCOR(pcop)->r = NULL; + PCOR(pcop)->rIdx = 0; + } else { + //fprintf(stderr, "Unnamed register duplicated for bit-access?!? Hope for the best ...\n"); + } + return pcop; +} + +/*-----------------------------------------------------------------* +* pCodeOp *newpCodeOpReg(int rIdx) - allocate a new register +* +* If rIdx >=0 then a specific register from the set of registers +* will be selected. If rIdx <0, then a new register will be searched +* for. +*-----------------------------------------------------------------*/ + +static pCodeOp *newpCodeOpReg(int rIdx) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + + pcop->name = NULL; + + if(rIdx >= 0) { + PCOR(pcop)->rIdx = rIdx; + PCOR(pcop)->r = pic14_regWithIdx(rIdx); + } else { + PCOR(pcop)->r = pic14_findFreeReg(REG_GPR); + + if(PCOR(pcop)->r) + PCOR(pcop)->rIdx = PCOR(pcop)->r->rIdx; + } + + if(PCOR(pcop)->r) + pcop->type = PCOR(pcop)->r->pc_type; + + return pcop; +} + +pCodeOp *newpCodeOpRegFromStr(const char *name) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR(pcop)->r = allocRegByName(name, 1); + PCOR(pcop)->rIdx = PCOR(pcop)->r->rIdx; + pcop->type = PCOR(pcop)->r->pc_type; + pcop->name = PCOR(pcop)->r->name; + + return pcop; +} + +static pCodeOp *newpCodeOpStr(const char *name) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpStr)); + pcop->type = PO_STR; + pcop->name = Safe_strdup(name); + + PCOS(pcop)->isPublic = 0; + + return pcop; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +pCodeOp *newpCodeOp(const char *name, PIC_OPTYPE type) +{ + pCodeOp *pcop; + + switch(type) { + case PO_BIT: + case PO_GPR_BIT: + pcop = newpCodeOpBit(name, -1, 0); + break; + + case PO_LITERAL: + pcop = newpCodeOpLit(-1); + break; + + case PO_LABEL: + pcop = newpCodeOpLabel(NULL,-1); + break; + + case PO_GPR_TEMP: + pcop = newpCodeOpReg(-1); + break; + + case PO_GPR_POINTER: + case PO_GPR_REGISTER: + if(name) + pcop = newpCodeOpRegFromStr(name); + else + pcop = newpCodeOpReg(-1); + break; + + case PO_STR: + pcop = newpCodeOpStr(name); + break; + + default: + pcop = Safe_alloc(sizeof(pCodeOp)); + pcop->type = type; + pcop->name = (name != NULL) ? Safe_strdup(name) : NULL; + } + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* addpCode2pBlock - place the pCode into the pBlock linked list */ +/*-----------------------------------------------------------------*/ +void addpCode2pBlock(pBlock *pb, pCode *pc) +{ + + if(!pb || !pc) + return; + + if(!pb->pcHead) { + /* If this is the first pcode to be added to a block that + * was initialized with a NULL pcode, then go ahead and + * make this pcode the head and tail */ + pb->pcHead = pb->pcTail = pc; + } else { + // if(pb->pcTail) + pb->pcTail->next = pc; + + pc->prev = pb->pcTail; + pc->pb = pb; + + pb->pcTail = pc; + } +} + +/*-----------------------------------------------------------------*/ +/* addpBlock - place a pBlock into the pFile */ +/*-----------------------------------------------------------------*/ +void addpBlock(pBlock *pb) +{ + // fprintf(stderr," Adding pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + if(!the_pFile) { + /* First time called, we'll pass through here. */ + //_ALLOC(the_pFile,sizeof(pFile)); + the_pFile = Safe_alloc(sizeof(pFile)); + the_pFile->pbHead = the_pFile->pbTail = pb; + the_pFile->functions = NULL; + return; + } + + the_pFile->pbTail->next = pb; + pb->prev = the_pFile->pbTail; + pb->next = NULL; + the_pFile->pbTail = pb; +} + +/*-----------------------------------------------------------------*/ +/* removepBlock - remove a pBlock from the pFile */ +/*-----------------------------------------------------------------*/ +static void removepBlock(pBlock *pb) +{ + pBlock *pbs; + + if(!the_pFile) + return; + + //fprintf(stderr," Removing pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + for(pbs = the_pFile->pbHead; pbs; pbs = pbs->next) { + if(pbs == pb) { + + if(pbs == the_pFile->pbHead) + the_pFile->pbHead = pbs->next; + + if (pbs == the_pFile->pbTail) + the_pFile->pbTail = pbs->prev; + + if(pbs->next) + pbs->next->prev = pbs->prev; + + if(pbs->prev) + pbs->prev->next = pbs->next; + + return; + } + } + + fprintf(stderr, "Warning: call to %s:%s didn't find pBlock\n",__FILE__,__FUNCTION__); +} + +/*-----------------------------------------------------------------*/ +/* printpCode - write the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ + +void printpCode(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + if(pc->print) { + pc->print(of,pc); + return; + } + + fprintf(of,"warning - unable to print pCode\n"); +} + +/*-----------------------------------------------------------------*/ +/* printpBlock - write the contents of a pBlock to a file */ +/*-----------------------------------------------------------------*/ + +void printpBlock(FILE *of, pBlock *pb) +{ + pCode *pc; + + if(!pb) + return; + + if(!of) + of = stderr; + + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCF(pc) && PCF(pc)->fname && !PCF(pc)->isInterrupt) { + fprintf(of, "S_%s_%s\tcode\n", PCF(pc)->modname, PCF(pc)->fname); + } + printpCode(of,pc); + + if (isPCI(pc)) + { + if (isPCI(pc) && (PCI(pc)->op == POC_PAGESEL || PCI(pc)->op == POC_BANKSEL)) { + pcode_doubles++; + } else { + pcode_insns++; + } + } + } // for +} + +/*-----------------------------------------------------------------*/ +/* */ +/* pCode processing */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ + +void unlinkpCode(pCode *pc) +{ + if(pc) { +#ifdef PCODE_DEBUG + fprintf(stderr,"Unlinking: "); + printpCode(stderr, pc); +#endif + if(pc->prev) + pc->prev->next = pc->next; + if(pc->next) + pc->next->prev = pc->prev; + +#if 0 + /* RN: I believe this should be right here, but this did not + * cure the bug I was hunting... */ + /* must keep labels -- attach to following instruction */ + if (isPCI(pc) && PCI(pc)->label && pc->next) + { + pCodeInstruction *pcnext = PCI(findNextInstruction (pc->next)); + if (pcnext) + { + pBranchAppend (pcnext->label, PCI(pc)->label); + } + } +#endif + pc->prev = pc->next = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void genericDestruct(pCode *pc) +{ + unlinkpCode(pc); + + if(isPCI(pc)) { + /* For instructions, tell the register (if there's one used) + * that it's no longer needed */ + reg_info *reg = getRegFromInstruction(pc); + if(reg) + deleteSetItem (&(reg->reglives.usedpCodes),pc); + } + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + + addpCode2pBlock(pb_dead_pcodes, pc); + + //free(pc); +} + + +/*-----------------------------------------------------------------*/ +/* Copies the pCodeInstruction flow pointer from source pCode */ +/*-----------------------------------------------------------------*/ +static void CopyFlow(pCodeInstruction *pcd, pCode *pcs) { + pCode *p; + pCodeFlow *pcflow = 0; + for (p=pcs; p; p=p->prev) { + if (isPCI(p)) { + pcflow = PCI(p)->pcflow; + break; + } + if (isPCF(p)) { + pcflow = (pCodeFlow*)p; + break; + } + } + PCI(pcd)->pcflow = pcflow; +} + +/*-----------------------------------------------------------------*/ +/* pCodeInsertAfter - splice in the pCode chain starting with pc2 */ +/* into the pCode chain containing pc1 */ +/*-----------------------------------------------------------------*/ +void pCodeInsertAfter(pCode *pc1, pCode *pc2) +{ + if(!pc1 || !pc2) + return; + + pc2->next = pc1->next; + if(pc1->next) + pc1->next->prev = pc2; + + pc2->pb = pc1->pb; + pc2->prev = pc1; + pc1->next = pc2; + + /* If this is an instrution type propogate the flow */ + if (isPCI(pc2)) + CopyFlow(PCI(pc2),pc1); +} + +/*------------------------------------------------------------------*/ +/* pCodeInsertBefore - splice in the pCode chain starting with pc2 */ +/* into the pCode chain containing pc1 */ +/*------------------------------------------------------------------*/ +void pCodeInsertBefore(pCode *pc1, pCode *pc2) +{ + if(!pc1 || !pc2) + return; + + pc2->prev = pc1->prev; + if(pc1->prev) + pc1->prev->next = pc2; + + pc2->pb = pc1->pb; + pc2->next = pc1; + pc1->prev = pc2; + + /* If this is an instrution type propogate the flow */ + if (isPCI(pc2)) + CopyFlow(PCI(pc2),pc1); +} + +/*-----------------------------------------------------------------*/ +/* pCodeOpCopy - copy a pcode operator */ +/*-----------------------------------------------------------------*/ +pCodeOp *pCodeOpCopy(pCodeOp *pcop) +{ + pCodeOp *pcopnew=NULL; + + if(!pcop) + return NULL; + + switch(pcop->type) { + case PO_NONE: + case PO_STR: + pcopnew = Safe_malloc(sizeof(pCodeOp)); + memcpy(pcopnew, pcop, sizeof(pCodeOp)); + break; + + case PO_W: + case PO_STATUS: + case PO_FSR: + case PO_INDF: + case PO_INTCON: + case PO_GPR_REGISTER: + case PO_GPR_TEMP: + case PO_GPR_POINTER: + case PO_SFR_REGISTER: + case PO_PCL: + case PO_PCLATH: + case PO_DIR: + //DFPRINTF((stderr,"pCodeOpCopy GPR register\n")); + pcopnew = Safe_malloc(sizeof(pCodeOpReg)); + memcpy(pcopnew, pcop, sizeof(pCodeOpReg)); + DFPRINTF((stderr," register index %d\n", PCOR(pcop)->r->rIdx)); + break; + + case PO_LITERAL: + //DFPRINTF((stderr,"pCodeOpCopy lit\n")); + pcopnew = Safe_malloc(sizeof(pCodeOpLit)); + memcpy(pcopnew, pcop, sizeof(pCodeOpLit)); + break; + + case PO_IMMEDIATE: + pcopnew = Safe_malloc(sizeof(pCodeOpImmd)); + memcpy(pcopnew, pcop, sizeof(pCodeOpImmd)); + break; + + case PO_GPR_BIT: + case PO_CRY: + case PO_BIT: + //DFPRINTF((stderr,"pCodeOpCopy bit\n")); + pcopnew = Safe_malloc(sizeof(pCodeOpRegBit)); + memcpy(pcopnew, pcop, sizeof(pCodeOpRegBit)); + break; + + case PO_LABEL: + //DFPRINTF((stderr,"pCodeOpCopy label\n")); + pcopnew = Safe_malloc(sizeof(pCodeOpLabel)); + memcpy(pcopnew, pcop, sizeof(pCodeOpLabel)); + break; + + case PO_WILD: + /* Here we expand the wild card into the appropriate type: */ + /* By recursively calling pCodeOpCopy */ + //DFPRINTF((stderr,"pCodeOpCopy wild\n")); + if(PCOW(pcop)->matched) + pcopnew = pCodeOpCopy(PCOW(pcop)->matched); + else { + // Probably a label + pcopnew = pCodeOpCopy(PCOW(pcop)->subtype); + pcopnew->name = Safe_strdup(PCOW(pcop)->pcwb->vars[PCOW(pcop)->id]); + //DFPRINTF((stderr,"copied a wild op named %s\n",pcopnew->name)); + } + + return pcopnew; + break; + + default: + assert ( !"unhandled pCodeOp type copied" ); + break; + } // switch + + pcopnew->name = (pcop->name != NULL) ? Safe_strdup(pcop->name) : NULL; + + return pcopnew; +} + +/*-----------------------------------------------------------------*/ +/* popCopyReg - copy a pcode operator */ +/*-----------------------------------------------------------------*/ +pCodeOp *popCopyReg(pCodeOpReg *pc) +{ + pCodeOpReg *pcor; + + pcor = Safe_alloc(sizeof(pCodeOpReg)); + pcor->pcop.type = pc->pcop.type; + if(pc->pcop.name) { + if(!(pcor->pcop.name = Safe_strdup(pc->pcop.name))) + fprintf(stderr,"oops %s %d",__FILE__,__LINE__); + } else + pcor->pcop.name = NULL; + + if (pcor->pcop.type == PO_IMMEDIATE){ + PCOL(pcor)->lit = PCOL(pc)->lit; + } else { + pcor->r = pc->r; + pcor->rIdx = pc->rIdx; + if (pcor->r) + pcor->r->wasUsed=1; + } + //DEBUGpic14_emitcode ("; ***","%s , copying %s, rIdx=%d",__FUNCTION__,pc->pcop.name,pc->rIdx); + + return PCOP(pcor); +} + +/*-----------------------------------------------------------------*/ +/* pCodeInstructionCopy - copy a pCodeInstructionCopy */ +/*-----------------------------------------------------------------*/ +pCode *pCodeInstructionCopy(pCodeInstruction *pci,int invert) +{ + pCodeInstruction *new_pci; + + if(invert) + new_pci = PCI(newpCode(pci->inverted_op,pci->pcop)); + else + new_pci = PCI(newpCode(pci->op,pci->pcop)); + + new_pci->pc.pb = pci->pc.pb; + new_pci->from = pci->from; + new_pci->to = pci->to; + new_pci->label = pci->label; + new_pci->pcflow = pci->pcflow; + + return PCODE(new_pci); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +void pCodeDeleteChain(pCode *f,pCode *t) +{ + pCode *pc; + + while(f && f!=t) { + DFPRINTF((stderr,"delete pCode:\n")); + pc = f->next; + //f->print(stderr,f); + //f->delete(f); this dumps core... + f = pc; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +char *get_op(pCodeOp *pcop,char *buffer, size_t size) +{ + reg_info *r; + static char b[50]; + char *s; + int use_buffer = 1; // copy the string to the passed buffer pointer + + if(!buffer) { + buffer = b; + size = sizeof(b); + use_buffer = 0; // Don't bother copying the string to the buffer. + } + + if(pcop) { + switch(pcop->type) { + case PO_INDF: + case PO_FSR: + if(use_buffer) { + SNPRINTF(buffer,size,"%s",PCOR(pcop)->r->name); + return buffer; + } + return pcop->name; + break; + case PO_GPR_TEMP: + if (PCOR(pcop)->r->type == REG_STK) + r = typeRegWithIdx(PCOR(pcop)->r->rIdx,REG_STK,1); + else + r = pic14_regWithIdx(PCOR(pcop)->r->rIdx); + + if(use_buffer) { + SNPRINTF(buffer,size,"%s",r->name); + return buffer; + } + + return r->name; + break; + + case PO_IMMEDIATE: + s = buffer; + if(PCOI(pcop)->_const) { + + if( PCOI(pcop)->offset >= 0 && PCOI(pcop)->offset<4) { + switch(PCOI(pcop)->offset) { + case 0: + SNPRINTF(s,size,"low (%s+%d)",pcop->name, PCOI(pcop)->index); + break; + case 1: + SNPRINTF(s,size,"high (%s+%d)",pcop->name, PCOI(pcop)->index); + break; + case 2: + SNPRINTF(s,size,"0x%02x",PCOI(pcop)->_const ? GPTRTAG_CODE : GPTRTAG_DATA); + break; + default: + fprintf (stderr, "PO_IMMEDIATE/_const/offset=%d\n", PCOI(pcop)->offset); + assert ( !"offset too large" ); + SNPRINTF(s,size,"(((%s+%d) >> %d)&0xff)", + pcop->name, + PCOI(pcop)->index, + 8 * PCOI(pcop)->offset ); + } + } else + SNPRINTF(s,size,"LOW (%s+%d)",pcop->name,PCOI(pcop)->index); + } else { + if( !PCOI(pcop)->offset) { // && PCOI(pcc->pcop)->offset<4) + SNPRINTF(s,size,"(%s + %d)", + pcop->name, + PCOI(pcop)->index); + } else { + switch(PCOI(pcop)->offset) { + case 0: + SNPRINTF(s,size,"(%s + %d)",pcop->name, PCOI(pcop)->index); + break; + case 1: + SNPRINTF(s,size,"high (%s + %d)",pcop->name, PCOI(pcop)->index); + break; + case 2: + SNPRINTF(s,size,"0x%02x",PCOI(pcop)->_const ? GPTRTAG_CODE : GPTRTAG_DATA); + break; + default: + fprintf (stderr, "PO_IMMEDIATE/mutable/offset=%d\n", PCOI(pcop)->offset); + assert ( !"offset too large" ); + SNPRINTF(s,size,"((%s + %d) >> %d)&0xff",pcop->name, PCOI(pcop)->index, 8*PCOI(pcop)->offset); + break; + } + } + } + return buffer; + break; + + case PO_DIR: + s = buffer; + if( PCOR(pcop)->instance) { + SNPRINTF(s,size,"(%s + %d)", + pcop->name, + PCOR(pcop)->instance ); + } else + SNPRINTF(s,size,"%s",pcop->name); + return buffer; + break; + + case PO_LABEL: + s = buffer; + if (pcop->name) { + if(PCOLAB(pcop)->offset == 1) + SNPRINTF(s,size,"HIGH(%s)",pcop->name); + else + SNPRINTF(s,size,"%s",pcop->name); + } + return buffer; + break; + + case PO_GPR_BIT: + if(PCOR(pcop)->r) { + if(use_buffer) { + SNPRINTF(buffer,size,"%s",PCOR(pcop)->r->name); + return buffer; + } + return PCOR(pcop)->r->name; + } + /* fall through to the default case */ + + default: + if(pcop->name) { + if(use_buffer) { + SNPRINTF(buffer,size,"%s",pcop->name); + return buffer; + } + return pcop->name; + } + } + } + + printf("PIC port internal warning: (%s:%d(%s)) %s not found\n", + __FILE__, __LINE__, __FUNCTION__, + pCodeOpType(pcop)); + + return "NO operand"; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static char *get_op_from_instruction( pCodeInstruction *pcc) +{ + if(pcc) + return get_op(pcc->pcop,NULL,0); + + return ("ERROR Null: get_op_from_instruction"); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodeOpPrint(FILE *of, pCodeOp *pcop) +{ + fprintf(of,"pcodeopprint- not implemented\n"); +} + +/*-----------------------------------------------------------------*/ +/* pCode2str - convert a pCode instruction to string */ +/*-----------------------------------------------------------------*/ +char *pCode2str(char *str, size_t size, pCode *pc) +{ + char *s = str; + + switch(pc->type) { + + case PC_OPCODE: + + SNPRINTF(s,size, "\t%s\t", PCI(pc)->mnemonic); + size -= strlen(s); + s += strlen(s); + + if( (PCI(pc)->num_ops >= 1) && (PCI(pc)->pcop)) { + if(PCI(pc)->isBitInst) { + if(PCI(pc)->pcop->type == PO_GPR_BIT) { + char *name = PCI(pc)->pcop->name; + if (!name) + name = PCOR(PCI(pc)->pcop)->r->name; + if( (((pCodeOpRegBit *)(PCI(pc)->pcop))->inBitSpace) ) + SNPRINTF(s,size,"(%s >> 3), (%s & 7)", name, name); + else + SNPRINTF(s,size,"%s,%d", name, (((pCodeOpRegBit *)(PCI(pc)->pcop))->bit)&7); + } else if(PCI(pc)->pcop->type == PO_GPR_BIT) { + SNPRINTF(s,size,"%s,%d", get_op_from_instruction(PCI(pc)),PCORB(PCI(pc)->pcop)->bit); + } else + SNPRINTF(s,size,"%s,0 ; ?bug", get_op_from_instruction(PCI(pc))); + } else { + if(PCI(pc)->pcop->type == PO_GPR_BIT) { + if( PCI(pc)->num_ops == 2) + SNPRINTF(s,size,"(%s >> 3),%c",get_op_from_instruction(PCI(pc)),((PCI(pc)->isModReg) ? 'F':'W')); + else + SNPRINTF(s,size,"(1 << (%s & 7))",get_op_from_instruction(PCI(pc))); + } else { + SNPRINTF(s,size,"%s",get_op_from_instruction(PCI(pc))); + size -= strlen(s); + s += strlen(s); + if( PCI(pc)->num_ops == 2) + SNPRINTF(s,size,",%c", ( (PCI(pc)->isModReg) ? 'F':'W')); + } + } + } + break; + + case PC_COMMENT: + /* assuming that comment ends with a \n */ + SNPRINTF(s,size,";%s", ((pCodeComment *)pc)->comment); + break; + + case PC_INLINE: + /* assuming that inline code ends with a \n */ + SNPRINTF(s,size,"%s", ((pCodeComment *)pc)->comment); + break; + + case PC_LABEL: + SNPRINTF(s,size,";label=%s, key=%d\n",PCL(pc)->label,PCL(pc)->key); + break; + case PC_FUNCTION: + SNPRINTF(s,size,";modname=%s,function=%s: id=%d\n",PCF(pc)->modname,PCF(pc)->fname); + break; + case PC_WILD: + SNPRINTF(s,size,";\tWild opcode: id=%d\n",PCW(pc)->id); + break; + case PC_FLOW: + SNPRINTF(s,size,";\t--FLOW change\n"); + break; + case PC_CSOURCE: + SNPRINTF(s,size,"%s\t.line\t%d; \"%s\"\t%s\n",(options.debug?"":";"),PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + break; + case PC_ASMDIR: + if(PCAD(pc)->directive) { + SNPRINTF(s,size,"\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else if(PCAD(pc)->arg) { + /* special case to handle inline labels without a tab */ + SNPRINTF(s,size,"%s\n", PCAD(pc)->arg); + } + break; + + case PC_BAD: + SNPRINTF(s,size,";A bad pCode is being used\n"); + } + + return str; +} + +/*-----------------------------------------------------------------*/ +/* genericPrint - the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ +static void genericPrint(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + switch(pc->type) { + case PC_COMMENT: + fprintf(of,";%s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_INLINE: + fprintf(of,"%s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_OPCODE: + // If the opcode has a label, print that first + { + char str[256]; + pCodeInstruction *pci = PCI(pc); + pBranch *pbl = pci->label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + + if(pci->cline) + genericPrint(of,PCODE(pci->cline)); + + + pCode2str(str, sizeof(str), pc); + + fprintf(of,"%s",str); + + /* Debug */ + if(debug_verbose) { + pCodeOpReg *pcor = PCOR(pci->pcop); + fprintf(of, "\t;id=%u,key=%03x,inCond:%x,outCond:%x",pc->id,pc->seq, pci->inCond, pci->outCond); + if(pci->pcflow) + fprintf(of,",flow seq=%03x",pci->pcflow->pc.seq); + if (pcor && pcor->pcop.type==PO_GPR_TEMP && !pcor->r->isFixed) + fprintf(of,",rIdx=r0x%X",pcor->rIdx); + } + } + fprintf(of,"\n"); + break; + + case PC_WILD: + fprintf(of,";\tWild opcode: id=%d\n",PCW(pc)->id); + if(PCW(pc)->pci.label) + pCodePrintLabel(of, PCW(pc)->pci.label->pc); + + if(PCW(pc)->operand) { + fprintf(of,";\toperand "); + pCodeOpPrint(of,PCW(pc)->operand ); + } + break; + + case PC_FLOW: + if(debug_verbose) { + fprintf(of,";<>Start of new flow, seq=0x%x",pc->seq); + if(PCFL(pc)->ancestor) + fprintf(of," ancestor = 0x%x", PCODE(PCFL(pc)->ancestor)->seq); + fprintf(of,"\n"); + fprintf(of,"; from: "); + { + pCodeFlowLink *link; + for (link = setFirstItem(PCFL(pc)->from); link; link = setNextItem (PCFL(pc)->from)) + { + fprintf(of,"%03x ",link->pcflow->pc.seq); + } + } + fprintf(of,"; to: "); + { + pCodeFlowLink *link; + for (link = setFirstItem(PCFL(pc)->to); link; link = setNextItem (PCFL(pc)->to)) + { + fprintf(of,"%03x ",link->pcflow->pc.seq); + } + } + fprintf(of,"\n"); + } + break; + + case PC_CSOURCE: + fprintf(of,"%s\t.line\t%d; \"%s\"\t%s\n", (options.debug?"":";"), PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + break; + + case PC_ASMDIR: + { + pBranch *pbl = PCAD(pc)->pci.label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + if(PCAD(pc)->directive) { + fprintf(of, "\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else + if(PCAD(pc)->arg) { + /* special case to handle inline labels without tab */ + fprintf(of, "%s\n", PCAD(pc)->arg); + } + break; + + case PC_LABEL: + default: + fprintf(of,"unknown pCode type %d\n",pc->type); + } +} + +/*-----------------------------------------------------------------*/ +/* pCodePrintFunction - prints function begin/end */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintFunction(FILE *of, pCode *pc) +{ + + if(!pc || !of) + return; + +#if 0 + if( ((pCodeFunction *)pc)->modname) + fprintf(of,"F_%s",((pCodeFunction *)pc)->modname); +#endif + if(PCF(pc)->fname) { + pBranch *exits = PCF(pc)->to; + int i=0; + + fprintf(of, "%s:", PCF(pc)->fname); + + if(options.verbose) + fprintf(of, "\t;Function start"); + + fprintf(of, "\n"); + + while(exits) { + i++; + exits = exits->next; + } + //if(i) i--; + fprintf(of,"; %d exit point%c\n",i, ((i==1) ? ' ':'s')); + + }else { + if((PCF(pc)->from && + PCF(pc)->from->pc->type == PC_FUNCTION && + PCF(PCF(pc)->from->pc)->fname) ) + fprintf(of,"; exit point of %s\n",PCF(PCF(pc)->from->pc)->fname); + else + fprintf(of,"; exit point [can't find entry point]\n"); + } +} + +/*-----------------------------------------------------------------*/ +/* pCodePrintLabel - prints label */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintLabel(FILE *of, pCode *pc) +{ + + if(!pc || !of) + return; + + if(PCL(pc)->label) + fprintf(of, "%s:\n", PCL(pc)->label); + else if (PCL(pc)->key >= 0) + fprintf(of, "_%05d_DS_:\n", PCL(pc)->key); + else + fprintf(of, ";wild card label: id=%d\n", -PCL(pc)->key); + +} + +/*-----------------------------------------------------------------*/ +/* unlinkpCodeFromBranch - Search for a label in a pBranch and */ +/* remove it if it is found. */ +/*-----------------------------------------------------------------*/ +static void unlinkpCodeFromBranch(pCode *pcl , pCode *pc) +{ + pBranch *b, *bprev; + + bprev = NULL; + + if(pcl->type == PC_OPCODE || pcl->type == PC_INLINE || pcl->type == PC_ASMDIR) + b = PCI(pcl)->label; + else { + fprintf(stderr, "LINE %d. can't unlink from non opcode\n",__LINE__); + exit(1); + } + + //fprintf (stderr, "%s \n",__FUNCTION__); + //pcl->print(stderr,pcl); + //pc->print(stderr,pc); + while(b) { + if(b->pc == pc) { + //fprintf (stderr, "found label\n"); + + /* Found a label */ + if(bprev) { + bprev->next = b->next; /* Not first pCode in chain */ + free(b); + } else { + pc->destruct(pc); + PCI(pcl)->label = b->next; /* First pCode in chain */ + free(b); + } + return; /* A label can't occur more than once */ + } + bprev = b; + b = b->next; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pBranch * pBranchAppend(pBranch *h, pBranch *n) +{ + pBranch *b; + + if(!h) + return n; + + if(h == n) + return n; + + b = h; + while(b->next) + b = b->next; + + b->next = n; + + return h; + +} + +/*-----------------------------------------------------------------*/ +/* pBranchLink - given two pcodes, this function will link them */ +/* together through their pBranches */ +/*-----------------------------------------------------------------*/ +static void pBranchLink(pCodeFunction *f, pCodeFunction *t) +{ + pBranch *b; + + // Declare a new branch object for the 'from' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(t); // The link to the 'to' pCode. + b->next = NULL; + + f->to = pBranchAppend(f->to,b); + + // Now do the same for the 'to' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(f); + b->next = NULL; + + t->from = pBranchAppend(t->from,b); + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int compareLabel(pCode *pc, pCodeOpLabel *pcop_label) +{ + pBranch *pbr; + + if(pc->type == PC_LABEL) { + if( ((pCodeLabel *)pc)->key == pcop_label->key) + return TRUE; + } + if(pc->type == PC_OPCODE || pc->type == PC_ASMDIR) { + pbr = PCI(pc)->label; + while(pbr) { + if(pbr->pc->type == PC_LABEL) { + if( ((pCodeLabel *)(pbr->pc))->key == pcop_label->key) + return TRUE; + } + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int checkLabel(pCode *pc) +{ + pBranch *pbr; + + if(pc && isPCI(pc)) { + pbr = PCI(pc)->label; + while(pbr) { + if(isPCL(pbr->pc) && (PCL(pbr->pc)->key >= 0)) + return TRUE; + + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* findLabelinpBlock - Search the pCode for a particular label */ +/*-----------------------------------------------------------------*/ +static pCode * findLabelinpBlock(pBlock *pb,pCodeOpLabel *pcop_label) +{ + pCode *pc; + + if(!pb) + return NULL; + + for(pc = pb->pcHead; pc; pc = pc->next) + if(compareLabel(pc,pcop_label)) + return pc; + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* findNextpCode - given a pCode, find the next of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * findNextpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) + return pc; + + pc = pc->next; + } + + return NULL; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* findPrevpCode - given a pCode, find the previous of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +static pCode * findPrevpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) { + /* + static unsigned int stop; + if (pc->id == 524) + stop++; // Place break point here + */ + return pc; + } + + pc = pc->prev; + } + + return NULL; +} +#endif + +/*-----------------------------------------------------------------*/ +/* findNextInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * findNextInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + if((pc->type == PC_OPCODE) + || (pc->type == PC_WILD) + || (pc->type == PC_ASMDIR)) + return pc; + +#ifdef PCODE_DEBUG + fprintf(stderr,"findNextInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->next; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* findNextInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * findPrevInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + + if((pc->type == PC_OPCODE) + || (pc->type == PC_WILD) + || (pc->type == PC_ASMDIR)) + return pc; + + +#ifdef PCODE_DEBUG + fprintf(stderr,"pic16_findPrevInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->prev; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +reg_info * getRegFromInstruction(pCode *pc) +{ + reg_info *r; + if(!pc || + !isPCI(pc) || + !PCI(pc)->pcop || + PCI(pc)->num_ops == 0 ) + return NULL; + + switch(PCI(pc)->pcop->type) { + case PO_STATUS: + case PO_FSR: + case PO_INDF: + case PO_INTCON: + case PO_BIT: + case PO_GPR_TEMP: + case PO_SFR_REGISTER: + case PO_PCL: + case PO_PCLATH: + return PCOR(PCI(pc)->pcop)->r; + + case PO_GPR_REGISTER: + case PO_GPR_BIT: + case PO_DIR: + r = PCOR(PCI(pc)->pcop)->r; + if (r) + return r; + return dirregWithName(PCI(pc)->pcop->name); + + case PO_LITERAL: + break; + + case PO_IMMEDIATE: + r = PCOI(PCI(pc)->pcop)->r; + if (r) + return r; + return dirregWithName(PCI(pc)->pcop->name); + + default: + break; + } + + return NULL; + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void AnalyzepBlock(pBlock *pb) +{ + pCode *pc; + + if(!pb) + return; + + /* Find all of the registers used in this pBlock + * by looking at each instruction and examining it's + * operands + */ + for(pc = pb->pcHead; pc; pc = pc->next) { + + /* Is this an instruction with operands? */ + if(pc->type == PC_OPCODE && PCI(pc)->pcop) { + + if((PCI(pc)->pcop->type == PO_GPR_TEMP) + || ((PCI(pc)->pcop->type == PO_GPR_BIT) && PCOR(PCI(pc)->pcop)->r && (PCOR(PCI(pc)->pcop)->r->pc_type == PO_GPR_TEMP))) { + + /* Loop through all of the registers declared so far in + this block and see if we find this one there */ + + reg_info *r = setFirstItem(pb->tregisters); + + while(r) { + if((r->rIdx == PCOR(PCI(pc)->pcop)->r->rIdx) && (r->type == PCOR(PCI(pc)->pcop)->r->type)) { + PCOR(PCI(pc)->pcop)->r = r; + break; + } + r = setNextItem(pb->tregisters); + } + + if(!r) { + /* register wasn't found */ + //r = Safe_alloc(sizeof(regs)); + //memcpy(r,PCOR(PCI(pc)->pcop)->r, sizeof(regs)); + //addSet(&pb->tregisters, r); + addSet(&pb->tregisters, PCOR(PCI(pc)->pcop)->r); + //PCOR(PCI(pc)->pcop)->r = r; + //fprintf(stderr,"added register to pblock: reg %d\n",r->rIdx); + }/* else + fprintf(stderr,"found register in pblock: reg %d\n",r->rIdx); + */ + } + if(PCI(pc)->pcop->type == PO_GPR_REGISTER) { + if(PCOR(PCI(pc)->pcop)->r) { + pic14_allocWithIdx (PCOR(PCI(pc)->pcop)->r->rIdx); + DFPRINTF((stderr,"found register in pblock: reg 0x%x\n",PCOR(PCI(pc)->pcop)->r->rIdx)); + } else { + if(PCI(pc)->pcop->name) + fprintf(stderr,"ERROR: %s is a NULL register\n",PCI(pc)->pcop->name ); + else + fprintf(stderr,"ERROR: NULL register\n"); + } + } + } + + + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ +static void InsertpFlow(pCode *pc, pCode **pflow) +{ + if(*pflow) + PCFL(*pflow)->end = pc; + + if(!pc || !pc->next) + return; + + *pflow = newpCodeFlow(); + pCodeInsertAfter(pc, *pflow); +} + +/*-----------------------------------------------------------------*/ +/* BuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* +* BuildFlow inserts pCodeFlow objects into the pCode chain at each +* point the instruction flow changes. +*/ +/*-----------------------------------------------------------------*/ +static void BuildFlow(pBlock *pb) +{ + pCode *pc; + pCode *last_pci=NULL; + pCode *pflow=NULL; + int seq = 0; + + if(!pb) + return; + + //fprintf (stderr,"build flow start seq %d ",GpcFlowSeq); + /* Insert a pCodeFlow object at the beginning of a pBlock */ + + InsertpFlow(pb->pcHead, &pflow); + + //pflow = newpCodeFlow(); /* Create a new Flow object */ + //pflow->next = pb->pcHead; /* Make the current head the next object */ + //pb->pcHead->prev = pflow; /* let the current head point back to the flow object */ + //pb->pcHead = pflow; /* Make the Flow object the head */ + //pflow->pb = pb; + + for( pc = findNextInstruction(pb->pcHead); + pc != NULL; + pc=findNextInstruction(pc)) { + + pc->seq = seq++; + PCI(pc)->pcflow = PCFL(pflow); + + //fprintf(stderr," build: "); + //pc->print(stderr, pc); + //pflow->print(stderr,pflow); + + if (checkLabel(pc)) { + + /* This instruction marks the beginning of a + * new flow segment */ + + pc->seq = 0; + seq = 1; + + /* If the previous pCode is not a flow object, then + * insert a new flow object. (This check prevents + * two consecutive flow objects from being insert in + * the case where a skip instruction preceeds an + * instruction containing a label.) */ + + last_pci = findPrevInstruction (pc->prev); + + if(last_pci && (PCI(last_pci)->pcflow == PCFL(pflow))) + InsertpFlow(last_pci, &pflow); + + PCI(pc)->pcflow = PCFL(pflow); + + } + + if(isPCI_SKIP(pc)) { + + /* The two instructions immediately following this one + * mark the beginning of a new flow segment */ + + while(pc && isPCI_SKIP(pc)) { + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = seq-1; + seq = 1; + + InsertpFlow(pc, &pflow); + pc=findNextInstruction(pc->next); + } + + seq = 0; + + if(!pc) + break; + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = 0; + InsertpFlow(pc, &pflow); + + } else if ( isPCI_BRANCH(pc) && !checkLabel(findNextInstruction(pc->next))) { + + InsertpFlow(pc, &pflow); + seq = 0; + + } + + last_pci = pc; + pc = pc->next; + } + + //fprintf (stderr,",end seq %d",GpcFlowSeq); + if(pflow) + PCFL(pflow)->end = pb->pcTail; +} + +/*-------------------------------------------------------------------*/ +/* unBuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* +* unBuildFlow removes pCodeFlow objects from a pCode chain +*/ +/*-----------------------------------------------------------------*/ +static void unBuildFlow(pBlock *pb) +{ + pCode *pc,*pcnext; + + if(!pb) + return; + + pc = pb->pcHead; + + while(pc) { + pcnext = pc->next; + + if(isPCI(pc)) { + + pc->seq = 0; + if(PCI(pc)->pcflow) { + //free(PCI(pc)->pcflow); + PCI(pc)->pcflow = NULL; + } + + } else if(isPCFL(pc) ) + pc->destruct(pc); + + pc = pcnext; + } + + +} + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void dumpCond(int cond) +{ + + static char *pcc_str[] = { + //"PCC_NONE", + "PCC_REGISTER", + "PCC_C", + "PCC_Z", + "PCC_DC", + "PCC_W", + "PCC_EXAMINE_PCOP", + "PCC_REG_BANK0", + "PCC_REG_BANK1", + "PCC_REG_BANK2", + "PCC_REG_BANK3" + }; + + int ncond = sizeof(pcc_str) / sizeof(char *); + int i,j; + + fprintf(stderr, "0x%04X\n",cond); + + for(i=0,j=1; ipc.seq); + + pc = findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + fprintf(stderr, " FlowStats - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + + fprintf(stderr, " FlowStats inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FlowStats outCond: "); + dumpCond(pcflow->outCond); + +} +#endif + +/*-----------------------------------------------------------------* +* int isBankInstruction(pCode *pc) - examine the pCode *pc to determine +* if it affects the banking bits. +* +* return: -1 == Banking bits are unaffected by this pCode. +* +* return: > 0 == Banking bits are affected. +* +* If the banking bits are affected, then the returned value describes +* which bits are affected and how they're affected. The lower half +* of the integer maps to the bits that are affected, the upper half +* to whether they're set or cleared. +* +*-----------------------------------------------------------------*/ +/* +static int isBankInstruction(pCode *pc) +{ + regs *reg; + int bank = -1; + + if(!isPCI(pc)) + return -1; + + if( ( (reg = getRegFromInstruction(pc)) != NULL) && isSTATUS_REG(reg)) { + + // Check to see if the register banks are changing + if(PCI(pc)->isModReg) { + + pCodeOp *pcop = PCI(pc)->pcop; + switch(PCI(pc)->op) { + + case POC_BSF: + if(PCORB(pcop)->bit == PIC_RP0_BIT) { + //fprintf(stderr, " isBankInstruction - Set RP0\n"); + return SET_BANK_BIT | PIC_RP0_BIT; + } + + if(PCORB(pcop)->bit == PIC_RP1_BIT) { + //fprintf(stderr, " isBankInstruction - Set RP1\n"); + return CLR_BANK_BIT | PIC_RP0_BIT; + } + break; + + case POC_BCF: + if(PCORB(pcop)->bit == PIC_RP0_BIT) { + //fprintf(stderr, " isBankInstruction - Clr RP0\n"); + return CLR_BANK_BIT | PIC_RP1_BIT; + } + if(PCORB(pcop)->bit == PIC_RP1_BIT) { + //fprintf(stderr, " isBankInstruction - Clr RP1\n"); + return CLR_BANK_BIT | PIC_RP1_BIT; + } + break; + default: + //fprintf(stderr, " isBankInstruction - Status register is getting Modified by:\n"); + //genericPrint(stderr, pc); + ; + } + } + + } + + return bank; +} +*/ + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +/* +static void FillFlow(pCodeFlow *pcflow) +{ + pCode *pc; + int cur_bank; + + if(!isPCFL(pcflow)) + return; + + // fprintf(stderr, " FillFlow - flow block (seq=%d)\n", pcflow->pc.seq); + + pc = findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + //fprintf(stderr, " FillFlow - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + cur_bank = -1; + + do { + isBankInstruction(pc); + pc = pc->next; + } while (pc && (pc != pcflow->end) && !isPCFL(pc)); + / * + if(!pc ) { + fprintf(stderr, " FillFlow - Bad end of flow\n"); + } else { + fprintf(stderr, " FillFlow - Ending flow with\n "); + pc->print(stderr,pc); + } + + fprintf(stderr, " FillFlow inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FillFlow outCond: "); + dumpCond(pcflow->outCond); + * / +} +*/ + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void LinkFlow_pCode(pCodeInstruction *from, pCodeInstruction *to) +{ + pCodeFlowLink *fromLink, *toLink; +#if 0 + fprintf(stderr, "%s: linking ", __FUNCTION__ ); + if (from) from->pc.print(stderr, &from->pc); + else fprintf(stderr, "(null)"); + fprintf(stderr, " -(%u)-> with -(%u)-> ", + from && from->pcflow ? from->pcflow->pc.seq : 0, + to && to->pcflow ? to->pcflow->pc.seq : 0); + if (to) to->pc.print(stderr, &to->pc); + else fprintf(stderr, "(null)"); +#endif + + if(!from || !to || !to->pcflow || !from->pcflow) + return; + + fromLink = newpCodeFlowLink(from->pcflow); + toLink = newpCodeFlowLink(to->pcflow); + + addSetIfnotP(&(from->pcflow->to), toLink); //to->pcflow); + addSetIfnotP(&(to->pcflow->from), fromLink); //from->pcflow); + +} + +/*-----------------------------------------------------------------* +* void LinkFlow(pBlock *pb) +* +* In BuildFlow, the PIC code has been partitioned into contiguous +* non-branching segments. In LinkFlow, we determine the execution +* order of these segments. For example, if one of the segments ends +* with a skip, then we know that there are two possible flow segments +* to which control may be passed. +*-----------------------------------------------------------------*/ +static void LinkFlow(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcflow; + pCode *pct; + + //fprintf(stderr,"linkflow \n"); + + if (!pb) return; + + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) + fprintf(stderr, "LinkFlow - pcflow is not a flow object "); + + //fprintf(stderr," link: "); + //pcflow->print(stderr,pcflow); + + //FillFlow(PCFL(pcflow)); + + /* find last instruction in flow */ + pc = findPrevInstruction (PCFL(pcflow)->end); + if (!pc) { + fprintf(stderr, "%s: flow without end (%u)?\n", + __FUNCTION__, pcflow->seq ); + continue; + } + + //fprintf(stderr, "LinkFlow - flow block (seq=%d) ", pcflow->seq); + //pc->print(stderr, pc); + if(isPCI_SKIP(pc)) { + //fprintf(stderr, "ends with skip\n"); + //pc->print(stderr,pc); + pct=findNextInstruction(pc->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + pct=findNextInstruction(pct->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + continue; + } + + if(isPCI_BRANCH(pc)) { + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + + //fprintf(stderr, "ends with branch\n "); + //pc->print(stderr,pc); + + if(!(pcol && isPCOLAB(pcol))) { + if((PCI(pc)->op != POC_RETLW) + && (PCI(pc)->op != POC_RETURN) + && (PCI(pc)->op != POC_CALL) + && (PCI(pc)->op != POC_RETFIE) ) + { + pc->print(stderr,pc); + fprintf(stderr, "ERROR: %s, branch instruction doesn't have label\n",__FUNCTION__); + } + } else { + + if( (pct = findLabelinpBlock(pb,pcol)) != NULL) + LinkFlow_pCode(PCI(pc),PCI(pct)); + else + fprintf(stderr, "ERROR: %s, couldn't find label. key=%d,lab=%s\n", + __FUNCTION__,pcol->key,((PCOP(pcol)->name)?PCOP(pcol)->name:"-")); + //fprintf(stderr,"newpCodeOpLabel: key=%d, name=%s\n",key,((s)?s:"")); + } + /* link CALLs to next instruction */ + if (PCI(pc)->op != POC_CALL) continue; + } + + if(isPCI(pc)) { + //fprintf(stderr, "ends with non-branching instruction:\n"); + //pc->print(stderr,pc); + + LinkFlow_pCode(PCI(pc),PCI(findNextInstruction(pc->next))); + + continue; + } + + if(pc) { + //fprintf(stderr, "ends with unknown\n"); + //pc->print(stderr,pc); + continue; + } + + fprintf(stderr, "ends with nothing: ERROR\n"); + + } +} + +static void pCodeReplace (pCode *old, pCode *new) +{ + pCodeInsertAfter (old, new); + + /* special handling for pCodeInstructions */ + if (isPCI(new) && isPCI(old)) + { + //assert (!PCI(new)->from && !PCI(new)->to && !PCI(new)->label && /*!PCI(new)->pcflow && */!PCI(new)->cline); + PCI(new)->from = PCI(old)->from; + PCI(new)->to = PCI(old)->to; + PCI(new)->label = PCI(old)->label; + PCI(new)->pcflow = PCI(old)->pcflow; + PCI(new)->cline = PCI(old)->cline; + } // if + + old->destruct (old); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void addpCodeComment(pCode *pc, const char *fmt, ...) +{ + va_list ap; + char buffer[4096]; + pCode *newpc; + + va_start(ap, fmt); + if (options.verbose || debug_verbose) { + buffer[0] = ';'; + buffer[1] = ' '; + vsprintf(&buffer[2], fmt, ap); + + newpc = newpCodeCharP(&buffer[0]); // strdup's the string + pCodeInsertAfter(pc, newpc); + } + va_end(ap); +} + +/*-----------------------------------------------------------------*/ +/* Inserts a new pCodeInstruction before an existing one */ +/*-----------------------------------------------------------------*/ +static void insertPCodeInstruction(pCodeInstruction *pci, pCodeInstruction *new_pci) +{ + pCode *pcprev; + + pcprev = findPrevInstruction(pci->pc.prev); + + pCodeInsertAfter(pci->pc.prev, &new_pci->pc); + + /* Move the label, if there is one */ + + if(pci->label) { + new_pci->label = pci->label; + pci->label = NULL; + } + + /* Move the C code comment, if there is one */ + + if(pci->cline) { + new_pci->cline = pci->cline; + pci->cline = NULL; + } + + /* The new instruction has the same pcflow block */ + new_pci->pcflow = pci->pcflow; + + /* Arrrrg: is pci's previous instruction is a skip, we need to + * change that into a jump (over pci and the new instruction) ... */ + if (pcprev && isPCI_SKIP(pcprev)) + { + symbol *lbl = newiTempLabel (NULL); + pCode *label = newpCodeLabel (NULL, lbl->key); + pCode *jump = newpCode(POC_GOTO, newpCodeOpLabel(NULL, lbl->key)); + + pCodeInsertAfter (pcprev, jump); + + // Yuck: Cannot simply replace INCFSZ/INCFSZW/DECFSZ/DECFSZW + // We replace them with INCF/INCFW/DECF/DECFW followed by 'BTFSS STATUS, Z' + switch (PCI(pcprev)->op) { + case POC_INCFSZ: + case POC_INCFSZW: + case POC_DECFSZ: + case POC_DECFSZW: + // These are turned into non-skipping instructions, so + // insert 'BTFSC STATUS, Z' after pcprev + pCodeInsertAfter (jump->prev, newpCode(POC_BTFSC, popCopyGPR2Bit(PCOP(&pc_status), PIC_Z_BIT))); + break; + default: + // no special actions required + break; + } + pCodeReplace (pcprev, pCodeInstructionCopy (PCI(pcprev), 1)); + pcprev = NULL; + pCodeInsertAfter((pCode*)pci, label); + pBlockMergeLabels(pci->pc.pb); + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int insertBankSel(pCodeInstruction *pci, const char *name) +{ + pCode *new_pc; + + pCodeOp *pcop; + + // Never BANKSEL STATUS, this breaks all kinds of code (e.g., interrupt handlers). + if (!strcmp("STATUS", name) || !strcmp("_STATUS", name)) return 0; + + pcop = popCopyReg(PCOR(pci->pcop)); + pcop->type = PO_GPR_REGISTER; // Sometimes the type is set to legacy 8051 - so override it + if (pcop->name == 0) + pcop->name = strdup(name); + new_pc = newpCode(POC_BANKSEL, pcop); + + insertPCodeInstruction(pci, PCI(new_pc)); + return 1; +} + +/* + * isValidIdChar - check if c may be present in an identifier + */ +static int isValidIdChar (char c) +{ + if (c >= 'a' && c <= 'z') return 1; + if (c >= 'A' && c <= 'Z') return 1; + if (c >= '0' && c <= '9') return 1; + if (c == '_') return 1; + return 0; +} + +/* + * bankcompare - check if two operand string refer to the same register + * This functions handles NAME and (NAME + x) in both operands. + * Returns 1 on same register, 0 on different (or unknown) registers. + */ +static int bankCompare(const char *op1, const char *op2) +{ + int i; + + if (!op1 && !op2) return 0; // both unknown, might be different though! + if (!op1 || !op2) return 0; + + // find start of operand name + while (op1[0] == '(' || op1[0] == ' ') op1++; + while (op2[0] == '(' || op2[0] == ' ') op2++; + + // compare till first non-identifier character + for (i = 0; (op1[i] == op2[i]) && isValidIdChar(op1[i]); i++); + if (!isValidIdChar(op1[i]) && !isValidIdChar(op2[i])) return 1; + + // play safe---assume different operands + return 0; +} + +/* + * Interface to BANKSEL generation. + * This function should return != 0 iff str1 and str2 denote operands that + * are known to be allocated into the same bank. Consequently, there will + * be no BANKSEL emitted if str2 is accessed while str1 has been used to + * select the current bank just previously. + * + * If in doubt, return 0. + */ +static int +pic14_operandsAllocatedInSameBank(const char *str1, const char *str2) { + // see glue.c(pic14printLocals) + + if (getenv("SDCC_PIC14_SPLIT_LOCALS")) { + // no clustering applied, each register resides in its own bank + } else { + // check whether BOTH names are local registers + // XXX: This is some kind of shortcut, should be safe... + // In this model, all r0xXXXX are allocated into a single section + // per file, so no BANKSEL required if accessing a r0xXXXX after a + // (different) r0xXXXX. Works great for multi-byte operands. + if (str1 && str2 && str1[0] == 'r' && str2[0] == 'r') return (1); + } // if + + // assume operands in different banks + return (0); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int sameBank(reg_info *reg, reg_info *previous_reg, const char *new_bank, const char *cur_bank, unsigned max_mask) +{ + if (!cur_bank) return 0; + + if (previous_reg && reg && previous_reg->isFixed && reg->isFixed && ((previous_reg->address & max_mask) == (reg->address & max_mask))) // only if exists + return 1; // if we have address info, we use it for banksel optimization + + // regard '(regname + X)' and '(regname + Y)' as equal + if (reg && reg->name && bankCompare(reg->name, cur_bank)) return 1; + if (new_bank && bankCompare(new_bank, cur_bank)) return 1; + + // check allocation policy from glue.c + if (reg && reg->name && pic14_operandsAllocatedInSameBank(reg->name, cur_bank)) return 1; + if (new_bank && pic14_operandsAllocatedInSameBank(new_bank, cur_bank)) return 1; + + // seems to be a different operand--might be a different bank + //printf ("BANKSEL from %s to %s/%s\n", cur_bank, reg->name, new_bank); + return 0; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void FixRegisterBanking(pBlock *pb) +{ + pCode *pc; + pCodeInstruction *pci; + reg_info *reg; + reg_info *previous_reg; // contains the previous variable access info + const char *cur_bank, *new_bank; + unsigned cur_mask, new_mask, max_mask; + int allRAMmshared; + + if (!pb) return; + + max_mask = pic14_getPIC()->bankMask; + cur_mask = max_mask; + cur_bank = NULL; + previous_reg = NULL; + + allRAMmshared = pic14_allRAMShared(); + + for (pc = pb->pcHead; pc; pc = pc->next) + { + // this one has a label---might check bank at all jumps here... + if (isPCI(pc) && (PCI(pc)->label || PCI(pc)->op == POC_CALL)) { + addpCodeComment(pc->prev, "BANKOPT3 drop assumptions: PCI with label or call found"); + previous_reg = NULL; + cur_bank = NULL; // start new flow + cur_mask = max_mask; + } + + // this one is/might be a label or BANKSEL---assume nothing + if (isPCL(pc) || isPCASMDIR(pc)) { + addpCodeComment(pc->prev, "BANKOPT4 drop assumptions: label or ASMDIR found"); + previous_reg = NULL; + cur_bank = NULL; + cur_mask = max_mask; + } + + // this one modifies STATUS + // XXX: this should be checked, but usually BANKSELs are not done this way in generated code + + if (isPCI(pc)) { + pci = PCI(pc); + if ((pci->inCond | pci->outCond) & PCC_REGISTER) { + // might need a BANKSEL + reg = getRegFromInstruction(pc); + + if (reg) { + new_bank = reg->name; + // reg->alias == 0: reg is in only one bank, we do not know which (may be any bank) + // reg->alias != 0: reg is in 2/4/8/2**N banks, we select one of them + new_mask = reg->alias; + } else if (pci->pcop && pci->pcop->name) { + new_bank = pci->pcop->name; + new_mask = 0; // unknown, assume worst case + } else { + assert(!"Could not get register from instruction."); + new_bank = "UNKNOWN"; + new_mask = 0; // unknown, assume worst case + } + + // optimizations... + // XXX: add switch to disable these + if (1) { + // reg present in all banks possibly selected? + if (new_mask == max_mask || (cur_mask && ((new_mask & cur_mask) == cur_mask))) { + // no BANKSEL required + addpCodeComment(pc->prev, "BANKOPT1 BANKSEL dropped; %s present in all of %s's banks", new_bank, cur_bank); + continue; + } + + // only one bank of memory and no SFR accessed? + // XXX: We can do better with fixed registers. + if (allRAMmshared && reg && (reg->type != REG_SFR) && (!reg->isFixed)) { + // no BANKSEL required + addpCodeComment(pc->prev, "BANKOPT1b BANKSEL dropped; %s present in all (of %s's) banks", new_bank, cur_bank); + continue; + } + + if (sameBank(reg, previous_reg, new_bank, cur_bank, max_mask)) { + // no BANKSEL required + addpCodeComment(pc->prev, "BANKOPT2 BANKSEL dropped; %s present in same bank as %s", new_bank, cur_bank); + continue; + } + } // if + + if (insertBankSel(pci, new_bank)) { + cur_mask = new_mask; + cur_bank = new_bank; + previous_reg = reg; + } // if + } // if + } // if + } // for +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepBlock(pBlock *pb) +{ + pCode *pc, *pcprev; + int matches =0; + + if(!pb || options.nopeep) + return 0; + + DFPRINTF((stderr," Optimizing pBlock: %c\n",getpBlock_dbName(pb))); + /* + for(pc = pb->pcHead; pc; pc = pc->next) + matches += pCodePeepMatchRule(pc); + */ + + pc = findNextInstruction(pb->pcHead); + if(!pc) + return 0; + + pcprev = pc->prev; + do { + if(pCodePeepMatchRule(pc)) { + matches++; + + if(pcprev) + pc = findNextInstruction(pcprev->next); + else + pc = findNextInstruction(pb->pcHead); + } else + pc = findNextInstruction(pc->next); + } while(pc); + + if(matches) + DFPRINTF((stderr," Optimizing pBlock: %c - matches=%d\n",getpBlock_dbName(pb),matches)); + + return matches; +} + +/*-----------------------------------------------------------------*/ +/* pBlockRemoveUnusedLabels - remove the pCode labels from the */ +/*-----------------------------------------------------------------*/ +static pCode * findInstructionUsingLabel(pCodeLabel *pcl, pCode *pcs) +{ + pCode *pc; + + for(pc = pcs; pc; pc = pc->next) { + + if(((pc->type == PC_OPCODE) || (pc->type == PC_INLINE) || (pc->type == PC_ASMDIR)) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL) && + (PCOLAB(PCI(pc)->pcop)->key == pcl->key)) + return pc; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void exchangeLabels(pCodeLabel *pcl, pCode *pc) +{ + const char *s; + + if(isPCI(pc) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL)) { + + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + + //fprintf(stderr,"changing label key from %d to %d\n",pcol->key, pcl->key); + if(pcol->pcop.name) + free(pcol->pcop.name); + + /* If the key is negative, then we (probably) have a label to + * a function and the name is already defined */ + + if(pcl->key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", pcl->key); + s = buffer; + } else + s = pcl->label; + + //SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", pcl->key); + if(!s) { + fprintf(stderr, "ERROR %s:%d function label is null\n",__FUNCTION__,__LINE__); + } + pcol->pcop.name = Safe_strdup(s); + pcol->key = pcl->key; + //pc->print(stderr,pc); + } +} + +/*-----------------------------------------------------------------*/ +/* pBlockRemoveUnusedLabels - remove the pCode labels from the */ +/* pCode chain if they're not used. */ +/*-----------------------------------------------------------------*/ +static void pBlockRemoveUnusedLabels(pBlock *pb) +{ + pCode *pc; pCodeLabel *pcl; + + if(!pb || !pb->pcHead) + return; + + for(pc = pb->pcHead; (pc=findNextInstruction(pc->next)) != NULL; ) { + pBranch *pbr = PCI(pc)->label; + if(pbr && pbr->next) { + pCode *pcd = pb->pcHead; + + //fprintf(stderr, "multiple labels\n"); + //pc->print(stderr,pc); + + pbr = pbr->next; + while(pbr) { + while ((pcd = findInstructionUsingLabel(PCL(PCI(pc)->label->pc), pcd)) != NULL) { + //fprintf(stderr,"Used by:\n"); + //pcd->print(stderr,pcd); + + exchangeLabels(PCL(pbr->pc),pcd); + pcd = pcd->next; + } + pbr = pbr->next; + } + } + } + + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCL(pc)) // Label pcode + pcl = PCL(pc); + else if (isPCI(pc) && PCI(pc)->label) // pcode instruction with a label + pcl = PCL(PCI(pc)->label->pc); + else continue; + + //fprintf(stderr," found A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + /* This pCode is a label, so search the pBlock to see if anyone + * refers to it */ + + if( (pcl->key>0) && (!findInstructionUsingLabel(pcl, pb->pcHead))) { + /* Couldn't find an instruction that refers to this label + * So, unlink the pCode label from it's pCode chain + * and destroy the label */ + //fprintf(stderr," removed A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + DFPRINTF((stderr," !!! REMOVED A LABEL !!! key = %d, %s\n", pcl->key,pcl->label)); + if(pc->type == PC_LABEL) { + unlinkpCode(pc); + pCodeLabelDestruct(pc); + } else { + unlinkpCodeFromBranch(pc, PCODE(pcl)); + /*if(pc->label->next == NULL && pc->label->pc == NULL) { + free(pc->label); + }*/ + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* pBlockMergeLabels - remove the pCode labels from the pCode */ +/* chain and put them into pBranches that are */ +/* associated with the appropriate pCode */ +/* instructions. */ +/*-----------------------------------------------------------------*/ +void pBlockMergeLabels(pBlock *pb) +{ + pBranch *pbr; + pCode *pc, *pcnext=NULL; + + if(!pb) + return; + + /* First, Try to remove any unused labels */ + //pBlockRemoveUnusedLabels(pb); + + /* Now loop through the pBlock and merge the labels with the opcodes */ + + pc = pb->pcHead; + + while(pc) { + pCode *pcn = pc->next; + + if(pc->type == PC_LABEL) { + + //fprintf(stderr," checking merging label %s\n",PCL(pc)->label); + //fprintf(stderr,"Checking label key = %d\n",PCL(pc)->key); + if((pcnext = findNextInstruction(pc) )) { + + // Unlink the pCode label from it's pCode chain + unlinkpCode(pc); + + //fprintf(stderr,"Merged label key = %d\n",PCL(pc)->key); + // And link it into the instruction's pBranch labels. (Note, since + // it's possible to have multiple labels associated with one instruction + // we must provide a means to accomodate the additional labels. Thus + // the labels are placed into the singly-linked list "label" as + // opposed to being a single member of the pCodeInstruction.) + + //_ALLOC(pbr,sizeof(pBranch)); + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc; + pbr->next = NULL; + + PCI(pcnext)->label = pBranchAppend(PCI(pcnext)->label,pbr); + } else { + fprintf(stderr, "WARNING: couldn't associate label %s with an instruction\n",PCL(pc)->label); + } + } else if(pc->type == PC_CSOURCE) { + + /* merge the source line symbolic info into the next instruction */ + if((pcnext = findNextInstruction(pc) )) { + + // Unlink the pCode label from it's pCode chain + unlinkpCode(pc); + PCI(pcnext)->cline = PCCS(pc); + //fprintf(stderr, "merging CSRC\n"); + //genericPrint(stderr,pcnext); + } + } + pc = pcn; + } + pBlockRemoveUnusedLabels(pb); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepCode(char dbName) +{ +#define MAX_PASSES 4 + + int matches = 0; + int passes = 0; + pBlock *pb; + + if(!the_pFile) + return 0; + + DFPRINTF((stderr," Optimizing pCode\n")); + + do { + matches = 0; + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) + matches += OptimizepBlock(pb); + } + } + while(matches && ++passes < MAX_PASSES); + + return matches; +} + +/*-----------------------------------------------------------------*/ +/* popCopyGPR2Bit - copy a pcode operator */ +/*-----------------------------------------------------------------*/ + +pCodeOp *popCopyGPR2Bit(pCodeOp *pc, int bitval) +{ + pCodeOp *pcop; + + pcop = newpCodeOpBit(pc->name, bitval, 0); + + if( !( (pcop->type == PO_LABEL) || + (pcop->type == PO_LITERAL) || + (pcop->type == PO_STR) )) + PCOR(pcop)->r = PCOR(pc)->r; /* This is dangerous... */ + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pBlockDestruct(pBlock *pb) +{ + + if(!pb) + return; + + + free(pb); + +} + +/*-----------------------------------------------------------------*/ +/* void mergepBlocks(char dbName) - Search for all pBlocks with the*/ +/* name dbName and combine them */ +/* into one block */ +/*-----------------------------------------------------------------*/ +static void mergepBlocks(char dbName) +{ + + pBlock *pb, *pbmerged = NULL,*pbn; + + pb = the_pFile->pbHead; + + //fprintf(stderr," merging blocks named %c\n",dbName); + while(pb) { + + pbn = pb->next; + //fprintf(stderr,"looking at %c\n",getpBlock_dbName(pb)); + if( getpBlock_dbName(pb) == dbName) { + + //fprintf(stderr," merged block %c\n",dbName); + + if(!pbmerged) { + pbmerged = pb; + } else { + addpCode2pBlock(pbmerged, pb->pcHead); + /* addpCode2pBlock doesn't handle the tail: */ + pbmerged->pcTail = pb->pcTail; + + pb->prev->next = pbn; + if(pbn) + pbn->prev = pb->prev; + + + pBlockDestruct(pb); + } + //printpBlock(stderr, pbmerged); + } + pb = pbn; + } + +} + +/*-----------------------------------------------------------------*/ +/* AnalyzeFlow - Examine the flow of the code and optimize */ +/* */ +/* level 0 == minimal optimization */ +/* optimize registers that are used only by two instructions */ +/* level 1 == maximal optimization */ +/* optimize by looking at pairs of instructions that use the */ +/* register. */ +/*-----------------------------------------------------------------*/ + +static void AnalyzeFlow(int level) +{ + static int times_called=0; + + pBlock *pb; + + if(!the_pFile) + return; + + + /* if this is not the first time this function has been called, + then clean up old flow information */ + if(times_called++) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); + + RegsUnMapLiveRanges(); + + } + + GpcFlowSeq = 1; + + /* Phase 2 - Flow Analysis - Register Banking + * + * In this phase, the individual flow blocks are examined + * and register banking is fixed. + */ + + //for(pb = the_pFile->pbHead; pb; pb = pb->next) + //FixRegisterBanking(pb); + + /* Phase 2 - Flow Analysis + * + * In this phase, the pCode is partition into pCodeFlow + * blocks. The flow blocks mark the points where a continuous + * stream of instructions changes flow (e.g. because of + * a call or goto or whatever). + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + BuildFlow(pb); + + + /* Phase 2 - Flow Analysis - linking flow blocks + * + * In this phase, the individual flow blocks are examined + * to determine their order of excution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + LinkFlow(pb); + + /* Phase 3 - Flow Analysis - Flow Tree + * + * In this phase, the individual flow blocks are examined + * to determine their order of excution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + BuildFlowTree(pb); + + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + +// for(pb = the_pFile->pbHead; pb; pb = pb->next) +// FixBankFlow(pb); + + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pCodeRegMapLiveRanges(pb); + + RemoveUnusedRegisters(); + +// for(pb = the_pFile->pbHead; pb; pb = pb->next) + pCodeRegOptimizeRegUsage(level); + + OptimizepCode('*'); + + /* + for(pb = the_pFile->pbHead; pb; pb = pb->next) + DumpFlow(pb); + */ + /* debug stuff */ + /* + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + + FillFlow(PCFL(pcflow)); + } + } + */ + /* + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + + FlowStats(PCFL(pcflow)); + } + } + */ +} + +/*-----------------------------------------------------------------*/ +/* AnalyzeBanking - Called after the memory addresses have been */ +/* assigned to the registers. */ +/* */ +/*-----------------------------------------------------------------*/ + +void AnalyzeBanking(void) +{ + pBlock *pb; + + if(!picIsInitialized()) { + werror(E_FILE_OPEN_ERR, "no memory size is known for this processor"); + exit(1); + } + + if (!the_pFile) return; + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + + AnalyzeFlow(0); + AnalyzeFlow(1); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + FixRegisterBanking(pb); + + AnalyzeFlow(0); + AnalyzeFlow(1); + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static DEFSETFUNC (resetrIdx) +{ + reg_info *r = (reg_info *)item; + if (!r->isFixed) { + r->rIdx = 0; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* InitRegReuse - Initialises variables for code analyzer */ +/*-----------------------------------------------------------------*/ +static void InitReuseReg(void) +{ + /* Find end of statically allocated variables for start idx */ + /* Start from begining of GPR. Note may not be 0x20 on some PICs */ + /* XXX: Avoid clashes with fixed registers, start late. */ + unsigned maxIdx = 0x1000; + reg_info *r; + for (r = setFirstItem(dynDirectRegs); r; r = setNextItem(dynDirectRegs)) { + if (r->type != REG_SFR) { + maxIdx += r->size; /* Increment for all statically allocated variables */ + } + } + peakIdx = maxIdx; + applyToSet(dynAllocRegs,resetrIdx); /* Reset all rIdx to zero. */ +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static unsigned +register_reassign(pBlock *pb, unsigned startIdx, unsigned level) +{ + pCode *pc; + unsigned temp; + unsigned idx = startIdx; + + /* check recursion */ + pc = setFirstItem(pb->function_entries); + if (!pc) + return idx; + + if (pb->visited) + { + set *regset; + /* TODO: Recursion detection missing, should emit a warning as recursive code will fail. */ + + // Find the highest rIdx used by this function for return. + regset = pb->tregisters; + idx = 0; + while (regset) + { + temp = ((reg_info *)regset->item)->rIdx; + if (temp > idx) + idx = temp; + regset = regset->next; + } // while + DFPRINTF((stderr, + "%*s(%u) function \"%s\" already visited: max idx = %04x\n", + 4 * level, "", level,PCF(pc)->fname, idx)); + return idx + 1; + } // if + + /* + * We now traverse the call tree depth first, assigning indices > startIdx + * to the registers of all called functions before assigning indices to + * the registers of the calling function, starting with one greater than + * the max. index used by any child function. + * This approach guarantees that, if f calls g, all registers of f have + * greater indices than those of g (also holds transitively). + * + * XXX: If a function f calls a function g in a different module, + * we should handle the case that g could call a function h + * in f's module. + * The consequence of this is that even though f and h might + * share registers (they do not call each other locally) when + * looking only at f's module, they actually must not do so! + * + * For a non-static function f, let ES(f) be the set of functions + * (including f) that can only be reached via f in the module-local + * call graph (ES(f) will hence be a subgraph). + * Let further REG(ES(f)) be the set of registers assigned to + * functions in ES(f). + * Then we should make sure that REG(ES(f)) and REG(ES(g)) are + * disjoint for all non-static functions f and g. + * + * Unfortunately, determining the sets ES(f) is non-trivial, + * so we ignore this problem and declare all modules non-reentrant. + * This is a bug. + */ + pb->visited = 1; + + DFPRINTF((stderr, + "%*s(%u) reassigning registers for functions called by \"%s\":base idx = %04x\n", + 4 * level, "", level, PCF(pc)->fname, startIdx)); + + for (pc = setFirstItem(pb->function_calls); pc; pc = setNextItem(pb->function_calls)) + { + if (pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) + { + char *dest = get_op_from_instruction(PCI(pc)); + pCode *pcn = findFunction(dest); + + if (pcn) + { + /* + * Reassign the registers of all called functions and record + * the max. index I used by any child function --> I+1 will be + * the first index available to this function. + * (Problem shown with regression test src/regression/sub2.c) + */ + unsigned childsMaxIdx; + childsMaxIdx = register_reassign(pcn->pb,startIdx,level+1); + if (childsMaxIdx > idx) + idx = childsMaxIdx; + } // if + } // if + } // for + + pc = setFirstItem(pb->function_entries); + DFPRINTF((stderr, + "%*s(%u) reassigning registers for function \"%s\":idx = %04x\n", + 4 * level, "", level, PCF(pc)->fname, idx)); + + if (pb->tregisters) + { + reg_info *r; + for (r = setFirstItem(pb->tregisters); r; r = setNextItem(pb->tregisters)) + { + if ((r->type == REG_GPR) && (!r->isFixed) && (r->rIdx < (int)idx)) + { + char s[20]; + set *regset; + /* + * Make sure, idx is not yet used in this routine ... + * XXX: This should no longer be required, as all functions + * are reassigned at most once ... + */ + do + { + regset = pb->tregisters; + // do not touch s->curr ==> outer loop! + while (regset && ((reg_info *)regset->item)->rIdx != idx) + regset = regset->next; + if (regset) + idx++; + } + while (regset); + r->rIdx = idx++; + if (peakIdx < idx) + peakIdx = idx; + + SNPRINTF(s, sizeof(s), "r0x%02X", r->rIdx); + DFPRINTF((stderr, + "%*s(%u) reassigning register %p \"%s\" to \"%s\"\n", + 4 * level, "", level, r, r->name, s)); + free(r->name); + r->name = Safe_strdup(s); + } // if + } // for + } // if + + /* return lowest index available for caller's registers */ + return idx; +} + +/*------------------------------------------------------------------*/ +/* ReuseReg were call tree permits */ +/* */ +/* Re-allocate the GPR for optimum reuse for a given pblock */ +/* eg if a function m() calls function f1() and f2(), where f1 */ +/* allocates a local variable vf1 and f2 allocates a local */ +/* variable vf2. Then providing f1 and f2 do not call each other */ +/* they may share the same general purpose registers for vf1 and */ +/* vf2. */ +/* This is done by first setting the the regs rIdx to start after */ +/* all the global variables, then walking through the call tree */ +/* renaming the registers to match their new idx and incrementng */ +/* it as it goes. If a function has already been called it will */ +/* only rename the registers if it has already used up those */ +/* registers ie rIdx of the function's registers is lower than the */ +/* current rIdx. That way the register will not be reused while */ +/* still being used by an eariler function call. */ +/* */ +/* Note for this to work the functions need to be declared static. */ +/* */ +/*------------------------------------------------------------------*/ +void +ReuseReg(void) +{ + pBlock *pb; + + if (options.noOverlay || !the_pFile) + return; + + InitReuseReg(); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + { + /* Non static functions can be called from other modules, + * so their registers must reassign */ + if (pb->function_entries + && (PCF(setFirstItem(pb->function_entries))->isPublic || !pb->visited)) + { + register_reassign(pb,peakIdx,0); + } // if + } // for +} + +/*-----------------------------------------------------------------*/ +/* buildCallTree - look at the flow and extract all of the calls */ +/* */ +/*-----------------------------------------------------------------*/ + +static void buildCallTree(void) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + /* Now build the call tree. + First we examine all of the pCodes for functions. + Keep in mind that the function boundaries coincide + with pBlock boundaries. + + The algorithm goes something like this: + We have two nested loops. The outer loop iterates + through all of the pBlocks/functions. The inner + loop iterates through all of the pCodes for + a given pBlock. When we begin iterating through + a pBlock, the variable pc_fstart, pCode of the start + of a function, is cleared. We then search for pCodes + of type PC_FUNCTION. When one is encountered, we + initialize pc_fstart to this and at the same time + associate a new pBranch object that signifies a + branch entry. If a return is found, then this signifies + a function exit point. We'll link the pCodes of these + returns to the matching pc_fstart. + + When we're done, a doubly linked list of pBranches + will exist. The head of this list is stored in + `the_pFile', which is the meta structure for all + of the pCode. Look at the printCallTree function + on how the pBranches are linked together. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pc_fstart=NULL; + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCF(pc)) { + pCodeFunction *pcf = PCF(pc); + if (pcf->fname) { + + if(STRCASECMP(pcf->fname, "_main") == 0) { + //fprintf(stderr," found main \n"); + pb->cmemmap = NULL; /* FIXME do we need to free ? */ + pb->dbName = 'M'; + } + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc_fstart = pc; + pbr->next = NULL; + + the_pFile->functions = pBranchAppend(the_pFile->functions,pbr); + + // Here's a better way of doing the same: + addSet(&pb->function_entries, pc); + + } else { + // Found an exit point in a function, e.g. return + // (Note, there may be more than one return per function) + if(pc_fstart) + pBranchLink(PCF(pc_fstart), pcf); + + addSet(&pb->function_exits, pc); + } + } else if(isCALL(pc)) { + addSet(&pb->function_calls,pc); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* AnalyzepCode - parse the pCode that has been generated and form */ +/* all of the logical connections. */ +/* */ +/* Essentially what's done here is that the pCode flow is */ +/* determined. */ +/*-----------------------------------------------------------------*/ + +void AnalyzepCode(char dbName) +{ + pBlock *pb; + int i,changes; + + if(!the_pFile) + return; + + mergepBlocks('D'); + + + /* Phase 1 - Register allocation and peep hole optimization + * + * The first part of the analysis is to determine the registers + * that are used in the pCode. Once that is done, the peep rules + * are applied to the code. We continue to loop until no more + * peep rule optimizations are found (or until we exceed the + * MAX_PASSES threshold). + * + * When done, the required registers will be determined. + * + */ + i = 0; + do { + + DFPRINTF((stderr," Analyzing pCode: PASS #%d\n",i+1)); + + /* First, merge the labels with the instructions */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) { + + DFPRINTF((stderr," analyze and merging block %c\n",dbName)); + pBlockMergeLabels(pb); + AnalyzepBlock(pb); + } else { + DFPRINTF((stderr," skipping block analysis dbName=%c blockname=%c\n",dbName,getpBlock_dbName(pb))); + } + } + + changes = OptimizepCode(dbName); + + } while(changes && (i++ < MAX_PASSES)); + + buildCallTree(); +} + +/*-----------------------------------------------------------------*/ +/* findFunction - Search for a function by name (given the name) */ +/* in the set of all functions that are in a pBlock */ +/* (note - I expect this to change because I'm planning to limit */ +/* pBlock's to just one function declaration */ +/*-----------------------------------------------------------------*/ +static pCode *findFunction(const char *fname) +{ + pBlock *pb; + pCode *pc; + if(!fname) + return NULL; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_entries); + while(pc) { + + if((pc->type == PC_FUNCTION) && + (PCF(pc)->fname) && + (strcmp(fname, PCF(pc)->fname)==0)) + return pc; + + pc = setNextItem(pb->function_entries); + + } + + } + return NULL; +} + +static void pBlockStats(FILE *of, pBlock *pb) +{ + + pCode *pc; + reg_info *r; + + fprintf(of,";***\n; pBlock Stats: dbName = %c\n;***\n",getpBlock_dbName(pb)); + + // for now just print the first element of each set +/* pc = setFirstItem(pb->function_entries); + if(pc) { + fprintf(of,";entry: "); + pc->print(of,pc); + }*/ + pc = setFirstItem(pb->function_exits); + if(pc) { + fprintf(of,";has an exit\n"); + //pc->print(of,pc); + } + + pc = setFirstItem(pb->function_calls); + if(pc) { + fprintf(of,";functions called:\n"); + + while(pc) { + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + fprintf(of,"; %s\n",get_op_from_instruction(PCI(pc))); + } + pc = setNextItem(pb->function_calls); + } + } + + r = setFirstItem(pb->tregisters); + if(r) { + int n = elementsInSet(pb->tregisters); + + fprintf(of,";%d compiler assigned register%c:\n",n, ( (n!=1) ? 's' : ' ')); + + while (r) { + fprintf(of,"; %s\n",r->name); + r = setNextItem(pb->tregisters); + } + } +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* printCallTree - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ +static void pct2(FILE *of,pBlock *pb,int indent) +{ + pCode *pc,*pcn; + int i; + // set *registersInCallPath = NULL; + + if(!of) + return; + + if(indent > 10) + return; //recursion ? + + pc = setFirstItem(pb->function_entries); + + if(!pc) + return; + + pb->visited = 0; + + for(i=0;itype == PC_FUNCTION) + fprintf(of,"%s\n",PCF(pc)->fname); + else + return; // ??? + + + pc = setFirstItem(pb->function_calls); + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + char *dest = get_op_from_instruction(PCI(pc)); + + pcn = findFunction(dest); + if(pcn) + pct2(of,pcn->pb,indent+1); + } else + fprintf(of,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + + +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/* ispCodeFunction - returns true if *pc is the pCode of a */ +/* function */ +/*-----------------------------------------------------------------*/ +static bool ispCodeFunction(pCode *pc) +{ + + if(pc && pc->type == PC_FUNCTION && PCF(pc)->fname) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* printCallTree - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ + +static void printCallTree(FILE *of) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!of) + of = stderr; + + fprintf(of, "\npBlock statistics\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next ) + pBlockStats(of,pb); + + + + fprintf(of,"Call Tree\n"); + pbr = the_pFile->functions; + while(pbr) { + if(pbr->pc) { + pc = pbr->pc; + if(!ispCodeFunction(pc)) + fprintf(of,"bug in call tree"); + + + fprintf(of,"Function: %s\n", PCF(pc)->fname); + + while(pc->next && !ispCodeFunction(pc->next)) { + pc = pc->next; + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) + fprintf(of,"\t%s\n",get_op_from_instruction(PCI(pc))); + } + } + + pbr = pbr->next; + } + + + fprintf(of,"\n**************\n\na better call tree\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(pb->visited) + pct2(of,pb,0); + } + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(of,"block dbname: %c\n", getpBlock_dbName(pb)); + } +} +#endif + + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +static void InlineFunction(pBlock *pb) +{ + pCode *pc; + pCode *pc_call; + + if(!pb) + return; + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(get_op_from_instruction(PCI(pc))); + pCode *pcp = pc->prev; + pCode *pct; + pCode *pce; + + pBranch *pbr; + + if(pcn && isPCF(pcn) && (PCF(pcn)->ncalled == 1) && !PCF(pcn)->isPublic && (pcp && (isPCI_BITSKIP(pcp)||!isPCI_SKIP(pcp)))) { /* Bit skips can be inverted other skips can not */ + + InlineFunction(pcn->pb); + + /* + At this point, *pc points to a CALL mnemonic, and + *pcn points to the function that is being called. + + To in-line this call, we need to remove the CALL + and RETURN(s), and link the function pCode in with + the CALLee pCode. + + */ + + pc_call = pc; + + /* Check if previous instruction was a bit skip */ + if (isPCI_BITSKIP(pcp)) { + pCodeLabel *pcl; + /* Invert skip instruction and add a goto */ + PCI(pcp)->op = (PCI(pcp)->op == POC_BTFSS) ? POC_BTFSC : POC_BTFSS; + + if(isPCL(pc_call->next)) { // Label pcode + pcl = PCL(pc_call->next); + } else if (isPCI(pc_call->next) && PCI(pc_call->next)->label) { // pcode instruction with a label + pcl = PCL(PCI(pc_call->next)->label->pc); + } else { + pcl = PCL(newpCodeLabel(NULL, newiTempLabel(NULL)->key+100)); + PCI(pc_call->next)->label->pc = (struct pCode*)pcl; + } + pCodeInsertAfter(pcp, newpCode(POC_GOTO, newpCodeOp(pcl->label,PO_STR))); + } + + /* remove callee pBlock from the pBlock linked list */ + removepBlock(pcn->pb); + + pce = pcn; + while(pce) { + pce->pb = pb; + pce = pce->next; + } + + /* Remove the Function pCode */ + pct = findNextInstruction(pcn->next); + + /* Link the function with the callee */ + if (pcp) pcp->next = pcn->next; + pcn->next->prev = pcp; + + /* Convert the function name into a label */ + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = newpCodeLabel(PCF(pcn)->fname, -1); + pbr->next = NULL; + PCI(pct)->label = pBranchAppend(PCI(pct)->label,pbr); + PCI(pct)->label = pBranchAppend(PCI(pct)->label,PCI(pc_call)->label); + + /* turn all of the return's except the last into goto's */ + /* check case for 2 instruction pBlocks */ + pce = findNextInstruction(pcn->next); + while(pce) { + pCode *pce_next = findNextInstruction(pce->next); + + if(pce_next == NULL) { + /* found the last return */ + pCode *pc_call_next = findNextInstruction(pc_call->next); + + //fprintf(stderr,"found last return\n"); + //pce->print(stderr,pce); + pce->prev->next = pc_call->next; + pc_call->next->prev = pce->prev; + PCI(pc_call_next)->label = pBranchAppend(PCI(pc_call_next)->label, + PCI(pce)->label); + } + + pce = pce_next; + } + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +void InlinepCode(void) +{ + + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!functionInlining) + return; + + /* Loop through all of the function definitions and count the + * number of times each one is called */ + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(get_op_from_instruction(PCI(pc))); + if(pcn && isPCF(pcn)) { + PCF(pcn)->ncalled++; + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + } + + //fprintf(stderr,"inlining %d\n",__LINE__); + + /* Now, Loop through the function definitions again, but this + * time inline those functions that have only been called once. */ + + InlineFunction(the_pFile->pbHead); + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); + +} + diff --git a/src/pic14/pcode.h b/src/pic14/pcode.h new file mode 100644 index 0000000..3faca44 --- /dev/null +++ b/src/pic14/pcode.h @@ -0,0 +1,907 @@ +/*------------------------------------------------------------------------- + + pcode.h - post code generation + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +#ifndef __PCODE_H__ +#define __PCODE_H__ + +#include "common.h" + +/* When changing these, you must also update the assembler template + * in device/lib/libsdcc/macros.inc */ +#define GPTRTAG_DATA 0x00 +#define GPTRTAG_CODE 0x80 + +/* Cyclic dependency with ralloc.h: */ +struct reg_info; + +/* + Post code generation + + The post code generation is an assembler optimizer. The assembly code + produced by all of the previous steps is fully functional. This step + will attempt to analyze the flow of the assembly code and agressively + optimize it. The peep hole optimizer attempts to do the same thing. + As you may recall, the peep hole optimizer replaces blocks of assembly + with more optimal blocks (e.g. removing redundant register loads). + However, the peep hole optimizer has to be somewhat conservative since + an assembly program has implicit state information that's unavailable + when only a few instructions are examined. + Consider this example: + + example1: + movwf t1 + movf t1,w + + The movf seems redundant since we know that the W register already + contains the same value of t1. So a peep hole optimizer is tempted to + remove the "movf". However, this is dangerous since the movf affects + the flags in the status register (specifically the Z flag) and subsequent + code may depend upon this. Look at these two examples: + + example2: + movwf t1 + movf t1,w ; Can't remove this movf + skpz + return + + example3: + movwf t1 + movf t1,w ; This movf can be removed + xorwf t2,w ; since xorwf will over write Z + skpz + return + +*/ + + +/*********************************************************************** + * debug stuff + * + * The DFPRINTF macro will call fprintf if PCODE_DEBUG is defined. + * The macro is used like: + * + * DPRINTF(("%s #%d\n","test", 1)); + * + * The double parenthesis (()) are necessary + * + ***********************************************************************/ +//#define PCODE_DEBUG + +#ifdef PCODE_DEBUG +#define DFPRINTF(args) (fprintf args) +#else +#define DFPRINTF(args) ((void)0) +#endif + + +/*********************************************************************** + * PIC status bits - this will move into device dependent headers + ***********************************************************************/ +#define PIC_C_BIT 0 +#define PIC_DC_BIT 1 +#define PIC_Z_BIT 2 +#define PIC_RP0_BIT 5 /* Register Bank select bits RP1:0 : */ +#define PIC_RP1_BIT 6 /* 00 - bank 0, 01 - bank 1, 10 - bank 2, 11 - bank 3 */ +#define PIC_IRP_BIT 7 /* Indirect register page select */ + +/*********************************************************************** + * PIC INTCON bits - this will move into device dependent headers + ***********************************************************************/ +#define PIC_RBIF_BIT 0 /* Port B level has changed flag */ +#define PIC_INTF_BIT 1 /* Port B bit 0 interrupt on edge flag */ +#define PIC_T0IF_BIT 2 /* TMR0 has overflowed flag */ +#define PIC_RBIE_BIT 3 /* Port B level has changed - Interrupt Enable */ +#define PIC_INTE_BIT 4 /* Port B bit 0 interrupt on edge - Int Enable */ +#define PIC_T0IE_BIT 5 /* TMR0 overflow Interrupt Enable */ +#define PIC_PIE_BIT 6 /* Peripheral Interrupt Enable */ +#define PIC_GIE_BIT 7 /* Global Interrupt Enable */ + + +/*********************************************************************** + * + * PIC_OPTYPE - Operand types that are specific to the PIC architecture + * + * If a PIC assembly instruction has an operand then here is where we + * associate a type to it. For example, + * + * movf reg,W + * + * The movf has two operands: 'reg' and the W register. 'reg' is some + * arbitrary general purpose register, hence it has the type PO_GPR_REGISTER. + * The W register, which is the PIC's accumulator, has the type PO_W. + * + ***********************************************************************/ + + + +typedef enum +{ + PO_NONE=0, // No operand e.g. NOP + PO_W, // The 'W' register + PO_STATUS, // The 'STATUS' register + PO_FSR, // The "file select register" (in 18c it's one of three) + PO_INDF, // The Indirect register + PO_INTCON, // Interrupt Control register + PO_GPR_REGISTER, // A general purpose register + PO_GPR_BIT, // A bit of a general purpose register + PO_GPR_TEMP, // A general purpose temporary register + PO_GPR_POINTER, // A general purpose pointer + PO_SFR_REGISTER, // A special function register (e.g. PORTA) + PO_PCL, // Program counter Low register + PO_PCLATH, // Program counter Latch high register + PO_LITERAL, // A constant + PO_IMMEDIATE, // (8051 legacy) + PO_DIR, // Direct memory (8051 legacy) + PO_CRY, // bit memory (8051 legacy) + PO_BIT, // bit operand. + PO_STR, // (8051 legacy) + PO_LABEL, + PO_WILD // Wild card operand in peep optimizer +} PIC_OPTYPE; + + +/*********************************************************************** + * + * PIC_OPCODE + * + * This is not a list of the PIC's opcodes per se, but instead + * an enumeration of all of the different types of pic opcodes. + * + ***********************************************************************/ + +typedef enum +{ + POC_WILD=-1, /* Wild card - used in the pCode peep hole optimizer + * to represent ANY pic opcode */ + POC_ADDLW=0, + POC_ADDWF, + POC_ADDFW, + POC_ANDLW, + POC_ANDWF, + POC_ANDFW, + POC_BCF, + POC_BSF, + POC_BTFSC, + POC_BTFSS, + POC_CALL, + POC_COMF, + POC_COMFW, + POC_CLRF, + POC_CLRW, + POC_CLRWDT, + POC_DECF, + POC_DECFW, + POC_DECFSZ, + POC_DECFSZW, + POC_GOTO, + POC_INCF, + POC_INCFW, + POC_INCFSZ, + POC_INCFSZW, + POC_IORLW, + POC_IORWF, + POC_IORFW, + POC_MOVF, + POC_MOVFW, + POC_MOVLW, + POC_MOVWF, + POC_NOP, + POC_RETLW, + POC_RETURN, + POC_RETFIE, + POC_RLF, + POC_RLFW, + POC_RRF, + POC_RRFW, + POC_SUBLW, + POC_SUBWF, + POC_SUBFW, + POC_SWAPF, + POC_SWAPFW, + POC_TRIS, + POC_XORLW, + POC_XORWF, + POC_XORFW, + POC_BANKSEL, + POC_PAGESEL, + + /* Enhanced instruction set. */ + + POC_ADDFSR, + POC_ADDWFC, + POC_ADDFWC, + POC_ASRF, + POC_ASRFW, + POC_BRA, + POC_BRW, + POC_CALLW, + POC_LSLF, + POC_LSLFW, + POC_LSRF, + POC_LSRFW, + POC_MOVIW, + POC_MOVIW_K, + POC_MOVLB, + POC_MOVLP, + POC_MOVWI, + POC_MOVWI_K, + POC_RESET, + POC_SUBWFB, + POC_SUBWFBW, + + MAX_PIC14MNEMONICS +} PIC_OPCODE; + + +/*********************************************************************** + * PC_TYPE - pCode Types + ***********************************************************************/ + +typedef enum +{ + PC_COMMENT=0, /* pCode is a comment */ + PC_INLINE, /* user's inline code */ + PC_OPCODE, /* PORT dependent opcode */ + PC_LABEL, /* assembly label */ + PC_FLOW, /* flow analysis */ + PC_FUNCTION, /* Function start or end */ + PC_WILD, /* wildcard - an opcode place holder used + * in the pCode peep hole optimizer */ + PC_CSOURCE, /* C-Source Line */ + PC_ASMDIR, /* Assembler directive */ + PC_BAD /* Mark the pCode object as being bad */ +} PC_TYPE; + +/************************************************/ +/*************** Structures ********************/ +/************************************************/ +/* These are here as forward references - the + * full definition of these are below */ +struct pCode; +struct pCodeWildBlock; +struct pCodeRegLives; + +/************************************************* + pBranch + + The first step in optimizing pCode is determining + the program flow. This information is stored in + single-linked lists in the for of 'from' and 'to' + objects with in a pcode. For example, most instructions + don't involve any branching. So their from branch + points to the pCode immediately preceding them and + their 'to' branch points to the pcode immediately + following them. A skip instruction is an example of + a pcode that has multiple (in this case two) elements + in the 'to' branch. A 'label' pcode is an where there + may be multiple 'from' branches. + *************************************************/ + +typedef struct pBranch +{ + struct pCode *pc; // Next pCode in a branch + struct pBranch *next; /* If more than one branch + * the next one is here */ + +} pBranch; + +/************************************************* + pCodeOp + + pCode Operand structure. + For those assembly instructions that have arguments, + the pCode will have a pCodeOp in which the argument + can be stored. For example + + movf some_register,w + + 'some_register' will be stored/referenced in a pCodeOp + + *************************************************/ + +typedef struct pCodeOp +{ + PIC_OPTYPE type; + char *name; + +} pCodeOp; + +typedef struct pCodeOpLit +{ + pCodeOp pcop; + int lit; +} pCodeOpLit; + +typedef struct pCodeOpImmd +{ + pCodeOp pcop; + int offset; /* low,med, or high byte of immediate value */ + int index; /* add this to the immediate value */ + unsigned _const:1; /* is in code space */ + unsigned _function:1; /* is a (pointer to a) function */ + + int rIdx; /* If this immd points to a register */ + struct reg_info *r; /* then this is the reg. */ + +} pCodeOpImmd; + +typedef struct pCodeOpLabel +{ + pCodeOp pcop; + int key; + int offset; /* low or high byte of label */ +} pCodeOpLabel; + +typedef struct pCodeOpReg +{ + pCodeOp pcop; // Can be either GPR or SFR + int rIdx; // Index into the register table + struct reg_info *r; + int instance; // byte # of Multi-byte registers + struct pBlock *pb; +} pCodeOpReg; + +typedef struct pCodeOpRegBit +{ + pCodeOpReg pcor; // The Register containing this bit + int bit; // 0-7 bit number. + PIC_OPTYPE subtype; // The type of this register. + unsigned int inBitSpace: 1; /* True if in bit space, else + just a bit of a register */ +} pCodeOpRegBit; + +typedef struct pCodeOpStr /* Only used here for the name of fn being called or jumped to */ +{ + pCodeOp pcop; + unsigned isPublic: 1; /* True if not static ie extern */ +} pCodeOpStr; + +typedef struct pCodeOpWild +{ + pCodeOp pcop; + + struct pCodeWildBlock *pcwb; + + int id; /* index into an array of char *'s that will match + * the wild card. The array is in *pcp. */ + pCodeOp *subtype; /* Pointer to the Operand type into which this wild + * card will be expanded */ + pCodeOp *matched; /* When a wild matches, we'll store a pointer to the + * opcode we matched */ + +} pCodeOpWild; + + +/************************************************* + pCode + + Here is the basic build block of a PIC instruction. + Each pic instruction will get allocated a pCode. + A linked list of pCodes makes a program. + +**************************************************/ + +typedef struct pCode +{ + PC_TYPE type; + + struct pCode *prev; // The pCode objects are linked together + struct pCode *next; // in doubly linked lists. + + unsigned id; // unique ID number for all pCodes to assist in debugging + int seq; // sequence number + + struct pBlock *pb; // The pBlock that contains this pCode. + + /* "virtual functions" + * The pCode structure is like a base class + * in C++. The subsequent structures that "inherit" + * the pCode structure will initialize these function + * pointers to something useful */ + void (*destruct)(struct pCode *_this); + void (*print) (FILE *of,struct pCode *_this); + +} pCode; + + +/************************************************* + pCodeComment +**************************************************/ + +typedef struct pCodeComment +{ + pCode pc; + + char *comment; + +} pCodeComment; + + +/************************************************* + pCodeComment +**************************************************/ + +typedef struct pCodeCSource +{ + pCode pc; + + int line_number; + char *line; + char *file_name; + +} pCodeCSource; + + +/************************************************* + pCodeFlow + + The Flow object is used as marker to separate + the assembly code into contiguous chunks. In other + words, everytime an instruction cause or potentially + causes a branch, a Flow object will be inserted into + the pCode chain to mark the beginning of the next + contiguous chunk. + +**************************************************/ + +typedef struct pCodeFlow +{ + pCode pc; + + pCode *end; /* Last pCode in this flow. Note that + the first pCode is pc.next */ + + set *from; /* flow blocks that can send control to this flow block */ + set *to; /* flow blocks to which this one can send control */ + struct pCodeFlow *ancestor; /* The most immediate "single" pCodeFlow object that + * executes prior to this one. In many cases, this + * will be just the previous */ + + int inCond; /* Input conditions - stuff assumed defined at entry */ + int outCond; /* Output conditions - stuff modified by flow block */ + + int firstBank; /* The first and last bank flags are the first and last */ + int lastBank; /* register banks used within one flow object */ + + int FromConflicts; + int ToConflicts; + + set *registers;/* Registers used in this flow */ + +} pCodeFlow; + + +/************************************************* + pCodeFlowLink + + The Flow Link object is used to record information + about how consecutive excutive Flow objects are related. + The pCodeFlow objects demarcate the pCodeInstructions + into contiguous chunks. The FlowLink records conflicts + in the discontinuities. For example, if one Flow object + references a register in bank 0 and the next Flow object + references a register in bank 1, then there is a discontinuity + in the banking registers. + +*/ +typedef struct pCodeFlowLink +{ + pCodeFlow *pcflow; /* pointer to linked pCodeFlow object */ + + int bank_conflict; /* records bank conflicts */ + +} pCodeFlowLink; + + +/************************************************* + pCodeInstruction + + Here we describe all the facets of a PIC instruction + (expansion for the 18cxxx is also provided). + +**************************************************/ + +typedef struct pCodeInstruction +{ + pCode pc; + + PIC_OPCODE op; // The opcode of the instruction. + + char const * const mnemonic; // Pointer to mnemonic string + + pBranch *from; // pCodes that execute before this one + pBranch *to; // pCodes that execute after + pBranch *label; // pCode instructions that have labels + + pCodeOp *pcop; /* Operand, if this instruction has one */ + pCodeFlow *pcflow; /* flow block to which this instruction belongs */ + pCodeCSource *cline; /* C Source from which this instruction was derived */ + + unsigned int num_ops; /* Number of operands (0,1,2 for mid range pics) */ + unsigned int isModReg: 1; /* If destination is W or F, then 1==F */ + unsigned int isBitInst: 1; /* e.g. BCF */ + unsigned int isBranch: 1; /* True if this is a branching instruction */ + unsigned int isSkip: 1; /* True if this is a skip instruction */ + unsigned int isLit: 1; /* True if this instruction has an literal operand */ + + PIC_OPCODE inverted_op; /* Opcode of instruction that's the opposite of this one */ + unsigned int inCond; // Input conditions for this instruction + unsigned int outCond; // Output conditions for this instruction + +} pCodeInstruction; + + +/************************************************* + pCodeAsmDir +**************************************************/ + +typedef struct pCodeAsmDir +{ + pCodeInstruction pci; + + char *directive; + char *arg; +} pCodeAsmDir; + + +/************************************************* + pCodeLabel +**************************************************/ + +typedef struct pCodeLabel +{ + pCode pc; + + char *label; + int key; + +} pCodeLabel; + + +/************************************************* + pCodeFunction +**************************************************/ + +typedef struct pCodeFunction +{ + pCode pc; + + char *modname; + char *fname; /* If NULL, then this is the end of + a function. Otherwise, it's the + start and the name is contained + here. */ + + pBranch *from; // pCodes that execute before this one + pBranch *to; // pCodes that execute after + pBranch *label; // pCode instructions that have labels + + int ncalled; /* Number of times function is called. */ + unsigned isPublic:1; /* True if the fn is not static and can be called from another module (ie a another c or asm file). */ + unsigned isInterrupt:1; /* True if the fn is interrupt. */ + +} pCodeFunction; + + +/************************************************* + pCodeWild +**************************************************/ + +typedef struct pCodeWild +{ + pCodeInstruction pci; + + int id; /* Index into the wild card array of a peepBlock + * - this wild card will get expanded into that pCode + * that is stored at this index */ + + /* Conditions on wild pcode instruction */ + int mustBeBitSkipInst:1; + int mustNotBeBitSkipInst:1; + int invertBitSkipInst:1; + + pCodeOp *operand; // Optional operand + pCodeOp *label; // Optional label + +} pCodeWild; + +/************************************************* + pBlock + + Here are PIC program snippets. There's a strong + correlation between the eBBlocks and pBlocks. + SDCC subdivides a C program into managable chunks. + Each chunk becomes a eBBlock and ultimately in the + PIC port a pBlock. + +**************************************************/ + +typedef struct pBlock +{ + memmap *cmemmap; /* The snippet is from this memmap */ + char dbName; /* if cmemmap is NULL, then dbName will identify the block */ + pCode *pcHead; /* A pointer to the first pCode in a link list of pCodes */ + pCode *pcTail; /* A pointer to the last pCode in a link list of pCodes */ + + struct pBlock *next; /* The pBlocks will form a doubly linked list */ + struct pBlock *prev; + + set *function_entries; /* dll of functions in this pblock */ + set *function_exits; + set *function_calls; + set *tregisters; + + set *FlowTree; + unsigned visited:1; /* set true if traversed in call tree */ + + unsigned seq; /* sequence number of this pBlock */ + +} pBlock; + +/************************************************* + pFile + + The collection of pBlock program snippets are + placed into a linked list that is implemented + in the pFile structure. + + The pcode optimizer will parse the pFile. + +**************************************************/ + +typedef struct pFile +{ + pBlock *pbHead; /* A pointer to the first pBlock */ + pBlock *pbTail; /* A pointer to the last pBlock */ + + pBranch *functions; /* A SLL of functions in this pFile */ + +} pFile; + + + +/************************************************* + pCodeWildBlock + + The pCodeWildBlock object keeps track of the wild + variables, operands, and opcodes that exist in + a pBlock. +**************************************************/ +typedef struct pCodeWildBlock +{ + pBlock *pb; + struct pCodePeep *pcp; // pointer back to ... I don't like this... + + int nvars; // Number of wildcard registers in target. + char **vars; // array of pointers to them + + int nops; // Number of wildcard operands in target. + pCodeOp **wildpCodeOps; // array of pointers to the pCodeOp's. + + int nwildpCodes; // Number of wildcard pCodes in target/replace + pCode **wildpCodes; // array of pointers to the pCode's. + +} pCodeWildBlock; + +/************************************************* + pCodePeep + + The pCodePeep object mimics the peep hole optimizer + in the main SDCC src (e.g. SDCCpeeph.c). Essentially + there is a target pCode chain and a replacement + pCode chain. The target chain is compared to the + pCode that is generated by gen.c. If a match is + found then the pCode is replaced by the replacement + pCode chain. +**************************************************/ +typedef struct pCodePeep +{ + pCodeWildBlock target; // code we'd like to optimize + pCodeWildBlock replace; // and this is what we'll optimize it with. + + /* (Note: a wildcard register is a place holder. Any register + * can be replaced by the wildcard when the pcode is being + * compared to the target. */ + + /* Post Conditions. A post condition is a condition that + * must be either true or false before the peep rule is + * accepted. For example, a certain rule may be accepted + * if and only if the Z-bit is not used as an input to + * the subsequent instructions in a pCode chain. + */ + unsigned int postFalseCond; + unsigned int postTrueCond; + +} pCodePeep; + +/************************************************* + + pCode peep command definitions + + Here are some special commands that control the +way the peep hole optimizer behaves + +**************************************************/ + +enum peepCommandTypes +{ + NOTBITSKIP = 0, + BITSKIP, + INVERTBITSKIP, + _LAST_PEEP_COMMAND_ +}; + +/************************************************* + peepCommand structure stores the peep commands. + +**************************************************/ + +typedef struct peepCommand +{ + int id; + char *cmd; +} peepCommand; + +/************************************************* + pCode Macros + +**************************************************/ +#define PCODE(x) ((pCode *)(x)) +#define PCI(x) ((pCodeInstruction *)(x)) +#define PCL(x) ((pCodeLabel *)(x)) +#define PCF(x) ((pCodeFunction *)(x)) +#define PCFL(x) ((pCodeFlow *)(x)) +#define PCFLINK(x)((pCodeFlowLink *)(x)) +#define PCW(x) ((pCodeWild *)(x)) +#define PCCS(x) ((pCodeCSource *)(x)) +#define PCAD(x) ((pCodeAsmDir *)(x)) + +#define PCOP(x) ((pCodeOp *)(x)) +#define PCOL(x) ((pCodeOpLit *)(x)) +#define PCOI(x) ((pCodeOpImmd *)(x)) +#define PCOLAB(x) ((pCodeOpLabel *)(x)) +#define PCOR(x) ((pCodeOpReg *)(x)) +#define PCORB(x) ((pCodeOpRegBit *)(x)) +#define PCOS(x) ((pCodeOpStr *)(x)) +#define PCOW(x) ((pCodeOpWild *)(x)) + +#define PBR(x) ((pBranch *)(x)) + +#define PCWB(x) ((pCodeWildBlock *)(x)) + +#define isPCOLAB(x) ((PCOP(x)->type) == PO_LABEL) +#define isPCOS(x) ((PCOP(x)->type) == PO_STR) + + +/* + macros for checking pCode types +*/ +#define isPCI(x) ((PCODE(x)->type == PC_OPCODE)) +#define isPCFL(x) ((PCODE(x)->type == PC_FLOW)) +#define isPCF(x) ((PCODE(x)->type == PC_FUNCTION)) +#define isPCL(x) ((PCODE(x)->type == PC_LABEL)) +#define isPCW(x) ((PCODE(x)->type == PC_WILD)) +#define isPCCS(x) ((PCODE(x)->type == PC_CSOURCE)) +#define isPCASMDIR(x) ((PCODE(x)->type == PC_ASMDIR)) + +/* + macros for checking pCodeInstruction types +*/ +#define isCALL(x) (isPCI(x) && (PCI(x)->op == POC_CALL)) +#define isPCI_BRANCH(x) (isPCI(x) && PCI(x)->isBranch) +#define isPCI_SKIP(x) (isPCI(x) && PCI(x)->isSkip) +#define isPCI_LIT(x) (isPCI(x) && PCI(x)->isLit) +#define isPCI_BITSKIP(x)(isPCI_SKIP(x) && PCI(x)->isBitInst) + + +#define isSTATUS_REG(r) ((r)->pc_type == PO_STATUS) + +/*-----------------------------------------------------------------* + * pCode functions. + *-----------------------------------------------------------------*/ + +pCode *newpCode(PIC_OPCODE op, pCodeOp *pcop); // Create a new pCode given an operand +pCode *newpCodeCharP(const char *cP); // Create a new pCode given a char * +pCode *newpCodeFunction(const char *g, const char *f, int, int); // Create a new function. +pCode *newpCodeLabel(const char *name,int key); // Create a new label given a key +pCode *newpCodeCSource(int ln, const char *f, const char *l); // Create a new symbol line. +pCode *newpCodeWild(int pCodeID, pCodeOp *optional_operand, pCodeOp *optional_label); +pCode *findNextInstruction(pCode *pci); +pCode *findPrevInstruction(pCode *pci); +pCode *findNextpCode(pCode *pc, PC_TYPE pct); +pCode *pCodeInstructionCopy(pCodeInstruction *pci,int invert); + +pBlock *newpCodeChain(memmap *cm,char c, pCode *pc); // Create a new pBlock +void printpBlock(FILE *of, pBlock *pb); // Write a pBlock to a file +void printpCode(FILE *of, pCode *pc); // Write a pCode to a file +void addpCode2pBlock(pBlock *pb, pCode *pc); // Add a pCode to a pBlock +void addpBlock(pBlock *pb); // Add a pBlock to a pFile +void unlinkpCode(pCode *pc); +void copypCode(FILE *of, char dbName); // Write all pBlocks with dbName to *of +void movepBlock2Head(char dbName); // move pBlocks around +void AnalyzeBanking(void); +void ReuseReg(void); +void AnalyzepCode(char dbName); +void InlinepCode(void); +void pCodeInitRegisters(void); +void pic14initpCodePeepCommands(void); +void pBlockConvert2ISR(pBlock *pb); +void pBlockMergeLabels(pBlock *pb); +void pCodeInsertAfter(pCode *pc1, pCode *pc2); +void pCodeInsertBefore(pCode *pc1, pCode *pc2); +void pCodeDeleteChain(pCode *f,pCode *t); + +pCode *newpCodeAsmDir(const char *asdir, const char *argfmt, ...); + +pCodeOp *newpCodeOpLabel(const char *name, int key); +pCodeOp *newpCodeOpImmd(const char *name, int offset, int index, int code_space,int is_func); +pCodeOp *newpCodeOpLit(int lit); +pCodeOp *newpCodeOpBit(const char *name, int bit,int inBitSpace); +pCodeOp *newpCodeOpWild(int id, pCodeWildBlock *pcwb, pCodeOp *subtype); +pCodeOp *newpCodeOpRegFromStr(const char *name); +pCodeOp *newpCodeOp(const char *name, PIC_OPTYPE p); +pCodeOp *pCodeOpCopy(pCodeOp *pcop); +pCodeOp *popCopyGPR2Bit(pCodeOp *pc, int bitval); +pCodeOp *popCopyReg(pCodeOpReg *pc); + +pBranch *pBranchAppend(pBranch *h, pBranch *n); + +struct reg_info * getRegFromInstruction(pCode *pc); + +char *get_op(pCodeOp *pcop, char *buff, size_t buf_size); +char *pCode2str(char *str, size_t size, pCode *pc); + +int pCodePeepMatchRule(pCode *pc); + +void pcode_test(void); +void resetpCodeStatistics (void); +void dumppCodeStatistics (FILE *of); + +/*-----------------------------------------------------------------* + * pCode objects. + *-----------------------------------------------------------------*/ + +extern pCodeOpReg pc_status; +extern pCodeOpReg pc_intcon; +extern pCodeOpReg pc_fsr; +extern pCodeOpReg pc_fsr0l; +extern pCodeOpReg pc_fsr0h; +extern pCodeOpReg *pc_indf; /* pointer to either pc_indf_ or pc_indf0 */ +extern pCodeOpReg pc_indf_; +extern pCodeOpReg pc_indf0; +extern pCodeOpReg pc_pcl; +extern pCodeOpReg pc_pclath; +extern pCodeOpReg pc_wsave; /* wsave, ssave and psave are used to save W, the Status and PCLATH*/ +extern pCodeOpReg pc_ssave; /* registers during an interrupt */ +extern pCodeOpReg pc_psave; /* registers during an interrupt */ + +extern pFile *the_pFile; +extern pCodeInstruction *pic14Mnemonics[MAX_PIC14MNEMONICS]; + +/* + * From pcodepeep.h: + */ +int getpCode(const char *mnem, unsigned dest); +int getpCodePeepCommand(const char *cmd); +int pCodeSearchCondition(pCode *pc, unsigned int cond, int contIfSkip); + +#endif // __PCODE_H__ + diff --git a/src/pic14/pcodeflow.c b/src/pic14/pcodeflow.c new file mode 100644 index 0000000..decf088 --- /dev/null +++ b/src/pic14/pcodeflow.c @@ -0,0 +1,226 @@ +/*------------------------------------------------------------------------- + + pcodeflow.c - post code generation flow analysis + + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ +/* + pcodeflow.c + + The purpose of the code in this file is to analyze the flow of the + pCode. + +*/ + +#include "pcodeflow.h" + +#if 0 +static void dbg_dumpFlow(pBlock *pb) +{ + pCode *pcflow; + + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) + fprintf(stderr, "LinkFlow - pcflow is not a flow object "); + + fprintf(stderr, "Flow: 0x%x",pcflow->seq); + if(PCFL(pcflow) && PCFL(pcflow)->ancestor) + fprintf(stderr,", ancestor 0x%x\n", + PCFL(pcflow)->ancestor->pc.seq); + else { + pCodeFlowLink *from = (PCFL(pcflow)->from) ? (PCFLINK(setFirstItem(PCFL(pcflow)->from))) : NULL; + fprintf(stderr," no ancestor"); + while(from) { + fprintf(stderr," (0x%x)",from->pcflow->pc.seq); + from = setNextItem(PCFL(pcflow)->from); + } + fprintf(stderr,"\n"); + } + } + +} +#endif + +#if 0 +/*-----------------------------------------------------------------* +* void BuildFlowSegment(set *segment, pCodeFlow *pcflow) +*-----------------------------------------------------------------*/ +static void BuildFlowSegment(pCodeFlow *pcflow) +{ + static int recursion=0; + pCodeFlow *pcflow_other; + set *flowset; + + if(!pcflow) + return; + + if(recursion++ > 200) { + fprintf(stderr, " exceeded recursion\n"); + return; + } + + fprintf(stderr,"examining 0x%x\n",pcflow->pc.seq); + + if(pcflow->from) { + + + flowset = pcflow->from; + + if(flowset && flowset->next == NULL) { + + /* + There is a flow before this one. In fact, there's + exactly one flow before this one (because ->next is + NULL). That means all children of this node pass + through both this node and the node immediately + before this one; i.e. they have the same ancestor. + */ + + if( (NULL == (pcflow_other = PCFLINK(flowset->item)->pcflow)) || + (NULL == pcflow_other)) { + fprintf(stderr,"2 error in flow link\n"); + exit(1); + } + pcflow->ancestor = pcflow_other->ancestor ; + + fprintf(stderr,"Assigning ancestor 0x%x from flow 0x%x\n", + pcflow->ancestor->pc.seq, pcflow_other->pc.seq); + + } else { + + if(flowset == NULL) { + + /* There are no flows before this one. + * If this is not the first flow object in the pBlock then + * there's an error */ + + if(!pcflow->ancestor) { + fprintf(stderr,"error in flow link\n"); + exit(1); + + } + + } else { + + /* Flow passes to this flow from multiple flows. Let's + look at just one of these. If the one we look at has + an ancestor, then that's our ancestor too. If the one + we look at doesn't have an ancestor, then that means + we haven't traversed that branch of the call tree + yet - but we will */ + + pcflow_other = PCFLINK(flowset->item)->pcflow; + if(pcflow_other) { + fprintf(stderr, "coming from 0x%x\n",pcflow_other->pc.seq); + if( pcflow_other->ancestor) + pcflow->ancestor = pcflow_other->ancestor; + } + } + + + } + + } else { + /* there are no nodes before this one */ + if(!pcflow->ancestor) + fprintf(stderr,"3 Error in flow link\n"); + } + + /* Now let's recursively expand the call tree */ + + if(pcflow->ancestor && pcflow->to) { + flowset = pcflow->to; + while(flowset) { + BuildFlowSegment(PCFLINK(flowset->item)->pcflow); + flowset = flowset->next; + } + } + +} +#endif + +void BuildFlowTree(pBlock *pb) +{ + pCodeFlow *first_pcflow, *pcflow; + + + // fprintf(stderr,"BuildFlowTree \n"); + + first_pcflow = PCFL(findNextpCode(pb->pcHead, PC_FLOW)); + if(!first_pcflow) + return; + + /* The very first node is like Adam, it's its own ancestor (i.e. + * there are no other flows in this pBlock prior to the first one). + */ + + first_pcflow->ancestor = first_pcflow; + + /* For each flow that has only one predecessor, it's easy to + identify the ancestor */ + pcflow = PCFL(findNextpCode(first_pcflow->pc.next, PC_FLOW)); + + while(pcflow) { + if(elementsInSet(pcflow->from) == 1) { + pCodeFlowLink *from = PCFLINK(setFirstItem(pcflow->from)); + + pcflow->ancestor = from->pcflow; + /* + fprintf(stderr,"Assigning ancestor 0x%x to flow 0x%x\n", + pcflow->ancestor->pc.seq, pcflow->pc.seq); + */ + } + + pcflow = PCFL(findNextpCode(pcflow->pc.next, PC_FLOW)); + + } + + pcflow = PCFL(findNextpCode(first_pcflow->pc.next, PC_FLOW)); + + while(pcflow) { + if(elementsInSet(pcflow->from) > 1) { + pCodeFlow *min_pcflow; + pCodeFlowLink *from = PCFLINK(setFirstItem(pcflow->from)); + + min_pcflow = from->pcflow; + + while( (from = setNextItem(pcflow->from)) != NULL) { + if(from->pcflow->pc.seq < min_pcflow->pc.seq) + min_pcflow = from->pcflow; + } + + pcflow->ancestor = min_pcflow; + /* + fprintf(stderr,"Assigning ancestor 0x%x to flow 0x%x from multiple\n", + pcflow->ancestor->pc.seq, pcflow->pc.seq); + */ + + } + + pcflow = PCFL(findNextpCode(pcflow->pc.next, PC_FLOW)); + + } + + // BuildFlowSegment(pcflow); + + //dbg_dumpFlow(pb); + +} diff --git a/src/pic14/pcodeflow.h b/src/pic14/pcodeflow.h new file mode 100644 index 0000000..1577770 --- /dev/null +++ b/src/pic14/pcodeflow.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + + pcode.h - post code generation + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +#ifndef __PCODEFLOW_H__ +#define __PCODEFLOW_H__ + +#include "pcode.h" + +/************************************************* + * pCode conditions: + * + * The "conditions" are bit-mapped flags that describe + * input and/or output conditions that are affected by + * the instructions. For example: + * + * MOVF SOME_REG,W + * + * This instruction depends upon 'SOME_REG'. Consequently + * it has the input condition PCC_REGISTER set to true. + * + * In addition, this instruction affects the Z bit in the + * status register and affects W. Thus the output conditions + * are the logical or: + * PCC_ZERO_BIT | PCC_W + * + * The conditions are intialized when the pCode for an + * instruction is created. They're subsequently used + * by the pCode optimizer determine state information + * in the program flow. + *************************************************/ + +#define PCC_NONE 0 +#define PCC_REGISTER (1<<0) +#define PCC_C (1<<1) +#define PCC_Z (1<<2) +#define PCC_DC (1<<3) +#define PCC_W (1<<4) +#define PCC_EXAMINE_PCOP (1<<5) +#define PCC_REG_BANK0 (1<<6) +#define PCC_REG_BANK1 (1<<7) +#define PCC_REG_BANK2 (1<<8) +#define PCC_REG_BANK3 (1<<9) +#define PCC_LITERAL (1<<10) + +/*------------------------------------------------------------*/ + +void BuildFlowTree(pBlock *pb); + +#endif // __PCODEFLOW_H__ + diff --git a/src/pic14/pcodepeep.c b/src/pic14/pcodepeep.c new file mode 100644 index 0000000..fad58af --- /dev/null +++ b/src/pic14/pcodepeep.c @@ -0,0 +1,2005 @@ +/*------------------------------------------------------------------------- + + pcodepeep.c - post code generation + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "pcode.h" +#include "pcodeflow.h" +#include "ralloc.h" + +//#define PCODE_DEBUG + +#define IS_PCCOMMENT(x) ( x && (x->type==PC_COMMENT)) + + +/****************************************************************/ +/****************************************************************/ +typedef struct DLList { + struct DLList *prev; + struct DLList *next; + // void *data; +} DLList; + + +typedef struct pCodePeepSnippets +{ + DLList dll; + pCodePeep *peep; +} pCodePeepSnippets; + + +/****************************************************************/ +/* */ +/* peepSnippets - */ +/* */ +/****************************************************************/ + +static pCodePeepSnippets *peepSnippets=NULL; + + +typedef struct pCodeToken +{ + int tt; // token type; + union { + char c; // character + int n; // number + char *s; // string + } tok; + +} pCodeToken; + +static pCodeToken tokArr[50]; +static unsigned tokIdx=0; + + +typedef enum { + PCT_NULL=0, + PCT_SPACE=1, + PCT_PERCENT, + PCT_LESSTHAN, + PCT_GREATERTHAN, + PCT_COLON, + PCT_COMMA, + PCT_COMMENT, + PCT_STRING, + PCT_NUMBER + +} pCodeTokens; + + +typedef struct parsedPattern { + struct pcPattern *pcp; + pCodeToken *pct; +} parsedPattern; + +#define MAX_PARSEDPATARR 50 +static parsedPattern parsedPatArr[MAX_PARSEDPATARR]; + +typedef enum { + PCP_LABEL=1, + PCP_NUMBER, + PCP_STR, + PCP_WILDVAR, + PCP_WILDSTR, + PCP_COMMA, + PCP_COMMENT +} pCodePatterns; + +static char pcpat_label[] = {PCT_PERCENT, PCT_NUMBER, PCT_COLON, 0}; +static char pcpat_number[] = {PCT_NUMBER, 0}; +static char pcpat_string[] = {PCT_STRING, 0}; +static char pcpat_wildString[] = {PCT_PERCENT, PCT_STRING, 0}; +static char pcpat_wildVar[] = {PCT_PERCENT, PCT_NUMBER, 0}; +static char pcpat_comma[] = {PCT_COMMA, 0}; +static char pcpat_comment[] = {PCT_COMMENT, 0}; + + +typedef struct pcPattern { + char pt; // Pattern type + char *tokens; // list of tokens that describe the pattern + void * (*f) (void *,pCodeWildBlock *); +} pcPattern; + +static pcPattern pcpArr[] = { + {PCP_LABEL, pcpat_label, NULL}, + {PCP_WILDSTR, pcpat_wildString, NULL}, + {PCP_STR, pcpat_string, NULL}, + {PCP_WILDVAR, pcpat_wildVar, NULL}, + {PCP_COMMA, pcpat_comma, NULL}, + {PCP_COMMENT, pcpat_comment, NULL}, + {PCP_NUMBER, pcpat_number, NULL} +}; + +#define PCPATTERNS (sizeof(pcpArr)/sizeof(pcPattern)) + +// Assembly Line Token +typedef enum { + ALT_LABEL=1, + ALT_COMMENT, + ALT_MNEM0, + ALT_MNEM0A, + ALT_MNEM1, + ALT_MNEM1A, + ALT_MNEM1B, + ALT_MNEM2, + ALT_MNEM2A, + ALT_MNEM3 +} altPatterns; + +static char alt_comment[] = { PCP_COMMENT, 0}; +static char alt_label[] = { PCP_LABEL, 0}; +static char alt_mnem0[] = { PCP_STR, 0}; +static char alt_mnem0a[] = { PCP_WILDVAR, 0}; +static char alt_mnem1[] = { PCP_STR, PCP_STR, 0}; +static char alt_mnem1a[] = { PCP_STR, PCP_WILDVAR, 0}; +static char alt_mnem1b[] = { PCP_STR, PCP_NUMBER, 0}; +static char alt_mnem2[] = { PCP_STR, PCP_STR, PCP_COMMA, PCP_STR, 0}; +static char alt_mnem2a[] = { PCP_STR, PCP_WILDVAR, PCP_COMMA, PCP_STR, 0}; +static char alt_mnem3[] = { PCP_STR, PCP_STR, PCP_COMMA, PCP_NUMBER, 0}; + +static void * cvt_altpat_label(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_comment(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem0(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem0a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1b(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem2(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem2a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem3(void *pp,pCodeWildBlock *pcwb); + +static pcPattern altArr[] = { + {ALT_LABEL, alt_label, cvt_altpat_label}, + {ALT_COMMENT, alt_comment,cvt_altpat_comment}, + {ALT_MNEM3, alt_mnem3, cvt_altpat_mnem3}, + {ALT_MNEM2A, alt_mnem2a, cvt_altpat_mnem2a}, + {ALT_MNEM2, alt_mnem2, cvt_altpat_mnem2}, + {ALT_MNEM1B, alt_mnem1b, cvt_altpat_mnem1b}, + {ALT_MNEM1A, alt_mnem1a, cvt_altpat_mnem1a}, + {ALT_MNEM1, alt_mnem1, cvt_altpat_mnem1}, + {ALT_MNEM0A, alt_mnem0a, cvt_altpat_mnem0a}, + {ALT_MNEM0, alt_mnem0, cvt_altpat_mnem0}, + +}; + +#define ALTPATTERNS (sizeof(altArr)/sizeof(pcPattern)) + +// forward declarations +static void * DLL_append(DLList *list, DLList *next); + +/*-----------------------------------------------------------------*/ +/* cvt_extract_destination - helper function extracts the register */ +/* destination from a parsedPattern. */ +/* */ +/*-----------------------------------------------------------------*/ +static int cvt_extract_destination(parsedPattern *pp) +{ + + if(pp->pct[0].tt == PCT_STRING) { + + // just check first letter for now + + if(toupper((unsigned char)*pp->pct[0].tok.s) == 'F') + return 1; + + } else if (pp->pct[0].tt == PCT_NUMBER) { + + if(pp->pct[0].tok.n) + return 1; + } + + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* pCodeOp *cvt_extract_status(char *reg, char *bit) */ +/* if *reg is the "status" register and *bit is one of the */ +/* status bits, then this function will create a new pCode op */ +/* containing the status register. */ +/*-----------------------------------------------------------------*/ + +static pCodeOp *cvt_extract_status(char *reg, char *bit) +{ + int len; + + if(STRCASECMP(reg, pc_status.pcop.name)) + return NULL; + + len = strlen(bit); + + if(len == 1) { + // check C,Z + if(toupper((unsigned char)*bit) == 'C') + return PCOP(popCopyGPR2Bit(PCOP(&pc_status),PIC_C_BIT)); + if(toupper((unsigned char)*bit) == 'Z') + return PCOP(popCopyGPR2Bit(PCOP(&pc_status),PIC_Z_BIT)); + } + + // Check DC + if(len ==2 && toupper((unsigned char)bit[0]) == 'D' && toupper((unsigned char)bit[1]) == 'C') + return PCOP(popCopyGPR2Bit(PCOP(&pc_status),PIC_DC_BIT)); + + return NULL; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_label - convert assembly line type to a pCode label */ +/* INPUT: pointer to the parsedPattern */ +/* */ +/* pp[0] - label */ +/* */ +/* label pattern => '%' number ':' */ +/* at this point, we wish to extract only the 'number' */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_label(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_label with ID = %d\n",p->pct[1].tok.n)); + return newpCodeLabel(NULL,-p->pct[1].tok.n); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_comment - convert assembly line type to a comment */ +/* INPUT: pointer to the parsedPattern */ +/* */ +/* pp[0] - comment */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_comment(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_comment = %s\n",p->pct[0].tok.s)); + return newpCodeCharP(p->pct[0].tok.s); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem0 - convert assembly line type to a wild pCode */ +/* instruction */ +/* */ +/* pp[0] - str */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem0(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + + DFPRINTF((stderr,"altpat_mnem0 %s\n", p->pct[0].tok.s)); + + opcode = getpCode(p->pct[0].tok.s,0); + + if(opcode < 0) { + /* look for special command strings like _NOTBITSKIP_ */ + + //fprintf(stderr, "Bad mnemonic\n"); + + opcode = getpCodePeepCommand(p->pct[0].tok.s); + //if(opcode > 0) + // fprintf(stderr," but valid peep command: %s, key = %d\n",p->pct[0].tok.s,opcode); + return NULL; + } + + pci = PCI(newpCode(opcode, NULL)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem0a - convert assembly line type to a wild pCode */ +/* instruction */ +/* */ +/* pp[0] - wild var */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem0a(void *pp, pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_mnem0a wild mnem # %d\n", p[0].pct[1].tok.n)); + + /* Save the index of the maximum wildcard mnemonic */ + if(p[0].pct[1].tok.n > pcwb->nwildpCodes) + pcwb->nwildpCodes = p[0].pct[1].tok.n; + + return newpCodeWild(p[0].pct[1].tok.n,NULL,NULL); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem1 - convert assembly line type to a pCode */ +/* instruction with 1 operand. */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - Operand */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1(void *pp,pCodeWildBlock *pcwb) +{ + + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + DFPRINTF((stderr,"altpat_mnem1 %s var %s\n", p->pct[0].tok.s,p[1].pct[0].tok.s)); + + opcode = getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + //fprintf(stderr, "Bad mnemonic\n"); + opcode = getpCodePeepCommand(p->pct[0].tok.s); + //if(opcode > 0) + //fprintf(stderr," but valid peep command: %s, key = %d\n",p->pct[0].tok.s,opcode); + + return NULL; + } + + if(pic14Mnemonics[opcode]->isBitInst) + pcosubtype = newpCodeOp(p[1].pct[0].tok.s,PO_BIT); + else + pcosubtype = newpCodeOp(p[1].pct[0].tok.s,PO_GPR_REGISTER); + + + pci = PCI(newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem1a - convert assembly line type to a pCode */ +/* instruction with 1 wild operand. */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - wild var */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1a(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + DFPRINTF((stderr,"altpat_mnem1a %s var %d\n", p->pct[0].tok.s,p[1].pct[1].tok.n)); + + opcode = getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + int cmd_id = getpCodePeepCommand(p->pct[0].tok.s); + pCode *pc=NULL; + + if(cmd_id<0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(p[0].pct[1].tok.n > pcwb->nwildpCodes) + pcwb->nwildpCodes = p[0].pct[1].tok.n; + + pc = newpCodeWild(p[1].pct[1].tok.n,NULL,NULL); + + switch(cmd_id) { + case NOTBITSKIP: + PCW(pc)->mustNotBeBitSkipInst = 1; + break; + case BITSKIP: + PCW(pc)->mustBeBitSkipInst = 1; + break; + case INVERTBITSKIP: + PCW(pc)->invertBitSkipInst = 1; + } + return pc; + } + + if(pic14Mnemonics[opcode]->isBitInst) + pcosubtype = newpCodeOpBit(NULL,-1,0); + else + pcosubtype = newpCodeOp(NULL,PO_GPR_REGISTER); + + + pci = PCI(newpCode(opcode, + newpCodeOpWild(p[1].pct[1].tok.n, pcwb, pcosubtype))); + + /* Save the index of the maximum wildcard variable */ + if(p[1].pct[1].tok.n > pcwb->nvars) + pcwb->nvars = p[1].pct[1].tok.n; + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1b(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + + DFPRINTF((stderr,"altpat_mnem1b %s var %d\n", p->pct[0].tok.s,p[1].pct[0].tok.n)); + + opcode = getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + pci = PCI(newpCode(opcode, newpCodeOpLit(p[1].pct[0].tok.n) )); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mnem2 */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - var */ +/* pp[2] - comma */ +/* pp[3] - destination */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem2(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + int dest; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + dest = cvt_extract_destination(&p[3]); + + DFPRINTF((stderr,"altpat_mnem2 %s var %s destination %s(%d)\n", + p->pct[0].tok.s, + p[1].pct[0].tok.s, + p[3].pct[0].tok.s, + dest)); + + + opcode = getpCode(p->pct[0].tok.s,dest); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(pic14Mnemonics[opcode]->isBitInst) { + pcosubtype = cvt_extract_status(p[1].pct[0].tok.s, p[3].pct[0].tok.s); + if(pcosubtype == NULL) { + fprintf(stderr, "bad operand?\n"); + return NULL; + } + + } else + pcosubtype = newpCodeOp(p[1].pct[0].tok.s,PO_GPR_REGISTER); + + + pci = PCI(newpCode(opcode,pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem2a - convert assembly line type to a pCode */ +/* instruction with 1 wild operand and a */ +/* destination operand (e.g. w or f) */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - wild var */ +/* pp[2] - comma */ +/* pp[3] - destination */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem2a(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + int dest; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + if(!pcwb) { + fprintf(stderr,"ERROR %s:%d - can't assemble line\n",__FILE__,__LINE__); + return NULL; + } + + dest = cvt_extract_destination(&p[3]); + + DFPRINTF((stderr,"altpat_mnem2a %s var %d destination %s(%d)\n", + p->pct[0].tok.s, + p[1].pct[1].tok.n, + p[3].pct[0].tok.s, + dest)); + + + opcode = getpCode(p->pct[0].tok.s,dest); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(pic14Mnemonics[opcode]->isBitInst) + pcosubtype = newpCodeOp(NULL,PO_BIT); + else + pcosubtype = newpCodeOp(NULL,PO_GPR_REGISTER); + + + pci = PCI(newpCode(opcode, + newpCodeOpWild(p[1].pct[1].tok.n, pcwb, pcosubtype))); + + /* Save the index of the maximum wildcard variable */ + if(p[1].pct[1].tok.n > pcwb->nvars) + pcwb->nvars = p[1].pct[1].tok.n; + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem3 - convert assembly line type to a pCode */ +/* This rule is for bsf/bcf type instructions */ +/* */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - register */ +/* pp[2] - comma */ +/* pp[3] - number */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem3(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype=NULL; + + DFPRINTF((stderr,"altpat_mnem3 %s var %s bit (%d)\n", + p->pct[0].tok.s, + p[1].pct[0].tok.s, + p[3].pct[0].tok.n)); + + + opcode = getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + + if(pic14Mnemonics[opcode]->isBitInst) { + //pcosubtype = cvt_extract_status(p[1].pct[0].tok.s, p[3].pct[0].tok.s); + + //if(pcosubtype == NULL) { + pcosubtype = newpCodeOpBit(p[1].pct[0].tok.s,p[3].pct[0].tok.n,0); + //} + } else + pcosubtype = newpCodeOp(p[1].pct[0].tok.s,PO_GPR_REGISTER); + + if(pcosubtype == NULL) { + fprintf(stderr, "Bad operand\n"); + return NULL; + } + + pci = PCI(newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* tokenizeLineNode - Convert a string (of char's) that was parsed */ +/* by SDCCpeeph.c into a string of tokens. */ +/* */ +/* */ +/* The tokenizer is of the classic type. When an item is encounterd*/ +/* it is converted into a token. The token is a structure that */ +/* encodes the item's type and it's value (when appropriate). */ +/* */ +/* Accepted token types: */ +/* SPACE NUMBER STRING % : , ; */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ + + +static void tokenizeLineNode(char *ln) +{ + char *lnstart=ln; + tokIdx = 0; // Starting off at the beginning + tokArr[0].tt = PCT_NULL; // and assume invalid character for first token. + + if(!ln || !*ln) + return; + + + while(*ln) { + + if(isspace((unsigned char)*ln)) { + // add a SPACE token and eat the extra spaces. + tokArr[tokIdx++].tt = PCT_SPACE; + while (isspace ((unsigned char)*ln)) + ln++; + continue; + } + + if(isdigit((unsigned char)*ln)) { + + tokArr[tokIdx].tt = PCT_NUMBER; + tokArr[tokIdx++].tok.n = strtol(ln, &ln, 0); + + continue; + + } + + switch(*ln) { + case '%': + tokArr[tokIdx++].tt = PCT_PERCENT; + break; + case '<': + tokArr[tokIdx++].tt = PCT_LESSTHAN; + break; + case '>': + tokArr[tokIdx++].tt = PCT_GREATERTHAN; + break; + case ':': + tokArr[tokIdx++].tt = PCT_COLON; + break; + case ';': + tokArr[tokIdx].tok.s = Safe_strdup(ln); + tokArr[tokIdx++].tt = PCT_COMMENT; + tokArr[tokIdx].tt = PCT_NULL; + return; + case ',': + tokArr[tokIdx++].tt = PCT_COMMA; + break; + + + default: + if(isalpha((unsigned char)*ln) || (*ln == '_') ) { + char buffer[50]; + int i=0; + + while( (isalpha((unsigned char)*ln) || isdigit((unsigned char)*ln) || (*ln == '_')) && i<49) + buffer[i++] = *ln++; + + ln--; + buffer[i] = 0; + + tokArr[tokIdx].tok.s = Safe_strdup(buffer); + tokArr[tokIdx++].tt = PCT_STRING; + + } else { + fprintf(stderr, "Error while parsing peep rules (check peeph.def)\n"); + fprintf(stderr, "Line: %s\n",lnstart); + fprintf(stderr, "Token: '%c'\n",*ln); + exit(1); + } + } + + /* Advance to next character in input string . + * Note, if none of the tests passed above, then + * we effectively ignore the `bad' character. + * Since the line has already been parsed by SDCCpeeph, + * chance are that there are no invalid characters... */ + + ln++; + + } + + tokArr[tokIdx].tt = 0; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + + + +static void dump1Token(pCodeTokens tt) +{ + + switch(tt) { + case PCT_SPACE: + fprintf(stderr, " space "); + break; + case PCT_PERCENT: + fprintf(stderr, " pct %%"); + break; + case PCT_LESSTHAN: + fprintf(stderr, " pct <"); + break; + case PCT_GREATERTHAN: + fprintf(stderr, " pct >"); + break; + case PCT_COLON: + fprintf(stderr, " col :"); + break; + case PCT_COMMA: + fprintf(stderr, " comma , "); + break; + case PCT_COMMENT: + fprintf(stderr, " comment "); + //fprintf(stderr,"%s",tokArr[i].tok.s); + break; + case PCT_STRING: + fprintf(stderr, " str "); + //fprintf(stderr,"%s",tokArr[i].tok.s); + break; + case PCT_NUMBER: + fprintf(stderr, " num "); + //fprintf(stderr,"%d",tokArr[i].tok.n); + break; + case PCT_NULL: + fprintf(stderr, " null "); + + } + +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int pcComparePattern(pCodeToken *pct, char *pat, int max_tokens) +{ + int i=0; + + if(!pct || !pat || !*pat) + return 0; + + //DFPRINTF((stderr,"comparing against:\n")); + + while(i < max_tokens) { + + if(*pat == 0){ + //DFPRINTF((stderr,"matched\n")); + return (i+1); + } + + //dump1Token(*pat); DFPRINTF((stderr,"\n")); + + if(pct->tt != *pat) + return 0; + + + pct++; + pat++; + } + + return 0; + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int altComparePattern(char *pct, parsedPattern *pat, int max_tokens) +{ + int i=0; + + if(!pct || !pat || !*pct) + return 0; + + + while(i < max_tokens) { + + if(*pct == 0) { + //DFPRINTF((stderr,"matched\n")); + return i; + } + + //dump1Token(*pat); DFPRINTF((stderr,"\n")); + + if( !pat || !pat->pcp ) + return 0; + + if (pat->pcp->pt != *pct) + return 0; + + //DFPRINTF((stderr," pct=%d\n",*pct)); + pct++; + pat++; + i++; + } + + return 0; + +} +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int advTokIdx(int *v, int amt) +{ + + if((unsigned) (*v + amt) > tokIdx) + return 1; + + *v += amt; + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* parseTokens - convert the tokens corresponding to a single line */ +/* of a peep hole assembly into a pCode object. */ +/* */ +/* */ +/* */ +/* */ +/* This is a simple parser that looks for strings of the type */ +/* allowed in the peep hole definition file. Essentially the format*/ +/* is the same as a line of assembly: */ +/* */ +/* label: mnemonic op1, op2, op3 ; comment */ +/* */ +/* Some of these items aren't present. It's the job of the parser */ +/* to determine which are and convert those into the appropriate */ +/* pcode. */ +/*-----------------------------------------------------------------*/ + +static int parseTokens(pCodeWildBlock *pcwb, pCode **pcret) +{ + pCode *pc; + int error = 0; + + if(!tokIdx) + return error; + +#ifdef PCODE_DEBUG + { + unsigned i; + for(i=0; i<=tokIdx; i++) + dump1Token(tokArr[i].tt); + fputc('\n',stderr); + } +#endif + + { + int lparsedPatIdx=0; + int lpcpIdx; + int ltokIdx =0; + int matching = 0; + int j=0; + int k=0; + + //pCodeOp *pcl = NULL; // Storage for a label + //pCodeOp *pco1 = NULL; // 1st operand + //pCodeOp *pco2 = NULL; // 2nd operand + //pCode *pc = NULL; // Mnemonic + + typedef enum { + PS_START, + PS_HAVE_LABEL, + PS_HAVE_MNEM, + PS_HAVE_1OPERAND, + PS_HAVE_COMMA, + PS_HAVE_2OPERANDS + } ParseStates; + + ParseStates state = PS_START; + + do { + + lpcpIdx=0; + matching = 0; + + if( ((tokArr[ltokIdx].tt == PCT_SPACE) ) + && (advTokIdx(<okIdx, 1)) ) // eat space + break; + + do { + j = pcComparePattern(&tokArr[ltokIdx], pcpArr[lpcpIdx].tokens, tokIdx +1); + if( j ) { + + switch(pcpArr[lpcpIdx].pt) { + case PCP_LABEL: + if(state == PS_START){ + DFPRINTF((stderr," label\n")); + state = PS_HAVE_LABEL; + } else + DFPRINTF((stderr," bad state (%d) for label\n",state)); + break; + + case PCP_STR: + DFPRINTF((stderr," %s is",tokArr[ltokIdx].tok.s)); + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + DFPRINTF((stderr," mnem\n")); + state = PS_HAVE_MNEM; + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand\n")); + //pco1 = newpCodeOp(NULL,PO_GPR_REGISTER); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + DFPRINTF((stderr," error expecting comma\n")); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2 operands\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_WILDVAR: + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + DFPRINTF((stderr," wild mnem\n")); + state = PS_HAVE_MNEM; + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand is wild\n")); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + DFPRINTF((stderr," error expecting comma\n")); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2nd operand is wild\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_NUMBER: + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + fprintf(stderr," ERROR number\n"); + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand is a number\n")); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + fprintf(stderr," error expecting comma\n"); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2nd operand is a number\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_WILDSTR: + break; + case PCP_COMMA: + if(state == PS_HAVE_1OPERAND){ + DFPRINTF((stderr," got a comma\n")); + state = PS_HAVE_COMMA; + } else + fprintf(stderr," unexpected comma\n"); + break; + + } + + matching = 1; + parsedPatArr[lparsedPatIdx].pcp = &pcpArr[lpcpIdx]; + parsedPatArr[lparsedPatIdx].pct = &tokArr[ltokIdx]; + lparsedPatIdx++; + + //dump1Token(tokArr[ltokIdx].tt); + + if(advTokIdx(<okIdx, strlen(pcpArr[lpcpIdx].tokens) ) ) { + DFPRINTF((stderr," reached end \n")); + matching = 0; + //return; + } + } + + + } while ((++lpcpIdx < PCPATTERNS) && !matching); + + } while (matching); + + parsedPatArr[lparsedPatIdx].pcp = NULL; + parsedPatArr[lparsedPatIdx].pct = NULL; + + j=k=0; + do { + int c; + + if( (c=altComparePattern( altArr[k].tokens, &parsedPatArr[j],10) ) ) { + + if( altArr[k].f) { + pc = altArr[k].f(&parsedPatArr[j],pcwb); + //if(pc && pc->print) + // pc->print(stderr,pc); + //if(pc && pc->destruct) pc->destruct(pc); dumps core? + + if(pc) { + if (pcret) { + *pcret = pc; + return 0; // Only accept one line for now. + } else + addpCode2pBlock(pcwb->pb, pc); + } else + error++; + } + j += c; + } + k++; + } + while(j<=lparsedPatIdx && kf ) + parsedPatArr[j].pcp->f(&parsedPatArr[j]); + DFPRINTF((stderr," %d",parsedPatArr[j].pcp->pt)); + j++; + } + while(jnext) { + + //DFPRINTF((stderr,"%s\n",ln->line)); + + tokenizeLineNode(ln->line); + + if(parseTokens(pcwb,NULL)) { + int i; + fprintf(stderr,"ERROR assembling line:\n%s\n",ln->line); + fprintf(stderr,"Tokens:\n"); + for(i=0; i<5; i++) + dump1Token(tokArr[i].tt); + fputc('\n',stderr); + exit (1); + } + } +} + +/*-----------------------------------------------------------------*/ +/* peepRuleCondition */ +/*-----------------------------------------------------------------*/ +static void peepRuleCondition(char *cond, pCodePeep *pcp) +{ + if(!cond || !pcp) + return; + + //DFPRINTF((stderr,"\nCondition: %s\n",cond)); + /* brute force compares for now */ + + if(STRCASECMP(cond, "NZ") == 0) { + //DFPRINTF((stderr,"found NZ\n")); + pcp->postFalseCond = PCC_Z; + + } + +} + +static void initpCodeWildBlock(pCodeWildBlock *pcwb) +{ + + // pcwb = Safe_alloc(sizeof(pCodeWildBlock)); + + if(!pcwb) + return; + + pcwb->vars = NULL; + pcwb->wildpCodes = NULL; + pcwb->wildpCodeOps = NULL; + + pcwb->nvars = 0; + pcwb->nwildpCodes = 0; + pcwb->nops = 0; + +} + +static void postinit_pCodeWildBlock(pCodeWildBlock *pcwb) +{ + + if(!pcwb) + return; + + pcwb->nvars+=2; + pcwb->nops = pcwb->nvars; + + pcwb->vars = Safe_calloc(pcwb->nvars, sizeof(char *)); + pcwb->wildpCodeOps = Safe_calloc(pcwb->nvars, sizeof(pCodeOp *)); + + pcwb->nwildpCodes+=2; + pcwb->wildpCodes = Safe_calloc(pcwb->nwildpCodes, sizeof(pCode *)); + +} + +static void initpCodePeep(pCodePeep *pcp) +{ + + // pcwb = Safe_alloc(sizeof(pCodeWildBlock)); + + if(!pcp) + return; + + initpCodeWildBlock(&pcp->target); + pcp->target.pb = newpCodeChain(NULL, 'W', NULL); + + initpCodeWildBlock(&pcp->replace); + pcp->replace.pb = newpCodeChain(NULL, 'W', NULL); + +} + +/*-----------------------------------------------------------------*/ +/* peepRules2pCode - parse the "parsed" peep hole rules to generate*/ +/* pCode. */ +/* */ +/* SDCCpeeph parses the peep rules file and extracts variables, */ +/* removes white space, and checks the syntax. This function */ +/* extends that processing to produce pCode objects. You can kind */ +/* think of this function as an "assembler", though instead of */ +/* taking raw text to produce machine code, it produces pCode. */ +/* */ +/*-----------------------------------------------------------------*/ +void peepRules2pCode(peepRule *rules) +{ + peepRule *pr; + + pCodePeep *currentRule; + pCodePeepSnippets *pcps; + + pic14initpCodePeepCommands(); + + /* The rules are in a linked-list. Each rule has two portions */ + /* There's the `target' and there's the `replace'. The target */ + /* is compared against the SDCC generated code and if it */ + /* matches, it gets replaced by the `replace' block of code. */ + /* */ + /* Here we loop through each rule and convert the target's and*/ + /* replace's into pCode target and replace blocks */ + + for (pr = rules; pr; pr = pr->next) { + + //DFPRINTF((stderr,"\nRule:\n\n")); + + pcps = Safe_alloc(sizeof(pCodePeepSnippets)); + peepSnippets = DLL_append((DLList*)peepSnippets,(DLList*)pcps); + + currentRule = pcps->peep = Safe_alloc(sizeof(pCodePeep)); + initpCodePeep(currentRule); + + /* Convert the target block */ + peepRuleBlock2pCodeBlock(pr->match, ¤tRule->target); + + //DFPRINTF((stderr,"finished target, here it is in pcode form:\n")); + //printpBlock(stderr, currentRule->target.pb); + + pBlockMergeLabels(currentRule->target.pb); + //printpBlock(stderr, currentRule->replace.pb); + + /* Convert the replace block */ + peepRuleBlock2pCodeBlock(pr->replace, ¤tRule->replace); + + //DFPRINTF((stderr,"replace with labels merged:\n")); + + pBlockMergeLabels(currentRule->replace.pb); + //printpBlock(stderr, currentRule->replace.pb); + + peepRuleCondition(pr->cond,currentRule); + + /* The rule has been converted to pCode. Now allocate + * space for the wildcards */ + + postinit_pCodeWildBlock(¤tRule->target); + postinit_pCodeWildBlock(¤tRule->replace); + + //return; // debug ... don't want to go through all the rules yet + } + + if (0) + { + pCodePeep *peepBlock; + DLList *peeprules; + + peeprules = (DLList *)peepSnippets; + fprintf(stderr,"target rules\n"); + while (peeprules) + { + fprintf(stderr," rule:\n"); + peepBlock = ((pCodePeepSnippets*)peeprules)->peep; + printpBlock(stderr, peepBlock->target.pb); + peeprules = peeprules->next; + } // while + fprintf(stderr," ... done\n"); + } // if +} + +/*-----------------------------------------------------------------*/ +/* DLList * DLL_append */ +/* */ +/* Append a DLList object to the end of a DLList (doubly linked */ +/* list). If The list to which we want to append is non-existant */ +/* then one is created. Other wise, the end of the list is sought */ +/* out and a new DLList object is appended to it. In either case, */ +/* the void *data is added to the newly created DLL object. */ +/*-----------------------------------------------------------------*/ + +static void * DLL_append(DLList *list, DLList *next) +{ + DLList *b; + + + /* If there's no list, then create one: */ + if(!list) { + next->next = next->prev = NULL; + return next; + } + + + /* Search for the end of the list. */ + b = list; + while(b->next) + b = b->next; + + /* Now append the new DLL object */ + b->next = next; + b->next->prev = b; + b = b->next; + b->next = NULL; + + return list; + +} + + +/*----------------------------------------------------------------- + + pCode peephole optimization + + + The pCode "peep hole" optimization is not too unlike the peep hole + optimization in SDCCpeeph.c. The major difference is that here we + use pCode's whereas there we use ASCII strings. The advantage with + pCode's is that we can ascertain flow information in the instructions + being optimized. + + + - elaborate... + +-----------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------*/ +/* pCodeSearchCondition - Search a pCode chain for a 'condition' */ +/* */ +/* return conditions */ +/* 1 - The Condition was found for a pCode's input */ +/* 0 - No matching condition was found for the whole chain */ +/* -1 - The Condition was found for a pCode's output */ +/* */ +/*-----------------------------------------------------------------*/ +int pCodeSearchCondition(pCode *pc, unsigned int cond, int contIfSkip) +{ + while(pc) { + + /* If we reach a function end (presumably an end since we most + probably began the search in the middle of a function), then + the condition was not found. */ + if(pc->type == PC_FUNCTION) + return 0; + + if(pc->type == PC_OPCODE) { + if(PCI(pc)->inCond & cond) { + if (contIfSkip) { + /* If previous instruction is a skip then continue search as condiction is not certain */ + pCode *pcp = findPrevInstruction(pc->prev); + if (pcp && !isPCI_SKIP(pcp)) { + return 1; + } + } else { + return 1; + } + } + if(PCI(pc)->outCond & cond) { + if (contIfSkip) { + /* If previous instruction is a skip then continue search as condiction is not certain */ + pCode *pcp = findPrevInstruction(pc->prev); + if (pcp && !isPCI_SKIP(pcp)) { + return -1; + } + } else { + return -1; + } + } + } + + pc = pc->next; + } + + return 0; +} + +/*----------------------------------------------------------------- +* int pCodeOpCompare(pCodeOp *pcops, pCodeOp *pcopd) +* +* Compare two pCodeOp's and return 1 if they're the same +*-----------------------------------------------------------------*/ +static int pCodeOpCompare(pCodeOp *pcops, pCodeOp *pcopd) +{ + char b[50], *n2; + + if(!pcops || !pcopd) + return 0; + /* + fprintf(stderr," Comparing operands %s", + get_op( pcops,NULL,0)); + + fprintf(stderr," to %s\n", + get_op( pcopd,NULL,0)); + */ + + if(pcops->type != pcopd->type) { + //fprintf(stderr," - fail - diff types\n"); + return 0; // different types + } + + if(pcops->type == PO_LITERAL) { + + if((PCOL(pcops)->lit >= 0) && (PCOL(pcops)->lit == PCOL(pcopd)->lit)) + return 1; + + return 0; + } + + b[0]=0; + get_op(pcops,b,50); + + n2 = get_op(pcopd,NULL,0); + + if( !n2 || strcmp(b,n2)) { + //fprintf(stderr," - fail - diff names: %s, len=%d, %s, len=%d\n",b,strlen(b), n2, strlen(n2) ); + return 0; // different names + } + + switch(pcops->type) { + case PO_DIR: + if( PCOR(pcops)->instance != PCOR(pcopd)->instance) { + //fprintf(stderr, " - fail different instances\n"); + return 0; + } + break; + default: + break; + } + + //fprintf(stderr," - pass\n"); + + return 1; +} + +static int pCodePeepMatchLabels(pCodePeep *peepBlock, pCode *pcs, pCode *pcd) +{ + int labindex; + + /* Check for a label associated with this wild pCode */ + // If the wild card has a label, make sure the source code does too. + if(PCI(pcd)->label) { + pCode *pcl = PCI(pcd)->label->pc; + +#ifdef PCODE_DEBUG + int li = -PCL(pcl)->key; + + if(peepBlock->target.vars[li] == NULL) { + if(PCI(pcs)->label) { + DFPRINTF((stderr,"first time for a label: %d %s\n",li,PCL(PCI(pcs)->label->pc)->label)); + } + } else { + // DFPRINTF((stderr,"label id = %d \n",PCL(PCI(pcd)->label->pc)->key)); + DFPRINTF((stderr," label id: %d %s\n",li,peepBlock->target.vars[li])); + if(PCI(pcs)->label) { + DFPRINTF((stderr," src %s\n",PCL(PCI(pcs)->label->pc)->label)); + } + } +#endif + + + if(!PCI(pcs)->label) + return 0; + + labindex = -PCL(pcl)->key; + if(peepBlock->target.vars[labindex] == NULL) { + // First time to encounter this label + peepBlock->target.vars[labindex] = PCL(PCI(pcs)->label->pc)->label; + DFPRINTF((stderr,"first time for a label: %d %s\n",labindex,PCL(PCI(pcs)->label->pc)->label)); + + } else { + if(strcmp(peepBlock->target.vars[labindex],PCL(PCI(pcs)->label->pc)->label) != 0) { + DFPRINTF((stderr,"labels don't match dest %s != src %s\n",peepBlock->target.vars[labindex],PCL(PCI(pcs)->label->pc)->label)); + return 0; + } + DFPRINTF((stderr,"matched a label %d %s -hey\n",labindex,peepBlock->target.vars[labindex])); + } + } else { + //DFPRINTF((stderr,"destination doesn't have a label\n")); + + if(PCI(pcs)->label) + return 0; + + //DFPRINTF((stderr,"neither src nor dest have labels\n")); + + } + + return 1; + +} + +/*-----------------------------------------------------------------*/ +/* pCodePeepMatchLine - Compare source and destination pCodes to */ +/* see they're the same. */ +/* */ +/* In this context, "source" refers to the coded generated by gen.c*/ +/* and "destination" refers to a pcode in a peep rule. If the dest-*/ +/* ination has no wild cards, then MatchLine will compare the two */ +/* pcodes (src and dest) for a one-to-one match. If the destination*/ +/* has wildcards, then those get expanded. When a wild card is */ +/* encountered for the first time it autmatically is considered a */ +/* match and the object that matches it is referenced in the */ +/* variables or opcodes array (depending on the type of match). */ +/* */ +/* */ +/* Inputs: */ +/* *peepBlock - A pointer to the peepBlock that contains the */ +/* entire rule to which the destination pcode belongs*/ +/* *pcs - a pointer to the source pcode */ +/* *pcd - a pointer to the destination pcode */ +/* */ +/* Returns: */ +/* 1 - pcodes match */ +/* 0 - pcodes don't match */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ + +static int pCodePeepMatchLine(pCodePeep *peepBlock, pCode *pcs, pCode *pcd) +{ + int index; // index into wild card arrays + + /* one-for-one match. Here the source and destination opcodes + * are not wild. However, there may be a label or a wild operand */ + + if(pcs) { + if(PCI(pcs)->label) { + DFPRINTF((stderr,"Match line source label: %s\n",PCL(PCI(pcs)->label->pc)->label)); + } + } + + if(pcs->type == pcd->type) { + + if(pcs->type == PC_OPCODE) { + + /* If the opcodes don't match then the line doesn't match */ + if(PCI(pcs)->op != PCI(pcd)->op) + return 0; + +#ifdef PCODE_DEBUG + DFPRINTF((stderr,"%s comparing\n",__FUNCTION__)); + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); +#endif + + if(!pCodePeepMatchLabels(peepBlock, pcs, pcd)) + return 0; + + /* Compare the operands */ + if(PCI(pcd)->pcop) { + // Volatile types should not be deleted or modified, these include SFR, externs and publics + // They can be used as a matched, however if a match is found then the optimiser intends + // to change some aspect of a block of code, which is most likely a critcal one. As this + // method of optimisation does not allow a means to distiguishing what may change, it is + // best to just negate any match. + if (PCI(pcs)->pcop) { + struct reg_info *r; + pCodeOp *pcop = PCI(pcs)->pcop; + switch(pcop->type) { + case PO_W: + case PO_STATUS: + case PO_FSR: + case PO_INDF: + case PO_INTCON: + case PO_PCL: + case PO_PCLATH: + case PO_SFR_REGISTER: + return 0; // SFR - do not modify + case PO_DIR: + case PO_GPR_REGISTER: + case PO_GPR_BIT: + case PO_GPR_TEMP: + case PO_GPR_POINTER: + r = PCOR(pcop)->r; + if (r->isPublic||r->isExtern||r->isFixed) // Changes to these types of registers should not be changed as they may be used else where + return 0; + default: + break; + } + } + if (PCI(pcd)->pcop->type == PO_WILD) { + char *n; + index = PCOW(PCI(pcd)->pcop)->id; + //DFPRINTF((stderr,"destination is wild\n")); +#ifdef DEBUG_PCODEPEEP + if (index > peepBlock->nops) { + DFPRINTF((stderr,"%s - variables exceeded\n",__FUNCTION__)); + exit(1); + } +#endif + n = PCI(pcs)->pcop->name; + if(peepBlock->target.vars[index]) { + if ((!n)||(strcmp(peepBlock->target.vars[index],n) != 0)) + return 0; // variable is different + } else { + DFPRINTF((stderr,"first time for a variable: %d, %s\n",index,n)); + peepBlock->target.vars[index] = n; + } + + PCOW(PCI(pcd)->pcop)->matched = PCI(pcs)->pcop; + if(!peepBlock->target.wildpCodeOps[index]) { + peepBlock->target.wildpCodeOps[index] = PCI(pcs)->pcop; + + //fprintf(stderr, "first time for wild opcode #%d\n",index); + return 1; + + } else { + /* + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); + fprintf(stderr, "comparing operands of these instructions, result %d\n", + pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]) + ); + */ + + return pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]); + } + /* + { + char *n; + + switch(PCI(pcs)->pcop->type) { + case PO_GPR_TEMP: + case PO_FSR: + //case PO_INDF: + //n = PCOR(PCI(pcs)->pcop)->r->name; + n = PCI(pcs)->pcop->name; + + break; + default: + n = PCI(pcs)->pcop->name; + } + + if(peepBlock->target.vars[index]) + return (strcmp(peepBlock->target.vars[index],n) == 0); + else { + DFPRINTF((stderr,"first time for a variable: %d, %s\n",index,n)); + peepBlock->target.vars[index] = n; + return 1; + } + } + */ + } else if (PCI(pcd)->pcop->type == PO_LITERAL) { + /* + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); + + fprintf(stderr, "comparing literal operands of these instructions, result %d\n", + pCodeOpCompare(PCI(pcs)->pcop, PCI(pcd)->pcop)); + */ + return pCodeOpCompare(PCI(pcs)->pcop, PCI(pcd)->pcop); + + } else { + /* FIXME - need an else to check the case when the destination + * isn't a wild card */ + /* + fprintf(stderr, "Destination is not wild: operand compare =%d\n", + pCodeOpCompare(PCI(pcs)->pcop, PCI(pcd)->pcop)); + */ + return pCodeOpCompare(PCI(pcs)->pcop, PCI(pcd)->pcop); + + } + } else + /* The pcd has no operand. Lines match if pcs has no operand either*/ + return (PCI(pcs)->pcop == NULL); + } + } + + /* Compare a wild instruction to a regular one. */ + + if((pcd->type == PC_WILD) && (pcs->type == PC_OPCODE)) { + + index = PCW(pcd)->id; +#ifdef PCODE_DEBUG + DFPRINTF((stderr,"%s comparing wild cards\n",__FUNCTION__)); + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); +#endif + peepBlock->target.wildpCodes[PCW(pcd)->id] = pcs; + + if(!pCodePeepMatchLabels(peepBlock, pcs, pcd)) { + DFPRINTF((stderr," Failing because labels don't match\n")); + return 0; + } + + if(PCW(pcd)->mustBeBitSkipInst & !(PCI(pcs)->isBitInst && PCI(pcs)->isSkip)) { + // doesn't match because the wild pcode must be a bit skip + DFPRINTF((stderr," Failing match because bit skip is req\n")); + //pcd->print(stderr,pcd); + //pcs->print(stderr,pcs); + return 0; + } + + if(PCW(pcd)->mustNotBeBitSkipInst & (PCI(pcs)->isBitInst && PCI(pcs)->isSkip)) { + // doesn't match because the wild pcode must *not* be a bit skip + DFPRINTF((stderr," Failing match because shouldn't be bit skip\n")); + //pcd->print(stderr,pcd); + //pcs->print(stderr,pcs); + return 0; + } + + if(PCW(pcd)->operand) { + PCOW(PCI(pcd)->pcop)->matched = PCI(pcs)->pcop; + if(peepBlock->target.vars[index]) { + int i = (strcmp(peepBlock->target.vars[index],PCI(pcs)->pcop->name) == 0); +#ifdef PCODE_DEBUG + + if(i) + DFPRINTF((stderr," (matched)\n")); + else { + DFPRINTF((stderr," (no match: wild card operand mismatch\n")); + DFPRINTF((stderr," peepblock= %s, pcodeop= %s\n", + peepBlock->target.vars[index], + PCI(pcs)->pcop->name)); + } +#endif + return i; + } else { + DFPRINTF((stderr," (matched %s\n",PCI(pcs)->pcop->name)); + peepBlock->target.vars[index] = PCI(pcs)->pcop->name; + return 1; + } + } + + pcs = findNextInstruction(pcs->next); + if(pcs) { + //DFPRINTF((stderr," (next to match)\n")); + //pcs->print(stderr,pcs); + } else if(pcd->next) { + /* oops, we ran out of code, but there's more to the rule */ + return 0; + } + + return 1; /* wild card matches */ + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodePeepClrVars(pCodePeep *pcp) +{ + + int i; + if(!pcp) + return; + /* + DFPRINTF((stderr," Clearing peep rule vars\n")); + DFPRINTF((stderr," %d %d %d %d %d %d\n", + pcp->target.nvars,pcp->target.nops,pcp->target.nwildpCodes, + pcp->replace.nvars,pcp->replace.nops,pcp->replace.nwildpCodes)); + */ + for(i=0;itarget.nvars; i++) + pcp->target.vars[i] = NULL; + for(i=0;itarget.nops; i++) + pcp->target.wildpCodeOps[i] = NULL; + for(i=0;itarget.nwildpCodes; i++) + pcp->target.wildpCodes[i] = NULL; + + for(i=0;ireplace.nvars; i++) + pcp->replace.vars[i] = NULL; + for(i=0;ireplace.nops; i++) + pcp->replace.wildpCodeOps[i] = NULL; + for(i=0;ireplace.nwildpCodes; i++) + pcp->replace.wildpCodes[i] = NULL; + + + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +int pCodePeepMatchRule(pCode *pc) +{ + pCodePeep *peepBlock; + pCode *pct, *pcin; + pCodeCSource *pc_cline=NULL; + DLList *peeprules; + int matched; + + peeprules = (DLList *)peepSnippets; + + while(peeprules) { + peepBlock = ((pCodePeepSnippets*)peeprules)->peep; + + if(!peepBlock || /*!peepBlock->target ||*/ !peepBlock->target.pb->pcHead) { + fprintf(stderr, "skipping rule because target pb is NULL\n"); + goto next_rule; + } + + pCodePeepClrVars(peepBlock); + /* + pcin = pc; + if(IS_PCCOMMENT(pcin)) + pc = pcin = findNextInstruction(pcin->next); + */ + pcin = pc = findNextInstruction(pc); + + pct = peepBlock->target.pb->pcHead; +#ifdef PCODE_DEBUG + { + pCode *pcr = peepBlock->replace.pb->pcHead; + if(pcr) pct->print(stderr,pcr); + } +#endif + matched = 0; + while(pct && pcin) { + + if(! (matched = pCodePeepMatchLine(peepBlock, pcin,pct))) + break; + + pcin = findNextInstruction(pcin->next); + pct = pct->next; + //debug: + //DFPRINTF((stderr," matched\n")); + + if(!pcin && pct) { + DFPRINTF((stderr," partial match... no more code\n")); + matched = 0; + } + if(!pct) { + DFPRINTF((stderr," end of rule\n")); + } + } + + if(matched && pcin) { + + /* So far we matched the rule up to the point of the conditions . + * In other words, all of the opcodes match. Now we need to see + * if the post conditions are satisfied. + * First we check the 'postFalseCond'. This means that we check + * to see if any of the subsequent pCode's in the pCode chain + * following the point just past where we have matched depend on + * the `postFalseCond' as input then we abort the match + */ + DFPRINTF((stderr," matched rule so far, now checking conditions\n")); + //pcin->print(stderr,pcin); + + if (pcin && peepBlock->postFalseCond && + (pCodeSearchCondition(pcin,peepBlock->postFalseCond,0) > 0) ) + matched = 0; + + //fprintf(stderr," condition results = %d\n",pCodeSearchCondition(pcin,peepBlock->postFalseCond)); + + + //if(!matched) fprintf(stderr,"failed on conditions\n"); + } + + if(matched) { + + pCode *pcprev; + pCode *pcr, *pcout; + + + /* We matched a rule! Now we have to go through and remove the + inefficient code with the optimized version */ +#ifdef PCODE_DEBUG + DFPRINTF((stderr, "Found a pcode peep match:\nRule:\n")); + //printpCodeString(stderr,peepBlock->target.pb->pcHead,10); + DFPRINTF((stderr,"first thing matched\n")); + pc->print(stderr,pc); + if(pcin) { + DFPRINTF((stderr,"last thing matched\n")); + pcin->print(stderr,pcin); + } +#endif + + + /* Unlink the original code */ + pcout = pc; + pcprev = pc->prev; + pcprev->next = pcin; + if(pcin) + pcin->prev = pc->prev; + + +#if 0 + { + /* DEBUG */ + /* Converted the deleted pCodes into comments */ + + char buf[256]; + pCodeCSource *pc_cline2=NULL; + + buf[0] = ';'; + buf[1] = '#'; + + while(pc && pc!=pcin) { + + if(pc->type == PC_OPCODE && PCI(pc)->cline) { + if(pc_cline) { + pc_cline2->pc.next = PCODE(PCI(pc)->cline); + pc_cline2 = PCCS(pc_cline2->pc.next); + } else { + pc_cline = pc_cline2 = PCI(pc)->cline; + pc_cline->pc.seq = pc->seq; + } + } + + pCode2str(&buf[2], 254, pc); + pCodeInsertAfter(pcprev, newpCodeCharP(buf)); + pcprev = pcprev->next; + pc = pc->next; + + } + if(pc_cline2) + pc_cline2->pc.next = NULL; + } +#endif + + if(pcin) + pCodeDeleteChain(pc,pcin); + + /* Generate the replacement code */ + pc = pcprev; + pcr = peepBlock->replace.pb->pcHead; // This is the replacement code + while (pcr) { + pCodeOp *pcop=NULL; + + /* If the replace pcode is an instruction with an operand, */ + /* then duplicate the operand (and expand wild cards in the process). */ + if(pcr->type == PC_OPCODE) { + if(PCI(pcr)->pcop) { + /* The replacing instruction has an operand. + * Is it wild? */ + if(PCI(pcr)->pcop->type == PO_WILD) { + int index = PCOW(PCI(pcr)->pcop)->id; + //DFPRINTF((stderr,"copying wildopcode\n")); + if(peepBlock->target.wildpCodeOps[index]) + pcop = pCodeOpCopy(peepBlock->target.wildpCodeOps[index]); + else + DFPRINTF((stderr,"error, wildopcode in replace but not source?\n")); + } else + pcop = pCodeOpCopy(PCI(pcr)->pcop); + } + //DFPRINTF((stderr,"inserting pCode\n")); + pCodeInsertAfter(pc, newpCode(PCI(pcr)->op,pcop)); + } else if (pcr->type == PC_WILD) { + if(PCW(pcr)->invertBitSkipInst) + DFPRINTF((stderr,"We need to invert the bit skip instruction\n")); + pCodeInsertAfter(pc, + pCodeInstructionCopy(PCI(peepBlock->target.wildpCodes[PCW(pcr)->id]), + PCW(pcr)->invertBitSkipInst)); + } else if (pcr->type == PC_COMMENT) { + pCodeInsertAfter(pc, newpCodeCharP( ((pCodeComment *)(pcr))->comment)); + } + + pc = pc->next; +#ifdef PCODE_DEBUG + DFPRINTF((stderr," NEW Code:")); + if(pc) pc->print(stderr,pc); +#endif + pcr = pcr->next; + } + + /* We have just replaced the inefficient code with the rule. + * Now, we need to re-add the C-source symbols if there are any */ + pc = pcprev; + while(pc && pc_cline ) { + + pc = findNextInstruction(pc->next); + if (!pc) break; + PCI(pc)->cline = pc_cline; + pc_cline = PCCS(pc_cline->pc.next); + + } + + /* Copy C code comments to new code. */ + pc = pcprev->next; + if (pc) { + for (; pc && pcout!=pcin; pcout=pcout->next) { + if (pcout->type==PC_OPCODE && PCI(pcout)->cline) { + while (pc->type!=PC_OPCODE || PCI(pc)->cline) { + pc = pc->next; + if (!pc) + break; + } + if (!pc) break; + PCI(pc)->cline = PCI(pcout)->cline; + } + } + } + + return 1; + } +next_rule: + peeprules = peeprules->next; + } + DFPRINTF((stderr," no rule matched\n")); + + return 0; +} diff --git a/src/pic14/pcoderegs.c b/src/pic14/pcoderegs.c new file mode 100644 index 0000000..84d9335 --- /dev/null +++ b/src/pic14/pcoderegs.c @@ -0,0 +1,802 @@ +/*------------------------------------------------------------------------- + + pcoderegs.c - post code generation register optimizations + + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +/* +pcoderegs.c + + The purpose of the code in this file is to optimize the register usage. + +*/ + +#include "main.h" +#include "pcoderegs.h" +#include "pcodeflow.h" +#include "ralloc.h" + + +static int total_registers_saved=0; +static int register_optimization=1; + +/*-----------------------------------------------------------------* +* void pCodeRegMapLiveRangesInFlow(pCodeFlow *pcfl) +*-----------------------------------------------------------------*/ +static void pCodeRegMapLiveRangesInFlow(pCodeFlow *pcfl) +{ + + pCode *pc=NULL; + + reg_info *reg; + + if(!pcfl) + return; + + + pc = findNextInstruction(pcfl->pc.next); + + while(pc && !isPCFL(pc)) { + while (pc && !isPCI(pc) && !isPCFL(pc)) + { + pc = pc->next; + } // while + if (!pc || isPCFL(pc)) continue; + assert( isPCI(pc) ); + + reg = getRegFromInstruction(pc); + #if 0 + pc->print(stderr, pc); + fprintf( stderr, "--> reg %p (%s,%u), inCond/outCond: %x/%x\n", + reg, reg ? reg->name : "(null)", reg ? reg->rIdx : -1, + PCI(pc)->inCond, PCI(pc)->outCond ); + #endif + if(reg) { + /* + fprintf(stderr, "flow seq %d, inst seq %d %s ",PCODE(pcfl)->seq,pc->seq,reg->name); + fprintf(stderr, "addr = 0x%03x, type = %d rIdx=0x%03x\n", + reg->address,reg->type,reg->rIdx); + */ + + addSetIfnotP(& (PCFL(pcfl)->registers), reg); + + if(PCC_REGISTER & PCI(pc)->inCond) + addSetIfnotP(& (reg->reglives.usedpFlows), pcfl); + + if(PCC_REGISTER & PCI(pc)->outCond) + addSetIfnotP(& (reg->reglives.assignedpFlows), pcfl); + + addSetIfnotP(& (reg->reglives.usedpCodes), pc); + reg->wasUsed = TRUE; + } + + + //pc = findNextInstruction(pc->next); + pc = pc->next; + + } + +} + +/*-----------------------------------------------------------------* +* void pCodeRegMapLiveRanges(pBlock *pb) +*-----------------------------------------------------------------*/ +void pCodeRegMapLiveRanges(pBlock *pb) +{ + pCode *pcflow; + + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) { + fprintf(stderr, "pCodeRegMapLiveRanges - pcflow is not a flow object "); + continue; + } + pCodeRegMapLiveRangesInFlow(PCFL(pcflow)); + } + +#if 0 + for( pcflow = findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = findNextpCode(pcflow->next, PC_FLOW) ) { + + regs *r = setFirstItem(PCFL(pcflow)->registers); + fprintf(stderr,"flow seq %d\n", pcflow->seq); + + while (r) { + fprintf(stderr, " %s\n",r->name); + r = setNextItem(PCFL(pcflow)->registers); + + } + + } +#endif + +} + + +/*-----------------------------------------------------------------* +* +*-----------------------------------------------------------------*/ +static void Remove1pcode(pCode *pc, reg_info *reg, int debug_code) +{ + + pCode *pcn=NULL; + + if(!reg || !pc) + return; + + deleteSetItem (&(reg->reglives.usedpCodes),pc); + + if(PCI(pc)->label) { + pcn = findNextInstruction(pc->next); + + if(pcn) + PCI(pcn)->label = pBranchAppend(PCI(pcn)->label,PCI(pc)->label); + } + + if(PCI(pc)->cline) { + if(!pcn) + pcn = findNextInstruction(pc->next); + + if(pcn) { + if(PCI(pcn)->cline) { + //fprintf(stderr, "source line has been optimized completely out\n"); + //pc->print(stderr,pc); + } else { + PCI(pcn)->cline = PCI(pc)->cline; + } + } + } + + + if(1) { + /* + * Debug stuff. Comment out the instruction we're about to delete. + */ + + char buff1[256]; + size_t size = 256; + + char *pbuff; + pbuff = &buff1[0]; + + SNPRINTF(pbuff, size, ";%d", debug_code); + size -= strlen(pbuff); + pbuff += strlen(pbuff); + pCode2str(pbuff, size, pc); + pCodeInsertBefore(pc, newpCodeCharP(buff1)); + //fprintf(stderr,"removing instruction:\n%s\n",buff1); + } + + pc->destruct(pc); + +} + +/*-----------------------------------------------------------------* +* void RemoveRegsFromSet(set *regset) +* +*-----------------------------------------------------------------*/ +static void RemoveRegsFromSet(set *regset) +{ + reg_info *reg; + int used; + + while(regset) { + reg = regset->item; + regset = regset->next; + + used = elementsInSet(reg->reglives.usedpCodes); + + if(used <= 1) { + + //fprintf(stderr," reg %s isfree=%d, wasused=%d\n",reg->name,reg->isFree,reg->wasUsed); + if(used == 0) { + //fprintf(stderr," getting rid of reg %s\n",reg->name); + reg->isFree = TRUE; + reg->wasUsed = FALSE; + } else { + pCode *pc; + + + pc = setFirstItem(reg->reglives.usedpCodes); + + if(reg->type == REG_SFR || reg->type == REG_STK || reg->isPublic || reg->isExtern) { + //fprintf(stderr, "not removing SFR reg %s even though used only once\n",reg->name); + continue; + } + + + if(isPCI(pc)) { + if(PCI(pc)->label) { + pCode *pcn = findNextInstruction(pc->next); + + if(pcn && PCI(pcn)->label) { + //fprintf(stderr,"can't delete instruction with label...\n"); + //pc->print(stderr,pc); + continue; + } + /* Move the label to the next instruction */ + + PCI(pcn)->label = PCI(pc)->label; + + } + + if(isPCI_SKIP(pc)) { + reg_info *r = getRegFromInstruction(pc); + fprintf(stderr, "WARNING, a skip instruction is being optimized out\n"); + pc->print(stderr,pc); + fprintf(stderr,"reg %s, type =%d\n",r->name, r->type); + } + //fprintf(stderr," removing reg %s because it is used only once\n",reg->name); + Remove1pcode(pc, reg, 1); + /* + unlinkpCode(pc); + deleteSetItem (&(reg->reglives.usedpCodes),pc); + */ + reg->isFree = TRUE; + reg->wasUsed = FALSE; + total_registers_saved++; // debugging stats. + } + } + } + + } +} + +static void pic14_ReMapLiveRanges(void) +{ + pBlock *pb; + if (!the_pFile) return; + RegsUnMapLiveRanges(); + for (pb = the_pFile->pbHead; pb; pb = pb->next) + { + #if 0 + pCode *pc = findNextpCode(pb->pcHead, PC_FLOW); + if (pc) { + pc->print( stderr, pc ); + } else { + fprintf( stderr, "unnamed pBlock\n"); + } + pc = findNextInstruction(pb->pcHead); + while (pc) { + pc->print( stderr, pc ); + pc = findNextInstruction(pc->next);; + } + #endif + pCodeRegMapLiveRanges(pb); + } // for +} + +/*-----------------------------------------------------------------* +* void RemoveUnusedRegisters(void) +* +*-----------------------------------------------------------------*/ +void RemoveUnusedRegisters(void) +{ + /* First, get rid of registers that are used only one time */ + pic14_ReMapLiveRanges(); + + //RemoveRegsFromSet(dynInternalRegs); + RemoveRegsFromSet(dynAllocRegs); + RemoveRegsFromSet(dynStackRegs); + /* + don't do DirectRegs yet - there's a problem with arrays + RemoveRegsFromSet(dynDirectRegs); + */ + RemoveRegsFromSet(dynDirectBitRegs); + + if(total_registers_saved) DFPRINTF((stderr, " *** Saved %d registers ***\n", total_registers_saved)); +} + + +/*-----------------------------------------------------------------* +* +*-----------------------------------------------------------------*/ +static void Remove2pcodes(pCode *pcflow, pCode *pc1, pCode *pc2, reg_info *reg, int can_free) +{ + static int debug_code=99; + if(!reg) + return; +#if 0 + fprintf (stderr, "%s:%d(%s): %d (reg:%s)\n", __FILE__, __LINE__, __FUNCTION__, debug_code, reg ? reg->name : "???"); + printpCode (stderr, pc1); + printpCode (stderr, pc2); +#endif + + //fprintf(stderr,"%s\n",__FUNCTION__); + if(pc1) + Remove1pcode(pc1, reg, debug_code++); + + if(pc2) { + Remove1pcode(pc2, reg, debug_code++); + deleteSetItem (&(PCFL(pcflow)->registers), reg); + + if(can_free) { + reg->isFree = TRUE; + reg->wasUsed = FALSE; + } + + } + + pCodeRegMapLiveRangesInFlow(PCFL(pcflow)); +} + +/*-----------------------------------------------------------------* +* +*-----------------------------------------------------------------*/ +static int regUsedinRange(pCode *pc1, pCode *pc2, reg_info *reg) +{ + int i=0; + reg_info *testreg; + + do { + testreg = getRegFromInstruction(pc1); + if(testreg && (testreg->rIdx == reg->rIdx)) { + return 1; + } + if (i++ > 1000) { + fprintf(stderr, "warning, regUsedinRange searched through too many pcodes\n"); + return 0; + } + + pc1 = findNextInstruction(pc1->next); + + } while (pc1 && (pc1 != pc2)) ; + + return 0; +} + +static int regIsSpecial (reg_info *reg, int mayBeGlobal) +{ + if (!reg) return 0; + + if (reg->type == REG_SFR || reg->type == REG_STK || (!mayBeGlobal && (reg->isPublic || reg->isExtern))) return 1; + + return 0; +} + +/*-----------------------------------------------------------------* +* void pCodeOptime2pCodes(pCode *pc1, pCode *pc2) +* +* ADHOC pattern checking +* Now look for specific sequences that are easy to optimize. +* Many of these sequences are characteristic of the compiler +* (i.e. it'd probably be a waste of time to apply these adhoc +* checks to hand written assembly.) +* +* +*-----------------------------------------------------------------*/ +static int pCodeOptime2pCodes(pCode *pc1, pCode *pc2, pCode *pcfl_used, reg_info *reg, int can_free, int optimize_level) +{ + pCode *pct1, *pct2; + reg_info *reg1, *reg2; + + int t = total_registers_saved; + + if (!isPCI(pc1) || !isPCI(pc2)) return 0; + if (PCI(pc1)->pcflow != PCI(pc2)->pcflow) return 0; + + if (pc2->seq < pc1->seq) { + pct1 = pc2; + pc2 = pc1; + pc1 = pct1; + } + + /* disable this optimization for now -- it's buggy */ + if (pic14_options.disable_df) return 0; + + //fprintf(stderr,"pCodeOptime2pCodes\n"); + //pc1->print(stderr,pc1); + //pc2->print(stderr,pc2); + + if((PCI(pc1)->op == POC_CLRF) && (PCI(pc2)->op == POC_MOVFW) ){ + /* + * CLRF sets Z + * MOVFW affects Z + * MOVWF does not touch Z + * MOVLW does not touch Z + */ + pCode *newpc; + /* + clrf reg ; pc1 + stuff... + movf reg,w ; pc2 + + can be replaced with (only if following instructions are not going to use W and reg is not used again later) + + stuff... + movlw 0 or clrf reg + */ + DFPRINTF((stderr, " optimising CLRF reg ... MOVF reg,W to ... MOVLW 0\n")); + pct2 = findNextInstruction(pc2->next); + if (pCodeSearchCondition(pct2, PCC_Z, 0) == -1) { + /* Z is definitely overwritten before use */ + newpc = newpCode(POC_MOVLW, newpCodeOpLit(0)); + + pCodeInsertAfter(pc2, newpc); + PCI(newpc)->pcflow = PCFL(pcfl_used); + newpc->seq = pc2->seq; + + //fprintf (stderr, "%s:%d(%s): Remove2pcodes (CLRF reg, ..., MOVF reg,W)\n", __FILE__, __LINE__, __FUNCTION__); + //Remove2pcodes(pcfl_used, pc2, NULL, reg, 0); + pc2->destruct(pc2); + //total_registers_saved++; // debugging stats. + } + } else if((PCI(pc1)->op == POC_CLRF) && (PCI(pc2)->op == POC_IORFW) ){ + DFPRINTF((stderr, " optimising CLRF/IORFW\n")); + + pct2 = findNextInstruction(pc2->next); + + /* We must ensure that Z is destroyed before being read---IORLW must be performed unless this is proven. */ + if (pCodeSearchCondition(pct2, PCC_Z, 0) != -1) { + pct2 = newpCode(POC_IORLW, newpCodeOpLit(0)); + pct2->seq = pc2->seq; + PCI(pct2)->pcflow = PCFL(pcfl_used); + pCodeInsertAfter(pc1,pct2); + } + //fprintf (stderr, "%s:%d(%s): Remove2pcodes (CLRF/IORFW)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + total_registers_saved++; // debugging stats. + + } else if(PCI(pc1)->op == POC_MOVWF) { + // Optimising MOVWF reg ... + + pct2 = findNextInstruction(pc2->next); + + if(PCI(pc2)->op == POC_MOVFW) { + // Optimising MOVWF reg ... MOVF reg,W + + if(PCI(pct2)->op == POC_MOVWF) { + /* + Change: + + movwf reg ; pc1 + stuff... + movf reg,w ; pc2 + movwf reg2 ; pct2 + + To: ( as long as 'stuff' does not use reg2 or if following instructions do not use W or reg is not used later) + + movwf reg2 + stuff... + + */ + reg2 = getRegFromInstruction(pct2); + /* Check reg2 is not used for something else before it is loaded with reg */ + if (reg2 && !regIsSpecial (reg2, 1) && !regUsedinRange(pc1,pc2,reg2)) { + pCode *pct3 = findNextInstruction(pct2->next); + /* Check following instructions are not relying on the use of W or the Z flag condiction */ + /* XXX: We must ensure that this value is destroyed before use---otherwise it might be used in + * subsequent flows (checking for < 1 is insufficient). */ + if ((pCodeSearchCondition(pct3,PCC_Z,0) == -1) && (pCodeSearchCondition(pct3,PCC_W,0) == -1)) { + DFPRINTF((stderr, " optimising MOVF reg ... MOVF reg,W MOVWF reg2 to MOVWF reg2 ...\n")); + pct2->seq = pc1->seq; + unlinkpCode(pct2); + pCodeInsertBefore(pc1,pct2); + if(regUsedinRange(pct2,0,reg)) + { + //fprintf (stderr, "%s:%d(%s): Remove2pcodes IF (MOVWF reg, ..., MOVW reg,W MOVWF reg2)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pc2, NULL, reg, can_free); + } else { + //fprintf (stderr, "%s:%d(%s): Remove2pcodes ELSE (MOVWF reg, ..., MOVW reg,W MOVWF reg2)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + } + total_registers_saved++; // debugging stats. + return 1; + } + } + } + } + + pct1 = findPrevInstruction(pc1->prev); + if(pct1 && (PCI(pct1)->pcflow == PCI(pc1)->pcflow)) { + + if ( (PCI(pct1)->op == POC_MOVFW) && + (PCI(pc2)->op == POC_MOVFW)) { + + reg1 = getRegFromInstruction(pct1); + if(reg1 && !regIsSpecial (reg1, 0) && !regUsedinRange(pc1,pc2,reg1)) { + DFPRINTF((stderr, " optimising MOVF reg1,W MOVWF reg ... MOVF reg,W\n")); + /* + Change: + + movf reg1,w + movwf reg + + stuff... + movf reg,w + + To: + + stuff... + + movf reg1,w + + Or, if we're not deleting the register then the "To" is: + + stuff... + + movf reg1,w + movwf reg + */ + pct2 = newpCode(PCI(pc2)->op, PCI(pct1)->pcop); + pCodeInsertAfter(pc2, pct2); + PCI(pct2)->pcflow = PCFL(pcfl_used); + pct2->seq = pc2->seq; + + if(can_free) { + //fprintf (stderr, "%s:%d(%s): Remove2pcodes CANFREE (MOVF reg1,W; MOVWF reg2; MOVF reg2,W)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + } else { + /* If we're not freeing the register then that means (probably) + * the register is needed somewhere else.*/ + unlinkpCode(pc1); + pCodeInsertAfter(pct2, pc1); + + //fprintf (stderr, "%s:%d(%s): Remove2pcodes ELSE (MOVF reg1,W; MOVWF reg2; MOVF reg2,W)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pc2, NULL, reg, can_free); + } + + //fprintf (stderr, "%s:%d(%s): Remove2pcodes ALWAYS (MOVF reg1,W; MOVWF reg2; MOVF reg2,W)\n", __FILE__, __LINE__, __FUNCTION__); + Remove2pcodes(pcfl_used, pct1, NULL, reg1, 0); + total_registers_saved++; // debugging stats. + + } + } + } + } + + return (total_registers_saved != t); +} + +/*-----------------------------------------------------------------* +* void pCodeRegOptimeRegUsage(pBlock *pb) +*-----------------------------------------------------------------*/ +static void OptimizeRegUsage(set *fregs, int optimize_multi_uses, int optimize_level) +{ + reg_info *reg; + int used; + pCode *pc1=NULL, *pc2=NULL; + + + while(fregs) { + pCode *pcfl_used, *pcfl_assigned; + + /* Step through the set by directly accessing the 'next' pointer. + * We could also step through by using the set API, but the + * the (debug) calls to print instructions affect the state + * of the set pointers */ + + reg = fregs->item; + fregs = fregs->next; + /* + if (strcmp(reg->name,"_SubState")==0) + fprintf(stderr,"Reg: %s\n",reg->name); + */ + + /* Catch inconsistently set-up live ranges + * (see tracker items #1469504 + #1474602) + * FIXME: Actually we should rather fix the + * computation of the liveranges instead... + */ + if (!reg || !reg->reglives.usedpFlows + || !reg->reglives.assignedpFlows) + { + //fprintf( stderr, "skipping reg w/o liveranges: %s\n", reg ? reg->name : "(unknown)"); + continue; + } + + if(reg->type == REG_SFR || reg->type == REG_STK || reg->isPublic || reg->isExtern|| reg->isFixed) { + //fprintf(stderr,"skipping SFR: %s\n",reg->name); + continue; + } + + pcfl_used = setFirstItem(reg->reglives.usedpFlows); + pcfl_assigned = setFirstItem(reg->reglives.assignedpFlows); + + used = elementsInSet(reg->reglives.usedpCodes); + if(used == 2) { + /* + In this section, all registers that are used in only in two + instructions are examined. If possible, they're optimized out. + */ + + /* + fprintf (stderr, "OptimizeRegUsage: %s addr=0x%03x rIdx=0x%03x type=%d used=%d\n", + reg->name, + reg->address, + reg->rIdx, reg->type, used); + */ + pc1 = setFirstItem(reg->reglives.usedpCodes); + pc2 = setNextItem(reg->reglives.usedpCodes); + + if(pcfl_used && pcfl_assigned) { + /* + expected case - the register has been assigned a value and is + subsequently used + */ + + //fprintf(stderr," used only twice\n"); + if(pcfl_used->seq == pcfl_assigned->seq) { + + //fprintf(stderr, " and used in same flow\n"); + + pCodeOptime2pCodes(pc1, pc2, pcfl_used, reg, 1,optimize_level); + + } else { + // fprintf(stderr, " and used in different flows\n"); + + } + + } else if(pcfl_used) { + + /* register has been used twice without ever being assigned */ + fprintf(stderr,"WARNING %s: reg %s used without being assigned\n",__FUNCTION__,reg->name); + + } else { + //fprintf(stderr,"WARNING %s.1: reg %s assigned without being used\n",__FUNCTION__,reg->name); + Remove2pcodes(pcfl_assigned, pc1, pc2, reg, 1); + total_registers_saved++; // debugging stats. + } + } else { + + /* register has been used either once, or more than twice */ + + if(used && !pcfl_used && pcfl_assigned) { + pCode *pc; + + //fprintf(stderr,"WARNING %s.2: reg %s assigned without being used\n",__FUNCTION__,reg->name); + + pc = setFirstItem(reg->reglives.usedpCodes); + while(pc) { + + pcfl_assigned = PCODE(PCI(pc)->pcflow); + Remove1pcode(pc, reg,2); + + deleteSetItem (&(PCFL(pcfl_assigned)->registers), reg); + /* + deleteSetItem (&(reg->reglives.usedpCodes),pc); + pc->destruct(pc); + */ + pc = setNextItem(reg->reglives.usedpCodes); + } + + + reg->isFree = TRUE; + reg->wasUsed = FALSE; + + total_registers_saved++; // debugging stats. + } else if( (used > 2) && optimize_multi_uses) { + + set *rset1=NULL; + set *rset2=NULL; + int searching=1; + + pCodeFlow *pcfl1=NULL, *pcfl2=NULL; + + /* examine the number of times this register is used */ + + + rset1 = reg->reglives.usedpCodes; + while(rset1 && searching) { + + pc1 = rset1->item; + rset2 = rset1->next; + + if(pc1 && isPCI(pc1) && ( (pcfl1 = PCI(pc1)->pcflow) != NULL) ) { + + if(rset2) { + + pc2 = rset2->item; + if(pc2 && isPCI(pc2) && ( (pcfl2 = PCI(pc2)->pcflow) != NULL) ) { + if(pcfl2 == pcfl1) { + + if(pCodeOptime2pCodes(pc1, pc2, pcfl_used, reg, 0,optimize_level)) + searching = 0; + } + } + + //rset2 = rset2->next; + + } + } + rset1 = rset2; + } + } + } + } +} + +/*-----------------------------------------------------------------* +* void pCodeRegOptimeRegUsage(pBlock *pb) +*-----------------------------------------------------------------*/ +void pCodeRegOptimizeRegUsage(int level) +{ + + int passes; + int saved = 0; + int t = total_registers_saved; + +#if 0 + /* This is currently broken (need rewrite to correctly + * handle arbitrary pCodeOps instead of registers only). */ + if (!pic14_options.disable_df) + optimizeDataflow (); +#endif + + if(!register_optimization) + return; +#define OPT_PASSES 4 + passes = OPT_PASSES; + + do { + saved = total_registers_saved; + + /* Identify registers used in one flow sequence */ + OptimizeRegUsage(dynAllocRegs,level, (OPT_PASSES-passes)); + OptimizeRegUsage(dynStackRegs,level, (OPT_PASSES-passes)); + OptimizeRegUsage(dynDirectRegs,0, (OPT_PASSES-passes)); + + if(total_registers_saved != saved) + DFPRINTF((stderr, " *** pass %d, Saved %d registers, total saved %d ***\n", + (1+OPT_PASSES-passes),total_registers_saved-saved,total_registers_saved)); + + passes--; + + } while( passes && ((total_registers_saved != saved) || (passes==OPT_PASSES-1)) ); + + if(total_registers_saved == t) + DFPRINTF((stderr, "No registers saved on this pass\n")); +} + + +/*-----------------------------------------------------------------* +* void RegsUnMapLiveRanges(set *regset) +* +*-----------------------------------------------------------------*/ +static void RegsSetUnMapLiveRanges(set *regset) +{ + reg_info *reg; + + while(regset) { + reg = regset->item; + regset = regset->next; + + + deleteSet(®->reglives.usedpCodes); + deleteSet(®->reglives.usedpFlows); + deleteSet(®->reglives.assignedpFlows); + + } + +} + +void RegsUnMapLiveRanges(void) +{ + + RegsSetUnMapLiveRanges(dynAllocRegs); + RegsSetUnMapLiveRanges(dynStackRegs); + RegsSetUnMapLiveRanges(dynDirectRegs); + RegsSetUnMapLiveRanges(dynProcessorRegs); + RegsSetUnMapLiveRanges(dynDirectBitRegs); + RegsSetUnMapLiveRanges(dynInternalRegs); + +} diff --git a/src/pic14/pcoderegs.h b/src/pic14/pcoderegs.h new file mode 100644 index 0000000..fb362ea --- /dev/null +++ b/src/pic14/pcoderegs.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + + pcoderegs.h - post code generation register optimizations + + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +#ifndef __PCODEREGS_H__ +#define __PCODEREGS_H__ + +#include "common.h" + +#include "pcode.h" + +/************************************************* + + pCodeRegLives + + Records the set of registers used in a flow object. + +**************************************************/ + +typedef struct pCodeRegLives { + set *usedpFlows; /* set of pFlow objects that use this register */ + set *assignedpFlows; /* set of pFlow objects that assign values to this register */ + set *usedpCodes; /* set of all instructions that use this register */ + +} pCodeRegLives; + +void pCodeRegMapLiveRanges(struct pBlock *pb); +void pCodeRegOptimizeRegUsage(int level); +void RegsUnMapLiveRanges(void); +void RemoveUnusedRegisters(void); + +#endif // __PCODEREGS_H__ diff --git a/src/pic14/peeph.def b/src/pic14/peeph.def new file mode 100644 index 0000000..b82d3a0 --- /dev/null +++ b/src/pic14/peeph.def @@ -0,0 +1,323 @@ +// PIC Port Peep rules +// +// +// INTRODUCTION: +// +// The peep hole optimizer searchs the +// the SDCC generated code for small snippets +// that can be optimized. As a user, you have +// control over this optimization process without +// having to learn the SDCC source code. (However +// you'll still need access to the source since +// these rules are compiled into the source.) +// +// The way it works is you specify the target +// snippet that you want replaced with a more +// efficient snippet that you write. Wild card +// variables allow the rules to be parameterized. +// +// In all of the SDCC ports, labels and operands +// can be wild cards. However, in the PIC even the +// instructions can be wild cards. +// +// EXAMPLE: +// +// Consider Peep Rule 1 as an example. This rule +// replaces some code like: +// +// skpz ;i.e. btfss status,Z +// goto lab1 +// clrw +//lab1: +// +// with: +// +// skpnz ;i.e. btfsc status,Z +// clrw +//lab1 +// +// However, the Rule has four wild cards. +// The first allows the btfss instruction operator +// be anything, not just the Z bit in status register. +// The second wild card applies to a label. +// The third wild card is for an instruction - any +// single instruction can be substituted. +// The fourth wild card is also an instruction. It's +// just an instruction place holder associated with +// a label (think of it as the PIC Port author's laziness +// imposed upon the user). +// +// +// CONDITIONS +// +// There are certain instances where a peep rule may not +// be applicable. Consider this subtle example: +// +// movwf R0 +// movf R0,W +// +// It would seem that the second move is unnecessary. But +// be careful! The movf instruction affects the 'Z' bit. +// So if this sequence is followed by a btfsc status,Z, you +// will have to leave the second move in. +// +// To get around this proble, the peep rule can be followed +// by a conditon: "if NZ". Which is to say, apply the rule +// if Z bit is not needed in the code that follows. The optimizer +// is smart enough to look more than one instruction past the +// target block... +// +// Special commands +// +// +// _NOTBITSKIP_ %1 - Creates a wild card instruction that +// will match all instructions except for +// bit skip instructions (btfsc or btfss) +// _BITSKIP_ %1 - Creates a wild card instruction that only +// will match a bit skip instruction (btfsc +// or btfss) +// _INVERTBITSKIP_ %1 - For the wild card instruction %1, invert +// the state of the bit skip. If %1 is not +// a bit skip instruction, then there's an +// error in the peep rule. +// +// +// + + +// Peep 1 +// Converts +// +// btfss reg1,bit +// goto label +// incf reg2,f +//label +// +// Into: +// +// btfsc reg1,bit +// incf reg2,f +//label +// +// Notice that wild cards will allow any instruction +// besides incf to be used in the above. +// +// Also, notice that this snippet is not valid if +// it follows another skip + +replace restart { + _NOTBITSKIP_ %1 + _BITSKIP_ %2 + goto %3 + %4 +%3: %5 +} by { + ; peep 1 - test/jump to test/skip + %1 + _INVERTBITSKIP_ %2 + %4 +%3: %5 +} + +replace restart { + _NOTBITSKIP_ %1 + _BITSKIP_ %2 + goto %3 +%4: %5 +%3: %6 +} by { + ; peep 1b - test/jump to test/skip + %1 + _INVERTBITSKIP_ %2 +%4: %5 +%3: %6 +} + + +//bogus test for pcode +//replace restart { +// movf %1,w ;comment at end +//%4: movf %1,w +// RETURN +// clrf INDF +// movlw 0xa5 +// movf fsr,w +// incf indf,f +// %2 +//} by { +// ; peep test remove redundant move +//%4: movf %1,w ;another comment +// %2 +//} if AYBABTU %3 + + +// peep 2 +replace restart { + movwf %1 + movf %1,w +} by { + ; peep 2 - Removed redundant move + movwf %1 +} if NZ + +// peep 3 +replace restart { + decf %1,f + movf %1,w + btfss STATUS,z + goto %2 +} by { + ; peep 3 - decf/mov/skpz to decfsz + decfsz %1,f + goto %2 +} + + +replace restart { + movf %1,w + movf %1,w +} by { + ; peep 4 - Removed redundant move + movf %1,w +} + + +replace restart { + movlw %1 + movwf %2 + movlw %1 +} by { + ; peep 5 - Removed redundant move + movlw %1 + movwf %2 +} + +replace restart { + movwf %1 + movwf %1 +} by { + ; peep 6 - Removed redundant move + movwf %1 +} + +replace restart { + movlw 0 + iorwf %1,w +} by { + ; peep 7 - Removed redundant move + movf %1,w +} + +replace restart { + movf %1,w + movwf %2 + decf %2,f +} by { + ; peep 8 - Removed redundant move + decf %1,w + movwf %2 +} + +replace restart { + movwf %1 + movf %2,w + xorwf %1,w +} by { + ; peep 9a - Removed redundant move + movwf %1 + xorwf %2,w +} + +replace restart { + movwf %1 + movf %2,w + iorwf %1,w +} by { + ; peep 9b - Removed redundant move + movwf %1 + iorwf %2,w +} + +replace restart { + movf %1,w + movwf %2 + movf %2,w +} by { + ; peep 9c - Removed redundant move + movf %1,w + movwf %2 +} + +replace restart { + movwf %1 + movf %1,w + movwf %2 +} by { + ; peep 9d - Removed redundant move + movwf %1 + movwf %2 +} if NZ + +// From: Frieder Ferlemann + +replace restart { + iorlw 0 +} by { + ; peep 10a - Removed unnecessary iorlw +} if NZ + +// From: Frieder Ferlemann + +replace restart { + xorlw 0 +} by { + ; peep 10b - Removed unnecessary xorlw +} if NZ + +// From: Frieder Ferlemann + +replace restart { + movf %1,w + movwf %1 +} by { + ; peep 11 - Removed redundant move + movf %1,w +} + +replace restart { + clrf %1 + rlf %1,f + movlw 0x01 + xorwf %1,f + movf %1,w + btfss STATUS,2 + goto %2 + +} by { + ; peep 13 - Optimized carry sequence + clrf %1 + incf %1,F + btfss status,C + goto %2 + clrf %1 + +} + +replace restart { + clrf %1 + rlf %1,f + movlw 0x01 + xorwf %1,f + movf %1,w + btfsc STATUS,2 + goto %2 + +} by { + ; peep 13a - Optimized carry sequence + clrf %1 + incf %1,F + btfsc status,C + goto %2 + clrf %1 + +} diff --git a/src/pic14/pic14.vcxproj b/src/pic14/pic14.vcxproj new file mode 100644 index 0000000..20df2a0 --- /dev/null +++ b/src/pic14/pic14.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B96E942E-39F5-4C7C-97FD-A095DE6847C6} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pic14.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/pic14.bsc + + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/pic14.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level2 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/pic14.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + + \ No newline at end of file diff --git a/src/pic14/pic14.vcxproj.filters b/src/pic14/pic14.vcxproj.filters new file mode 100644 index 0000000..4441929 --- /dev/null +++ b/src/pic14/pic14.vcxproj.filters @@ -0,0 +1,79 @@ + + + + + {b6bfe4fa-6c81-435a-846b-011a5bd2df84} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {b03a3465-38b0-4e30-b02e-4a95779a54c5} + h;hpp;hxx;hm;inl + + + {503c9bb6-fa0c-4277-a55f-a0cd889032d1} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/pic14/ralloc.c b/src/pic14/ralloc.c new file mode 100644 index 0000000..19562df --- /dev/null +++ b/src/pic14/ralloc.c @@ -0,0 +1,3874 @@ +/*------------------------------------------------------------------------ + + SDCCralloc.c - source file for register allocation. (8051) specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + Added Pic Port T.scott Dattalo scott@dattalo.com (2000) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "device.h" +#include "gen.h" +#include "ralloc.h" + + +set *dynAllocRegs=NULL; +set *dynStackRegs=NULL; +set *dynProcessorRegs=NULL; +set *dynDirectRegs=NULL; +set *dynDirectBitRegs=NULL; +set *dynInternalRegs=NULL; + + +#ifdef DEBUG_FENTRY2 +# define FENTRY2 printf +#else +# define FENTRY2 1 ? (void)0 : (*(void (*)(const char *, ...))0) +#endif + +/* this should go in SDCCicode.h, but it doesn't. */ +#define IS_REF(op) (IS_SYMOP(op) && op->svt.symOperand->isref == 1) + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +/* Global data */ +static struct +{ + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; +} +_G; + +static int pic14_ptrRegReq; /* one byte pointer register required */ + +static hTab *dynDirectRegNames= NULL; +// static hTab *regHash = NULL; /* a hash table containing ALL registers */ + +static int dynrIdx = 0x1000; + +int Gstack_base_addr=0; /* The starting address of registers that + * are used to pass and return parameters */ +static int Gstack_size = 0; + +static int debug = 0; // should be 0 when committed, creates .d files +static FILE *debugF = NULL; + +/*-----------------------------------------------------------------*/ +/* debugLog - open a file for debugging information */ +/*-----------------------------------------------------------------*/ +static void +debugLog (const char *fmt,...) +{ + static int append = 0; // First time through, open the file without append. + + char buffer[256]; + //char *bufferP=buffer; + va_list ap; + + if (!debug || !dstFileName) + return; + + if (!debugF) + { + /* create the file name */ + SNPRINTF(buffer, sizeof(buffer), "%s.d", dstFileName); + + if (!(debugF = fopen (buffer, (append ? "a+" : "w")))) + { + werror (E_FILE_OPEN_ERR, buffer); + exit (1); + } + + append = 1; // Next time debugLog is called, we'll append the debug info + } + + va_start (ap, fmt); + vsprintf (buffer, fmt, ap); + va_end (ap); + + fprintf (debugF, "%s", buffer); + //if (options.verbose) fprintf (stderr, "%s: %s", __FUNCTION__, buffer); +} + +static void +debugNewLine (void) +{ + if (debugF) + fputc ('\n', debugF); +} +/*-----------------------------------------------------------------*/ +/* pic14_debugLogClose - closes the debug log file (if opened) */ +/*-----------------------------------------------------------------*/ +void +pic14_debugLogClose (void) +{ + if (debugF) + { + fclose (debugF); + debugF = NULL; + } +} + +static char * +debugAopGet (const char *str, operand * op) +{ + if (!debug) return NULL; + + if (str) debugLog (str); + + printOperand (op, debugF); + debugNewLine (); + + return NULL; +} + +static const char * +decodeOp (unsigned int op) +{ + + if (op < 128 && op > ' ') + { + buffer[0] = op & 0xff; + buffer[1] = '\0'; + return buffer; + } + + switch (op) + { + case IDENTIFIER: return "IDENTIFIER"; + case TYPE_NAME: return "TYPE_NAME"; + case CONSTANT: return "CONSTANT"; + case STRING_LITERAL: return "STRING_LITERAL"; + case SIZEOF: return "SIZEOF"; + case PTR_OP: return "PTR_OP"; + case INC_OP: return "INC_OP"; + case DEC_OP: return "DEC_OP"; + case LEFT_OP: return "LEFT_OP"; + case RIGHT_OP: return "RIGHT_OP"; + case LE_OP: return "LE_OP"; + case GE_OP: return "GE_OP"; + case EQ_OP: return "EQ_OP"; + case NE_OP: return "NE_OP"; + case AND_OP: return "AND_OP"; + case OR_OP: return "OR_OP"; + case MUL_ASSIGN: return "MUL_ASSIGN"; + case DIV_ASSIGN: return "DIV_ASSIGN"; + case MOD_ASSIGN: return "MOD_ASSIGN"; + case ADD_ASSIGN: return "ADD_ASSIGN"; + case SUB_ASSIGN: return "SUB_ASSIGN"; + case LEFT_ASSIGN: return "LEFT_ASSIGN"; + case RIGHT_ASSIGN: return "RIGHT_ASSIGN"; + case AND_ASSIGN: return "AND_ASSIGN"; + case XOR_ASSIGN: return "XOR_ASSIGN"; + case OR_ASSIGN: return "OR_ASSIGN"; + case TYPEDEF: return "TYPEDEF"; + case EXTERN: return "EXTERN"; + case STATIC: return "STATIC"; + case AUTO: return "AUTO"; + case REGISTER: return "REGISTER"; + case CODE: return "CODE"; + case EEPROM: return "EEPROM"; + case INTERRUPT: return "INTERRUPT"; + case SFR: return "SFR"; + case AT: return "AT"; + case SBIT: return "SBIT"; + case REENTRANT: return "REENTRANT"; + case USING: return "USING"; + case XDATA: return "XDATA"; + case DATA: return "DATA"; + case IDATA: return "IDATA"; + case PDATA: return "PDATA"; + case VAR_ARGS: return "VAR_ARGS"; + case CRITICAL: return "CRITICAL"; + case NONBANKED: return "NONBANKED"; + case BANKED: return "BANKED"; + case SD_CHAR: return "CHAR"; + case SD_SHORT: return "SHORT"; + case SD_INT: return "INT"; + case SD_LONG: return "LONG"; + case SIGNED: return "SIGNED"; + case UNSIGNED: return "UNSIGNED"; + case SD_FLOAT: return "FLOAT"; + case DOUBLE: return "DOUBLE"; + case SD_CONST: return "CONST"; + case VOLATILE: return "VOLATILE"; + case SD_VOID: return "VOID"; + case BIT: return "BIT"; + case STRUCT: return "STRUCT"; + case UNION: return "UNION"; + case ENUM: return "ENUM"; + case RANGE: return "RANGE"; + case SD_FAR: return "FAR"; + case CASE: return "CASE"; + case DEFAULT: return "DEFAULT"; + case IF: return "IF"; + case ELSE: return "ELSE"; + case SWITCH: return "SWITCH"; + case WHILE: return "WHILE"; + case DO: return "DO"; + case FOR: return "FOR"; + case GOTO: return "GOTO"; + case CONTINUE: return "CONTINUE"; + case BREAK: return "BREAK"; + case RETURN: return "RETURN"; + case INLINEASM: return "INLINEASM"; + case IFX: return "IFX"; + case ADDRESS_OF: return "ADDRESS_OF"; + case GET_VALUE_AT_ADDRESS: return "GET_VALUE_AT_ADDRESS"; + case SPIL: return "SPIL"; + case UNSPIL: return "UNSPIL"; + case GETHBIT: return "GETHBIT"; + case BITWISEAND: return "BITWISEAND"; + case UNARYMINUS: return "UNARYMINUS"; + case IPUSH: return "IPUSH"; + case IPOP: return "IPOP"; + case PCALL: return "PCALL"; + case ENDFUNCTION: return "ENDFUNCTION"; + case JUMPTABLE: return "JUMPTABLE"; + case RRC: return "RRC"; + case RLC: return "RLC"; + case CAST: return "CAST"; + case CALL: return "CALL"; + case PARAM: return "PARAM "; + case NULLOP: return "NULLOP"; + case BLOCK: return "BLOCK"; + case LABEL: return "LABEL"; + case RECEIVE: return "RECEIVE"; + case SEND: return "SEND"; + } + + SNPRINTF(buffer, sizeof(buffer), "unknown op %d %c", op, op & 0xff); + return buffer; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static const char * +debugLogRegType (short type) +{ + switch (type) + { + case REG_GPR: return "REG_GPR"; + case REG_PTR: return "REG_PTR"; + case REG_CND: return "REG_CND"; + } + + SNPRINTF(buffer, sizeof(buffer), "unknown reg type %d", type); + return buffer; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int regname2key(char const *name) +{ + int key = 0; + + if(!name) + return 0; + + while(*name) { + key += (*name++) + 1; + } + + return ((key + (key >> 4) + (key >> 8)) & 0x3f); +} + +/*-----------------------------------------------------------------*/ +/* regWithIdx - Search through a set of registers that matches idx */ +/*-----------------------------------------------------------------*/ +static reg_info * +regWithIdx (set *dRegs, int idx, int fixed) +{ + reg_info *dReg; + + for (dReg = setFirstItem(dRegs); dReg; dReg = setNextItem(dRegs)) { + + if(idx == dReg->rIdx && (fixed == (int)dReg->isFixed)) { + while (dReg->reg_alias) dReg = dReg->reg_alias; + return dReg; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* newReg - allocate and init memory for a new register */ +/*-----------------------------------------------------------------*/ +static reg_info* newReg(short type, PIC_OPTYPE pc_type, int rIdx, const char *name, int size, int alias) +{ + reg_info *dReg, *reg_alias; + + /* check whether a matching register already exists */ + dReg = dirregWithName(name); + if (dReg) { + //printf( "%s: already present: %s\n", __FUNCTION__, name ); + return (dReg); + } + + // check whether a register at that location exists + reg_alias = regWithIdx(dynDirectRegs, rIdx, FALSE); + if (!reg_alias) reg_alias = regWithIdx(dynDirectRegs, rIdx, TRUE); + + // create a new register + dReg = Safe_alloc(sizeof(reg_info)); + dReg->type = type; + dReg->pc_type = pc_type; + dReg->rIdx = rIdx; + if(name) { + dReg->name = Safe_strdup(name); + } else { + SNPRINTF(buffer, sizeof(buffer), "r0x%02X", dReg->rIdx); + dReg->name = Safe_strdup(buffer); + } + + dReg->isFree = FALSE; + dReg->wasUsed = FALSE; + dReg->isFixed = (type == REG_SFR) ? TRUE : FALSE; + dReg->isMapped = FALSE; + dReg->isEmitted = FALSE; + dReg->isPublic = FALSE; + dReg->isExtern = FALSE; + dReg->address = 0; + dReg->size = size; + dReg->alias = alias; + dReg->reg_alias = reg_alias; + dReg->reglives.usedpFlows = newSet(); + dReg->reglives.assignedpFlows = newSet(); + if (type != REG_STK) hTabAddItem(&dynDirectRegNames, regname2key(dReg->name), dReg); + debugLog( "%s: Created register %s.\n", __FUNCTION__, dReg->name); + + return dReg; +} + +/*-----------------------------------------------------------------*/ +/* regWithName - Search through a set of registers that matches name */ +/*-----------------------------------------------------------------*/ +static reg_info * +regWithName (set *dRegs, const char *name) +{ + reg_info *dReg; + + for (dReg = setFirstItem(dRegs); dReg; dReg = setNextItem(dRegs)) { + if((strcmp(name,dReg->name)==0)) { + return dReg; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* regWithName - Search for a registers that matches name */ +/*-----------------------------------------------------------------*/ +reg_info * +regFindWithName (const char *name) +{ + reg_info *dReg; + + if( (dReg = regWithName ( dynDirectRegs, name)) != NULL ) { + debugLog ("Found a Direct Register!\n"); + return dReg; + } + if( (dReg = regWithName ( dynDirectBitRegs, name)) != NULL) { + debugLog ("Found a Direct Bit Register!\n"); + return dReg; + } + + if (*name=='_') name++; // Step passed '_' + + if( (dReg = regWithName ( dynAllocRegs, name)) != NULL) { + debugLog ("Found a Dynamic Register!\n"); + return dReg; + } + if( (dReg = regWithName ( dynProcessorRegs, name)) != NULL) { + debugLog ("Found a Processor Register!\n"); + return dReg; + } + if( (dReg = regWithName ( dynInternalRegs, name)) != NULL) { + debugLog ("Found an Internal Register!\n"); + return dReg; + } + if( (dReg = regWithName ( dynStackRegs, name)) != NULL) { + debugLog ("Found an Stack Register!\n"); + return dReg; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* regFindFree - Search for a free register in a set of registers */ +/*-----------------------------------------------------------------*/ +static reg_info * +regFindFree (set *dRegs) +{ + reg_info *dReg; + + for (dReg = setFirstItem(dRegs); dReg; dReg = setNextItem(dRegs)) { + if(dReg->isFree) + return dReg; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* initStack - allocate registers for a pseudo stack */ +/*-----------------------------------------------------------------*/ +void initStack(int base_address, int size, int shared) +{ + int i; + PIC_device *pic; + + pic = pic14_getPIC(); + Gstack_base_addr = base_address; + Gstack_size = size; + //fprintf(stderr,"initStack [base:0x%02x, size:%d]\n", base_address, size); + + for(i = 0; ibankMask : 0x180) : 0x0); + r->isFixed = TRUE; + r->isPublic = TRUE; + r->isEmitted = TRUE; + //r->name[0] = 's'; + addSet(&dynStackRegs,r); + } +} + +/*-----------------------------------------------------------------* +*-----------------------------------------------------------------*/ +reg_info * +allocProcessorRegister(int rIdx, const char *name, short po_type, int alias) +{ + //fprintf(stderr,"allocProcessorRegister %s addr =0x%x\n",name,rIdx); + return addSet(&dynProcessorRegs,newReg(REG_SFR, po_type, rIdx, name,1,alias)); +} + +/*-----------------------------------------------------------------* +*-----------------------------------------------------------------*/ + +reg_info * +allocInternalRegister(int rIdx, const char *name, PIC_OPTYPE po_type, int alias) +{ + reg_info *reg = newReg(REG_GPR, po_type, rIdx, name,1,alias); + + //fprintf(stderr,"allocInternalRegister %s addr =0x%x\n",name,rIdx); + if(reg) { + reg->wasUsed = FALSE; + return addSet(&dynInternalRegs,reg); + } + + return NULL; +} +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocReg (short type) +{ + reg_info *reg; + + debugLog ("%s of type %s\n", __FUNCTION__, debugLogRegType (type)); + //fprintf(stderr,"allocReg\n"); + + reg = pic14_findFreeReg (type); + + reg->isFree = FALSE; + reg->wasUsed = TRUE; + + return reg; + + //return addSet(&dynAllocRegs,newReg(REG_GPR, PO_GPR_TEMP,dynrIdx++,NULL,1,0)); +} + + +/*-----------------------------------------------------------------*/ +/* dirregWithName - search for register by name */ +/*-----------------------------------------------------------------*/ +reg_info * +dirregWithName (const char *name) +{ + int hkey; + reg_info *reg; + + if(!name) + return NULL; + + /* hash the name to get a key */ + + hkey = regname2key(name); + + reg = hTabFirstItemWK(dynDirectRegNames, hkey); + + while(reg) { + + if(STRCASECMP(reg->name, name) == 0) { + // handle registers with multiple names + while (reg->reg_alias) reg = reg->reg_alias; + return(reg); + } + + reg = hTabNextItemWK (dynDirectRegNames); + + } + + return NULL; // name wasn't found in the hash table +} + +/*-----------------------------------------------------------------*/ +/* allocNewDirReg - allocates a new register of given type */ +/*-----------------------------------------------------------------*/ +reg_info * +allocNewDirReg (sym_link *symlnk,const char *name) +{ + reg_info *reg; + int address = 0; + sym_link *spec = getSpec (symlnk); + + /* if this is at an absolute address, then get the address. */ + if (SPEC_ABSA (spec) ) { + address = SPEC_ADDR (spec); + //fprintf(stderr,"reg %s is at an absolute address: 0x%03x\n",name,address); + } + + /* Register wasn't found in hash, so let's create + * a new one and put it in the hash table AND in the + * dynDirectRegNames set */ + if (IS_CONFIG_ADDRESS(address)) { + debugLog (" -- %s is declared at a config word address (0x%x)\n",name, address); + reg = 0; + } else { + int idx; + if (address) { + if (IS_BITVAR (spec)) + idx = address >> 3; + else + idx = address; + } else { + idx = dynrIdx++; + } + reg = newReg(REG_GPR, PO_DIR, idx, (char*)name,getSize (symlnk),0 ); + debugLog (" -- added %s to hash, size = %d\n", (char*)name,reg->size); + + if (SPEC_ABSA (spec) ) { + reg->type = REG_SFR; + } + + if (IS_BITVAR (spec)) { + addSet(&dynDirectBitRegs, reg); + reg->isBitField = TRUE; + } else + addSet(&dynDirectRegs, reg); + + if (!IS_STATIC (spec)) { + reg->isPublic = TRUE; + } + if (IS_EXTERN (spec)) { + reg->isExtern = TRUE; + } + } + + if (address && reg) { + reg->isFixed = TRUE; + reg->address = address; + debugLog (" -- and it is at a fixed address 0x%02x\n",reg->address); + } + + return reg; +} + +/*-----------------------------------------------------------------*/ +/* allocDirReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +reg_info * +allocDirReg (operand *op) +{ + reg_info *reg; + char *name; + + if(!IS_SYMOP(op)) { + debugLog ("%s BAD, op is NULL\n", __FUNCTION__); + return NULL; + } + + name = OP_SYMBOL (op)->rname[0] ? OP_SYMBOL (op)->rname : OP_SYMBOL (op)->name; + + /* If the symbol is at a fixed address, then remove the leading underscore + * from the name. This is hack to allow the .asm include file named registers + * to match the .c declared register names */ + + //if (SPEC_ABSA ( OP_SYM_ETYPE(op)) && (*name == '_')) + //name++; + + debugLog ("%s symbol name %s\n", __FUNCTION__,name); + { + if(SPEC_CONST ( OP_SYM_ETYPE(op)) && (IS_CHAR ( OP_SYM_ETYPE(op)) )) { + debugLog(" %d const char\n",__LINE__); + debugLog(" value = %s \n",SPEC_CVAL( OP_SYM_ETYPE(op))); + } + + debugLog(" %d storage class %d \n",__LINE__,SPEC_SCLS( OP_SYM_ETYPE(op))); + if (IS_CODE ( OP_SYM_ETYPE(op)) ) + debugLog(" %d code space\n",__LINE__); + + if (IS_INTEGRAL ( OP_SYM_ETYPE(op)) ) + debugLog(" %d integral\n",__LINE__); + if (IS_LITERAL ( OP_SYM_ETYPE(op)) ) + debugLog(" %d literal\n",__LINE__); + if (IS_SPEC ( OP_SYM_ETYPE(op)) ) + debugLog(" %d specifier\n",__LINE__); + debugAopGet(NULL, op); + } + + if (IS_CODE ( OP_SYM_ETYPE(op)) ) + return NULL; + + /* First, search the hash table to see if there is a register with this name */ + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) && !(IS_BITVAR (OP_SYM_ETYPE(op))) ) { + reg = regWithIdx (dynProcessorRegs, SPEC_ADDR ( OP_SYM_ETYPE(op)), TRUE); + /* + if(!reg) + fprintf(stderr,"ralloc %s is at fixed address but not a processor reg, addr=0x%x\n", + name, SPEC_ADDR ( OP_SYM_ETYPE(op))); + else + fprintf(stderr,"ralloc %s at fixed address has already been declared, addr=0x%x\n", + name, SPEC_ADDR ( OP_SYM_ETYPE(op))); + */ + } else { + //fprintf(stderr,"ralloc:%d %s \n", __LINE__,name); + + reg = dirregWithName(name); + } + +#if 0 + if(!reg) { + int address = 0; + + /* if this is at an absolute address, then get the address. */ + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { + address = SPEC_ADDR ( OP_SYM_ETYPE(op)); + //fprintf(stderr,"reg %s is at an absolute address: 0x%03x\n",name,address); + } + + /* Register wasn't found in hash, so let's create + * a new one and put it in the hash table AND in the + * dynDirectRegNames set */ + if(!IS_CONFIG_ADDRESS(address)) { + //fprintf(stderr,"allocating new reg %s\n",name); + + reg = newReg(REG_GPR, PO_DIR, dynrIdx++, name,getSize (OP_SYMBOL (op)->type),0 ); + debugLog (" -- added %s to hash, size = %d\n", name,reg->size); + + //hTabAddItem(&dynDirectRegNames, regname2key(name), reg); + + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { + + //fprintf(stderr, " ralloc.c at fixed address: %s - changing to REG_SFR\n",name); + reg->type = REG_SFR; + } + + if (IS_BITVAR (OP_SYM_ETYPE(op))) { + addSet(&dynDirectBitRegs, reg); + reg->isBitField = TRUE; + } else + addSet(&dynDirectRegs, reg); + + if (!IS_STATIC (OP_SYM_ETYPE(op))) { + reg->isPublic = TRUE; + } + if (IS_EXTERN (OP_SYM_ETYPE(op))) { + reg->isExtern = TRUE; + } + + } else { + debugLog (" -- %s is declared at a config word address (0x%x)\n",name, address); + + } + } + + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { + reg->isFixed = TRUE; + reg->address = SPEC_ADDR ( OP_SYM_ETYPE(op)); + debugLog (" -- and it is at a fixed address 0x%02x\n",reg->address); + } +#endif + + if(reg) { + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { + reg->isFixed = TRUE; + reg->address = SPEC_ADDR ( OP_SYM_ETYPE(op)); + debugLog (" -- and it is at a fixed address 0x%02x\n",reg->address); + } + } else { + allocNewDirReg (OP_SYM_TYPE(op),name); + } + + return reg; +} + + +/*-----------------------------------------------------------------*/ +/* allocRegByName - allocates register with given name */ +/*-----------------------------------------------------------------*/ +reg_info * +allocRegByName (const char *name, int size) +{ + reg_info *reg; + + if(!name) { + fprintf(stderr, "%s - allocating a NULL register\n",__FUNCTION__); + exit(1); + } + + /* First, search the hash table to see if there is a register with this name */ + reg = dirregWithName(name); + + if(!reg) { + int found = FALSE; + symbol *sym; + /* Register wasn't found in hash, so let's create + * a new one and put it in the hash table AND in the + * dynDirectRegNames set */ + //fprintf (stderr,"%s symbol name %s, size:%d\n", __FUNCTION__,name,size); + reg = newReg(REG_GPR, PO_DIR, dynrIdx++, name,size,0 ); + for (sym = setFirstItem(sfr->syms); sym; sym = setNextItem(sfr->syms)) { + if (strcmp(reg->name+1,sym->name)==0) { + unsigned a = SPEC_ADDR(sym->etype); + reg->address = a; + reg->isFixed = TRUE; + reg->type = REG_SFR; + if (!IS_STATIC (sym->etype)) { + reg->isPublic = TRUE; + } + if (IS_EXTERN (sym->etype)) { + reg->isExtern = TRUE; + } + if (IS_BITVAR (sym->etype)) + reg->isBitField = TRUE; + found = TRUE; + break; + } + } + if (!found) { + for (sym = setFirstItem(data->syms); sym; sym = setNextItem(data->syms)) { + if (strcmp(reg->name+1,sym->name)==0) { + unsigned a = SPEC_ADDR(sym->etype); + reg->address = a; + if (!IS_STATIC (sym->etype)) { + reg->isPublic = TRUE; + } + if (IS_EXTERN (sym->etype)) { + reg->isExtern = TRUE; + } + if (IS_BITVAR (sym->etype)) + reg->isBitField = TRUE; + found = TRUE; + break; + } + } + } + + debugLog (" -- added %s to hash, size = %d\n", name,reg->size); + + //hTabAddItem(&dynDirectRegNames, regname2key(name), reg); + if (reg->isBitField) { + addSet(&dynDirectBitRegs, reg); + } else + addSet(&dynDirectRegs, reg); + } + + return reg; +} + +/*-----------------------------------------------------------------*/ +/* RegWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +typeRegWithIdx (int idx, int type, int fixed) +{ + + reg_info *dReg; + + debugLog ("%s - requesting index = 0x%x\n", __FUNCTION__,idx); + + switch (type) { + + case REG_GPR: + if( (dReg = regWithIdx ( dynAllocRegs, idx, fixed)) != NULL) { + debugLog ("Found a Dynamic Register!\n"); + return dReg; + } + if( (dReg = regWithIdx ( dynDirectRegs, idx, fixed)) != NULL ) { + debugLog ("Found a Direct Register!\n"); + return dReg; + } + + break; + case REG_STK: + if( (dReg = regWithIdx ( dynStackRegs, idx, FALSE)) != NULL ) { + debugLog ("Found a Stack Register!\n"); + return dReg; + } else + if( (dReg = regWithIdx ( dynStackRegs, idx, TRUE)) != NULL ) { + debugLog ("Found a Stack Register!\n"); + return dReg; + } + else { + werror (E_STACK_OUT, "Register"); + /* return an existing register just to avoid the SDCC crash */ + return regWithIdx ( dynStackRegs, 0x7f, 0); + } + break; + case REG_SFR: + if( (dReg = regWithIdx ( dynProcessorRegs, idx, fixed)) != NULL ) { + debugLog ("Found a Processor Register!\n"); + return dReg; + } + + case REG_CND: + case REG_PTR: + default: + break; + } + + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic14_regWithIdx - returns pointer to register with index number*/ +/*-----------------------------------------------------------------*/ +reg_info * +pic14_regWithIdx (int idx) +{ + reg_info *dReg; + + if( (dReg = typeRegWithIdx(idx,REG_GPR,0)) != NULL) + return dReg; + + if( (dReg = typeRegWithIdx(idx,REG_SFR,0)) != NULL) + return dReg; + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic14_regWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +pic14_allocWithIdx (int idx) +{ + reg_info *dReg; + + debugLog ("%s - allocating with index = 0x%x\n", __FUNCTION__,idx); + + if( (dReg = regWithIdx ( dynAllocRegs, idx, FALSE)) != NULL) { + + debugLog ("Found a Dynamic Register!\n"); + } else if( (dReg = regWithIdx ( dynStackRegs, idx, FALSE)) != NULL ) { + debugLog ("Found a Stack Register!\n"); + } else if( (dReg = regWithIdx ( dynProcessorRegs, idx, FALSE)) != NULL ) { + debugLog ("Found a Processor Register!\n"); + } else if( (dReg = regWithIdx ( dynInternalRegs, idx, FALSE)) != NULL ) { + debugLog ("Found an Internal Register!\n"); + } else if( (dReg = regWithIdx ( dynInternalRegs, idx, TRUE)) != NULL ) { + debugLog ("Found an Internal Register!\n"); + } else { + debugLog ("Dynamic Register not found.\n"); + + //fprintf(stderr,"%s %d - requested register: 0x%x\n",__FUNCTION__,__LINE__,idx); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "regWithIdx not found"); + exit (1); + } + + dReg->wasUsed = TRUE; + dReg->isFree = FALSE; + + return dReg; +} +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +reg_info * +pic14_findFreeReg(short type) +{ + // int i; + reg_info* dReg; + + switch (type) { + case REG_GPR: + if((dReg = regFindFree(dynAllocRegs)) != NULL) + return dReg; + return addSet(&dynAllocRegs,newReg(REG_GPR, PO_GPR_TEMP,dynrIdx++,NULL,1,0)); + + case REG_STK: + + if((dReg = regFindFree(dynStackRegs)) != NULL) + return dReg; + + return NULL; + + case REG_PTR: + case REG_CND: + case REG_SFR: + default: + return NULL; + } +} +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (reg_info * reg) +{ + debugLog ("%s\n", __FUNCTION__); + reg->isFree = TRUE; +} + + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ +/* dynamically allocate as many as we need and worry about + * fitting them into a PIC later */ + + return 100; +#if 0 + int i; + int nfr = 0; + + debugLog ("%s\n", __FUNCTION__); + for (i = 0; i < pic14_nRegs; i++) + if (regspic14[i].isFree && regspic14[i].type == type) + nfr++; + return nfr; +#endif +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + debugLog ("%s\n", __FUNCTION__); + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} + +static void packBits(set *bregs) +{ + set *regset; + reg_info *breg; + reg_info *bitfield=NULL; + reg_info *relocbitfield=NULL; + int bit_no=0; + int byte_no=-1; + char buffer[20]; + + for (regset = bregs; regset; regset = regset->next) { + breg = regset->item; + breg->isBitField = TRUE; + //fprintf(stderr,"bit reg: %s\n",breg->name); + + if(breg->isFixed) { + //fprintf(stderr,"packing bit at fixed address = 0x%03x\n",breg->address); + + bitfield = typeRegWithIdx (breg->address >> 3, -1 , 1); + breg->rIdx = breg->address & 7; + breg->address >>= 3; + + if(!bitfield) { + //SNPRINTF(buffer, sizeof(buffer), "fbitfield%02x", breg->address); + SNPRINTF(buffer, sizeof(buffer), "0x%02x", breg->address); + //fprintf(stderr,"new bit field\n"); + bitfield = newReg(REG_SFR, PO_GPR_BIT,breg->address,buffer,1,0); + bitfield->isBitField = TRUE; + bitfield->isFixed = TRUE; + bitfield->address = breg->address; + //addSet(&dynDirectRegs,bitfield); + addSet(&dynInternalRegs,bitfield); + //hTabAddItem(&dynDirectRegNames, regname2key(buffer), bitfield); + } else { + //fprintf(stderr," which is occupied by %s (addr = %d)\n",bitfield->name,bitfield->address); + ; + } + breg->reg_alias = bitfield; + bitfield = NULL; + + } else { + if(!relocbitfield || bit_no >7) { + byte_no++; + bit_no=0; + SNPRINTF(buffer, sizeof(buffer), "bitfield%d", byte_no); + //fprintf(stderr,"new relocatable bit field\n"); + relocbitfield = newReg(REG_GPR, PO_GPR_BIT,dynrIdx++,buffer,1,0); + relocbitfield->isBitField = TRUE; + //addSet(&dynDirectRegs,relocbitfield); + addSet(&dynInternalRegs,relocbitfield); + //hTabAddItem(&dynDirectRegNames, regname2key(buffer), relocbitfield); + } + + breg->reg_alias = relocbitfield; + breg->address = dynrIdx; /* byte_no; */ + breg->rIdx = bit_no++; + } + } +} + + + +static void bitEQUs(FILE *of, set *bregs) +{ + reg_info *breg,*bytereg; + int bit_no=0; + + //fprintf(stderr," %s\n",__FUNCTION__); + for (breg = setFirstItem(bregs); breg; breg = setNextItem(bregs)) { + //fprintf(stderr,"bit reg: %s\n",breg->name); + + bytereg = breg->reg_alias; + if(bytereg) + fprintf(of, "%s\tEQU\t((%s << 3) + %d)\n", + breg->name, bytereg->name, breg->rIdx & 0x0007); + else { + //fprintf(stderr, "bit field is not assigned to a register\n"); + fprintf(of, "%s\tEQU\t((bitfield%d << 3) + %d)\n", + breg->name, bit_no >> 3, bit_no & 0x0007); + bit_no++; + } + } +} + +void writeUsedRegs(FILE *of) +{ + packBits(dynDirectBitRegs); + bitEQUs(of,dynDirectBitRegs); +} + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + debugLog ("%s\n", __FUNCTION__); + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return (sym->usl.spillLoc ? FALSE : TRUE); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return (sym->usl.spillLoc ? TRUE : FALSE); +} + +/*-----------------------------------------------------------------*/ +/* directSpilLoc - will return 1 if the splilocation is in direct */ +/*-----------------------------------------------------------------*/ +static int +directSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + if (sym->usl.spillLoc && + (IN_DIRSPACE (SPEC_OCLS (sym->usl.spillLoc->etype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil location */ +/* but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return ((sym->usl.spillLoc && !sym->uptr) ? TRUE : FALSE); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && + allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), + eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + debugLog ("%s\n", __FUNCTION__); + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + debugLog ("%s\n", __FUNCTION__); + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + /* if usage is the same then prefer the spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + debugLog ("%s\n", __FUNCTION__); + + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) + { + if (sym->liveTo > fsym->liveFrom) + return FALSE; + + } + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + debugLog ("%s\n", __FUNCTION__); + /* if already found */ + if (*sloc) + return FALSE; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + int k; + + debugLog ("%s\n", __FUNCTION__); + if (!_G.regAssigned || bitVectIsZero(_G.regAssigned)) + return; + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; + lrsym = hTabNextItem (liveRanges, &k)) + { + /* if no registers assigned to it or + spilt */ + /* if it does not overlap with this then + not need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || + (lrsym->liveTo < forSym->liveFrom)) + continue; + } +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model, noOverlay; + char slocBuffer[120]; + + debugLog ("%s\n", __FUNCTION__); + + FENTRY2("called."); + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = FALSE; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + SNPRINTF(slocBuffer, sizeof(slocBuffer), "sloc%d", _G.slocNum++); + sloc = newiTemp(slocBuffer); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + + /* we don't allow it to be allocated` + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + and turn off overlaying + */ + + useXstack = options.useXstack; + model = options.model; + noOverlay = options.noOverlay; + options.noOverlay = 1; + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; + options.noOverlay = noOverlay; + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + debugLog ("%s\n", __FUNCTION__); + FENTRY2("called."); + + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + + /* if (sym->_G.stackSpil) */ + /* return TRUE; */ + + if (!sym->usl.spillLoc) + return FALSE; + + etype = getSpec (sym->usl.spillLoc->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + debugLog ("%s : %s\n", __FUNCTION__, sym->rname); + FENTRY2("sym: %s, spillLoc:%p (%s)\n", sym->rname, sym->usl.spillLoc, sym->usl.spillLoc ? sym->usl.spillLoc->rname : ""); + + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc)) + createStackSpil (sym); + + /* mark it has spilt & put it in the spilt set */ + sym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to some + LIVE ranges */ + if (!pic14_ptrRegReq && isSpiltOnStack (sym)) + { + pic14_ptrRegReq++; + spillLRWithPtrReg (sym); + } + + if (sym->usl.spillLoc && !sym->remat) + sym->usl.spillLoc->allocreq = 1; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + debugLog ("%s\n", __FUNCTION__); + FENTRY2("called."); + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* get all live ranges that are rematerizable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strcpy (sym->rname, (sym->usl.spillLoc->rname[0] ? + sym->usl.spillLoc->rname : + sym->usl.spillLoc->name)); + sym->spildir = 1; + /* mark it as allocation required */ + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that not + used in the remainder of the block */ + if (!_G.blockSpil && + !isiCodeInFunctionCall (ic) && + (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (!sym->remat) + { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack , for which one? the least + used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq = 1; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + debugLog ("%s\n", __FUNCTION__); + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!pic14_ptrRegReq && isSpiltOnStack (ssym)) + { + pic14_ptrRegReq++; + spillLRWithPtrReg (ssym); + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? + ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + return ((ssym == forSym) ? FALSE : TRUE); +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + + debugLog ("%s\n", __FUNCTION__); +tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j=0; j<=sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = FALSE; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + + debugLog ("%s\n", __FUNCTION__); +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!pic14_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j=0; j<=sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = FALSE; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol *sym, reg_info *reg) +{ + int i; + + debugLog ("%s\n", __FUNCTION__); + for (i = 0; i < sym->nRegs; i++) { + if (sym->regs[i] == reg) { + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + symbol *result; + + debugLog ("%s\n", __FUNCTION__); + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* Prevent the result from being assigned the same registers as (one) + * operand as many genXXX-functions fail otherwise. + * POINTER_GET(ic) || ic->op == LEFT_OP || ic->op == RIGHT_OP || ic->op == NOT + * are known to fail. */ + if (sym->liveTo == ic->seq && IC_RESULT(ic)) + { + switch (ic->op) + { + case '=': /* assignment */ + case BITWISEAND: /* bitwise AND */ + case '|': /* bitwise OR */ + case '^': /* bitwise XOR */ + case '~': /* bitwise negate */ + case RLC: /* rotate through carry */ + case RRC: + case UNARYMINUS: + case '+': /* addition */ + case '-': /* subtraction */ + /* go ahead, these are safe to use with + * non-disjoint register sets */ + break; + + default: + /* do not release operand registers */ + //fprintf (stderr, "%s:%u: operand not freed: ", __FILE__, __LINE__); piCode (ic, stderr); fprintf (stderr, "\n"); + continue; + } // switch + } + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = TRUE; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && + ic->prev->op == IPOP && + !ic->prev->parmPush && + IS_SYMOP(IC_LEFT (ic->prev)) && + !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && + !(SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == RETURN || + POINTER_SET (ic)) && + IS_SYMOP (IC_RESULT (ic)) && + (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && + !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + + sym->nRegs) >= result->nRegs) + ) + { + for (i = 0; i < max (sym->nRegs, result->nRegs); i++) + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + debugLog ("%s\n", __FUNCTION__); + /* not spilt any more */ + sym->isspilt = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) { + sym->regs[i]->isFree = FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + debugLog ("%s\n", __FUNCTION__); + /* first check if there are any avlb registers + of te type required */ + if (rt == REG_PTR) + { + /* special case for pointer type + if pointer type not avlb then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return FALSE; + if (nFreeRegs (REG_GPR) >= nr) + return FALSE; + } + else + { + if (pic14_ptrRegReq) + { + if (nFreeRegs(rt) >= nr) + return FALSE; + } + else + { + if ((nFreeRegs(REG_PTR) + nFreeRegs(REG_GPR)) >= nr) + return FALSE; + } + } + + debugLog (" ... yep it will (cause a spill)\n"); + /* it will cause a spil */ + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to res- */ +/* ult and operand, if this happens make sure they are in the same */ +/* position as the operand otherwise chaos results */ +/*-----------------------------------------------------------------*/ +static void +positionRegs (symbol * result, symbol * opsym, int lineno) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = FALSE; + + debugLog ("%s\n", __FUNCTION__); + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return; +again: + shared = FALSE; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + { + for (j = 0; j < count; j++) + { + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = TRUE; + goto xchgPositions; + } + } + } +xchgPositions: + if (shared) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + goto again; + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode * ic) +{ + symbol * sym; + + if (!op) return; + if (!IS_ITEMP (op)) return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) return; + if (!sym->nRegs) return; + if (sym->regs[0]) return; + + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, + sym->prereqv ? sym->prereqv->name : sym->name); + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + debugLog ("%s\n", __FUNCTION__); + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* of all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + debugLog (" op: %s\n", decodeOp (ic->op)); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq = 1; + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + continue; + + /* now we need to allocate registers + only for the result */ + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j; + int ptrRegSet = 0; + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && + !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + continue; + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + willCS = willCauseSpill (sym->nRegs, sym->regType); + spillable = computeSpillable (ic); + if (sym->remat || + (willCS && bitVectIsZero (spillable))) + { + spillThis (sym); + continue; + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) { + if (sym->usl.spillLoc) { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) { + spillThis (sym); + continue; + } + } else { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith(spillable,noSpilLoc,ebbs[i],ic)) { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) { + spillThis (sym); + continue; + } + } + } + } + + if (ic->op == RECEIVE) + debugLog ("When I get clever, I'll optimize the receive logic\n"); + + /* if we need ptr regs for the right side + then mark it */ + if (POINTER_GET (ic) + && IS_SYMOP(IC_LEFT(ic)) + && getSize (OP_SYMBOL (IC_LEFT (ic))->type) + <= (unsigned) NEARPTRSIZE) + { + pic14_ptrRegReq++; + ptrRegSet = 1; + } + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + + debugLog (" %d - \n", __LINE__); + if(debugF) + bitVectDebugOn(_G.regAssigned, debugF); + for (j = 0; j < sym->nRegs; j++) + { + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + + /* if the allocation failed which means + this was spilt then break */ + if (!sym->regs[j]) + break; + } + debugLog (" %d - \n", __LINE__); + + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && + IS_SYMOP(IC_RESULT(ic)) && + OP_SYMBOL (IC_LEFT (ic))->nRegs && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_LEFT (ic)), ic->lineno); + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && + IS_SYMOP(IC_RESULT(ic)) && + OP_SYMBOL (IC_RIGHT (ic))->nRegs && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_RIGHT (ic)), ic->lineno); + + debugLog (" %d - \n", __LINE__); + if (ptrRegSet) + { + debugLog (" %d - \n", __LINE__); + pic14_ptrRegReq--; + ptrRegSet = 0; + } + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +static bitVect * +rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + debugLog ("%s\n", __FUNCTION__); + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (pic14_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit(rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect(pic14_nRegs); + + debugLog ("%s\n", __FUNCTION__); + /* do the special cases first */ + if (ic->op == IFX) + { + return bitVectUnion(rmask, rUmaskForOp(IC_COND(ic))); + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + return bitVectUnion(rmask, rUmaskForOp(IC_JTCOND(ic))); + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion(rmask, rUmaskForOp(IC_LEFT(ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion(rmask, rUmaskForOp(IC_RIGHT(ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion(rmask, rUmaskForOp(IC_RESULT(ic))); + + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + debugLog ("%s\n", __FUNCTION__); + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (pic14_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "createRegMask cannot find live range"); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + if (sym->regs[k]) + ic->rMask = + bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (void) +{ + symbol *sym; + int k; + //iCode *ic; + + debugLog ("%s\n", __FUNCTION__); + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) { + + debugLog (" %d - %s\n", __LINE__, sym->rname); + + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + + /* if the live range is a temporary */ + if (sym->isitmp) { + debugLog (" %d - itemp register\n", __LINE__); + + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->accuse) { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + debugLog (" %d - no reg needed - accumulator used\n", __LINE__); + + continue; + } + + if (sym->ruonly) { + //if (IS_AGGREGATE (sym->type) || sym->isptr) + // sym->type = aggrToPtr (sym->type, FALSE); + debugLog (" %d - used as a return\n", __LINE__); + + //continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer and the + pointer we are getting is rematerializable and + in "data" space */ + +#if 0 + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (sym->defs))) && + POINTER_GET (ic) && + !IS_BITVAR (sym->etype) && + (aggrToPtrDclType (operandType (IC_LEFT (ic)), FALSE) == POINTER)) { + + if (ptrPseudoSymSafe (sym, ic)) { + symbol *psym; + + debugLog (" %d - \n", __LINE__); + + /* create a pseudo symbol & force a spil */ + //X symbol *psym = newSymbol (rematStr (OP_SYMBOL (IC_LEFT (ic))), 1); + psym = rematStr (OP_SYMBOL (IC_LEFT (ic))); + psym->type = sym->type; + psym->etype = sym->etype; + psym->psbase = ptrBaseRematSym (OP_SYMBOL (IC_LEFT (ic))); + strcpy (psym->rname, psym->name); + sym->isspilt = 1; + sym->usl.spillLoc = psym; + continue; + } + + /* if in data space or idata space then try to + allocate pointer register */ + } +#endif + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : + getSize (sym->type)); + + + if(IS_PTR_CONST (sym->type)) { + debugLog (" %d const pointer type requires %d registers, changing to 2\n",__LINE__,sym->nRegs); + sym->nRegs = 2; + } + + if (sym->nRegs > 4) { + fprintf (stderr, "allocated more than 4 or 0 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && + IS_PTR (sym->type) && + sym->uptr) + sym->regType = REG_PTR; + else + sym->regType = REG_GPR; + + debugLog (" reg name %s, reg type %s\n", sym->rname, debugLogRegType (sym->regType)); + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + } +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + debugLog ("%s\n", __FUNCTION__); + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && + getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > 1) + return NULL; + + if (POINTER_SET (dic) && + getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) > 1) + return NULL; + + /* if any three is a true symbol in far space */ + if (IC_RESULT (dic) && + IS_TRUE_SYMOP (IC_RESULT (dic)) && + isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && + !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && + !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) + { + if ((dic->op == LEFT_OP || + dic->op == RIGHT_OP || + dic->op == '-') && + IS_OP_LITERAL (IC_RIGHT (dic))) + return NULL; + else + return dic; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + debugLog ("%s\n", __FUNCTION__); + + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + + /* if this is at an absolute address, then get the address. */ + if (SPEC_ABSA ( OP_SYM_ETYPE(IC_RESULT(ic))) ) { + if(IS_CONFIG_ADDRESS( SPEC_ADDR ( OP_SYM_ETYPE(IC_RESULT(ic))))) { + debugLog (" %d - found config word declaration\n", __LINE__); + if(IS_VALOP(IC_RIGHT(ic))) { + debugLog (" setting config word to %x\n", + (int) ulFromVal (OP_VALUE (IC_RIGHT(ic)))); + pic14_assignConfigWordValue( SPEC_ADDR ( OP_SYM_ETYPE(IC_RESULT(ic))), + (int) ulFromVal (OP_VALUE (IC_RIGHT(ic)))); + } + + /* remove the assignment from the iCode chain. */ + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + + return TRUE; + + } + } + + if (!IS_ITEMP (IC_RESULT (ic))) { + allocDirReg(IC_RESULT (ic)); + debugLog (" %d - result is not temp\n", __LINE__); + } + /* + if (IC_LEFT (ic) && !IS_ITEMP (IC_LEFT (ic))) { + debugLog (" %d - left is not temp, allocating\n", __LINE__); + allocDirReg(IC_LEFT (ic)); + } + */ + + if (!IS_ITEMP (IC_RIGHT (ic))) { + debugLog (" %d - not packing - right is not temp\n", __LINE__); + + /* only pack if this is not a function pointer */ + if (!IS_REF (IC_RIGHT (ic))) + allocDirReg(IC_RIGHT (ic)); + return FALSE; + } + + if (OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + debugLog (" %d - not packing - right side fails \n", __LINE__); + return FALSE; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ + if (isOperandInFarSpace (IC_RESULT (ic))) + { + if ((dic = farSpacePackable (ic))) + goto pack; + else + return FALSE; + } + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol before we find the definition then + we cannot pack */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* if there is a function call and this is + a parameter & not my parameter then don't pack it */ + if ((dic->op == CALL || dic->op == PCALL) && + (OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->ismyparm)) + { + debugLog (" %d - \n", __LINE__); + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) + { + debugLog (" %d - dic is VOLATILE \n", __LINE__); + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + /* A previous result was assigned to the same register - we'll our definition */ + debugLog (" %d - dic result key == ic right key -- pointer set=%c\n", + __LINE__, ((POINTER_SET (dic)) ? 'Y' : 'N')); + if (POINTER_SET (dic)) + dic = NULL; + + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + debugLog (" %d - dic right key == ic rightor result key\n", __LINE__); + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + debugLog (" %d - dic left key == ic rightor result key\n", __LINE__); + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + debugLog (" %d - dic result key == ic result key -- pointer set=Y\n", + __LINE__); + dic = NULL; + break; + } + } + + if (!dic) + return FALSE; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + return FALSE; + } + } + + /* if the result is on stack or iaccess then it must be + the same at least one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || + (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || + (IC_RIGHT (dic) && + IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return FALSE; + } +pack: + debugLog (" packing. removing %s\n", OP_SYMBOL (IC_RIGHT (ic))->rname); + debugLog (" replacing with %s\n", OP_SYMBOL (IC_RESULT (dic))->rname); + /* found the definition */ + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS(IC_RESULT (dic))=bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*-----------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if definition by assignment */ + if (dic->op == '=' && + !POINTER_SET (dic) && + IC_RESULT (dic)->key == op->key + /* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + ) + { + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then return + true */ + if (!IS_SYMOP (IC_RIGHT (dic))) + break; + + /* if the symbol is in far space then + we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && + OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && + IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + return NULL; + } + + break; + + } + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) { + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + } + + return dic; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + int change = 0; + + debugLog ("%s\n", __FUNCTION__); + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_LEFT (ic), ic); + iCode *sic; + + if (!dic) + goto right; + + debugAopGet ("removing left:", IC_LEFT (ic)); + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_LEFT (ic)->key); + + OP_SYMBOL (IC_LEFT (ic)) = OP_SYMBOL (IC_RIGHT (dic)); + IC_LEFT (ic)->key = OP_KEY (IC_RIGHT (dic)); + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + /* do the same for the right operand */ +right: + if (!change && + IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + iCode *sic; + + if (!dic) + return change; + + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) + { + sym_link *etype = getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return change; + } + + debugAopGet ("removing right:", IC_RIGHT (ic)); + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_RIGHT (ic)->key); + + OP_SYMBOL (IC_RIGHT (ic)) = OP_SYMBOL (IC_RIGHT (dic)); + IC_RIGHT (ic)->key = OP_KEY (IC_RIGHT (dic)); + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + return change; +} + + +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + debugLog ("%s\n", __FUNCTION__); + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* only upto 2 bytes since we cannot predict + the usage of b, & acc */ + if (getSize (operandType (op)) > (fReturnSizePic - 2) && + ic->op != RETURN && + ic->op != SEND) + return NULL; + + /* this routine will mark the a symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one defintion */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = + hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from + a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + + if (!dic) + { + /* Not sure why we advance dic ... Make sure that we do + * not SEGFAULT by dereferencing a NULL pitr later on. */ + return NULL; + } // if + } + + + /* otherwise check that the definition does + not contain any symbols in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + IS_OP_RUONLY (IC_LEFT (ic)) || + IS_OP_RUONLY (IC_RIGHT (ic))) + { + return NULL; + } + + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + sic = dic; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (dic = dic->next; dic && dic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 3) + return NULL; + + /* if left or right or result is in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + isOperandInFarSpace (IC_RESULT (dic)) || + IS_OP_RUONLY (IC_LEFT (dic)) || + IS_OP_RUONLY (IC_RIGHT (dic)) || + IS_OP_RUONLY (IC_RESULT (dic))) + { + return NULL; + } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + debugLog ("%s\n", __FUNCTION__); + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static void +packRegsForAccUse (iCode * ic) +{ + //iCode *uic; + + debugLog ("%s\n", __FUNCTION__); + + /* result too large for WREG? */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return; + + /* We have to make sure that OP_SYMBOL(IC_RESULT(ic)) + * is never used as an operand to an instruction that + * cannot have WREG as an operand (e.g. BTFSx cannot + * operate on WREG... + * For now, store all results into proper registers. */ + return; + +#if 0 + /* if this is an aggregate, e.g. a one byte char array */ + if (IS_AGGREGATE(operandType(IC_RESULT(ic)))) { + return; + } + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + + /* if + or - then it has to be one byte result */ + if ((ic->op == '+' || ic->op == '-') + && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if shift operation make sure right side is not a literal */ + if (ic->op == RIGHT_OP && + (isOperandLiteral (IC_RIGHT (ic)) || + getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (ic->op == LEFT_OP && + (isOperandLiteral (IC_RIGHT (ic)) || + getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + if (IS_BITWISE_OP (ic) && + getSize (operandType (IC_RESULT (ic))) > 1) + return; + + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + /* has only one use */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) > 1) + return; + + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (ic->next != uic) + return; + + /* if it is a conditional branch then we definitely can */ + if (uic->op == IFX) + goto accuse; + + if (uic->op == JUMPTABLE) + return; + + /* if the usage is not is an assignment + or an arithmetic / bitwise / shift operation then not */ + if (POINTER_SET (uic) && + getSize (aggrToPtr (operandType (IC_RESULT (uic)), FALSE)) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (uic->op != '=' && + !IS_ARITHMETIC_OP (uic) && + !IS_BITWISE_OP (uic) && + uic->op != LEFT_OP && + uic->op != RIGHT_OP) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if used in ^ operation then make sure right is not a + literl */ + if (uic->op == '^' && isOperandLiteral (IC_RIGHT (uic))) + return; + + /* if shift operation make sure right side is not a literal */ + if (uic->op == RIGHT_OP && + (isOperandLiteral (IC_RIGHT (uic)) || + getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + if (uic->op == LEFT_OP && + (isOperandLiteral (IC_RIGHT (uic)) || + getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + /* make sure that the result of this icode is not on the + stack, since acc is used to compute stack offset */ + if (IS_TRUE_SYMOP (IC_RESULT (uic)) && + OP_SYMBOL (IC_RESULT (uic))->onStack) + return; + + /* if either one of them in far space then we cannot */ + if ((IS_TRUE_SYMOP (IC_LEFT (uic)) && + isOperandInFarSpace (IC_LEFT (uic))) || + (IS_TRUE_SYMOP (IC_RIGHT (uic)) && + isOperandInFarSpace (IC_RIGHT (uic)))) + return; + + /* if the usage has only one operand then we can */ + if (IC_LEFT (uic) == NULL || + IC_RIGHT (uic) == NULL) + goto accuse; + + /* make sure this is on the left side if not + a '+' since '+' is commutative */ + if (ic->op != '+' && + IC_LEFT (uic)->key != IC_RESULT (ic)->key) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if one of them is a literal then we can */ + if ( ((IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic))) || + (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)))) && + (getSize (operandType (IC_RESULT (uic))) <= 1)) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + return; + } + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if the other one is not on stack then we can */ + if (IC_LEFT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_RIGHT (uic)) || + (IS_TRUE_SYMOP (IC_RIGHT (uic)) && + !OP_SYMBOL (IC_RIGHT (uic))->onStack))) + goto accuse; + + if (IC_RIGHT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_LEFT (uic)) || + (IS_TRUE_SYMOP (IC_LEFT (uic)) && + !OP_SYMBOL (IC_LEFT (uic))->onStack))) + goto accuse; + + return; + +accuse: + debugLog ("%s - Yes we are using the accumulator\n", __FUNCTION__); + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; +#endif +} + +/*-----------------------------------------------------------------*/ +/* packForPush - hueristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForReceive (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + + if (!ic->next) + return; + + for (dic = ic->next; dic; dic = dic->next) + { + if (IC_LEFT (dic) && (IC_RESULT (ic)->key == IC_LEFT (dic)->key)) + debugLog (" used on left\n"); + if (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key) + debugLog (" used on right\n"); + if (IC_RESULT (dic) && IC_RESULT (ic)->key == IC_RESULT (dic)->key) + debugLog (" used on result\n"); + + if ((IC_LEFT (dic) && (IC_RESULT (ic)->key == IC_LEFT (dic)->key)) || + (IC_RESULT (dic) && IC_RESULT (ic)->key == IC_RESULT (dic)->key)) + return; + } + + debugLog (" hey we can remove this unnecessary assign\n"); +} + +/*-----------------------------------------------------------------*/ +/* packForPush - hueristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *lic; + bitVect *dbv; + int disallowHiddenAssignment = 0; + + debugLog ("%s\n", __FUNCTION__); + if (ic->op != IPUSH || !IS_ITEMP (IC_LEFT (ic))) + return; + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + if (dic->op != '=' || POINTER_SET (dic)) + return; + + /* If the defining iCode is outside of this block, we need to recompute */ + /* ebp (see the mcs51 version of packForPush), but we weren't passed */ + /* enough data to do that. Just bail out instead if that happens. */ + if (dic->seq < ebp->fSeq) + return; + + if (IS_SYMOP (IC_RIGHT (dic))) { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS (IC_RIGHT (dic)); + for (lic = ic; lic && lic != dic; lic = lic->prev) { + if (bitVectBitValue (dbv, lic->key)) + return; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC (operandType (IC_LEFT (ic)))) { + sym_link *itype = operandType (IC_LEFT (ic)); + sym_link *ditype = operandType (IC_RIGHT (dic)); + + if (SPEC_USIGN (itype) != SPEC_USIGN (ditype) || SPEC_LONG (itype) != SPEC_LONG (ditype)) + return; + } + /* extend the live range of replaced operand if needed */ + if (OP_SYMBOL (IC_RIGHT (dic))->liveTo < ic->seq) { + OP_SYMBOL (IC_RIGHT (dic))->liveTo = ic->seq; + } + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + } + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + IC_LEFT (ic) = IC_RIGHT (dic); + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); +} + +static void printSymType(char * str, sym_link *sl) +{ + if (debug) { + debugLog (" %s Symbol type: ",str); + printTypeChain( sl, debugF); + debugLog ("\n"); + } +} + +/*-----------------------------------------------------------------*/ +/* some debug code to print the symbol S_TYPE. Note that +* the function checkSClass in src/SDCCsymt.c dinks with +* the S_TYPE in ways the PIC port doesn't fully like...*/ +/*-----------------------------------------------------------------*/ +static void isData(sym_link *sl) +{ + FILE *of = stderr; + + // avoid garbage `data' and `sfr' output + if(!sl || !debugF) + return; + + if(debugF) + of = debugF; + + for ( ; sl; sl=sl->next) { + if(!IS_DECL(sl) ) { + switch (SPEC_SCLS(sl)) { + + case S_DATA: fprintf (of, "data "); break; + case S_XDATA: fprintf (of, "xdata "); break; + case S_SFR: fprintf (of, "sfr "); break; + case S_SBIT: fprintf (of, "sbit "); break; + case S_CODE: fprintf (of, "code "); break; + case S_IDATA: fprintf (of, "idata "); break; + case S_PDATA: fprintf (of, "pdata "); break; + case S_LITERAL: fprintf (of, "literal "); break; + case S_STACK: fprintf (of, "stack "); break; + case S_XSTACK: fprintf (of, "xstack "); break; + case S_BIT: fprintf (of, "bit "); break; + case S_EEPROM: fprintf (of, "eeprom "); break; + default: break; + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* packRegisters - does some transformations to reduce register */ +/* pressure */ +/*-----------------------------------------------------------------*/ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + debugLog ("%s\n", __FUNCTION__); + + while (1) { + + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + /* debug stuff */ + if (ic->op == '=') + { + if (POINTER_SET (ic)) + debugLog ("pointer is set\n"); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + } + + } + + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) { + + if(IS_SYMOP ( IC_LEFT(ic))) { + sym_link *etype = getSpec (operandType (IC_LEFT (ic))); + + debugAopGet (" left:", IC_LEFT (ic)); + if(IS_PTR_CONST(OP_SYMBOL(IC_LEFT(ic))->type)) + debugLog (" is a pointer\n"); + + if(IS_OP_VOLATILE(IC_LEFT(ic))) + debugLog (" is volatile\n"); + + isData(etype); + + printSymType(" ", OP_SYMBOL(IC_LEFT(ic))->type); + } + + if(IS_SYMOP ( IC_RIGHT(ic))) { + debugAopGet (" right:", IC_RIGHT (ic)); + printSymType(" ", OP_SYMBOL(IC_RIGHT(ic))->type); + } + + if(IS_SYMOP ( IC_RESULT(ic))) { + debugAopGet (" result:", IC_RESULT (ic)); + printSymType(" ", OP_SYMBOL(IC_RESULT(ic))->type); + } + + if (POINTER_SET (ic)) + debugLog (" %d - Pointer set\n", __LINE__); + + + /* Look for two subsequent iCodes with */ + /* iTemp := _c; */ + /* _c = iTemp & op; */ + /* and replace them by */ + /* iTemp := _c; */ + /* _c = _c & op; */ + if ((ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') && + ic->prev && + ic->prev->op == '=' && + IS_ITEMP (IC_LEFT (ic)) && + IC_LEFT (ic) == IC_RESULT (ic->prev) && + isOperandEqual (IC_RESULT(ic), IC_RIGHT(ic->prev))) { + + iCode* ic_prev = ic->prev; + symbol* prev_result_sym = OP_SYMBOL (IC_RESULT (ic_prev)); + + ReplaceOpWithCheaperOp (&IC_LEFT (ic), IC_RESULT (ic)); + + if (IC_RESULT (ic_prev) != IC_RIGHT (ic)) { + bitVectUnSetBit (OP_USES (IC_RESULT (ic_prev)), ic->key); + if (/*IS_ITEMP (IC_RESULT (ic_prev)) && */ + prev_result_sym->liveTo == ic->seq) { + prev_result_sym->liveTo = ic_prev->seq; + } + } + + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + bitVectSetBit (ic->rlive, IC_RESULT (ic)->key); + + if (bitVectIsZero (OP_USES (IC_RESULT (ic_prev)))) { + bitVectUnSetBit (ic->rlive, IC_RESULT (ic)->key); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic_prev)), ic_prev->key); + remiCodeFromeBBlock (ebp, ic_prev); + hTabDeleteItem (&iCodehTab, ic_prev->key, ic_prev, DELETE_ITEM, NULL); + } + } + + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + debugLog (" %d - %s. result is rematerializable\n", __LINE__,__FUNCTION__); + + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !isOperandGlobal (IC_RESULT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + debugLog (" %d - %s. straight rematerializable\n", __LINE__,__FUNCTION__); + + OP_SYMBOL (IC_RESULT (ic))->remat = + OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = + OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + IS_OP_LITERAL (IC_RIGHT (ic)))) + { + debugLog (" %d - %s. rematerializable because op is +/-\n", __LINE__,__FUNCTION__); + //int i = + operandLitValue (IC_RIGHT (ic)); + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP(IC_RESULT(ic))) + { + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + debugLog (" marking as a pointer (set) =>"); + debugAopGet (" result:", IC_RESULT (ic)); + } + if (POINTER_GET (ic) && IS_SYMOP(IC_LEFT(ic))) + { + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + debugLog (" marking as a pointer (get) =>"); + debugAopGet (" left:", IC_LEFT (ic)); + } + + if (!SKIP_IC2 (ic)) + { + /* if we are using a symbol on the stack + then we should say pic14_ptrRegReq */ + if (ic->op == IFX && IS_SYMOP (IC_COND (ic))) + pic14_ptrRegReq += ((OP_SYMBOL (IC_COND (ic))->onStack || + OP_SYMBOL (IC_COND (ic))->iaccess) ? 1 : 0); + else if (ic->op == JUMPTABLE && IS_SYMOP (IC_JTCOND (ic))) + pic14_ptrRegReq += ((OP_SYMBOL (IC_JTCOND (ic))->onStack || + OP_SYMBOL (IC_JTCOND (ic))->iaccess) ? 1 : 0); + else + { + if (IS_SYMOP (IC_LEFT (ic))) + pic14_ptrRegReq += ((OP_SYMBOL (IC_LEFT (ic))->onStack || + OP_SYMBOL (IC_LEFT (ic))->iaccess) ? 1 : 0); + if (IS_SYMOP (IC_RIGHT (ic))) + pic14_ptrRegReq += ((OP_SYMBOL (IC_RIGHT (ic))->onStack || + OP_SYMBOL (IC_RIGHT (ic))->iaccess) ? 1 : 0); + if (IS_SYMOP (IC_RESULT (ic))) + pic14_ptrRegReq += ((OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess) ? 1 : 0); + } + + debugLog (" %d - pointer reg req = %d\n", __LINE__,pic14_ptrRegReq); + + } + + /* if the condition of an if instruction + is defined in the previous instruction then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + ((ic->op == BITWISEAND || + ic->op == '|' || + ic->op == '^') && + isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + debugLog (" %d\n", __LINE__); + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* reduce for support function calls */ + if (ic->supportRtn || ic->op == '+' || ic->op == '-') + packRegsForSupport (ic, ebp); + + /* if a parameter is passed, it's in W, so we may not + need to place a copy in a register */ + if (ic->op == RECEIVE) + packForReceive (ic, ebp); + + /* some cases the redundant moves can + can be eliminated for return statements */ + if ((ic->op == RETURN || ic->op == SEND) && + !isOperandInFarSpace (IC_LEFT (ic)) && + !options.model) + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + + /* if pointer set & left has a size more than + one and right is not in far space */ + if (POINTER_SET (ic) && + !isOperandInFarSpace (IC_RIGHT (ic)) && + IS_SYMOP(IC_RESULT(ic)) && + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && + getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + + packRegsForOneuse (ic, IC_RESULT (ic), ebp); + + /* if pointer get */ + if (POINTER_GET (ic) && + !isOperandInFarSpace (IC_RESULT (ic)) && + IS_SYMOP(IC_LEFT(ic)) && + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && + getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + + + /* if this is cast for intergral promotion then + check if only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) { + + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + debugLog (" %d - casting\n", __LINE__); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType)) { + + + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) { + + if (IS_ARITHMETIC_OP (dic)) { + + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS(IC_RESULT (dic))=bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } else + OP_SYMBOL (IC_RIGHT (ic))->ruonly = 0; + } + } else { + + /* if the type from and type to are the same + then if this is the only use then packit */ + if (compareType (operandType (IC_RIGHT (ic)), + operandType (IC_LEFT (ic))) == 1) { + + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) { + + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS(IC_RESULT (dic))=bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + } + } + } + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH) + { + packForPush (ic, ebp); + } + + + /* pack registers for accumulator use, when the + result of an arithmetic or bit wise operation + has only one use, that use is immediately following + the defintion and the using iCode has only one + operand or has two operands but one is literal & + the result of that operation is not on stack then + we can leave the result of this operation in acc:b + combination */ + if ((IS_ARITHMETIC_OP (ic) + + || IS_BITWISE_OP (ic) + + || ic->op == LEFT_OP || ic->op == RIGHT_OP + + ) && + IS_ITEMP (IC_RESULT (ic)) && + getSize (operandType (IC_RESULT (ic))) <= 2) + + packRegsForAccUse (ic); + } +} + +static void +dumpEbbsToDebug (eBBlock ** ebbs, int count) +{ + int i; + + if (!debug || !debugF) + return; + + for (i = 0; i < count; i++) + { + fprintf (debugF, "\n----------------------------------------------------------------\n"); + fprintf (debugF, "Basic Block %s : loop Depth = %d noPath = %d , lastinLoop = %d\n", + ebbs[i]->entryLabel->name, + ebbs[i]->depth, + ebbs[i]->noPath, + ebbs[i]->isLastInLoop); + fprintf (debugF, "depth 1st num %d : bbnum = %d 1st iCode = %d , last iCode = %d\n", + ebbs[i]->dfnum, + ebbs[i]->bbnum, + ebbs[i]->fSeq, + ebbs[i]->lSeq); + fprintf (debugF, "visited %d : hasFcall = %d\n", + ebbs[i]->visited, + ebbs[i]->hasFcall); + + fprintf (debugF, "\ndefines bitVector :"); + bitVectDebugOn (ebbs[i]->defSet, debugF); + fprintf (debugF, "\nlocal defines bitVector :"); + bitVectDebugOn (ebbs[i]->ldefs, debugF); + fprintf (debugF, "\npointers Set bitvector :"); + bitVectDebugOn (ebbs[i]->ptrsSet, debugF); + fprintf (debugF, "\nin pointers Set bitvector :"); + bitVectDebugOn (ebbs[i]->inPtrsSet, debugF); + fprintf (debugF, "\ninDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->inDefs, debugF); + fprintf (debugF, "\noutDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->outDefs, debugF); + fprintf (debugF, "\nusesDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->usesDefs, debugF); + fprintf (debugF, "\n----------------------------------------------------------------\n"); + printiCChain (ebbs[i]->sch, debugF); + } +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +pic14_assignRegisters (ebbIndex *ebbi) +{ + int i; + iCode *ic; + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + + debugLog ("<><><><><><><><><><><><><><><><><>\nstarting\t%s:%s\n", + __FILE__, __FUNCTION__); + debugLog ("ebbs before optimizing:\n"); + dumpEbbsToDebug (ebbs, count); + + setToNull ((void *) &_G.funcrUsed); + pic14_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + + /* mark all r0xZZZZ registers as 'used' to guarantee that + * disjoint sets of registers are allocated to functions */ + if (1) + { + reg_info *r; + + for (r = setFirstItem (dynAllocRegs); r; r = setNextItem (dynAllocRegs)) + { + r->isFree = FALSE; + } + } + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + packRegisters (ebbs[i]); + + if (1) + { + reg_info *reg; + int hkey; + int i = 0; + + debugLog ("dir registers allocated so far:\n"); + reg = hTabFirstItem (dynDirectRegNames, &hkey); + + while (reg) + { + debugLog (" -- #%d reg = %s key %d, rIdx = %d, size %d\n", + i++, reg->name, hkey, reg->rIdx, reg->size); + reg = hTabNextItem (dynDirectRegNames, &hkey); + } + } + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { + _G.dataExtend = 0; + } + + /* after that create the register mask for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + redoStackOffsets (); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + debugLog ("ebbs after optimizing:\n"); + dumpEbbsToDebug (ebbs, count); + + genpic14Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + + debugLog ("leaving\n<><><><><><><><><><><><><><><><><>\n"); + pic14_debugLogClose (); +} diff --git a/src/pic14/ralloc.h b/src/pic14/ralloc.h new file mode 100644 index 0000000..bcc1704 --- /dev/null +++ b/src/pic14/ralloc.h @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + PIC port - T. Scott Dattalo scott@dattalo.com (2000) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#include "common.h" + +#include "pcoderegs.h" + + +enum { + REG_PTR=1, + REG_GPR, + REG_CND, + REG_SFR, + REG_STK, + REG_TMP +}; + +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + * REG_GPR, REG_PTR or REG_CND + * This like the "meta-type" */ + short pc_type; /* pcode type */ + short rIdx; /* index into register table */ + char *name; /* name */ + + unsigned isFree:1; /* is currently unassigned */ + unsigned wasUsed:1; /* becomes true if register has been used */ + unsigned isFixed:1; /* True if address can't change */ + unsigned isMapped:1; /* The Register's address has been mapped to physical RAM */ + unsigned isBitField:1; /* True if reg is type bit OR is holder for several bits */ + unsigned isEmitted:1; /* True if the reg has been written to a .asm file */ + unsigned isPublic:1; /* True if the reg is not static and can be modified in another module (ie a another c or asm file) */ + unsigned isExtern:1; /* True if the reg is in another module */ + unsigned address; /* reg's address if isFixed | isMapped is true */ + unsigned size; /* 0 for byte, 1 for int, 4 for long */ + unsigned alias; /* Alias mask if register appears in multiple banks */ + struct reg_info *reg_alias; /* If more than one register share the same address + * then they'll point to each other. (primarily for bits)*/ + pCodeRegLives reglives; /* live range mapping */ +} +reg_info; +extern reg_info regspic14[]; +extern int Gstack_base_addr; + +/* + As registers are created, they're added to a set (based on the + register type). Here are the sets of registers that are supported + in the PIC port: +*/ +extern set *dynAllocRegs; +extern set *dynStackRegs; +extern set *dynProcessorRegs; +extern set *dynDirectRegs; +extern set *dynDirectBitRegs; +extern set *dynInternalRegs; + + +void initStack(int base_address, int size, int shared); +reg_info *pic14_regWithIdx(int); +reg_info *dirregWithName(const char *name); +void pic14_assignRegisters(ebbIndex *ebbi); +reg_info *pic14_findFreeReg(short type); +reg_info *pic14_allocWithIdx (int idx); +reg_info *typeRegWithIdx(int idx, int type, int fixed); +reg_info *regFindWithName(const char *name); + +void pic14_debugLogClose(void); +void writeUsedRegs(FILE *of); + +reg_info *allocDirReg(operand *op ); +reg_info *allocInternalRegister(int rIdx, const char *name, PIC_OPTYPE po_type, int alias); +reg_info *allocProcessorRegister(int rIdx, const char *name, short po_type, int alias); +reg_info *allocRegByName(const char *name, int size ); +reg_info *allocNewDirReg(sym_link *symlnk, const char *name); + +/* Define register address that are constant across PIC family */ +#define IDX_INDF 0 +#define IDX_INDF0 0 +#define IDX_TMR0 1 +#define IDX_PCL 2 +#define IDX_STATUS 3 +#define IDX_FSR 4 +#define IDX_FSR0L 4 +#define IDX_FSR0H 5 +#define IDX_PCLATH 0x0a +#define IDX_INTCON 0x0b + +#define IDX_KZ 0x7fff /* Known zero - actually just a general purpose reg. */ +#define IDX_WSAVE 0x7ffe +#define IDX_SSAVE 0x7ffd +#define IDX_PSAVE 0x7ffc + +#define pic14_nRegs 128 + +#endif diff --git a/src/pic16/Makefile b/src/pic16/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/pic16/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pic16/Makefile.in b/src/pic16/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/pic16/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/pic16/NOTES b/src/pic16/NOTES new file mode 100644 index 0000000..4141a7d --- /dev/null +++ b/src/pic16/NOTES @@ -0,0 +1,210 @@ +NOTES file for SDCC pic16 port +$Id: NOTES 5487 2009-08-08 15:04:04Z MaartenBrock $ + +Current pic16 port status is: Development + +Some things may change without notification between port updates. The latest +SVN snapshot is guarenteed to compile without problems, but does not +guarantee backwards compatibility. + +For any questions please ask the current port +developers. + +Current developer: +Raphael Neider + +Other people to contact: +Vangelis Rokas +Scott Dattalo + +====================================================================== +====================================================================== +2006-Mar-14 Vangelis Rokas +1. Added enviroment variable 'PIC16_PACKED_BITFIELDS' which agressively +packs bitfields in structures + +2005-Mar-23 Vangelis Rokas +1. I have added some optimizations that are controlled via enviroment +variables to allow checking. Later these will be either enabled globally +or controlled by command line options. The variables are: + a. OPTIMIZE_BITFIELD_POINTER_GET : optimizes bit field pointer reads + + b. NO_REG_OPT : there is no register optimization performed by pCode + optimizer + + + +2004-Oct-29 Vangelis Rokas +1. Function parameters are passed now all via stack. This might +lower performance, but some issues are solved this way. Later +we can enable passing through WREG,PRODL,PRODH,FSR0L by implementing +specific pragmas + + +2004-Sep-27 Vangelis Rokas +1. Function parameters have been extended to cover functions with +variable arguments. Now function parameters follow the rules below: + + a) void foo(long a, int b, char c) + void foo(long a, int b, char c) reentrant + +Stack layout: c, b+1, b, a+3, a+2, a+1 and WREG = a + + b) prototype: void foo(long a, int b, ...) + + example: foo(0xaaffeedd, 0xbbcc, 0x4455, 0x7788); + +Stack layout: 0x77, 0x88, 0x44, 0x55, 0xbb, 0xcc, 0xaa, 0xff, 0xee, 0xdd + +WREG is not used in functions with variable arguments so that the address +of the first parameter can be taken. + + + + +2004-Sep-24 Vangelis Rokas +1. Began implementation of generic pointers, current specs are: + 0x0 xxxxxxx -> code pointer + 0x8 xxxxxxx -> data pointer + 0x4 xxxxxxx -> eeprom pointer (currently unimplemented) + + +2004-Aug-30 Vangelis Rokas +1. A few months ago Hans Dorn had the idea to support general pointer +for accessing all code, eeprom data and data ram memory. These pointers +would have 3 bytes of size (24-bits), of which only the 21 lower bits +would be useful. The rest of them could be used to indicate the pointer +type. I.e. +0x4xxxxxxx would mean code pointer +0x8xxxxxxx would mean eeprom pointer +0xcxxxxxxx would mean data ram pointer + +The implementatio of such pointers needs a lot of work and general +reform of pointer access, data initializing functions and the writing +of support functions for decoding their type. + +The implementation of such pointers along with the implementation of +a more SDCC like stack access system will allow the writing of variable +arguments functions like printf etc... Also one set of functions will be +able to handle all data types without having to worry about where they +are placed. + + +2004-May-23 Vangelis Rokas + +1. The improvement of the port has been stalled a bit. But, struct/union +SFR headers are ready for the PIC18F[45][45][28] chips along with their +respective sources. + +2. The genCmp function should be rewritten from scratch. + +3. The internal helper functions for char/int/long/float arithmetic +now compile and will be placed in the appropriate directory under device/ + + + +2004-Feb-20 Vangelis Rokas +Major update with many bugfixes. + +1. The most of the pic16 regression tests (former pic regression tests) pass +successfully. Many thanks to Hans Dorn who did a great job with the +arithmetic, shift and pointer functions. + +2. Bit fields now work properly. + +3. Stack is permanently enabled. Command argument -pstack-enable is deleted +and no more recognized by the port. + + +2004-Feb-07 Vangelis Rokas + +1. Fixed a bug so that compiler allocated internal registered, will +be shared along multiple sources via '.registers' section placed +in absolute data memory address 0x0000. Registers 0x00 to 0x7f are +considered as internal since they can be used for fast access. + + + +2004-Jan-11 Vangelis Rokas + +1. Compiling +The current release of the port can produce object code which is not +completely bug free. To use the new features the user should enable them via +command line arguments. A sane set of command arguments that I use to test +programs is: + +- debug options + --debug enable sdcc debug information + --debug-xtra enable pic16 port debug information (most useful) + --debug-ralloc enable register allocator debug messages + --pcode-verbose enable verbose pcode generator messages + +- port options + --pno-banksel disable banksel directives for the assembler + --pomit-config-words does not emit configuration instruction in + the translation This is useful when copmiling + multiple sources, when you do not want multiple + config instructions in the end file + --pomit-ivt disables the dumping of the interrupt vector tables + in the translation for the same reasons as above + --penable-stack enables stack support. This option uses stack to + pass function arguments, and reuses registers between + functions by saving the registers used in the function + on the stack + +- compiler options + --all-callee-saves you may omit this options as the port + enables it by default, all functions are currently + compiled as reentrant and they are marked as + callee-saves + --no-peep peephole optimizer is better to be switched off, + because it behaves strangely in some cases + --fomit-frame-pointer this omits frame pointer in functions that + don't use registers (maybe changed later, not + important) + + +2. Functions +The current implementation puts every function in its own code section in +PIC's program memory. This may not be the standard, but I think its more +flexible. + +3. Pragmas +Since SDCC is goind for a release, its better to document pragmas available. + +3.1. code +The 'code' pragma emits a function's code at a specific address in program +memory. Currently it is only used for functions. +Syntax: +#pragma code [function_name] [address] + +3.2. stack +The 'stack' pragma initializes the stack/frame pointer at an address of the +data ram other than the default (which is the end of the available data ram) +Synatx: +#pragma stack [address] + +3.3. maxram +The 'maxram' pragma sets maximum data ram of the device. Currently is not +used at all, but it may be useful in the future when devices with external +memory will be supported. +Syntax: +#pragma maxram [max_address] + + +4. Internal compiler functions +Internal SDCC functions like, __fsmul, etc... are currently supported by the +port, but the libraries for the pic16 port are not yet ready. So one cannot +use long and float variables. + + +5. Special Function Registers (SFRs) +The processor SFRs are not anymore declared in any header file. The user can +define by himself all the needed SFR's. The code to that is: + +sfr at [sfr_address] [sfr_name]; + +Where sfr_address is the SFR address in the data ram, and sfr_name is the +name of the SFR. i.e.: + +sfr at 0xf80 PORTA; diff --git a/src/pic16/device.c b/src/pic16/device.c new file mode 100644 index 0000000..45b17be --- /dev/null +++ b/src/pic16/device.c @@ -0,0 +1,1133 @@ +/*------------------------------------------------------------------------- + device.c - Accomodates subtle variations in PIC16 devices + + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + PIC16 port: + Copyright (C) 2002, Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" +#include "dbuf_string.h" + +#include "main.h" +#include "pcode.h" +#include "ralloc.h" +#include "device.h" + +void pic16_printIval (symbol * sym, sym_link * type, initList * ilist, char ptype, void *p); +extern void pic16_pCodeConstString (char *name, const char *value, unsigned length); + +stats_t statistics = { 0, 0, 0, 0 }; + +#define DEVICE_FILE_NAME "pic16devices.txt" + +static PIC16_device default_device = { + { "p18f452", "18f452", "pic18f452", "f452" }, + 0x600, + 0x80, + { /* configuration words */ + 0x300001, 0x30000d, + { { 0x27, 0, 0xff } /* 1 */ , { 0x0f, 0, 0xff } /* 2 */ , + { 0x0f, 0, 0xff } /* 3 */ , { -1 , 0, 0xff } /* 4 */ , + { 0x01, 0, 0xff } /* 5 */ , { 0x85, 0, 0xff } /* 6 */ , + { -1 , 0, 0xff } /* 7 */ , { 0x0f, 0, 0xff } /* 8 */ , + { 0xc0, 0, 0xff } /* 9 */ , { 0x0f, 0, 0xff } /* a */ , + { 0xe0, 0, 0xff } /* b */ , { 0x0f, 0, 0xff } /* c */ , + { 0x40, 0, 0xff } /* d */ } + }, + { /* ID locations */ + 0x200000, 0x200007, + { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, + { 0, 0 }, { 0, 0 }, { 0, 0 } } + }, + 0, + NULL +}; + +PIC16_device *pic16 = &default_device; +static PIC16_device *devices = NULL; + +extern set *includeDirsSet; +extern set *userIncDirsSet; + +extern char *iComments2; + +void +pic16_dump_equates (FILE *of, set *equs) +{ + reg_info *r; + + r = setFirstItem (equs); + if (!r) + return; + + fprintf (of, "\n%s", iComments2); + fprintf (of, ";\tEquates to used internal registers\n"); + fprintf (of, "%s", iComments2); + + for (; r; r = setNextItem (equs)) + { + fprintf (of, "%s\tequ\t0x%03x\n", r->name, r->address); + } // for +} + + +void +pic16_dump_access (FILE *of, set *section) +{ + reg_info *r; + + r = setFirstItem (section); + if (!r) + return; + + fprintf (of, "%s", iComments2); + fprintf (of, ";\tAccess bank symbols\n"); + fprintf (of, "%s", iComments2); + + fprintf (of, "\tudata_acs\n"); + for (; r; r = setNextItem (section)) + { + fprintf (of, "%s\tres\t%d\n", r->name, r->size); + statistics.adsize += r->size; + } // for +} + +int +regCompare (const void *a, const void *b) +{ + const reg_info *const *i = a; + const reg_info *const *j = b; + + /* Sort primarily by the address ... */ + if ((*i)->address > (*j)->address) + return (1); + + if ((*i)->address < (*j)->address) + return (-1); + + /* ... and secondarily by size. */ + /* Register size sorting may have strange results, use with care! */ + if ((*i)->size > (*j)->size) + return (1); + + if ((*i)->size < (*j)->size) + return (-1); + + /* Finally, if in same address and same size, sort by name. */ + return (strcmp ((*i)->name, (*j)->name)); +} + +int +symCompare (const void *a, const void *b) +{ + const symbol *const *i = a; + const symbol *const *j = b; + + /* Sort primarily by the address ... */ + if (SPEC_ADDR ((*i)->etype) > SPEC_ADDR ((*j)->etype)) + return (1); + + if (SPEC_ADDR ((*i)->etype) < SPEC_ADDR ((*j)->etype)) + return (-1); + + /* ... and secondarily by size. */ + /* Register size sorting may have strange results, use with care! */ + if (getSize ((*i)->etype) > getSize ((*j)->etype)) + return (1); + + if (getSize ((*i)->etype) < getSize ((*j)->etype)) + return (-1); + + /* Finally, if in same address and same size, sort by name. */ + return (strcmp ((*i)->rname, (*j)->rname)); +} + +void +pic16_dump_usection (FILE *of, set *section, int fix) +{ + static int abs_usection_no = 0; + static unsigned int usection_no = 0; + reg_info *r, *rprev; + unsigned int init_addr, i; + reg_info **rlist; + reg_info *r1; + + /* put all symbols in an array */ + if (!elementsInSet (section)) + return; + + rlist = Safe_calloc (elementsInSet (section), sizeof (reg_info *)); + r = rlist[0]; + i = 0; + for (rprev = setFirstItem (section); rprev; rprev = setNextItem (section)) + { + rlist[i] = rprev; + i++; + } // for + + if (!i) + { + if (rlist) + Safe_free (rlist); + + return; + } // if + + /* sort symbols according to their address */ + qsort (rlist, i, sizeof (reg_info *), regCompare); + + if (!fix) + { +#define EMIT_SINGLE_UDATA_SECTION 0 +#if EMIT_SINGLE_UDATA_SECTION + fprintf (of, "\n\n\tudata\n"); + for (r = setFirstItem (section); r; r = setNextItem (section)) + { + fprintf (of, "%s\tres\t%d\n", r->name, r->size); + statistics.udsize += r->size; + } // for +#else + for (r = setFirstItem (section); r; r = setNextItem (section)) + { + //fprintf (of, "\nudata_%s_%s\tudata\n", moduleName, r->name); + fprintf (of, "\nudata_%s_%u\tudata\n", moduleName, usection_no++); + fprintf (of, "%s\tres\t%d\n", r->name, r->size); + statistics.udsize += r->size; + } // for +#endif + } + else + { + unsigned int j = 0; + unsigned int prev_size = 0; + + rprev = NULL; + init_addr = (rlist[j]->address & 0x0FFF); // warning(s) emitted below + fprintf (of, "\n\nustat_%s_%02d\tudata\t0X%04X\n", moduleName, abs_usection_no++, (init_addr & 0x0FFF)); + + for (j = 0; j < i; j++) + { + r = rlist[j]; + r1 = NULL; + if (j < i - 1) + r1 = rlist[j + 1]; + + init_addr = (r->address & 0x0FFF); + if (init_addr != r->address) + { + fprintf (stderr, "%s: WARNING: Changed address of pinned variable %s from 0x%x to 0x%x\n", + moduleName, r->name, r->address, init_addr); + } // if + + if ((rprev && (init_addr != ((rprev->address & 0x0FFF) + prev_size)))) + fprintf (of, "\n\nustat_%s_%02d\tudata\t0X%04X\n", moduleName, abs_usection_no++, init_addr); + + /* XXX: Does not handle partial overlap correctly. */ + if (r1 && (init_addr == (r1->address & 0x0FFF))) + { + prev_size = 0; + fprintf (of, "%-15s\n", r->name); + } + else + { + prev_size = r->size; + fprintf (of, "%-15s\tres\t%d\n", r->name, prev_size); + statistics.udsize += prev_size; + } + + rprev = r; + } // for + } // if + + Safe_free (rlist); +} + +void +pic16_dump_gsection (FILE *of, set *sections) +{ + reg_info *r; + sectName *sname; + + for (sname = setFirstItem (sections); sname; sname = setNextItem (sections)) + { + if (!strcmp (sname->name, "access")) + continue; + + fprintf (of, "\n\n%s\tudata\n", sname->name); + + for (r = setFirstItem (sname->regsSet); r; r = setNextItem (sname->regsSet)) + { +#if 0 + fprintf (stderr, "%s:%d emitting variable %s for section %s (%p)\n", + __FILE__, __LINE__, r->name, sname->name, sname); +#endif + fprintf (of, "%s\tres\t%d\n", r->name, r->size); + statistics.udsize += r->size; + } // for + } // for +} + +void +pic16_dump_isection (FILE *of, set *section, int fix) +{ + static int abs_isection_no = 0; + symbol *s, *sprev; + unsigned int init_addr, i; + symbol **slist; + + /* put all symbols in an array */ + if (!elementsInSet (section)) + return; + + slist = Safe_calloc (elementsInSet (section), sizeof (symbol *)); + s = slist[0]; + i = 0; + for (sprev = setFirstItem (section); sprev; sprev = setNextItem (section)) + { + slist[i] = sprev; + i++; + } // for + + if (!i) + { + if (slist) + Safe_free (slist); + + return; + } // if + + /* sort symbols according to their address */ + qsort (slist, i, sizeof (symbol *), symCompare); + + pic16_initDB (); + + if (!fix) + { + fprintf (of, "\n\n\tidata\n"); + for (s = setFirstItem (section); s; s = setNextItem (section)) + { + if (s->ival) + { + fprintf (of, "%s", s->rname); + pic16_printIval (s, s->type, s->ival, 'f', (void *)of); + pic16_flushDB ('f', (void *)of); + } + else + { + if (IS_ARRAY (s->type) && IS_CHAR (s->type->next) + && SPEC_CVAL (s->etype).v_char) + { + //fprintf (stderr, "%s:%d printing code string from %s\n", __FILE__, __LINE__, s->rname); + pic16_pCodeConstString (s->rname , SPEC_CVAL (s->etype).v_char, getSize (s->type)); + } + else + { + assert (0); + } // if + } // if + } // for + } + else + { + unsigned int j = 0; + + sprev = NULL; + init_addr = SPEC_ADDR (slist[j]->etype); + fprintf (of, "\n\nistat_%s_%02d\tidata\t0X%04X\n", moduleName, abs_isection_no++, init_addr); + + for (j = 0; j < i; j++) + { + s = slist[j]; + init_addr = SPEC_ADDR (s->etype); + + if (sprev && (init_addr > (SPEC_ADDR (sprev->etype) + getSize (sprev->etype)))) + fprintf(of, "\nistat_%s_%02d\tidata\t0X%04X\n", moduleName, abs_isection_no++, init_addr); + + if (s->ival) + { + fprintf (of, "%s", s->rname); + pic16_printIval (s, s->type, s->ival, 'f', (void *)of); + pic16_flushDB ('f', (void *)of); + } + else + { + if (IS_ARRAY (s->type) && IS_CHAR (s->type->next) + && SPEC_CVAL (s->etype).v_char) + { + //fprintf (stderr, "%s:%d printing code string from %s\n", __FILE__, __LINE__, s->rname); + pic16_pCodeConstString (s->rname , SPEC_CVAL (s->etype).v_char, getSize (s->type)); + } + else + { + assert (0); + } // if + } // if + + sprev = s; + } // for + } // if + + Safe_free (slist); +} + +void +pic16_dump_int_registers (FILE *of, set *section) +{ + reg_info *r, *rprev; + int i; + reg_info **rlist; + + /* put all symbols in an array */ + if (!elementsInSet (section)) + return; + + rlist = Safe_calloc (elementsInSet (section), sizeof (reg_info *)); + r = rlist[0]; + i = 0; + for (rprev = setFirstItem (section); rprev; rprev = setNextItem (section)) + { + rlist[i] = rprev; + i++; + } // for + + if (!i) + { + if (rlist) + Safe_free (rlist); + + return; + } // if + + /* sort symbols according to their address */ + qsort (rlist, i, sizeof (reg_info *), regCompare); + + fprintf (of, "\n\n; Internal registers\n"); + + fprintf (of, "%s\tudata_ovr\t0x0000\n", ".registers"); + for (r = setFirstItem (section); r; r = setNextItem (section)) + { + fprintf (of, "%s\tres\t%d\n", r->name, r->size); + statistics.intsize += r->size; + } // for + + Safe_free (rlist); +} + +/** + * Find the device structure for the named device. + * Consider usind pic16_find_device() instead! + * + * @param name + * a name for the desired device + * @param head + * a pointer to the head of the list of devices + * @return + * a pointer to the structure for the desired + * device, or NULL + */ +static PIC16_device * +find_in_list(const char *name, PIC16_device *head) +{ + int i; + + while (head) { + for (i = 0; i < 4; i++) { + if (0 == STRCASECMP(head->name[i], name)) { + return (head); + } // if + } // for + + head = head->next; + } // while + + return (NULL); +} + +/** + * Print a list of supported devices. + * If --verbose was given, also emit key characteristics (memory size, + * access bank split point, address range of SFRs and config words). + * + * @param head + * a pointer to the head of the list of devices + */ +static void +pic16_list_devices(PIC16_device *head) +{ + int i = 0; + + if (options.verbose) { + printf("device RAM split config words\n"); + } // if + while (head) { + printf("%-10s ", head->name[0]); + if (options.verbose) { + printf("%5d 0x%02x 0x%06x..0x%06x\n", + head->RAMsize, + head->acsSplitOfs, + head->cwInfo.confAddrStart, + head->cwInfo.confAddrEnd); + } else { + i++; + if (0 == (i % 6)) { + printf("\n"); + } // if + } // if + head = head->next; + } // while + printf("\n"); +} + +/** + * Read a single line from the given file. + * + * @param file + * a pointer to the open file to read + * @return + * a pointer to a malloc'ed copy of the (next) line, or NULL + */ +static char * +get_line (FILE *file) +{ + static struct dbuf_s dbuf; + static int initialized = 0; + + if (!initialized) + { + dbuf_init (&dbuf, 129); + initialized = 1; + } + else + dbuf_set_length (&dbuf, 0); + + + if (dbuf_getline (&dbuf, file) != 0) + { + dbuf_chomp (&dbuf); + /* (char *) type cast is an ugly hack since pic16_find_device() modifies the buffer */ + return (char *)dbuf_get_buf (&dbuf); + } + else + { + dbuf_destroy(&dbuf); + initialized = 0; + return NULL; + } +} + +/** + * Truncate the given string in place (!) at the first '#' character (if any). + * + * @param line + * a pointer to the string to truncate + * @return + * a pointer to the truncated string (i.e., line) + */ +static char * +strip_comment (char *line) +{ + char *l = line; + char c; + + if (!line) + { + return (line); + } // if + + while (0 != (c = *l)) + { + if ('#' == c) + { + *l = 0; + return (line); + } // if + l++; + } // while + + return (line); +} + +/** + * Report errors in the device specification. + * + * @param msg + * a pointer to the detailed message + */ +#define SYNTAX(msg) do { \ + fprintf(stderr, "%s:%d: Syntax error: %s\n", \ + DEVICE_FILE_NAME, lineno, msg); \ +} while (0) + +/** + * Locate and read in the device specification (if required) and + * return the device structure for the named device. + * + * @param name + * a pointer to the name of the desired device + * @return + * a pointer to the device structure, or NULL + */ +static PIC16_device * +pic16_find_device(const char *name) +{ + const char *path; + char buffer[PATH_MAX]; + char *line, *key; + const char *sep = " \t\n\r"; + FILE *f = NULL; + PIC16_device *d = NULL, *template; + PIC16_device *head = NULL, *tail = NULL; + set *_sets[] = { userIncDirsSet, includeDirsSet }; + set **sets = &_sets[0]; + int lineno = 0; + int res, i; + int val[4]; + + if (!devices) + { + //printf("%s: searching %s\n", __func__, DEVICE_FILE_NAME); + + // locate the specification file in the include search paths + for (i = 0; (NULL == f) && (i < 2); i++) + { + for (path = setFirstItem(sets[i]); + (NULL == f) && path; + path = setNextItem(sets[i])) + { + SNPRINTF(&buffer[0], PATH_MAX, "%s%s%s", + path, DIR_SEPARATOR_STRING, DEVICE_FILE_NAME); + //printf("%s: checking %s\n", __func__, &buffer[0]); + f = fopen(&buffer[0], "r"); + } // for + } // for + } // if + + if (devices) + { + // list already set up, nothing to do + } + else if (NULL == f) + { + fprintf(stderr, "ERROR: device list %s not found, specify its path via -I\n", + DEVICE_FILE_NAME); + d = &default_device; + } + else + { + // parse the specification file and construct a linked list of + // supported devices + d = NULL; + while (NULL != (line = get_line(f))) + { + strip_comment(line); + //printf("%s: read %s\n", __func__, line); + lineno++; + key = strtok(line, sep); + if (!key) + { + // empty line---ignore + } + else if (0 == strcmp(key, "name")) + { + // name %s + if (d) + { + if (tail) + { + tail->next = d; + } + else + { + head = d; + } // if + tail = d; + d = NULL; + } // if + + res = sscanf(&line[1 + strlen(key)], " %16s", &buffer[3]); + if ((1 < res) || (3 > strlen(&buffer[3]))) + { + SYNTAX(" (e.g., 18f452) expected."); + } + else + { + d = Safe_alloc(sizeof(PIC16_device)); + + // { "p18f452", "18f452", "pic18f452", "f452" } + buffer[0] = 'p'; + buffer[1] = 'i'; + buffer[2] = 'c'; + d->name[3] = Safe_strdup(&buffer[5]); + d->name[2] = Safe_strdup(&buffer[0]); + d->name[1] = Safe_strdup(&buffer[3]); + buffer[2] = 'p'; + d->name[0] = Safe_strdup(&buffer[2]); + } // if + } + else if (0 == strcmp(key, "using")) + { + // using %s + res = sscanf(&line[1 + strlen(key)], " %16s", &buffer[0]); + if ((1 < res) || (3 > strlen(&buffer[3]))) + { + SYNTAX(" (e.g., 18f452) expected."); + } + else + { + template = find_in_list(&buffer[0], head); + if (!template) + { + SYNTAX(" (e.g., 18f452) expected."); + } + else + { + memcpy(&d->RAMsize, &template->RAMsize, + ((char *)&d->next) - ((char *)&d->RAMsize)); + } // if + } // if + } + else if (0 == strcmp(key, "ramsize")) + { + // ramsize %i + res = sscanf(&line[1 + strlen(key)], " %i", &val[0]); + if (res < 1) + { + SYNTAX(" (e.g., 256) expected."); + } + else + { + d->RAMsize = val[0]; + } // if + } + else if (0 == strcmp(key, "split")) + { + // split %i + res = sscanf(&line[1 + strlen(key)], " %i", &val[0]); + if (res < 1) + { + SYNTAX(" (e.g., 0x80) expected."); + } + else + { + d->acsSplitOfs = val[0]; + } // if + } + else if (0 == strcmp(key, "configrange")) + { + // configrange %i %i + res = sscanf(&line[1 + strlen(key)], " %i %i", + &val[0], &val[1]); + if (res < 2) + { + SYNTAX(" (e.g., 0xf60 0xfff) expected."); + } + else + { + d->cwInfo.confAddrStart = val[0]; + d->cwInfo.confAddrEnd = val[1]; + } // if + } + else if (0 == strcmp(key, "configword")) + { + // configword %
i %i %i [%i] + res = sscanf(&line[1 + strlen(key)], " %i %i %i %i", + &val[0], &val[1], &val[2], &val[3]); + if (res < 3) + { + SYNTAX("
[] (e.g., 0x200001 0x0f 0x07) expected."); + } + else + { + val[0] -= d->cwInfo.confAddrStart; + if ((val[0] < 0) + || (val[0] > (d->cwInfo.confAddrEnd - d->cwInfo.confAddrStart)) + || (val[0] >= CONFIGURATION_WORDS)) + { + SYNTAX("address out of bounds."); + } + else + { + d->cwInfo.crInfo[val[0]].mask = val[1]; + d->cwInfo.crInfo[val[0]].value = val[2]; + d->cwInfo.crInfo[val[0]].andmask = 0; + if (res >= 4) + { + // apply extra mask (e.g., to disable XINST) + d->cwInfo.crInfo[val[0]].andmask = val[3]; + } // if + } // if + } // if + } + else if (0 == strcmp(key, "idlocrange")) + { + // idlocrange %i %i + res = sscanf(&line[1 + strlen(key)], " %i %i", + &val[0], &val[1]); + if (res < 2) + { + SYNTAX(" (e.g., 0xf60 0xfff) expected."); + } + else + { + d->idInfo.idAddrStart = val[0]; + d->idInfo.idAddrEnd = val[1]; + } // if + } + else if (0 == strcmp(key, "idword")) + { + // idword %
i %i + res = sscanf(&line[1 + strlen(key)], " %i %i", + &val[0], &val[1]); + if (res < 2) + { + SYNTAX("
(e.g., 0x3fffff 0x00) expected."); + } + else + { + val[0] -= d->idInfo.idAddrStart; + if ((val[0] < 0) + || (val[0] > (d->idInfo.idAddrEnd - d->idInfo.idAddrStart)) + || (val[0] >= IDLOCATION_BYTES)) + { + SYNTAX("address out of bounds."); + } + else + { + d->idInfo.irInfo[val[0]].value = val[1]; + } // if + } // if + } + else if (0 == strcmp(key, "XINST")) + { + // XINST %i + res = sscanf(&line[1 + strlen(key)], " %i", &val[0]); + if (res < 1) + { + SYNTAX(" (e.g., 1) expected."); + } + else + { + d->xinst = val[0]; + } // if + } + else + { + printf("%s: Invalid keyword in %s ignored: %s\n", + __func__, DEVICE_FILE_NAME, key); + } // if + } // while + + if (d) + { + if (tail) + { + tail->next = d; + } + else + { + head = d; + } // if + tail = d; + d = NULL; + } // if + + devices = head; + + fclose(f); + } // if + + d = find_in_list(name, devices); + if (!d) + { + d = &default_device; + } // if + + return (d); +} + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ +void pic16_init_pic(const char *pic_type) +{ + pic16 = pic16_find_device(pic_type); + + if (&default_device == pic16) { + if (pic_type) { + fprintf(stderr, "'%s' was not found.\n", pic_type); + } else { + fprintf(stderr, "No processor has been specified (use -pPROCESSOR_NAME)\n"); + } // if + + if (devices) { + fprintf(stderr,"Valid devices are (use --verbose for more details):\n"); + pic16_list_devices(devices); + } // if + exit(EXIT_FAILURE); + } // if +} + +/*-----------------------------------------------------------------* + * char *pic16_processor_base_name(void) - Include file is derived from this. + *-----------------------------------------------------------------*/ + +const char *pic16_processor_base_name(void) +{ + if(!pic16) + return NULL; + + return pic16->name[0]; +} + +#define DEBUG_CHECK 0 + +/* + * return 1 if register wasn't found and added, 0 otherwise + */ +int checkAddReg(set **set, reg_info *reg) +{ + reg_info *tmp; + + + if(!reg)return 0; +#if DEBUG_CHECK + fprintf(stderr, "%s: about to insert REGister: %s ... ", __FUNCTION__, reg->name); +#endif + + for(tmp = setFirstItem(*set); tmp; tmp = setNextItem(*set)) { + if(!strcmp(tmp->name, reg->name))break; + } + + if(!tmp) { + addSet(set, reg); +#if DEBUG_CHECK + fprintf(stderr, "added\n"); +#endif + return 1; + } + +#if DEBUG_CHECK + fprintf(stderr, "already added\n"); +#endif + return 0; +} + +int checkAddSym(set **set, symbol *sym) +{ + symbol *tmp; + + if(!sym)return 0; +#if DEBUG_CHECK + fprintf(stderr, "%s: about to add SYMbol: %s ... ", __FUNCTION__, sym->name); +#endif + + for(tmp = setFirstItem( *set ); tmp; tmp = setNextItem(*set)) { + if(!strcmp(tmp->name, sym->name))break; + } + + if(!tmp) { + addSet(set, sym); +#if DEBUG_CHECK + fprintf(stderr, "added\n"); +#endif + return 1; + } + +#if DEBUG_CHECK + fprintf(stderr, "already added\n"); +#endif + + return 0; +} + +int checkSym(set *set, symbol *sym) +{ + symbol *tmp; + + if(!sym)return 0; + +#if DEUG_CHECK + fprintf(stderr, "%s: about to search for SYMbol: %s ... ", __FUNCTION__, sym->name); +#endif + + for(tmp = setFirstItem( set ); tmp; tmp = setNextItem( set )) { + if(!strcmp(tmp->name, sym->name))break; + } + + if(!tmp) { +#if DEBUG_CHECK + fprintf(stderr, "not found\n"); +#endif + return 0; + } + +#if DEBUG_CHECK + fprintf(stderr, "found\n"); +#endif + + return 1; +} + +/*-----------------------------------------------------------------* + * void pic16_groupRegistersInSection - add each register to its * + * corresponding section * + *-----------------------------------------------------------------*/ +void pic16_groupRegistersInSection(set *regset) +{ + reg_info *reg; + sectSym *ssym; + int docontinue=0; + + for(reg=setFirstItem(regset); reg; reg = setNextItem(regset)) { + +#if 0 + fprintf(stderr, "%s:%d group registers in section, reg: %s (used: %d, %p)\n", + __FILE__, __LINE__, reg->name, reg->wasUsed, reg); +#endif + if((reg->wasUsed + && !(reg->regop && SPEC_EXTR(OP_SYM_ETYPE(reg->regop)))) + ) { + + /* avoid grouping registers that have an initial value, + * they will be added later in idataSymSet */ + if(reg->regop && (OP_SYMBOL(reg->regop)->ival && !OP_SYMBOL(reg->regop)->level)) + continue; + +#if 0 + fprintf(stderr, "%s:%d register %s alias:%d fix:%d ival=%i level=%i code=%i\n", + __FILE__, __LINE__, reg->name, reg->alias, reg->isFixed, + (reg->regop?(OP_SYMBOL(reg->regop)->ival?1:0):-1), + (reg->regop?(OP_SYMBOL(reg->regop)->level):-1), + (reg->regop?(IS_CODE(OP_SYM_ETYPE(reg->regop))):-1) ); +#endif + + docontinue=0; + for(ssym=setFirstItem(sectSyms);ssym;ssym=setNextItem(sectSyms)) { + if(!strcmp(ssym->name, reg->name)) { +// fprintf(stderr, "%s:%d section found %s (%p) with var %s\n", +// __FILE__, __LINE__, ssym->section->name, ssym->section, ssym->name); + if(strcmp(ssym->section->name, "access")) { + addSet(&ssym->section->regsSet, reg); + docontinue=1; + break; + } else { + docontinue=0; + reg->accessBank = 1; + break; + } + } + } + + if(docontinue)continue; + +// fprintf(stderr, "%s:%d reg: %s\n", __FILE__, __LINE__, reg->name); + + if(reg->alias == 0x80) { + checkAddReg(&pic16_equ_data, reg); + } else + if(reg->isFixed) { + checkAddReg(&pic16_fix_udata, reg); + } else + if(!reg->isFixed) { + if(reg->pc_type == PO_GPR_TEMP) + checkAddReg(&pic16_int_regs, reg); + else { + if(reg->accessBank) { + if(reg->alias != 0x40) + checkAddReg(&pic16_acs_udata, reg); + } else + checkAddReg(&pic16_rel_udata, reg); + } + } + } + } +} + + +/*-----------------------------------------------------------------* + * void pic16_assignConfigWordValue(int address, int value) + * + * All high performance RISC CPU PICs have seven config word starting + * at address 0x300000. + * This routine will assign a value to that address. + * + *-----------------------------------------------------------------*/ +void +pic16_assignConfigWordValue(int address, unsigned int value) +{ + int i; + + for (i = 0; i < pic16->cwInfo.confAddrEnd - pic16->cwInfo.confAddrStart + 1; i++) + { + if ((address == pic16->cwInfo.confAddrStart + i) + && (pic16->cwInfo.crInfo[i].mask != -1) + && (pic16->cwInfo.crInfo[i].mask != 0)) + { + +#if 0 + fprintf(stderr, "setting location 0x%x to value 0x%x, mask: 0x%x, test: 0x%x\n", + pic16->cwInfo.confAddrStart + i, + (~value) & 0xff, + pic16->cwInfo.crInfo[i].mask, + (pic16->cwInfo.crInfo[i].mask) & (~value)); +#endif + +#if 0 + if ((((pic16->cwInfo.crInfo[i].mask) & (~value)) & 0xff) != ((~value) & 0xff)) + { + fprintf(stderr, "%s:%d a wrong value has been given for configuration register 0x%x\n", + __FILE__, __LINE__, address); + return; + } // if +#endif + + pic16->cwInfo.crInfo[i].value = (value & 0xff); + if (pic16->cwInfo.crInfo[i].andmask + && ((value & 0xff) != (value & 0xff & pic16->cwInfo.crInfo[i].andmask))) + { + // apply andmask if effective + printf ("INFO: changing configuration word at 0x%x from 0x%x to 0x%x due to %s\n", + address, + (value & 0xff), + (value & 0xff & pic16->cwInfo.crInfo[i].andmask), + DEVICE_FILE_NAME); + pic16->cwInfo.crInfo[i].value &= pic16->cwInfo.crInfo[i].andmask; + } // if + pic16->cwInfo.crInfo[i].emit = 1; + return; + } // if + } // for +} + +void +pic16_assignIdByteValue(int address, char value) +{ + int i; + + for (i = 0; i < pic16->idInfo.idAddrEnd - pic16->idInfo.idAddrStart + 1; i++) + { + if (address == pic16->idInfo.idAddrStart + i) + { + pic16->idInfo.irInfo[i].value = value; + pic16->idInfo.irInfo[i].emit = 1; + } // if + } // for +} diff --git a/src/pic16/device.h b/src/pic16/device.h new file mode 100644 index 0000000..cdfbba0 --- /dev/null +++ b/src/pic16/device.h @@ -0,0 +1,147 @@ +/*------------------------------------------------------------------------- + + device.c - Accomodates subtle variations in PIC16 devices + + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* + PIC device abstraction + + There are dozens of variations of PIC microcontrollers. This include + file attempts to abstract those differences so that SDCC can easily + deal with them. +*/ + +#ifndef __DEVICE_H__ +#define __DEVICE_H__ + +#define CONFIGURATION_WORDS 20 +#define IDLOCATION_BYTES 20 + +typedef struct { + unsigned int mask; + int emit; + unsigned int value; + unsigned int andmask; +} configRegInfo_t; + +typedef struct { + int confAddrStart; /* starting address */ + int confAddrEnd; /* ending address */ + configRegInfo_t crInfo[ CONFIGURATION_WORDS ]; +} configWordsInfo_t; + +typedef struct { + unsigned char emit; + unsigned char value; +} idRegInfo_t; + +typedef struct { + int idAddrStart; /* starting ID address */ + int idAddrEnd; /* ending ID address */ + idRegInfo_t irInfo[ IDLOCATION_BYTES ]; +} idBytesInfo_t; + + +#define PROCESSOR_NAMES 4 +/* Processor unique attributes */ +typedef struct PIC16_device { + char *name[PROCESSOR_NAMES]; /* aliases for the processor name */ + /* RAMsize *must* be the first item to copy for 'using' */ + unsigned int RAMsize; /* size of Data RAM - VR 031120 */ + unsigned int acsSplitOfs; /* access bank split offset */ + configWordsInfo_t cwInfo; /* configuration words info */ + idBytesInfo_t idInfo; /* ID Locations info */ + int xinst; /* device supports XINST */ + /* next *must* be the first field NOT being copied via 'using' */ + struct PIC16_device *next; /* linked list */ +} PIC16_device; + +extern PIC16_device *pic16; + +/* Given a pointer to a register, this macro returns the bank that it is in */ +#define REG_ADDR(r) ((r)->isBitField ? (((r)->address)>>3) : (r)->address) + +#define OF_LR_SUPPORT 0x00000001 +#define OF_NO_OPTIMIZE_GOTO 0x00000002 +#define OF_OPTIMIZE_CMP 0x00000004 +#define OF_OPTIMIZE_DF 0x00000008 + +typedef struct { + int no_banksel; + int opt_banksel; + int omit_configw; + int omit_ivt; + int leave_reset; + int stack_model; + int ivt_loc; + int nodefaultlibs; + int dumpcalltree; + char *crt_name; + int no_crt; + int ip_stack; + unsigned long opt_flags; + int gstack; + unsigned int debgen; + int xinst; + int no_warn_non_free; +} pic16_options_t; + +extern pic16_options_t pic16_options; + +#define STACK_MODEL_SMALL (pic16_options.stack_model == 0) +#define STACK_MODEL_LARGE (pic16_options.stack_model == 1) + +extern set *fix_idataSymSet; +extern set *rel_idataSymSet; + +#if 0 +/* This is an experimental code for #pragma inline + and is temporarily disabled for 2.5.0 release */ +extern set *asmInlineMap; +#endif /* 0 */ + +typedef struct { + unsigned long isize; + unsigned long adsize; + unsigned long udsize; + unsigned long idsize; + unsigned long intsize; +} stats_t; + +extern stats_t statistics; + +typedef struct pic16_config_options_s { + char *config_str; + struct pic16_config_options_s *next; +} pic16_config_options_t; + +extern pic16_config_options_t *pic16_config_options; + +/****************************************/ +const char *pic16_processor_base_name(void); +void pic16_assignConfigWordValue(int address, unsigned int value); +void pic16_assignIdByteValue(int address, char value); +int pic16_isREGinBank(reg_info *reg, int bank); +int pic16_REGallBanks(reg_info *reg); + +int checkAddReg(set **set, reg_info *reg); +int checkAddSym(set **set, symbol *reg); +int checkSym(set *set, symbol *reg); + +#endif /* __DEVICE_H__ */ diff --git a/src/pic16/gen.c b/src/pic16/gen.c new file mode 100644 index 0000000..3150a07 --- /dev/null +++ b/src/pic16/gen.c @@ -0,0 +1,11583 @@ +/*------------------------------------------------------------------------- + gen.c - source file for code generation for pic16 + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + PIC port: + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + PIC16 port: + Copyright (C) 2002, Martin Dubuc m.dubuc@rogers.com + Copyright (C) 2003-2006,Vangelis Rokas + Bug Fixes - Raphael Neider (2004,2005) + Bug Fixes - Borut Razem (2007) + Bug Fixes - Mauro Giachero (2008) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +#include +#include +#include +#include + +#include "common.h" +#include "ralloc.h" +#include "pcode.h" +#include "gen.h" +#include "genutils.h" +#include "device.h" +#include "main.h" +#include "glue.h" +#include "dbuf_string.h" + +/* The PIC port(s) do not need to distinguish between POINTER and FPOINTER. */ +#define PIC_IS_DATA_PTR(x) (IS_DATA_PTR(x) || IS_FARPTR(x)) +#define PIC_IS_FARPTR(x) (IS_DATA_PTR(x) || IS_FARPTR(x)) +#define PIC_IS_TAGGED(x) (IS_GENPTR(x) || IS_CODEPTR(x)) +#define IS_DIRECT(op) ((AOP_TYPE(op) == AOP_PCODE) && (AOP(op)->aopu.pcop->type == PO_DIR)) + +/* Wrapper to execute `code' at most once. */ +#define PERFORM_ONCE(id,code) do { static char id = 0; if (!id) { id = 1; code } } while (0) + +void pic16_genMult8X8_n (operand *, operand *, operand *); +extern void pic16_printpBlock (FILE * of, pBlock * pb); +static asmop *newAsmop (short type); +static pCodeOp *pic16_popRegFromString (char *str, int size, int offset, operand * op); +extern pCode *pic16_newpCodeAsmDir (const char *asdir, const char *argfmt, ...); +static void mov2fp (pCodeOp * dst, asmop * src, int offset); +static pCodeOp *pic16_popRegFromIdx (int rIdx); +static void genCritical (iCode * ic); +static void genEndCritical (iCode * ic); + +int pic16_labelOffset = 0; +extern int pic16_debug_verbose; + +extern set *externs; + +/* max_key keeps track of the largest label number used in + a function. This is then used to adjust the label offset + for the next function. +*/ +static int max_key = 0; +static int GpseudoStkPtr = 0; + +pCodeOp *pic16_popGetImmd (char *name, unsigned int offset, int index); + +const char *pic16_AopType (short type); + +void pic16_pushpCodeOp (pCodeOp * pcop); +void pic16_poppCodeOp (pCodeOp * pcop); + + +#define BYTEofLONG(l,b) ( (l>> (b<<3)) & 0xff) + +/* set the following macro to 1 to enable passing the + * first byte of functions parameters via WREG */ +#define USE_WREG_IN_FUNC_PARAMS 0 + + +/* this is the down and dirty file with all kinds of + kludgy & hacky stuff. This is what it is all about + CODE GENERATION for a specific MCU . some of the + routines may be reusable, will have to see */ +static char *zero = "#0x00"; +static char *one = "#0x01"; + + +/* + * Function return value policy (MSB-->LSB): + * 8 bits -> WREG + * 16 bits -> PRODL:WREG + * 24 bits -> PRODH:PRODL:WREG + * 32 bits -> FSR0L:PRODH:PRODL:WREG + * >32 bits -> on stack, and FSR0 points to the beginning + */ +char *fReturnpic16[] = { "WREG", "PRODL", "PRODH", "FSR0L" }; +int fReturnIdx[] = { IDX_WREG, IDX_PRODL, IDX_PRODH, IDX_FSR0L }; + +unsigned pic16_fReturnSizePic = 4; /* shared with ralloc.c */ +static char **fReturn = fReturnpic16; + +static char *accUse[] = { "WREG" }; + +static struct +{ + short accInUse; + short inLine; + short debugLine; + short nRegsSaved; + set *sendSet; + set *stackRegSet; + int usefastretfie; + bitVect *fregsUsed; /* registers used in function */ + bitVect *sregsAlloc; + set *sregsAllocSet; /* registers used to store stack variables */ + int stack_lat; /* stack offset latency */ + int resDirect; + int useWreg; /* flag when WREG is used to pass function parameter */ +} _G; + +extern struct dbuf_s *codeOutBuf; + +static unsigned char SLMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00 + }; + +static unsigned char SRMask[] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, + 0x07, 0x03, 0x01, 0x00 + }; + +static pBlock *pb; + +/*-----------------------------------------------------------------*/ +/* my_powof2(n) - If `n' is an integaer power of 2, then the */ +/* exponent of 2 is returned, otherwise -1 is */ +/* returned. */ +/* note that this is similar to the function `powof2' in SDCCsymt */ +/* if(n == 2^y) */ +/* return y; */ +/* return -1; */ +/*-----------------------------------------------------------------*/ +int +pic16_my_powof2 (unsigned long num) +{ + if (num) + { + if ((num & (num - 1)) == 0) + { + int nshifts = -1; + while (num) + { + num >>= 1; + nshifts++; + } + return nshifts; + } + } + + return -1; +} + +void +DEBUGpic16_pic16_AopType (int line_no, operand * left, operand * right, operand * result) +{ + DEBUGpic16_emitcode ("; ", "line = %d result %s=%s, left %s=%s, right %s=%s, size = %d", + line_no, + ((result) ? pic16_AopType (AOP_TYPE (result)) : "-"), + ((result) ? pic16_aopGet (AOP (result), 0, TRUE, FALSE) : "-"), + ((left) ? pic16_AopType (AOP_TYPE (left)) : "-"), + ((left) ? pic16_aopGet (AOP (left), 0, TRUE, FALSE) : "-"), + ((right) ? pic16_AopType (AOP_TYPE (right)) : "-"), + ((right) ? pic16_aopGet (AOP (right), 0, FALSE, FALSE) : "-"), ((result) ? AOP_SIZE (result) : 0)); +} + +void +DEBUGpic16_pic16_AopTypeSign (int line_no, operand * left, operand * right, operand * result) +{ + + DEBUGpic16_emitcode ("; ", "line = %d, signs: result %s=%c, left %s=%c, right %s=%c", + line_no, + ((result) ? pic16_AopType (AOP_TYPE (result)) : "-"), + ((result) ? (SPEC_USIGN (operandType (result)) ? 'u' : 's') : '-'), + ((left) ? pic16_AopType (AOP_TYPE (left)) : "-"), + ((left) ? (SPEC_USIGN (operandType (left)) ? 'u' : 's') : '-'), + ((right) ? pic16_AopType (AOP_TYPE (right)) : "-"), + ((right) ? (SPEC_USIGN (operandType (right)) ? 'u' : 's') : '-')); + +} + +void +pic16_emitpcomment (char *fmt, ...) +{ + va_list ap; + struct dbuf_s dbuf; + const char *line; + + dbuf_init (&dbuf, INITIAL_INLINEASM); + + dbuf_append_char (&dbuf, ';'); + va_start (ap, fmt); + dbuf_vprintf (&dbuf, fmt, ap); + va_end (ap); + + line = dbuf_detach_c_str (&dbuf); + emit_raw (line); + dbuf_free (line); + + pic16_addpCode2pBlock (pb, pic16_newpCodeCharP (genLine.lineCurr->line)); +} + +void +DEBUGpic16_emitcode (char *inst, char *fmt, ...) +{ + va_list ap; + + if (!pic16_debug_verbose) + return; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + + pic16_addpCode2pBlock (pb, pic16_newpCodeCharP (genLine.lineCurr->line)); +} + +void +pic16_emitpLabel (int key) +{ + if (key > max_key) + max_key = key; + + pic16_addpCode2pBlock (pb, pic16_newpCodeLabel (NULL, labelKey2num (key + pic16_labelOffset))); +} + +void +pic16_emitpLabelFORCE (int key) +{ + if (key > max_key) + max_key = key; + + pic16_addpCode2pBlock (pb, pic16_newpCodeLabelFORCE (NULL, labelKey2num (key + pic16_labelOffset))); +} + +/* gen.h defines a macro pic16_emitpcode that allows for debug information to be inserted on demand + * NEVER call pic16_emitpcode_real directly, please... */ +void +pic16_emitpcode_real (PIC_OPCODE poc, pCodeOp * pcop) +{ + + if (pcop) + pic16_addpCode2pBlock (pb, pic16_newpCode (poc, pcop)); + else + DEBUGpic16_emitcode (";", "%s ignoring NULL pcop", __FUNCTION__); +} + +void +pic16_emitpinfo (INFO_TYPE itype, pCodeOp * pcop) +{ + if (pcop) + pic16_addpCode2pBlock (pb, pic16_newpCodeInfo (itype, pcop)); + else + DEBUGpic16_emitcode (";", "%s ignoring NULL pcop", __FUNCTION__); +} + +void +pic16_emitpcodeNULLop (PIC_OPCODE poc) +{ + + pic16_addpCode2pBlock (pb, pic16_newpCode (poc, NULL)); + +} + + +#if 1 +#define pic16_emitcode DEBUGpic16_emitcode +#else +/*-----------------------------------------------------------------*/ +/* pic16_emitcode - writes the code into a file : for now it is simple */ +/*-----------------------------------------------------------------*/ +void +pic16_emitcode (char *inst, char *fmt, ...) +{ + va_list ap; + char lb[INITIAL_INLINEASM]; + unsigned char *lbp = lb; + + va_start (ap, fmt); + + if (inst && *inst) + { + if (fmt && *fmt) + SNPRINTF(lb, sizeof(lb), "%s\t", inst); + else + SNPRINTF(lb, sizeof(lb), "%s", inst); + + vsprintf (lb + (strlen (lb)), fmt, ap); + } + else + vsprintf (lb, fmt, ap); + + while (isspace (*lbp)) + lbp++; + + if (lbp && *lbp) + genLine.lineCurr = (genLine.lineCurr ? + connectLine (genLine.lineCurr, newLineNode (lb)) : (genLine.lineHead = newLineNode (lb))); + genLine.lineCurr->isInline = genLine.lineElement.isInline; + genLine.lineCurr->isDebug = genLine.lineElement.isDebug; + genLine.lineCurr->isLabel = (lbp[strlen (lbp) - 1] == ':'); + genLine.lineCurr->isComment = (*lbp == ';'); + +// VR fprintf(stderr, "lb = <%s>\n", lbp); + +// if(pic16_debug_verbose) +// pic16_addpCode2pBlock(pb,pic16_newpCodeCharP(lb)); + + va_end (ap); +} +#endif + + +/*-----------------------------------------------------------------*/ +/* pic16_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +pic16_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + pic16_emitcode (";", "%s ==.", debugSym); + genLine.lineElement.isDebug = 0; +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_alloc(sizeof(asmop)); + aop->type = type; + return aop; +} + +/*-----------------------------------------------------------------*/ +/* resolveIfx - converts an iCode ifx into a form more useful for */ +/* generating code */ +/*-----------------------------------------------------------------*/ +static void +resolveIfx (resolvedIfx * resIfx, iCode * ifx) +{ + FENTRY2; + +// DEBUGpic16_emitcode("; ***","%s %d",__FUNCTION__,__LINE__); + + if (!resIfx) + return; + + + resIfx->condition = 1; /* assume that the ifx is true */ + resIfx->generated = 0; /* indicate that the ifx has not been used */ + + if (!ifx) + { + resIfx->lbl = newiTempLabel (NULL); /* oops, there is no ifx. so create a label */ + +#if 1 + DEBUGpic16_emitcode ("; ***", "%s %d null ifx creating new label key =%d", __FUNCTION__, __LINE__, resIfx->lbl->key); +#endif + + } + else + { + if (IC_TRUE (ifx)) + { + resIfx->lbl = IC_TRUE (ifx); + } + else + { + resIfx->lbl = IC_FALSE (ifx); + resIfx->condition = 0; + } + +#if 1 + if (IC_TRUE (ifx)) + DEBUGpic16_emitcode ("; +++", "ifx true is non-null"); + else + DEBUGpic16_emitcode ("; +++", "ifx true is null"); + if (IC_FALSE (ifx)) + DEBUGpic16_emitcode ("; +++", "ifx false is non-null"); + else + DEBUGpic16_emitcode ("; +++", "ifx false is null"); +#endif + } + + DEBUGpic16_emitcode ("; ***", "%s lbl->key=%d, (lab offset=%d)", __FUNCTION__, resIfx->lbl->key, pic16_labelOffset); + +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* pointerCode - returns the code for a pointer type */ +/*-----------------------------------------------------------------*/ +static int +pointerCode (sym_link * etype) +{ + + return PTR_TYPE (SPEC_OCLS (etype)); + +} +#endif + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (iCode * ic, operand * op, bool result) +{ + symbol *sym = OP_SYMBOL (op); + asmop *aop; + memmap *space = SPEC_OCLS (sym->etype); + + FENTRY2; + + _G.resDirect = 0; /* clear flag that instructs the result is loaded directly from aopForSym */ + +// sym = OP_SYMBOL(op); + + /* if already has one */ + if (sym->aop) + { + DEBUGpic16_emitcode ("; ***", "already has sym %s %d", __FUNCTION__, __LINE__); + return sym->aop; + } + +#if 0 + /* if symbol was initially placed onStack then we must re-place it + * to direct memory, since pic16 does not have a specific stack */ + if (sym->onStack) + { + fprintf (stderr, "%s:%d symbol %s on stack\n", __FILE__, __LINE__, OP_SYMBOL (op)->name); + } +#endif + + +#if 0 + if (sym->iaccess) + { + if (space->paged) + { + fprintf (stderr, "%s:%d symbol %s points to paged data\n", __FILE__, __LINE__, sym->name); + + sym->aop = aop = newAsmop (AOP_PAGED); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + DEBUGpic16_emitcode (";", "%d sym->rname = %s, size = %d", __LINE__, sym->rname, aop->size); + pic16_allocDirReg (IC_LEFT (ic)); + return aop; + } + assert (0); + } +#endif + +#if 1 + /* assign depending on the storage class */ + /* if it is on the stack or indirectly addressable */ + /* space we need to assign either r0 or r1 to it */ + if (sym->onStack) // || sym->iaccess) + { + pCodeOp *pcop[4]; + int i; + + DEBUGpic16_emitcode ("; ***", "%s:%d sym->onStack:%d || sym->iaccess:%d", + __FUNCTION__, __LINE__, sym->onStack, sym->iaccess); + + /* acquire a temporary register -- it is saved in function */ + + sym->aop = aop = newAsmop (AOP_STA); + aop->aopu.stk.stk = sym->stack; + aop->size = getSize (sym->type); + + + DEBUGpic16_emitcode ("; +++ ", "%s:%d\top = %s", __FILE__, __LINE__, pic16_decodeOp (ic->op)); + if ((ic->op == '=' /*|| ic->op == CAST */ ) && IC_RESULT (ic) && AOP (IC_RESULT (ic)) + && (AOP_TYPE (IC_RESULT (ic)) == AOP_REG)) + { +// pic16_DumpAop("aopForSym", AOP( IC_RESULT(ic) )); + + for (i = 0; i < aop->size; i++) + aop->aopu.stk.pop[i] = pcop[i] = pic16_popRegFromIdx (AOP (IC_RESULT (ic))->aopu.aop_reg[i]->rIdx); + _G.resDirect = 1; /* notify that result will be loaded directly from aopForSym */ + } + else if (1 && ic->op == SEND) + { + + /* if SEND do the send here */ + _G.resDirect = 1; + } + else + { +// debugf3("symbol `%s' level = %d / %d\n", sym->name, ic->level, ic->seq); + for (i = 0; i < aop->size; i++) + { + aop->aopu.stk.pop[i] = pcop[i] = pic16_popGetTempRegCond (_G.fregsUsed, _G.sregsAlloc, 0); + _G.sregsAlloc = bitVectSetBit (_G.sregsAlloc, PCOR (pcop[i])->r->rIdx); + } + } + + +// fprintf(stderr, "%s:%d\t%s\tsym size %d\n", __FILE__, __LINE__, __FUNCTION__, aop->size); + +#if 1 + DEBUGpic16_emitcode (";", "%d sym->rname = %s, size = %d stack = %d", __LINE__, sym->rname, aop->size, sym->stack); + + // we do not need to load the value if it is to be defined... + if (result) + return aop; + + if (_G.accInUse) + { + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_wreg)); + } + + for (i = 0; i < aop->size; i++) + { + + /* initialise for stack access via frame pointer */ + // operands on stack are accessible via "{FRAME POINTER} + index" with index + // starting at 2 for arguments and growing from 0 downwards for + // local variables (index == 0 is not assigned so we add one here) + { + int soffs = sym->stack; + if (soffs <= 0) + { + assert (soffs < 0); + soffs++; + } // if + + if (1 && ic->op == SEND) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (soffs + aop->size - i - 1 /*+ _G.stack_lat */ )); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (pic16_frame_plusw), + pic16_popCopyReg (pic16_stack_postdec))); + } + else + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (soffs + i /*+ _G.stack_lat */ )); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (pic16_frame_plusw), pcop[i])); + } + } + } + + if (_G.accInUse) + { + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_wreg)); + } + + return (aop); +#endif + +#if 0 + /* now assign the address of the variable to + the pointer register */ + if (aop->type != AOP_STK) + { + + if (sym->onStack) + { + if (_G.accInUse) + pic16_emitcode ("push", "acc"); + + pic16_emitcode ("mov", "a,_bp"); + pic16_emitcode ("add", "a,#0x%02x", + ((sym->stack < 0) ? ((char) (sym->stack - _G.nRegsSaved)) : ((char) sym->stack)) & 0xff); + pic16_emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + + if (_G.accInUse) + pic16_emitcode ("pop", "acc"); + } + else + pic16_emitcode ("mov", "%s,#%s", aop->aopu.aop_ptr->name, sym->rname); + aop->paged = space->paged; + } + else + aop->aopu.aop_stk = sym->stack; + return aop; +#endif + + } +#endif + +#if 1 + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = pic16_popGetImmd (sym->rname, 0, 0); + PCOI (aop->aopu.pcop)->_const = IN_CODESPACE (space); + PCOI (aop->aopu.pcop)->index = 0; + aop->size = FARPTRSIZE; + DEBUGpic16_emitcode (";", "%d size = %d, name =%s", __LINE__, aop->size, sym->rname); + return aop; + } +#endif + + + + //DEBUGpic16_emitcode(";","%d",__LINE__); + /* if in bit space */ + if (IN_BITSPACE (space)) + { + sym->aop = aop = newAsmop (AOP_CRY); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + DEBUGpic16_emitcode (";", "%d sym->rname = %s, size = %d", __LINE__, sym->rname, aop->size); + return aop; + } + /* if it is in direct space */ + if (IN_DIRSPACE (space)) + { + if (!strcmp (sym->rname, "_WREG")) + { + sym->aop = aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); /* should always be 1 */ + assert (aop->size == 1); + DEBUGpic16_emitcode (";", "%d sym->rname (AOP_ACC) = %s, size = %d", __LINE__, sym->rname, aop->size); + return (aop); + } + else + { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + DEBUGpic16_emitcode (";", "%d sym->rname (AOP_DIR) = %s, size = %d", __LINE__, sym->rname, aop->size); + pic16_allocDirReg (IC_LEFT (ic)); + return (aop); + } + } + + if (IN_FARSPACE (space) && !IN_CODESPACE (space)) + { + sym->aop = aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + DEBUGpic16_emitcode (";", "%d sym->rname = %s, size = %d", __LINE__, sym->rname, aop->size); + pic16_allocDirReg (IC_LEFT (ic)); + return aop; + } + + + /* only remaining is far space */ + sym->aop = aop = newAsmop (AOP_PCODE); + + /* change the next if to 1 to revert to good old immediate code */ + if (IN_CODESPACE (space)) + { + aop->aopu.pcop = pic16_popGetImmd (sym->rname, 0, 0); + PCOI (aop->aopu.pcop)->_const = IN_CODESPACE (space); + PCOI (aop->aopu.pcop)->index = 0; + } + else + { + /* try to allocate via direct register */ + aop->aopu.pcop = pic16_popRegFromString (sym->rname, getSize (sym->type), sym->offset, op); // Patch 8 +// aop->size = getSize( sym->type ); + } + + DEBUGpic16_emitcode (";", "%d: rname %s, val %d, const = %d", __LINE__, sym->rname, 0, PCOI (aop->aopu.pcop)->_const); + +#if 0 + if (!pic16_allocDirReg (IC_LEFT (ic))) + return NULL; +#endif + + if (IN_DIRSPACE (space)) + aop->size = NEARPTRSIZE; + else if (IN_CODESPACE (space) || IN_FARSPACE (space)) + aop->size = FARPTRSIZE; + else if (IC_LEFT (ic) && AOP (IC_LEFT (ic))) + aop->size = AOP_SIZE (IC_LEFT (ic)); + else if (IC_RIGHT (ic) && AOP (IC_RIGHT (ic))) + aop->size = AOP_SIZE (IC_RIGHT (ic)); + else if (sym->onStack) + { + aop->size = NEARPTRSIZE; + } + else + { + if (SPEC_SCLS (sym->etype) == S_PDATA) + { + fprintf (stderr, "%s: %d symbol in PDATA space\n", __FILE__, __LINE__); + aop->size = FARPTRSIZE; + } + else + assert (0); + } + + DEBUGpic16_emitcode (";", "%d size = %d", __LINE__, aop->size); + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerialzes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (operand * op, bool result) // x symbol *sym) +{ + symbol *sym = OP_SYMBOL (op); + operand *refop; + iCode *ic = NULL; + asmop *aop = newAsmop (AOP_PCODE); + int val = 0; + int viaimmd = 0; + + FENTRY2; + + ic = sym->rematiCode; + + if (IS_OP_POINTER (op)) + { + DEBUGpic16_emitcode (";", "%s %d IS_OP_POINTER", __FUNCTION__, __LINE__); + } + +// if(!result) /* fixme-vr */ + for (;;) + { +// chat *iLine = printILine(ic); +// pic16_emitpcomment("ic: %s\n", iLine); +// dbuf_free(iLine); + + if (ic->op == '+') + { + val += (int) operandLitValue (IC_RIGHT (ic)); + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + } + else + break; + + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + + refop = IC_LEFT (ic); + + if (!op->isaddr) + viaimmd++; + else + viaimmd = 0; + + /* set the following if to 1 to revert to good old immediate code */ + if (IN_CODESPACE (SPEC_OCLS (OP_SYM_ETYPE (refop))) || viaimmd) + { + + DEBUGpic16_emitcode (";", "%s:%d immediate, size: %d", __FILE__, __LINE__, getSize (sym->type)); + + aop->aopu.pcop = pic16_popGetImmd (OP_SYMBOL (IC_LEFT (ic))->rname, 0, val); + +#if 0 + PCOI (aop->aopu.pcop)->_const = IS_PTR_CONST (operandType (op)); +#else + PCOI (aop->aopu.pcop)->_const = IS_CODEPTR (operandType (op)); +#endif + + PCOI (aop->aopu.pcop)->index = val; + + aop->size = getSize (sym->type); + } + else + { + DEBUGpic16_emitcode (";", "%s:%d dir size: %d", __FILE__, __LINE__, getSize (OP_SYMBOL (IC_LEFT (ic))->type)); + + aop->aopu.pcop = pic16_popRegFromString (OP_SYMBOL (IC_LEFT (ic))->rname, + getSize (OP_SYMBOL (IC_LEFT (ic))->type), val, op); + + aop->size = getSize (OP_SYMBOL (IC_LEFT (ic))->type); + } + + + DEBUGpic16_emitcode (";", "%d: rname %s, val %d, const = %d", __LINE__, OP_SYMBOL (IC_LEFT (ic))->rname, +#if 0 + val, IS_PTR_CONST (operandType (op))); +#else + val, IS_CODEPTR (operandType (op))); +#endif + +// DEBUGpic16_emitcode(";","aop type %s",pic16_AopType(AOP_TYPE(IC_LEFT(ic)))); + + pic16_allocDirReg (IC_LEFT (ic)); + + if (IN_CODESPACE (SPEC_OCLS (OP_SYM_ETYPE (op)))) + aop->code = 1; + + return aop; +} + +#if 0 +static int +aopIdx (asmop * aop, int offset) +{ + if (!aop) + return -1; + + if (aop->type != AOP_REG) + return -2; + + return aop->aopu.aop_reg[offset]->rIdx; + +} +#endif + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return TRUE; + + if (sym1->rname[0] && sym2->rname[0] && strcmp (sym1->rname, sym2->rname) == 0) + return TRUE; + + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (SYM_SPIL_LOC (sym1) == sym2)) + return TRUE; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (SYM_SPIL_LOC (sym2) == sym1)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* pic16_sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +bool +pic16_sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1 == aop2) + return TRUE; + + DEBUGpic16_emitcode (";***", "%s aop1->type = %s\taop2->type = %s\n", __FUNCTION__, + pic16_AopType (aop1->type), pic16_AopType (aop2->type)); + + if (aop1->type == AOP_ACC && aop2->type == AOP_ACC) + return TRUE; + + if (aop1->type != AOP_REG || aop2->type != AOP_REG) + return FALSE; + + /* This is a bit too restrictive if one is a subset of the other... + if (aop1->size != aop2->size ) + return FALSE ; + */ + + for (i = 0; i < min (aop1->size, aop2->size); i++) + { +// if(aop1->aopu.aop_reg[i]->type != aop2->aopu.aop_reg[i]->type)return FALSE; + +// if(aop1->aopu.aop_reg[i]->type == AOP_REG) + if (strcmp (aop1->aopu.aop_reg[i]->name, aop2->aopu.aop_reg[i]->name)) + return FALSE; + } + + return TRUE; +} + +bool +pic16_sameRegsOfs (asmop * aop1, asmop * aop2, int offset) +{ + DEBUGpic16_emitcode (";***", "%s aop1->type = %s\taop2->type = %s (offset = %d)\n", __FUNCTION__, + pic16_AopType (aop1->type), pic16_AopType (aop2->type), offset); + + if (aop1 == aop2) + return TRUE; + if (aop1->type != AOP_REG || aop2->type != AOP_REG) + return FALSE; + + if (strcmp (aop1->aopu.aop_reg[offset]->name, aop2->aopu.aop_reg[offset]->name)) + return FALSE; + + return TRUE; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +void +pic16_aopOp (operand * op, iCode * ic, bool result) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + DEBUGpic16_emitcode (";", "%s %d", __FUNCTION__, __LINE__); + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + { + sym_link *type = operandType (op); +#if 0 + if (IS_PTR_CONST (type)) +#else + if (IS_CODEPTR (type)) +#endif + DEBUGpic16_emitcode (";", "%d aop type is const pointer", __LINE__); + } + + /* if already has a asmop then continue */ + if (op->aop) + return; + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + DEBUGpic16_emitcode (";", "%d has symbol", __LINE__); + op->aop = OP_SYMBOL (op)->aop; + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + DEBUGpic16_emitcode (";", "%d - true symop", __LINE__); + op->aop = aopForSym (ic, op, result); + return; + } + + /* this is a temporary : this has + only four choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + DEBUGpic16_emitcode ("; ***", "%d: symbol name = %s, regType = %d", __LINE__, sym->name, sym->regType); + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + aop = op->aop = sym->aop = newAsmop (AOP_CRY); + aop->size = 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + +// debugf3("symbol %s\tisspilt: %d\tnRegs: %d\n", sym->rname, sym->isspilt, sym->nRegs); + DEBUGpic16_emitcode (";", "%d", __LINE__); + /* rematerialize it NOW */ + if (sym->remat) + { + + sym->aop = op->aop = aop = aopForRemat (op, result); + return; + } + +#if 1 + if (sym->accuse) + { + int i; + aop = op->aop = sym->aop = newAsmop (AOP_ACC); + aop->size = getSize (sym->type); + for (i = 0; i < 1; i++) + { + aop->aopu.aop_str[i] = accUse[i]; +// aop->aopu.pcop = pic16_popRegFromString("WREG", aop->size, SYM_SPIL_LOC(sym)->offset); + } + fprintf (stderr, "%s:%d allocating AOP_ACC for sym= %s\n", __FILE__, __LINE__, sym->name); + DEBUGpic16_emitcode (";", "%d size=%d", __LINE__, aop->size); + return; + } +#endif + +#if 1 + if (sym->ruonly) + { + /* + sym->aop = op->aop = aop = newAsmop(AOP_PCODE); + aop->aopu.pcop = pic16_popGetImmd(SYM_SPIL_LOC(sym)->rname,0,SYM_SPIL_LOC(sym)->offset); + //pic16_allocDirReg (IC_LEFT(ic)); + aop->size = getSize(sym->type); + */ + + unsigned i; + + aop = op->aop = sym->aop = newAsmop (AOP_REG); + aop->size = getSize (sym->type); + for (i = 0; i < pic16_fReturnSizePic; i++) + aop->aopu.aop_reg[i] = PCOR (pic16_popRegFromIdx (fReturnIdx[i]))->r; + + DEBUGpic16_emitcode (";", "%d", __LINE__); + return; + } +#endif + /* else spill location */ + if (sym->isspilt && SYM_SPIL_LOC (sym) && getSize (sym->type) != getSize (SYM_SPIL_LOC (sym)->type)) + { + /* force a new aop if sizes differ */ + SYM_SPIL_LOC (sym)->aop = NULL; + } + +#if 0 + DEBUGpic16_emitcode (";", "%s %d %s sym->rname = %s, offset %d", + __FUNCTION__, __LINE__, SYM_SPIL_LOC (sym)->rname, sym->rname, SYM_SPIL_LOC (sym)->offset); +#endif + + //aop->aopu.pcop = pic16_popGetImmd(SYM_SPIL_LOC(sym)->rname,0,SYM_SPIL_LOC(sym)->offset); + if (sym->isspilt && SYM_SPIL_LOC (sym) && SYM_SPIL_LOC (sym)->rname) + { + sym->aop = op->aop = aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = pic16_popRegFromString (SYM_SPIL_LOC (sym)->rname, + getSize (sym->type), SYM_SPIL_LOC (sym)->offset, op); + } + else if (getSize (sym->type) <= 1) + { + //fprintf (stderr, "%s:%d called for a spillLocation -- assigning WREG instead --- CHECK (size:%u)!\n", __FUNCTION__, __LINE__, getSize(sym->type)); + pic16_emitpcomment (";!!! %s:%d called for a spillLocation -- assigning WREG instead --- CHECK", __FUNCTION__, + __LINE__); + assert (getSize (sym->type) <= 1); + sym->aop = op->aop = aop = newAsmop (AOP_PCODE); + aop->aopu.pcop = pic16_popCopyReg (&pic16_pc_wreg); + } + else + { + /* We need some kind of dummy area for getSize(sym->type) byte, + * use WREG for all storage locations. + * XXX: This only works if we are implementing a `dummy read', + * the stored value will not be retrievable... + * See #1503234 for a case requiring this. */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = getSize (sym->type); + for (i = 0; i < aop->size; i++) + aop->aopu.aop_reg[i] = pic16_pc_wreg.r; + } + aop->size = getSize (sym->type); + + return; + } + + { + sym_link *type = operandType (op); +#if 0 + if (IS_PTR_CONST (type)) +#else + if (IS_CODEPTR (type)) +#endif + DEBUGpic16_emitcode (";", "%d aop type is const pointer", __LINE__); + } + + /* must be in a register */ + DEBUGpic16_emitcode (";", "%d register type nRegs=%d", __LINE__, sym->nRegs); + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + aop->aopu.aop_reg[i] = sym->regs[i]; +} + +/*-----------------------------------------------------------------*/ +/* pic16_freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +void +pic16_freeAsmop (operand * op, asmop * aaop, iCode * ic, bool pop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + if (aop->freed) + goto dealloc; + + aop->freed = 1; + +#if 1 + switch (aop->type) + { + case AOP_STA: + { + int i; + + /* we must store the result on stack */ + if ((op == IC_RESULT (ic)) && RESULTONSTA (ic)) + { + // operands on stack are accessible via "FSR2 + index" with index + // starting at 2 for arguments and growing from 0 downwards for + // local variables (index == 0 is not assigned so we add one here) + int soffs = OP_SYMBOL (IC_RESULT (ic))->stack; + if (soffs <= 0) + { + assert (soffs < 0); + soffs++; + } // if + if (_G.accInUse) + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_wreg)); + for (i = 0; i < aop->size; i++) + { + /* initialise for stack access via frame pointer */ + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (soffs + i /*+ _G.stack_lat */ )); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (aop->aopu.stk.pop[i], pic16_popCopyReg (pic16_frame_plusw))); + } + + if (_G.accInUse) + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_wreg)); + } + + if (!_G.resDirect) + { + for (i = 0; i < aop->size; i++) + { + PCOR (aop->aopu.stk.pop[i])->r->isFree = 1; + + if (bitVectBitValue (_G.sregsAlloc, PCOR (aop->aopu.stk.pop[i])->r->rIdx)) + { + bitVectUnSetBit (_G.sregsAlloc, PCOR (aop->aopu.stk.pop[i])->r->rIdx); +// pic16_popReleaseTempReg(aop->aopu.stk.pop[i], 0); + } + } + + if (_G.sregsAllocSet) + { + reg_info *sr; + + _G.sregsAllocSet = reverseSet (_G.sregsAllocSet); + for (sr = setFirstItem (_G.sregsAllocSet); sr; sr = setFirstItem (_G.sregsAllocSet)) + { + pic16_poppCodeOp (pic16_popRegFromIdx (sr->rIdx)); + deleteSetItem (&_G.sregsAllocSet, sr); + } + } + } + _G.resDirect = 0; + } + break; +#if 0 + case AOP_STK: + { + int sz = aop->size; + int stk = aop->aopu.aop_stk + aop->size; + bitVectUnSetBit (ic->rUsed, R0_IDX); + bitVectUnSetBit (ic->rUsed, R1_IDX); + + getFreePtr (ic, &aop, FALSE); + + if (options.stack10bit) + { + /* I'm not sure what to do here yet... */ + /* #STUB */ + fprintf (stderr, "*** Warning: probably generating bad code for " "10 bit stack mode.\n"); + } + + if (stk) + { + pic16_emitcode ("mov", "a,_bp"); + pic16_emitcode ("add", "a,#0x%02x", ((char) stk) & 0xff); + pic16_emitcode ("mov", "%s,a", aop->aopu.aop_ptr->name); + } + else + { + pic16_emitcode ("mov", "%s,_bp", aop->aopu.aop_ptr->name); + } + + while (sz--) + { + pic16_emitcode ("pop", "acc"); + pic16_emitcode ("mov", "@%s,a", aop->aopu.aop_ptr->name); + if (!sz) + break; + pic16_emitcode ("dec", "%s", aop->aopu.aop_ptr->name); + } + op->aop = aop; + pic16_freeAsmop (op, NULL, ic, TRUE); + if (_G.r0Pushed) + { + pic16_emitcode ("pop", "ar0"); + _G.r0Pushed--; + } + + if (_G.r1Pushed) + { + pic16_emitcode ("pop", "ar1"); + _G.r1Pushed--; + } + } +#endif + + } +#endif + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_aopGet - for fetching value of the aop */ +/*-----------------------------------------------------------------*/ +char * +pic16_aopGet (asmop * aop, int offset, bool bit16, bool dname) +{ + //DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + /* offset is greater than size then zero */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + return zero; + + /* depending on type */ + switch (aop->type) + { + case AOP_DIR: + if (offset > 0) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", aop->aopu.aop_dir, offset); + DEBUGpic16_emitcode (";", "oops AOP_DIR did this %s\n", buffer); + return Safe_strdup(buffer); + } + else + return Safe_strdup(aop->aopu.aop_dir); + + case AOP_REG: + return aop->aopu.aop_reg[offset]->name; + + case AOP_CRY: + return aop->aopu.aop_dir; + + case AOP_ACC: + DEBUGpic16_emitcode (";Warning -pic port ignoring get(AOP_ACC)", "%d\toffset: %d", __LINE__, offset); +// fprintf(stderr, "%s:%d Warning -pic port ignoring get(AOP_ACC)\n",__FILE__, __LINE__); +// assert( 0 ); +// return aop->aopu.aop_str[offset]; //->"AOP_accumulator_bug"; + return Safe_strdup("WREG"); + + case AOP_LIT: + SNPRINTF(buffer, sizeof(buffer), "0x%02x", pic16aopLiteral (aop->aopu.aop_lit, offset)); + return Safe_strdup(buffer); + + case AOP_STR: + aop->coff = offset; + +// if (strcmp(aop->aopu.aop_str[offset],"a") == 0 && +// dname) +// return "acc"; + if (!strcmp (aop->aopu.aop_str[offset], "WREG")) + { + aop->type = AOP_ACC; + return Safe_strdup ("_WREG"); + } + DEBUGpic16_emitcode (";", "%d - %s", __LINE__, aop->aopu.aop_str[offset]); + return aop->aopu.aop_str[offset]; + + case AOP_PCODE: + { + pCodeOp *pcop = aop->aopu.pcop; + DEBUGpic16_emitcode (";", "%d: pic16_aopGet AOP_PCODE type %s", __LINE__, pic16_pCodeOpType (pcop)); + if (pcop->name) + { + DEBUGpic16_emitcode (";", "%s offset %d", pcop->name, PCOI (pcop)->offset); + //SNPRINTF(buffer, sizeof(buffer), "(%s+0x%02x)", pcop->name, PCOI(aop->aopu.pcop)->offset); + if (offset > 0) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", pic16_get_op (pcop, NULL, 0), offset); + } + else + { + SNPRINTF(buffer, sizeof(buffer), "%s", pic16_get_op (pcop, NULL, 0)); + } + } + else + SNPRINTF(buffer, sizeof(buffer), "0x%02x", PCOI (aop->aopu.pcop)->offset); + + } + return Safe_strdup(buffer); + +#if 0 + case AOP_PAGED: + DEBUGpic16_emitcode (";", "oops AOP_PAGED did this %s\n", s); + if (offset) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", aop->aopu.aop_dir, offset); + } + else + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + DEBUGpic16_emitcode (";", "oops AOP_PAGED did this %s\n", s); + return Safe_strdup(buffer); +#endif + + case AOP_STA: + return Safe_strdup(PCOR(aop->aopu.stk.pop[offset])->r->name); + + case AOP_STK: +// pCodeOp *pcop = aop->aop + break; + + } + + fprintf (stderr, "%s:%d unsupported aop->type: %s\n", __FILE__, __LINE__, pic16_AopType (aop->type)); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopget got unsupported aop->type"); + exit (0); +} + + + +/* lock has the following meaning: When allocating temporary registers + * for stack variables storage, the value of the temporary register is + * saved on stack. Its value is restored at the end. This procedure is + * done via calls to pic16_aopOp and pic16_freeAsmop functions. There is + * a possibility that before a call to pic16_aopOp, a temporary register + * is allocated for a while and it is freed after some time, this will + * mess the stack and values will not be restored properly. So use lock=1 + * to allocate temporary registers used internally by the programmer, and + * lock=0 to allocate registers for stack use. lock=1 will emit a warning + * to inform the compiler developer about a possible bug. This is an internal + * feature for developing the compiler -- VR */ + +int _TempReg_lock = 0; +/*-----------------------------------------------------------------*/ +/* pic16_popGetTempReg - create a new temporary pCodeOp */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetTempReg (int lock) +{ + pCodeOp *pcop = NULL; + symbol *cfunc; + +// DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if (_TempReg_lock) + { +// werror(W_POSSBUG2, __FILE__, __LINE__); + } + + _TempReg_lock += lock; + + cfunc = currFunc; + currFunc = NULL; + + pcop = pic16_newpCodeOp (NULL, PO_GPR_TEMP); + if (pcop && pcop->type == PO_GPR_TEMP && PCOR (pcop)->r) + { + PCOR (pcop)->r->wasUsed = 1; + PCOR (pcop)->r->isFree = 0; + + /* push value on stack */ + pic16_pushpCodeOp (pic16_pCodeOpCopy (pcop)); + } + + currFunc = cfunc; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* pic16_popGetTempRegCond - create a new temporary pCodeOp which */ +/* is not part of f, but don't save if */ +/* inside v */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetTempRegCond (bitVect * f, bitVect * v, int lock) +{ + pCodeOp *pcop = NULL; + symbol *cfunc; + int i; + +// DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + if (_TempReg_lock) + { +// werror(W_POSSBUG2, __FILE__, __LINE__); + } + + _TempReg_lock += lock; + + cfunc = currFunc; + currFunc = NULL; + + i = bitVectFirstBit (f); + while (i < 128) + { + + /* bypass registers that are used by function */ + if (!bitVectBitValue (f, i)) + { + + /* bypass registers that are already allocated for stack access */ + if (!bitVectBitValue (v, i)) + { + +// debugf("getting register rIdx = %d\n", i); + /* ok, get the operand */ + pcop = pic16_newpCodeOpReg (i); + + /* should never by NULL */ + assert (pcop != NULL); + + + /* sanity check */ + if (pcop && pcop->type == PO_GPR_TEMP && PCOR (pcop)->r) + { + int found = 0; + + PCOR (pcop)->r->wasUsed = 1; + PCOR (pcop)->r->isFree = 0; + + + { + reg_info *sr; + + for (sr = setFirstItem (_G.sregsAllocSet); sr; sr = setNextItem (_G.sregsAllocSet)) + { + + if (sr->rIdx == PCOR (pcop)->r->rIdx) + { + /* already used in previous steps, break */ + found = 1; + break; + } + } + } + + /* caller takes care of the following */ +// bitVectSetBit(v, i); + + if (!found) + { + /* push value on stack */ + pic16_pushpCodeOp (pic16_pCodeOpCopy (pcop)); + addSet (&_G.sregsAllocSet, PCOR (pcop)->r); + } + + break; + } + } + } + i++; + } + + currFunc = cfunc; + + return pcop; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_popReleaseTempReg - create a new temporary pCodeOp */ +/*-----------------------------------------------------------------*/ +void +pic16_popReleaseTempReg (pCodeOp * pcop, int lock) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + _TempReg_lock -= lock; + + if (pcop && pcop->type == PO_GPR_TEMP && PCOR (pcop)->r) + { + PCOR (pcop)->r->isFree = 1; + + pic16_poppCodeOp (pic16_pCodeOpCopy (pcop)); + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_popGetLabel - create a new pCodeOp of type PO_LABEL */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetLabel (int key) +{ + + DEBUGpic16_emitcode ("; ***", "%s key=%d, label offset %d", __FUNCTION__, key, pic16_labelOffset); + + if (key > max_key) + max_key = key; + + return pic16_newpCodeOpLabel (NULL, labelKey2num (key + pic16_labelOffset)); +} + +/*-----------------------------------------------------------------*/ +/* pic16_popCopyReg - copy a pcode operator */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popCopyReg (pCodeOpReg * pc) +{ + pCodeOpReg *pcor; + + pcor = Safe_malloc(sizeof(pCodeOpReg)); + memcpy(pcor, pc, sizeof(pCodeOpReg)); + pcor->r->wasUsed = TRUE; + + //pcor->pcop.type = pc->pcop.type; + if (pc->pcop.name) + { + if (!(pcor->pcop.name = Safe_strdup (pc->pcop.name))) + fprintf (stderr, "oops %s %d", __FILE__, __LINE__); + } + else + pcor->pcop.name = NULL; + + //pcor->r = pc->r; + //pcor->rIdx = pc->rIdx; + //pcor->r->wasUsed=1; + //pcor->instance = pc->instance; + +// DEBUGpic16_emitcode ("; ***","%s , copying %s, rIdx=%d",__FUNCTION__,pc->pcop.name,pc->rIdx); + + return PCOP (pcor); +} + +/*-----------------------------------------------------------------*/ +/* pic16_popGetLit - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetLit (int lit) +{ + return pic16_newpCodeOpLit (lit); +} + +/* Allow for 12 bit literals (LFSR x, ). */ +pCodeOp * +pic16_popGetLit12 (int lit) +{ + return pic16_newpCodeOpLit12 (lit); +} + +/*-----------------------------------------------------------------*/ +/* pic16_popGetLit2 - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetLit2 (int lit, pCodeOp * arg2) +{ + return pic16_newpCodeOpLit2 (lit, arg2); +} + + +/*-----------------------------------------------------------------*/ +/* pic16_popGetImmd - asm operator to pcode immediate conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetImmd (char *name, unsigned int offset, int index) +{ + return pic16_newpCodeOpImmd (name, offset, index, 0); +} + + +/*-----------------------------------------------------------------*/ +/* pic16_popGet - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGetWithString (char *str) +{ + pCodeOp *pcop; + + + if (!str) + { + fprintf (stderr, "NULL string %s %d\n", __FILE__, __LINE__); + exit (1); + } + + pcop = pic16_newpCodeOp (str, PO_STR); + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/* pic16_popRegFromString - */ +/*-----------------------------------------------------------------*/ +static pCodeOp * +pic16_popRegFromString (char *str, int size, int offset, operand * op) +{ + + pCodeOp *pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->type = PO_DIR; + + DEBUGpic16_emitcode (";", "%d %s %s %d/%d", __LINE__, __FUNCTION__, str, size, offset); // patch 14 + // fprintf(stderr, "%s:%d: register name = %s pos = %d/%d\n", __FUNCTION__, __LINE__, str, offset, size); + + if (!str) + str = "BAD_STRING"; + + pcop->name = Safe_strdup(str); + + //pcop->name = Safe_strdup( ( (str) ? str : "BAD STRING")); + + PCOR (pcop)->r = pic16_dirregWithName (pcop->name); +// PCOR(pcop)->r->wasUsed = 1; + + /* make sure that register doesn't exist, + * and operand isn't NULL + * and symbol isn't in codespace (codespace symbols are handled elsewhere) */ + if ((PCOR (pcop)->r == NULL) && (op) && !IN_CODESPACE (SPEC_OCLS (OP_SYM_ETYPE (op)))) + { +// fprintf(stderr, "%s:%d - couldn't find %s in allocated regsters, size= %d ofs= %d\n", +// __FUNCTION__, __LINE__, str, size, offset); + + PCOR (pcop)->r = pic16_allocRegByName (pcop->name, size, op); + //fprintf(stderr, "%s:%d: WARNING: need to allocate new register by name -> %s\n", __FILE__, __LINE__, str); + + } + PCOR (pcop)->instance = offset; + + return pcop; +} + +static pCodeOp * +pic16_popRegFromIdx (int rIdx) +{ + pCodeOp *pcop; + +// DEBUGpic16_emitcode ("; ***","%s,%d\trIdx=0x%x", __FUNCTION__,__LINE__,rIdx); +// fprintf(stderr, "%s:%d rIdx = 0x%0x\n", __FUNCTION__, __LINE__, rIdx); + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR (pcop)->rIdx = rIdx; + PCOR (pcop)->r = pic16_regWithIdx (rIdx); + if (!PCOR (pcop)->r) + PCOR (pcop)->r = pic16_allocWithIdx (rIdx); + + PCOR (pcop)->r->isFree = 0; + PCOR (pcop)->r->wasUsed = 1; + + pcop->type = PCOR (pcop)->r->pc_type; + + return pcop; +} + +/*---------------------------------------------------------------------------------*/ +/* pic16_popGet2 - a variant of pic16_popGet to handle two memory operand commands */ +/* VR 030601 */ +/*---------------------------------------------------------------------------------*/ +pCodeOp * +pic16_popGet2 (asmop * aop_src, asmop * aop_dst, int offset) +{ + pCodeOp2 *pcop2 = (pCodeOp2 *) pic16_newpCodeOp2 (pic16_popGet (aop_src, offset), pic16_popGet (aop_dst, offset)); + return PCOP (pcop2); +} + +/*--------------------------------------------------------------------------------.-*/ +/* pic16_popGet2p - a variant of pic16_popGet to handle two memory operand commands */ +/* VR 030601 , adapted by Hans Dorn */ +/*--------------------------------------------------------------------------------.-*/ +pCodeOp * +pic16_popGet2p (pCodeOp * src, pCodeOp * dst) +{ + pCodeOp2 *pcop2; + pcop2 = (pCodeOp2 *) pic16_newpCodeOp2 (src, dst); + return PCOP (pcop2); +} + +/*---------------------------------------------------------------------------------*/ +/* pic16_popCombine2 - combine two pCodeOpReg variables into one for use with */ +/* movff instruction */ +/*---------------------------------------------------------------------------------*/ +pCodeOp * +pic16_popCombine2 (pCodeOpReg * src, pCodeOpReg * dst, int noalloc) +{ + pCodeOp2 *pcop2 = (pCodeOp2 *) pic16_newpCodeOp2 (pic16_popCopyReg (src), pic16_popCopyReg (dst)); + + return PCOP (pcop2); +} + +/*-----------------------------------------------------------------*/ +/* pic16_popGet - asm operator to pcode operator conversion */ +/*-----------------------------------------------------------------*/ +pCodeOp * +pic16_popGet (asmop * aop, int offset) //, bool bit16, bool dname) +{ +// char *s = buffer ; +// char *rs; + pCodeOp *pcop; + + FENTRY2; + + /* offset is greater than size then zero */ + +// if (offset > (aop->size - 1) && +// aop->type != AOP_LIT) +// return NULL; //zero; + + /* depending on type */ + switch (aop->type) + { + case AOP_STA: + /* pCodeOp is already allocated from aopForSym */ + DEBUGpic16_emitcode (";---", "%d getting stack + offset %d\n", __LINE__, offset); + pcop = pic16_pCodeOpCopy (aop->aopu.stk.pop[offset]); + return (pcop); + + case AOP_ACC: + { + int rIdx = IDX_WREG; //aop->aopu.aop_reg[offset]->rIdx; + +// fprintf (stderr, "%s:%d returning register AOP_ACC %s\n", __FILE__, __LINE__, aop->aopu.aop_str[offset]); + + DEBUGpic16_emitcode (";", "%d\tAOP_ACC", __LINE__); + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR (pcop)->rIdx = rIdx; + PCOR (pcop)->r = pic16_typeRegWithIdx (rIdx, REG_SFR, 1); // pic16_regWithIdx(rIdx); + PCOR (pcop)->r->wasUsed = 1; + PCOR (pcop)->r->isFree = 0; + + PCOR (pcop)->instance = offset; + pcop->type = PCOR (pcop)->r->pc_type; +// DEBUGpic16_emitcode(";","%d register idx = %d name =%s",__LINE__,rIdx,rs); + return pcop; + + +// return pic16_popRegFromString(aop->aopu.aop_str[offset], aop->size, offset); +// return pic16_newpCodeOpRegFromStr(aop->aopu.aop_str[offset]); + +// assert( 0 ); + } + + case AOP_DIR: + DEBUGpic16_emitcode (";", "%d\tAOP_DIR (name = %s)", __LINE__, aop->aopu.aop_dir); + return pic16_popRegFromString (aop->aopu.aop_dir, aop->size, offset, NULL); + +#if 0 + case AOP_PAGED: + DEBUGpic16_emitcode (";", "%d\tAOP_DIR", __LINE__); + return pic16_popRegFromString (aop->aopu.aop_dir, aop->size, offset, NULL); +#endif + + case AOP_REG: + { + int rIdx; + +// debugf2("aop = %p\toffset = %d\n", aop, offset); +// assert (aop && aop->aopu.aop_reg[offset] != NULL); + rIdx = aop->aopu.aop_reg[offset]->rIdx; + + DEBUGpic16_emitcode (";", "%d\tAOP_REG", __LINE__); + + pcop = Safe_alloc(sizeof(pCodeOpReg)); +// pcop->type = PO_GPR_REGISTER; + PCOR (pcop)->rIdx = rIdx; + PCOR (pcop)->r = pic16_allocWithIdx (rIdx); //pic16_regWithIdx(rIdx); + PCOR (pcop)->r->wasUsed = 1; + PCOR (pcop)->r->isFree = 0; + + PCOR (pcop)->instance = offset; + pcop->type = PCOR (pcop)->r->pc_type; + + DEBUGpic16_emitcode (";*+*", "%d\tAOP_REG type = %s", __LINE__, dumpPicOptype (pcop->type)); +// rs = aop->aopu.aop_reg[offset]->name; +// DEBUGpic16_emitcode(";","%d register idx = %d name = %s",__LINE__,rIdx,rs); + return pcop; + } + + case AOP_CRY: + DEBUGpic16_emitcode (";", "%d\tAOP_CRY", __LINE__); + + pcop = pic16_newpCodeOpBit (aop->aopu.aop_dir, -1, 1, PO_GPR_REGISTER); + PCOR (pcop)->instance = offset; + PCOR (pcop)->r = pic16_dirregWithName (aop->aopu.aop_dir); + //if(PCOR(pcop)->r == NULL) + //fprintf(stderr,"%d - couldn't find %s in allocated registers\n",__LINE__,aop->aopu.aop_dir); + return pcop; + + case AOP_LIT: + DEBUGpic16_emitcode (";", "%d\tAOP_LIT", __LINE__); + return pic16_newpCodeOpLit (pic16aopLiteral (aop->aopu.aop_lit, offset)); + + case AOP_STR: + DEBUGpic16_emitcode (";", "%d AOP_STR %s", __LINE__, aop->aopu.aop_str[offset]); + return pic16_newpCodeOpRegFromStr (aop->aopu.aop_str[offset]); + + /* + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR(pcop)->r = pic16_allocRegByName(aop->aopu.aop_str[offset]); + PCOR(pcop)->rIdx = PCOR(pcop)->r->rIdx; + pcop->type = PCOR(pcop)->r->pc_type; + pcop->name = PCOR(pcop)->r->name; + + return pcop; + */ + + case AOP_PCODE: + DEBUGpic16_emitcode (";", "pic16_popGet AOP_PCODE (%s) %d %s offset %d", pic16_pCodeOpType (aop->aopu.pcop), + __LINE__, ((aop->aopu.pcop->name) ? (aop->aopu.pcop->name) : "no name"), offset); + pcop = pic16_pCodeOpCopy (aop->aopu.pcop); + switch (aop->aopu.pcop->type) + { + case PO_DIR: + PCOR (pcop)->instance += offset; + break; + case PO_IMMEDIATE: + PCOI (pcop)->offset = offset; + break; + case PO_WREG: + assert (offset == 0); + break; + default: + fprintf (stderr, "%s: unhandled aop->aopu.pcop->type %d\n", __FUNCTION__, aop->aopu.pcop->type); + assert (0); /* should never reach here */ ; + } + return pcop; + } + + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "pic16_popGet got unsupported aop->type"); + exit (0); +} + +/*-------------------------------------------------------------------------*/ +/* pic16_popGetImmed - immediate asm operator to pcode operator conversion */ +/*-------------------------------------------------------------------------*/ +static pCodeOp * +pic16_popGetImmed(asmop *aop, int offset, int overload) +{ + pCodeOp *pcop; + + FENTRY2; + + if (aop->type == AOP_PCODE) + { + if (aop->aopu.pcop->type == PO_IMMEDIATE) + { + pcop = pic16_pCodeOpCopy(aop->aopu.pcop); + PCOI(pcop)->offset = offset; // offset: "LOW", "HIGH", "UPPER" + PCOI(pcop)->index += overload; // LOW(ptr + index + overload) + return pcop; + } + + fprintf(stderr, "%s: Only handled PO_IMMEDIATE.\n", __FUNCTION__); + assert(0); + } + + werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "pic16_popGetImmed supported only AOP_PCODE."); + exit(0); +} + +/*-----------------------------------------------------------------*/ +/* pic16_aopPut - puts a string for a aop */ +/*-----------------------------------------------------------------*/ +void +pic16_aopPut (asmop * aop, char *s, int offset) +{ + symbol *lbl; + + return; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (aop->size && offset > (aop->size - 1)) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "pic16_aopPut got offset > aop->size"); + exit (0); + } + + /* will assign value to value */ + /* depending on where it is ofcourse */ + switch (aop->type) + { + case AOP_DIR: + if (offset > 0) + { + SNPRINTF(buffer, sizeof(buffer), "(%s + %d)", aop->aopu.aop_dir, offset); + fprintf (stderr, "oops pic16_aopPut:AOP_DIR did this %s\n", s); + } + else + SNPRINTF(buffer, sizeof(buffer), "%s", aop->aopu.aop_dir); + + if (strcmp(buffer, s)) + { + DEBUGpic16_emitcode (";", "%d", __LINE__); + if (strcmp (s, "W")) + pic16_emitcode ("movf", "%s,w", s); + + pic16_emitcode ("movwf", "%s", buffer); + + if (strcmp (s, "W")) + { + pic16_emitcode (";BUG!? should have this:movf", "%s,w %d", s, __LINE__); + if (offset >= aop->size) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (aop, offset)); + break; + } + else + pic16_emitpcode (POC_MOVLW, pic16_popGetImmd (s, offset, 0)); + } + + pic16_emitpcode (POC_MOVWF, pic16_popGet (aop, offset)); + + + } + break; + + case AOP_REG: + if (strcmp (aop->aopu.aop_reg[offset]->name, s) != 0) // && + { + //strcmp(aop->aopu.aop_reg[offset]->dname,s)!= 0){ + /* + if (*s == '@' || + strcmp(s,"r0") == 0 || + strcmp(s,"r1") == 0 || + strcmp(s,"r2") == 0 || + strcmp(s,"r3") == 0 || + strcmp(s,"r4") == 0 || + strcmp(s,"r5") == 0 || + strcmp(s,"r6") == 0 || + strcmp(s,"r7") == 0 ) + pic16_emitcode("mov","%s,%s ; %d", + aop->aopu.aop_reg[offset]->dname,s,__LINE__); + else + */ + + if (strcmp (s, "W") == 0) + pic16_emitcode ("movf", "%s,w ; %d", s, __LINE__); + + pic16_emitcode ("movwf", "%s", aop->aopu.aop_reg[offset]->name); + + if (strcmp (s, zero) == 0) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (aop, offset)); + + } + else if (strcmp (s, "W") == 0) + { + pCodeOp *pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->type = PO_GPR_REGISTER; + + PCOR (pcop)->rIdx = -1; + PCOR (pcop)->r = NULL; + + DEBUGpic16_emitcode (";", "%d", __LINE__); + pcop->name = Safe_strdup (s); + pic16_emitpcode (POC_MOVFW, pcop); + pic16_emitpcode (POC_MOVWF, pic16_popGet (aop, offset)); + } + else if (strcmp (s, one) == 0) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (aop, offset)); + pic16_emitpcode (POC_INCF, pic16_popGet (aop, offset)); + } + else + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (aop, offset)); + } + } + break; + + case AOP_STK: + if (strcmp (s, "a") == 0) + pic16_emitcode ("push", "acc"); + else + pic16_emitcode ("push", "%s", s); + break; + + case AOP_CRY: + /* if bit variable */ + if (!aop->aopu.aop_dir) + { + pic16_emitcode ("clr", "a"); + pic16_emitcode ("rlc", "a"); + } + else + { + if (s == zero) + pic16_emitcode ("clr", "%s", aop->aopu.aop_dir); + else if (s == one) + pic16_emitcode ("setb", "%s", aop->aopu.aop_dir); + else if (!strcmp (s, "c")) + pic16_emitcode ("mov", "%s,c", aop->aopu.aop_dir); + else + { + lbl = newiTempLabel (NULL); + + if (strcmp (s, "a")) + { + MOVA (s); + } + pic16_emitcode ("clr", "c"); + pic16_emitcode ("jz", "%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "c"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + pic16_emitcode ("mov", "%s,c", aop->aopu.aop_dir); + } + } + break; + + case AOP_STR: + aop->coff = offset; + if (strcmp (aop->aopu.aop_str[offset], s)) + pic16_emitcode ("mov", "%s,%s ; %d", aop->aopu.aop_str[offset], s, __LINE__); + break; + + case AOP_ACC: + aop->coff = offset; + if (!offset && (strcmp (s, "acc") == 0)) + break; + + if (strcmp (aop->aopu.aop_str[offset], s)) + pic16_emitcode ("mov", "%s,%s ; %d", aop->aopu.aop_str[offset], s, __LINE__); + break; + + default: + fprintf (stderr, "%s:%d: unknown aop->type = 0x%x\n", __FILE__, __LINE__, aop->type); +// werror(E_INTERNAL_ERROR,__FILE__,__LINE__, +// "pic16_aopPut got unsupported aop->type"); +// exit(0); + } + +} + +/*-----------------------------------------------------------------*/ +/* pic16_mov2w - generate either a MOVLW or MOVFW based operand type */ +/*-----------------------------------------------------------------*/ +void +pic16_mov2w (asmop * aop, int offset) +{ + int isWREG = (aop->type != AOP_STA && ! strcmp (pic16_aopGet(aop, 0, TRUE, FALSE), "WREG")); + + DEBUGpic16_emitcode ("; ***", "%s %d offset=%d", __FUNCTION__, __LINE__, offset); + + if (pic16_isLitAop (aop)) + pic16_emitpcode (POC_MOVLW, pic16_popGet (aop, offset)); + else if (isWREG) + DEBUGpic16_emitcode ("; ***", "ignore MOVF\tWREG, W", __FUNCTION__, __LINE__); + else + pic16_emitpcode (POC_MOVFW, pic16_popGet (aop, offset)); +} + +void +pic16_mov2w_volatile (asmop * aop) +{ + int i; + + if (!pic16_isLitAop (aop)) + { + // may need to protect this from the peepholer -- this is not nice but works... + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (";", "VOLATILE READ - BEGIN")); + for (i = 0; i < aop->size; i++) + { + if (i > 0) + { + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (";", "VOLATILE READ - MORE")); + } // if + pic16_emitpcode (POC_MOVFW, pic16_popGet (aop, i)); + } // for + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (";", "VOLATILE READ - END")); + } +} + +void +pic16_mov2f (asmop * dst, asmop * src, int offset) +{ + if (pic16_isLitAop (src)) + { + int dstIsWREG = (dst->type != AOP_STA && ! strcmp(pic16_aopGet(dst, 0, TRUE, FALSE), "WREG")); + + pic16_emitpcode (POC_MOVLW, pic16_popGet (src, offset)); + + if (! dstIsWREG) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet(dst, offset)); + } + } + else + { + int srcIsWREG; + + if (pic16_sameRegsOfs (src, dst, offset)) + return; + + srcIsWREG = (src->type != AOP_STA && ! strcmp (pic16_aopGet (src, 0, TRUE, FALSE), "WREG")); + + if (srcIsWREG) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (dst, offset)); + } + else + { + int dstIsINTCON = (src->type != AOP_STA && ! strcmp (pic16_aopGet(src, 0, TRUE, FALSE), "INTCON")); + int dstIsPCL = (src->type != AOP_STA && ! strcmp (pic16_aopGet(src, 0, TRUE, FALSE), "PCL")); + + if (dstIsINTCON || dstIsPCL) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (src, offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (dst, offset)); + } + else + { + pic16_emitpcode(POC_MOVFF, pic16_popGet2p (pic16_popGet (src, offset), + pic16_popGet (dst, offset))); + } + } + } +} + +static void +pic16_movLit2f (pCodeOp * pc, int lit) +{ + if (0 == (lit & 0x00ff)) + { + pic16_emitpcode (POC_CLRF, pc); + } + else if (0xff == (lit & 0x00ff)) + { + pic16_emitpcode (POC_SETF, pc); + } + else + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (lit & 0x00ff)); + if (pc->type != PO_WREG) + pic16_emitpcode (POC_MOVWF, pc); + } +} + +static void +mov2fp (pCodeOp * dst, asmop * src, int offset) +{ + if (pic16_isLitAop (src)) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (src, offset)); + + if (dst->type != PO_WREG) + { + pic16_emitpcode (POC_MOVWF, dst); + } + } + else + { + if (dst->type == PO_INTCON || dst->type == PO_PCL) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (src, offset)); + pic16_emitpcode (POC_MOVWF, dst); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (src, offset), dst)); + } + } +} + +void +pic16_testStackOverflow (void) +{ +#define GSTACK_TEST_NAME "_gstack_test" + + pic16_emitpcode (POC_CALL, pic16_popGetWithString (GSTACK_TEST_NAME)); + + { + symbol *sym; + + sym = newSymbol (GSTACK_TEST_NAME, 0); + SNPRINTF(sym->rname, sizeof(sym->rname), "%s", /*port->fun_prefix, */ GSTACK_TEST_NAME); +// strcpy(sym->rname, GSTACK_TEST_NAME); + checkAddSym (&externs, sym); + } +} + +/* push pcop into stack */ +void +pic16_pushpCodeOp (pCodeOp * pcop) +{ +// DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if (pcop->type == PO_LITERAL) + { + pic16_emitpcode (POC_MOVLW, pcop); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pcop, pic16_popCopyReg (pic16_stack_postdec))); + } + + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + +/* pop pcop from stack */ +void +pic16_poppCodeOp (pCodeOp * pcop) +{ + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (pic16_stack_preinc), pcop)); + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + + +/*-----------------------------------------------------------------*/ +/* pushw - pushes wreg to stack */ +/*-----------------------------------------------------------------*/ +void +pushw (void) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + + +/*-----------------------------------------------------------------*/ +/* pushaop - pushes aop to stack */ +/*-----------------------------------------------------------------*/ +void +pushaop (asmop * aop, int offset) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (_G.resDirect) + return; + + if (pic16_isLitAop (aop)) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (aop, offset)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (aop, offset), pic16_popCopyReg (pic16_stack_postdec))); + } + + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + +/*-----------------------------------------------------------------*/ +/* popaop - pops aop from stack */ +/*-----------------------------------------------------------------*/ +void +popaop (asmop * aop, int offset) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_emitpcode (POC_MOVFF, pic16_popCombine2 (pic16_stack_preinc, PCOR (pic16_popGet (aop, offset)), 0)); + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + +void +popaopidx (asmop * aop, int offset, int index) +{ + int ofs = 1; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (STACK_MODEL_LARGE) + ofs++; + + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (index + ofs)); + pic16_emitpcode (POC_MOVFF, pic16_popCombine2 (pic16_frame_plusw, PCOR (pic16_popGet (aop, offset)), 0)); + if (pic16_options.gstack) + pic16_testStackOverflow (); +} + +/*-----------------------------------------------------------------*/ +/* pic16_getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +int +pic16_getDataSize (operand * op) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + + return AOP_SIZE (op); + + // tsd- in the pic port, the genptr size is 1, so this code here + // fails. ( in the 8051 port, the size was 4). +#if 0 + int size; + size = AOP_SIZE (op); + if (size == GPTRSIZE) + { + sym_link *type = operandType (op); + if (IS_GENPTR (type)) + { + /* generic pointer; arithmetic operations + * should ignore the high byte (pointer type). + */ + size--; + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + } + } + return size; +#endif +} + +/*-----------------------------------------------------------------*/ +/* pic16_outAcc - output Acc */ +/*-----------------------------------------------------------------*/ +void +pic16_outAcc (operand * result) +{ + int size, offset; + DEBUGpic16_emitcode ("; ***", "%s %d - ", __FUNCTION__, __LINE__); + DEBUGpic16_pic16_AopType (__LINE__, NULL, NULL, result); + + + size = pic16_getDataSize (result); + if (size) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + size--; + offset = 1; + /* unsigned or positive */ + while (size--) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset++)); + } + +} + +/*-----------------------------------------------------------------*/ +/* pic16_outBitC - output a bit C */ +/* Move to result the value of Carry flag -- VR */ +/*-----------------------------------------------------------------*/ +void +pic16_outBitC (operand * result) +{ + int i; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + fprintf (stderr, "%s:%d: pic16 port warning: unsupported case\n", __FILE__, __LINE__); + pic16_aopPut (AOP (result), "c", 0); + } + else + { + + i = AOP_SIZE (result); + while (i--) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), 0)); + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_outBitOp - output a bit from Op */ +/* Move to result the value of set/clr op -- VR */ +/*-----------------------------------------------------------------*/ +void +pic16_outBitOp (operand * result, pCodeOp * pcop) +{ + int i; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + fprintf (stderr, "%s:%d: pic16 port warning: unsupported case\n", __FILE__, __LINE__); + pic16_aopPut (AOP (result), "c", 0); + } + else + { + + i = AOP_SIZE (result); + while (i--) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } + pic16_emitpcode (POC_RRCF, pcop); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), 0)); + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_toBoolean - emit code for orl a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +void +pic16_toBoolean (operand * oper) +{ + int size = AOP_SIZE (oper) - 1; + int offset = 1; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (AOP_TYPE (oper) != AOP_ACC) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (oper), 0)); + } + while (size--) + { + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (oper), offset++)); + } +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand * op, operand * result) +{ + int size, offset = 0; + + FENTRY; + /* for this we just need to flip the + first it then copy the rest in place */ + size = AOP_SIZE (op); + assert (size == AOP_SIZE (result)); + + while (size--) + { + pic16_mov2f (AOP (result), AOP (op), offset); + offset++; + } + + /* toggle the MSB's highest bit */ + pic16_emitpcode (POC_BTG, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), offset - 1), 7)); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (iCode * ic) +{ + int lsize, rsize, i; + sym_link *optype; + symbol *label; + int needLabel = 0; + + FENTRY; + + /* assign asmops */ + pic16_aopOp (IC_LEFT (ic), ic, FALSE); + pic16_aopOp (IC_RESULT (ic), ic, TRUE); + + /* if both in bit space then special case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (IC_RESULT (ic)), 0)); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (IC_LEFT (ic)), 0)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (IC_RESULT (ic)), 0)); + goto release; + } + + optype = operandType (IC_LEFT (ic)); + + /* if float then do float stuff */ + if (IS_FLOAT (optype) || IS_FIXED (optype)) + { + if (IS_FIXED (optype)) + debugf ("implement fixed16x16 type\n", 0); + + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + goto release; + } + + /* otherwise subtract from zero by taking the 2's complement */ + lsize = AOP_SIZE (IC_LEFT (ic)); + rsize = AOP_SIZE (IC_RESULT (ic)); + label = newiTempLabel (NULL); + + if (pic16_sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + /* If the result is longer than the operand, + store sign extension (0x00 or 0xff) in W */ + if (rsize > lsize) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0x00)); + pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (pic16_popGet (AOP (IC_LEFT (ic)), lsize - 1), 7)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xFF)); + } + for (i = rsize - 1; i > 0; --i) + { + if (i > lsize - 1) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + } + else + { + pic16_emitpcode (POC_COMF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + } // if + } // for + pic16_emitpcode (POC_NEGF, pic16_popGet (AOP (IC_RESULT (ic)), 0)); + for (i = 1; i < rsize; ++i) + { + if (i == rsize - 1) + { + emitSKPNZ; + } + else + { + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (label->key)); + needLabel++; + } + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + } // for + } + else + { + for (i = min (rsize, lsize) - 1; i >= 0; i--) + { + pic16_emitpcode (POC_COMFW, pic16_popGet (AOP (IC_LEFT (ic)), i)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + } // for + /* Sign extend if the result is longer than the operand */ + if (rsize > lsize) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0x00)); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (IC_RESULT (ic)), lsize - 1), 7)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xFF)); + for (i = rsize - 1; i > lsize - 1; --i) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + } // for + } // if + if (rsize > 1) + { + for (i = 0; i < rsize - 2; i++) + { + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (IC_RESULT (ic)), i)); + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (label->key)); + needLabel++; + } // for + pic16_emitpcode (POC_INFSNZ, pic16_popGet (AOP (IC_RESULT (ic)), rsize - 2)); + } // if + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (IC_RESULT (ic)), rsize - 1)); + } + if (needLabel) + pic16_emitpLabel (label->key); + +release: + /* release the aops */ + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, (RESULTONSTACK (ic) ? 0 : 1)); + pic16_freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +void +pic16_loadFromReturn (operand * op, int offset, pCodeOp * src) +{ + if ((AOP (op)->type == AOP_PCODE) && (AOP (op)->aopu.pcop->type == PO_IMMEDIATE)) + { + pic16_emitpcode (POC_MOVFW, src); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (op), offset)); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (src, pic16_popGet (AOP (op), offset))); + } +} + +/*-----------------------------------------------------------------*/ + +static void +free_stack_parameters (int Parameter_count) +{ + if (Parameter_count < 1) + { + return; + } + + if (Parameter_count == 1) + { + pic16_emitpcode (POC_MOVF, pic16_popCopyReg (pic16_stack_postinc)); + } + else if (Parameter_count == 2) + { + pic16_emitpcode (POC_MOVF, pic16_popCopyReg (pic16_stack_postinc)); + pic16_emitpcode (POC_MOVF, pic16_popCopyReg (pic16_stack_postinc)); + } + else + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (Parameter_count)); + pic16_emitpcode (POC_ADDWF, pic16_popCopyReg (pic16_stackpnt_lo)); // &pic16_pc_fsr1l)); + + if (STACK_MODEL_LARGE) + { + emitSKPNC; + pic16_emitpcode (POC_INCF, pic16_popCopyReg (pic16_stackpnt_hi)); // &pic16_pc_fsr1h)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - assign results to oper, rescall==1 is */ +/* called from genCall() or genPcall() */ +/*-----------------------------------------------------------------*/ +static void +assignResultValue (operand * oper, int res_size, int rescall) +{ + int size = AOP_SIZE (oper); + int offset = 0; + + FENTRY2; +// DEBUGpic16_emitcode ("; ***","%s %d rescall:%d size:%d",__FUNCTION__,__LINE__,rescall,size); // patch 14 + DEBUGpic16_pic16_AopType (__LINE__, oper, NULL, NULL); + + if (rescall) + { + /* assign result from a call/pcall function() */ + + /* function results are stored in a special order, + * see top of file with Function return policy, or manual */ + + if (size <= 4) + { + /* 8-bits, result in WREG */ + if (AOP_TYPE(oper) != AOP_ACC) + { + /* If destination NOT WREG. */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (oper), 0)); + } + else + { + DEBUGpic16_emitcode ("; ***", "ignore MOVWF\tWREG", __FUNCTION__, __LINE__); + } + + if (size > 1 && res_size > 1) + { + /* 16-bits, result in PRODL:WREG */ + pic16_loadFromReturn (oper, 1, pic16_popCopyReg (&pic16_pc_prodl)); + } + + if (size > 2 && res_size > 2) + { + /* 24-bits, result in PRODH:PRODL:WREG */ + pic16_loadFromReturn (oper, 2, pic16_popCopyReg (&pic16_pc_prodh)); // patch 14 + } + + if (size > 3 && res_size > 3) + { + /* 32-bits, result in FSR0L:PRODH:PRODL:WREG */ + pic16_loadFromReturn (oper, 3, pic16_popCopyReg (&pic16_pc_fsr0l)); // patch14 + } + + pic16_addSign (oper, res_size, IS_UNSIGNED (operandType (oper))); + + } + else + { + /* >32-bits, result on stack, and FSR0 points to beginning. + * Fix stack when done */ + /* FIXME FIXME */ +// debugf("WARNING: Possible bug when returning more than 4-bytes\n"); + while (size--) + { +// DEBUGpic16_emitcode("; ", "POC_MOVLW %d", GpseudoStkPtr); +// DEBUGpic16_emitcode("; ", "POC_MOVFW PLUSW2"); + + popaopidx (AOP (oper), size, GpseudoStkPtr); + GpseudoStkPtr++; + } + + /* fix stack */ + free_stack_parameters (AOP_SIZE (oper)); + } + } + else + { + int areg = 0; /* matching argument register */ + +// debugf("_G.useWreg = %d\tGpseudoStkPtr = %d\n", _G.useWreg, GpseudoStkPtr); + areg = SPEC_ARGREG (OP_SYM_ETYPE (oper)) - 1; + + + /* its called from genReceive (probably) -- VR */ + /* I hope this code will not be called from somewhere else in the future! + * We manually set the pseudo stack pointer in genReceive. - dw + */ + if (!GpseudoStkPtr && _G.useWreg) + { +// DEBUGpic16_emitcode("; ", "pop %d", GpseudoStkPtr); + + /* The last byte in the assignment is in W */ + if (areg <= GpseudoStkPtr) + { + size--; + + if (AOP_TYPE(oper) != AOP_ACC) + { + /* If destination NOT WREG. */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (oper), offset /*size*/)); + } + else + { + DEBUGpic16_emitcode ("; ***", "ignore MOVWF\tWREG", __FUNCTION__, __LINE__); + } + + offset++; +// debugf("receive from WREG\n", 0); + } + GpseudoStkPtr++; /* otherwise the calculation below fails (-_G.useWreg) */ + } +// GpseudoStkPtr++; + _G.stack_lat = AOP_SIZE (oper) - 1; + + while (size) + { + size--; + GpseudoStkPtr++; + popaopidx (AOP (oper), offset, GpseudoStkPtr - _G.useWreg); +// debugf("receive from STACK\n", 0); + offset++; + } + } +} + + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (iCode * ic) +{ +// int size, offset=0; + + FENTRY; + DEBUGpic16_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); + + if (ic->parmPush) + { + pic16_aopOp (IC_LEFT (ic), ic, FALSE); + + /* send to stack as normal */ + addSet (&_G.sendSet, ic); +// addSetHead(&_G.sendSet,ic); + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + } + + +#if 0 + int size, offset = 0; + char *l; + + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + + /* and the item is spilt then do nothing */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + pic16_aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + /* push it on the stack */ + while (size--) + { + l = pic16_aopGet (AOP (IC_LEFT (ic)), offset++, FALSE, TRUE); + if (*l == '#') + { + MOVA (l); + l = "acc"; + } + pic16_emitcode ("push", "%s", l); + } + return; + } + + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +#endif +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (iCode * ic) +{ + FENTRY; + DEBUGpic16_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); +#if 0 + int size, offset; + + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + pic16_aopOp (IC_LEFT (ic), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = (size - 1); + while (size--) + pic16_emitcode ("pop", "%s", pic16_aopGet (AOP (IC_LEFT (ic)), offset--, FALSE, TRUE)); + + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); +#endif +} + +static int +wparamCmp (void *p1, void *p2) +{ + return (!strcmp ((char *) p1, (char *) p2)); +} + +int +inWparamList (char *s) +{ + return isinSetWith (wparamList, s, wparamCmp); +} + + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (iCode * ic) +{ + sym_link *ftype; + int stackParms = 0; + int use_wreg = 0; + int inwparam = 0; + char *fname; + + FENTRY; + + ftype = OP_SYM_TYPE (IC_LEFT (ic)); + /* if caller saves & we have not saved then */ +// if (!ic->regsSaved) +// saveRegisters(ic); + + /* initialise stackParms for IPUSH pushes */ +// stackParms = pseudoStkPtr; +// fprintf(stderr, "%s:%d ic parmBytes = %d\n", __FILE__, __LINE__, ic->parmBytes); + fname = OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name; + inwparam = (inWparamList (OP_SYMBOL (IC_LEFT (ic))->name)) || (FUNC_ISWPARAM (OP_SYM_TYPE (IC_LEFT (ic)))); + +#if 0 + gpsimDebug_StackDump (__FILE__, __LINE__, fname); +#endif + + /* if send set is not empty the assign */ + if (_G.sendSet) + { + iCode *sic; + int pseudoStkPtr = -1; + int firstTimeThruLoop = 1; + + + /* reverse sendSet if function is not reentrant */ + if (!IFFUNC_ISREENT (ftype)) + _G.sendSet = reverseSet (_G.sendSet); + + /* First figure how many parameters are getting passed */ + stackParms = 0; + use_wreg = 0; + + for (sic = setFirstItem (_G.sendSet); sic; sic = setNextItem (_G.sendSet)) + { + int size; +// int offset = 0; + + pic16_aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + + stackParms += size; + + /* pass the last byte through WREG */ + if (inwparam) + { + + while (size--) + { + DEBUGpic16_emitcode ("; ", "%d left %s", __LINE__, pic16_AopType (AOP_TYPE (IC_LEFT (sic)))); + DEBUGpic16_emitcode ("; ", "push %d", pseudoStkPtr - 1); + + if (!firstTimeThruLoop) + { + /* If this is not the first time we've been through the loop + * then we need to save the parameter in a temporary + * register. The last byte of the last parameter is + * passed in W. */ + + pushw (); +// --pseudoStkPtr; // sanity check + use_wreg = 1; + } + + firstTimeThruLoop = 0; + + pic16_mov2w (AOP (IC_LEFT (sic)), size); + +// offset++; + } + } + else + { + /* all arguments are passed via stack */ + use_wreg = 0; + + while (size--) + { + DEBUGpic16_emitcode ("; ", "%d left %s", __LINE__, pic16_AopType (AOP_TYPE (IC_LEFT (sic)))); + DEBUGpic16_emitcode ("; ", "push %d", pseudoStkPtr - 1); + +// pushaop(AOP(IC_LEFT(sic)), size); + pic16_mov2w (AOP (IC_LEFT (sic)), size); + + if (!_G.resDirect) + pushw (); + } + } + + pic16_freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + + if (inwparam) + { + if (IFFUNC_HASVARARGS (ftype) || IFFUNC_ISREENT (ftype)) + { + pushw (); /* save last parameter to stack if functions has varargs */ + use_wreg = 0; + } + else + use_wreg = 1; + } + else + use_wreg = 0; + + _G.stackRegSet = _G.sendSet; + _G.sendSet = NULL; + } + + /* make the call */ + pic16_emitpcode (POC_CALL, pic16_popGetWithString (fname)); + + GpseudoStkPtr = 0; + + /* if we need to assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) + && (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + + _G.accInUse++; + pic16_aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + /* Must not assign an 8-bit result to a 16-bit variable; + * this would use (used...) the uninitialized PRODL! */ + /* FIXME: Need a proper way to obtain size of function result type, + * OP_SYM_ETYPE does not work: it dereferences pointer types! */ + assignResultValue (IC_RESULT (ic), getSize (OP_SYM_TYPE (IC_LEFT (ic))->next), 1); + + DEBUGpic16_emitcode ("; ", "%d left %s", __LINE__, pic16_AopType (AOP_TYPE (IC_RESULT (ic)))); + + pic16_freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + + if (!stackParms && ic->parmBytes) + { + stackParms = ic->parmBytes; + } + + stackParms -= use_wreg; + free_stack_parameters (stackParms); + +#if 0 + gpsimDebug_StackDump (__FILE__, __LINE__, fname); +#endif + + /* adjust the stack for parameters if required */ +// fprintf(stderr, "%s:%d: %s ic->parmBytes= %d\n", __FILE__, __LINE__, OP_SYMBOL(IC_LEFT(ic))->name, ic->parmBytes); + +#if 0 + /* if register bank was saved then pop them */ + if (ic->bankSaved) + unsaverbank (FUNC_REGBANK (dtype), ic, TRUE); + + /* if we hade saved some registers then unsave them */ + if (ic->regsSaved && !IFFUNC_CALLEESAVES (dtype)) + unsaveRegisters (ic); +#endif +} + + + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/* new version, created from genCall - HJD */ +/*-----------------------------------------------------------------*/ +static void +genPcall (iCode * ic) +{ + sym_link *fntype; + int stackParms = 0; + symbol *retlbl = newiTempLabel (NULL); + pCodeOp *pcop_lbl = pic16_popGetLabel (retlbl->key); + + FENTRY; + + fntype = operandType (IC_LEFT (ic))->next; + + /* if send set is not empty the assign */ + if (_G.sendSet) + { + iCode *sic; + int pseudoStkPtr = -1; + + /* reverse sendSet if function is not reentrant */ + if (!IFFUNC_ISREENT (fntype)) + _G.sendSet = reverseSet (_G.sendSet); + + stackParms = 0; + + for (sic = setFirstItem (_G.sendSet); sic; sic = setNextItem (_G.sendSet)) + { + int size; + + pic16_aopOp (IC_LEFT (sic), sic, FALSE); + size = AOP_SIZE (IC_LEFT (sic)); + stackParms += size; + + /* all parameters are passed via stack, since WREG is clobbered + * by the calling sequence */ + while (size--) + { + DEBUGpic16_emitcode ("; ", "%d left %s", __LINE__, pic16_AopType (AOP_TYPE (IC_LEFT (sic)))); + DEBUGpic16_emitcode ("; ", "push %d", pseudoStkPtr - 1); + + pic16_mov2w (AOP (IC_LEFT (sic)), size); + pushw (); + } + + pic16_freeAsmop (IC_LEFT (sic), NULL, sic, TRUE); + } + + _G.stackRegSet = _G.sendSet; + _G.sendSet = NULL; + } + + pic16_aopOp (IC_LEFT (ic), ic, FALSE); + + // push return address + // push $ on return stack, then replace with retlbl + + /* Thanks to Thorsten Klose for pointing out that the following + * snippet should be interrupt safe */ + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_intcon), pic16_popCopyReg (&pic16_pc_postdec1))); + pic16_emitpcode (POC_BCF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_intcon), 7)); + + pic16_emitpcodeNULLop (POC_PUSH); + + pic16_emitpcode (POC_MOVLW, pic16_popGetImmd (pcop_lbl->name, 0, 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tosl)); + pic16_emitpcode (POC_MOVLW, pic16_popGetImmd (pcop_lbl->name, 1, 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tosh)); + pic16_emitpcode (POC_MOVLW, pic16_popGetImmd (pcop_lbl->name, 2, 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tosu)); + + /* Conditionally re-enable interrupts, but keep interrupt flags in + * INTCON intact (thanks to J. van der Boon, #3420588). */ + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_preinc1), 7)); + pic16_emitpcode (POC_BSF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_intcon), 7)); + + /* make the call by writing the pointer into pc */ + mov2fp (pic16_popCopyReg (&pic16_pc_pclatu), AOP (IC_LEFT (ic)), 2); + mov2fp (pic16_popCopyReg (&pic16_pc_pclath), AOP (IC_LEFT (ic)), 1); + + // note: MOVFF to PCL not allowed + pic16_mov2w (AOP (IC_LEFT (ic)), 0); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_pcl)); + + + /* return address is here: (X) */ + pic16_emitpLabelFORCE (retlbl->key); + + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + + GpseudoStkPtr = 0; + /* if we need assign a result value */ + if ((IS_ITEMP (IC_RESULT (ic)) + && (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) || IS_TRUE_SYMOP (IC_RESULT (ic))) + { + + _G.accInUse++; + pic16_aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + /* FIXME: Need proper way to obtain the function result's type. + * OP_SYM_TYPE(IC_LEFT(ic))->next does not work --> points to function pointer */ + assignResultValue (IC_RESULT (ic), getSize (OP_SYM_TYPE (IC_LEFT (ic))->next->next), 1); + + DEBUGpic16_emitcode ("; ", "%d left %s", __LINE__, pic16_AopType (AOP_TYPE (IC_RESULT (ic)))); + + pic16_freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + } + +// stackParms -= use_wreg; + + free_stack_parameters (stackParms); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (iCode * ic) +{ + // DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic)) + return 1; + } + + return 0; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* inExcludeList - return 1 if the string is in exclude Reg list */ +/*-----------------------------------------------------------------*/ +static bool +inExcludeList (char *s) +{ + DEBUGpic16_emitcode ("; ***", "%s %d - WARNING no code generated", __FUNCTION__, __LINE__); + int i = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (options.excludeRegs[i] && STRCASECMP (options.excludeRegs[i], "none") == 0) + return FALSE; + + for (i = 0; options.excludeRegs[i]; i++) + { + if (options.excludeRegs[i] && STRCASECMP (s, options.excludeRegs[i]) == 0) + return TRUE; + } + return FALSE; +} +#endif + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode * ic) +{ + symbol *sym; + sym_link *ftype; + + FENTRY; + DEBUGpic16_emitcode ("; ***", "%s %d curr label offset=%dprevious max_key=%d ", __FUNCTION__, __LINE__, pic16_labelOffset, + max_key); + + pic16_labelOffset += (max_key + 4); + max_key = 0; + GpseudoStkPtr = 0; + _G.nRegsSaved = 0; + + ftype = operandType (IC_LEFT (ic)); + sym = OP_SYMBOL (IC_LEFT (ic)); + + if (IFFUNC_ISISR (sym->type)) + { + /* create an absolute section at the interrupt vector: + * that is 0x0008 for interrupt 1 (high), 0x0018 interrupt 2 (low) */ + symbol *asym; + char asymname[128]; + pBlock *apb; + + //debugf ("interrupt number: %hhi\n", FUNC_INTNO (sym->type)); + + if (FUNC_INTNO (sym->type) == INTNO_UNSPEC) + SNPRINTF(asymname, sizeof(asymname), "ivec_%s", sym->name); + else + SNPRINTF(asymname, sizeof(asymname), "ivec_0x%x_%s", FUNC_INTNO (sym->type), sym->name); + + /* when an interrupt is declared as naked, do not emit the special + * wrapper segment at vector address. The user should take care for + * this instead. -- VR */ + + if (!IFFUNC_ISNAKED (ftype) && (FUNC_INTNO (sym->type) != INTNO_UNSPEC)) + { + asym = newSymbol (asymname, 0); + apb = pic16_newpCodeChain (NULL, 'A', pic16_newpCodeCharP ("; Starting pCode block for absolute section")); + pic16_addpBlock (apb); + + pic16_addpCode2pBlock (apb, pic16_newpCodeCharP (";-----------------------------------------")); + pic16_addpCode2pBlock (apb, pic16_newpCodeFunction (moduleName, asym->name)); + //pic16_addpCode2pBlock (apb, pic16_newpCode (POC_GOTO, pic16_popGetWithString (sym->rname))); + //pic16_addpCode2pBlock (apb, pic16_newpCode (POC_GOTO, pic16_newpCodeOpLabel (sym->rname, 0))); + pic16_addpCode2pBlock (apb, pic16_newpCodeAsmDir ("GOTO", "%s", sym->rname)); /* this suppresses a warning in LinkFlow */ + + /* mark the end of this tiny function */ + pic16_addpCode2pBlock (apb, pic16_newpCodeFunction (NULL, NULL)); + } + else + { + SNPRINTF(asymname, sizeof(asymname), "%s", sym->rname); + } + + { + absSym *abSym; + + abSym = Safe_alloc(sizeof(absSym)); + strncpy(abSym->name, asymname, sizeof(abSym->name) - 1); + abSym->name[sizeof(abSym->name) - 1] = '\0'; + + switch (FUNC_INTNO (sym->type)) + { + case 0: + abSym->address = 0x000000; + break; + case 1: + abSym->address = 0x000008; + break; + case 2: + abSym->address = 0x000018; + break; + default: + //fprintf (stderr, "no interrupt number is given\n"); + abSym->address = -1; + break; + } + + /* relocate interrupt vectors if needed */ + if (abSym->address != -1) + abSym->address += pic16_options.ivt_loc; + + addSet (&absSymSet, abSym); + } + } + + /* create the function header */ + pic16_emitcode (";", "-----------------------------------------"); + pic16_emitcode (";", " function %s", sym->name); + pic16_emitcode (";", "-----------------------------------------"); + + /* prevent this symbol from being emitted as 'extern' */ + pic16_stringInSet (sym->rname, &pic16_localFunctions, 1); + + pic16_emitcode ("", "%s:", sym->rname); + pic16_addpCode2pBlock (pb, pic16_newpCodeFunction (moduleName, sym->rname)); + + { + absSym *ab; + + for (ab = setFirstItem (absSymSet); ab; ab = setNextItem (absSymSet)) + { + if (!strcmp (ab->name, sym->rname)) + { + pic16_pBlockConvert2Absolute (pb); + break; + } + } + } + + currFunc = sym; /* update the currFunc symbol */ + _G.fregsUsed = sym->regsUsed; + _G.sregsAlloc = newBitVect (128); + + if (IFFUNC_ISNAKED (ftype)) + { + DEBUGpic16_emitcode ("; ***", "__naked function, no prologue"); + return; + } + + /* if this is an interrupt service routine then + * save wreg, status, bsr, prodl, prodh, fsr0l, fsr0h */ + if (IFFUNC_ISISR (sym->type)) + { + _G.usefastretfie = 1; /* use shadow registers by default */ + + /* an ISR should save: WREG, STATUS, BSR, PRODL, PRODH, FSR0L, FSR0H */ + if (!FUNC_ISSHADOWREGS (sym->type)) + { + /* do not save WREG, STATUS, BSR for high priority interrupts + * because they are stored in the hardware shadow registers already */ + _G.usefastretfie = 0; + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_status)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_bsr)); + pushw (); + } + + /* these should really be optimized somehow, because not all + * interrupt handlers modify them */ + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_prodl)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_prodh)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_fsr0l)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_fsr0h)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_pclath)); + pic16_pushpCodeOp (pic16_popCopyReg (&pic16_pc_pclatu)); + + //pic16_pBlockConvert2ISR (pb); + } + + /* emit code to setup stack frame if user enabled, + * and function is not main () */ + + //debugf (stderr, "function name: %s ARGS=%p\n", sym->name, FUNC_ARGS (sym->type)); + if (strcmp (sym->name, "main")) + { + if (0 || !options.omitFramePtr + //|| sym->regsUsed + || IFFUNC_ARGS (sym->type) || FUNC_HASSTACKPARM (sym->etype)) + { + /* setup the stack frame */ + if (STACK_MODEL_LARGE) + pic16_pushpCodeOp (pic16_popCopyReg (pic16_framepnt_hi)); + pic16_pushpCodeOp (pic16_popCopyReg (pic16_framepnt_lo)); + + if (STACK_MODEL_LARGE) + pic16_emitpcode (POC_MOVFF, pic16_popCombine2 (pic16_stackpnt_hi, pic16_framepnt_hi, 0)); + pic16_emitpcode (POC_MOVFF, pic16_popCombine2 (pic16_stackpnt_lo, pic16_framepnt_lo, 0)); + } + } + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto) && sym->stack) + { + if (sym->stack > 127) + werror (W_STACK_OVERFLOW, sym->name); + + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (sym->stack)); + pic16_emitpcode (POC_SUBWF, pic16_popCopyReg (pic16_stackpnt_lo)); //&pic16_pc_fsr1l)); + emitSKPC; + pic16_emitpcode (POC_DECF, pic16_popCopyReg (pic16_stackpnt_hi)); //&pic16_pc_fsr1h)); + } + + if (inWparamList (sym->name) || FUNC_ISWPARAM (sym->type)) + { + if (IFFUNC_HASVARARGS (sym->type) || IFFUNC_ISREENT (sym->type)) + _G.useWreg = 0; + else + _G.useWreg = 1; + } + else + _G.useWreg = 0; + + /* if callee-save to be used for this function + * then save the registers being used in this function */ + //if (IFFUNC_CALLEESAVES (sym->type)) + if (strcmp (sym->name, "main")) + { + int i; + + /* if any registers used */ + if (sym->regsUsed) + { + pic16_emitpinfo (INF_LOCALREGS, pic16_newpCodeOpLocalRegs (LR_ENTRY_BEGIN)); + + if (!pic16_options.xinst) + { + /* save the registers used */ + DEBUGpic16_emitcode ("; **", "Saving used registers in stack"); + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { +#if 0 + fprintf (stderr, "%s:%d local register w/rIdx = %d is used in function\n", __FUNCTION__, __LINE__, i); +#endif + pic16_pushpCodeOp (pic16_popRegFromIdx (i)); + _G.nRegsSaved++; + + if (!pic16_regWithIdx (i)->wasUsed) + { + fprintf (stderr, "%s:%d register %s is used in function but was wasUsed = 0\n", + __FILE__, __LINE__, pic16_regWithIdx (i)->name); + pic16_regWithIdx (i)->wasUsed = 1; + } + } + } + } + else + { + /* xinst */ + DEBUGpic16_emitcode ("; **", "Allocate a space in stack to be used as temporary registers"); + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + _G.nRegsSaved++; + } + + //pic16_emitpcode (POC_ADDFSR, pic16_popGetLit2 (2, pic16_popGetLit (_G.nRegsSaved))); + } + + pic16_emitpinfo (INF_LOCALREGS, pic16_newpCodeOpLocalRegs (LR_ENTRY_END)); + } + } + + /* if critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (ftype)) + { + genCritical (NULL); + } // if + + DEBUGpic16_emitcode ("; ", "need to adjust stack = %d", sym->stack); + //fprintf (stderr, "Function '%s' uses %d bytes of stack\n", sym->name, sym->stack); +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + + FENTRY; + + if (IFFUNC_ISNAKED (sym->type)) + { + DEBUGpic16_emitcode ("; ***", "__naked function, no epilogue"); + return; + } + + if (IFFUNC_ISCRITICAL (sym->type)) + { + genEndCritical (NULL); + } // if + + _G.stack_lat = 0; + + //sym->regsUsed = _G.fregsUsed; + + /* now we need to restore the registers */ + /* if any registers used */ + + /* first restore registers that might be used for stack access */ + if (_G.sregsAllocSet) + { + reg_info *sr; + + _G.sregsAllocSet = reverseSet (_G.sregsAllocSet); + for (sr = setFirstItem (_G.sregsAllocSet); sr; sr = setNextItem (_G.sregsAllocSet)) + { + pic16_poppCodeOp (pic16_popRegFromIdx (sr->rIdx)); + } + } + + if (strcmp (sym->name, "main") && sym->regsUsed) + { + int i; + + pic16_emitpinfo (INF_LOCALREGS, pic16_newpCodeOpLocalRegs (LR_EXIT_BEGIN)); + /* restore registers used */ + DEBUGpic16_emitcode ("; **", "Restoring used registers from stack"); + for (i = sym->regsUsed->size; i >= 0; i--) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + pic16_poppCodeOp (pic16_popRegFromIdx (i)); + _G.nRegsSaved--; + } + } + pic16_emitpinfo (INF_LOCALREGS, pic16_newpCodeOpLocalRegs (LR_EXIT_END)); + } + + if ((IFFUNC_ISREENT (sym->type) || options.stackAuto) && sym->stack) + { + if (sym->stack == 1) + { + pic16_emitpcode (POC_INFSNZ, pic16_popCopyReg (pic16_stackpnt_lo)); + pic16_emitpcode (POC_INCF, pic16_popCopyReg (pic16_stackpnt_hi)); + } + else + { + // we have to add more than one... + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postinc)); // this holds a return value! + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (sym->stack - 1)); + pic16_emitpcode (POC_ADDWF, pic16_popCopyReg (pic16_stackpnt_lo)); + emitSKPNC; + pic16_emitpcode (POC_INCF, pic16_popCopyReg (pic16_stackpnt_hi)); + pic16_emitpcode (POC_COMF, pic16_popCopyReg (&pic16_pc_wreg)); // WREG = - (WREG+1)! + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_plusw)); // this holds a retrun value! + } + } + + if (strcmp (sym->name, "main")) + { + if (0 || !options.omitFramePtr + //|| sym->regsUsed + || IFFUNC_ARGS (sym->type) || FUNC_HASSTACKPARM (sym->etype)) + { + /* restore stack frame */ + pic16_poppCodeOp (pic16_popCopyReg (pic16_framepnt_lo)); + if (STACK_MODEL_LARGE) + pic16_poppCodeOp (pic16_popCopyReg (pic16_framepnt_hi)); + } + } + + _G.useWreg = 0; + + if (IFFUNC_ISISR (sym->type)) + { + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_pclatu)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_pclath)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_fsr0h)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_fsr0l)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_prodh)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_prodl)); + + if (!FUNC_ISSHADOWREGS (sym->type)) + { + /* do not restore interrupt vector for WREG, STATUS, BSR + * for high priority interrupt, see genFunction */ + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_bsr)); + pic16_poppCodeOp (pic16_popCopyReg (&pic16_pc_status)); + } + //_G.interruptvector = 0; /* sanity check */ + + /* if debug then send end of function */ + /* if (options.debug && currFunc) */ + if (currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (_G.usefastretfie) + pic16_emitpcode (POC_RETFIE, pic16_newpCodeOpLit (1)); + else + pic16_emitpcodeNULLop (POC_RETFIE); + + pic16_addpCode2pBlock (pb, pic16_newpCodeFunction (NULL, NULL)); + + _G.usefastretfie = 0; + return; + } + + /* if debug then send end of function */ + if (currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + /* insert code to restore stack frame, if user enabled it + * and function is not main () */ + + pic16_emitpcodeNULLop (POC_RETURN); + + /* Mark the end of a function */ + pic16_addpCode2pBlock (pb, pic16_newpCodeFunction (NULL, NULL)); +} + + +void +pic16_storeForReturn (iCode * ic, /*operand *op, */ int offset, pCodeOp * dest) +{ + unsigned long lit = 1; + operand *op; + + op = IC_LEFT (ic); + + // this fails for pic16_isLitOp(op) (if op is an AOP_PCODE) + if (AOP_TYPE (op) == AOP_LIT) + { + if (!IS_FLOAT (operandType (op))) + { + lit = ulFromVal (AOP (op)->aopu.aop_lit); + } + else + { + union + { + unsigned long lit_int; + float lit_float; + } info; + + /* take care if literal is a float */ + info.lit_float = (float)floatFromVal (AOP (op)->aopu.aop_lit); + lit = info.lit_int; + } + } + + if (AOP_TYPE (op) == AOP_LIT) + { + /* FIXME: broken for + * char __at(0x456) foo; + * return &foo; + * (upper byte is 0x00 (__code space) instead of 0x80 (__data) */ + pic16_movLit2f (dest, (lit >> (8ul * offset))); + } + else if (AOP_TYPE (op) == AOP_PCODE && AOP (op)->aopu.pcop->type == PO_IMMEDIATE) + { + /* char *s= "aaa"; return s; */ + /* XXX: Using UPPER(__str_0) will yield 0b00XXXXXX, so + * that the generic pointer is interpreted correctly + * as referring to __code space, but this is fragile! */ + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (op), offset)); + /* XXX: should check that dest != WREG */ + pic16_emitpcode (POC_MOVWF, dest); + } + else + { + if (dest->type == PO_WREG && (offset == 0)) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (op), offset)); + return; + } + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (op), offset), dest)); + } +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (iCode * ic) +{ + int size; + operand *left; + + FENTRY; + /* if we have no return value then + * just generate the "ret" */ + + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + * move the return value into place */ + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + if (size <= 4) + { + if (size > 3) + pic16_storeForReturn (ic, /*IC_LEFT(ic), */ 3, pic16_popCopyReg (&pic16_pc_fsr0l)); + + if (size > 2) + pic16_storeForReturn (ic, /*IC_LEFT(ic), */ 2, pic16_popCopyReg (&pic16_pc_prodh)); + + if (size > 1) + pic16_storeForReturn (ic, /*IC_LEFT(ic), */ 1, pic16_popCopyReg (&pic16_pc_prodl)); + + pic16_storeForReturn (ic, /*IC_LEFT(ic), */ 0, pic16_popCopyReg (&pic16_pc_wreg)); + + } + else + { + /* >32-bits, setup stack and FSR0 */ + while (size--) + { +// DEBUGpic16_emitcode("; ", "POC_MOVLW %d", GpseudoStkPtr); +// DEBUGpic16_emitcode("; ", "POC_MOVFW PLUSW2"); + + pic16_pushpCodeOp (pic16_popGet (AOP (IC_LEFT (ic)), size)); + +// popaopidx(AOP(oper), size, GpseudoStkPtr); + GpseudoStkPtr++; + } + + /* setup FSR0 */ + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (pic16_stackpnt_lo), pic16_popCopyReg (&pic16_pc_fsr0l))); + + if (STACK_MODEL_LARGE) + { + pic16_emitpcode (POC_MOVFF, + pic16_popGet2p (pic16_popCopyReg (pic16_stackpnt_hi), pic16_popCopyReg (&pic16_pc_fsr0h))); + } + else + { + pic16_emitpcode (POC_CLRF, pic16_popCopyReg (pic16_stackpnt_hi)); + } + } + + pic16_freeAsmop (IC_LEFT (ic), NULL, ic, TRUE); + +jumpret: + /* generate a jump to the return label + * if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (returnLabel->key)); + pic16_emitcode ("goto", "_%05d_DS_", labelKey2num (returnLabel->key + pic16_labelOffset)); + } +} + +static set *critical_temps = NULL; + +static void +genCritical (iCode * ic) +{ + pCodeOp *saved_intcon; + + if (!critical_temps) + critical_temps = newSet (); + + saved_intcon = pic16_popGetTempReg (0); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_intcon), saved_intcon)); + pic16_emitpcode (POC_BCF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_intcon), 7)); + addSetHead (&critical_temps, saved_intcon); +} + +static void +genEndCritical (iCode * ic) +{ + pCodeOp *saved_intcon = NULL; + + saved_intcon = getSet (&critical_temps); + if (!saved_intcon) + { + fprintf (stderr, "Critical section left, but none entered -- ignoring for now.\n"); + return; + } // if + + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (saved_intcon, 7)); + pic16_emitpcode (POC_BSF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_intcon), 7)); + pic16_popReleaseTempReg (saved_intcon, 0); +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (iCode * ic) +{ + FENTRY; + + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + pic16_emitpLabel (IC_LABEL (ic)->key); +// pic16_emitcode("","_%05d_DS_:",labelKey2num (IC_LABEL(ic)->key + pic16_labelOffset)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a goto */ +/*-----------------------------------------------------------------*/ +//tsd +static void +genGoto (iCode * ic) +{ + FENTRY; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_LABEL (ic)->key)); +// pic16_emitcode ("goto","_%05d_DS_",labelKey2num (IC_LABEL(ic)->key + pic16_labelOffset)); +} + + +/*-----------------------------------------------------------------*/ +/* genMultbits :- multiplication of bits */ +/*-----------------------------------------------------------------*/ +static void +genMultbits (operand * left, operand * right, operand * result) +{ + FENTRY; + + if (!pic16_sameRegs (AOP (result), AOP (right))) + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + +} + + +/*-----------------------------------------------------------------*/ +/* genMultOneByte : 8 bit multiplication & division */ +/*-----------------------------------------------------------------*/ +static void +genMultOneByte (operand * left, operand * right, operand * result) +{ + + FENTRY; + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + DEBUGpic16_pic16_AopTypeSign (__LINE__, left, right, result); + + /* (if two literals, the value is computed before) */ + /* if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + /* size is already checked in genMult == 1 */ +// size = AOP_SIZE(result); + + if (AOP_TYPE (right) == AOP_LIT) + { + pic16_emitpcomment ("multiply lit val:%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + else + { + pic16_emitpcomment ("multiply variable :%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + + pic16_genMult8X8_n (left, right, result); +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* genMultOneWord : 16 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneWord (operand * left, operand * right, operand * result) +{ + FENTRY; + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + DEBUGpic16_pic16_AopTypeSign (__LINE__, left, right, result); + + /* (if two literals, the value is computed before) + * if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + /* size is checked already == 2 */ +// size = AOP_SIZE(result); + + if (AOP_TYPE (right) == AOP_LIT) + { + pic16_emitpcomment ("multiply lit val:%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + else + { + pic16_emitpcomment ("multiply variable :%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + + pic16_genMult16X16_16 (left, right, result); +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/* genMultOneLong : 32 bit multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneLong (operand * left, operand * right, operand * result) +{ + FENTRY; + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + DEBUGpic16_pic16_AopTypeSign (__LINE__, left, right, result); + + /* (if two literals, the value is computed before) + * if one literal, literal on the right */ + if (AOP_TYPE (left) == AOP_LIT) + { + operand *t = right; + right = left; + left = t; + } + + /* size is checked already == 4 */ +// size = AOP_SIZE(result); + + if (AOP_TYPE (right) == AOP_LIT) + { + pic16_emitpcomment ("multiply lit val:%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + else + { + pic16_emitpcomment ("multiply variable :%s by variable %s and store in %s", + pic16_aopGet (AOP (right), 0, FALSE, FALSE), + pic16_aopGet (AOP (left), 0, FALSE, FALSE), pic16_aopGet (AOP (result), 0, FALSE, FALSE)); + } + + pic16_genMult32X32_32 (left, right, result); +} +#endif + + + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + FENTRY; + /* assign the amsops */ + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + /* special cases first * + * both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genMultbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genMultOneByte (left, right, result); + goto release; + } + +#if 0 + /* if both are of size == 2 */ + if (AOP_SIZE (left) == 2 && AOP_SIZE (right) == 2) + { + genMultOneWord (left, right, result); + goto release; + } + + /* if both are of size == 4 */ + if (AOP_SIZE (left) == 4 && AOP_SIZE (right) == 4) + { + genMultOneLong (left, right, result); + goto release; + } +#endif + + fprintf (stderr, "%s: should have been transformed into function call\n", __FUNCTION__); + assert (!"Multiplication should have been transformed into function call!"); + + pic16_emitcode ("multiply ", "sizes are greater than 4 ... need to insert proper algor."); + + + fprintf (stderr, "operand sizes result: %d left: %d right: %d\n", AOP_SIZE (result), AOP_SIZE (left), AOP_SIZE (right)); + /* should have been converted to function call */ + assert (0); + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* genDivbits :- division of bits */ +/*-----------------------------------------------------------------*/ +static void +genDivbits (operand * left, operand * right, operand * result) +{ + char *l; + + FENTRY; + /* the result must be bit */ + pic16_emitcode ("mov", "b,%s", pic16_aopGet (AOP (right), 0, FALSE, FALSE)); + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + + MOVA (l); + + pic16_emitcode ("div", "ab"); + pic16_emitcode ("rrc", "a"); + pic16_aopPut (AOP (result), "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genDivOneByte : 8 bit division */ +/*-----------------------------------------------------------------*/ +static void +genDivOneByte (operand * left, operand * right, operand * result) +{ + sym_link *opetype = operandType (result); + char *l; + symbol *lbl; + int size, offset; + + /* result = divident / divisor + * - divident may be a register or a literal, + * - divisor may be a register or a literal, + * so there are 3 cases (literal / literal is optimized + * by the front-end) to handle. + * In addition we must handle signed and unsigned, which + * result in 6 final different cases -- VR */ + + FENTRY; + + size = AOP_SIZE (result) - 1; + offset = 1; + /* signed or unsigned */ + if (SPEC_USIGN (opetype)) + { + pCodeOp *pct1, /* count */ + *pct2, /* reste */ + *pct3; /* temp */ + symbol *label1, *label2, *label3;; + + + /* unsigned is easy */ + + pct1 = pic16_popGetTempReg (1); + pct2 = pic16_popGetTempReg (1); + pct3 = pic16_popGetTempReg (1); + + label1 = newiTempLabel (NULL); + label2 = newiTempLabel (NULL); + label3 = newiTempLabel (NULL); + + /* the following algorithm is extracted from divuint.c */ + + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (8)); + pic16_emitpcode (POC_MOVWF, pic16_pCodeOpCopy (pct1)); + + pic16_emitpcode (POC_CLRF, pic16_pCodeOpCopy (pct2)); + + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), 0)); + + pic16_emitpLabel (label1->key); + + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_pCodeOpCopy (pct2)); + + + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popCopyReg (&pic16_pc_wreg)); + + + emitSKPNC; + pic16_emitpcode (POC_INCF, pic16_pCodeOpCopy (pct2)); + + pic16_emitpcode (POC_MOVWF, pic16_pCodeOpCopy (pct3)); + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), 0)); + + pic16_emitpcode (POC_CPFSLT, pic16_pCodeOpCopy (pct2)); + pic16_emitpcode (POC_BRA, pic16_popGetLabel (label3->key)); + pic16_emitpcode (POC_BRA, pic16_popGetLabel (label2->key)); + + pic16_emitpLabel (label3->key); + pic16_emitpcode (POC_SUBWF, pic16_pCodeOpCopy (pct2)); + pic16_emitpcode (POC_INCF, pic16_pCodeOpCopy (pct3)); + + + + pic16_emitpLabel (label2->key); + pic16_emitpcode (POC_MOVFW, pic16_pCodeOpCopy (pct3)); + pic16_emitpcode (POC_DECFSZ, pic16_pCodeOpCopy (pct1)); + pic16_emitpcode (POC_BRA, pic16_popGetLabel (label1->key)); + + /* result is in wreg */ + if (AOP_TYPE (result) != AOP_ACC) + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + pic16_popReleaseTempReg (pct3, 1); + pic16_popReleaseTempReg (pct2, 1); + pic16_popReleaseTempReg (pct1, 1); + + return; + } + + /* signed is a little bit more difficult */ + + /* save the signs of the operands */ + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + MOVA (l); + pic16_emitcode ("xrl", "a,%s", pic16_aopGet (AOP (right), 0, FALSE, TRUE)); + pic16_emitcode ("push", "acc"); /* save it on the stack */ + + /* now sign adjust for both left & right */ + l = pic16_aopGet (AOP (right), 0, FALSE, FALSE); + MOVA (l); + lbl = newiTempLabel (NULL); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "a"); + pic16_emitcode ("inc", "a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + pic16_emitcode ("mov", "b,a"); + + /* sign adjust left side */ + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + MOVA (l); + + lbl = newiTempLabel (NULL); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "a"); + pic16_emitcode ("inc", "a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + + /* now the division */ + pic16_emitcode ("div", "ab"); + /* we are interested in the lower order + only */ + pic16_emitcode ("mov", "b,a"); + lbl = newiTempLabel (NULL); + pic16_emitcode ("pop", "acc"); + /* if there was an over flow we don't + adjust the sign of the result */ + pic16_emitcode ("jb", "ov,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + CLRC; + pic16_emitcode ("clr", "a"); + pic16_emitcode ("subb", "a,b"); + pic16_emitcode ("mov", "b,a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + + /* now we are done */ + pic16_aopPut (AOP (result), "b", 0); + if (size > 0) + { + pic16_emitcode ("mov", "c,b.7"); + pic16_emitcode ("subb", "a,acc"); + } + while (size--) + pic16_aopPut (AOP (result), "a", offset++); + +} +#endif + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + int negated = 0; + int leftVal = 0, rightVal = 0; + int signedLits = 0; + char *functions[2][2] = { {"__divschar", "__divuchar"}, {"__modschar", "__moduchar"} }; + int op = 0; + + /* Division is a very lengthy algorithm, so it is better + * to call support routines than inlining algorithm. + * Division functions written here just in case someone + * wants to inline and not use the support libraries -- VR */ + + FENTRY; + + /* assign the amsops */ + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + if (ic->op == '/') + op = 0; + else if (ic->op == '%') + op = 1; + else + assert (!"invalid operation requested in genDivMod"); + + /* get literal values */ + if (IS_VALOP (left)) + { + leftVal = (int) ulFromVal (OP_VALUE (left)); + assert (leftVal >= -128 && leftVal < 256); + if (leftVal < 0) + { + signedLits++; + } + } + if (IS_VALOP (right)) + { + rightVal = (int) ulFromVal (OP_VALUE (right)); + assert (rightVal >= -128 && rightVal < 256); + if (rightVal < 0) + { + signedLits++; + } + } + + /* We should only come here to convert all + * / : {u8_t, s8_t} x {u8_t, s8_t} -> {u8_t, s8_t} + * with exactly one operand being s8_t into + * u8_t x u8_t -> u8_t. All other cases should have been + * turned into calls to support routines beforehand... */ + if ((AOP_SIZE (left) == 1 || IS_VALOP (left)) && (AOP_SIZE (right) == 1 || IS_VALOP (right))) + { + if ((!IS_UNSIGNED (operandType (right)) || rightVal < 0) && (!IS_UNSIGNED (operandType (left)) || leftVal < 0)) + { + /* Both operands are signed or negative, use _divschar + * instead of _divuchar */ + pushaop (AOP (right), 0); + pushaop (AOP (left), 0); + + /* call _divschar */ + pic16_emitpcode (POC_CALL, pic16_popGetWithString (functions[op][0])); + + { + symbol *sym; + sym = newSymbol (functions[op][0], 0); + sym->used++; + strcpy (sym->rname, functions[op][0]); + checkAddSym (&externs, sym); + } + + /* assign result */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + if (AOP_SIZE (result) > 1) + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_prodl), pic16_popGet (AOP (result), 1))); + /* sign extend */ + pic16_addSign (result, 2, 1); + } + + /* clean up stack */ + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + + goto release; + } + + /* push right operand */ + if (IS_VALOP (right)) + { + if (rightVal < 0) + { + pic16_pushpCodeOp (pic16_popGetLit (-rightVal)); + negated++; + } + else + { + pushaop (AOP (right), 0); + } + } + else if (!IS_UNSIGNED (operandType (right))) + { + pic16_mov2w (AOP (right), 0); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (right), 0, 7)); + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + negated++; + } + else + { + pushaop (AOP (right), 0); + } + + /* push left operand */ + if (IS_VALOP (left)) + { + if (leftVal < 0) + { + pic16_pushpCodeOp (pic16_popGetLit (-leftVal)); + negated++; + } + else + { + pushaop (AOP (left), 0); + } + } + else if (!IS_UNSIGNED (operandType (left))) + { + pic16_mov2w (AOP (left), 0); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (left), 0, 7)); + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + negated++; + } + else + { + pushaop (AOP (left), 0); + } + + /* call _divuchar */ + pic16_emitpcode (POC_CALL, pic16_popGetWithString (functions[op][1])); + + { + symbol *sym; + sym = newSymbol (functions[op][1], 0); + sym->used++; + strcpy (sym->rname, functions[op][1]); + checkAddSym (&externs, sym); + } + + /* Revert negation(s) from above. + * This is inefficient: if both operands are negative, this + * should not touch WREG. However, determining that exactly + * one operand was negated costs at least 3 instructions, + * so there is nothing to be gained here, is there? + * + * I negate WREG because either operand might share registers with + * result, so assigning first might destroy an operand. */ + + /* For the modulus operator, (a/b)*b == a shall hold. + * Thus: a>0, b>0 --> a/b >= 0 and a%b >= 0 + * a>0, b<0 --> a/b <= 0 and a%b >= 0 (e.g. 128 / -5 = -25, -25*(-5) = 125 and +3 remaining) + * a<0, b>0 --> a/b <= 0 and a%b < 0 (e.g. -128 / 5 = -25, -25* 5 = -125 and -3 remaining) + * a<0, b<0 --> a/b >= 0 and a%b < 0 (e.g. -128 / -5 = 25, 25*(-5) = -125 and -3 remaining) + * Only invert the result if the left operand is negative (sigh). + */ + if (AOP_SIZE (result) <= 1 || !negated) + { + if (ic->op == '/') + { + if (IS_VALOP (right)) + { + if (rightVal < 0) + { + /* we negated this operand above */ + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + } + } + else if (!IS_UNSIGNED (operandType (right))) + { + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (right), 0, 7)); + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + } + } + + if (IS_VALOP (left)) + { + if (leftVal < 0) + { + /* we negated this operand above */ + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + } + } + else if (!IS_UNSIGNED (operandType (left))) + { + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (left), 0, 7)); + pic16_emitpcode (POC_NEGF, pic16_popCopyReg (&pic16_pc_wreg)); + } + + /* Move result to destination. */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + /* Zero-extend: no operand was signed (or result is just a byte). */ + pic16_addSign (result, 1, 0); + } + else + { + assert (AOP_SIZE (result) > 1); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), 1)); + if (ic->op == '/') + { + if (IS_VALOP (right)) + { + if (rightVal < 0) + { + /* we negated this operand above */ + pic16_emitpcode (POC_COMF, pic16_popGet (AOP (result), 1)); + } + } + else if (!IS_UNSIGNED (operandType (right))) + { + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (right), 0, 7)); + pic16_emitpcode (POC_COMF, pic16_popGet (AOP (result), 1)); + } + } + + if (IS_VALOP (left)) + { + if (leftVal < 0) + { + /* we negated this operand above */ + pic16_emitpcode (POC_COMF, pic16_popGet (AOP (result), 1)); + } + } + else if (!IS_UNSIGNED (operandType (left))) + { + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (left), 0, 7)); + pic16_emitpcode (POC_COMF, pic16_popGet (AOP (result), 1)); + } + + /* Move result to destination. */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + /* Negate result if required. */ + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (result), 1, 7)); + pic16_emitpcode (POC_NEGF, pic16_popGet (AOP (result), 0)); + + /* Sign-extend. */ + pic16_addSign (result, 2, 1); + } + + /* clean up stack */ + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + goto release; + } + +#if 0 + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genDivbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genDivOneByte (left, right, result); + goto release; + } +#endif + + /* should have been converted to function call */ + assert (0); +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* genModbits :- modulus of bits */ +/*-----------------------------------------------------------------*/ +static void +genModbits (operand * left, operand * right, operand * result) +{ + char *l; + + FENTRY; + + werror (W_POSSBUG2, __FILE__, __LINE__); + /* the result must be bit */ + pic16_emitcode ("mov", "b,%s", pic16_aopGet (AOP (right), 0, FALSE, FALSE)); + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + + MOVA (l); + + pic16_emitcode ("div", "ab"); + pic16_emitcode ("mov", "a,b"); + pic16_emitcode ("rrc", "a"); + pic16_aopPut (AOP (result), "c", 0); +} + +/*-----------------------------------------------------------------*/ +/* genModOneByte : 8 bit modulus */ +/*-----------------------------------------------------------------*/ +static void +genModOneByte (operand * left, operand * right, operand * result) +{ + sym_link *opetype = operandType (result); + char *l; + symbol *lbl; + + FENTRY; + werror (W_POSSBUG2, __FILE__, __LINE__); + + /* signed or unsigned */ + if (SPEC_USIGN (opetype)) + { + /* unsigned is easy */ + pic16_emitcode ("mov", "b,%s", pic16_aopGet (AOP (right), 0, FALSE, FALSE)); + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + MOVA (l); + pic16_emitcode ("div", "ab"); + pic16_aopPut (AOP (result), "b", 0); + return; + } + + /* signed is a little bit more difficult */ + + /* save the signs of the operands */ + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + MOVA (l); + + pic16_emitcode ("xrl", "a,%s", pic16_aopGet (AOP (right), 0, FALSE, FALSE)); + pic16_emitcode ("push", "acc"); /* save it on the stack */ + + /* now sign adjust for both left & right */ + l = pic16_aopGet (AOP (right), 0, FALSE, FALSE); + MOVA (l); + + lbl = newiTempLabel (NULL); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "a"); + pic16_emitcode ("inc", "a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + pic16_emitcode ("mov", "b,a"); + + /* sign adjust left side */ + l = pic16_aopGet (AOP (left), 0, FALSE, FALSE); + MOVA (l); + + lbl = newiTempLabel (NULL); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "a"); + pic16_emitcode ("inc", "a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + + /* now the multiplication */ + pic16_emitcode ("div", "ab"); + /* we are interested in the lower order + only */ + lbl = newiTempLabel (NULL); + pic16_emitcode ("pop", "acc"); + /* if there was an over flow we don't + adjust the sign of the result */ + pic16_emitcode ("jb", "ov,%05d_DS_", labelKey2num (lbl->key)); + pic16_emitcode ("jnb", "acc.7,%05d_DS_", labelKey2num (lbl->key)); + CLRC; + pic16_emitcode ("clr", "a"); + pic16_emitcode ("subb", "a,b"); + pic16_emitcode ("mov", "b,a"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + + /* now we are done */ + pic16_aopPut (AOP (result), "b", 0); + +} +#endif + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (iCode * ic) +{ + /* Task deferred to genDiv */ + genDiv (ic); +#if 0 + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + operand *result = IC_RESULT (ic); + + FENTRY; + + /* assign the amsops */ + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + /* special cases first */ + /* both are bits */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + genModbits (left, right, result); + goto release; + } + + /* if both are of size == 1 */ + if (AOP_SIZE (left) == 1 && AOP_SIZE (right) == 1) + { + genModOneByte (left, right, result); + goto release; + } + + /* should have been converted to function call */ + assert (0); + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +#endif +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +/* + note: May need to add parameter to indicate when a variable is in bit space. +*/ +static void +genIfxJump (iCode * ic, char *jval) +{ + FENTRY; + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + + if (strcmp (jval, "a") == 0) + emitSKPZ; + else if (strcmp (jval, "c") == 0) + emitSKPNC; + else + { + DEBUGpic16_emitcode ("; ***", "%d - assuming %s is in bit space", __LINE__, jval); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit (jval, -1, 1, PO_GPR_REGISTER)); + } + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ic)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_TRUE (ic)->key + pic16_labelOffset)); + + } + else + { + /* false label is present */ + if (strcmp (jval, "a") == 0) + emitSKPNZ; + else if (strcmp (jval, "c") == 0) + emitSKPC; + else + { + DEBUGpic16_emitcode ("; ***", "%d - assuming %s is in bit space", __LINE__, jval); + pic16_emitpcode (POC_BTFSS, pic16_newpCodeOpBit (jval, -1, 1, PO_GPR_REGISTER)); + } + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ic)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_FALSE (ic)->key + pic16_labelOffset)); + + } + + + /* mark the icode as generated */ + ic->generated = 1; +} + +static void +genIfxpCOpJump (iCode * ic, pCodeOp * jop) +{ + FENTRY; + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + DEBUGpic16_emitcode ("; ***", "%d - assuming is in bit space", __LINE__); + pic16_emitpcode (POC_BTFSC, jop); + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ic)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_TRUE (ic)->key + pic16_labelOffset)); + + } + else + { + /* false label is present */ + DEBUGpic16_emitcode ("; ***", "%d - assuming is in bit space", __LINE__); + pic16_emitpcode (POC_BTFSS, jop); + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ic)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_FALSE (ic)->key + pic16_labelOffset)); + } + + + /* mark the icode as generated */ + ic->generated = 1; +} + +#if 0 +// not needed ATM + +/*-----------------------------------------------------------------*/ +/* genSkip */ +/*-----------------------------------------------------------------*/ +static void +genSkip (iCode * ifx, int status_bit) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (!ifx) + return; + + if (IC_TRUE (ifx)) + { + switch (status_bit) + { + case 'z': + emitSKPNZ; + break; + + case 'c': + emitSKPNC; + break; + + case 'd': + emitSKPDC; + break; + + } + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + // pic16_emitcode("goto","_%05d_DS_",labelKey2num (IC_TRUE(ifx)->key + pic16_labelOffset)); + + } + else + { + + switch (status_bit) + { + + case 'z': + emitSKPZ; + break; + + case 'c': + emitSKPC; + break; + + case 'd': + emitSKPDC; + break; + } + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + // pic16_emitcode("goto","_%05d_DS_",labelKey2num (IC_FALSE(ifx)->key + pic16_labelOffset)); + + } + +} +#endif + +/*-----------------------------------------------------------------*/ +/* genSkipc */ +/*-----------------------------------------------------------------*/ +static void +genSkipc (resolvedIfx * rifx) +{ + DEBUGpic16_emitcode ("; ***", "%s %d rifx= %p", __FUNCTION__, __LINE__, rifx); + + if (!rifx) + return; + + if (rifx->condition) + emitSKPNC; + else + emitSKPC; + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rifx->lbl->key)); + rifx->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* mov2w_regOrLit :- move to WREG either the offset's byte from */ +/* aop (if it's NOT a literal) or from lit (if */ +/* aop is a literal) */ +/*-----------------------------------------------------------------*/ +void +mov2w_regOrLit (asmop * aop, unsigned long lit, int offset) +{ + if (aop->type == AOP_LIT) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (lit >> (offset * 8))); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (aop, offset)); + } +} + +/*-----------------------------------------------------------------*/ +/* genCmp :- greater or less than comparison */ +/*-----------------------------------------------------------------*/ + +/* genCmp performs a left < right comparison, stores + * the outcome in result (if != NULL) and generates + * control flow code for the ifx (if != NULL). + * + * This version leaves in sequences like + * "B[CS]F STATUS,0; BTFS[CS] STATUS,0" + * which should be optmized by the peephole + * optimizer - RN 2005-01-01 */ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign) +{ + resolvedIfx rIfx; + int size; + int offs; + symbol *templbl; + operand *dummy; + unsigned long lit; + unsigned long mask; + int performedLt; + + FENTRY; + + assert (left && right); + assert (AOP_SIZE (left) == AOP_SIZE (right)); + + size = AOP_SIZE (right) - 1; + mask = (0x100UL << (size * 8)) - 1; + // in the end CARRY holds "left < right" (performedLt == 1) or "left >= right" (performedLt == 0) + performedLt = 1; + templbl = NULL; + lit = 0; + + resolveIfx (&rIfx, ifx); + + /* handle for special cases */ + if (pic16_genCmp_special (left, right, result, ifx, &rIfx, sign)) + return; + + /********************************************************************** + * handle bits - bit compares are promoted to int compares seemingly! * + **********************************************************************/ +#if 0 + // THIS IS COMPLETELY UNTESTED! + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + pCodeOp *pcleft = pic16_popGet (AOP (left), 0); + pCodeOp *pcright = pic16_popGet (AOP (right), 0); + assert (pcleft->type == PO_GPR_BIT && pcright->type == PO_GPR_BIT); + + emitSETC; + // 1 < {0,1} is false --> clear C by skipping the next instruction + //pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (AOP(left),0), PCORB(pcleft)->bit); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (left), 0)); + // {0,1} < 0 is false --> clear C by NOT skipping the next instruction + pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (pic16_popGet (AOP (right), 0), PCORB (pcright)->bit)); + emitCLRC; // only skipped for left=0 && right=1 + + goto correct_result_in_carry; + } // if +#endif + + /************************************************* + * make sure that left is register (or the like) * + *************************************************/ + if (!isAOP_REGlike (left)) + { + DEBUGpc ("swapping arguments (AOP_TYPEs %d/%d)", AOP_TYPE (left), AOP_TYPE (right)); + assert (isAOP_LIT (left)); + assert (isAOP_REGlike (right)); + // swap left and right + // left < right <==> right > left <==> (right >= left + 1) + lit = ulFromVal (AOP (left)->aopu.aop_lit); + + if ((!sign && (lit & mask) == mask) || (sign && (lit & mask) == (mask >> 1))) + { + // MAXVALUE < right? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } // if + + // This fails for lit = 0xFF (unsigned) AND lit = 0x7F (signed), + // that's why we handled it above. + lit++; + + dummy = left; + left = right; + right = dummy; + + performedLt ^= 1; // instead of "left < right" we check for "right >= left+1, i.e. "right < left+1" + } + else if (isAOP_LIT (right)) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } // if + + assert (isAOP_REGlike (left)); // left must be register or the like + assert (isAOP_REGlike (right) || isAOP_LIT (right)); // right may be register-like or a literal + + /************************************************* + * special cases go here * + *************************************************/ + + if (isAOP_LIT (right)) + { + if (!sign) + { + // unsigned comparison to a literal + DEBUGpc ("unsigned compare: left %s lit(0x%X=%lu), size=%d", performedLt ? "<" : ">=", lit, lit, size + 1); + if (lit == 0) + { + // unsigned left < 0? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + } + else + { + // signed comparison to a literal + DEBUGpc ("signed compare: left %s lit(0x%X=%ld), size=%d, mask=%x", performedLt ? "<" : ">=", lit, lit, size + 1, + mask); + if ((lit & mask) == ((0x80 << (size * 8)) & mask)) + { + // signed left < 0x80000000? always false + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + else if (lit == 0) + { + // compare left < 0; set CARRY if SIGNBIT(left) is set + if (performedLt) + emitSETC; + else + emitCLRC; + pic16_emitpcode (POC_BTFSS, pic16_popCopyGPR2Bit (pic16_popGet (AOP (left), size), 7)); + if (performedLt) + emitCLRC; + else + emitSETC; + goto correct_result_in_carry; + } + } // if (!sign) + } // right is literal + + /************************************************* + * perform a general case comparison * + * make sure we get CARRY==1 <==> left >= right * + *************************************************/ + // compare most significant bytes + //DEBUGpc ("comparing bytes at offset %d", size); + if (!sign) + { + // unsigned comparison + if (AOP_TYPE (left) == AOP_PCODE && AOP (left)->aopu.pcop->type == PO_IMMEDIATE) + { + // right >= left(PO_IMMEDIATE) + pic16_emitpcode (POC_MOVLW, pic16_popGetImmed (AOP (left), size, 1)); // left+1 + pic16_emitpcode (POC_SUBFW, pic16_popGet(AOP (right), size)); + performedLt = 0; + } + else if (AOP_TYPE (right) == AOP_PCODE && AOP (right)->aopu.pcop->type == PO_IMMEDIATE) + { + // left < right(PO_IMMEDIATE) + pic16_emitpcode (POC_MOVLW, pic16_popGetImmed (AOP (right), size, 0)); // right+0 + pic16_emitpcode (POC_SUBFW, pic16_popGet (AOP (left), size)); + } + else + { + mov2w_regOrLit (AOP(right), lit, size); + pic16_emitpcode (POC_SUBFW, pic16_popGet (AOP (left), size)); + } + } + else + { + // signed comparison + // (add 2^n to both operands then perform an unsigned comparison) + if (isAOP_LIT (right)) + { + // left >= LIT <-> LIT-left <= 0 <-> LIT-left == 0 OR !(LIT-left >= 0) + unsigned char litbyte = (lit >> (8 * size)) & 0xFF; + + if (litbyte == 0x80) + { + // left >= 0x80 -- always true, but more bytes to come + pic16_mov2w (AOP (left), size); + pic16_emitpcode (POC_XORLW, pic16_popGetLit (0x80)); // set ZERO flag + emitSETC; + } + else + { + // left >= LIT <-> left + (-LIT) >= 0 <-> left + (0x100-LIT) >= 0x100 + pic16_mov2w (AOP (left), size); + pic16_emitpcode (POC_ADDLW, pic16_popGetLit (0x80)); + pic16_emitpcode (POC_ADDLW, pic16_popGetLit ((0x100 - (litbyte + 0x80)) & 0x00FF)); + } // if + } + else + { + /* using PRODL as a temporary register here */ + pCodeOp *pctemp = pic16_popCopyReg (&pic16_pc_prodl); + //pCodeOp *pctemp = pic16_popGetTempReg(1); + pic16_mov2w (AOP (left), size); + pic16_emitpcode (POC_ADDLW, pic16_popGetLit (0x80)); + pic16_emitpcode (POC_MOVWF, pctemp); + pic16_mov2w (AOP (right), size); + pic16_emitpcode (POC_ADDLW, pic16_popGetLit (0x80)); + pic16_emitpcode (POC_SUBFW, pctemp); + //pic16_popReleaseTempReg(pctemp, 1); + } + } // if (!sign) + + // compare remaining bytes (treat as unsigned case from above) + templbl = newiTempLabel (NULL); + offs = size; + while (offs--) + { + //DEBUGpc ("comparing bytes at offset %d", offs); + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (templbl->key)); + + if (AOP_TYPE (left) == AOP_PCODE && AOP (left)->aopu.pcop->type == PO_IMMEDIATE) + { + // right >= left(PO_IMMEDIATE) + pic16_emitpcode (POC_MOVLW, pic16_popGetImmed( AOP (left), offs, 1)); // left+1 + pic16_emitpcode (POC_SUBFW, pic16_popGet (AOP (right), offs)); + performedLt = 0; + } + else if (AOP_TYPE (right) == AOP_PCODE && AOP (right)->aopu.pcop->type == PO_IMMEDIATE) + { + // left < right(PO_IMMEDIATE) + pic16_emitpcode (POC_MOVLW, pic16_popGetImmed (AOP (right), offs, 0)); // right+0 + pic16_emitpcode (POC_SUBFW, pic16_popGet (AOP (left), offs)); + } + else + { + mov2w_regOrLit (AOP (right), lit, offs); + pic16_emitpcode (POC_SUBFW, pic16_popGet (AOP (left), offs)); + } + } // while (offs) + pic16_emitpLabel (templbl->key); + goto result_in_carry; + +result_in_carry: + + /**************************************************** + * now CARRY contains the result of the comparison: * + * SUBWF sets CARRY iff * + * F-W >= 0 <==> F >= W <==> !(F < W) * + * (F=left, W=right) * + ****************************************************/ + + if (performedLt) + { + if (result && AOP_TYPE (result) != AOP_CRY) + { + // value will be stored + emitTOGC; + } + else + { + // value wil only be used in the following genSkipc() + rIfx.condition ^= 1; + } + } // if + +correct_result_in_carry: + + // assign result to variable (if neccessary) + if (result && AOP_TYPE (result) != AOP_CRY) + { + //DEBUGpc ("assign result"); + size = AOP_SIZE (result); + while (size--) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), size)); + } // while + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), 0)); + } // if (result) + + // perform conditional jump + if (ifx) + { + //DEBUGpc ("generate control flow"); + genSkipc (&rIfx); + ifx->generated = 1; + } // if +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + FENTRY; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the amsops */ + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + genCmp (right, left, result, ifx, sign); + + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + FENTRY; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the amsops */ + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + genCmp (left, right, result, ifx, sign); + + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* pic16_isLitOp - check if operand has to be treated as literal */ +/*-----------------------------------------------------------------*/ +bool +pic16_isLitOp (operand * op) +{ + return ((AOP_TYPE (op) == AOP_LIT) + || ((AOP_TYPE (op) == AOP_PCODE) + && ((AOP (op)->aopu.pcop->type == PO_LITERAL) || (AOP (op)->aopu.pcop->type == PO_IMMEDIATE)))); +} + +/*-----------------------------------------------------------------*/ +/* pic16_isLitAop - check if operand has to be treated as literal */ +/*-----------------------------------------------------------------*/ +bool +pic16_isLitAop (asmop * aop) +{ + return ((aop->type == AOP_LIT) + || ((aop->type == AOP_PCODE) && ((aop->aopu.pcop->type == PO_LITERAL) || (aop->aopu.pcop->type == PO_IMMEDIATE)))); +} + + + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + symbol *falselbl = newiTempLabel (NULL); + symbol *donelbl = newiTempLabel (NULL); + + int preserve_result = 0; + int generate_result = 0; + int i = 0; + unsigned long lit = -1; + + FENTRY; + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + if ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (left) == AOP_CRY)) + { + werror (W_POSSBUG2, __FILE__, __LINE__); + DEBUGpic16_emitcode ("; ***", "%s %d -- ERROR", __FUNCTION__, __LINE__); + fprintf (stderr, "%s %d error - left/right CRY operands not supported\n", __FUNCTION__, __LINE__); + goto release; + } + + if (pic16_isLitOp (left) || (AOP_TYPE (right) == AOP_ACC)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + } + + if (regsInCommon (left, result) || regsInCommon (right, result)) + preserve_result = 1; + + if (result && AOP_SIZE (result)) + generate_result = 1; + + if (generate_result && !preserve_result) + { + for (i = 0; i < AOP_SIZE (result); i++) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } + + assert (AOP_SIZE (left) == AOP_SIZE (right)); + for (i = 0; i < AOP_SIZE (left); i++) + { + if (AOP_TYPE (left) != AOP_ACC) + { + if (pic16_isLitOp (left)) + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (left), i)); + else + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), i)); + } + if (pic16_isLitOp (right)) + { + if (pic16_isLitOp (left) || (0 != ((lit >> (8 * i)) & 0x00FF))) + { + pic16_emitpcode (POC_XORLW, pic16_popGet (AOP (right), i)); + } + } + else + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (right), i)); + + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (falselbl->key)); + } + + // result == true + + if (generate_result && preserve_result) + { + for (i = 0; i < AOP_SIZE (result); i++) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } + + if (generate_result) + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (result), 0)); // result = true + + if (generate_result && preserve_result) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (donelbl->key)); + + if (ifx && IC_TRUE (ifx)) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + + if (ifx && IC_FALSE (ifx)) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (donelbl->key)); + + pic16_emitpLabel (falselbl->key); + + // result == false + + if (ifx && IC_FALSE (ifx)) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + + if (generate_result && preserve_result) + { + for (i = 0; i < AOP_SIZE (result); i++) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } + + pic16_emitpLabel (donelbl->key); + + if (ifx) + ifx->generated = 1; + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); + +} + + +#if 0 +// old version kept for reference + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + unsigned long lit = 0L; + int size, offset = 0; + symbol *falselbl = newiTempLabel (NULL); + + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (ifx) + DEBUGpic16_emitcode ("; ifx is non-null", ""); + else + DEBUGpic16_emitcode ("; ifx is null", ""); + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + /* if literal, literal on the right or + if the right is in a pointer register and left + is not */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + + if (ifx && !AOP_SIZE (result)) + { + symbol *tlbl; + DEBUGpic16_emitcode ("; ***", "%s %d CASE 1", __FUNCTION__, __LINE__); + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + DEBUGpic16_emitcode ("; ***", "%s %d CASE 11", __FUNCTION__, __LINE__); + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic16_emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + pic16_emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic16_emitcode ("jb", "%s,%05d_DS_", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "c"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + } + /* if true label then we jump if condition + supplied is true */ + tlbl = newiTempLabel (NULL); + if (IC_TRUE (ifx)) + { + pic16_emitcode ("jnc", "%05d_DS_", labelKey2num (tlbl->key)); + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_TRUE (ifx)->key)); + } + else + { + pic16_emitcode ("jc", "%05d_DS_", labelKey2num (tlbl->key)); + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ifx)->key)); + } + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key + pic16_labelOffset)); + + { + /* left and right are both bit variables, result is carry */ + resolvedIfx rIfx; + + resolveIfx (&rIfx, ifx); + + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_ANDLW, pic16_popGet (AOP (left), 0)); + genSkipz2 (&rIfx, 0); + } + } + else + { + + DEBUGpic16_emitcode ("; ***", "%s %d CASE 12", __FUNCTION__, __LINE__); + + /* They're not both bit variables. Is the right a literal? */ + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + switch (size) + { + + case 1: + switch (lit & 0xff) + { + case 1: + if (IC_TRUE (ifx)) + { + pic16_emitpcode (POC_DECFW, pic16_popGet (AOP (left), offset)); + emitSKPNZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + } + else + { + pic16_emitpcode (POC_DECFSZW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + } + break; + case 0xff: + if (IC_TRUE (ifx)) + { + pic16_emitpcode (POC_INCFW, pic16_popGet (AOP (left), offset)); + emitSKPNZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + } + else + { + pic16_emitpcode (POC_INCFSZW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + } + break; + default: + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + if (lit) + pic16_emitpcode (POC_XORLW, pic16_popGetLit (lit & 0xff)); + genSkip (ifx, 'z'); + } // switch lit + + + /* end of size == 1 */ + break; + + case 2: + genc16bit2lit (left, lit, offset); + genSkip (ifx, 'z'); + break; + /* end of size == 2 */ + + default: + /* size is 4 */ + if (lit == 0) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), 1)); + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), 2)); + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), 3)); + genSkip (ifx, 'z'); + } + else + { + /* search for patterns that can be optimized */ + + genc16bit2lit (left, lit, 0); + lit >>= 16; + if (lit) + { + if (IC_TRUE (ifx)) + emitSKPZ; // if hi word unequal + else + emitSKPNZ; // if hi word equal + // fail early + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (falselbl->key)); + genc16bit2lit (left, lit, 2); + genSkip (ifx, 'z'); + } + else + { + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), 2)); + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), 3)); + genSkip (ifx, 'z'); + } + } + pic16_emitpLabel (falselbl->key); + break; + + } // switch size + + ifx->generated = 1; + goto release; + + + } + else if (AOP_TYPE (right) == AOP_CRY) + { + /* we know the left is not a bit, but that the right is */ + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (((IC_TRUE (ifx)) ? POC_BTFSC : POC_BTFSS), pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_XORLW, pic16_popGetLit (1)); + + /* if the two are equal, then W will be 0 and the Z bit is set + * we could test Z now, or go ahead and check the high order bytes if + * the variable we're comparing is larger than a byte. */ + + while (--size) + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), offset)); + + if (IC_TRUE (ifx)) + { + emitSKPNZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + // pic16_emitcode(" goto","_%05d_DS_",labelKey2num (IC_TRUE(ifx)->key + pic16_labelOffset)); + } + else + { + emitSKPZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + // pic16_emitcode(" goto","_%05d_DS_",labelKey2num (IC_FALSE(ifx)->key + pic16_labelOffset)); + } + + } + else + { + /* They're both variables that are larger than bits */ + int s = size; + + tlbl = newiTempLabel (NULL); + + while (size--) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (right), offset)); + + if (IC_TRUE (ifx)) + { + if (size) + { + emitSKPZ; + + DEBUGpic16_emitcode (";", "\tIC_TRUE emitSKPZ"); + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (tlbl->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (tlbl->key + pic16_labelOffset)); + } + else + { + emitSKPNZ; + + DEBUGpic16_emitcode (";", "\tIC_TRUE emitSKPNZ"); + + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_TRUE (ifx)->key + pic16_labelOffset)); + } + } + else + { + emitSKPZ; + + DEBUGpic16_emitcode (";", "\tnot IC_TRUE emitSKPZ"); + + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_FALSE (ifx)->key)); + pic16_emitcode (" goto", "_%05d_DS_", labelKey2num (IC_FALSE (ifx)->key + pic16_labelOffset)); + } + offset++; + } + if (s > 1 && IC_TRUE (ifx)) + { + pic16_emitpLabel (tlbl->key); + pic16_emitcode ("", "_%05d_DS_:", labelKey2num (tlbl->key + pic16_labelOffset)); + } + } + } + /* mark the icode as generated */ + ifx->generated = 1; + goto release; + } + + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + DEBUGpic16_emitcode ("; ***", "%s %d CASE 2", __FUNCTION__, __LINE__); + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned long lit = ulFromVal (AOP (right)->aopu.aop_lit); + if (lit == 0L) + { + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic16_emitcode ("cpl", "c"); + } + else if (lit == 1L) + { + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + pic16_emitcode ("clr", "c"); + } + /* AOP_TYPE(right) == AOP_CRY */ + } + else + { + symbol *lbl = newiTempLabel (NULL); + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic16_emitcode ("jb", "%s,%05d_DS_", AOP (right)->aopu.aop_dir, labelKey2num (lbl->key)); + pic16_emitcode ("cpl", "c"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (lbl->key)); + } + /* c = 1 if egal */ + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + pic16_outBitC (result); + goto release; + } + if (ifx) + { + genIfxJump (ifx, "c"); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + pic16_outBitC (result); + } + else + { + + DEBUGpic16_emitcode ("; ***", "%s %d CASE 3", __FUNCTION__, __LINE__); + gencjne (left, right, result, ifx); + /* + if(ifx) + gencjne(left,right,newiTempLabel(NULL)); + else { + if(IC_TRUE(ifx)->key) + gencjne(left,right,IC_TRUE(ifx)->key); + else + gencjne(left,right,IC_FALSE(ifx)->key); + ifx->generated = 1; + goto release ; + } + if (AOP_TYPE(result) == AOP_CRY && AOP_SIZE(result)) { + pic16_aopPut(AOP(result),"a",0); + goto release ; + } + + if (ifx) { + genIfxJump (ifx,"a"); + goto release ; + } + */ + /* if the result is used in an arithmetic operation + then put the result in place */ + /* + if (AOP_TYPE(result) != AOP_CRY) + pic16_outAcc(result); + */ + /* leave the result in acc */ + } + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} +#endif + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (iCode * ic) +{ + operand *left, *right, *result; + /* symbol *tlbl; */ + + FENTRY; + + /* note here that && operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + /* if both are bit variables */ + /* if (AOP_TYPE(left) == AOP_CRY && */ + /* AOP_TYPE(right) == AOP_CRY ) { */ + /* pic16_emitcode("mov","c,%s",AOP(left)->aopu.aop_dir); */ + /* pic16_emitcode("anl","c,%s",AOP(right)->aopu.aop_dir); */ + /* pic16_outBitC(result); */ + /* } else { */ + /* tlbl = newiTempLabel(NULL); */ + /* pic16_toBoolean(left); */ + /* pic16_emitcode("jz","%05d_DS_",labelKey2num (tlbl->key)); */ + /* pic16_toBoolean(right); */ + /* pic16_emitcode("","%05d_DS_:",labelKey2num (tlbl->key)); */ + /* pic16_outBitAcc(result); */ + /* } */ + + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +/* + tsd pic port - + modified this code, but it doesn't appear to ever get called +*/ + +static void +genOrOp (iCode * ic) +{ + operand *left, *right, *result; + symbol *tlbl; + + FENTRY; + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + pic16_emitcode ("clrc", ""); + pic16_emitcode ("btfss", "(%s >> 3), (%s & 7)", AOP (left)->aopu.aop_dir, AOP (left)->aopu.aop_dir); + pic16_emitcode ("btfsc", "(%s >> 3), (%s & 7)", AOP (right)->aopu.aop_dir, AOP (right)->aopu.aop_dir); + pic16_emitcode ("setc", ""); + + } + else + { + tlbl = newiTempLabel (NULL); + pic16_toBoolean (left); + emitSKPZ; + pic16_emitcode ("goto", "%05d_DS_", labelKey2num (tlbl->key + pic16_labelOffset)); + pic16_toBoolean (right); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key + pic16_labelOffset)); + + pic16_outBitAcc (result); + } + + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = { 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx + 1; + return 0; +} + +/*-----------------------------------------------------------------*/ +/* continueIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +continueIfTrue (iCode * ic) +{ + FENTRY; + if (IC_TRUE (ic)) + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_TRUE (ic)->key)); + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpIfTrue - */ +/*-----------------------------------------------------------------*/ +static void +jumpIfTrue (iCode * ic) +{ + FENTRY; + if (!IC_TRUE (ic)) + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ic)->key)); + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl) +{ + // ugly but optimized by peephole + FENTRY; + if (IC_TRUE (ic)) + { + symbol *nlbl = newiTempLabel (NULL); + pic16_emitcode ("sjmp", "%05d_DS_", labelKey2num (nlbl->key)); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_TRUE (ic)->key)); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (nlbl->key)); + } + else + { + pic16_emitcode ("ljmp", "%05d_DS_", labelKey2num (IC_FALSE (ic)->key)); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + resolvedIfx rIfx; + + FENTRY; + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + resolveIfx (&rIfx, ifx); + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic16_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + // if(bit & yy) + // result = bit & yy; + if (AOP_TYPE (left) == AOP_CRY) + { + // c = bit & literal; + if (AOP_TYPE (right) == AOP_LIT) + { + if (lit & 1) + { + if (size && pic16_sameRegs (AOP (result), AOP (left))) + // no change + goto release; + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // bit(result) = 0; + if (size && (AOP_TYPE (result) == AOP_CRY)) + { + pic16_emitcode ("clr", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jumpIfTrue (ifx); + goto release; + } + pic16_emitcode ("clr", "c"); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit & bit; + pic16_emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + pic16_emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // c = bit & val; + MOVA (pic16_aopGet (AOP (right), 0, FALSE, FALSE)); + // c = lsb + pic16_emitcode ("rrc", "a"); + pic16_emitcode ("anl", "c,%s", AOP (left)->aopu.aop_dir); + } + } + // bit = c + // val = c + if (size) + pic16_outBitC (result); + // if(bit & ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + // if (val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + symbol *tlbl = newiTempLabel (NULL); + int sizel = AOP_SIZE (left); + int nonnull = 0; + char emitBra; + + if (size) + emitSETC; + + /* get number of non null bytes in literal */ + while (sizel--) + { + if (lit & (0xff << (sizel * 8))) + ++nonnull; + } + + emitBra = nonnull || rIfx.condition; + + for (sizel = AOP_SIZE (left); sizel--; ++offset, lit >>= 8) + { + unsigned char bytelit = lit & 0xFF; + + if (bytelit != 0) + { + int posbit; + + --nonnull; + + /* patch provided by Aaron Colwell */ + if ((posbit = isLiteralBit (bytelit)) != 0) + { + if (nonnull) + { + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), offset, FALSE, FALSE), posbit - 1, 0, + PO_GPR_REGISTER)); + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.condition ? rIfx.lbl->key : tlbl->key)); + } + else + { + pic16_emitpcode (rIfx.condition ? POC_BTFSC : POC_BTFSS, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), offset, FALSE, FALSE), posbit - 1, 0, + PO_GPR_REGISTER)); + } + } + else + { + if (bytelit == 0xff) + { + /* Aaron had a MOVF instruction here, changed to MOVFW cause + * a peephole could optimize it out -- VR */ + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (bytelit)); + } + if (nonnull) + { + if (rIfx.condition) + { + emitSKPZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.lbl->key)); /* to false */ + } + else + { + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (tlbl->key)); /* to true */ + } + } + else + { + /* last non null byte */ + if (rIfx.condition) + emitSKPZ; + else + emitSKPNZ; + } + } + } + } + + // bit = left & literal + if (size) + { + emitCLRC; + pic16_emitpLabel (tlbl->key); + } + + // if(left & literal) + else + { + if (ifx) + { + if (emitBra) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.lbl->key)); + ifx->generated = 1; + } + pic16_emitpLabel (tlbl->key); + goto release; + } + pic16_outBitC (result); + goto release; + } + + /* if left is same as result */ + if (pic16_sameRegs (AOP (result), AOP (left))) + { + int know_W = -1; + for (; size--; offset++, lit >>= 8) + { + if (AOP_TYPE (right) == AOP_LIT) + { + switch (lit & 0xff) + { + case 0x00: + /* and'ing with 0 has clears the result */ +// pic16_emitcode("clrf","%s",pic16_aopGet(AOP(result),offset,FALSE,FALSE)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + break; + case 0xff: + /* and'ing with 0xff is a nop when the result and left are the same */ + break; + + default: + { + int p = pic16_my_powof2 ((~lit) & 0xff); + if (p >= 0) + { + /* only one bit is clear in the literal, so use a bcf instruction */ +// pic16_emitcode("bcf","%s,%d",pic16_aopGet(AOP(left),offset,FALSE,TRUE),p); + pic16_emitpcode (POC_BCF, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), offset, FALSE, FALSE), p, 0, + PO_GPR_REGISTER)); + + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("andlw", "0x%x", lit & 0xff); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (lit & 0xff)); + know_W = -1; + } + else + { + if (know_W != (lit & 0xff)) + { + pic16_emitcode ("movlw", "0x%x", lit & 0xff); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (lit & 0xff)); + } + + know_W = lit & 0xff; + pic16_emitcode ("andwf", "%s,f", pic16_aopGet (AOP (left), offset, FALSE, TRUE)); + pic16_emitpcode (POC_ANDWF, pic16_popGet (AOP (left), offset)); + } + } + } + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (right), offset)); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_ANDWF, pic16_popGet (AOP (left), offset)); + + } + } + } + + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left & right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = min (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + pic16_emitcode ("setb", "c"); + while (sizer--) + { + MOVA (pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + pic16_emitcode ("anl", "a,%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic16_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + pic16_emitcode ("clrf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + break; + case 0xff: + pic16_emitcode ("movf", "%s,w", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + break; + default: + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("andlw", "0x%02x", t); + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (t)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + pic16_emitcode ("movlw", "0x%02x", t); + pic16_emitcode ("andwf", "%s,w", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (t)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (left), offset)); + + if (AOP_TYPE (result) != AOP_ACC) + { + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + DEBUGpic16_emitcode ("; ***", "ignore MOVWF\tWREG", __FUNCTION__, __LINE__); + } + } + } + } + continue; + } + + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("andwf", "%s,w", pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (right), offset)); + } + else + { + pic16_emitcode ("movf", "%s,w", pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + pic16_emitcode ("andwf", "%s,w", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (left), offset)); + } + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + } + } + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + resolvedIfx rIfx; + + FENTRY; + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + resolveIfx (&rIfx, ifx); + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic16_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + // if(bit | yy) + // xx = bit | yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit) + { + // lit != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + //pic16_emitcode("bsf","(%s >> 3), (%s & 7)", + // AOP(result)->aopu.aop_dir, + // AOP(result)->aopu.aop_dir); + else if (ifx) + continueIfTrue (ifx); + goto release; + } + } + else + { + // lit == 0 => result = left + if (size && pic16_sameRegs (AOP (result), AOP (left))) + goto release; + pic16_emitcode (";XXX mov", "c,%s %s,%d", AOP (left)->aopu.aop_dir, __FILE__, __LINE__); + } + } + else + { + if (AOP_TYPE (right) == AOP_CRY) + { + if (pic16_sameRegs (AOP (result), AOP (left))) + { + // c = bit | bit; + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + + } + else + { + if (AOP_TYPE (result) == AOP_ACC) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0)); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (1)); + + } + else + { + + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BTFSS, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + + } + } + } + else + { + // c = bit | val; + symbol *tlbl = newiTempLabel (NULL); + pic16_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + + + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + if (AOP_TYPE (right) == AOP_ACC) + { + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0)); + emitSKPNZ; + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + } + + + + if (!((AOP_TYPE (result) == AOP_CRY) && ifx)) + pic16_emitcode (";XXX setb", "c"); + pic16_emitcode (";XXX jb", "%s,%05d_DS_", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + pic16_toBoolean (right); + pic16_emitcode (";XXX jnz", "%05d_DS_", labelKey2num (tlbl->key)); + if ((AOP_TYPE (result) == AOP_CRY) && ifx) + { + jmpTrueOrFalse (ifx, tlbl); + goto release; + } + else + { + CLRC; + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + } + } + // bit = c + // val = c + if (size) + pic16_outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + if (IS_OP_VOLATILE (left)) + { + pic16_mov2w_volatile (AOP (left)); + } // if + if (lit) + { + if (rIfx.condition) + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.lbl->key)); /* to false */ + ifx->generated = 1; + } + else + wassert (0); + + goto release; + } + + /* if left is same as result */ + if (pic16_sameRegs (AOP (result), AOP (left))) + { + int know_W = -1; + for (; size--; offset++, lit >>= 8) + { + if (AOP_TYPE (right) == AOP_LIT) + { + if (((lit & 0xff) == 0) && !IS_OP_VOLATILE (left)) + { + /* or'ing with 0 has no effect */ + continue; + } + else + { + int p = pic16_my_powof2 (lit & 0xff); + if (p >= 0) + { + /* only one bit is set in the literal, so use a bsf instruction */ + pic16_emitpcode (POC_BSF, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), offset, FALSE, FALSE), p, 0, + PO_GPR_REGISTER)); + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitpcode (POC_IORLW, pic16_popGetLit (lit & 0xff)); + know_W = -1; + } + else + { + if (know_W != (lit & 0xff)) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (lit & 0xff)); + } + + know_W = lit & 0xff; + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP(left), offset)); + } + } + + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (right), offset)); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP (left), offset)); + } + } + } + } + else + { + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left | right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + pic16_emitcode (";XXX ", " %s,%d", __FILE__, __LINE__); + + + if (size) + pic16_emitcode (";XXX setb", "c"); + while (sizer--) + { + MOVA (pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + pic16_emitcode (";XXX orl", "a,%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitcode (";XXX jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic16_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + for (; (size--); offset++) + { + // normal case + // result = left & right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + break; + default: + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("iorlw", "0x%02x", t); + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (t)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + pic16_emitcode("movlw", "0x%02x", t); + pic16_emitcode("iorwf", "%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitpcode(POC_MOVLW, pic16_popGetLit (t)); + pic16_emitpcode(POC_IORFW, pic16_popGet (AOP (left), offset)); + + if (AOP_TYPE (result) != AOP_ACC) + { + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + DEBUGpic16_emitcode ("; ***", "ignore MOVWF\tWREG", __FUNCTION__, __LINE__); + } + } + } + } + continue; + } + + // faster than result <- left, anl result,right + // and better if result is SFR + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP (right), offset)); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_IORFW, pic16_popGet (AOP (left), offset)); + } + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + } + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long lit = 0L; + resolvedIfx rIfx; + + FENTRY; + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + pic16_aopOp ((right = IC_RIGHT (ic)), ic, FALSE); + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + resolveIfx (&rIfx, ifx); + + /* if left is a literal & right is not || + if left needs acc & right does not */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (left) && !AOP_NEEDSACC (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (pic16_sameRegs (AOP (result), AOP (right))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if right is bit then exchange them */ + if (AOP_TYPE (right) == AOP_CRY && AOP_TYPE (left) != AOP_CRY) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + // if(bit ^ yy) + // xx = bit ^ yy; + if (AOP_TYPE (left) == AOP_CRY) + { + if (AOP_TYPE (right) == AOP_LIT) + { + // c = bit & literal; + if (lit >> 1) + { + // lit>>1 != 0 => result = 1 + if (AOP_TYPE (result) == AOP_CRY) + { + if (size) + { + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), offset)); + } + else if (ifx) + continueIfTrue (ifx); + goto release; + } + pic16_emitcode ("setb", "c"); + } + else + { + // lit == (0 or 1) + if (lit == 0) + { + // lit == 0, result = left + if (size && pic16_sameRegs (AOP (result), AOP (left))) + goto release; + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + } + else + { + // lit == 1, result = not(left) + if (size && pic16_sameRegs (AOP (result), AOP (left))) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (result), offset)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offset)); + pic16_emitcode ("cpl", "%s", AOP (result)->aopu.aop_dir); + goto release; + } + else + { + pic16_emitcode ("mov", "c,%s", AOP (left)->aopu.aop_dir); + pic16_emitcode ("cpl", "c"); + } + } + } + } + else + { + // right != literal + symbol *tlbl = newiTempLabel (NULL); + if (AOP_TYPE (right) == AOP_CRY) + { + // c = bit ^ bit; + pic16_emitcode ("mov", "c,%s", AOP (right)->aopu.aop_dir); + } + else + { + int sizer = AOP_SIZE (right); + // c = bit ^ val + // if val>>1 != 0, result = 1 + pic16_emitcode ("setb", "c"); + while (sizer) + { + MOVA (pic16_aopGet (AOP (right), sizer - 1, FALSE, FALSE)); + if (sizer == 1) + // test the msb of the lsb + pic16_emitcode ("anl", "a,#0xfe"); + pic16_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + sizer--; + } + // val = (0,1) + pic16_emitcode ("rrc", "a"); + } + pic16_emitcode ("jnb", "%s,%05d_DS_", AOP (left)->aopu.aop_dir, labelKey2num (tlbl->key)); + pic16_emitcode ("cpl", "c"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + } + // bit = c + // val = c + if (size) + pic16_outBitC (result); + // if(bit | ...) + else if ((AOP_TYPE (result) == AOP_CRY) && ifx) + genIfxJump (ifx, "c"); + goto release; + } + + // if(val ^ 0xZZ) - size = 0, ifx != FALSE - + // bit = val ^ 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + symbol *tlbl = newiTempLabel (NULL); + int sizel; + + if (size) + emitSETC; + + for (sizel = AOP_SIZE (left); sizel--; ++offset, lit >>= 8) + { + unsigned char bytelit = lit & 0xFF; + + switch (bytelit) + { + case 0xff: + pic16_emitpcode (POC_COMFW, pic16_popGet (AOP (left), offset)); + break; + + case 0x00: + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + break; + + default: + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (bytelit)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (left), offset)); + break; + } + if (sizel) + { + if (rIfx.condition) + { + /* rIfx.lbl might be far away... */ + emitSKPZ; + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.lbl->key)); /* to false */ + } + else + { + pic16_emitpcode (POC_BNZ, pic16_popGetLabel (tlbl->key)); /* to true */ + } + } + else + { + /* last non null byte */ + if (rIfx.condition) + emitSKPZ; + else + emitSKPNZ; + } + } + + // bit = left ^ literal + if (size) + { + emitCLRC; + pic16_emitpLabel (tlbl->key); + } + // if (left ^ literal) + else + { + if (ifx) + { + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (rIfx.lbl->key)); + ifx->generated = 1; + } + pic16_emitpLabel (tlbl->key); + goto release; + } + + pic16_outBitC (result); + goto release; + } + + if (pic16_sameRegs (AOP (result), AOP (left))) + { + /* if left is same as result */ + int know_W = -1; + + for (; size--; offset++) + { + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + if (t == 0x00L) + continue; + else + { + int p = pic16_my_powof2 (t); + + if (p >= 0) + { + /* Only one bit is toggle in the literal, so use a btg instruction. */ + pic16_emitpcode (POC_BTG, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), offset, FALSE, FALSE), p, 0, PO_GPR_REGISTER)); + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("xorlw", "0x%02x", t); + pic16_emitpcode (POC_XORLW, pic16_popGetLit (t)); + know_W = -1; + } + else + { + if (know_W != t) + { + pic16_emitcode ("movlw", "0x%02x", t); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (t)); + } + + know_W = t; + } + + pic16_emitcode ("xorwf", "%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (left), offset)); + } + } + } + else + { + if (AOP_TYPE (left) == AOP_ACC) + pic16_emitcode ("xrl", "a,%s", pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (left), offset)); + } + } + } + } + else + { + // left ^ result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + // result = bit + // if(size), result in bit + // if(!size && ifx), conditional oper: if(left ^ right) + symbol *tlbl = newiTempLabel (NULL); + int sizer = max (AOP_SIZE (left), AOP_SIZE (right)); + if (size) + pic16_emitcode ("setb", "c"); + while (sizer--) + { + if ((AOP_TYPE (right) == AOP_LIT) && (((lit >> (offset * 8)) & 0x0FFL) == 0x00L)) + { + MOVA (pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + } + else + { + MOVA (pic16_aopGet (AOP (right), offset, FALSE, FALSE)); + pic16_emitcode ("xrl", "a,%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + } + pic16_emitcode ("jnz", "%05d_DS_", labelKey2num (tlbl->key)); + offset++; + } + if (size) + { + CLRC; + pic16_emitcode ("", "%05d_DS_:", labelKey2num (tlbl->key)); + pic16_outBitC (result); + } + else if (ifx) + jmpTrueOrFalse (ifx, tlbl); + } + else + { + for (; (size--); offset++) + { + // normal case + // result = left ^ right + if (AOP_TYPE (right) == AOP_LIT) + { + int t = (lit >> (offset * 8)) & 0x0FFL; + switch (t) + { + case 0x00: + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + break; + + case 0xff: + pic16_emitpcode (POC_COMFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + break; + + default: + { + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitcode ("xorlw", "0x%02x", t); + pic16_emitpcode (POC_XORLW, pic16_popGetLit (t)); + } + else + { + pic16_emitcode ("movlw", "0x%02x", t); + pic16_emitcode ("xorwf", "%s", pic16_aopGet (AOP (left), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (t)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (left), offset)); + } + + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (result), offset, FALSE, FALSE)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + } + continue; + } + + // faster than result <- left, anl result,right + // and better if result is SFR + if (AOP_TYPE (left) == AOP_ACC) + { + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (right), offset)); + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (left), offset)); + } + if (AOP_TYPE (result) != AOP_ACC) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + DEBUGpic16_emitcode ("; ***", "ignore MOVWF\tWREG", __FUNCTION__, __LINE__); + } + } + } + } + +release: + pic16_freeAsmop (left, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (right, NULL, ic, (RESULTONSTACK (ic) ? FALSE : TRUE)); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genInline - write the inline code out */ +/*-----------------------------------------------------------------*/ +static void +pic16_genInline (iCode * ic) +{ + char *buffer, *bp, *bp1; + bool inComment = FALSE; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + genLine.lineElement.isInline += (!options.asmpeep); + + buffer = bp = bp1 = Safe_strdup (IC_INLINE (ic)); + + while ((bp1 = strstr (bp, "\\n"))) + { + *bp1++ = '\n'; + *bp1++ = ' '; + bp = bp1; + } + bp = bp1 = buffer; + +#if 0 + /* This is an experimental code for #pragma inline + and is temporarily disabled for 2.5.0 release */ + if (asmInlineMap) + { + symbol *sym; + char *s; + char *cbuf; + int cblen; + + cbuf = Safe_strdup (buffer); + cblen = strlen (buffer) + 1; + memset (cbuf, 0, cblen); + + bp = buffer; + bp1 = cbuf; + while (*bp) + { + if (*bp != '%') + *bp1++ = *bp++; + else + { + int i; + + bp++; + i = *bp - '0'; + if (i > elementsInSet (asmInlineMap)) + break; + + bp++; + s = indexSet (asmInlineMap, i); + DEBUGpc ("searching symbol s = `%s'", s); + sym = findSym (SymbolTab, NULL, s); + + if (sym->reqv) + { + strcat (bp1, sym->reqv->operand.symOperand->regs[0]->name); + } + else + { + strcat (bp1, sym->rname); + } + + while (*bp1) + bp1++; + } + + if (strlen (bp1) > cblen - 16) + { + int i = strlen (cbuf); + cblen += 50; + cbuf = realloc (cbuf, cblen); + memset (cbuf + i, 0, 50); + bp1 = cbuf + i; + } + } + + free (buffer); + buffer = Safe_strdup (cbuf); + free (cbuf); + + bp = bp1 = buffer; + } +#endif /* 0 */ + + /* emit each line as a code */ + while (*bp) + { + switch (*bp) + { + case ';': + inComment = TRUE; + ++bp; + break; + + case '\x87': + case '\n': + inComment = FALSE; + *bp++ = '\0'; + if (*bp1) + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (bp1, NULL)); // inline directly, no process + bp1 = bp; + break; + + default: + /* Add \n for labels, not dirs such as c:\mydir */ + if (!inComment && (*bp == ':') && (isspace ((unsigned char) bp[1]))) + { + ++bp; + *bp = '\0'; + ++bp; + /* print label, use this special format with NULL directive + * to denote that the argument should not be indented with tab */ + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (NULL, bp1)); // inline directly, no process + bp1 = bp; + } + else + ++bp; + break; + } + } + + if ((bp1 != bp) && *bp1) + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir (bp1, NULL)); // inline directly, no process + + Safe_free (buffer); + + /* consumed; we can free it here */ + dbuf_free (IC_INLINE (ic)); + + genLine.lineElement.isInline -= (!options.asmpeep); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (iCode * ic) +{ + operand *left, *result; + int size, same; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + + same = pic16_sameRegs (AOP (result), AOP (left)); + + size = AOP_SIZE (result); + + DEBUGpic16_emitcode ("; ***", "%s %d size:%d same:%d", __FUNCTION__, __LINE__, size, same); + + /* get the lsb and put it into the carry */ + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), 0)); + + while (size--) + { + + if (same) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (left), size)); + } + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), size)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), size)); + } + } + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (iCode * ic) +{ + operand *left, *result; + int size, offset = 0; + int same; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* rotate right with carry */ + left = IC_LEFT (ic); + result = IC_RESULT (ic); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + + same = pic16_sameRegs (AOP (result), AOP (left)); + + /* move it to the result */ + size = AOP_SIZE (result); + + /* get the msb and put it into the carry */ + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), size - 1)); + + offset = 0; + + while (size--) + { + + if (same) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), offset)); + } + else + { + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + + offset++; + } + + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + + +/* gpasm can get the highest order bit with HIGH/UPPER + * so the following probably is not needed -- VR */ + +/*-----------------------------------------------------------------*/ +/* genGetHbit - generates code get highest order bit */ +/*-----------------------------------------------------------------*/ +static void +genGetHbit (iCode * ic) +{ + operand *left, *result; + left = IC_LEFT (ic); + result = IC_RESULT (ic); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, FALSE); + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* get the highest order byte into a */ + MOVA (pic16_aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE, FALSE)); + if (AOP_TYPE (result) == AOP_CRY) + { + pic16_emitcode ("rlc", "a"); + pic16_outBitC (result); + } + else + { + pic16_emitcode ("rl", "a"); + pic16_emitcode ("anl", "a,#0x01"); + pic16_outAcc (result); + } + + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +static void +genGetABit (iCode * ic) +{ + operand *left, *right, *result; + int shCount; + int offset; + int i; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + shCount = (int) ulFromVal (AOP (right)->aopu.aop_lit); + offset = shCount / 8; + shCount %= 8; + + /* load and mask the source byte */ + pic16_mov2w (AOP (left), offset); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (1 << shCount)); + + /* move selected bit to bit 0 */ + switch (shCount) + { + case 0: + /* nothing more to do */ + break; + case 1: + /* shift bit 1 into bit 0 */ + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 4: + /* shift bit 4 into bit 0 */ + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 7: + /* shift bit 7 into bit 0 */ + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + default: + /* keep W==0, force W=0x01 otherwise */ + emitSKPZ; + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (1)); + break; + } // switch + + /* write result */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + for (i = 1; i < AOP_SIZE (result); ++i) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), i)); + } // for + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + shCount &= 0x0007; // shCount : 0..7 + switch (shCount) + { + case 0: + break; + case 1: + pic16_emitcode ("rl", "a"); + break; + case 2: + pic16_emitcode ("rl", "a"); + pic16_emitcode ("rl", "a"); + break; + case 3: + pic16_emitcode ("swap", "a"); + pic16_emitcode ("rr", "a"); + break; + case 4: + pic16_emitcode ("swap", "a"); + break; + case 5: + pic16_emitcode ("swap", "a"); + pic16_emitcode ("rl", "a"); + break; + case 6: + pic16_emitcode ("rr", "a"); + pic16_emitcode ("rr", "a"); + break; + case 7: + pic16_emitcode ("rr", "a"); + break; + } +} +#endif + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (int shCount, int doMask) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + switch (shCount) + { + case 0: + return; + break; + case 1: + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 2: + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 3: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 4: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 6: + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 7: + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + } + if (doMask) + { + /* no masking is required in genPackBits */ + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (SLMask[shCount])); + } +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount, int andmask) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + assert ((shCount >= 0) && (shCount <= 8)); + switch (shCount) + { + case 0: + return; + break; + case 1: + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 2: + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 3: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 4: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 6: + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + case 7: + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + break; + default: + // Rotating by 8 is a NOP. + break; + } + + if (andmask) + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (SRMask[shCount])); + else + DEBUGpic16_emitcode ("; ***", "%s omitting masking the result", __FUNCTION__); +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2ResultSigned (operand * left, int offl, operand * result, int offr, int shCount) +{ + int same; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + assert ((shCount >= 0) && (shCount <= 8)); + + same = ((left == result) || (AOP (left) == AOP (result))) && (offl == offr); + + /* Do NOT use result for intermediate results, it might be an SFR!. */ + switch (shCount) + { + case 0: + if (!same) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 1: + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl)); + if (same) + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 2: + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x3f)); // keep sign bit in W<5> + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (PCOP (&pic16_pc_wreg), 5)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xc0)); // sign-extend + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 3: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x1f)); // keep sign in W<4> + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (PCOP (&pic16_pc_wreg), 4)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xe0)); // sign-extend + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 4: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x0f)); // keep sign in W<3> + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (PCOP (&pic16_pc_wreg), 3)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xf0)); // sign-extend + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x07)); // keep sign in W<2> + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (PCOP (&pic16_pc_wreg), 2)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xf8)); // sign-extend + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 6: + pic16_emitpcode (POC_RLNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x03)); // keep sign bit in W<1> + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (PCOP (&pic16_pc_wreg), 1)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xfc)); // sign-extend + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 7: + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0x00)); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (left), offl), 7)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xff)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + default: + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr)); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + int same; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + assert ((shCount >= 0) && (shCount <= 8)); + + same = ((left == result) || (AOP (left) == AOP (result))) && (offl == offr); + + /* Copy the msb into the carry if signed. */ + if (sign) + { + shiftR1Left2ResultSigned (left, offl, result, offr, shCount); + return; + } + + /* Do NOT use result for intermediate results, it might be an SFR!. */ + switch (shCount) + { + case 0: + if (!same) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 1: + if (same) + { + emitCLRC; + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + else + { + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x7f)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 2: + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x3f)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 3: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x1f)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 4: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x0f)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x07)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 6: + pic16_emitpcode (POC_RLNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x03)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 7: + pic16_emitpcode (POC_RLNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x01)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + default: + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr)); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + int same; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + assert ((shCount >= 0) && (shCount <= 8)); + + same = ((left == result) || (AOP (left) == AOP (result))) && (offl == offr); + + /* Do NOT use result for intermediate results, it might be an SFR!. */ + switch (shCount) + { + case 0: + if (!same) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 1: + if (same) + { + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), offl)); + } + else + { + pic16_emitpcode (POC_RLNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xfe)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 2: + { + if (same) + { + if (AOP_TYPE (left) == AOP_ACC) + { + /* Reduces the number of instructions. */ + pic16_emitpcode(POC_ADDFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode(POC_ADDFW, pic16_popGet (AOP (left), offl)); + } + else + { + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), offl)); + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), offl)); + } + } + else + { + pic16_emitpcode (POC_RLNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xfc)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + + break; + } + + case 3: + if (same && AOP_TYPE(left) == AOP_ACC) + { + /* Reduces the number of instructions. */ + pic16_emitpcode (POC_ADDFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ADDFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ADDFW, pic16_popGet (AOP (left), offl)); + } + else + { + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xf8)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + break; + + case 4: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xf0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xe0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 6: + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RRNCFW, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xc0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + case 7: + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x80)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + break; + + default: + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr)); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand * left, int offl, operand * result, int offr) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (!pic16_sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand * left, int offl, operand * result, int offr, int shCount) +{ + int same = pic16_sameRegs (AOP (result), AOP (left)); + int i; + + DEBUGpic16_emitcode ("; ***", "%s %d shCount:%d same:%d offl:%d offr:%d", __FUNCTION__, __LINE__, shCount, same, offl, offr); + + if (same && (offl != offr)) // shift bytes + { + if (offr > offl) + { + for (i = 1; i > -1; i--) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (result), offl + i)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + i)); + } + } + else // just treat as different later on + { + same = 0; + } + } + + if (same) + { + switch (shCount) + { + case 0: + break; + case 1: + case 2: + case 3: + + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + + while (--shCount) + { + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + } + + break; + case 4: + case 5: + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0x0f)); + pic16_emitpcode (POC_ANDWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_SWAPF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_SWAPF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr + MSB16)); + if (shCount >= 5) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + } + break; + case 6: + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xc0)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + break; + case 7: + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + + } + else + { + switch (shCount) + { + case 0: + break; + case 1: + case 2: + case 3: + /* note, use a mov/add for the shift since the mov has a + chance of getting optimized out */ + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + + while (--shCount) + { + emitCLRC; + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + } + break; + + case 4: + case 5: + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xF0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x0F)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr + MSB16)); + + + if (shCount == 5) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + } + break; + case 6: + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRNCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRNCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRNCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xc0)); + pic16_emitpcode (POC_ANDWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr)); + break; + case 7: + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + } + +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (operand * left, int offl, operand * result, int offr, int shCount, int sign) +{ + int same = pic16_sameRegs (AOP (result), AOP (left)); + int i; + DEBUGpic16_emitcode ("; ***", "%s %d shCount:%d same:%d sign:%d", __FUNCTION__, __LINE__, shCount, same, sign); + + if (same && (offl != offr)) // shift right bytes + { + if (offr < offl) + { + for (i = 0; i < 2; i++) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (result), offl + i)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + i)); + } + } + else // just treat as different later on + { + same = 0; + } + } + + switch (shCount) + { + case 0: + break; + case 1: + case 2: + case 3: + /* obtain sign from left operand */ + if (sign) + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offr + MSB16)); + else + emitCLRC; + + if (same) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + + while (--shCount) + { + if (sign) + /* now get sign from already assigned result (avoid BANKSEL) */ + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (result), offr + MSB16)); + else + emitCLRC; + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + break; + case 4: + case 5: + if (same) + { + + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xf0)); + pic16_emitpcode (POC_ANDWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_SWAPF, pic16_popGet (AOP (result), offr)); + + pic16_emitpcode (POC_SWAPF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ANDFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr)); + } + else + { + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x0f)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + + pic16_emitpcode (POC_SWAPFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0xf0)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr)); + } + + if (shCount >= 5) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (result), offr)); + } + + if (sign) + { + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xf0 + (shCount - 4) * 8)); + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (result), offr + MSB16, FALSE, FALSE), 7 - shCount, 0, + PO_GPR_REGISTER)); + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (result), offr + MSB16)); + } + + break; + + case 6: + if (same) + { + + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x03)); + if (sign) + { + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (result), offr, FALSE, FALSE), 0, 0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xfc)); + } + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_XORWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_XORFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + } + else + { + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (result), offr + MSB16)); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit (0x03)); + if (sign) + { + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (result), offr + MSB16, FALSE, FALSE), 0, 0, + PO_GPR_REGISTER)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xfc)); + } + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr + MSB16)); + //pic16_emitpcode(POC_RLCF, pic16_popGet(AOP(result),offr)); + + + } + + break; + case 7: + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl)); + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), offl + MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offr)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offr + MSB16)); + if (sign) + { + emitSKPNC; + pic16_emitpcode (POC_DECF, pic16_popGet (AOP (result), offr + MSB16)); + } + else + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (result), offr + MSB16)); + } +} + + +/*-----------------------------------------------------------------*/ +/* shiftLLeftOrResult - shift left one byte from left, or to result*/ +/*-----------------------------------------------------------------*/ +static void +shiftLLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + /* shift left accumulator */ + AccLsh (shCount, 1); + /* or with result */ + /* back to result */ + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP (result), offr)); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLeftOrResult - shift right one byte from left,or to result*/ +/*-----------------------------------------------------------------*/ +static void +shiftRLeftOrResult (operand * left, int offl, operand * result, int offr, int shCount) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), offl)); + /* shift right accumulator */ + AccRsh (shCount, 1); + /* or with result */ + /* back to result */ + pic16_emitpcode (POC_IORWF, pic16_popGet (AOP (result), offr)); +} + +/*-----------------------------------------------------------------*/ +/* genlshOne - left shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genlshOne (operand * result, operand * left, int shCount) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + shiftL1Left2Result (left, LSB, result, LSB, shCount); +} + +/*-----------------------------------------------------------------*/ +/* genlshTwo - left shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshTwo (operand * result, operand * left, int shCount) +{ + int size; + + DEBUGpic16_emitcode ("; ***", "%s %d shCount:%d", __FUNCTION__, __LINE__, shCount); + size = pic16_getDataSize (result); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + + if (size > 1) + { + if (shCount) + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + else + movLeft2Result (left, LSB, result, MSB16); + } + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + } + + /* 1 <= shCount <= 7 */ + else + { + if (size == 1) + shiftL1Left2Result (left, LSB, result, LSB, shCount); + else + shiftL2Left2Result (left, LSB, result, LSB, shCount); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftLLong - shift left one long from left to result */ +/* offr = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftLLong (operand * left, operand * result, int offr) +{ + int size = AOP_SIZE (result); + int same = pic16_sameRegs (AOP (left), AOP (result)); + int i; + + DEBUGpic16_emitcode ("; ***", "%s %d offr:%d size:%d", __FUNCTION__, __LINE__, offr, size); + + if (same && (offr == MSB16)) //shift one byte + { + for (i = size - 1; i >= MSB16; i--) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), i - 1)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (left), i)); + } + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), LSB)); + } + + if (size > LSB + offr) + { + if (same) + { + pic16_emitpcode (POC_ADDWF, pic16_popGet (AOP (left), LSB + offr)); + } + else + { + pic16_emitpcode (POC_ADDFW, pic16_popGet (AOP (left), LSB)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), LSB + offr)); + } + } + + if (size > MSB16 + offr) + { + if (same) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), MSB16 + offr)); + } + else + { + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB16 + offr)); + } + } + + if (size > MSB24 + offr) + { + if (same) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), MSB24 + offr)); + } + else + { + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), MSB24)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB24 + offr)); + } + } + + if (size > MSB32 + offr) + { + if (same) + { + pic16_emitpcode (POC_RLCF, pic16_popGet (AOP (left), MSB32 + offr)); + } + else + { + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), MSB32)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB32 + offr)); + } + } + if (offr != LSB) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + +} + +/*-----------------------------------------------------------------*/ +/* genlshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genlshFour (operand * result, operand * left, int shCount) +{ + int size; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + size = AOP_SIZE (result); + + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + /* lowest order of left goes to the highest + order of the destination */ + shiftL1Left2Result (left, LSB, result, MSB32, shCount); + else + movLeft2Result (left, LSB, result, MSB32); + + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), MSB16)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), MSB24)); + + return; + } + + /* more than two bytes */ + else if (shCount >= 16) + { + /* lower order two bytes goes to higher order two bytes */ + shCount -= 16; + /* if some more remaining */ + if (shCount) + shiftL2Left2Result (left, LSB, result, MSB24, shCount); + else + { + movLeft2Result (left, MSB16, result, MSB32); + movLeft2Result (left, LSB, result, MSB24); + } + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), MSB16)); + return; + } + + /* if more than 1 byte */ + else if (shCount >= 8) + { + /* lower order three bytes goes to higher order three bytes */ + shCount -= 8; + if (size == 2) + { + if (shCount) + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + else + movLeft2Result (left, LSB, result, MSB16); + } + else /* size = 4 */ + { + if (shCount == 0) + { + movLeft2Result (left, MSB24, result, MSB32); + movLeft2Result (left, MSB16, result, MSB24); + movLeft2Result (left, LSB, result, MSB16); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + } + else if (shCount == 1) + shiftLLong (left, result, MSB16); + else + { + shiftL2Left2Result (left, MSB16, result, MSB24, shCount); + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + shiftRLeftOrResult (left, LSB, result, MSB24, 8 - shCount); + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), LSB)); + } + } + } + + /* 1 <= shCount <= 7 */ + else if (shCount <= 3) + { + shiftLLong (left, result, LSB); + while (--shCount >= 1) + shiftLLong (result, result, LSB); + } + /* 3 <= shCount <= 7, optimize */ + else + { + shiftL2Left2Result (left, MSB24, result, MSB24, shCount); + shiftRLeftOrResult (left, MSB16, result, MSB24, 8 - shCount); + shiftL2Left2Result (left, LSB, result, LSB, shCount); + } +} + +/*-----------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count */ +/*-----------------------------------------------------------------*/ +void +pic16_genLeftShiftLiteral (operand * left, operand * right, operand * result, iCode * ic) +{ + int shCount = abs ((int) ulFromVal (AOP (right)->aopu.aop_lit)); + int size; + + FENTRY; + DEBUGpic16_emitcode ("; ***", "shCount:%d", shCount); + pic16_freeAsmop (right, NULL, ic, TRUE); + + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + size = getSize (operandType (result)); + +#if VIEW_SIZE + pic16_emitcode ("; shift left ", "result %d, left %d", size, AOP_SIZE (left)); +#endif + + /* I suppose that the left size >= result size */ + if (shCount == 0) + { + while (size--) + { + movLeft2Result (left, size, result, size); + } + } + + else if (shCount >= (size * 8)) + while (size--) + pic16_aopPut (AOP (result), zero, size); + else + { + switch (size) + { + case 1: + genlshOne (result, left, shCount); + break; + + case 2: + case 3: + genlshTwo (result, left, shCount); + break; + + case 4: + genlshFour (result, left, shCount); + break; + } + } + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------* + * genMultiAsm - repeat assembly instruction for size of register. + * if endian == 1, then the high byte (i.e base address + size of + * register) is used first else the low byte is used first; + *-----------------------------------------------------------------*/ +static void +genMultiAsm (PIC_OPCODE poc, operand * reg, int size, int endian) +{ + + int offset = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!reg) + return; + + if (!endian) + { + endian = 1; + } + else + { + endian = -1; + offset = size - 1; + } + + while (size--) + { + pic16_emitpcode (poc, pic16_popGet (AOP (reg), offset)); + offset += endian; + } + +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand * result, operand * left, int shCount, int sign) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + shiftR1Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (operand * result, operand * left, int shCount, int sign) +{ + DEBUGpic16_emitcode ("; ***", "%s %d shCount:%d", __FUNCTION__, __LINE__, shCount); + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (shCount) + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB16, result, LSB); + + pic16_addSign (result, 1, sign); + } + + /* 1 <= shCount <= 7 */ + else + shiftR2Left2Result (left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* shiftRLong - shift right one long from left to result */ +/* offl = LSB or MSB16 */ +/*-----------------------------------------------------------------*/ +static void +shiftRLong (operand * left, int offl, operand * result, int sign) +{ + int size = AOP_SIZE (result); + int same = pic16_sameRegs (AOP (left), AOP (result)); + int i; + DEBUGpic16_emitcode ("; ***", "%s %d offl:%d size:%d", __FUNCTION__, __LINE__, offl, size); + + if (same && (offl == MSB16)) //shift one byte right + { + for (i = MSB16; i < size; i++) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (left), i)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (left), i - 1)); + } + } + + if (sign) + pic16_emitpcode (POC_RLCFW, pic16_popGet (AOP (left), MSB32)); + else + emitCLRC; + + if (same) + { + if (offl == LSB) + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (left), MSB32)); + } + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), MSB32)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB32 - offl)); + } + + if (offl == MSB16) + { + /* add sign of "a" */ + pic16_addSign (result, MSB32, sign); + } + + if (same) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (left), MSB24)); + } + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), MSB24)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB24 - offl)); + } + + if (same) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (left), MSB16)); + } + else + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), MSB16)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), MSB16 - offl)); + } + + if (same) + { + pic16_emitpcode (POC_RRCF, pic16_popGet (AOP (left), LSB)); + } + else + { + if (offl == LSB) + { + pic16_emitpcode (POC_RRCFW, pic16_popGet (AOP (left), LSB)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), LSB)); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genrshFour - shift four byte by a known amount != 0 */ +/*-----------------------------------------------------------------*/ +static void +genrshFour (operand * result, operand * left, int shCount, int sign) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* if shifting more that 3 bytes */ + if (shCount >= 24) + { + shCount -= 24; + if (shCount) + shiftR1Left2Result (left, MSB32, result, LSB, shCount, sign); + else + movLeft2Result (left, MSB32, result, LSB); + + pic16_addSign (result, MSB16, sign); + } + else if (shCount >= 16) + { + shCount -= 16; + if (shCount) + shiftR2Left2Result (left, MSB24, result, LSB, shCount, sign); + else + { + movLeft2Result (left, MSB24, result, LSB); + movLeft2Result (left, MSB32, result, MSB16); + } + pic16_addSign (result, MSB24, sign); + } + else if (shCount >= 8) + { + shCount -= 8; + if (shCount == 1) + shiftRLong (left, MSB16, result, sign); + else if (shCount == 0) + { + movLeft2Result (left, MSB16, result, LSB); + movLeft2Result (left, MSB24, result, MSB16); + movLeft2Result (left, MSB32, result, MSB24); + pic16_addSign (result, MSB32, sign); + } + else //shcount >= 2 + { + shiftR2Left2Result (left, MSB16, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB32, result, MSB16, 8 - shCount); + /* the last shift is signed */ + shiftR1Left2Result (left, MSB32, result, MSB24, shCount, sign); + pic16_addSign (result, MSB32, sign); + } + } + else /* 1 <= shCount <= 7 */ + { + if (shCount <= 2) + { + shiftRLong (left, LSB, result, sign); + if (shCount == 2) + shiftRLong (result, LSB, result, sign); + } + else + { + shiftR2Left2Result (left, LSB, result, LSB, shCount, 0); + shiftLLeftOrResult (left, MSB24, result, MSB16, 8 - shCount); + shiftR2Left2Result (left, MSB24, result, MSB24, shCount, sign); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, iCode * ic, int sign) +{ + int shCount = abs ((int) ulFromVal (AOP (right)->aopu.aop_lit)); + int lsize, res_size; + + pic16_freeAsmop (right, NULL, ic, TRUE); + + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + DEBUGpic16_emitcode ("; ***", "%s %d shCount:%d result:%d left:%d", __FUNCTION__, __LINE__, shCount, AOP_SIZE (result), + AOP_SIZE (left)); + +#if VIEW_SIZE + pic16_emitcode ("; shift right ", "result %d, left %d", AOP_SIZE (result), AOP_SIZE (left)); +#endif + + lsize = pic16_getDataSize (left); + res_size = pic16_getDataSize (result); + /* test the LEFT size !!! */ + + /* I suppose that the left size >= result size */ + if (shCount == 0) + { + assert (res_size <= lsize); + while (res_size--) + { + pic16_mov2f (AOP (result), AOP (left), res_size); + } // for + } + else if (shCount >= (lsize * 8)) + { + if (sign) + { + /* + * Do NOT use + * CLRF result + * BTFSC left, 7 + * SETF result + * even for 8-bit operands; result might be an SFR. + */ + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0x00)); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (left), lsize - 1), 7)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xff)); + while (res_size--) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), res_size)); + } + } + else // unsigned + { + while (res_size--) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), res_size)); + } + } + } + else // 0 < shCount < 8*lsize + { + switch (res_size) + { + case 1: + genrshOne (result, left, shCount, sign); + break; + + case 2: + genrshTwo (result, left, shCount, sign); + break; + + case 4: + genrshFour (result, left, shCount, sign); + break; + default: + break; + } + } + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenericShift - generates code for left or right shifting */ +/*-----------------------------------------------------------------*/ +static void +genGenericShift (iCode * ic, int isShiftLeft) +{ + operand *left, *right, *result; + int offset; + int sign, signedCount; + symbol *label_complete, *label_loop_pos, *label_loop_neg, *label_negative; + PIC_OPCODE pos_shift, neg_shift; + + FENTRY; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + + sign = !SPEC_USIGN (operandType (left)); + signedCount = !SPEC_USIGN (operandType (right)); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT) + { + long lit = (long) ulFromVal (AOP (right)->aopu.aop_lit); + if (signedCount && lit < 0) + { + lit = -lit; + isShiftLeft = !isShiftLeft; + } + // we should modify right->aopu.aop_lit here! + // Instead we use abs(shCount) in genXXXShiftLiteral()... + // lit > 8*size is handled in pic16_genXXXShiftLiteral() + if (isShiftLeft) + pic16_genLeftShiftLiteral (left, right, result, ic); + else + genRightShiftLiteral (left, right, result, ic, sign); + + goto release; + } // if (right is literal) + + /* shift count is unknown then we have to form a loop. + * Note: we take only the lower order byte since shifting + * more than 32 bits make no sense anyway, ( the + * largest size of an object can be only 32 bits ) + * Note: we perform arithmetic shifts if the left operand is + * signed and we do an (effective) right shift, i. e. we + * shift in the sign bit from the left. */ + + label_complete = newiTempLabel (NULL); + label_loop_pos = newiTempLabel (NULL); + label_loop_neg = NULL; + label_negative = NULL; + pos_shift = isShiftLeft ? POC_RLCF : POC_RRCF; + neg_shift = isShiftLeft ? POC_RRCF : POC_RLCF; + + if (signedCount) + { + // additional labels needed + label_loop_neg = newiTempLabel (NULL); + label_negative = newiTempLabel (NULL); + } // if + + /* + * The code below overwrites the shift count for `val = (1 << val)' + * when it assigns LEFT to RESULT (== RIGHT == shift count). + * XXX: This problem should have been/is also addressed in ralloc.c, + * but the code there seems not to catch this case ... + */ + if (pic16_sameRegs (AOP (right), AOP (result))) + { + // We abuse FSR0L as a temporary, pic16_popGetTempReg() is too costly. + pic16_mov2w (AOP (right), 0); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_fsr0l)); + } // if + + // copy source to result -- this will effectively truncate the left operand to the size of result! + // (e.g. char c = 0x100 << -3 will become c = 0x00 >> 3 == 0x00 instad of 0x20) + // This is fine, as it only occurs for left shifting with negative count which is not standardized! + for (offset = 0; offset < min (AOP_SIZE (left), AOP_SIZE (result)); offset++) + { + pic16_mov2f (AOP (result), AOP (left), offset); + } // for + + // if result is longer than left, fill with zeros (or sign) + if (AOP_SIZE (left) < AOP_SIZE (result)) + { + if (sign && AOP_SIZE (left) > 0) + { + // shift signed operand -- fill with sign + pic16_emitpcode (POC_CLRF, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), AOP_SIZE (left) - 1), 7)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xFF)); + for (offset = AOP_SIZE (left); offset < AOP_SIZE (result); offset++) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } // for + } + else + { + // shift unsigned operand -- fill result with zeros + for (offset = AOP_SIZE (left); offset < AOP_SIZE (result); offset++) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + } // for + } + } // if (size mismatch) + + /* load/restore shift count */ + if (pic16_sameRegs (AOP (right), AOP (result))) + { + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_fsr0l)); + } + else + { + pic16_mov2w (AOP (right), 0); + } // if + + pic16_emitpcode (POC_BZ, pic16_popGetLabel (label_complete->key)); + if (signedCount) + pic16_emitpcode (POC_BN, pic16_popGetLabel (label_negative->key)); + +#if 0 + // perform a shift by one (shift count is positive) + // cycles used for shifting {unsigned,signed} values on n bytes by [unsigned,signed] shift count c>0: + // 2n+[2,3]+({1,3}+n+3)c-2+[0,2]=({4,6}+n)c+2n+[0,3] ({5,7}c+[2,5] / {6,8}c+[4, 7] / {8,10}c+[ 8,11]) + pic16_emitpLabel (label_loop_pos->key); + emitCLRC; + if (sign && (pos_shift == POC_RRCF)) + { + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), AOP_SIZE (result) - 1), 7)); + emitSETC; + } // if + genMultiAsm (pos_shift, result, AOP_SIZE (result), pos_shift == POC_RRCF); + pic16_emitpcode (POC_DECFSZ, pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_BRA, pic16_popGetLabel (label_loop_pos->key)); +#else + // perform a shift by one (shift count is positive) + // cycles used for shifting {unsigned,signed} values on n bytes by [unsigned,signed] shift count c>0: + // 2n+[2,3]+2+({0,2}+n+3)c-1+[0,2]=({3,5}+n)c+2n+[3,6] ({4,6}c+[5,8] / {5,7}c+[7,10] / {7, 9}c+[11,14]) + // This variant is slower for 0key); + if (sign && (pos_shift == POC_RRCF)) + { + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), AOP_SIZE (result) - 1), 7)); + emitSETC; + } // if + genMultiAsm (pos_shift, result, AOP_SIZE (result), pos_shift == POC_RRCF); + //pic16_emitpcode (POC_INCF, pic16_popCopyReg (&pic16_pc_wreg)); // gpsim does not like this... + pic16_emitpcode (POC_ADDLW, pic16_popGetLit (0x01)); + pic16_emitpcode (POC_BNC, pic16_popGetLabel (label_loop_pos->key)); +#endif + + if (signedCount) + { + pic16_emitpcode (POC_BRA, pic16_popGetLabel (label_complete->key)); + + pic16_emitpLabel (label_negative->key); + // perform a shift by -1 (shift count is negative) + // 2n+4+1+({0,2}+n+3)*c-1=({3,5}+n)c+2n+4 ({4,6}c+6 / {5,7}c+8 / {7,9}c+12) + emitCLRC; + pic16_emitpLabel (label_loop_neg->key); + if (sign && (neg_shift == POC_RRCF)) + { + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), AOP_SIZE (result) - 1), 7)); + emitSETC; + } // if + genMultiAsm (neg_shift, result, AOP_SIZE (result), neg_shift == POC_RRCF); + //pic16_emitpcode (POC_INCF, pic16_popCopyReg (&pic16_pc_wreg)); // gpsim does not like this... + pic16_emitpcode (POC_ADDLW, pic16_popGetLit (0x01)); + pic16_emitpcode (POC_BNC, pic16_popGetLabel (label_loop_neg->key)); + } // if (signedCount) + + pic16_emitpLabel (label_complete->key); + +release: + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +static void +genLeftShift (iCode * ic) +{ + genGenericShift (ic, 1); +} + +static void +genRightShift (iCode * ic) +{ + genGenericShift (ic, 0); +} + + +/* load FSR0 with address of/from op according to pic16_isLitOp() or if lit is 1 */ +void +pic16_loadFSR0 (operand * op, int lit) +{ + if ((IS_SYMOP (op) && OP_SYMBOL (op)->remat) || pic16_isLitOp (op)) + { + if (AOP_TYPE (op) == AOP_LIT) + { + /* handle 12 bit integers correctly */ + unsigned int val = (unsigned int) ulFromVal (AOP (op)->aopu.aop_lit); + if ((val & 0x0fff) != val) + { + fprintf (stderr, "WARNING: Accessing memory at 0x%x truncated to 0x%x.\n", val, (val & 0x0fff)); + val &= 0x0fff; + } + pic16_emitpcode (POC_LFSR, pic16_popGetLit2 (0, pic16_popGetLit12 (val))); + } + else + { + pic16_emitpcode (POC_LFSR, pic16_popGetLit2 (0, pic16_popGet (AOP (op), 0))); + } + } + else + { + assert (!IS_SYMOP (op) || !OP_SYMBOL (op)->remat); + // set up FSR0 with address of result + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (op), 0), pic16_popCopyReg (&pic16_pc_fsr0l))); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (op), 1), pic16_popCopyReg (&pic16_pc_fsr0h))); + } +} + +/*----------------------------------------------------------------*/ +/* pic16_derefPtr - move one byte from the location ptr points to */ +/* to WREG (doWrite == 0) or one byte from WREG */ +/* to the location ptr points to (doWrite != 0) */ +/*----------------------------------------------------------------*/ +static void +pic16_derefPtr (operand * ptr, int p_type, int doWrite, int *fsr0_setup) +{ + if (!IS_PTR (operandType (ptr))) + { + if (doWrite) + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (ptr), 0)); + else + pic16_mov2w (AOP (ptr), 0); + return; + } + + //assert (IS_DECL(operandType(ptr)) && (p_type == DCL_TYPE(operandType(ptr)))); + /* We might determine pointer type right here: */ + p_type = DCL_TYPE (operandType (ptr)); + + switch (p_type) + { + case POINTER: + case FPOINTER: + case IPOINTER: + case PPOINTER: + if (!fsr0_setup || !*fsr0_setup) + { + pic16_loadFSR0 (ptr, 0); + if (fsr0_setup) + *fsr0_setup = 1; + } + if (doWrite) + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_indf0)); + else + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_indf0)); + break; + + case GPOINTER: + if (AOP (ptr)->aopu.aop_reg[2]) + { + if (doWrite) + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (pic16_stack_postdec)); + // prepare call to __gptrget1, this is actually genGenPointerGet(result, WREG, ?ic?) + mov2fp (pic16_popCopyReg (&pic16_pc_fsr0l), AOP (ptr), 0); + mov2fp (pic16_popCopyReg (&pic16_pc_prodl), AOP (ptr), 1); + pic16_mov2w (AOP (ptr), 2); + pic16_callGenericPointerRW (doWrite, 1); + } + else + { + // data pointer (just 2 byte given) + if (!fsr0_setup || !*fsr0_setup) + { + pic16_loadFSR0 (ptr, 0); + if (fsr0_setup) + *fsr0_setup = 1; + } + if (doWrite) + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_indf0)); + else + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_indf0)); + } + break; + + case CPOINTER: + /* XXX: Writing to CPOINTERs not (yet) implemented. */ + assert (!doWrite && "Cannot write into __code space!"); + if ((AOP_TYPE (ptr) == AOP_PCODE) + && ((AOP (ptr)->aopu.pcop->type == PO_IMMEDIATE) || (AOP (ptr)->aopu.pcop->type == PO_DIR))) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (ptr), 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrl)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (ptr), 1)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrh)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (ptr), 2)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptru)); + } + else + { + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrl), AOP (ptr), 0); + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrh), AOP (ptr), 1); + mov2fp (pic16_popCopyReg (&pic16_pc_tblptru), AOP (ptr), 2); + } // if + + pic16_emitpcodeNULLop (POC_TBLRD_POSTINC); + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_tablat)); + break; + + default: + assert (0 && "invalid pointer type specified"); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, operand * left, char *rname, int ptype) +{ + int shCnt; + sym_link *etype; + unsigned blen = 0, bstr = 0; + int same; + pCodeOp *op; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + etype = getSpec (operandType (result)); + + // if(IS_BITFIELD(etype)) { + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + // } + + DEBUGpic16_emitcode ("; ***", "%s %d - reading %s bitfield int %s destination", __FUNCTION__, __LINE__, + SPEC_USIGN (OP_SYM_ETYPE (left)) ? "an unsigned" : "a signed", + SPEC_USIGN (OP_SYM_TYPE (result)) ? "an unsigned" : "a signed"); + +#if 1 + if ((blen == 1) && (bstr < 8) && (!IS_PTR (operandType (left)) || IS_DIRECT (left) || PIC_IS_DATA_PTR (operandType (left)))) + { + /* it is a single bit, so use the appropriate bit instructions */ + DEBUGpic16_emitcode (";", "%s %d optimize bit read", __FUNCTION__, __LINE__); + + same = pic16_sameRegs (AOP (left), AOP (result)); + op = (same ? pic16_popCopyReg (&pic16_pc_wreg) : pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_CLRF, op); + + if (!IS_PTR (operandType (left)) || IS_DIRECT (left)) + { + /* workaround to reduce the extra lfsr instruction */ + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (left), 0), bstr)); + } + else + { + assert (PIC_IS_DATA_PTR (operandType (left))); + pic16_loadFSR0 (left, 0); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_indf0), bstr)); + } + + if (SPEC_USIGN (OP_SYM_ETYPE (left))) + { + /* unsigned bitfields result in either 0 or 1 */ + pic16_emitpcode (POC_INCF, op); + } + else + { + /* signed bitfields result in either 0 or -1 */ + pic16_emitpcode (POC_DECF, op); + } + if (same) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + } + + pic16_addSign (result, 1, !SPEC_USIGN (OP_SYM_TYPE (result))); + return; + } + +#endif + + if (!IS_PTR (operandType (left)) || IS_DIRECT (left)) + { + // access symbol directly + pic16_mov2w (AOP (left), 0); + } + else + { + pic16_derefPtr (left, ptype, 0, NULL); + } + + /* if we have bitdisplacement then it fits */ + /* into this byte completely or if length is */ + /* less than a byte */ + if ((shCnt = SPEC_BSTR (etype)) || (SPEC_BLEN (etype) <= 8)) + { + + /* shift right acc */ + AccRsh (shCnt, 0); + + pic16_emitpcode (POC_ANDLW, pic16_popGetLit ((((unsigned char) - 1) >> (8 - SPEC_BLEN (etype))) & SRMask[shCnt])); + + /* VR -- normally I would use the following, but since we use the hack, + * to avoid the masking from AccRsh, why not mask it right now? */ + + /* + pic16_emitpcode(POC_ANDLW, pic16_popGetLit(((unsigned char) -1)>>(8 - SPEC_BLEN(etype)))); + */ + + /* extend signed bitfields to 8 bits */ + if (!SPEC_USIGN (OP_SYM_ETYPE (left)) && (bstr + blen < 8)) + { + assert (blen + bstr > 0); + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_wreg), bstr + blen - 1)); + pic16_emitpcode (POC_IORLW, pic16_popGetLit (0xFF << (bstr + blen))); + } + + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + pic16_addSign (result, 1, !SPEC_USIGN (OP_SYM_TYPE (result))); + return; + } + + fprintf (stderr, "SDCC pic16 port error: the port currently does not support *reading*\n"); + fprintf (stderr, "bitfields of size >=8. Instead of generating wrong code, bailing out...\n"); + exit (EXIT_FAILURE); +} + + +static void +genDataPointerGet (operand * left, operand * result, iCode * ic) +{ + int size, offset = 0, leoffset = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (result, ic, TRUE); + + FENTRY; + + size = AOP_SIZE (result); +// fprintf(stderr, "%s:%d size= %d\n", __FILE__, __LINE__, size); + + +#if 1 + if (!strcmp (pic16_aopGet (AOP (result), 0, TRUE, FALSE), pic16_aopGet (AOP (left), 0, TRUE, FALSE))) + { + DEBUGpic16_emitcode ("; ***", "left and result names are same, skipping moving"); + goto release; + } +#endif + + if (AOP (left)->aopu.pcop->type == PO_DIR) + leoffset = PCOR (AOP (left)->aopu.pcop)->instance; + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + + while (size--) + { + DEBUGpic16_emitcode ("; ***", "%s loop offset=%d leoffset=%d", __FUNCTION__, offset, leoffset); + +// pic16_DumpOp("(result)",result); + if (pic16_isLitAop (AOP (result))) + { + pic16_mov2w (AOP (left), offset); // patch 8 + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (left), offset), //patch 8 + pic16_popGet (AOP (result), offset))); + } + + offset++; + leoffset++; + } + +release: + pic16_freeAsmop (result, NULL, ic, TRUE); +} + + + +/*-----------------------------------------------------------------*/ +/* genNearPointerGet - pic16_emitcode for near pointer fetch */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerGet (operand * left, operand * result, iCode * ic) +{ +// asmop *aop = NULL; + //regs *preg = NULL ; + sym_link *rtype, *retype; + sym_link *ltype, *letype; + + FENTRY; + + rtype = operandType (result); + retype = getSpec (rtype); + ltype = operandType (left); + letype = getSpec (ltype); + + pic16_aopOp (left, ic, FALSE); + +// pic16_DumpOp("(left)",left); +// pic16_DumpOp("(result)",result); + + /* if left is rematerialisable and + * result is not bit variable type and + * the left is pointer to data space i.e + * lower 128 bytes of space */ + + if (AOP_TYPE (left) == AOP_PCODE && !IS_BITFIELD (retype) && DCL_TYPE (ltype) == POINTER) + { + + genDataPointerGet (left, result, ic); + pic16_freeAsmop (left, NULL, ic, TRUE); + return; + } + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (result, ic, TRUE); + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + +#if 1 + if (IS_BITFIELD (retype) && (SPEC_BLEN (retype) == 1)) + { + iCode *nextic; + pCodeOp *jop; + int bitstrt; + + /* if this is bitfield of size 1, see if we are checking the value + * of a single bit in an if-statement, + * if yes, then don't generate usual code, but execute the + * genIfx directly -- VR */ + + nextic = ic->next; + + /* CHECK: if next iCode is IFX + * and current result operand is nextic's conditional operand + * and current result operand live ranges ends at nextic's key number + */ + if ((nextic->op == IFX) && (result == IC_COND (nextic)) && (OP_LIVETO (result) == nextic->seq) && (OP_SYMBOL (left)->remat) // below fails for "if (p->bitfield)" + ) + { + /* everything is ok then */ + /* find a way to optimize the genIfx iCode */ + + bitstrt = SPEC_BSTR (retype) % 8; + + jop = pic16_popCopyGPR2Bit (pic16_popGet (AOP (left), 0), bitstrt); + + genIfxpCOpJump (nextic, jop); + + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); + return; + } + } +#endif + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (letype)) + genUnpackBits (result, left, NULL, POINTER); + else + { + /* we have can just get the values */ + int size = AOP_SIZE (result); + int offset = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + pic16_loadFSR0 (left, 0); + + while (size--) + { + if (size) + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_postinc0), + pic16_popGet (AOP (result), offset++))); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_indf0), + pic16_popGet (AOP (result), offset++))); + } + } + } + +#if 0 + /* now some housekeeping stuff */ + if (aop) + { + /* we had to allocate for this iCode */ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + * already in a pointer register, then + * if size > 0 && this could be used again + * we have to point it back to where it + * belongs */ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (AOP_SIZE (result) > 1 && !OP_SYMBOL (left)->remat && (OP_SYMBOL (left)->liveTo > ic->seq || ic->depth)) + { +// int size = AOP_SIZE(result) - 1; +// while (size--) +// pic16_emitcode("dec","%s",rname); + } + } +#endif + + /* done */ + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerGet - gget value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerGet (operand * left, operand * result, iCode * ic) +{ + int size; + sym_link *letype = getSpec (operandType (left)); + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + size = AOP_SIZE (result); + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + + /* if bit then unpack */ + if (IS_BITFIELD (letype)) + { + genUnpackBits (result, left, "BAD", GPOINTER); + goto release; + } + + /* set up WREG:PRODL:FSR0L with address from left */ + mov2fp (pic16_popCopyReg (&pic16_pc_fsr0l), AOP (left), 0); + mov2fp (pic16_popCopyReg (&pic16_pc_prodl), AOP (left), 1); + pic16_mov2w (AOP (left), 2); + pic16_callGenericPointerRW (0, size); + + assignResultValue (result, size, 1); + +release: + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genConstPointerGet - get value from const generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genConstPointerGet (operand * left, operand * result, iCode * ic) +{ + //sym_link *retype = getSpec(operandType(result)); + // symbol *albl = newiTempLabel(NULL); // patch 15 + // symbol *blbl = newiTempLabel(NULL); // + // PIC_OPCODE poc; // patch 15 + int size; + int offset = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (left, ic, FALSE); + pic16_aopOp (result, ic, TRUE); + size = AOP_SIZE (result); + + /* if bit then unpack */ + if (IS_BITFIELD (getSpec (operandType (left)))) + { + genUnpackBits (result, left, "BAD", GPOINTER); + goto release; + } // if + + DEBUGpic16_pic16_AopType (__LINE__, left, NULL, result); + + DEBUGpic16_emitcode ("; ", " %d getting const pointer", __LINE__); + + // set up table pointer + if ((AOP_TYPE (left) == AOP_PCODE) + && ((AOP (left)->aopu.pcop->type == PO_IMMEDIATE) || (AOP (left)->aopu.pcop->type == PO_DIR))) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (left), 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrl)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (left), 1)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrh)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (left), 2)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptru)); + } + else + { + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrl), AOP (left), 0); + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrh), AOP (left), 1); + mov2fp (pic16_popCopyReg (&pic16_pc_tblptru), AOP (left), 2); + } + + while (size--) + { + pic16_emitpcodeNULLop (POC_TBLRD_POSTINC); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_tablat), pic16_popGet (AOP (result), offset))); + offset++; + } + +release: + pic16_freeAsmop (left, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (iCode * ic) +{ + operand *left, *result; + sym_link *type, *etype; + int p_type; + + FENTRY; + + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (left); + etype = getSpec (type); + +#if 0 + if (IS_PTR_CONST (type)) +#else + if (IS_CODEPTR (type)) +#endif + DEBUGpic16_emitcode ("; ***", "%d - const pointer", __LINE__); + + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + p_type = DCL_TYPE (type); + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + DEBUGpic16_emitcode ("; ***", "%d - resolve pointer by storage class", __LINE__); + + if (SPEC_OCLS (etype)->codesp) + { + DEBUGpic16_emitcode ("; ***", "%d - cpointer", __LINE__); + //p_type = CPOINTER ; + } + else if (SPEC_OCLS (etype)->fmap && !SPEC_OCLS (etype)->paged) + { + DEBUGpic16_emitcode ("; ***", "%d - fpointer", __LINE__); + /*p_type = FPOINTER ; */ + } + else if (SPEC_OCLS (etype)->fmap && SPEC_OCLS (etype)->paged) + { + DEBUGpic16_emitcode ("; ***", "%d - ppointer", __LINE__); + /* p_type = PPOINTER; */ + } + else if (SPEC_OCLS (etype) == idata) + { + DEBUGpic16_emitcode ("; ***", "%d - ipointer", __LINE__); + /* p_type = IPOINTER; */ + } + else + { + DEBUGpic16_emitcode ("; ***", "%d - pointer", __LINE__); + /* p_type = POINTER ; */ + } + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case FPOINTER: + case IPOINTER: + case PPOINTER: + genNearPointerGet (left, result, ic); + break; + + case CPOINTER: + genConstPointerGet (left, result, ic); + //pic16_emitcodePointerGet (left,result,ic); + break; + + case GPOINTER: +#if 0 + if (IS_PTR_CONST (type)) + genConstPointerGet (left, result, ic); + else +#endif + genGenPointerGet (left, result, ic); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerGet: illegal pointer type"); + + } +} + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * result, operand * right, char *rname, int p_type) +{ + unsigned shCnt = 0; + int offset = 0; + int rLen = 0; + unsigned blen, bstr; + int shifted_and_masked = 0; + unsigned long lit = (unsigned long) - 1; + sym_link *retype; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + retype = getSpec (operandType (right)); + + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + if ((blen == 1) && (bstr < 8)) + { + /* it is a single bit, so use the appropriate bit instructions */ + + DEBUGpic16_emitcode (";", "%s %d optimize bit assignment", __FUNCTION__, __LINE__); + + if (!IS_PTR (operandType (result)) || IS_DIRECT (result)) + { + /* workaround to reduce the extra lfsr instruction */ + if (lit) + { + pic16_emitpcode (POC_BSF, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), 0), bstr)); + } + else + { + pic16_emitpcode (POC_BCF, pic16_popCopyGPR2Bit (pic16_popGet (AOP (result), 0), bstr)); + } + } + else + { + if (PIC_IS_DATA_PTR (operandType (result))) + { + pic16_loadFSR0 (result, 0); + pic16_emitpcode (lit ? POC_BSF : POC_BCF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_indf0), bstr)); + } + else + { + /* get old value */ + pic16_derefPtr (result, p_type, 0, NULL); + pic16_emitpcode (lit ? POC_BSF : POC_BCF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_wreg), bstr)); + /* write back new value */ + pic16_derefPtr (result, p_type, 1, NULL); + } + } + + return; + } + /* IORLW below is more efficient */ + //pic16_emitpcode(POC_MOVLW, pic16_popGetLit((lit & ((1UL << blen) - 1)) << bstr)); + lit = (lit & ((1UL << blen) - 1)) << bstr; + shifted_and_masked = 1; + offset++; + } + else if (IS_DIRECT (result) && !IS_PTR (operandType (result)) + && IS_BITFIELD (retype) && (AOP_TYPE (right) == AOP_REG || AOP_TYPE (right) == AOP_DIR) && (blen == 1)) + { + int rbstr; + + rbstr = SPEC_BSTR (retype); + + if (IS_BITFIELD (etype)) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BCF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_wreg), bstr)); + } + else + { + pic16_emitpcode (POC_CLRF, pic16_popCopyReg (&pic16_pc_wreg)); + } + + pic16_emitpcode (POC_BTFSC, pic16_popCopyGPR2Bit (pic16_popGet (AOP (right), 0), rbstr)); + + if (IS_BITFIELD (etype)) + { + pic16_emitpcode (POC_BSF, pic16_popCopyGPR2Bit (pic16_popCopyReg (&pic16_pc_wreg), bstr)); + } + else + { + pic16_emitpcode (POC_INCF, pic16_popCopyReg (&pic16_pc_wreg)); + } + + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + return; + } + else + { + /* move right to W */ + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset++)); + } + + /* if the bit length is less than or */ + /* it exactly fits a byte then */ + if ((shCnt = SPEC_BSTR (etype)) || SPEC_BLEN (etype) <= 8) + { + int fsr0_setup = 0; + + if (blen != 8 || (bstr % 8) != 0) + { + // we need to combine the value with the old value + if (!shifted_and_masked) + { + pic16_emitpcode (POC_ANDLW, pic16_popGetLit ((1U << blen) - 1)); + + DEBUGpic16_emitcode (";", "shCnt = %d SPEC_BSTR(etype) = %d:%d", shCnt, SPEC_BSTR (etype), SPEC_BLEN (etype)); + + /* shift left acc, do NOT mask the result again */ + AccLsh (shCnt, 0); + + /* using PRODH as a temporary register here */ + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_prodh)); + } + + if ((IS_SYMOP (result) && !IS_PTR (operandType (result))) || IS_DIRECT (result)) + { + /* access symbol directly */ + pic16_mov2w (AOP (result), 0); + } + else + { + /* get old value */ + pic16_derefPtr (result, p_type, 0, &fsr0_setup); + } +#if 1 + pic16_emitpcode (POC_ANDLW, pic16_popGetLit ((unsigned char) ((unsigned char) (0xff << (blen + bstr)) | + (unsigned char) (0xff >> (8 - bstr))))); + if (!shifted_and_masked) + { + pic16_emitpcode (POC_IORFW, pic16_popCopyReg (&pic16_pc_prodh)); + } + else + { + /* We have the shifted and masked (literal) right value in `lit' */ + if (lit != 0) + pic16_emitpcode (POC_IORLW, pic16_popGetLit (lit)); + } + } + else // if (blen == 8 && (bstr % 8) == 0) + { + if (shifted_and_masked) + { + // move right (literal) to WREG (only case where right is not yet in WREG) + pic16_mov2w (AOP (right), (bstr / 8)); + } + } + + /* write new value back */ + if ((IS_SYMOP (result) && !IS_PTR (operandType (result))) || IS_DIRECT (result)) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + } + else + { + pic16_derefPtr (result, p_type, 1, &fsr0_setup); + } +#endif + + return; + } + + +#if 0 + fprintf (stderr, "SDCC pic16 port error: the port currently does not support\n"); + fprintf (stderr, "bitfields of size >=8. Instead of generating wrong code, bailing out...\n"); + exit (EXIT_FAILURE); +#endif + + + pic16_loadFSR0 (result, 0); // load FSR0 with address of result + rLen = SPEC_BLEN (etype) - 8; + + /* now generate for lengths greater than one byte */ + while (1) + { + rLen -= 8; + if (rLen <= 0) + { + mov2fp (pic16_popCopyReg (&pic16_pc_prodh), AOP (right), offset); + break; + } + + switch (p_type) + { + case POINTER: + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_postinc0)); + break; + + /* + case FPOINTER: + MOVA(l); + pic16_emitcode("movx","@dptr,a"); + break; + + case GPOINTER: + MOVA(l); + DEBUGpic16_emitcode(";lcall","__gptrput"); + break; + */ + default: + assert (0); + } + + + pic16_mov2w (AOP (right), offset++); + } + + /* last last was not complete */ + if (rLen) + { + /* save the byte & read byte */ + switch (p_type) + { + case POINTER: + // pic16_emitpcode(POC_MOVWF, pic16_popCopyReg(&pic16_pc_prodl)); + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_indf0)); + break; + + /* + case FPOINTER: + pic16_emitcode ("mov","b,a"); + pic16_emitcode("movx","a,@dptr"); + break; + + case GPOINTER: + pic16_emitcode ("push","b"); + pic16_emitcode ("push","acc"); + pic16_emitcode ("lcall","__gptrget"); + pic16_emitcode ("pop","b"); + break; + */ + default: + assert (0); + } + DEBUGpic16_emitcode (";", "rLen = %i", rLen); + pic16_emitpcode (POC_ANDLW, pic16_popGetLit ((unsigned char) - 1 << -rLen)); + pic16_emitpcode (POC_IORFW, pic16_popCopyReg (&pic16_pc_prodh)); + // pic16_emitcode ("anl","a,#0x%02x",((unsigned char)-1 << -rLen) ); + // pic16_emitcode ("orl","a,b"); + } + + // if (p_type == GPOINTER) + // pic16_emitcode("pop","b"); + + switch (p_type) + { + + case POINTER: + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_indf0)); + // pic16_emitcode("mov","@%s,a",rname); + break; + /* + case FPOINTER: + pic16_emitcode("movx","@dptr,a"); + break; + + case GPOINTER: + DEBUGpic16_emitcode(";lcall","__gptrput"); + break; + */ + default: + assert (0); + } + + // pic16_freeAsmop(right, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDataPointerSet - remat pointer to data space */ +/*-----------------------------------------------------------------*/ +static void +genDataPointerSet (operand * right, operand * result, iCode * ic) +{ + int size, offset = 0, resoffset = 0; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (right, ic, FALSE); + + size = AOP_SIZE (right); + +// fprintf(stderr, "%s:%d size= %d\n", __FILE__, __LINE__, size); + +#if 0 + if (AOP_TYPE (result) == AOP_PCODE) + { + fprintf (stderr, "genDataPointerSet %s, %d\n", + AOP (result)->aopu.pcop->name, + (AOP (result)->aopu.pcop->type == PO_DIR) ? + PCOR (AOP (result)->aopu.pcop)->instance : PCOI (AOP (result)->aopu.pcop)->offset); + } +#endif + + if (AOP (result)->aopu.pcop->type == PO_DIR) + resoffset = PCOR (AOP (result)->aopu.pcop)->instance; + + while (size--) + { + if (AOP_TYPE (right) == AOP_LIT) + { + unsigned int lit = pic16aopLiteral (AOP (IC_RIGHT (ic))->aopu.aop_lit, offset); + pic16_movLit2f (pic16_popGet (AOP (result), offset), lit); + } + else + { + pic16_mov2w (AOP (right), offset); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); // patch 8 + } + offset++; + resoffset++; + } + + pic16_freeAsmop (right, NULL, ic, TRUE); +} + + + +/*-----------------------------------------------------------------*/ +/* genNearPointerSet - pic16_emitcode for near pointer put */ +/*-----------------------------------------------------------------*/ +static void +genNearPointerSet (operand * right, operand * result, iCode * ic) +{ + asmop *aop = NULL; + sym_link *retype; + sym_link *ptype = operandType (result); + sym_link *resetype; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + retype = getSpec (operandType (right)); + resetype = getSpec (operandType (result)); + + pic16_aopOp (result, ic, FALSE); + + /* if the result is rematerializable & + * in data space & not a bit variable */ + + /* and result is not a bit variable */ + if (AOP_TYPE (result) == AOP_PCODE && DCL_TYPE (ptype) == POINTER && !IS_BITFIELD (retype) && !IS_BITFIELD (resetype)) + { + + genDataPointerSet (right, result, ic); + pic16_freeAsmop (result, NULL, ic, TRUE); + return; + } + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_aopOp (right, ic, FALSE); + DEBUGpic16_pic16_AopType (__LINE__, NULL, right, result); + + /* if bitfield then unpack the bits */ + if (IS_BITFIELD (resetype)) + { + genPackBits (resetype, result, right, NULL, POINTER); + } + else + { + /* we have can just get the values */ + int size = AOP_SIZE (right); + int offset = 0; + + pic16_loadFSR0 (result, 0); + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + while (size--) + { + if (pic16_isLitOp (right)) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), offset)); + if (size) + { + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_postinc0)); + } + else + { + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_indf0)); + } + } + else // no literal + { + if (size) + { + pic16_emitpcode (POC_MOVFF, + pic16_popGet2p (pic16_popGet (AOP (right), offset), pic16_popCopyReg (&pic16_pc_postinc0))); + } + else + { + pic16_emitpcode (POC_MOVFF, + pic16_popGet2p (pic16_popGet (AOP (right), offset), pic16_popCopyReg (&pic16_pc_indf0))); + } + } + + offset++; + } + } + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* now some housekeeping stuff */ + if (aop) + { + /* we had to allocate for this iCode */ + pic16_freeAsmop (NULL, aop, ic, TRUE); + } + else + { + /* we did not allocate which means left + * already in a pointer register, then + * if size > 0 && this could be used again + * we have to point it back to where it + * belongs */ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + if (AOP_SIZE (right) > 1 && !OP_SYMBOL (result)->remat && (OP_SYMBOL (result)->liveTo > ic->seq || ic->depth)) + { + + int size = AOP_SIZE (right) - 1; + + while (size--) + pic16_emitcode ("decf", "fsr0,f"); + //pic16_emitcode("dec","%s",rname); + } + } + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + /* done */ +//release: + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genGenPointerSet - set value from generic pointer space */ +/*-----------------------------------------------------------------*/ +static void +genGenPointerSet (operand * right, operand * result, iCode * ic) +{ + int size; + sym_link *retype = getSpec (operandType (result)); + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + pic16_aopOp (result, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + size = AOP_SIZE (right); + + DEBUGpic16_pic16_AopType (__LINE__, NULL, right, result); + + + /* if bit then unpack */ + if (IS_BITFIELD (retype)) + { +// pic16_emitpcode(POC_LFSR,pic16_popGetLit2(0,pic16_popGetLit(lit))); + genPackBits (retype, result, right, "dptr", GPOINTER); + goto release; + } + + size = AOP_SIZE (right); + + DEBUGpic16_emitcode ("; ***", "%s %d size=%d", __FUNCTION__, __LINE__, size); + + + /* load value to write in TBLPTRH:TBLPTRL:PRODH:[stack] */ + + /* value of right+0 is placed on stack, which will be retrieved + * by the support function thus restoring the stack. The important + * thing is that there is no need to manually restore stack pointer + * here */ + pushaop (AOP (right), 0); +// mov2fp(pic16_popCopyReg(&pic16_pc_postdec1), AOP(right), 0); + if (size > 1) + mov2fp (pic16_popCopyReg (&pic16_pc_prodh), AOP (right), 1); + if (size > 2) + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrl), AOP (right), 2); + if (size > 3) + mov2fp (pic16_popCopyReg (&pic16_pc_tblptrh), AOP (right), 3); + + /* load address to write to in WREG:FSR0H:FSR0L */ + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (result), 0), pic16_popCopyReg (&pic16_pc_fsr0l))); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (result), 1), pic16_popCopyReg (&pic16_pc_prodl))); + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (result), 2)); + + pic16_callGenericPointerRW (1, size); + +release: + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode * ic) +{ + operand *right, *result; + sym_link *type, *etype; + int p_type; + + FENTRY; + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + /* depending on the type of pointer we need to + move it to the correct pointer register */ + type = operandType (result); + etype = getSpec (type); + + /* if left is of type of pointer then it is simple */ + if (IS_PTR (type) && !IS_FUNC (type->next)) + { + p_type = DCL_TYPE (type); + } + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + /* if (SPEC_OCLS(etype)->codesp ) { */ + /* p_type = CPOINTER ; */ + /* } */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && !SPEC_OCLS(etype)->paged) */ + /* p_type = FPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && SPEC_OCLS(etype)->paged) */ + /* p_type = PPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype) == idata ) */ + /* p_type = IPOINTER ; */ + /* else */ + /* p_type = POINTER ; */ + } + + /* now that we have the pointer type we assign + the pointer values */ + switch (p_type) + { + case POINTER: + case FPOINTER: + case IPOINTER: + case PPOINTER: + genNearPointerSet (right, result, ic); + break; + + case GPOINTER: + genGenPointerSet (right, result, ic); + break; + + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "genPointerSet: illegal pointer type"); + } +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode * ic, iCode * popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + + FENTRY; + + pic16_aopOp (cond, ic, FALSE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY) + pic16_toBoolean (cond); + else + isbit = 1; + /* the result is now in the accumulator */ + pic16_freeAsmop (cond, NULL, ic, TRUE); + + /* if there was something to be popped then do it */ + if (popIc) + genIpop (popIc); + + /* if the condition is a bit variable */ + if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + { + genIfxJump (ic, "c"); + DEBUGpic16_emitcode ("; isbit SPIL_LOC", "%s", SPIL_LOC (cond)->rname); + } + else + { + if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname); + else + genIfxJump (ic, "a"); + } + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (iCode * ic) +{ + operand *result, *left; + int size; + symbol *sym; // = OP_SYMBOL(IC_LEFT(ic)); + pCodeOp *pcop0, *pcop1, *pcop2; + + FENTRY; + + pic16_aopOp ((result = IC_RESULT (ic)), ic, TRUE); + + sym = OP_SYMBOL (IC_LEFT (ic)); + + if (sym->onStack) + { + /* get address of symbol on stack */ + DEBUGpic16_emitcode ("; ", "%s symbol %s on stack", __FUNCTION__, sym->name); +#if 0 + fprintf (stderr, "%s:%d symbol %s on stack offset %i\n", __FILE__, __LINE__, + OP_SYMBOL (IC_LEFT (ic))->name, OP_SYMBOL (IC_LEFT (ic))->stack); +#endif + + // operands on stack are accessible via "FSR2 + index" with index + // starting at 2 for arguments and growing from 0 downwards for + // local variables (index == 0 is not assigned so we add one here) + { + int soffs = OP_SYMBOL (IC_LEFT (ic))->stack; + + if (soffs <= 0) + { + assert (soffs < 0); + soffs++; + } // if + + DEBUGpic16_emitcode ("*!*", "accessing stack symbol at offset=%d", soffs); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (soffs & 0x00FF)); + pic16_emitpcode (POC_ADDFW, pic16_popCopyReg (pic16_framepnt_lo)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit ((soffs >> 8) & 0x00FF)); + pic16_emitpcode (POC_ADDFWC, pic16_popCopyReg (pic16_framepnt_hi)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 1)); + } + + goto release; + } + +// if(pic16_debug_verbose) { +// fprintf(stderr, "%s:%d %s symbol %s , codespace=%d\n", +// __FILE__, __LINE__, __FUNCTION__, sym->name, IN_CODESPACE( SPEC_OCLS(sym->etype))); +// } + + pic16_aopOp ((left = IC_LEFT (ic)), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + + pcop0 = PCOP (pic16_newpCodeOpImmd (sym->rname, 0, 0, IN_CODESPACE (SPEC_OCLS (sym->etype)))); + pcop1 = PCOP (pic16_newpCodeOpImmd (sym->rname, 1, 0, IN_CODESPACE (SPEC_OCLS (sym->etype)))); + pcop2 = PCOP (pic16_newpCodeOpImmd (sym->rname, 2, 0, IN_CODESPACE (SPEC_OCLS (sym->etype)))); + + if (size == 3) + { + pic16_emitpcode (POC_MOVLW, pcop0); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_MOVLW, pcop1); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 1)); + pic16_emitpcode (POC_MOVLW, pcop2); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 2)); + } + else if (size == 2) + { + pic16_emitpcode (POC_MOVLW, pcop0); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_MOVLW, pcop1); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 1)); + } + else + { + pic16_emitpcode (POC_MOVLW, pcop0); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + } + + pic16_freeAsmop (left, NULL, ic, FALSE); +release: + pic16_freeAsmop (result, NULL, ic, TRUE); +} + + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (iCode * ic) +{ + operand *result, *right; + sym_link *restype, *rtype; + int size, offset, know_W; + unsigned long lit = 0L; + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + FENTRY; + + /* if they are the same */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + /* reversed order operands are aopOp'ed so that result operand + * is effective in case right is a stack symbol. This maneauver + * allows to use the _G.resDirect flag later */ + pic16_aopOp (result, ic, TRUE); + pic16_aopOp (right, ic, FALSE); + + DEBUGpic16_pic16_AopType (__LINE__, NULL, right, result); + + /* if they are the same registers */ + if (pic16_sameRegs (AOP (right), AOP (result))) + goto release; + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + /* if the right size is a literal then + we know what the value is */ + if (AOP_TYPE (right) == AOP_LIT) + { + + pic16_emitpcode ((((int) operandLitValue (right)) ? POC_BSF : POC_BCF), pic16_popGet (AOP (result), 0)); + + if (((int) operandLitValue (right))) + pic16_emitcode ("bsf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + else + pic16_emitcode ("bcf", "(%s >> 3),(%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) + { + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + + goto release; + } + + /* we need to or */ + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + pic16_toBoolean (right); + emitSKPZ; + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + //pic16_aopPut(AOP(result),"a",0); + goto release; + } + + /* bit variables done */ + /* general case */ + size = AOP_SIZE (result); + offset = 0; + + /* bit variables done */ + /* general case */ + size = AOP_SIZE (result); + restype = operandType (result); + rtype = operandType (right); + offset = 0; + + if (AOP_TYPE (right) == AOP_LIT) + { + if (!(IS_FLOAT (operandType (right)) || IS_FIXED (operandType (right)))) + { + lit = ulFromVal (AOP (right)->aopu.aop_lit); + + /* patch tag for literals that are cast to pointers */ + if (IS_CODEPTR (restype)) + { + //fprintf (stderr, "%s:%u: INFO: `(__code*)literal'\n", ic->filename, ic->lineno); + lit = (lit & 0x00ffff) | (GPTR_TAG_CODE << 16); + } + else + { + if (IS_GENPTR (restype)) + { + if (IS_CODEPTR (rtype)) + { + //fprintf (stderr, "%s:%u: INFO: `(generic*)(literal __code*)'\n", ic->filename, ic->lineno); + lit = (lit & 0x00ffff) | (GPTR_TAG_CODE << 16); + } + else if (PIC_IS_DATA_PTR (rtype)) + { + //fprintf (stderr, "%s:%u: INFO: `(generic*)(literal __data*)'\n", ic->filename, ic->lineno); + lit = (lit & 0x00ffff) | (GPTR_TAG_DATA << 16); + } + else if (!IS_PTR (rtype) || IS_GENPTR (rtype)) + { + //fprintf (stderr, "%s:%u: INFO: `(generic*)literal' -- accepting specified tag %02x\n", ic->filename, ic->lineno, (unsigned char)(lit >> 16)); + } + else if (IS_PTR (rtype)) + { + fprintf (stderr, "%s:%u: WARNING: `(generic*)literal' -- assuming __data space\n", ic->filename, + ic->lineno); + lit = (lit & 0x00ffff) | (GPTR_TAG_DATA << 16); + } + } + } + } + else + { + union + { + unsigned long lit_int; + float lit_float; + } info; + + + if (IS_FIXED16X16 (operandType (right))) + { + lit = (unsigned long) fixed16x16FromDouble (floatFromVal (AOP (right)->aopu.aop_lit)); + } + else + { + /* take care if literal is a float */ + info.lit_float = (float)floatFromVal (AOP (right)->aopu.aop_lit); + lit = info.lit_int; + } + } + } + +// fprintf(stderr, "%s:%d: assigning value 0x%04lx (%d:%d)\n", __FUNCTION__, __LINE__, lit, +// sizeof(unsigned long int), sizeof(float)); + + + if (AOP_TYPE (right) == AOP_REG) + { + DEBUGpic16_emitcode ("; ", "%s:%d assign from register\n", __FUNCTION__, __LINE__); + while (size--) + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2 (AOP (right), AOP (result), offset++)); + } // while + goto release; + } + + /* when do we have to read the program memory? + * - if right itself is a symbol in code space + * (we don't care what it points to if it's a pointer) + * - AND right is not a function (we would want its address) + */ + if (AOP_TYPE (right) != AOP_LIT + && IN_CODESPACE (SPEC_OCLS (OP_SYM_ETYPE (right))) && !IS_FUNC (OP_SYM_TYPE (right)) && !IS_ITEMP (right)) + { + + DEBUGpic16_emitcode ("; ", "%s:%d symbol in code space, take special care\n", __FUNCTION__, __LINE__); + //fprintf(stderr, "%s:%d symbol %s = [ %s ] is in code space\n", __FILE__, __LINE__, OP_SYMBOL(result)->name, OP_SYMBOL(right)->name); + + // set up table pointer + if (pic16_isLitOp (right)) + { +// fprintf(stderr, "%s:%d inside block 1\n", __FILE__, __LINE__); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrl)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), 1)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptrh)); + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), 2)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_tblptru)); + } + else + { +// fprintf(stderr, "%s:%d inside block 2\n", __FILE__, __LINE__); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (right), 0), pic16_popCopyReg (&pic16_pc_tblptrl))); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (right), 1), pic16_popCopyReg (&pic16_pc_tblptrh))); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popGet (AOP (right), 2), pic16_popCopyReg (&pic16_pc_tblptru))); + } + + /* must fetch 3 bytes for pointers (was OP_SYM_ETYPE before) */ + size = min ((int)getSize (OP_SYM_TYPE (right)), AOP_SIZE (result)); + while (size--) + { + pic16_emitpcodeNULLop (POC_TBLRD_POSTINC); + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_tablat), + pic16_popGet (AOP (result), offset))); + offset++; + } + + /* FIXME: for pointers we need to extend differently (according + * to pointer type DATA/CODE/EEPROM/... :*/ + size = getSize (OP_SYM_TYPE (right)); + if (AOP_SIZE (result) > size) + { + size = AOP_SIZE (result) - size; + while (size--) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + offset++; + } + } + goto release; + } + +#if 0 + /* VR - What is this?! */ + if (AOP_TYPE (right) == AOP_DIR && (AOP_TYPE (result) == AOP_REG) && size == 1) + { + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (aopIdx (AOP (result), 0) == 4) + { + /* this is a workaround to save value of right into wreg too, + * value of wreg is going to be used later */ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + goto release; + } + else +// assert(0); + DEBUGpic16_emitcode ("; WARNING", "%s %d ignoring register storage", __FUNCTION__, __LINE__); + } +#endif + + size = AOP_SIZE (right); + if (size > AOP_SIZE (result)) + size = AOP_SIZE (result); + know_W = -1; + while (size--) + { + DEBUGpic16_emitcode ("; ***", "%s %d size %d", __FUNCTION__, __LINE__, size); + if (AOP_TYPE (right) == AOP_LIT) + { + if (lit & 0xff) + { + if (know_W != (lit & 0xff)) + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (lit & 0xff)); + know_W = lit & 0xff; + + if (AOP_TYPE (result) != AOP_ACC) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + } + else + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + + lit >>= 8; + + } + else if (AOP_TYPE (right) == AOP_CRY) + { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + if (offset == 0) + { + //debugf("%s: BTFSS offset == 0\n", __FUNCTION__); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (result), 0)); + } + } + else if ((AOP_TYPE (right) == AOP_PCODE) && (AOP (right)->aopu.pcop->type == PO_IMMEDIATE)) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!_G.resDirect) /* use this aopForSym feature */ + { + if (AOP_TYPE (result) == AOP_ACC) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + } + else if (AOP_TYPE (right) == AOP_ACC) + { + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2 (AOP (right), AOP (result), offset)); + } + } + } + + offset++; + } + pic16_addSign (result, AOP_SIZE (right), !IS_UNSIGNED (operandType (right))); + +release: + pic16_freeAsmop (result, NULL, ic, TRUE); + pic16_freeAsmop (right, NULL, ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generates code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (iCode * ic) +{ + symbol *jtab; + char *l; + pCodeOp *jt_label; + + FENTRY; + + pic16_aopOp (IC_JTCOND (ic), ic, FALSE); + /* get the condition into accumulator */ + l = pic16_aopGet (AOP (IC_JTCOND (ic)), 0, FALSE, FALSE); + MOVA (l); + /* multiply by three */ + pic16_emitcode ("add", "a,acc"); + pic16_emitcode ("add", "a,%s", pic16_aopGet (AOP (IC_JTCOND (ic)), 0, FALSE, FALSE)); + + jtab = newiTempLabel (NULL); + pic16_emitcode ("mov", "dptr,#%05d_DS_", labelKey2num (jtab->key)); + pic16_emitcode ("jmp", "@a+dptr"); + pic16_emitcode ("", "%05d_DS_:", labelKey2num (jtab->key)); + +#if 0 + pic16_emitpcode (POC_MOVLW, pic16_popGetLabel (jtab->key)); + pic16_emitpcode (POC_ADDFW, pic16_popGet (AOP (IC_JTCOND (ic)), 0)); + emitSKPNC; + pic16_emitpcode (POC_INCF, pic16_popCopyReg (&pic16_pc_pclath)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_pcl)); + pic16_emitpLabel (jtab->key); + +#else + + jt_label = pic16_popGetLabel (jtab->key); + //fprintf (stderr, "Creating jump table...\n"); + + // calculate offset into jump table (idx * sizeof(GOTO)) + // This solution does not use temporary registers and the code is shorter. + pic16_emitpcode (POC_CLRF , pic16_popCopyReg (&pic16_pc_pclath)); + pic16_emitpcode (POC_CLRF , pic16_popCopyReg (&pic16_pc_pclatu)); + pic16_emitpcode (POC_RLCFW , pic16_popGet (AOP (IC_JTCOND (ic)), 0)); + pic16_emitpcode (POC_RLCF , pic16_popCopyReg (&pic16_pc_pclath)); + pic16_emitpcode (POC_RLCFW , pic16_popCopyReg (&pic16_pc_wreg)); + pic16_emitpcode (POC_RLCF , pic16_popCopyReg (&pic16_pc_pclath)); + pic16_emitpcode (POC_ANDLW , pic16_popGetLit (0xFC)); + pic16_emitpcode (POC_ADDLW , pic16_popGetImmd (jt_label->name, 0, 0)); + pic16_emitpcode (POC_MOVWF , pic16_popCopyReg (pic16_stack_postdec)); + pic16_emitpcode (POC_MOVLW , pic16_popGetImmd (jt_label->name, 1, 0)); + pic16_emitpcode (POC_ADDWFC, pic16_popCopyReg (&pic16_pc_pclath)); + pic16_emitpcode (POC_MOVLW , pic16_popGetImmd (jt_label->name, 2, 0)); + pic16_emitpcode (POC_ADDWFC, pic16_popCopyReg (&pic16_pc_pclatu)); + + // jump into the table + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (pic16_stack_preinc)); + pic16_emitpcode (POC_MOVWF, pic16_popCopyReg (&pic16_pc_pcl)); + + pic16_emitpLabelFORCE (jtab->key); +#endif + + pic16_freeAsmop (IC_JTCOND (ic), NULL, ic, TRUE); +// pic16_emitpinfo(INF_LOCALREGS, pic16_newpCodeOpLocalRegs(LR_ENTRY_BEGIN)); + + pic16_emitpinfo (INF_OPTIMIZATION, pic16_newpCodeOpOpt (OPT_JUMPTABLE_BEGIN, "")); + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { +// pic16_emitcode("ljmp","%05d_DS_",labelKey2num (jtab->key)); + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (jtab->key)); + + } + pic16_emitpinfo (INF_OPTIMIZATION, pic16_newpCodeOpOpt (OPT_JUMPTABLE_END, "")); + +} + +/*-----------------------------------------------------------------*/ +/* genMixedOperation - gen code for operators between mixed types */ +/*-----------------------------------------------------------------*/ +/* + TSD - Written for the PIC port - but this unfortunately is buggy. + This routine is good in that it is able to efficiently promote + types to different (larger) sizes. Unfortunately, the temporary + variables that are optimized out by this routine are sometimes + used in other places. So until I know how to really parse the + iCode tree, I'm going to not be using this routine :(. +*/ +static int +genMixedOperation (iCode * ic) +{ +#if 0 + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + operand *right = IC_RIGHT (ic); + int ret = 0; + int big, small; + int offset; + + iCode *nextic; + operand *nextright = NULL, *nextleft = NULL, *nextresult = NULL; + + pic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + nextic = ic->next; + if (!nextic) + return 0; + + nextright = IC_RIGHT (nextic); + nextleft = IC_LEFT (nextic); + nextresult = IC_RESULT (nextic); + + pic16_aopOp (right, ic, FALSE); + pic16_aopOp (result, ic, FALSE); + pic16_aopOp (nextright, nextic, FALSE); + pic16_aopOp (nextleft, nextic, FALSE); + pic16_aopOp (nextresult, nextic, FALSE); + + if (pic16_sameRegs (AOP (IC_RESULT (ic)), AOP (IC_RIGHT (nextic)))) + { + + operand *t = right; + right = nextright; + nextright = t; + + pic16_emitcode (";remove right +", ""); + + } + else if (pic16_sameRegs (AOP (IC_RESULT (ic)), AOP (IC_LEFT (nextic)))) + { + /* + operand *t = right; + right = nextleft; + nextleft = t; + */ + pic16_emitcode (";remove left +", ""); + } + else + return 0; + + big = AOP_SIZE (nextleft); + small = AOP_SIZE (nextright); + + switch (nextic->op) + { + + case '+': + pic16_emitcode (";optimize a +", ""); + /* if unsigned or not an integral type */ + if (AOP_TYPE (IC_LEFT (nextic)) == AOP_CRY) + { + pic16_emitcode (";add a bit to something", ""); + } + else + { + + pic16_emitcode ("movf", "%s,w", AOP (nextright)->aopu.aop_dir); + + if (!pic16_sameRegs (AOP (IC_LEFT (nextic)), AOP (IC_RESULT (nextic)))) + { + pic16_emitcode ("addwf", "%s,w", AOP (nextleft)->aopu.aop_dir); + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (IC_RESULT (nextic)), 0, FALSE, FALSE)); + } + else + pic16_emitcode ("addwf", "%s,f", AOP (nextleft)->aopu.aop_dir); + + offset = 0; + while (--big) + { + + offset++; + + if (--small) + { + if (!pic16_sameRegs (AOP (IC_LEFT (nextic)), AOP (IC_RESULT (nextic)))) + { + pic16_emitcode ("movf", "%s,w", pic16_aopGet (AOP (IC_LEFT (nextic)), offset, FALSE, FALSE)); + pic16_emitcode ("movwf", "%s,f", pic16_aopGet (AOP (IC_RESULT (nextic)), offset, FALSE, FALSE)); + } + + pic16_emitcode ("movf", "%s,w", pic16_aopGet (AOP (IC_LEFT (nextic)), offset, FALSE, FALSE)); + emitSKPNC; + pic16_emitcode ("btfsc", "(%s >> 3), (%s & 7)", + AOP (IC_RIGHT (nextic))->aopu.aop_dir, AOP (IC_RIGHT (nextic))->aopu.aop_dir); + pic16_emitcode (" incf", "%s,w", pic16_aopGet (AOP (IC_LEFT (nextic)), offset, FALSE, FALSE)); + pic16_emitcode ("movwf", "%s", pic16_aopGet (AOP (IC_RESULT (nextic)), offset, FALSE, FALSE)); + + } + else + { + pic16_emitcode ("rlf", "known_zero,w"); + + /* + if right is signed + btfsc right,7 + addlw ff + */ + if (!pic16_sameRegs (AOP (IC_LEFT (nextic)), AOP (IC_RESULT (nextic)))) + { + pic16_emitcode ("addwf", "%s,w", pic16_aopGet (AOP (IC_LEFT (nextic)), offset, FALSE, FALSE)); + pic16_emitcode ("movwf", "%s,f", pic16_aopGet (AOP (IC_RESULT (nextic)), offset, FALSE, FALSE)); + } + else + { + pic16_emitcode ("addwf", "%s,f", pic16_aopGet (AOP (IC_RESULT (nextic)), offset, FALSE, FALSE)); + } + } + } + ret = 1; + } + } + ret = 1; + +release: + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); + pic16_freeAsmop (nextright, NULL, ic, TRUE); + pic16_freeAsmop (nextleft, NULL, ic, TRUE); + if (ret) + nextic->generated = 1; + + return ret; +#else + return 0; +#endif +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (iCode * ic) +{ + operand *result = IC_RESULT (ic); + sym_link *ctype = operandType (IC_LEFT (ic)); + sym_link *rtype = operandType (IC_RIGHT (ic)); + sym_link *restype = operandType (IC_RESULT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + + + FENTRY; + + /* if they are equivalent then do nothing */ +// if (operandsEqu(IC_RESULT(ic),IC_RIGHT(ic))) +// return ; + + pic16_aopOp (result, ic, FALSE); + pic16_aopOp (right, ic, FALSE); + + DEBUGpic16_pic16_AopType (__LINE__, NULL, right, result); + + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + + /* if the right size is a literal then + * we know what the value is */ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (AOP_TYPE (right) == AOP_LIT) + { + pic16_emitpcode ((((int) operandLitValue (right)) ? POC_BSF : POC_BCF), pic16_popGet (AOP (result), 0)); + + if (((int) operandLitValue (right))) + pic16_emitcode ("bsf", "(%s >> 3), (%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + else + pic16_emitcode ("bcf", "(%s >> 3), (%s & 7)", AOP (result)->aopu.aop_dir, AOP (result)->aopu.aop_dir); + goto release; + } + + /* the right is also a bit variable */ + if (AOP_TYPE (right) == AOP_CRY) + { + emitCLRC; + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + + pic16_emitcode ("clrc", ""); + pic16_emitcode ("btfsc", "(%s >> 3), (%s & 7)", AOP (right)->aopu.aop_dir, AOP (right)->aopu.aop_dir); + pic16_aopPut (AOP (result), "c", 0); + goto release; + } + + /* we need to or */ + if (AOP_TYPE (right) == AOP_REG) + { + pic16_emitpcode (POC_BCF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit (pic16_aopGet (AOP (right), 0, FALSE, FALSE), 0, 0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_BSF, pic16_popGet (AOP (result), 0)); + } + pic16_toBoolean (right); + pic16_aopPut (AOP (result), "a", 0); + goto release; + } + + if ((AOP_TYPE (right) == AOP_CRY) && (AOP_TYPE (result) == AOP_REG)) + { + int offset = 1; + + size = AOP_SIZE (result); + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), 0)); + pic16_emitpcode (POC_BTFSC, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_INCF, pic16_popGet (AOP (result), 0)); + + while (size--) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset++)); + + goto release; + } + + if (IS_BOOL (operandType (result))) + { + pic16_toBoolean (right); + emitSKPNZ; + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (1)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + goto release; + } + + if (IS_BITFIELD (getSpec (restype)) && IS_BITFIELD (getSpec (rtype))) + { + DEBUGpic16_emitcode ("***", "%d casting a bit to another bit", __LINE__); + } + + /* port from pic14 to cope with generic pointers */ + if (PIC_IS_TAGGED (restype)) + { + operand *result = IC_RESULT (ic); + //operand *left = IC_LEFT(ic); + operand *right = IC_RIGHT (ic); + int tag = 0xff; + + /* copy common part */ + int max, size = AOP_SIZE (result); + if (size > AOP_SIZE (right)) + size = AOP_SIZE (right); + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + max = size; + while (size--) + { + pic16_mov2w (AOP (right), size); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), size)); + } // while + + /* upcast into generic pointer type? */ + if (IS_GENPTR (restype) && !PIC_IS_TAGGED (rtype) && (AOP_SIZE (result) > max)) + { + /* determine appropriate tag for right */ + if (PIC_IS_DATA_PTR (rtype)) + tag = GPTR_TAG_DATA; + else if (IS_CODEPTR (rtype)) + tag = GPTR_TAG_CODE; + else if (PIC_IS_DATA_PTR (ctype)) + { + //fprintf (stderr, "%s:%u: WARNING: casting `(generic*)(__data*)(non-pointer)'\n", ic->filename, ic->lineno); + tag = GPTR_TAG_DATA; + } + else if (IS_CODEPTR (ctype)) + { + //fprintf (stderr, "%s:%u: WARNING: casting `(generic*)(__code*)(non-pointer)'\n", ic->filename, ic->lineno); + tag = GPTR_TAG_CODE; + } + else if (IS_PTR (rtype)) + { + PERFORM_ONCE (weirdcast, + fprintf (stderr, "%s:%u: WARNING: casting `(generic*)(unknown*)' -- assuming __data space\n", + ic->filename, ic->lineno);); + tag = GPTR_TAG_DATA; + } + else + { + PERFORM_ONCE (weirdcast, + fprintf (stderr, "%s:%u: WARNING: casting `(generic*)(non-pointer)' -- assuming __data space\n", + ic->filename, ic->lineno);); + tag = GPTR_TAG_DATA; + } + + assert (AOP_SIZE (result) == 3); + /* zero-extend address... */ + for (size = max; size < AOP_SIZE (result) - 1; size++) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), size)); + /* ...and add tag */ + pic16_movLit2f (pic16_popGet (AOP (result), AOP_SIZE (result) - 1), tag); + } + else if (IS_CODEPTR (restype) && AOP_SIZE (result) > max) + { + //fprintf (stderr, "%s:%u: INFO: code pointer\n", ic->filename, ic->lineno); + for (size = max; size < AOP_SIZE (result) - 1; size++) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), size)); + /* add __code tag */ + pic16_movLit2f (pic16_popGet (AOP (result), AOP_SIZE (result) - 1), GPTR_TAG_CODE); + } + else if (AOP_SIZE (result) > max) + { + /* extend non-pointers */ + //fprintf (stderr, "%s:%u: zero-extending value cast to pointer\n", ic->filename, ic->lineno); + pic16_addSign (result, max, 0); + } // if + goto release; + } + + /* if they are the same size : or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + + /* if they are in the same place */ + if (pic16_sameRegs (AOP (right), AOP (result))) + goto release; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); +#if 0 + if (IS_PTR_CONST (rtype)) +#else + if (IS_CODEPTR (rtype)) +#endif + DEBUGpic16_emitcode ("; ***", "%d - right is const pointer", __LINE__); + +#if 0 + if (IS_PTR_CONST (operandType (IC_RESULT (ic)))) +#else + if (IS_CODEPTR (operandType (IC_RESULT (ic)))) +#endif + DEBUGpic16_emitcode ("; ***", "%d - result is const pointer", __LINE__); + + if ((AOP_TYPE (right) == AOP_PCODE) && AOP (right)->aopu.pcop->type == PO_IMMEDIATE) + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), 0)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 0)); + + if (AOP_SIZE (result) < 2) + { + fprintf (stderr, "%d -- casting a ptr to a char\n", __LINE__); + } + else + { + pic16_emitpcode (POC_MOVLW, pic16_popGet (AOP (right), 1)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 1)); + } + } + else + { + /* if they in different places then copy */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + pic16_emitpcode (POC_MOVFW, pic16_popGet (AOP (right), offset)); + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset)); + offset++; + } + } + goto release; + } + + /* if the result is of type pointer */ + if (IS_PTR (ctype)) + { + int p_type; + sym_link *type = operandType (right); + sym_link *etype = getSpec (type); + + DEBUGpic16_emitcode ("; ***", "%s %d - pointer cast", __FUNCTION__, __LINE__); + + /* pointer to generic pointer */ + if (IS_GENPTR (ctype)) + { + + if (IS_PTR (type)) + p_type = DCL_TYPE (type); + else + { + /* we have to go by the storage class */ + p_type = PTR_TYPE (SPEC_OCLS (etype)); + + /* if (SPEC_OCLS(etype)->codesp ) */ + /* p_type = CPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && !SPEC_OCLS(etype)->paged) */ + /* p_type = FPOINTER ; */ + /* else */ + /* if (SPEC_OCLS(etype)->fmap && SPEC_OCLS(etype)->paged) */ + /* p_type = PPOINTER; */ + /* else */ + /* if (SPEC_OCLS(etype) == idata ) */ + /* p_type = IPOINTER ; */ + /* else */ + /* p_type = POINTER ; */ + } + + /* the first two bytes are known */ + DEBUGpic16_emitcode ("; ***", "%s %d - pointer cast2", __FUNCTION__, __LINE__); + size = GPTRSIZE - 1; + offset = 0; + while (size--) + { + if (offset < AOP_SIZE (right)) + { + DEBUGpic16_emitcode ("; ***", "%s %d - pointer cast3 ptype = 0x%x", __FUNCTION__, __LINE__, p_type); + pic16_mov2f (AOP (result), AOP (right), offset); + /* + if ((AOP_TYPE(right) == AOP_PCODE) && + AOP(right)->aopu.pcop->type == PO_IMMEDIATE) { + pic16_emitpcode(POC_MOVLW, pic16_popGet(AOP(right),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),offset)); + } else { + + pic16_aopPut(AOP(result), + pic16_aopGet(AOP(right),offset,FALSE,FALSE), + offset); + } + */ + } + else + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + offset++; + } + /* the last byte depending on type */ + switch (p_type) + { + case POINTER: + case FPOINTER: + case IPOINTER: + case PPOINTER: + pic16_movLit2f (pic16_popGet (AOP (result), GPTRSIZE - 1), GPTR_TAG_DATA); + break; + + case CPOINTER: + pic16_emitpcode (POC_MOVFF, pic16_popGet2 (AOP (right), AOP (result), GPTRSIZE - 1)); + break; + + case GPOINTER: + if (GPTRSIZE > AOP_SIZE (right)) + { + // assume __data pointer... THIS MIGHT BE WRONG! + pic16_movLit2f (pic16_popGet (AOP (result), GPTRSIZE - 1), GPTR_TAG_DATA); + } + else + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2 (AOP (right), AOP (result), GPTRSIZE - 1)); + } + break; + + default: + /* this should never happen */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got unknown pointer type"); + exit (1); + } + //pic16_aopPut(AOP(result),l, GPTRSIZE - 1); + goto release; + } + + + assert (0); + /* just copy the pointers */ + size = AOP_SIZE (result); + offset = 0; + while (size--) + { + pic16_aopPut (AOP (result), pic16_aopGet (AOP (right), offset, FALSE, FALSE), offset); + offset++; + } + goto release; + } + + + + /* so we now know that the size of destination is greater + than the size of the source. + Now, if the next iCode is an operator then we might be + able to optimize the operation without performing a cast. + */ + if (genMixedOperation (ic)) + goto release; + + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + /* we move to result for the size of source */ + size = AOP_SIZE (right); + offset = 0; + + while (size--) + { + if (!_G.resDirect) + pic16_mov2f (AOP (result), AOP (right), offset); + offset++; + } + + /* now depending on the sign of the destination */ + size = AOP_SIZE (result) - AOP_SIZE (right); + /* if unsigned or not an integral type */ + if (SPEC_USIGN (getSpec (rtype)) || !IS_SPEC (rtype)) + { + while (size--) + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset++)); + } + else + { + /* we need to extend the sign :( */ + + if (size == 1) + { + /* Save one instruction of casting char to int */ + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP (result), offset)); + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (right), offset - 1, FALSE, FALSE), 7, 0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_SETF, pic16_popGet (AOP (result), offset)); + } + else + { + pic16_emitpcode (POC_CLRF, pic16_popCopyReg (&pic16_pc_wreg)); + + if (offset) + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (right), offset - 1, FALSE, FALSE), 7, 0, PO_GPR_REGISTER)); + else + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (right), offset, FALSE, FALSE), 7, 0, PO_GPR_REGISTER)); + + pic16_emitpcode (POC_MOVLW, pic16_popGetLit (0xff)); + + while (size--) + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), offset++)); + } + } + +release: + pic16_freeAsmop (right, NULL, ic, TRUE); + pic16_freeAsmop (result, NULL, ic, TRUE); + +} + +/*-----------------------------------------------------------------*/ +/* genDjnz - generate decrement & jump if not zero instrucion */ +/*-----------------------------------------------------------------*/ +static int +genDjnz (iCode * ic, iCode * ifx) +{ + DEBUGpic16_emitcode ("; ***", "%s %d", __FUNCTION__, __LINE__); + + if (!ifx) + return 0; + + /* if the if condition has a false label + then we cannot save */ + if (IC_FALSE (ifx)) + return 0; + + /* if the minus is not of the form + a = a - 1 */ + if (!isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) || !IS_OP_LITERAL (IC_RIGHT (ic))) + return 0; + + if (operandLitValue (IC_RIGHT (ic)) != 1) + return 0; + + /* if the size of this greater than one then no + saving */ + if (getSize (operandType (IC_RESULT (ic))) > 1) + return 0; + + /* otherwise we can save BIG */ + pic16_aopOp (IC_RESULT (ic), ic, FALSE); + + pic16_emitpcode (POC_DECFSZ, pic16_popGet (AOP (IC_RESULT (ic)), 0)); + pic16_emitpcode (POC_GOTO, pic16_popGetLabel (IC_TRUE (ifx)->key)); + + pic16_freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); + ifx->generated = 1; + return 1; +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (iCode * ic) +{ + + FENTRY; + +#if 0 + fprintf (stderr, "%s:%d %s for symbol %s\tonStack: %d\n", __FILE__, __LINE__, __FUNCTION__, + OP_SYMBOL (IC_RESULT (ic))->rname, OP_SYMBOL (IC_RESULT (ic))->onStack); +#endif +// pic16_DumpOp(__FUNCTION__, IC_RESULT(ic)); + + if (isOperandInFarSpace (IC_RESULT (ic)) && (OP_SYMBOL (IC_RESULT (ic))->isspilt || IS_TRUE_SYMOP (IC_RESULT (ic)))) + { + + int size = getSize (operandType (IC_RESULT (ic))); + int offset = pic16_fReturnSizePic - size; + + assert (0); + while (size--) + { + pic16_emitcode ("push", "%s", (strcmp (fReturn[pic16_fReturnSizePic - offset - 1], "a") ? + fReturn[pic16_fReturnSizePic - offset - 1] : "acc")); + offset++; + } + + DEBUGpic16_emitcode ("; ***", "1 %s %d", __FUNCTION__, __LINE__); + + pic16_aopOp (IC_RESULT (ic), ic, FALSE); + size = AOP_SIZE (IC_RESULT (ic)); + offset = 0; + while (size--) + { + pic16_emitcode ("pop", "acc"); + pic16_aopPut (AOP (IC_RESULT (ic)), "a", offset++); + } + } + else + { + DEBUGpic16_emitcode ("; ***", "2 %s %d argreg = %d", __FUNCTION__, __LINE__, + SPEC_ARGREG (OP_SYM_ETYPE (IC_RESULT (ic)))); + _G.accInUse++; + pic16_aopOp (IC_RESULT (ic), ic, FALSE); + _G.accInUse--; + + /* set pseudo stack pointer to where it should be - dw */ + GpseudoStkPtr = ic->parmBytes; + + /* setting GpseudoStkPtr has side effects here: */ + /* FIXME: What's the correct size of the return(ed) value? + * For now, assuming '4' as before... */ + assignResultValue (IC_RESULT (ic), 4, 0); + } + + pic16_freeAsmop (IC_RESULT (ic), NULL, ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (iCode * ic) +{ + operand *op; + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + if (IN_CODESPACE (SPEC_OCLS (OP_SYM_ETYPE (op)))) + { + fprintf (stderr, "%s: volatile symbols in codespace?!? -- might go wrong...\n", __FUNCTION__); + return; + } + pic16_aopOp (op, ic, FALSE); + pic16_mov2w_volatile (AOP (op)); + pic16_freeAsmop (op, NULL, ic, TRUE); + } + else if (op) + { + fprintf (stderr, "%s: not implemented for non-symbols (volatile operand might not be read)\n", __FUNCTION__); + } // if +} + +/*-----------------------------------------------------------------*/ +/* genpic16Code - generate code for pic16 based controllers */ +/*-----------------------------------------------------------------*/ +/* + * At this point, ralloc.c has gone through the iCode and attempted + * to optimize in a way suitable for a PIC. Now we've got to generate + * PIC instructions that correspond to the iCode. + * + * Once the instructions are generated, we'll pass through both the + * peep hole optimizer and the pCode optimizer. + *-----------------------------------------------------------------*/ + +void +genpic16Code (iCode * lic) +{ + iCode *ic; + int cln = 0; + + pb = pic16_newpCodeChain (GcurMemmap, 0, pic16_newpCodeCharP ("; Starting pCode block")); + pic16_addpBlock (pb); + +#if 0 + /* if debug information required */ + if (options.debug && currFunc) + { + if (currFunc) + { + cdbSymbol (currFunc, cdbFile, FALSE, TRUE); + } + } +#endif + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + DEBUGpic16_emitcode (";ic ", "\t%c 0x%x\t(%s)", ic->op, ic->op, pic16_decodeOp (ic->op)); + if (cln != ic->lineno) + { + if (options.debug) + { + debugFile->writeCLine (ic); + } + + if (!options.noCcodeInAsm) + { + pic16_addpCode2pBlock (pb, pic16_newpCodeCSource (ic->lineno, ic->filename, + printCLine (ic->filename, ic->lineno))); + } + + cln = ic->lineno; + } + + if (options.iCodeInAsm) + { + const char *iLine; + + /* insert here code to print iCode as comment */ + iLine = printILine (ic); + pic16_emitpcomment ("ic:%d: %s", ic->seq, iLine); + dbuf_free (iLine); + } + + /* if the result is marked as + * spilt and rematerializable or code for + * this has already been generated then + * do nothing */ + if (resultRemat (ic) || ic->generated) + continue; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + pic16_genNot (ic); + break; + + case '~': + pic16_genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + /* IPOP happens only when trying to restore a + * spilt live range, if there is an ifx statement + * following this pop then the if statement might + * be using some of the registers being popped which + * would destroy the contents of the register so + * we need to check for this condition and handle it */ + if (ic->next && ic->next->op == IFX && regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) + genIfx (ic->next, ic); + else + genIpop (ic); + break; + + case CALL: + genCall (ic); + break; + + case PCALL: + genPcall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genRet (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + pic16_genPlus (ic); + break; + + case '-': + if (!genDjnz (ic, ifxForOp (IC_RESULT (ic), ic))) + pic16_genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + genDiv (ic); + break; + + case '%': + genMod (ic); + break; + + case '>': + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + /* note these two are xlated by algebraic equivalence + * during parsing SDCC.y */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + genAndOp (ic); + break; + + case OR_OP: + genOrOp (ic); + break; + + case '^': + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + pic16_genInline (ic); + break; + + case RRC: + genRRC (ic); + break; + + case RLC: + genRLC (ic); + break; + + case GETABIT: + genGetABit (ic); + break; + + case GETHBIT: + genGetHbit (ic); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic); + break; + + case '=': + if (POINTER_SET (ic)) + genPointerSet (ic); + else + genAssign (ic); + break; + + case IFX: + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + genReceive (ic); + break; + + case SEND: + addSet (&_G.sendSet, ic); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + default: + fprintf (stderr, "UNHANDLED iCode: "); + piCode (ic, stderr); + ic = ic; + } + } + + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + +#ifdef PCODE_DEBUG + DFPRINTF ((stderr, "printing pBlock\n\n")); + pic16_printpBlock (stdout, pb); +#endif + + /* destroy the line list */ + destroy_line_list (); +} diff --git a/src/pic16/gen.h b/src/pic16/gen.h new file mode 100644 index 0000000..3b3be0b --- /dev/null +++ b/src/pic16/gen.h @@ -0,0 +1,224 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for PIC16 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + PIC port - T. Scott Dattalo scott@dattalo.com (2000) + PIC16 port - Martin Dubuc m.dubuc@rogers.com (2000) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCGENPIC16_H +#define SDCCGENPIC16_H + +/* If you change these, you also have to update the library files + * device/lib/pic16/libsdcc/gptr{get,put}{1,2,3,4}.c */ +#define GPTR_TAG_DATA 0x80 +#define GPTR_TAG_EEPROM 0x40 +#define GPTR_TAG_CODE 0x00 /* must be 0 becaue of UPPER(sym)==0 */ + +struct pCodeOp; + +enum + { + AOP_LIT = 1, + AOP_REG, + AOP_DIR, + AOP_STK, + AOP_STR, + AOP_CRY, + AOP_ACC, + AOP_PCODE, + AOP_STA // asmop on stack + }; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop + { + + short type; /* can have values + AOP_LIT - operand is a literal value + AOP_REG - is in registers + AOP_DIR - direct just a name + AOP_STK - should be pushed on stack this + can happen only for the result + AOP_CRY - carry contains the value of this + AOP_STR - array of strings + AOP_ACC - result is in the acc:b pair + */ + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned freed:1; /* already freed */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + reg_info *aop_ptr; /* either -> to r0 or r1 */ + int aop_stk; /* stack offset when AOP_STK */ + char *aop_str[4]; /* just a string array containing the location */ +/* regs *aop_alloc_reg; * points to a dynamically allocated register */ + pCodeOp *pcop; + struct { + int stk; + pCodeOp *pop[4]; + } stk; + } + aopu; + } +asmop; + +void genpic16Code (iCode *); + +extern unsigned pic16_fReturnSizePic; + + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size + +#define AOP_NEEDSACC(x) (AOP(x) && (AOP_TYPE(x) == AOP_CRY || \ + AOP(x)->paged)) + +#define RESULTONSTACK(x) \ + (IC_RESULT(x) && IC_RESULT(x)->aop && \ + IC_RESULT(x)->aop->type == AOP_STK ) +#define RESULTONSTA(x) (IC_RESULT(x) && IC_RESULT(x)->aop && IC_RESULT(x)->aop->type == AOP_STA) + + +#define MOVA(x) if (strcmp(x,"a") && strcmp(x,"acc")) pic16_emitcode(";XXX mov","a,%s %s,%d",x,__FILE__,__LINE__); +#define CLRC pic16_emitcode(";XXX clr","c %s,%d",__FILE__,__LINE__); + + +#define BIT_NUMBER(x) (x & 7) +#define BIT_REGISTER(x) (x>>3) + + +#define LSB 0 +#define MSB16 1 +#define MSB24 2 +#define MSB32 3 + + +#define FUNCTION_LABEL_INC 40 + +/*-----------------------------------------------------------------*/ +/* Macros for emitting skip instructions */ +/*-----------------------------------------------------------------*/ + +#define emitSKPC pic16_emitpcode(POC_BTFSS,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_C_BIT)) +#define emitSKPNC pic16_emitpcode(POC_BTFSC,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_C_BIT)) +#define emitSKPZ pic16_emitpcode(POC_BTFSS,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_Z_BIT)) +#define emitSKPNZ pic16_emitpcode(POC_BTFSC,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_Z_BIT)) +#define emitSKPDC pic16_emitpcode(POC_BTFSS,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_DC_BIT)) +#define emitSKPNDC pic16_emitpcode(POC_BTFSC,pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_DC_BIT)) +#define emitCLRZ pic16_emitpcode(POC_BCF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_Z_BIT)) +#define emitCLRC pic16_emitpcode(POC_BCF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_C_BIT)) +#define emitCLRDC pic16_emitpcode(POC_BCF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_DC_BIT)) +#define emitSETZ pic16_emitpcode(POC_BSF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_Z_BIT)) +#define emitSETC pic16_emitpcode(POC_BSF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_C_BIT)) +#define emitSETDC pic16_emitpcode(POC_BSF, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_DC_BIT)) + +#define emitTOGC pic16_emitpcode(POC_BTG, pic16_popCopyGPR2Bit(PCOP(&pic16_pc_status),PIC_C_BIT)) + +int pic16_getDataSize(operand *op); +void pic16_emitpcode_real(PIC_OPCODE poc, pCodeOp *pcop); +#define pic16_emitpcode(poc,pcop) do { if (pic16_pcode_verbose) pic16_emitpcomment ("%s:%u(%s):", __FILE__, __LINE__, __FUNCTION__); pic16_emitpcode_real(poc,pcop); } while(0) +void pic16_emitpLabel(int key); +void pic16_emitcode (char *inst,char *fmt, ...); +void DEBUGpic16_emitcode (char *inst,char *fmt, ...); +void pic16_emitDebuggerSymbol (const char *); +bool pic16_sameRegs (asmop *aop1, asmop *aop2 ); +char *pic16_aopGet (asmop *aop, int offset, bool bit16, bool dname); +void DEBUGpic16_pic16_AopType(int line_no, operand *left, operand *right, operand *result); +void DEBUGpic16_pic16_AopTypeSign(int line_no, operand *left, operand *right, operand *result); + + +bool pic16_genPlusIncr (iCode *ic); +void pic16_outBitAcc(operand *result); +void pic16_genPlusBits (iCode *ic); +void pic16_genPlus (iCode *ic); +bool pic16_genMinusDec (iCode *ic); +void pic16_addSign(operand *result, int offset, int sign); +void pic16_genMinusBits (iCode *ic); +void pic16_genMinus (iCode *ic); +void pic16_genLeftShiftLiteral (operand *left, operand *right, operand *result, iCode *ic); + +pCodeOp *pic16_popGet2p(pCodeOp *src, pCodeOp *dst); +void pic16_emitpcomment (char *fmt, ...); + +pCodeOp *pic16_popGetLabel(int key); +pCodeOp *pic16_popCopyReg(pCodeOpReg *pc); +pCodeOp *pic16_popCopyGPR2Bit(pCodeOp *pc, int bitval); +pCodeOp *pic16_popGetLit(int lit); +pCodeOp *pic16_popGetLit2(int lit, pCodeOp *arg2); +pCodeOp *popGetWithString(char *str); +pCodeOp *pic16_popGet (asmop *aop, int offset);//, bool bit16, bool dname); +pCodeOp *pic16_popGetTempReg(int lock); +pCodeOp *pic16_popGetTempRegCond(bitVect *, bitVect *, int lock); +void pic16_popReleaseTempReg(pCodeOp *pcop, int lock); + +pCodeOp *pic16_popCombine2(pCodeOpReg *src, pCodeOpReg *dst, int noalloc); + +void pic16_aopPut (asmop *aop, char *s, int offset); +void pic16_outAcc(operand *result); +void pic16_aopOp (operand *op, iCode *ic, bool result); +void pic16_outBitC(operand *result); +void pic16_toBoolean(operand *oper); +void pic16_freeAsmop (operand *op, asmop *aaop, iCode *ic, bool pop); +const char *pic16_pCodeOpType( pCodeOp *pcop); +int pic16_my_powof2 (unsigned long num); + +void pic16_mov2w (asmop *aop, int offset); +void pic16_mov2f(asmop *dst, asmop *src, int offset); + +bool pic16_isLitOp(operand *op); +bool pic16_isLitAop(asmop *aop); + +void dumpiCode(iCode *lic); + +int inWparamList(char *s); + +#include "device.h" + +#define DUMP_FUNCTION_ENTRY 1 +#define DUMP_FUNCTION_EXIT 0 + +#if DUMP_FUNCTION_ENTRY +#define FENTRY if(pic16_options.debgen&2)pic16_emitpcomment("**{\t%d %s", __LINE__, __FUNCTION__) +#define FENTRY2 if(pic16_options.debgen&2)pic16_emitpcomment("**{\t%d %s", __LINE__, __FUNCTION__) +#else +#define FENTRY +#define FENTRY2 +#endif + +#if DUMP_FUNCTION_EXIT +#define FEXIT if(pic16_options.debgen&2)pic16_emitpcomment("; **}", "%d %s", __LINE__, __FUNCTION__) +#define FEXIT2 if(pic16_options.debgen&2)pic16_emitpcomment("**{\t%d %s", __LINE__, __FUNCTION__) +#else +#define FEXIT +#define FEXIT2 +#endif + +#define ERROR werror(W_POSSBUG2, __FILE__, __LINE__) +#endif diff --git a/src/pic16/genarith.c b/src/pic16/genarith.c new file mode 100644 index 0000000..fd710e0 --- /dev/null +++ b/src/pic16/genarith.c @@ -0,0 +1,2036 @@ +/*------------------------------------------------------------------------- + genarith.c - source file for code generation - arithmetic + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + PIC port: + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + PIC16 port: + Copyright (C) 2002, Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ +/* + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +*/ + +#include +#include +#include +#include +#include "SDCCglobl.h" +#include "newalloc.h" + +#include "common.h" +#include "SDCCpeeph.h" +#include "ralloc.h" +#include "pcode.h" +#include "gen.h" + +#if 1 +#define pic16_emitcode DEBUGpic16_emitcode +#endif + +extern void pic16_emitpLabelFORCE(int key); + +#define BYTEofLONG(l,b) ( (l>> (b<<3)) & 0xff) + +void DEBUGpic16_pic16_AopType(int line_no, operand *left, operand *right, operand *result); +void pic16_emitpcomment(char *, ...); +pCodeOp *pic16_popGet2p(pCodeOp *src, pCodeOp *dst); +const char *pic16_AopType(short type) +{ + switch(type) { + case AOP_LIT: return "AOP_LIT"; + case AOP_REG: return "AOP_REG"; + case AOP_DIR: return "AOP_DIR"; + case AOP_STK: return "AOP_STK"; + case AOP_STR: return "AOP_STR"; + case AOP_CRY: return "AOP_CRY"; + case AOP_ACC: return "AOP_ACC"; + case AOP_PCODE: return "AOP_PCODE"; + case AOP_STA: return "AOP_STA"; + } + + return "BAD TYPE"; +} + +const char *pic16_pCodeOpType(pCodeOp *pcop) +{ + + if(pcop) { + + switch(pcop->type) { + + case PO_NONE: return "PO_NONE"; + case PO_W: return "PO_W"; + case PO_WREG: return "PO_WREG"; + case PO_STATUS: return "PO_STATUS"; + case PO_BSR: return "PO_BSR"; + case PO_FSR0: return "PO_FSR0"; + case PO_INDF0: return "PO_INDF0"; + case PO_INTCON: return "PO_INTCON"; + case PO_GPR_REGISTER: return "PO_GPR_REGISTER"; + case PO_GPR_BIT: return "PO_GPR_BIT"; + case PO_GPR_TEMP: return "PO_GPR_TEMP"; + case PO_SFR_REGISTER: return "PO_SFR_REGISTER"; + case PO_PCL: return "PO_PCL"; + case PO_PCLATH: return "PO_PCLATH"; + case PO_PCLATU: return "PO_PCLATU"; + case PO_PRODL: return "PO_PRODL"; + case PO_PRODH: return "PO_PRODH"; + case PO_LITERAL: return "PO_LITERAL"; + case PO_REL_ADDR: return "PO_REL_ADDR"; + case PO_IMMEDIATE: return "PO_IMMEDIATE"; + case PO_DIR: return "PO_DIR"; + case PO_CRY: return "PO_CRY"; + case PO_BIT: return "PO_BIT"; + case PO_STR: return "PO_STR"; + case PO_LABEL: return "PO_LABEL"; + case PO_WILD: return "PO_WILD"; + case PO_TWO_OPS: return "PO_TWO_OPS"; + } + } + + return "BAD PO_TYPE"; +} + +const char *pic16_pCodeOpSubType(pCodeOp *pcop) +{ + + if(pcop && (pcop->type == PO_GPR_BIT)) { + + switch(PCORB(pcop)->subtype) { + + case PO_NONE: return "PO_NONE"; + case PO_W: return "PO_W"; + case PO_WREG: return "PO_WREG"; + case PO_STATUS: return "PO_STATUS"; + case PO_BSR: return "PO_BSR"; + case PO_FSR0: return "PO_FSR0"; + case PO_INDF0: return "PO_INDF0"; + case PO_INTCON: return "PO_INTCON"; + case PO_GPR_REGISTER: return "PO_GPR_REGISTER"; + case PO_GPR_BIT: return "PO_GPR_BIT"; + case PO_GPR_TEMP: return "PO_GPR_TEMP"; + case PO_SFR_REGISTER: return "PO_SFR_REGISTER"; + case PO_PCL: return "PO_PCL"; + case PO_PCLATH: return "PO_PCLATH"; + case PO_PCLATU: return "PO_PCLATU"; + case PO_PRODL: return "PO_PRODL"; + case PO_PRODH: return "PO_PRODH"; + case PO_LITERAL: return "PO_LITERAL"; + case PO_REL_ADDR: return "PO_REL_ADDR"; + case PO_IMMEDIATE: return "PO_IMMEDIATE"; + case PO_DIR: return "PO_DIR"; + case PO_CRY: return "PO_CRY"; + case PO_BIT: return "PO_BIT"; + case PO_STR: return "PO_STR"; + case PO_LABEL: return "PO_LABEL"; + case PO_WILD: return "PO_WILD"; + case PO_TWO_OPS: return "PO_TWO_OPS"; + } + } + + return "BAD PO_TYPE"; +} + +/*-----------------------------------------------------------------*/ +/* pic16_genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +bool pic16_genPlusIncr (iCode *ic) +{ + unsigned int icount; + unsigned int size = pic16_getDataSize(IC_RESULT(ic)); + + FENTRY; + + DEBUGpic16_emitcode ("; ","result %s, left %s, right %s", + pic16_AopType(AOP_TYPE(IC_RESULT(ic))), + pic16_AopType(AOP_TYPE(IC_LEFT(ic))), + pic16_AopType(AOP_TYPE(IC_RIGHT(ic)))); + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE(IC_RIGHT(ic)) != AOP_LIT) + { + return FALSE; + } + + DEBUGpic16_emitcode ("; ","%s %d",__FUNCTION__,__LINE__); + /* if the literal value of the right hand side + is greater than 2 then it is faster to add */ + if ((icount = (unsigned int)ulFromVal(AOP(IC_RIGHT(ic))->aopu.aop_lit)) > 2) + { + return FALSE; + } + + /* if increment 16 bits in register */ + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) && icount == 1) + { + int offset = LSB; + + switch (size) + { + case 2: + pic16_emitpcode(POC_INFSNZ, pic16_popGet(AOP(IC_RESULT(ic)), offset++)); + case 1: + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + break; + + default: + { + symbol *inclbl = newiTempLabel(NULL); + pCodeOp *pcop_lbl = pic16_popGetLabel(inclbl->key); + int label_use = 0; + + size -= 2; + if (size) + { + label_use = 1; + } + + while (size--) + { + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(IC_RESULT(ic)), offset++)); + pic16_emitpcode(POC_BNC, pcop_lbl); + } + + pic16_emitpcode(POC_INFSNZ, pic16_popGet(AOP(IC_RESULT(ic)), offset++)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + + if (label_use) + { + pic16_emitpLabelFORCE(inclbl->key); + } + } + } // switch (size) + + return TRUE; + } + +// DEBUGpic16_emitcode ("; ","%s %d",__FUNCTION__,__LINE__); + /* if left is in accumulator - probably a bit operation*/ // VR - why this is a bit operation?! + if( (AOP_TYPE(IC_LEFT(ic)) == AOP_ACC) && + (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) ) { + + pic16_emitpcode(POC_BCF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + if(icount) + pic16_emitpcode(POC_XORLW,pic16_popGetLit(1)); + //pic16_emitcode("xorlw","1"); + else + pic16_emitpcode(POC_ANDLW,pic16_popGetLit(1)); + //pic16_emitcode("andlw","1"); + + emitSKPZ; + pic16_emitpcode(POC_BSF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + + return TRUE; + } + + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE(IC_RESULT(ic)) > 1 || + AOP_SIZE(IC_LEFT(ic)) > 1 ) + return FALSE ; + + /* If we are incrementing the same register by two: */ + + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + + while (icount--) + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + //pic16_emitcode("incf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + return TRUE ; + } + + DEBUGpic16_emitcode ("; ","couldn't increment "); + + return FALSE ; +} + +/*-----------------------------------------------------------------*/ +/* pic16_outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +void pic16_outBitAcc(operand *result) +{ + symbol *tlbl = newiTempLabel(NULL); + /* if the result is a bit */ + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + assert(0); // not implemented for PIC16? + + if (AOP_TYPE(result) == AOP_CRY){ + pic16_aopPut(AOP(result),"a",0); + } + else { + pic16_emitcode("jz","%05d_DS_",tlbl->key+100); + pic16_emitcode("mov","a,#01"); + pic16_emitcode("","%05d_DS_:",tlbl->key+100); + pic16_outAcc(result); + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_genPlusBits - generates code for addition of two bits */ +/*-----------------------------------------------------------------*/ +void pic16_genPlusBits (iCode *ic) +{ + FENTRY; + + DEBUGpic16_emitcode ("; ","result %s, left %s, right %s", + pic16_AopType(AOP_TYPE(IC_RESULT(ic))), + pic16_AopType(AOP_TYPE(IC_LEFT(ic))), + pic16_AopType(AOP_TYPE(IC_RIGHT(ic)))); + /* + The following block of code will add two bits. + Note that it'll even work if the destination is + the carry (C in the status register). + It won't work if the 'Z' bit is a source or destination. + */ + + /* If the result is stored in the accumulator (w) */ + //if(strcmp(pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE),"a") == 0 ) { + switch(AOP_TYPE(IC_RESULT(ic))) { + case AOP_ACC: + pic16_emitpcode(POC_CLRF, pic16_popCopyReg(&pic16_pc_wreg)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_XORLW, pic16_popGetLit(1)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_XORLW, pic16_popGetLit(1)); + break; + case AOP_REG: + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(0)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_XORLW, pic16_popGetLit(1)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_XORLW, pic16_popGetLit(1)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + break; + default: + pic16_emitpcode(POC_MOVLW, pic16_popGet(AOP(IC_RESULT(ic)),0)); + pic16_emitpcode(POC_BCF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_XORWF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_XORWF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + break; + } + +} + +#if 0 +/* This is the original version of this code. + * + * This is being kept around for reference, + * because I am not entirely sure I got it right... + */ +static void adjustArithmeticResult(iCode *ic) +{ + if (AOP_SIZE(IC_RESULT(ic)) == 3 && + AOP_SIZE(IC_LEFT(ic)) == 3 && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_LEFT(ic)))) + pic16_aopPut(AOP(IC_RESULT(ic)), + pic16_aopGet(AOP(IC_LEFT(ic)),2,FALSE,FALSE), + 2); + + if (AOP_SIZE(IC_RESULT(ic)) == 3 && + AOP_SIZE(IC_RIGHT(ic)) == 3 && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_RIGHT(ic)))) + pic16_aopPut(AOP(IC_RESULT(ic)), + pic16_aopGet(AOP(IC_RIGHT(ic)),2,FALSE,FALSE), + 2); + + if (AOP_SIZE(IC_RESULT(ic)) == 3 && + AOP_SIZE(IC_LEFT(ic)) < 3 && + AOP_SIZE(IC_RIGHT(ic)) < 3 && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_LEFT(ic))) && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_RIGHT(ic)))) { + char buffer[5]; + SNPRINTF(buffer, sizeof(buffer), "#%d", pointerCode(getSpec(operandType(IC_LEFT(ic))))); + pic16_aopPut(AOP(IC_RESULT(ic)),buffer,2); + } +} +//#else +/* This is the pure and virtuous version of this code. + * I'm pretty certain it's right, but not enough to toss the old + * code just yet... + */ +static void adjustArithmeticResult(iCode *ic) +{ + if (opIsGptr(IC_RESULT(ic)) && + opIsGptr(IC_LEFT(ic)) && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_LEFT(ic)))) + { + pic16_aopPut(AOP(IC_RESULT(ic)), + pic16_aopGet(AOP(IC_LEFT(ic)), GPTRSIZE - 1,FALSE,FALSE), + GPTRSIZE - 1); + } + + if (opIsGptr(IC_RESULT(ic)) && + opIsGptr(IC_RIGHT(ic)) && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_RIGHT(ic)))) + { + pic16_aopPut(AOP(IC_RESULT(ic)), + pic16_aopGet(AOP(IC_RIGHT(ic)),GPTRSIZE - 1,FALSE,FALSE), + GPTRSIZE - 1); + } + + if (opIsGptr(IC_RESULT(ic)) && + AOP_SIZE(IC_LEFT(ic)) < GPTRSIZE && + AOP_SIZE(IC_RIGHT(ic)) < GPTRSIZE && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_LEFT(ic))) && + !pic16_sameRegs(AOP(IC_RESULT(ic)),AOP(IC_RIGHT(ic)))) { + char buffer[5]; + + SNPRINTF(buffer, sizeof(buffer), "#%d", pointerCode(getSpec(operandType(IC_LEFT(ic))))); + pic16_aopPut(AOP(IC_RESULT(ic)),buffer,GPTRSIZE - 1); + } +} +#endif + +#if 1 +/*-----------------------------------------------------------------*/ +/* genAddlit - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void genAddLit2byte (operand *result, int offr, int lit) +{ + FENTRY; + + switch(lit & 0xff) + { + case 0: + break; + + case 1: + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),offr)); + break; + + case 0xff: + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),offr)); + break; + + default: + { + int resultIsWREG = (AOP_TYPE(result) != AOP_STA && ! strcmp(pic16_aopGet(AOP(result), 0, TRUE, FALSE), "WREG")); + + if (resultIsWREG) + { + pic16_emitpcode(POC_ADDLW, pic16_popGetLit(lit & 0xff)); + } + else + { + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),offr)); + } + } + } +} +#endif + +static void emitMOVWF(operand *reg, int offset) +{ + if(!reg) + return; + + if (AOP_TYPE(reg) == AOP_ACC) { + DEBUGpic16_emitcode ("; ***","%s %d ignoring mov into W",__FUNCTION__,__LINE__); + return; + } + + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(reg),offset)); + +} + + +#if 1 + +static void genAddLit (iCode *ic, int lit) +{ + + int size,same; + int lo, offset; + + operand *result; + operand *left; + + FENTRY; + + left = IC_LEFT(ic); + result = IC_RESULT(ic); + same = pic16_sameRegs(AOP(left), AOP(result)); + size = pic16_getDataSize(result); + + if ((AOP_PCODE == AOP_TYPE(left)) + && (PO_IMMEDIATE == AOP(left)->aopu.pcop->type)) + { + /* see #1888004 for an example case for this */ + for (offset = 0; offset < size; offset++) { + pic16_emitpcode(POC_MOVLW, pic16_newpCodeOpImmd(AOP(left)->aopu.pcop->name, + offset, PCOI(AOP(left)->aopu.pcop)->index + lit, 0)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result), offset)); + } // for + return; + } // if + + if(same) { + + /* Handle special cases first */ + if(size == 1) + genAddLit2byte (result, 0, lit); + + else if(size == 2) { + int hi = 0xff & (lit >> 8); + lo = lit & 0xff; + + switch(hi) { + case 0: + + /* lit = 0x00LL */ + DEBUGpic16_emitcode ("; hi = 0","%s %d",__FUNCTION__,__LINE__); + switch(lo) { + case 0: + break; + + case 1: + pic16_emitpcode(POC_INFSNZ, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + + case 0xff: + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCFSZW, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + + break; + default: + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lit&0xff)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPNC; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + } + break; + + case 1: + /* lit = 0x01LL */ + DEBUGpic16_emitcode ("; hi = 1","%s %d",__FUNCTION__,__LINE__); + switch(lo) { + case 0: /* 0x0100 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + case 1: /* 0x0101 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + case 0xff: /* 0x01ff */ + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCFSZW, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + default: /* 0x01LL */ + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPNC; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + } + break; + + case 0xff: + DEBUGpic16_emitcode ("; hi = ff","%s %d",__FUNCTION__,__LINE__); + /* lit = 0xffLL */ + switch(lo) { + case 0: /* 0xff00 */ + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + break; + case 1: /*0xff01 */ + pic16_emitpcode(POC_INCFSZ, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + break; + + case 0xff: /* 0xffff */ + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_ADDWFC, pic16_popGet(AOP(result),MSB16)); + break; + + default: + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPC; + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + + } + + break; + + default: + DEBUGpic16_emitcode ("; hi is generic","%d %s %d",hi,__FUNCTION__,__LINE__); + + /* lit = 0xHHLL */ + switch(lo) { + case 0: /* 0xHH00 */ + genAddLit2byte (result, MSB16, hi); + break; + case 1: /* 0xHH01 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWFC,pic16_popGet(AOP(result),MSB16)); + break; +/* case 0xff: * 0xHHff * + pic16_emitpcode(POC_MOVFW, pic16_popGet(AOP(result),0,FALSE,FALSE)); + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16,FALSE,FALSE)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),MSB16,FALSE,FALSE)); + break; +*/ default: /* 0xHHLL */ + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWFC,pic16_popGet(AOP(result),MSB16)); + break; + } + + } + } else { + int carry_info = 0; + int prev_W = -1; + /* size > 2 */ + DEBUGpic16_emitcode ("; add lit to long","%s %d",__FUNCTION__,__LINE__); + + offset = 0; + while (size--) + { + lo = BYTEofLONG(lit,0); + + if (carry_info) + { + if (prev_W != (lo & 0xFF)) + { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lo)); + prev_W = lo & 0xFF; + } + + pic16_emitpcode(POC_ADDWFC, pic16_popGet(AOP(result),offset)); + } + else { + /* no carry info from previous step */ + /* this means this is the first time to add */ + switch (lo) + { + case 0: + break; + + case 1: + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),offset)); + carry_info = 1; + break; + + default: + { + if (prev_W != (lo & 0xFF)) + { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lo)); + prev_W = lo & 0xFF; + } + + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),offset)); + + if (lit < 0x100) + carry_info = 3; /* Were adding only one byte and propogating the carry. */ + else + carry_info = 2; + break; + } + } + } + offset++; + lit >>= 8; + } + +/* + lo = BYTEofLONG(lit,0); + + if(lit < 0x100) { + if(lo) { + if(lo == 1) { + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitSKPNZ; + } else { + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitSKPNC; + } + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),1,FALSE,FALSE)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),2,FALSE,FALSE)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),3,FALSE,FALSE)); + + } + } + +*/ + } + } else { + int offset = 1; + DEBUGpic16_emitcode ("; left and result aren't same","%s %d",__FUNCTION__,__LINE__); + + if(size == 1) { + + if(AOP_TYPE(left) == AOP_ACC) { + /* left addend is already in accumulator */ + switch(lit & 0xff) { + case 0: + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + default: + pic16_emitpcode(POC_ADDLW, pic16_popGetLit(lit & 0xff)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + } + } else { + /* left addend is in a register */ + switch(lit & 0xff) { + case 0: + pic16_mov2w(AOP(left),0); + emitMOVWF(result, 0); + break; + case 1: + pic16_emitpcode(POC_INCFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + case 0xff: + pic16_emitpcode(POC_DECFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + default: + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + } + } + +// } else if (pic16_isLitAop(AOP(left))) { +// // adding two literals +// assert ( !"adding two literals is not yet supported" ); + } else { + int clear_carry=0; + + /* left is not the accumulator */ + if(lit & 0xff) { + pic16_mov2w(AOP(left),0); + pic16_emitpcode(POC_ADDLW, pic16_popGetLit(lit & 0xff)); + } else { + pic16_mov2w(AOP(left),0); + /* We don't know the state of the carry bit at this point */ + clear_carry = 1; + } + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + while(--size) { + lit >>= 8; + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + if (offset < AOP_SIZE(left)) { + pic16_emitpcode(clear_carry ? POC_ADDFW : POC_ADDFWC, pic16_popGet(AOP(left),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),offset)); + } else { + pic16_emitpcode(POC_CLRF, pic16_popGet(AOP(result),offset)); + if (!SPEC_USIGN(operandType(IC_LEFT(ic)))) { + /* sign-extend left (in result) */ + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple(AOP(left),AOP_SIZE(left)-1,7)); + pic16_emitpcode(POC_SETF, pic16_popGet(AOP(result),offset)); + } + pic16_emitpcode(clear_carry ? POC_ADDWF : POC_ADDWFC, pic16_popGet(AOP(result),offset)); + } + clear_carry = 0; + offset++; + } + } + } +} + +#else + /* this fails when result is an SFR because value is written there + * during addition and not at the end */ + +static void genAddLit (iCode *ic, int lit) +{ + + int size,sizeL,same; + int i, llit, prev_W; + + operand *result; + operand *left; + + FENTRY; + + + left = IC_LEFT(ic); + result = IC_RESULT(ic); + same = pic16_sameRegs(AOP(left), AOP(result)); + size = pic16_getDataSize(result); + sizeL = pic16_getDataSize(left); + llit = lit; + +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + /* move left to result -- possibly sign extend */ + for (i=0; i < MIN(size, sizeL); i++) { + pic16_mov2f (AOP(result), AOP(left), i); + } // for i +#undef MIN + + /* extend to result size */ + pic16_addSign(result, sizeL, !IS_UNSIGNED(operandType(left))); + + /* special cases */ + if (lit == 0) { + /* nothing to do */ + } else if (lit == 1) { + switch (size) { + case 1: + /* handled below */ + break; + case 2: + pic16_emitpcode (POC_INFSNZ, pic16_popGet (AOP(result), 0)); + break; + default: + assert (size > 2); + pic16_emitpcode (POC_INCF, pic16_popGet(AOP(result), 0)); + for (i=1; i < size-1; i++) { + emitSKPNC; /* a jump here saves up to 2(size-2)cycles */ + pic16_emitpcode (POC_INCF, pic16_popGet(AOP(result), i)); + } // for i + emitSKPNC; + break; + } // switch + + pic16_emitpcode (POC_INCF, pic16_popGet (AOP(result), size-1)); + } else { + /* general case */ + + /* add literal to result */ + prev_W = -1; + for (i = 0; i < size; i++) + { + if (prev_W != (llit & 0xFF)) + { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(llit)); + prev_W = llit & 0xFF; + } + + llit >>= 8; /* FIXME: arithmetic right shift for signed literals? */ + pic16_emitpcode(i == 0 ? POC_ADDWF : POC_ADDWFC, pic16_popGet(AOP(result), i)); + } + } + +#if 0 + + if(same) { + + /* Handle special cases first */ + if(size == 1) + genAddLit2byte (result, 0, lit); + + else if(size == 2) { + int hi = 0xff & (lit >> 8); + lo = lit & 0xff; + + switch(hi) { + case 0: + + /* lit = 0x00LL */ + DEBUGpic16_emitcode ("; hi = 0","%s %d",__FUNCTION__,__LINE__); + switch(lo) { + case 0: + break; + case 1: + pic16_emitpcode(POC_INFSNZ, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + case 0xff: + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCFSZW, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + + break; + default: + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lit&0xff)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPNC; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + + + } + break; + + case 1: + /* lit = 0x01LL */ + DEBUGpic16_emitcode ("; hi = 1","%s %d",__FUNCTION__,__LINE__); + switch(lo) { + case 0: /* 0x0100 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + case 1: /* 0x0101 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + case 0xff: /* 0x01ff */ + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCFSZW, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + break; + default: /* 0x01LL */ + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPNC; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16)); + } + break; + + case 0xff: + DEBUGpic16_emitcode ("; hi = ff","%s %d",__FUNCTION__,__LINE__); + /* lit = 0xffLL */ + switch(lo) { + case 0: /* 0xff00 */ + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + break; + case 1: /*0xff01 */ + pic16_emitpcode(POC_INCFSZ, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + break; +/* case 0xff: * 0xffff * + pic16_emitpcode(POC_INCFSZW, pic16_popGet(AOP(result),0,FALSE,FALSE)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),MSB16,FALSE,FALSE)); + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + break; +*/ + default: + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),0)); + emitSKPC; + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16)); + + } + + break; + + default: + DEBUGpic16_emitcode ("; hi is generic","%d %s %d",hi,__FUNCTION__,__LINE__); + + /* lit = 0xHHLL */ + switch(lo) { + case 0: /* 0xHH00 */ + genAddLit2byte (result, MSB16, hi); + break; + case 1: /* 0xHH01 */ + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWFC,pic16_popGet(AOP(result),MSB16)); + break; +/* case 0xff: * 0xHHff * + pic16_emitpcode(POC_MOVFW, pic16_popGet(AOP(result),0,FALSE,FALSE)); + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(result),MSB16,FALSE,FALSE)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWF,pic16_popGet(AOP(result),MSB16,FALSE,FALSE)); + break; +*/ default: /* 0xHHLL */ + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),0)); + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(hi)); + pic16_emitpcode(POC_ADDWFC,pic16_popGet(AOP(result),MSB16)); + break; + } + + } + } else { + int carry_info = 0; + int offset = 0; + /* size > 2 */ + DEBUGpic16_emitcode ("; add lit to long","%s %d",__FUNCTION__,__LINE__); + + while(size--) { + lo = BYTEofLONG(lit,0); + + if(carry_info) { + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWFC, pic16_popGet(AOP(result),offset)); + }else { + /* no carry info from previous step */ + /* this means this is the first time to add */ + switch(lo) { + case 0: + break; + case 1: + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),offset)); + carry_info=1; + break; + default: + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),offset)); + if(lit <0x100) + carry_info = 3; /* Were adding only one byte and propogating the carry */ + else + carry_info = 2; + break; + } + } + offset++; + lit >>= 8; + } + +/* + lo = BYTEofLONG(lit,0); + + if(lit < 0x100) { + if(lo) { + if(lo == 1) { + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitSKPNZ; + } else { + pic16_emitpcode(POC_MOVLW,pic16_popGetLit(lo)); + pic16_emitpcode(POC_ADDWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitSKPNC; + } + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),1,FALSE,FALSE)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),2,FALSE,FALSE)); + emitSKPNZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),3,FALSE,FALSE)); + + } + } + +*/ + } + } else { + int offset = 1; + DEBUGpic16_emitcode ("; left and result aren't same","%s %d",__FUNCTION__,__LINE__); + + if(size == 1) { + + if(AOP_TYPE(left) == AOP_ACC) { + /* left addend is already in accumulator */ + switch(lit & 0xff) { + case 0: + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + default: + pic16_emitpcode(POC_ADDLW, pic16_popGetLit(lit & 0xff)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + } + } else { + /* left addend is in a register */ + switch(lit & 0xff) { + case 0: + pic16_mov2w(AOP(left),0); + emitMOVWF(result, 0); + break; + case 1: + pic16_emitpcode(POC_INCFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + case 0xff: + pic16_emitpcode(POC_DECFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + break; + default: + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDFW, pic16_popGet(AOP(left),0)); + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + } + } + + } else { + int clear_carry=0; + + /* left is not the accumulator */ + if(lit & 0xff) { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDFW, pic16_popGet(AOP(left),0)); + } else { + pic16_mov2w(AOP(left),0); + /* We don't know the state of the carry bit at this point */ + clear_carry = 1; + } + //pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),0,FALSE,FALSE)); + emitMOVWF(result,0); + while(--size) { + + lit >>= 8; + if(lit & 0xff) { + if(clear_carry) { + /* The ls byte of the lit must've been zero - that + means we don't have to deal with carry */ + + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDFW, pic16_popGet(AOP(left),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),offset)); + + clear_carry = 0; + + } else { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_ADDFWC, pic16_popGet(AOP(left),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),offset)); + } + + } else { + pic16_emitpcode(POC_CLRF, pic16_popGet(AOP(result),offset)); + pic16_mov2w(AOP(left),offset); + pic16_emitpcode(POC_ADDWFC, pic16_popGet(AOP(result),offset)); + } + offset++; + } + } + } +#endif +} + +#endif + +/*-----------------------------------------------------------------*/ +/* pic16_genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +void pic16_genPlus (iCode *ic) +{ + int i, size, offset = 0; + operand *result, *left, *right; + + FENTRY; + + /* special cases :- */ + result = IC_RESULT(ic); + left = IC_LEFT(ic); + right = IC_RIGHT(ic); + pic16_aopOp (left,ic,FALSE); + pic16_aopOp (right,ic,FALSE); + pic16_aopOp (result,ic,TRUE); + DEBUGpic16_pic16_AopType(__LINE__,left, right, result); + // pic16_DumpOp("(left)",left); + + /* if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + + if ( (AOP_TYPE(left) == AOP_LIT) || (pic16_sameRegs(AOP(right), AOP(result))) ) { + operand *t = right; + right = IC_RIGHT(ic) = left; + left = IC_LEFT(ic) = t; + } + + /* if both left & right are in bit space */ + if (AOP_TYPE(left) == AOP_CRY && + AOP_TYPE(right) == AOP_CRY) { + pic16_genPlusBits (ic); + goto release ; + } + + /* if left in bit space & right literal */ + if (AOP_TYPE(left) == AOP_CRY && + AOP_TYPE(right) == AOP_LIT) { + /* if result in bit space */ + if(AOP_TYPE(result) == AOP_CRY){ + if(ulFromVal (AOP(right)->aopu.aop_lit) != 0L) { + pic16_emitpcode(POC_MOVLW, pic16_popGet(AOP(result),0)); + if (!pic16_sameRegs(AOP(left), AOP(result)) ) + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(left),0)); + pic16_emitpcode(POC_XORWF, pic16_popGet(AOP(result),0)); + } + } else { + unsigned long lit = ulFromVal (AOP(right)->aopu.aop_lit); + size = pic16_getDataSize(result); + while (size--) { + pic16_emitpcode (POC_CLRF, pic16_popGet (AOP(result), offset)); + pic16_emitpcode (POC_MOVLW, pic16_popGetLit ((lit >> (8*offset)) & 0xFF)); + pic16_emitpcode (POC_ADDWFC, pic16_popGet(AOP(result), offset++)); + //MOVA(pic16_aopGet(AOP(right),offset,FALSE,FALSE)); + //pic16_emitcode("addc","a,#00 ;%d",__LINE__); + //pic16_aopPut(AOP(result),"a",offset++); + } + } + goto release ; + } // left == CRY + + /* if I can do an increment instead + of add then GOOD for ME */ + if (pic16_genPlusIncr (ic) == TRUE) + goto release; + + size = pic16_getDataSize(result); + + if(AOP(right)->type == AOP_LIT) { + /* Add a literal to something else */ + //bool know_W=0; + unsigned lit = (unsigned) ulFromVal (AOP(right)->aopu.aop_lit); + //unsigned l1=0; + + //offset = 0; + DEBUGpic16_emitcode(";","adding lit to something. size %d",size); + + genAddLit (ic, lit); + goto release; + + } else if(AOP_TYPE(right) == AOP_CRY) { + + pic16_emitcode(";bitadd","right is bit: %s",pic16_aopGet(AOP(right),0,FALSE,FALSE)); + pic16_emitcode(";bitadd","left is bit: %s",pic16_aopGet(AOP(left),0,FALSE,FALSE)); + pic16_emitcode(";bitadd","result is bit: %s",pic16_aopGet(AOP(result),0,FALSE,FALSE)); + + /* here we are adding a bit to a char or int */ + if(size == 1) { + if (pic16_sameRegs(AOP(left), AOP(result)) ) { + + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(right),0)); + pic16_emitpcode(POC_INCF , pic16_popGet(AOP(result),0)); + } else { // not same + + if(AOP_TYPE(left) == AOP_ACC) { + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(right),0)); + pic16_emitpcode(POC_XORLW , pic16_popGetLit(1)); + } else { + pic16_mov2w(AOP(left),0); + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(right),0)); + pic16_emitpcode(POC_INCFW , pic16_popGet(AOP(left),0)); + } + + if(AOP_TYPE(result) != AOP_ACC) { + + if(AOP_TYPE(result) == AOP_CRY) { + pic16_emitpcode(POC_ANDLW , pic16_popGetLit(1)); + pic16_emitpcode(POC_BCF , pic16_popGet(AOP(result),0)); + emitSKPZ; + pic16_emitpcode(POC_BSF , pic16_popGet(AOP(result),0)); + } else { + pic16_emitpcode(POC_MOVWF , pic16_popGet(AOP(result),0)); + } + } + } + + } else { + int offset = 1; + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if (pic16_sameRegs(AOP(left), AOP(result)) ) { + emitCLRZ; + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(right),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),0)); + } else { + emitCLRZ; // needed here as well: INCFW is not always executed, Z is undefined then + pic16_mov2w(AOP(left),0); + pic16_emitpcode(POC_BTFSC, pic16_popGet(AOP(right),0)); + pic16_emitpcode(POC_INCFW, pic16_popGet(AOP(left),0)); + emitMOVWF(right,0); + } + + while(--size){ + emitSKPZ; + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(result),offset++)); + } + + } + + } else { + // add bytes + + // Note: the following is an example of WISC code, eg. + // it's supposed to run on a Weird Instruction Set Computer :o) + + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + if ( AOP_TYPE(left) == AOP_ACC) { + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + pic16_emitpcode(POC_ADDFW, pic16_popGet(AOP(right),0)); + if ( AOP_TYPE(result) != AOP_ACC) + pic16_emitpcode(POC_MOVWF,pic16_popGet(AOP(result),0)); + goto release; // we're done, since WREG is 1 byte + } + + + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + size = min( AOP_SIZE(result), AOP_SIZE(right) ); + size = min( size, AOP_SIZE(left) ); + offset = 0; + + if(pic16_debug_verbose) { +// fprintf(stderr, "%s:%d result: %d\tleft: %d\tright: %d\n", __FILE__, __LINE__, +// AOP_SIZE(result), AOP_SIZE(left), AOP_SIZE(right)); +// fprintf(stderr, "%s:%d size of operands: %d\n", __FILE__, __LINE__, size); + } + + + + if ((AOP_TYPE(left) == AOP_PCODE) && ( + (AOP(left)->aopu.pcop->type == PO_LITERAL) || +// (AOP(left)->aopu.pcop->type == PO_DIR) || // patch 9 + (AOP(left)->aopu.pcop->type == PO_IMMEDIATE))) + { + // add to literal operand + + // add first bytes + for(i=0; iaopu.aop_lit)); + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = (unsigned int) ulFromVal (AOP(IC_RIGHT(ic))->aopu.aop_lit)) > 2) + return FALSE ; + + /* if decrement 16 bits in register */ + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) && + (size > 1) && + (icount == 1)) { + + if(size == 2) { + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(IC_RESULT(ic)),LSB)); + emitSKPC; + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(IC_RESULT(ic)),MSB16)); + + pic16_emitcode("decf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),LSB,FALSE,FALSE)); + pic16_emitcode("incfsz","%s,w",pic16_aopGet(AOP(IC_RESULT(ic)),LSB,FALSE,FALSE)); + pic16_emitcode(" decf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),MSB16,FALSE,FALSE)); + } else { + /* size is 3 or 4 */ + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(IC_RESULT(ic)),LSB)); + pic16_emitpcode(POC_CLRF, pic16_popCopyReg(&pic16_pc_wreg)); + pic16_emitpcode(POC_SUBWFB_D1, pic16_popGet(AOP(IC_RESULT(ic)),MSB16)); + pic16_emitpcode(POC_SUBWFB_D1, pic16_popGet(AOP(IC_RESULT(ic)),MSB24)); + + pic16_emitcode("movlw","0xff"); + pic16_emitcode("addwf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),LSB,FALSE,FALSE)); + + //emitSKPNC; + pic16_emitcode("addwf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),MSB16,FALSE,FALSE)); + //emitSKPNC; + pic16_emitcode("addwf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),MSB24,FALSE,FALSE)); + + if(size > 3) { + pic16_emitpcode(POC_SUBWFB_D1, pic16_popGet(AOP(IC_RESULT(ic)),MSB32)); + + pic16_emitcode("skpnc",""); + //emitSKPNC; + pic16_emitcode("addwf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),MSB32,FALSE,FALSE)); + } + + } + + return TRUE; + + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE(IC_RESULT(ic)) > 1 || + AOP_SIZE(IC_LEFT(ic)) > 1 ) + return FALSE ; + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic)))) { + + while (icount--) + pic16_emitpcode(POC_DECF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + + //pic16_emitcode ("decf","%s,f",pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + return TRUE ; + } + + DEBUGpic16_emitcode ("; returning"," result=%s, left=%s", + pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE), + pic16_aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE)); + if(size==1) { + + pic16_emitcode("decf","%s,w",pic16_aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE)); + pic16_emitcode("movwf","%s",pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + pic16_emitpcode(POC_DECFW, pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + + return TRUE; + } + + return FALSE ; +} + +/*-----------------------------------------------------------------*/ +/* pic16_addSign - propogate sign bit to higher bytes */ +/*-----------------------------------------------------------------*/ +void pic16_addSign(operand *result, int offset, int sign) +{ + int size = (pic16_getDataSize(result) - offset); + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + + if(size > 0){ + if(sign && offset) { + + if(size == 1) { + pic16_emitpcode(POC_CLRF,pic16_popGet(AOP(result),offset)); + pic16_emitpcode(POC_BTFSC,pic16_newpCodeOpBit(pic16_aopGet(AOP(result),offset-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode(POC_SETF, pic16_popGet(AOP(result),offset)); + } else { + + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(0)); + pic16_emitpcode(POC_BTFSC, pic16_newpCodeOpBit(pic16_aopGet(AOP(result),offset-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(0xff)); + while(size--) + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(result),offset+size)); + + } + } else + while(size--) + pic16_emitpcode(POC_CLRF,pic16_popGet(AOP(result),offset++)); + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +void pic16_genMinus (iCode *ic) +{ + int size, offset = 0, same=0; + unsigned long lit = 0L; + + FENTRY; + pic16_aopOp (IC_LEFT(ic),ic,FALSE); + pic16_aopOp (IC_RIGHT(ic),ic,FALSE); + pic16_aopOp (IC_RESULT(ic),ic,TRUE); + + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY && + AOP_TYPE(IC_RIGHT(ic)) == AOP_LIT) { + operand *t = IC_RIGHT(ic); + IC_RIGHT(ic) = IC_LEFT(ic); + IC_LEFT(ic) = t; + } + + DEBUGpic16_emitcode ("; ","result %s, left %s, right %s", + pic16_AopType(AOP_TYPE(IC_RESULT(ic))), + pic16_AopType(AOP_TYPE(IC_LEFT(ic))), + pic16_AopType(AOP_TYPE(IC_RIGHT(ic)))); + + /* special cases :- */ + /* if both left & right are in bit space */ + if (AOP_TYPE(IC_LEFT(ic)) == AOP_CRY && + AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { + pic16_genPlusBits (ic); + goto release ; + } + + /* if I can do an decrement instead + of subtract then GOOD for ME */ +// if (pic16_genMinusDec (ic) == TRUE) +// goto release; + + size = pic16_getDataSize(IC_RESULT(ic)); + same = pic16_sameRegs(AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic))); + + if(AOP(IC_RIGHT(ic))->type == AOP_LIT) { + /* Add a literal to something else */ + + lit = ulFromVal (AOP(IC_RIGHT(ic))->aopu.aop_lit); + lit = - (long)lit; + + genAddLit ( ic, lit); + } else if(AOP_TYPE(IC_RIGHT(ic)) == AOP_CRY) { + // bit subtraction + + pic16_emitcode(";bitsub","right is bit: %s",pic16_aopGet(AOP(IC_RIGHT(ic)),0,FALSE,FALSE)); + pic16_emitcode(";bitsub","left is bit: %s",pic16_aopGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE)); + pic16_emitcode(";bitsub","result is bit: %s",pic16_aopGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + + /* here we are subtracting a bit from a char or int */ + if(size == 1) { + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_DECF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + } else { + + if(AOP_TYPE(IC_LEFT(ic)) == AOP_ACC) { + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_XORLW , pic16_popGetLit(1)); + }else if( (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) ) { + + lit = ulFromVal (AOP(IC_LEFT(ic))->aopu.aop_lit); + + if(AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + if (pic16_sameRegs(AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic))) ) { + if(lit & 1) { + pic16_emitpcode(POC_MOVLW , pic16_popGetLit(1)); + pic16_emitpcode(POC_XORWF , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + } + }else{ + pic16_emitpcode(POC_BCF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + if(lit & 1) + pic16_emitpcode(POC_BTFSS , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + else + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_BSF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + } + goto release; + } else { + pic16_emitpcode(POC_MOVLW , pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_MOVLW , pic16_popGetLit((lit-1) & 0xff)); + //pic16_emitpcode(POC_MOVWF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + + } + + } else { + pic16_mov2w(AOP(IC_LEFT(ic)),0); + pic16_emitpcode(POC_BTFSC , pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_DECFW , pic16_popGet(AOP(IC_LEFT(ic)),0)); + } + + if(AOP_TYPE(IC_RESULT(ic)) != AOP_ACC) { + + pic16_emitpcode(POC_MOVWF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + + } else { + pic16_emitpcode(POC_ANDLW , pic16_popGetLit(1)); +/* + pic16_emitpcode(POC_BCF , pic16_popGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + emitSKPZ; + pic16_emitpcode(POC_BSF , pic16_popGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); +*/ + } + + } + + } + } else if((AOP(IC_LEFT(ic))->type == AOP_LIT) && + (AOP_TYPE(IC_RIGHT(ic)) != AOP_ACC)) { + + lit = ulFromVal (AOP(IC_LEFT(ic))->aopu.aop_lit); + DEBUGpic16_emitcode ("; left is lit","line %d result %s, left %s, right %s",__LINE__, + pic16_AopType(AOP_TYPE(IC_RESULT(ic))), + pic16_AopType(AOP_TYPE(IC_LEFT(ic))), + pic16_AopType(AOP_TYPE(IC_RIGHT(ic)))); + + + if( (size == 1) && ((lit & 0xff) == 0) ) { + /* res = 0 - right */ + if (pic16_sameRegs(AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic))) ) { + pic16_emitpcode(POC_NEGF, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + } else { + pic16_emitpcode(POC_COMFW, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + pic16_emitpcode(POC_INCF, pic16_popGet(AOP(IC_RESULT(ic)),0)); + } + goto release; + } + + pic16_mov2w(AOP(IC_RIGHT(ic)),0); + pic16_emitpcode(POC_SUBLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_MOVWF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + + + offset = 0; + while(--size) { + lit >>= 8; + offset++; + if(same) { + // here we have x = lit - x for sizeof(x)>1 + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_SUBFWB_D1, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } else { + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit & 0xff)); + pic16_emitpcode(POC_SUBFWB_D0, pic16_popGet(AOP(IC_RIGHT(ic)),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } + } + + + } else { + + DEBUGpic16_emitcode ("; ","line %d result %s, left %s, right %s",__LINE__, + pic16_AopType(AOP_TYPE(IC_RESULT(ic))), + pic16_AopType(AOP_TYPE(IC_LEFT(ic))), + pic16_AopType(AOP_TYPE(IC_RIGHT(ic)))); + + if ((AOP_SIZE(IC_LEFT(ic)) < AOP_SIZE(IC_RESULT(ic))) + && pic16_sameRegs (AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic)))) { + // extend left in result + pic16_addSign (IC_RESULT(ic), AOP_SIZE(IC_LEFT(ic)), !SPEC_USIGN(getSpec(operandType(IC_LEFT(ic))))); + } + + if ((AOP_SIZE(IC_RIGHT(ic)) < AOP_SIZE(IC_RESULT(ic))) + && pic16_sameRegs (AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic)))) { + // extend right in result---fails if left resides in result as well... + assert ((IC_LEFT(ic) == IC_RIGHT(ic)) || !pic16_sameRegs (AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic)))); + pic16_addSign (IC_RESULT(ic), AOP_SIZE(IC_RIGHT(ic)), !SPEC_USIGN(getSpec(operandType(IC_RIGHT(ic))))); + } + + if(AOP_TYPE(IC_LEFT(ic)) == AOP_ACC) { + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + pic16_emitpcode(POC_SUBFW, pic16_popGet(AOP(IC_RIGHT(ic)),0)); + pic16_emitpcode(POC_SUBLW, pic16_popGetLit(0)); + if ( AOP_TYPE(IC_RESULT(ic)) != AOP_ACC) + pic16_emitpcode(POC_MOVWF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + } else { + + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + if(AOP_TYPE(IC_RIGHT(ic)) != AOP_ACC) + pic16_mov2w(AOP(IC_RIGHT(ic)),0); + + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) + pic16_emitpcode(POC_SUBWF, pic16_popGet(AOP(IC_LEFT(ic)),0)); + else { + if( (AOP_TYPE(IC_LEFT(ic)) == AOP_LIT) ) { + pic16_emitpcode(POC_SUBLW, pic16_popGet(AOP(IC_LEFT(ic)),0)); + } else { + pic16_emitpcode(POC_SUBFW, pic16_popGet(AOP(IC_LEFT(ic)),0)); + } + if ( AOP_TYPE(IC_RESULT(ic)) != AOP_ACC) { + if ( AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + pic16_emitpcode(POC_BCF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + emitSKPZ; + pic16_emitpcode(POC_BSF , pic16_popGet(AOP(IC_RESULT(ic)),0)); + }else + pic16_emitpcode(POC_MOVWF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + } + } + } + + /* + pic16_emitpcode(POC_MOVFW, pic16_popGet(AOP(IC_RIGHT(ic)),0,FALSE,FALSE)); + + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + pic16_emitpcode(POC_SUBFW, pic16_popGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + } else { + pic16_emitpcode(POC_SUBFW, pic16_popGet(AOP(IC_LEFT(ic)),0,FALSE,FALSE)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),0,FALSE,FALSE)); + } + */ + offset = 1; + size--; + + while (size--) { + if (pic16_sameRegs (AOP(IC_RIGHT(ic)), AOP(IC_RESULT(ic)))) { + pic16_mov2w (AOP(IC_RESULT(ic)), offset); + } else if (offset < AOP_SIZE(IC_RIGHT(ic))) + pic16_mov2w(AOP(IC_RIGHT(ic)),offset); + else { + // right operand is too short, not overwritten with result + pic16_emitpcode (POC_CLRF, pic16_popCopyReg (&pic16_pc_wreg)); + if (!SPEC_USIGN(operandType(IC_RIGHT(ic)))) { + // signed -- sign extend the right operand + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit(pic16_aopGet(AOP(IC_RIGHT(ic)),AOP_SIZE(IC_RIGHT(ic))-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_SETF, pic16_popCopyReg (&pic16_pc_wreg)); + } + } + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic)))) { + pic16_emitpcode(POC_SUBWFB_D1, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } else if (offset < AOP_SIZE(IC_LEFT(ic))) { + pic16_emitpcode(POC_SUBWFB_D0, pic16_popGet(AOP(IC_LEFT(ic)),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } else { + // left operand is too short, not overwritten with result + pic16_emitpcode (POC_CLRF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + if (!SPEC_USIGN(operandType(IC_LEFT(ic)))) { + // signed -- sign extend the left operand + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit(pic16_aopGet(AOP(IC_LEFT(ic)),AOP_SIZE(IC_LEFT(ic))-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_SETF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); // keep CARRY/#BORROW bit intact! + } + pic16_emitpcode(POC_SUBWFB_D1, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } + offset++; + } + } + + // adjustArithmeticResult(ic); + + release: + pic16_freeAsmop(IC_LEFT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + pic16_freeAsmop(IC_RIGHT(ic),NULL,ic,(RESULTONSTACK(ic) ? FALSE : TRUE)); + pic16_freeAsmop(IC_RESULT(ic),NULL,ic,TRUE); +} + + +/*-----------------------------------------------------------------* + * pic_genMult8XLit_n - multiplication of two 8-bit numbers. + * + * + *-----------------------------------------------------------------*/ +static void +pic16_genMult8XLit_n (operand *left, operand *right, operand *result) +{ + int lit; + int size = AOP_SIZE (result); + + FENTRY; + DEBUGpic16_pic16_AopType (__LINE__, left, right, result); + + if (AOP_TYPE (right) != AOP_LIT) + { + fprintf (stderr, "%s %d - right operand is not a literal\n", __FILE__, __LINE__); + exit (1); + } + + lit = (int) ulFromVal (AOP (right)->aopu.aop_lit); + assert ((lit >= -128) && (lit < 256) ); + + /* We need to adjust the high byte of the result (in PRODH) if + * (a) literals < 0 or + * (b) signed register operands < 0. + * + * If lit >= 0 and val is unsigned, MULLW will do the trick. + * + * If val is signed and 8 bit: + * val = -2^7 * v_7 + SUM(2^i * v_i, i=0..6) + * = -2^7 * v_7 + -2^7 * v_7 + SUM(2^i * v_i, i=0..7) + * = -2^8 * v_7 + SUM(2^i * v_i, i=0..7) + * = -2^8 * v_7 + UNSIGNED(val) + * + * For lit >= 0: + * lit * val = lit * UNSIGNED(val) - 2^8 * v_7 * lit + * + * For lit < 0, lit = l_7 .. l_0: + * + * lit * val = (-2^8 * l_7 + UNSIGNED(lit)) * (-2^8 * v_7 + UNSIGNED(val)) + * = 2^8 * l_7 * 2^8 * v_7 + * - 2^8 * l_7 * UNSIGNED(val) + * - 2^8 * v_7 * UNSIGNED(lit) + * + UNSIGNED(lit) * UNSIGNED(val) + * = 2^16 * l_7 * v_7 + * - 2^8 * (l_7 * UNSIGNED(val) + v_7 * UNSIGNED(lit)) + * + UNSIGNED(lit) * UNSIGNED(val) + * + * '2^16 * X' is only of interest if the result is larger than 16 * bit + * ==> ignored. + * '-2^8 * X' requires subtracting the original values of 'val' + * respectively 'lit' from the high byte of the result. + * UNSIGNED(lit) * UNSIGNED(val) can be computed via a MUL instruction. + * + * So, we do: + * MOVF val, W + * MULLW lit + * # Now, PRODH:PRODL hold UNSIGNED(val) * UNSIGNED(lit) + * if result > 8 bit: + * if lit < 0: + * SUBWF PRODH, F # -2^8 * l_7 * UNSIGNED(val) + * MOVF PRODH, W + * BTFSC val, 7 + * SUBLW lit # -2^8 * v_7 * UNSIGNED(lit) + * MOVWF result[1] # this might overwrite val! + * MOVFF PRODL, result[0] # this might overwrite val! + */ + + pic16_mov2w (AOP (left), 0); + pic16_emitpcode (POC_MULLW, pic16_popGetLit (lit & 0x00ff)); + + if (size > 1) + { + /* Adjust PRODH for signed operands. */ + if (lit < 0) + { + /* left is still in WREG */ + pic16_emitpcode (POC_SUBWF, pic16_popCopyReg (&pic16_pc_prodh)); + } // if + + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_prodh)); + if (!SPEC_USIGN (operandType (left))) + { + pic16_emitpcode (POC_BTFSC, + pic16_newpCodeOpBit (pic16_aopGet (AOP (left), 0, FALSE, FALSE), + 7, 0, PO_GPR_REGISTER)); + pic16_emitpcode (POC_SUBLW, pic16_popGetLit (lit & 0x00ff)); + } // if + + /* Assign result (high byte) -- this may overwrite val. */ + pic16_emitpcode (POC_MOVWF, pic16_popGet (AOP (result), 1)); + } // if + + /* Assign result (low byte) -- this may overwrite val. */ + pic16_emitpcode (POC_MOVFF, + pic16_popGet2p (pic16_popCopyReg (&pic16_pc_prodl), + pic16_popGet (AOP (result), 0))); + + /* Must sign/zero-extend here if size > 2. */ + if (size > 2) + { + pic16_addSign (result, 2, !IS_UNSIGNED (operandType (left))); + } // if +} + +/*-----------------------------------------------------------------* + * genMult8X8_n - multiplication of two 8-bit numbers. + * + * + *-----------------------------------------------------------------*/ +void +pic16_genMult8X8_n (operand *left, operand *right, operand *result) +{ + FENTRY; + + /* Special case: multiply by literal. */ + if (AOP_TYPE (right) == AOP_LIT) + { + pic16_genMult8XLit_n (left, right, result); + return; + } + else if (AOP_TYPE (left) == AOP_LIT) + { + pic16_genMult8XLit_n (right, left, result); + return; + } // if + + /* cases: + A = A x B B = A x B + A = B x C + W = A x B + W = W x B W = B x W + */ + /* if result == right then exchange left and right */ + if (pic16_sameRegs (AOP (result), AOP (right))) + { + operand *tmp; + tmp = left; + left = right; + right = tmp; + } // if + + if (AOP_TYPE (left) != AOP_ACC) + { + // left is not WREG + if (AOP_TYPE (right) != AOP_ACC) + { + pic16_mov2w (AOP (left), 0); + pic16_emitpcode (POC_MULWF, pic16_popGet (AOP (right), 0)); + } + else + { + pic16_emitpcode (POC_MULWF, pic16_popGet (AOP (left), 0)); + } // if + } + else + { + pic16_emitpcode (POC_MULWF, pic16_popGet (AOP (right), 0)); + } // if + + /* result is in PRODL:PRODH */ + if (AOP_TYPE (result) != AOP_ACC) + { + int isPRODL = (AOP (result)->type != AOP_STA && ! strcmp (pic16_aopGet (AOP (result), 0, TRUE, FALSE), "_PRODL")); + + if (AOP_SIZE (result) > 1) + { + /* If s8 x s8 --> s16 multiplication was called for, fixup high byte. + * (left=a1a0, right=b1b0, X1: high byte, X0: low byte) + * + * a1a0 * b1b0 + * -------------- + * a1b0 a0b0 + * a1b1 a0b1 + * --------------- + * a0b0 a1= 0, b1= 0 (both unsigned) + * -b0 a0b0 a1=-1, b1= 0 (a signed and < 0, b unsigned or >= 0) + * -a0 a0b0 a1= 0, b1=-1 (b signed and < 0, a unsigned or >= 0) + * -(a0+b0) a0b0 a1=-1, b1=-1 (a and b signed and < 0) + * + * Currently, PRODH:PRODL holds a0b0 as 16 bit value; we need to + * subtract a0 and/or b0 from PRODH. */ + if (!IS_UNSIGNED (operandType (right))) + { + /* right operand (b1) signed and < 0, then subtract left op (a0) */ + pic16_mov2w (AOP (left), 0); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (right), 0, 7)); + pic16_emitpcode (POC_SUBWF, pic16_popCopyReg (&pic16_pc_prodh)); + } // if + + if (!IS_UNSIGNED (getSpec (operandType (left)))) + { + /* left operand (a1) signed and < 0, then subtract right op (b0) */ + pic16_mov2w (AOP (right), 0 ); + pic16_emitpcode (POC_BTFSC, pic16_newpCodeOpBit_simple (AOP (left), 0, 7)); + pic16_emitpcode (POC_SUBWF, pic16_popCopyReg (&pic16_pc_prodh)); + } // if + + /* Assing high byte of result -- this may overwrite one of the operands. */ + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg(&pic16_pc_prodh), pic16_popGet (AOP (result), 1))); + } // if + + /* Assign low byte of result -- this may overwrite one of the operands. */ + if (!isPRODL) + { + pic16_emitpcode (POC_MOVFF, pic16_popGet2p (pic16_popCopyReg (&pic16_pc_prodl), pic16_popGet (AOP (result), 0))); + } // if + + /* Must sign/zero-extend here if size > 2. */ + if (AOP_SIZE (result) > 2) + { + pic16_addSign (result, 2, !IS_UNSIGNED (operandType (left))); + } // if + } + else + { + pic16_emitpcode (POC_MOVFW, pic16_popCopyReg (&pic16_pc_prodl)); + } +} diff --git a/src/pic16/genutils.c b/src/pic16/genutils.c new file mode 100644 index 0000000..6f3d83b --- /dev/null +++ b/src/pic16/genutils.c @@ -0,0 +1,622 @@ +/*------------------------------------------------------------------------- + genutils.c - source file for code generation for pic16 + code generation utility functions + + Created by Vangelis Rokas (vrokas@otenet.gr) [Nov-2003] + + Based on : + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + and - Jean-Louis VERN.jlvern@writeme.com (1999) + Bug Fixes - Wojciech Stryjewski wstryj1@tiger.lsu.edu (1999 v2.1.9a) + PIC port - Scott Dattalo scott@dattalo.com (2000) + PIC16 port - Martin Dubuc m.dubuc@rogers.com (2002) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! + + Notes: + 000123 mlh Moved aopLiteral to SDCCglue.c to help the split + Made everything static +-------------------------------------------------------------------------*/ + +/********************************************************** + * Here is a list with completed genXXXXX functions + * + * genNot + * + */ + + +#include +#include +#include +#include +#include "SDCCglobl.h" +#include "newalloc.h" + +#include "common.h" +#include "SDCCpeeph.h" +#include "ralloc.h" +#include "pcode.h" +#include "device.h" +#include "gen.h" + +#include "genutils.h" + +#if 1 +#define pic16_emitcode DEBUGpic16_emitcode +#endif + +#if defined(GEN_Not) +/*-----------------------------------------------------------------*/ +/* pic16_genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +void pic16_genNot (iCode *ic) +{ +/* + * result[AOP_CRY,AOP_REG] = ! left[AOP_CRY, AOP_REG] + */ + + FENTRY; + + /* assign asmOps to operand & result */ + pic16_aopOp (IC_LEFT(ic),ic,FALSE); + pic16_aopOp (IC_RESULT(ic),ic,TRUE); + DEBUGpic16_pic16_AopType(__LINE__,IC_LEFT(ic),NULL,IC_RESULT(ic)); + + /* if in bit space then a special case */ + if (AOP_TYPE(IC_LEFT(ic)) == AOP_CRY) { + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY) { + pic16_emitpcode(POC_MOVLW,pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_XORWF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + } else { + pic16_emitpcode(POC_CLRF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + pic16_emitpcode(POC_BTFSS,pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_INCF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + } + goto release; + } + +#if 0 + if(AOP_SIZE(IC_LEFT(ic)) == 1) { + pic16_emitpcode(POC_COMFW,pic16_popGet(AOP(IC_LEFT(ic)),0)); + pic16_emitpcode(POC_ANDLW,pic16_popGetLit(1)); + pic16_emitpcode(POC_MOVWF,pic16_popGet(AOP(IC_RESULT(ic)),0)); + goto release; + } +#endif + + pic16_toBoolean( IC_LEFT(ic) ); + emitSETC; + pic16_emitpcode(POC_TSTFSZ, pic16_popCopyReg( &pic16_pc_wreg )); + emitCLRC; + pic16_outBitC( IC_RESULT(ic) ); + +release: + /* release the aops */ + pic16_freeAsmop(IC_LEFT(ic),NULL,ic,(RESULTONSTACK(ic) ? 0 : 1)); + pic16_freeAsmop(IC_RESULT(ic),NULL,ic,TRUE); +} + +#endif /* defined(GEN_Not) */ + + + +#if defined(GEN_Cpl) +/*-----------------------------------------------------------------*/ +/* pic16_genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +void pic16_genCpl (iCode *ic) +{ + int offset = 0; + int size ; + +/* + * result[CRY,REG] = ~left[CRY,REG] + */ + FENTRY; + + DEBUGpic16_emitcode ("; ***","%s %d",__FUNCTION__,__LINE__); + /* assign asmOps to operand & result */ + pic16_aopOp (IC_LEFT(ic),ic,FALSE); + pic16_aopOp (IC_RESULT(ic),ic,TRUE); + DEBUGpic16_pic16_AopType(__LINE__,IC_LEFT(ic),NULL,IC_RESULT(ic)); + + /* if both are in bit space then + * a special case */ + if (AOP_TYPE(IC_RESULT(ic)) == AOP_CRY + && AOP_TYPE(IC_LEFT(ic)) == AOP_CRY ) { + + /* FIXME */ + pic16_emitcode("mov","c,%s",IC_LEFT(ic)->aop->aopu.aop_dir); + pic16_emitcode("cpl","c"); + pic16_emitcode("mov","%s,c",IC_RESULT(ic)->aop->aopu.aop_dir); + goto release; + } + + size = AOP_SIZE(IC_RESULT(ic)); + if (size >= AOP_SIZE(IC_LEFT(ic))) size = AOP_SIZE(IC_LEFT(ic)); + + while (size--) { + if (pic16_sameRegs(AOP(IC_LEFT(ic)), AOP(IC_RESULT(ic))) ) { + pic16_emitpcode(POC_COMF, pic16_popGet(AOP(IC_LEFT(ic)), offset)); + } else { + pic16_emitpcode(POC_COMFW, pic16_popGet(AOP(IC_LEFT(ic)),offset)); + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)),offset)); + } + offset++; + } + + /* handle implicit upcast */ + size = AOP_SIZE(IC_RESULT(ic)); + if (offset < size) + { + if (SPEC_USIGN(operandType(IC_LEFT(ic)))) { + while (offset < size) { + pic16_emitpcode(POC_SETF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + offset++; + } // while + } else { + if ((offset + 1) == size) { + /* just one byte to fix */ + pic16_emitpcode(POC_SETF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + pic16_emitpcode(POC_BTFSC, pic16_newpCodeOpBit(pic16_aopGet(AOP(IC_RESULT(ic)),offset-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode(POC_CLRF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + } else { + /* two or more byte to adjust */ + pic16_emitpcode(POC_SETF, pic16_popCopyReg( &pic16_pc_wreg )); + pic16_emitpcode(POC_BTFSC, pic16_newpCodeOpBit(pic16_aopGet(AOP(IC_RESULT(ic)),offset-1,FALSE,FALSE),7,0, PO_GPR_REGISTER)); + pic16_emitpcode(POC_CLRF, pic16_popCopyReg( &pic16_pc_wreg )); + while (offset < size) { + pic16_emitpcode(POC_MOVWF, pic16_popGet(AOP(IC_RESULT(ic)), offset)); + offset++; + } // while + } // if + } + } // if + +release: + /* release the aops */ + pic16_freeAsmop(IC_LEFT(ic),NULL,ic,(RESULTONSTACK(ic) ? 0 : 1)); + pic16_freeAsmop(IC_RESULT(ic),NULL,ic,TRUE); +} +#endif /* defined(GEN_Cpl) */ + + + +/*-----------------------------------------------------------------*/ +/* Helper function to dump operand into comment lines */ +/*-----------------------------------------------------------------*/ + +void pic16_DumpValue(char *prefix, value *val) +{ +// char s[INITIAL_INLINEASM]; + if(!val) return; + + DEBUGpic16_emitcode (";", " %s Dump value",prefix); + DEBUGpic16_emitcode (";", " %s name:%s",prefix,val->name); +} + +void pic16_DumpPcodeOp(char *prefix, pCodeOp *pcop) +{ +// char s[INITIAL_INLINEASM]; + if(!pcop) return; + + DEBUGpic16_emitcode (";", " %s Dump pCodeOp",prefix); + DEBUGpic16_emitcode (";", " %s name:%s",prefix,pcop->name); + if(pcop->type == PO_NONE) { + DEBUGpic16_emitcode (";", " %s type:PO_NONE",prefix); + } + if(pcop->type == PO_W) { + DEBUGpic16_emitcode (";", " %s type:PO_W",prefix); + } + if(pcop->type == PO_WREG) { + DEBUGpic16_emitcode (";", " %s type:PO_WREG",prefix); + } + if(pcop->type == PO_STATUS) { + DEBUGpic16_emitcode (";", " %s type:PO_STATUS",prefix); + } + if(pcop->type == PO_BSR) { + DEBUGpic16_emitcode (";", " %s type:PO_BSR",prefix); + } + if(pcop->type == PO_FSR0) { + DEBUGpic16_emitcode (";", " %s type:PO_FSR0",prefix); + } + if(pcop->type == PO_INDF0) { + DEBUGpic16_emitcode (";", " %s type:PO_INDF0",prefix); + } + if(pcop->type == PO_INTCON) { + DEBUGpic16_emitcode (";", " %s type:PO_INTCON",prefix); + } + if(pcop->type == PO_GPR_REGISTER) { + DEBUGpic16_emitcode (";", " %s type:PO_GPR_REGISTER",prefix); + } + if(pcop->type == PO_GPR_BIT) { + DEBUGpic16_emitcode (";", " %s type:PO_GPR_BIT",prefix); + } + if(pcop->type == PO_GPR_TEMP) { + DEBUGpic16_emitcode (";", " %s type:PO_GPR_TEMP",prefix); + } + if(pcop->type == PO_SFR_REGISTER) { + DEBUGpic16_emitcode (";", " %s type:PO_SFR_REGISTER",prefix); + } + if(pcop->type == PO_PCL) { + DEBUGpic16_emitcode (";", " %s type:PO_PCL",prefix); + } + if(pcop->type == PO_PCLATH) { + DEBUGpic16_emitcode (";", " %s type:PO_PCLATH",prefix); + } + if(pcop->type == PO_LITERAL) { + DEBUGpic16_emitcode (";", " %s type:PO_LITERAL",prefix); + DEBUGpic16_emitcode (";", " %s lit:%s",prefix,PCOL(pcop)->lit); + } + if(pcop->type == PO_REL_ADDR) { + DEBUGpic16_emitcode (";", " %s type:PO_REL_ADDR",prefix); + } + if(pcop->type == PO_IMMEDIATE) { + DEBUGpic16_emitcode (";", " %s type:PO_IMMEDIATE",prefix); + } + if(pcop->type == PO_DIR) { + DEBUGpic16_emitcode (";", " %s type:PO_DIR",prefix); + } + if(pcop->type == PO_CRY) { + DEBUGpic16_emitcode (";", " %s type:PO_CRY",prefix); + } + if(pcop->type == PO_BIT) { + DEBUGpic16_emitcode (";", " %s type:PO_BIT",prefix); + } + if(pcop->type == PO_STR) { + DEBUGpic16_emitcode (";", " %s type:PO_STR",prefix); + } + if(pcop->type == PO_LABEL) { + DEBUGpic16_emitcode (";", " %s type:PO_LABEL",prefix); + } + if(pcop->type == PO_WILD) { + DEBUGpic16_emitcode (";", " %s type:PO_WILD",prefix); + } +} + + + +void pic16_DumpAop(char *prefix, asmop *aop) +{ + char s[INITIAL_INLINEASM]; + if(!aop) return; + + DEBUGpic16_emitcode (";", " %s Dump asmop",prefix); + if (aop->type == AOP_LIT) + { + DEBUGpic16_emitcode (";", " %s type:AOP_LIT",prefix); + SNPRINTF(s, sizeof(s), "%s (aopu.aop_lit)",prefix); + pic16_DumpValue(s,aop->aopu.aop_lit); + } + if (aop->type == AOP_REG) + DEBUGpic16_emitcode (";", " %s type:AOP_REG",prefix); + if (aop->type == AOP_DIR) + { + DEBUGpic16_emitcode (";", " %s type:AOP_DIR",prefix); + DEBUGpic16_emitcode (";", " %s aopu.aop_dir:%s",prefix,aop->aopu.aop_dir); + } + if (aop->type == AOP_STK) + DEBUGpic16_emitcode (";", " %s type:AOP_STK",prefix); + if (aop->type == AOP_STA) + DEBUGpic16_emitcode (";", " %s type:AOP_STA",prefix); + if (aop->type == AOP_STR) + { + DEBUGpic16_emitcode (";", " %s type:AOP_STR",prefix); + DEBUGpic16_emitcode (";", " %s aopu.aop_str:%s/%s/%s/%s",prefix,aop->aopu.aop_str[0], + aop->aopu.aop_str[1],aop->aopu.aop_str[2],aop->aopu.aop_str[3]); + } + if (aop->type == AOP_CRY) + DEBUGpic16_emitcode (";", " %s type:AOP_CRY",prefix); + if (aop->type == AOP_ACC) + DEBUGpic16_emitcode (";", " %s type:AOP_ACC",prefix); + if (aop->type == AOP_PCODE) + { + DEBUGpic16_emitcode (";", " %s type:AOP_PCODE",prefix); + SNPRINTF(s, sizeof(s), "%s (aopu.pcop)",prefix); + pic16_DumpPcodeOp(s,aop->aopu.pcop); + } + + + DEBUGpic16_emitcode (";", " %s coff:%d",prefix,aop->coff); + DEBUGpic16_emitcode (";", " %s size:%d",prefix,aop->size); + DEBUGpic16_emitcode (";", " %s code:%d",prefix,aop->code); + DEBUGpic16_emitcode (";", " %s paged:%d",prefix,aop->paged); + DEBUGpic16_emitcode (";", " %s freed:%d",prefix,aop->freed); + +} + +void pic16_DumpSymbol(char *prefix, symbol *sym) +{ + char s[INITIAL_INLINEASM]; + if(!sym) return; + + DEBUGpic16_emitcode (";", " %s Dump symbol",prefix); + DEBUGpic16_emitcode (";", " %s name:%s",prefix,sym->name); + DEBUGpic16_emitcode (";", " %s rname:%s",prefix,sym->rname); + DEBUGpic16_emitcode (";", " %s level:%d",prefix,sym->level); + DEBUGpic16_emitcode (";", " %s block:%d",prefix,sym->block); + DEBUGpic16_emitcode (";", " %s key:%d",prefix,sym->key); + DEBUGpic16_emitcode (";", " %s implicit:%d",prefix,sym->implicit); + DEBUGpic16_emitcode (";", " %s undefined:%d",prefix,sym->undefined); + DEBUGpic16_emitcode (";", " %s _isparm:%d",prefix,sym->_isparm); + DEBUGpic16_emitcode (";", " %s ismyparm:%d",prefix,sym->ismyparm); + DEBUGpic16_emitcode (";", " %s isitmp:%d",prefix,sym->isitmp); + DEBUGpic16_emitcode (";", " %s islbl:%d",prefix,sym->islbl); + DEBUGpic16_emitcode (";", " %s isref:%d",prefix,sym->isref); + DEBUGpic16_emitcode (";", " %s isind:%d",prefix,sym->isind); + DEBUGpic16_emitcode (";", " %s isinvariant:%d",prefix,sym->isinvariant); + DEBUGpic16_emitcode (";", " %s cdef:%d",prefix,sym->cdef); + DEBUGpic16_emitcode (";", " %s addrtaken:%d",prefix,sym->addrtaken); + DEBUGpic16_emitcode (";", " %s isreqv:%d",prefix,sym->isreqv); + DEBUGpic16_emitcode (";", " %s udChked:%d",prefix,sym->udChked); + DEBUGpic16_emitcode (";", " %s isLiveFcall:%d",prefix,sym->isLiveFcall); + DEBUGpic16_emitcode (";", " %s isspilt:%d",prefix,sym->isspilt); + DEBUGpic16_emitcode (";", " %s spillA:%d",prefix,sym->spillA); + DEBUGpic16_emitcode (";", " %s remat:%d",prefix,sym->remat); + DEBUGpic16_emitcode (";", " %s isptr:%d",prefix,sym->isptr); + DEBUGpic16_emitcode (";", " %s uptr:%d",prefix,sym->uptr); + DEBUGpic16_emitcode (";", " %s isFree:%d",prefix,sym->isFree); + DEBUGpic16_emitcode (";", " %s islocal:%d",prefix,sym->islocal); + DEBUGpic16_emitcode (";", " %s blockSpil:%d",prefix,sym->blockSpil); + DEBUGpic16_emitcode (";", " %s remainSpil:%d",prefix,sym->remainSpil); + DEBUGpic16_emitcode (";", " %s stackSpil:%d",prefix,sym->stackSpil); + DEBUGpic16_emitcode (";", " %s onStack:%d",prefix,sym->onStack); + DEBUGpic16_emitcode (";", " %s iaccess:%d",prefix,sym->iaccess); + DEBUGpic16_emitcode (";", " %s ruonly:%d",prefix,sym->ruonly); + DEBUGpic16_emitcode (";", " %s spildir:%d",prefix,sym->spildir); + DEBUGpic16_emitcode (";", " %s ptrreg:%d",prefix,sym->ptrreg); + DEBUGpic16_emitcode (";", " %s noSpilLoc:%d",prefix,sym->noSpilLoc); + DEBUGpic16_emitcode (";", " %s isstrlit:%d",prefix,sym->isstrlit); + DEBUGpic16_emitcode (";", " %s accuse:%d",prefix,sym->accuse); + DEBUGpic16_emitcode (";", " %s dptr:%d",prefix,sym->dptr); + DEBUGpic16_emitcode (";", " %s allocreq:%d",prefix,sym->allocreq); + DEBUGpic16_emitcode (";", " %s stack:%d",prefix,sym->stack); + DEBUGpic16_emitcode (";", " %s xstack:%d",prefix,sym->xstack); + DEBUGpic16_emitcode (";", " %s nRegs:%d",prefix,sym->nRegs); + DEBUGpic16_emitcode (";", " %s regType:%d",prefix,sym->regType); + + // struct regs !!! + + if(sym->aop) + { + SNPRINTF(s, sizeof(s), "%s (aop)",prefix); + pic16_DumpAop(s,sym->aop); + } else { + DEBUGpic16_emitcode (";", " %s aop:NULL",prefix); + } +} + +void pic16_DumpOp(char *prefix, operand *op) +{ + char s[INITIAL_INLINEASM]; + if(!op) return; + + DEBUGpic16_emitcode (";", " %s Dump operand",prefix); + if(IS_SYMOP(op)) + DEBUGpic16_emitcode (";", " %s type: SYMBOL",prefix); + if(IS_VALOP(op)) + DEBUGpic16_emitcode (";", " %s type: VALUE",prefix); + if(IS_TYPOP(op)) + DEBUGpic16_emitcode (";", " %s type: TYPE",prefix); + DEBUGpic16_emitcode (";", " %s isaddr:%d",prefix,op->isaddr); + DEBUGpic16_emitcode (";", " %s isvolatile:%d",prefix,op->isvolatile); + DEBUGpic16_emitcode (";" ," %s isGlobal:%d",prefix,op->isGlobal); + DEBUGpic16_emitcode (";", " %s isPtr:%d",prefix,op->isPtr); + DEBUGpic16_emitcode (";", " %s isGptr:%d",prefix,op->isGptr); + DEBUGpic16_emitcode (";", " %s isParm:%d",prefix,op->isParm); + DEBUGpic16_emitcode (";", " %s isLiteral:%d",prefix,op->isLiteral); + DEBUGpic16_emitcode (";", " %s key:%d",prefix,op->key); + if(IS_SYMOP(op)) { + SNPRINTF(s, sizeof(s), "%s (symOperand)",prefix); + pic16_DumpSymbol(s, OP_SYMBOL (op)); + } +} + +void pic16_DumpOpX(FILE *fp, char *prefix, operand *op) +{ + if(!op)return; + + fprintf(fp, "%s [", prefix); + fprintf(fp, "%s", IS_SYMOP(op)?"S":" "); + fprintf(fp, "%s", IS_VALOP(op)?"V":" "); + fprintf(fp, "%s", IS_TYPOP(op)?"T":" "); + fprintf(fp, "] "); + + fprintf(fp, "isaddr:%d,", op->isaddr); + fprintf(fp, "isvolatile:%d,", op->isvolatile); + fprintf(fp, "isGlobal:%d,", op->isGlobal); + fprintf(fp, "isPtr:%d,", op->isPtr); + fprintf(fp, "isParm:%d,", op->isParm); + fprintf(fp, "isLit:%d\n", op->isLiteral); +} + + +void _debugf(char *f, int l, char *frm, ...) +{ + va_list ap; + + va_start(ap, frm); + fprintf(stderr, "%s:%d ", f, l); + vfprintf(stderr, frm, ap); + va_end(ap); +} + + + +void gpsimio2_pcop(pCodeOp *pcop) +{ + pic16_emitpcode(POC_MOVFF, pic16_popGet2p(pcop, pic16_popCopyReg(&pic16_pc_gpsimio2))); +} + +void gpsimio2_lit(unsigned char lit) +{ + pic16_emitpcode(POC_MOVLW, pic16_popGetLit(lit)); + pic16_emitpcode(POC_MOVFF, pic16_popGet2p(pic16_popCopyReg(&pic16_pc_wreg), pic16_popCopyReg(&pic16_pc_gpsimio2))); +} + +void gpsimio2_str(char *buf) +{ + while(*buf) { + gpsimio2_lit(*buf); + buf++; + } +} + +void gpsimDebug_StackDump(char *fname, int line, char *info) +{ + pic16_emitpcomment("; gpsim debug stack dump; %s @ %d\tinfo: ", fname, line, info); + + gpsimio2_str("&c[S:"); + gpsimio2_str(info); + gpsimio2_str("] &h"); + + pic16_emitpcode(POC_MOVFF, pic16_popGet2p(pic16_popCopyReg(&pic16_pc_fsr1h), + pic16_popCopyReg(&pic16_pc_gpsimio2))); + pic16_emitpcode(POC_MOVFF, pic16_popGet2p(pic16_popCopyReg(&pic16_pc_fsr1l), + pic16_popCopyReg(&pic16_pc_gpsimio2))); + + gpsimio2_lit('\n'); +} + +const char *gptr_fns[4][2] = { + { "_gptrget1", "_gptrput1" }, + { "_gptrget2", "_gptrput2" }, + { "_gptrget3", "_gptrput3" }, + { "_gptrget4", "_gptrput4" } }; + +extern set *externs; + +/* generate a call to the generic pointer read/write functions */ +void pic16_callGenericPointerRW(int rw, int size) +{ + char buf[32]; + symbol *sym; + + if(size>4) { + werror(W_POSSBUG2, __FILE__, __LINE__); + abort(); + } + + strcpy(buf, port->fun_prefix); + strcat(buf, gptr_fns[size-1][rw]); + + pic16_emitpcode (POC_CALL, pic16_popGetWithString (buf)); + + sym = newSymbol( buf, 0 ); + sym->used++; + strcpy(sym->rname, buf); + checkAddSym(&externs, sym); +} + + + +/* check all condition and return appropriate instruction, POC_CPFSGT or POC_CPFFSLT */ +static int selectCompareOp(resolvedIfx *rIfx, iCode *ifx, + operand *result, int offset, int invert_op) +{ + /* add code here */ + + /* check condition, > or < ?? */ + if(rIfx->condition != 0)invert_op ^= 1; + + if(ifx && IC_FALSE(ifx))invert_op ^= 1; + + if(!ifx)invert_op ^= 1; + + DEBUGpic16_emitcode("; +++", "%s:%d %s] rIfx->condition= %d, ifx&&IC_FALSE(ifx)= %d, invert_op = %d", + __FILE__, __LINE__, __FUNCTION__, rIfx->condition, (ifx && IC_FALSE(ifx)), invert_op); + + /* do selection */ + if(!invert_op)return POC_CPFSGT; + else return POC_CPFSLT; +} + +/* return 1 if function handles compare, 0 otherwise */ +/* this functions handles special cases like: + * reg vs. zero + * reg vs. one + */ +int pic16_genCmp_special(operand *left, operand *right, operand *result, + iCode *ifx, resolvedIfx *rIfx, int sign) +{ + int size; + int offs=0; + symbol *tmplbl; + unsigned long lit; + int op, cmp_op=0, cond_pre; + + FENTRY; + + if(!(pic16_options.opt_flags & OF_OPTIMIZE_CMP))return 0; + + size = max(AOP_SIZE(left), AOP_SIZE(right)); + + cond_pre = rIfx->condition; // must restore old value on return with 0!!! + + if(!isAOP_REGlike(left)) { + operand *dummy; + + dummy = left; + left = right; + right = dummy; + + /* invert comparing operand */ +// cmp_op ^= 1; + rIfx->condition ^= 1; + } + + + if(isAOP_REGlike(left) && isAOP_LIT(right)) { + /* comparing register vs. literal */ + lit = ulFromVal(AOP(right)->aopu.aop_lit); + + + if(size == 1) { + op = selectCompareOp(rIfx, ifx, result, offs, cmp_op); + + DEBUGpic16_emitcode("%%", "comparing operand %s, condition: %d", (op==POC_CPFSLT?"POC_CPFSLT":"POC_CPFSGT"), rIfx->condition); + + if(!sign) { + /* unsigned compare */ + switch( lit ) { + case 0: + if(ifx && IC_FALSE(ifx)) { + tmplbl = newiTempLabel( NULL ); + pic16_emitpcode(POC_TSTFSZ, pic16_popGet(AOP(left), 0)); + pic16_emitpcode(POC_BRA, pic16_popGetLabel(tmplbl->key)); + pic16_emitpcode(POC_GOTO, pic16_popGetLabel(rIfx->lbl->key)); + pic16_emitpLabel(tmplbl->key); + + ifx->generated = 1; + return 1; + } + break; + } /* switch */ + + } /* if(!sign) */ + + } /* if(size==1) */ + + } /* */ + + rIfx->condition = cond_pre; + return 0; +} diff --git a/src/pic16/genutils.h b/src/pic16/genutils.h new file mode 100644 index 0000000..d8d1966 --- /dev/null +++ b/src/pic16/genutils.h @@ -0,0 +1,75 @@ + +/* +** $Id: genutils.h 4051 2006-03-06 08:27:52Z vrokas $ +*/ + +#ifndef __GENUTILS_H__ +#define __GENUTILS_H__ + + +#include "common.h" + + +#if !defined(__BORLANDC__) && !defined(_MSC_VER) +#define DEBUGpc(fmt,...) DEBUGpic16_emitcode("; =:=", "%s:%s:%d: " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#else +#define DEBUGpc 1 ? (void)0 : printf +#endif +#define isAOP_LIT(x) (AOP_TYPE(x) == AOP_LIT) +#define isAOP_REGlike(x) (AOP_TYPE(x) == AOP_REG || AOP_TYPE(x) == AOP_DIR || AOP_TYPE(x) == AOP_PCODE || AOP_TYPE(x) == AOP_STA) + + +/* Resolved ifx structure. This structure stores information + * about an iCode ifx that makes it easier to generate code. + */ +typedef struct resolvedIfx { + symbol *lbl; /* pointer to a label */ + int condition; /* true or false ifx */ + int generated; /* set true when the code associated with the ifx + * is generated */ +} resolvedIfx; + + +/* + * The various GEN_xxxxx macros handle which functions + * should be included in the gen.c source. We are going to use + * our own functions here so, they must be commented out from + * gen.c + */ + +#define GEN_Not +void pic16_genNot(iCode *ic); + +#define GEN_Cpl +void pic16_genCpl(iCode *ic); + + +/* + * global function definitions + */ +void pic16_DumpValue(char *prefix, value *val); +void pic16_DumpPcodeOp(char *prefix, pCodeOp *pcop); +void pic16_DumpAop(char *prefix, asmop *aop); +void pic16_DumpSymbol(char *prefix, symbol *sym); +void pic16_DumpOp(char *prefix, operand *op); +void pic16_DumpOpX(FILE *fp, char *prefix, operand *op); + +pCodeOp *pic16_popGetWithString(char *str); +void pic16_callGenericPointerRW(int rw, int size); + + + +void gpsimio2_pcop(pCodeOp *pcop); +void gpsimio2_lit(unsigned char lit); + +void gpsimDebug_StackDump(char *fname, int line, char *info); + +int pic16_genCmp_special(operand *left, operand *right, operand *result, + iCode *ifx, resolvedIfx *rIfx, int sign); + +#ifndef debugf +#define debugf(frm, rest) _debugf(__FILE__, __LINE__, frm, rest) +#endif +void _debugf(char *f, int l, char *frm, ...); + +#endif /* __GENUTILS_H__ */ diff --git a/src/pic16/glue.c b/src/pic16/glue.c new file mode 100644 index 0000000..9be151d --- /dev/null +++ b/src/pic16/glue.c @@ -0,0 +1,1980 @@ +/*------------------------------------------------------------------------- + glue.c - glues everything we have done together into one file. + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "../common.h" +#include +#include "ralloc.h" +#include "pcode.h" +#include "newalloc.h" +#include "gen.h" +#include "device.h" +#include "main.h" +#include "dbuf_string.h" +#include + + +extern symbol *interrupts[256]; +void pic16_printIval (symbol * sym, sym_link * type, initList * ilist, char ptype, void *p); +extern int noAlloc; +extern set *publics; +extern set *externs; +extern unsigned maxInterrupts; +extern symbol *mainf; +extern char *VersionString; +extern struct dbuf_s *codeOutBuf; +extern char *iComments1; +extern char *iComments2; +extern int has_xinst_config; + +extern int initsfpnt; +extern unsigned long pFile_isize; + +extern unsigned long pic16_countInstructions(); +set *pic16_localFunctions = NULL; +set *rel_idataSymSet = NULL; +set *fix_idataSymSet = NULL; + +extern void pic16_AnalyzeBanking (void); +extern void pic16_OptimizeJumps (void); +extern void pic16_OptimizeBanksel (void); +extern void pic16_InlinepCode (void); +extern void pic16_writeUsedRegs (FILE *); + +extern void initialComments (FILE * afile); +extern void printPublics (FILE * afile); + +void pic16_pCodeInitRegisters (void); +pCodeOp *pic16_popCopyReg (pCodeOpReg *pc); +extern void pic16_pCodeConstString (char *name, const char *value, unsigned length); + + +/*-----------------------------------------------------------------*/ +/* aopLiteral - string from a literal value */ +/*-----------------------------------------------------------------*/ +unsigned int pic16aopLiteral (value *val, int offset) +{ + union { + float f; + unsigned char c[4]; + } fl; + + /* if it is a float then it gets tricky */ + /* otherwise it is fairly simple */ + if (!(IS_FLOAT(val->type) || IS_FIXED(val->type))) { + unsigned long v = ulFromVal (val); + + return ( (v >> (offset * 8)) & 0xff); + } + + if(IS_FIXED16X16(val->type)) { + unsigned long v = (unsigned long)fixed16x16FromDouble( floatFromVal( val ) ); + + return ( (v >> (offset * 8)) & 0xff); + } + + /* it is type float */ + fl.f = (float) floatFromVal(val); +#ifdef WORDS_BIGENDIAN + return fl.c[3-offset]; +#else + return fl.c[offset]; +#endif + +} + +iCode *tic; +symbol *nsym; +char tbuffer[512], *tbuf=tbuffer; + + +/*-----------------------------------------------------------------*/ +/* emitRegularMap - emit code for maps with no special cases */ +/*-----------------------------------------------------------------*/ +static void +pic16emitRegularMap (memmap * map, bool addPublics, bool arFlag) +{ + symbol *sym; +// int i, size, bitvars = 0;; + +// fprintf(stderr, "%s:%d map name= %s\n", __FUNCTION__, __LINE__, map->sname); + + if(addPublics) + dbuf_printf (&map->oBuf, ";\t.area\t%s\n", map->sname); + /* print the area name */ + + for (sym = setFirstItem (map->syms); sym; sym = setNextItem (map->syms)) { + +#if 0 + fprintf(stderr, "%s\t%s: sym: %s\tused: %d\textern: %d\tstatic: %d\taggregate: %d\tregister: 0x%x\tfunction: %d\n", + __FUNCTION__, + map->sname, sym->name, sym->used, IS_EXTERN(sym->etype), IS_STATIC(sym->etype), + IS_AGGREGATE(sym->type), (SPEC_SCLS(sym->etype) == S_REGISTER), IS_FUNC(sym->type)); + printTypeChain( sym->type, stderr ); + fprintf(stderr, "\n"); +#endif + + /* if extern then add to externs */ + if (IS_EXTERN (sym->etype)) { + /* reduce overhead while linking by not declaring + * extern unused external functions (usually declared + * in header files) */ + if(IS_FUNC(sym->type) && !sym->used)continue; + + /* make sure symbol is not in publics section */ + if(!checkSym(publics, sym)) + checkAddSym(&externs, sym); + continue; + } + + /* if allocation required check is needed + * then check if the symbol really requires + * allocation only for local variables */ + if (arFlag && !IS_AGGREGATE (sym->type) && + !(sym->_isparm && !IS_REGPARM (sym->etype)) && + !sym->allocreq && sym->level) { + +// fprintf(stderr, "%s:%d special case, continuing...\n", __FILE__, __LINE__); + + continue; + } + + /* if global variable & not static or extern + * and addPublics allowed then add it to the public set */ + if ((sym->used) && (sym->level == 0 || + (sym->_isparm && !IS_REGPARM (sym->etype))) && + addPublics && + !IS_STATIC (sym->etype) && !IS_FUNC(sym->type)) { + + checkAddSym(&publics, sym); + } else if (IS_AGGREGATE(sym->type) && sym->level == 0 && ! IS_STATIC(sym->etype)) { + /* If a global variable is not static and will be included in the published list. */ + + checkAddSym(&publics, sym); + } else + /* new version */ + if(IS_STATIC(sym->etype) + && !sym->ival) /* && !sym->level*/ { + reg_info *reg; + sectSym *ssym; + int found=0; + +// debugf("adding symbol %s\n", sym->name); +#define SET_IMPLICIT 1 + +#if SET_IMPLICIT + if(IS_STRUCT(sym->type)) + sym->implicit = 1; +#endif + + reg = pic16_allocDirReg( operandFromSymbol( sym )); + + if(reg) { + for(ssym=setFirstItem(sectSyms); ssym; ssym=setNextItem(sectSyms)) { + if(!strcmp(ssym->name, reg->name))found=1; + } + + if(!found) + checkAddReg(&pic16_rel_udata, reg); +#if 0 + else + debugf("Did find %s in pic16_rel_udata already. Check!\n", reg->name); +// checkAddSym(&publics, sym); +#endif + + } + } + + /* if extern then do nothing or is a function + * then do nothing */ + if (IS_FUNC (sym->type) && !IS_STATIC(sym->etype)) { + if(SPEC_OCLS(sym->etype) == code) { +// fprintf(stderr, "%s:%d: symbol added: %s\n", __FILE__, __LINE__, sym->rname); + checkAddSym(&publics, sym); + } + continue; + } + + /* if is has an absolute address then generate + an equate for this no need to allocate space */ + if (SPEC_ABSA (sym->etype)) { +// fprintf (stderr,"; %s == 0x%04x\t\treqv= %p nRegs= %d\n", +// sym->name, SPEC_ADDR (sym->etype), sym->reqv, sym->regType); + + dbuf_printf (&map->oBuf, "%s\tEQU\t0x%04x\n", + sym->rname, + SPEC_ADDR (sym->etype)); + + /* emit only if it is global */ + if(sym->level == 0) { + reg_info *reg; + + reg = pic16_dirregWithName( sym->name ); + if(!reg) { + /* here */ +// fprintf(stderr, "%s:%d: implicit add of symbol = %s\n", +// __FUNCTION__, __LINE__, sym->name); + + /* if IS_STRUCT is omitted the following + * fixes structures but break char/int etc */ +#if SET_IMPLICIT + if(IS_STRUCT(sym->type)) + sym->implicit = 1; // mark as implicit +#endif + if(!sym->ival) { + reg = pic16_allocDirReg( operandFromSymbol(sym) ); + if(reg) { + if(checkAddReg(&pic16_fix_udata, reg)) { + /* and add to globals list if not exist */ + addSet(&publics, sym); + } + } + } else + addSet(&publics, sym); + } + } + } else { + if(!sym->used && (sym->level == 0)) { + reg_info *reg; + + /* symbol not used, just declared probably, but its in + * level 0, so we must declare it fine as global */ + +// fprintf(stderr, "EXTRA symbol declaration sym= %s\n", sym->name); + +#if SET_IMPLICIT + if(IS_STRUCT(sym->type)) + sym->implicit = 1; // mark as implicit +#endif + if(!sym->ival) { + if(IS_AGGREGATE(sym->type)) { + reg=pic16_allocRegByName(sym->rname, getSize( sym->type ), NULL); + } else { + reg = pic16_allocDirReg( operandFromSymbol( sym ) ); + } + + { + sectSym *ssym; + int found=0; + +#if 0 + fprintf(stderr, "%s:%d sym->rname: %s reg: %p reg->name: %s\n", __FILE__, __LINE__, + sym->rname, reg, (reg?reg->name:"<>")); +#endif + + if(reg) { + for(ssym=setFirstItem(sectSyms); ssym; ssym=setNextItem(sectSyms)) { + if(!strcmp(ssym->name, reg->name))found=1; + } + + if(!found) + if(checkAddReg(&pic16_rel_udata, reg)) { + addSetHead(&publics, sym); + } + } + } + + + + } else + + addSetHead(&publics, sym); + } + +#if 0 + /* allocate space */ + /* If this is a bit variable, then allocate storage after 8 bits have been declared */ + /* unlike the 8051, the pic does not have a separate bit area. So we emulate bit ram */ + /* by grouping the bits together into groups of 8 and storing them in the normal ram. */ + if (IS_BITVAR (sym->etype)) { + bitvars++; + } else { + dbuf_printf (map->oBuf, "\t%s\n", sym->rname); + if ((size = (unsigned int) getSize (sym->type) & 0xffff) > 1) { + for (i = 1; i < size; i++) + dbuf_printf (map->oBuf, "\t%s_%d\n", sym->rname, i); + } + } + dbuf_printf (map->oBuf, "\t.ds\t0x%04x\n", (unsigned int)getSize (sym->type) & 0xffff); +#endif + } + + /* FIXME -- VR Fix the following, so that syms to be placed + * in the idata section and let linker decide about their fate */ + + /* if it has an initial value then do it only if + it is a global variable */ + + if (sym->ival + && ((sym->level == 0) + || IS_STATIC(sym->etype)) ) { + ast *ival = NULL; + +#if 0 + if(SPEC_OCLS(sym->etype)==data) { + fprintf(stderr, "%s: sym %s placed in data segment\n", map->sname, sym->name); + } + + if(SPEC_OCLS(sym->etype)==code) { + fprintf(stderr, "%s: sym %s placed in code segment\n", map->sname, sym->name); + } +#endif + +#if 0 + fprintf(stderr, "'%s': sym '%s' has initial value SPEC_ABSA: %d, IS_AGGREGATE: %d\n", + map->sname, sym->name, SPEC_ABSA(sym->etype), IS_AGGREGATE(sym->type)); +#endif + + if (IS_AGGREGATE (sym->type)) { + if(SPEC_ABSA(sym->etype)) + addSet(&fix_idataSymSet, copySymbol(sym)); + else + addSet(&rel_idataSymSet, copySymbol(sym)); +// ival = initAggregates (sym, sym->ival, NULL); + } else { + if(SPEC_ABSA(sym->etype)) + addSet(&fix_idataSymSet, copySymbol(sym)); + else + addSet(&rel_idataSymSet, copySymbol(sym)); + +// ival = newNode ('=', newAst_VALUE(symbolVal (sym)), +// decorateType (resolveSymbols (list2expr (sym->ival)), RESULT_TYPE_NONE)); + } + + if(ival) { + setAstFileLine (ival, sym->fileDef, sym->lineDef); + codeOutBuf = &statsg->oBuf; + GcurMemmap = statsg; + eBBlockFromiCode (iCodeFromAst (ival)); + sym->ival = NULL; + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* pic16_initPointer - pointer initialization code massaging */ +/*-----------------------------------------------------------------*/ +static value * +pic16_initPointer (initList * ilist, sym_link *toType) +{ + value *val; + ast *expr; + + if (!ilist) { + return valCastLiteral(toType, 0.0, 0); + } + + expr = decorateType(resolveSymbols( list2expr (ilist) ), FALSE); +// expr = list2expr( ilist ); + + if (!expr) + goto wrong; + + /* try it the old way first */ + if (expr->etype && (val = constExprValue (expr, FALSE))) + return val; + + /* ( ptr + constant ) */ + if (IS_AST_OP (expr) && + (expr->opval.op == '+' || expr->opval.op == '-') && + IS_AST_SYM_VALUE (expr->left) && + (IS_ARRAY(expr->left->ftype) || IS_PTR(expr->left->ftype)) && + compareType(toType, expr->left->ftype) && + IS_AST_LIT_VALUE (expr->right)) { + return valForCastAggr (expr->left, expr->left->ftype, + expr->right, + expr->opval.op); + } + + /* (char *)&a */ + if (IS_AST_OP(expr) && expr->opval.op==CAST && + IS_AST_OP(expr->right) && expr->right->opval.op=='&') { + if (compareType(toType, expr->left->ftype)!=1) { + werror (W_INIT_WRONG); + printFromToType(expr->left->ftype, toType); + } + // skip the cast ??? + expr=expr->right; + } + + /* no then we have to do these cludgy checks */ + /* pointers can be initialized with address of + a variable or address of an array element */ + if (IS_AST_OP (expr) && expr->opval.op == '&') { + + /* AST nodes for symbols of type struct or union may be missing type */ + /* due to the abuse of the sym->implicit flag, so get the type */ + /* directly from the symbol if it's missing in the node. */ + sym_link *etype = expr->left->etype; + sym_link *ftype = expr->left->ftype; + if (!etype && IS_AST_SYM_VALUE(expr->left)) + { + etype = expr->left->opval.val->sym->etype; + ftype = expr->left->opval.val->sym->type; + } + + /* address of symbol */ + if (IS_AST_SYM_VALUE (expr->left) && etype) { + val = AST_VALUE (expr->left); + val->type = newLink (DECLARATOR); + if(SPEC_SCLS (etype) == S_CODE) { + DCL_TYPE (val->type) = CPOINTER; + CodePtrPointsToConst (val->type); + } + else if (SPEC_SCLS (etype) == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (SPEC_SCLS (etype) == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (SPEC_SCLS (etype) == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (SPEC_SCLS (etype) == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + + val->type->next = ftype; + val->etype = getSpec (val->type); + return val; + } + + /* if address of indexed array */ + if (IS_AST_OP (expr->left) && expr->left->opval.op == '[') + return valForArray (expr->left); + + /* if address of structure element then + case 1. a.b ; */ + if (IS_AST_OP (expr->left) && + expr->left->opval.op == '.') { + return valForStructElem (expr->left->left, + expr->left->right); + } + + /* case 2. (&a)->b ; + (&some_struct)->element */ + if (IS_AST_OP (expr->left) && + expr->left->opval.op == PTR_OP && + IS_ADDRESS_OF_OP (expr->left->left)) { + return valForStructElem (expr->left->left->left, + expr->left->right); + } + } + /* case 3. (((char *) &a) +/- constant) */ + if (IS_AST_OP (expr) && + (expr->opval.op == '+' || expr->opval.op == '-') && + IS_AST_OP (expr->left) && expr->left->opval.op == CAST && + IS_AST_OP (expr->left->right) && + expr->left->right->opval.op == '&' && + IS_AST_LIT_VALUE (expr->right)) { + + return valForCastAggr (expr->left->right->left, + expr->left->left->opval.lnk, + expr->right, expr->opval.op); + + } + /* case 4. (char *)(array type) */ + if (IS_CAST_OP(expr) && IS_AST_SYM_VALUE (expr->right) && + IS_ARRAY(expr->right->ftype)) { + + val = copyValue (AST_VALUE (expr->right)); + val->type = newLink (DECLARATOR); + if (SPEC_SCLS (expr->right->etype) == S_CODE) { + DCL_TYPE (val->type) = CPOINTER; + CodePtrPointsToConst (val->type); + } + else if (SPEC_SCLS (expr->right->etype) == S_XDATA) + DCL_TYPE (val->type) = FPOINTER; + else if (SPEC_SCLS (expr->right->etype) == S_XSTACK) + DCL_TYPE (val->type) = PPOINTER; + else if (SPEC_SCLS (expr->right->etype) == S_IDATA) + DCL_TYPE (val->type) = IPOINTER; + else if (SPEC_SCLS (expr->right->etype) == S_EEPROM) + DCL_TYPE (val->type) = EEPPOINTER; + else + DCL_TYPE (val->type) = POINTER; + val->type->next = expr->right->ftype->next; + val->etype = getSpec (val->type); + return val; + } + + wrong: + if (expr) + werrorfl (expr->filename, expr->lineno, E_INCOMPAT_PTYPES); + else + werror (E_INCOMPAT_PTYPES); + return NULL; + +} + + +/*-----------------------------------------------------------------*/ +/* printPointerType - generates ival for pointer type */ +/*-----------------------------------------------------------------*/ +static void +_pic16_printPointerType (const char *name, char ptype, void *p) +{ + char buf[256]; + + SNPRINTF(buf, sizeof(buf), "LOW(%s)", name); + pic16_emitDS (buf, ptype, p); + SNPRINTF(buf, sizeof(buf), "HIGH(%s)", name); + pic16_emitDS (buf, ptype, p); +} + +/*-----------------------------------------------------------------*/ +/* printPointerType - generates ival for pointer type */ +/*-----------------------------------------------------------------*/ +static void +pic16_printPointerType (const char *name, char ptype, void *p) +{ + _pic16_printPointerType (name, ptype, p); + //pic16_flushDB(ptype, p); /* breaks char* const arr[] = {&c, &c, &c}; */ +} + +/*-----------------------------------------------------------------*/ +/* printGPointerType - generates ival for generic pointer type */ +/*-----------------------------------------------------------------*/ +static void +pic16_printGPointerType (const char *iname, const unsigned int itype, + char ptype, void *p) +{ + char buf[256]; + + _pic16_printPointerType (iname, ptype, p); + + switch (itype) + { + case CPOINTER: /* fall through */ + case FUNCTION: /* fall through */ + case GPOINTER: + /* GPTRs pointing to __data space should be reported as POINTERs */ + SNPRINTF(buf, sizeof(buf), "UPPER(%s)", iname); + pic16_emitDS (buf, ptype, p); + break; + + case POINTER: /* fall through */ + case FPOINTER: /* fall through */ + case IPOINTER: /* fall through */ + case PPOINTER: /* __data space */ + SNPRINTF(buf, sizeof(buf), "0x%02x", GPTR_TAG_DATA); + pic16_emitDS (buf, ptype, p); + break; + + default: + debugf ("itype = %d\n", itype ); + assert (0); + } + + if (itype == GPOINTER) { + fprintf(stderr, "%s: initialized generic pointer with unknown storage class assumes object in code space\n", __func__); + } + + //pic16_flushDB(ptype, p); /* might break char* const arr[] = {...}; */ +} + + +/* set to 0 to disable debug messages */ +#define DEBUG_PRINTIVAL 0 + +/*-----------------------------------------------------------------*/ +/* pic16_printIvalType - generates ival for int/char */ +/*-----------------------------------------------------------------*/ +static void +pic16_printIvalType (symbol *sym, sym_link * type, initList * ilist, char ptype, void *p) +{ + value *val; + int i; + +// fprintf(stderr, "%s for symbol %s\n",__FUNCTION__, sym->rname); + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + + /* if initList is deep */ + if (ilist && ilist->type == INIT_DEEP) + ilist = ilist->init.deep; + + if (!IS_AGGREGATE(sym->type) && getNelements(type, ilist)>1) { + werror (W_EXCESS_INITIALIZERS, "scalar", sym->name, sym->lineDef); + } + + if (!(val = list2val (ilist, TRUE))) { + // assuming a warning has been thrown + val = constCharVal (0); + } + + if (val->type != type) { + val = valCastLiteral(type, floatFromVal (val), (TYPE_TARGET_ULONGLONG) ullFromVal (val)); + } + + for (i = 0; i < (int)getSize (type); i++) { + pic16_emitDB(pic16aopLiteral(val, i), ptype, p); + } // for +} + +/*--------------------------------------------------------------------*/ +/* pic16_printIvalChar - generates initital value for character array */ +/*--------------------------------------------------------------------*/ +static int +pic16_printIvalChar (symbol *sym, sym_link * type, initList * ilist, const char *s, char ptype, void *p) +{ + value *val; + int len; + size_t remain, ilen; + + if(!p) + return 0; + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + if(!s) { + val = list2val (ilist, TRUE); + + /* if the value is a character string */ + if(IS_ARRAY (val->type) && IS_CHAR (val->etype)) { + /* length of initializer string (might contain \0, so do not use strlen) */ + ilen = DCL_ELEM(val->type); + +#if 0 + /* This causes structflexarray.c to fail. */ + if(!DCL_ELEM (type)) + DCL_ELEM (type) = ilen; +#endif + + /* len is 0 if declartion equals initializer, + * >0 if declaration greater than initializer + * <0 if declaration less than initializer + * Strategy: if >0 emit 0x00 for the rest of the length, + * if <0 then emit only the length of declaration elements + * and warn user + */ + len = DCL_ELEM (type) - ilen; + +// fprintf(stderr, "%s:%d ilen = %i len = %i DCL_ELEM(type) = %i SPEC_CVAL-len = %i\n", __FILE__, __LINE__, +// ilen, len, DCL_ELEM(type), strlen(SPEC_CVAL(val->etype).v_char)); + + if(len >= 0) { + /* emit initializer */ + for(remain=0; remainetype).v_char[ remain ], ptype, p); + } // for + + /* fill array with 0x00 */ + while(len--) { + pic16_emitDB(0x00, ptype, p); + } // while + } else if (!DCL_ELEM (type)) { + // flexible arrays: char str[] = "something"; */ + for(remain=0; remainetype).v_char[ remain ], ptype, p); + } // for + } else { + werror (W_EXCESS_INITIALIZERS, "array of chars", sym->name, sym->lineDef); + for(remain=0; remainetype).v_char[ remain ], ptype, p); + } // for + } // if + + +// if((remain = (DCL_ELEM (type) - strlen (SPEC_CVAL (val->etype).v_char) - 1)) > 0) { +// } + return 1; + } else return 0; + } else { + for(remain=0; remainnext) && + ilist && ilist->type == INIT_NODE) { + if (!IS_LITERAL(list2val(ilist, TRUE)->etype)) { + werror (W_INIT_WRONG); + return; + } + + if(pic16_printIvalChar (sym, type, + (ilist->type == INIT_DEEP ? ilist->init.deep : ilist), + SPEC_CVAL (sym->etype).v_char, ptype, p)) + return; + } + /* not the special case */ + if (ilist && ilist->type != INIT_DEEP) + { + werror (E_INIT_STRUCT, sym->name); + return; + } + + iloop = (ilist ? ilist->init.deep : NULL); + lcnt = DCL_ELEM (type); + + for (;;) + { + size++; + pic16_printIval (sym, type->next, iloop, ptype, p); + iloop = (iloop ? iloop->next : NULL); + + + /* if not array limits given & we */ + /* are out of initialisers then */ + if (!DCL_ELEM (type) && !iloop) + break; + + /* no of elements given and we */ + /* have generated for all of them */ + if (!--lcnt) { + /* if initializers left */ + if (iloop) { + werror (W_EXCESS_INITIALIZERS, "array", sym->name, sym->lineDef); + } + break; + } + } + +#if 0 + /* This causes bug #1843745. */ + /* if we have not been given a size */ + if (!DCL_ELEM (type)) + DCL_ELEM (type) = size; +#endif + + return; +} + +/*-----------------------------------------------------------------*/ +/* pic16_printIvalBitFields - generate initializer for bitfields */ +/*-----------------------------------------------------------------*/ +static void +pic16_printIvalBitFields (symbol **sym, initList **ilist, char ptype, void *p) +{ + symbol *lsym = *sym; + initList *lilist = *ilist; + unsigned long ival = 0; + unsigned size = 0; + int bit_start = 0; + unsigned i; + + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + + while (lsym && IS_BITFIELD (lsym->type)) + { + unsigned bit_length = SPEC_BLEN (lsym->etype); + if (0 == bit_length) + { + /* bit-field structure member with a width of 0 */ + lsym = lsym->next; + break; + } + else if (!SPEC_BUNNAMED (lsym->etype)) + { + /* not an unnamed bit-field structure member */ + value *val = list2val (lilist, TRUE); + + if (size) + { + if (bit_length > 8) + size += (bit_length + 7) / 8; + } + else + size = (bit_length + 7) / 8; + + ival |= (ulFromVal (val) & ((1ul << bit_length) - 1ul)) << bit_start; + lilist = (lilist ? lilist->next : NULL); + } + bit_start += bit_length; + lsym = lsym->next; + if (lsym && IS_BITFIELD (lsym->type) && (0 == SPEC_BSTR (lsym->etype))) + { + /* a new integer */ + break; + } + } + + for (i = 0; i < size; i++) + pic16_emitDB (BYTE_IN_LONG (ival, i), ptype, p); + + *sym = lsym; + *ilist = lilist; +} + + +/*-----------------------------------------------------------------*/ +/* printIvalStruct - generates initial value for structures */ +/*-----------------------------------------------------------------*/ +static void +pic16_printIvalStruct (symbol * sym, sym_link * type, + initList * ilist, char ptype, void *p) +{ + symbol *sflds; + initList *iloop = NULL; + + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + sflds = SPEC_STRUCT (type)->fields; + + if (ilist) + { + if (ilist->type != INIT_DEEP) + { + werrorfl (sym->fileDef, sym->lineDef, E_INIT_STRUCT, sym->name); + return; + } + + iloop = ilist->init.deep; + } + + while (sflds) + { +// fprintf(stderr, "%s:%d sflds: %p\tiloop = %p\n", __FILE__, __LINE__, sflds, iloop); + if (IS_BITFIELD (sflds->type)) + { + pic16_printIvalBitFields (&sflds, &iloop, ptype, p); + } + else + { + pic16_printIval (sym, sflds->type, iloop, ptype, p); + sflds = sflds->next; + iloop = iloop ? iloop->next : NULL; + } + } + if (iloop) + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "struct", sym->name); +} + +/*-----------------------------------------------------------------*/ +/* printIvalUnion - generates initial value for unions */ +/*-----------------------------------------------------------------*/ +static void +pic16_printIvalUnion (symbol * sym, sym_link * type, + initList * ilist, char ptype, void *p) +{ + //symbol *sflds; + initList *iloop = NULL; + unsigned int size; + symbol *sflds = NULL; + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + assert (type); + + sflds = SPEC_STRUCT (type)->fields; + + if (ilist) { + if (ilist->type != INIT_DEEP) { + werrorfl (sym->fileDef, sym->lineDef, E_INIT_STRUCT, sym->name); + return; + } + + iloop = ilist->init.deep; + } + + size = SPEC_STRUCT(type)->size; + sflds = SPEC_STRUCT(type)->fields; + + /* skip past holes, print value */ + while (iloop && iloop->type == INIT_HOLE) + { + iloop = iloop->next; + sflds = sflds->next; + } + pic16_printIval (sym, sflds->type, iloop, ptype, p); + + /* if the first field is not the longest, fill with 0s */ + while (size > getSize (sflds->type)) { + pic16_emitDB(0, ptype, p); + size--; + } // while +} + +static int +pic16_isUnion( symbol *sym, sym_link *type ) +{ + if (type && SPEC_STRUCT(type)->type == UNION) return 1; + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* pic16_printIvalCharPtr - generates initial values for character pointers */ +/*--------------------------------------------------------------------------*/ +static int +pic16_printIvalCharPtr (symbol * sym, sym_link * type, value * val, char ptype, void *p) +{ + int size = 0; + int i; + + /* PENDING: this is _very_ mcs51 specific, including a magic + number... + It's also endin specific. + + VR - Attempting to port this function to pic16 port - 8-Jun-2004 + */ + + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + size = getSize (type); + + if (val->name && strlen (val->name)) + { + if (size == 1) /* This appears to be Z80 specific?? */ + { + pic16_emitDS(val->name, ptype, p); + } + else if (size == 2) + { + pic16_printPointerType (val->name, ptype, p); + } + else if (size == 3) + { + int type; + type = PTR_TYPE (SPEC_OCLS (val->etype)); + if (val->sym && val->sym->isstrlit) { + // this is a literal string + type = CPOINTER; + } + pic16_printGPointerType(val->name, type, ptype, p); + } + else + { + fprintf (stderr, "*** internal error: unknown size in " + "printIvalCharPtr.\n"); + assert(0); + } + } + else + { + // these are literals assigned to pointers + for (i = 0; i < size; i++) + { + pic16_emitDB(pic16aopLiteral(val, i), ptype, p); + } // for + } + + if (val->sym && val->sym->isstrlit) { // && !isinSet(statsg->syms, val->sym)) { + if(ptype == 'p' && !isinSet(statsg->syms, val->sym))addSet (&statsg->syms, val->sym); + else if(ptype == 'f' /*&& !isinSet(rel_idataSymSet, val->sym)*/)addSet(&rel_idataSymSet, val->sym); + } + + return 1; +} + +/*-----------------------------------------------------------------------*/ +/* pic16_printIvalFuncPtr - generate initial value for function pointers */ +/*-----------------------------------------------------------------------*/ +static void +pic16_printIvalFuncPtr (sym_link * type, initList * ilist, char ptype, void *p) +{ + value *val; + int dLvl = 0; + + +#if DEBUG_PRINTIVAL + fprintf(stderr, "%s\n",__FUNCTION__); +#endif + + if (ilist) + val = list2val (ilist, TRUE); + else + val = valCastLiteral(type, 0.0, 0); + + if (!val) { + // an error has been thrown already + val = constCharVal (0); + } + + if (IS_LITERAL(val->etype)) { + if (0 && compareType(type, val->etype) == 0) { + werrorfl (ilist->filename, ilist->lineno, E_INCOMPAT_TYPES); + printFromToType (val->type, type); + } + pic16_printIvalCharPtr (NULL, type, val, ptype, p); + return; + } + + /* check the types */ + if ((dLvl = compareType (val->type, type->next)) <= 0) + { + pic16_emitDB(0x00, ptype, p); + return; + } + + /* now generate the name */ + if (!val->sym) { + pic16_printGPointerType (val->name, CPOINTER /*DCL_TYPE(val->type)*/, ptype, p); + } else { + pic16_printGPointerType (val->sym->rname, CPOINTER /*DCL_TYPE(val->type)*/, ptype, p); + + if(IS_FUNC(val->sym->type) && !val->sym->used && !IS_STATIC(val->sym->etype)) { + + if(!checkSym(publics, val->sym)) + if(checkAddSym(&externs, val->sym) && (ptype == 'f')) { + /* this has not been declared as extern + * so declare it as a 'late extern' just after the symbol */ + fprintf((FILE *)p, ";\tdeclare symbol as extern\n"); + fprintf((FILE *)p, "\textern\t%s\n", val->sym->rname); + fprintf((FILE *)p, ";\tcontinue variable declaration\n"); + } + } + } + + return; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_printIvalPtr - generates initial value for pointers */ +/*-----------------------------------------------------------------*/ +static void +pic16_printIvalPtr (symbol * sym, sym_link * type, initList * ilist, char ptype, void *p) +{ + value *val; + int size; + int i; + +#if 0 + fprintf(stderr, "%s:%d initialising pointer: %s size: %d\n", __FILE__, __LINE__, + sym->rname, getSize(sym->type)); +#endif + + /* if deep then */ + if (ilist && (ilist->type == INIT_DEEP)) + ilist = ilist->init.deep; + + /* function pointer */ + if (IS_FUNC (type->next)) + { + pic16_printIvalFuncPtr (type, ilist, ptype, p); + return; + } + + if (!(val = pic16_initPointer (ilist, type))) + return; + + /* if character pointer */ + if (IS_CHAR (type->next)) + if (pic16_printIvalCharPtr (sym, type, val, ptype, p)) + return; + + /* check the type */ + if (compareType (type, val->type) == 0) { + werrorfl (ilist->filename, ilist->lineno, W_INIT_WRONG); + printFromToType (val->type, type); + } + + size = getSize (type); + + /* if val is literal */ + if (IS_LITERAL (val->etype)) + { + for (i = 0; i < size; i++) + { + pic16_emitDB(pic16aopLiteral(val, i), ptype, p); + } // for + return; + } + + if (size == 1) /* Z80 specific?? */ + { + pic16_emitDS(val->name, ptype, p); + } + else if (size == 2) + { + pic16_printPointerType (val->name, ptype, p); + } + else if (size == 3) + { + int itype = 0; + itype = PTR_TYPE (SPEC_OCLS (val->etype)); + pic16_printGPointerType (val->name, itype, ptype, p); + } + else + { + assert(0); + } +} + + + +/*-----------------------------------------------------------------*/ +/* pic16_printIval - generates code for initial value */ +/*-----------------------------------------------------------------*/ +void pic16_printIval (symbol * sym, sym_link * type, initList * ilist, char ptype, void *p) +{ +// sym_link *itype; + + if (!p) + return; + +#if 0 + fprintf(stderr, "%s:%d generating init for %s\n", __FILE__, __LINE__, sym->name); + fprintf(stderr, "%s: IS_STRUCT: %d IS_ARRAY: %d IS_PTR: %d IS_SPEC: %d\n", sym->name, + IS_STRUCT(type), IS_ARRAY(type), IS_PTR(type), IS_SPEC(type)); +#endif + + /* Handle designated initializers */ + if (ilist) + ilist = reorderIlist (type, ilist); + + /* If this is a hole, substitute an appropriate initializer. */ + if (ilist && ilist->type == INIT_HOLE) + { + if (IS_AGGREGATE (type)) + { + ilist = newiList(INIT_DEEP, NULL); /* init w/ {} */ + } + else + { + ast *ast = newAst_VALUE (constVal("0")); + ast = decorateType (ast, RESULT_TYPE_NONE); + ilist = newiList(INIT_NODE, ast); + } + } + + /* if structure then */ + if (IS_STRUCT (type)) + { + if (pic16_isUnion(sym, type)) + { + //fprintf(stderr,"%s union\n",__FUNCTION__); + pic16_printIvalUnion (sym, type, ilist, ptype, p); + } else { + //fprintf(stderr,"%s struct\n",__FUNCTION__); + pic16_printIvalStruct (sym, type, ilist, ptype, p); + } + return; + } + + /* if this is an array */ + if (IS_ARRAY (type)) + { +// fprintf(stderr,"%s array\n",__FUNCTION__); + pic16_printIvalArray (sym, type, ilist, ptype, p); + return; + } + +#if 0 + if (ilist) + { + // not an aggregate, ilist must be a node + if (ilist->type!=INIT_NODE) { + // or a 1-element list + if (ilist->init.deep->next) { + werrorfl (sym->fileDef, sym->lineDef, W_EXCESS_INITIALIZERS, "scalar", + sym->name); + } else { + ilist=ilist->init.deep; + } + } + +#if 0 + // and the type must match + itype=ilist->init.node->ftype; + + if (compareType(type, itype)==0) { + // special case for literal strings + if (IS_ARRAY (itype) && IS_CHAR (getSpec(itype)) && + // which are really code pointers + IS_PTR(type) && DCL_TYPE(type)==CPOINTER) { + // no sweat + } else { +// werrorfl (ilist->filename, ilist->lineno, E_TYPE_MISMATCH, "assignment", " "); +// printFromToType(itype, type); + } + } +#endif + } +#endif + + /* if this is a pointer */ + if (IS_PTR (type)) + { +// fprintf(stderr,"%s pointer\n",__FUNCTION__); + pic16_printIvalPtr (sym, type, ilist, ptype, p); + return; + } + + + /* if type is SPECIFIER */ + if (IS_SPEC (type)) + { +// fprintf(stderr,"%s spec\n",__FUNCTION__); + pic16_printIvalType (sym, type, ilist, ptype, p); + return; + } +} + +static int +PIC16_IS_CONFIG_ADDRESS(int address) +{ + return ((address >= pic16->cwInfo.confAddrStart && address <= pic16->cwInfo.confAddrEnd)); +} + +static int +PIC16_IS_IDLOC_ADDRESS(int address) +{ + return ((address >= pic16->idInfo.idAddrStart && address <= pic16->idInfo.idAddrEnd)); +} + +/*-----------------------------------------------------------------*/ +/* emitStaticSeg - emitcode for the static segment */ +/*-----------------------------------------------------------------*/ +static void +pic16emitStaticSeg (memmap * map) +{ + symbol *sym; + static int didcode = 0; + + //fprintf(stderr, "%s\n",__FUNCTION__); + + pic16_initDB (); + + /* for all variables in this segment do */ + for (sym = setFirstItem (map->syms); sym; sym = setNextItem (map->syms)) + { + int size = getSize (sym->type); + +#if 0 + fprintf (stderr, "%s\t%s: sym: %s\tused: %d\tSPEC_ABSA: %d\tSPEC_AGGREGATE: %d\tCODE: %d\n\ +CODESPACE: %d\tCONST: %d\tPTRCONST: %d\tSPEC_CONST: %d\n", __FUNCTION__, map->sname, sym->name, sym->used, SPEC_ABSA (sym->etype), IS_AGGREGATE (sym->type), IS_CODE (sym->etype), IN_CODESPACE (SPEC_OCLS (sym->etype)), IS_CONSTANT (sym->etype), IS_PTR_CONST (sym->etype), SPEC_CONST (sym->etype)); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); +#endif + + if (SPEC_ABSA (sym->etype) && PIC16_IS_CONFIG_ADDRESS (SPEC_ADDR (sym->etype))) + { + pic16_assignConfigWordValue (SPEC_ADDR (sym->etype), (int) ulFromVal (list2val (sym->ival, TRUE))); + continue; + } + + if (SPEC_ABSA (sym->etype) && PIC16_IS_IDLOC_ADDRESS (SPEC_ADDR (sym->etype))) + { + pic16_assignIdByteValue (SPEC_ADDR (sym->etype), (char) ulFromVal (list2val (sym->ival, TRUE))); + continue; + } + + /* if it is "extern" then do nothing */ + if (IS_EXTERN (sym->etype) /* && !SPEC_ABSA(sym->etype) */ ) + { + checkAddSym (&externs, sym); + continue; + } + + /* bail out on incomplete types */ + if (0 == size) + { + werrorfl (sym->fileDef, sym->lineDef, E_UNKNOWN_SIZE, sym->name); + } + + /* if it is not static add it to the public + table */ + if (!IS_STATIC (sym->etype)) + { + /* do not emit if it is a config word declaration */ + checkAddSym (&publics, sym); + } + + /* print extra debug info if required */ + if (options.debug || sym->level == 0) + { + /* NOTE to me - cdbFile may be null in which case, + * the sym name will be printed to stdout. oh well */ + debugFile->writeSymbol (sym); + } + + /* if it has an absolute address */ + if (SPEC_ABSA (sym->etype)) + { +// fprintf(stderr, "%s:%d spec_absa is true for symbol: %s\n", +// __FILE__, __LINE__, sym->name); + + if (!sym->ival && IS_ARRAY (sym->type) && IS_CHAR (sym->type->next) && SPEC_CVAL (sym->etype).v_char) + { + /* symbol has absolute address but no initial value */ + /* special case for character strings */ + +// fprintf(stderr, "%s:%d printing code string from %s\n", __FILE__, __LINE__, sym->rname); + + pic16_pCodeConstString (sym->rname, SPEC_CVAL (sym->etype).v_char, getSize (sym->type)); + } + else + { + pBlock *pb; + symbol *asym; + absSym *abSym; + pCode *pcf; + + /* symbol has absolute address and initial value */ + ++noAlloc; + resolveIvalSym (sym->ival, sym->type); + asym = newSymbol (sym->rname, 0); + abSym = Safe_alloc(sizeof (absSym)); + strncpy(abSym->name, sym->rname, sizeof(abSym->name) - 1); + abSym->name[sizeof(abSym->name) - 1] = '\0'; + abSym->address = SPEC_ADDR (sym->etype); + addSet (&absSymSet, abSym); + + pb = pic16_newpCodeChain (NULL, 'A', pic16_newpCodeCharP ("; Starting pCode block for absolute Ival")); + pic16_addpBlock (pb); + + pcf = pic16_newpCodeFunction (moduleName, asym->name); + PCF (pcf)->absblock = TRUE; + + pic16_addpCode2pBlock (pb, pcf); + pic16_addpCode2pBlock (pb, pic16_newpCodeLabel (sym->rname, -1)); + //fprintf(stderr, "%s:%d [1] generating init for label: %s\n", __FILE__, __LINE__, sym->rname); + /* if it has an initial value */ + if (sym->ival) + { + pic16_printIval (sym, sym->type, sym->ival, 'p', (void *) pb); + pic16_flushDB ('p', (void *) pb); + } + + pic16_addpCode2pBlock (pb, pic16_newpCodeFunction (NULL, NULL)); + --noAlloc; + } + } + else + { +// fprintf(stderr, "%s:%d spec_absa is false for symbol: %s\n", +// __FILE__, __LINE__, sym->name); + + + /* if it has an initial value */ + if (sym->ival) + { + pBlock *pb; + + /* symbol doesn't have absolute address but has initial value */ + dbuf_printf (&code->oBuf, "%s:\n", sym->rname); + ++noAlloc; + resolveIvalSym (sym->ival, sym->type); + + pb = pic16_newpCodeChain (NULL, 'P', pic16_newpCodeCharP ("; Starting pCode block for Ival")); + pic16_addpBlock (pb); + + if (!didcode) + { + /* make sure that 'code' directive is emitted before, once */ + pic16_addpCode2pBlock (pb, pic16_newpCodeAsmDir ("code", NULL)); + + ++didcode; + } + + pic16_addpCode2pBlock (pb, pic16_newpCodeLabel (sym->rname, -1)); + //fprintf(stderr, "%s:%d [2] generating init for label: %s\n", __FILE__, __LINE__, sym->rname); + pic16_printIval (sym, sym->type, sym->ival, 'p', (void *) pb); + pic16_flushDB ('p', (void *) pb); + --noAlloc; + } + else + { + + /* symbol doesn't have absolute address and no initial value */ + /* allocate space */ +// fprintf(stderr, "%s:%d [3] generating init for label: %s\n", __FILE__, __LINE__, sym->rname); + dbuf_printf (&code->oBuf, "%s:\n", sym->rname); + /* special case for character strings */ + if (IS_ARRAY (sym->type) && IS_CHAR (sym->type->next) && SPEC_CVAL (sym->etype).v_char) + { + +// fprintf(stderr, "%s:%d printing code string for %s\n", __FILE__, __LINE__, sym->rname); + + pic16_pCodeConstString (sym->rname, SPEC_CVAL (sym->etype).v_char, getSize (sym->type)); + } + else + { + assert (0); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_emitConfigRegs - emits the configuration registers */ +/*-----------------------------------------------------------------*/ +void pic16_emitConfigRegs (FILE *of) +{ + int i; + + if (pic16_config_options) + { + pic16_config_options_t *p; + + /* check if mixing config directives */ + for (i = 0; i <= (pic16->cwInfo.confAddrEnd - pic16->cwInfo.confAddrStart); ++i) + if (pic16->cwInfo.crInfo[i].emit) + { + werror (E_MIXING_CONFIG); + break; + } + + /* emit new config directives */ + for (p = pic16_config_options; p; p = p->next) + fprintf (of, "\t%s\n", p->config_str); + } + + /* emit old __config directives */ + for (i = 0; i <= (pic16->cwInfo.confAddrEnd - pic16->cwInfo.confAddrStart); ++i) + if (pic16->cwInfo.crInfo[i].emit) //mask != -1) + fprintf (of, "\t__config 0x%06x, 0x%02x\n", + pic16->cwInfo.confAddrStart + i, + (unsigned char) pic16->cwInfo.crInfo[i].value); +} + +void pic16_emitIDRegs (FILE *of) +{ + int i; + + for (i=0; i <= (pic16->idInfo.idAddrEnd - pic16->idInfo.idAddrStart); i++) + if (pic16->idInfo.irInfo[i].emit) + fprintf (of, "\t__idlocs 0x%06x, 0x%02x\n", + pic16->idInfo.idAddrStart + i, + (unsigned char) pic16->idInfo.irInfo[i].value); +} + + +static void +pic16emitMaps () +{ + /* no special considerations for the following + data, idata & bit & xdata */ + pic16emitRegularMap (data, TRUE, TRUE); + pic16emitRegularMap (idata, TRUE, TRUE); + pic16emitRegularMap (bit, TRUE, FALSE); + pic16emitRegularMap (xdata, TRUE, TRUE); + pic16emitRegularMap (sfr, FALSE, FALSE); + pic16emitRegularMap (sfrbit, FALSE, FALSE); + pic16emitRegularMap (code, TRUE, FALSE); + pic16emitStaticSeg (statsg); + pic16emitStaticSeg (c_abs); +} + +/*-----------------------------------------------------------------*/ +/* createInterruptVect - creates the interrupt vector */ +/*-----------------------------------------------------------------*/ +static void +pic16createInterruptVect (struct dbuf_s * vBuf) +{ + /* if the main is only a prototype ie. no body then do nothing */ +#if 0 + if (!IFFUNC_HASBODY(mainf->type)) { + /* if ! compile only then main function should be present */ + if (!options.cc_only) + werror (E_NO_MAIN); + return; + } +#endif +#if 0 + if((!pic16_options.omit_ivt) || (pic16_options.omit_ivt && pic16_options.leave_reset)) { + dbuf_printf (vBuf, ";\t.area\t%s\n", CODE_NAME); + dbuf_printf (vBuf, ".intvecs\tcode\t0x%06x\n", pic16_options.ivt_loc); + + /* this is an overkill since WE are the port, + * and we know if we have a genIVT function! */ + if(port->genIVT) { + port->genIVT(vFile, interrupts, maxInterrupts); + } + } +#endif + +} + + +/*-----------------------------------------------------------------*/ +/* pic16initialComments - puts in some initial comments */ +/*-----------------------------------------------------------------*/ +static void +pic16initialComments (FILE * afile) +{ + initialComments (afile); + fprintf (afile, "; PIC16 port for the Microchip 16-bit core micros\n"); + if (pic16_options.xinst) { + fprintf (afile, "; * Extended Instruction Set\n"); + } // if + + if (pic16_mplab_comp) { + fprintf(afile, "; * MPLAB/MPASM/MPASMWIN/MPLINK compatibility mode enabled\n"); + } // if + fprintf (afile, "%s", iComments2); + + if (options.debug) { + fprintf (afile, "\n\t.ident \"SDCC version %s #%s [pic16 port]%s\"\n", + SDCC_VERSION_STR, getBuildNumber(), (!pic16_options.xinst?"":" {extended}") ); + } // if +} + +int +pic16_stringInSet(const char *str, set **world, int autoAdd) +{ + char *s; + + if (!str) return 1; + assert(world); + + for (s = setFirstItem(*world); s; s = setNextItem(*world)) + { + /* found in set */ + if (0 == strcmp(s, str)) return 1; + } + + /* not found */ + if (autoAdd) addSet(world, Safe_strdup(str)); + return 0; +} + +static int +pic16_emitSymbolIfNew(FILE *file, const char *fmt, const char *sym, int checkLocals) +{ + static set *emitted = NULL; + + if (!pic16_stringInSet(sym, &emitted, 1)) { + /* sym was not in emittedSymbols */ + if (!checkLocals || !pic16_stringInSet(sym, &pic16_localFunctions, 0)) { + /* sym is not a locally defined function---avoid bug #1443651 */ + fprintf( file, fmt, sym ); + return 0; + } + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* printPublics - generates global declarations for publics */ +/*-----------------------------------------------------------------*/ +static void +pic16printPublics (FILE *afile) +{ + symbol *sym; + + fprintf (afile, "\n%s", iComments2); + fprintf (afile, "; public variables in this module\n"); + fprintf (afile, "%s", iComments2); + + for(sym = setFirstItem (publics); sym; sym = setNextItem (publics)) + /* sanity check */ + if(!IS_STATIC(sym->etype)) + pic16_emitSymbolIfNew(afile, "\tglobal\t%s\n", sym->rname, 0); +} + +/*-----------------------------------------------------------------*/ +/* printExterns - generates extern declarations for externs */ +/*-----------------------------------------------------------------*/ +static void +pic16_printExterns(FILE *afile) +{ + symbol *sym; + + /* print nothing if no externs to declare */ + if(!elementsInSet(externs) && !elementsInSet(pic16_builtin_functions)) + return; + + fprintf(afile, "\n%s", iComments2); + fprintf(afile, "; extern variables in this module\n"); + fprintf(afile, "%s", iComments2); + + for(sym = setFirstItem(externs); sym; sym = setNextItem(externs)) + pic16_emitSymbolIfNew(afile, "\textern\t%s\n", sym->rname, 1); + + for(sym = setFirstItem(pic16_builtin_functions); sym; sym = setNextItem(pic16_builtin_functions)) + pic16_emitSymbolIfNew(afile, "\textern\t_%s\n", sym->name, 1); +} + +/*-----------------------------------------------------------------*/ +/* emitOverlay - will emit code for the overlay stuff */ +/*-----------------------------------------------------------------*/ +static void +pic16emitOverlay (struct dbuf_s *aBuf) +{ + set *ovrset; + + if (!elementsInSet (ovrSetSets)) + dbuf_printf (aBuf, ";\t.area\t%s\n", port->mem.overlay_name); + + /* for each of the sets in the overlay segment do */ + for (ovrset = setFirstItem (ovrSetSets); ovrset; + ovrset = setNextItem (ovrSetSets)) + { + + symbol *sym; + + if (elementsInSet (ovrset)) + { + /* this dummy area is used to fool the assembler + otherwise the assembler will append each of these + declarations into one chunk and will not overlay + sad but true */ + dbuf_printf (aBuf, ";\t.area _DUMMY\n"); + /* output the area informtion */ + dbuf_printf (aBuf, ";\t.area\t%s\n", port->mem.overlay_name); /* MOF */ + } + + for (sym = setFirstItem (ovrset); sym; + sym = setNextItem (ovrset)) + { + + /* if extern then do nothing */ + if (IS_EXTERN (sym->etype)) + continue; + + /* if allocation required check is needed + then check if the symbol really requires + allocation only for local variables */ + if (!IS_AGGREGATE (sym->type) && + !(sym->_isparm && !IS_REGPARM (sym->etype)) + && !sym->allocreq && sym->level) + continue; + + /* if global variable & not static or extern + and addPublics allowed then add it to the public set */ + if ((sym->_isparm && !IS_REGPARM (sym->etype)) + && !IS_STATIC (sym->etype)) { +// fprintf(stderr, "%s:%d %s accessed\n", __FILE__, __LINE__, __FUNCTION__); + checkAddSym(&publics, sym); +// addSetHead (&publics, sym); + } + + /* if extern then do nothing or is a function + then do nothing */ + if (IS_FUNC (sym->type)) + continue; + + + /* if is has an absolute address then generate + an equate for this no need to allocate space */ + if (SPEC_ABSA (sym->etype)) + { + + if (options.debug || sym->level == 0) + dbuf_printf (aBuf, " == 0x%04x\n", SPEC_ADDR (sym->etype)); + + dbuf_printf (aBuf, "%s\t=\t0x%04x\n", + sym->rname, + SPEC_ADDR (sym->etype)); + } + else + { + if (options.debug || sym->level == 0) + dbuf_printf (aBuf, "==.\n"); + + /* allocate space */ + dbuf_printf (aBuf, "%s:\n", sym->rname); + dbuf_printf (aBuf, "\t.ds\t0x%04x\n", (unsigned int) getSize (sym->type) & 0xffff); + } + + } + } +} + +static void +emitStatistics(FILE *asmFile) +{ + unsigned long isize, udsize, ramsize; + statistics.isize = pic16_countInstructions(); + isize = (((long)statistics.isize) >= 0) ? statistics.isize : 0; + udsize = (((long)statistics.udsize) >= 0) ? statistics.udsize : 0; + ramsize = pic16 ? pic16->RAMsize : 0x200; + ramsize -= 256; /* ignore access bank and SFRs */ + if (ramsize == 0) ramsize = 64; /* prevent division by zero (below) */ + + fprintf (asmFile, "\n\n; Statistics:\n"); + fprintf (asmFile, "; code size:\t%5ld (0x%04lx) bytes (%5.2f%%)\n; \t%5ld (0x%04lx) words\n", + isize, isize, (isize*100.0)/(128UL << 10), + isize>>1, isize>>1); + fprintf (asmFile, "; udata size:\t%5ld (0x%04lx) bytes (%5.2f%%)\n", + udsize, udsize, (udsize*100.0) / (1.0 * ramsize)); + fprintf (asmFile, "; access size:\t%5ld (0x%04lx) bytes\n", + statistics.intsize, statistics.intsize); + + fprintf (asmFile, "\n\n"); +} + + + +/*-----------------------------------------------------------------*/ +/* glue - the final glue that hold the whole thing together */ +/*-----------------------------------------------------------------*/ +void +pic16glue () +{ + FILE *asmFile; + struct dbuf_s ovrBuf; + struct dbuf_s vBuf; + + dbuf_init(&ovrBuf, 4096); + dbuf_init(&vBuf, 4096); + + mainf = newSymbol ("main", 0); + mainf->block = 0; + + mainf = findSymWithLevel(SymbolTab, mainf); + + if (mainf && IFFUNC_HASBODY(mainf->type) && pic16->xinst && !has_xinst_config) + { + fprintf(stderr, "WARNING: The target device seems to support XINST and no #pragma config XINST=OFF was found.\n"); + fprintf(stderr, " The code generated by SDCC does probably not work when XINST is enabled (possibly by default).\n"); + fprintf(stderr, " Please make sure to disable XINST.\n"); + fprintf(stderr, " (If the target does not actually support XINST, please report this as a bug in SDCC.)\n"); + } // if + + pic16_pCodeInitRegisters(); + + if(pic16_options.no_crt && mainf && IFFUNC_HASBODY(mainf->type)) { + pBlock *pb = pic16_newpCodeChain(NULL,'X',pic16_newpCodeCharP("; Starting pCode block")); + + pic16_addpBlock(pb); + + /* entry point @ start of CSEG */ + pic16_addpCode2pBlock(pb,pic16_newpCodeLabel("__sdcc_program_startup",-1)); + + if(initsfpnt) { + pic16_addpCode2pBlock(pb, pic16_newpCode(POC_LFSR, + pic16_popGetLit2(1, pic16_newpCodeOpRegFromStr("_stack_end")))); + pic16_addpCode2pBlock(pb, pic16_newpCode(POC_LFSR, + pic16_popGetLit2(2, pic16_newpCodeOpRegFromStr("_stack_end")))); + } + + /* put in the call to main */ + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_CALL,pic16_newpCodeOp("_main",PO_STR))); + + + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP(";\treturn from main will return to caller\n")); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_RETURN,NULL)); + } + + /* At this point we've got all the code in the form of pCode structures */ + /* Now it needs to be rearranged into the order it should be placed in the */ + /* code space */ + + pic16_movepBlock2Head('P'); // Last + pic16_movepBlock2Head(code->dbName); + pic16_movepBlock2Head('X'); + pic16_movepBlock2Head(statsg->dbName); // First + + /* print the global struct definitions */ + + /* PENDING: this isnt the best place but it will do */ + if (port->general.glue_up_main) { + /* create the interrupt vector table */ + pic16createInterruptVect (&vBuf); + } + + /* emit code for the all the variables declared */ + pic16emitMaps (); + + /* do the overlay segments */ + pic16emitOverlay(&ovrBuf); + pic16_AnalyzepCode('*'); + +#if 1 + if(pic16_options.dumpcalltree) { + FILE *cFile; + + SNPRINTF(buffer, sizeof(buffer), "%s.calltree", dstFileName); + cFile = fopen(buffer, "w"); + pic16_printCallTree( cFile ); + fclose(cFile); + } +#endif + + pic16_InlinepCode(); + pic16_AnalyzepCode('*'); + + if(pic16_debug_verbose) + pic16_pcode_test(); + + /* now put it all together into the assembler file */ + /* create the assembler file name */ + if((noAssemble || options.c1mode) && fullDstFileName) { + SNPRINTF(buffer, sizeof(buffer), "%s", fullDstFileName); + } else { + SNPRINTF(buffer, sizeof(buffer), "%s.asm", dstFileName); + } + + if(!(asmFile = fopen (buffer, "w"))) { + werror (E_FILE_OPEN_ERR, buffer); + exit (1); + } + + /* initial comments */ + pic16initialComments (asmFile); + + /* print module name */ + if(options.debug) + fprintf(asmFile, "\t.file\t\"%s\"\n", fullSrcFileName); + + /* Let the port generate any global directives, etc. */ + if(port->genAssemblerPreamble) { + port->genAssemblerPreamble(asmFile); + } + + /* Put all variables into a cblock */ + pic16_AnalyzeBanking(); + +#if 0 + if(pic16_options.opt_flags & OF_LR_SUPPORT) { + pic16_OptimizeLocalRegs(); + } +#endif + + /* remove redundant BANKSELs -- added by RN 2005-01-17 */ + if(pic16_options.opt_banksel > 1) { + pic16_OptimizeBanksel(); + } + + /* turn GOTOs into BRAs -- added by RN 2004-11-16 */ + if(!(pic16_options.opt_flags & OF_NO_OPTIMIZE_GOTO)) { + pic16_OptimizeJumps(); + } + + /* print the global variables in this module */ + pic16printPublics (asmFile); + + /* print the extern variables to this module */ + pic16_printExterns(asmFile); + + pic16_writeUsedRegs(asmFile); + +#if 0 + /* no xdata in pic */ + /* if external stack then reserve space of it */ + if(mainf && IFFUNC_HASBODY(mainf->type) && options.useXstack ) { + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; external stack\n"); + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile,";\t.area XSEG (XDATA)\n"); /* MOF */ + fprintf (asmFile,";\t.ds 256\n"); + } +#endif + +#if 0 + /* no xdata in pic */ + /* copy xtern ram data */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; external ram data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&xdata->oBuf, asmFile); +#endif + +#if 0 + /* copy the bit segment */ + fprintf (asmFile, "%s", iComments2); + fprintf (asmFile, "; bit data\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&bit->oBuf, asmFile); +#endif + + /* copy the interrupt vector table */ + if(mainf && IFFUNC_HASBODY(mainf->type)) { + fprintf (asmFile, "\n%s", iComments2); + fprintf (asmFile, "; interrupt vector\n"); + fprintf (asmFile, "%s", iComments2); + dbuf_write_and_destroy (&vBuf, asmFile); + } + + /* copy global & static initialisations */ + fprintf (asmFile, "\n%s", iComments2); + fprintf (asmFile, "; global & static initialisations\n"); + fprintf (asmFile, "%s", iComments2); + + if(pic16_debug_verbose) + fprintf(asmFile, "; A code from now on!\n"); + + pic16_copypCode(asmFile, 'A'); + + if(pic16_options.no_crt) { + if(mainf && IFFUNC_HASBODY(mainf->type)) { + fprintf(asmFile, "\tcode\n"); + fprintf(asmFile,"__sdcc_gsinit_startup:\n"); + } + } + +// dbuf_write_and_destroy (&code->oBuf, stderr); + + fprintf(asmFile, "; I code from now on!\n"); + pic16_copypCode(asmFile, 'I'); + + if(pic16_debug_verbose) + fprintf(asmFile, "; dbName from now on!\n"); + + pic16_copypCode(asmFile, statsg->dbName); + + if(pic16_options.no_crt) { + if (port->general.glue_up_main && mainf && IFFUNC_HASBODY(mainf->type)) { + fprintf (asmFile,"\tgoto\t__sdcc_program_startup\n"); + } + } + + if(pic16_debug_verbose) + fprintf(asmFile, "; X code from now on!\n"); + + pic16_copypCode(asmFile, 'X'); + + if(pic16_debug_verbose) + fprintf(asmFile, "; M code from now on!\n"); + + pic16_copypCode(asmFile, 'M'); + + pic16_copypCode(asmFile, code->dbName); + + pic16_copypCode(asmFile, 'P'); + + emitStatistics(asmFile); + + fprintf (asmFile,"\tend\n"); + fclose (asmFile); +} diff --git a/src/pic16/glue.h b/src/pic16/glue.h new file mode 100644 index 0000000..26b850d --- /dev/null +++ b/src/pic16/glue.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + + SDCCglue.h - glues everything we have done together into one file. + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef PIC16_GLUE_H +#define PIC16_GLUE_H + +#include "SDCCset.h" + +extern set *pic16_localFunctions; + +extern void pic16_pCodeInitRegisters(void); +extern void pic16glue (void); +extern unsigned int pic16aopLiteral (value *val, int offset); +extern int pic16_stringInSet(const char *str, set **world, int autoAdd); + +#endif diff --git a/src/pic16/graph.c b/src/pic16/graph.c new file mode 100644 index 0000000..be59017 --- /dev/null +++ b/src/pic16/graph.c @@ -0,0 +1,180 @@ +/*------------------------------------------------------------------------- + + graph.c - implementation of general graphs + + Written By - Raphael Neider (2005) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* $Id: graph.c 9490 2016-01-31 11:44:32Z molnarkaroly $ */ + +#include "graph.h" + +/* === helpers ====================================================== */ + +int default_compare (void *data1, void *data2) +{ + return (data1 == data2); +} + +/* === GraphEdge ==================================================== */ + +GraphEdge *newGEdge (GraphNode *src, GraphNode *dest, unsigned int weight) { + GraphEdge *edge = (GraphEdge *)Safe_alloc(sizeof(GraphEdge)); + edge->src = src; + edge->node = dest; + edge->weight = weight; + return edge; +} + +GraphEdge *deleteGEdge (GraphEdge *edge) { + GraphEdge *head; + // remove edge from list + if (edge->next) edge->next->prev = edge->prev; + if (edge->prev) edge->prev->next = edge->next; + + if (edge->prev) head = edge->prev; else head = edge->next; + Safe_free (edge); + return head; +} + +/* === GraphNode ==================================================== */ + +GraphNode *newGNode (void *data, hash_t hash) { + GraphNode *node = (GraphNode*)Safe_alloc(sizeof(GraphNode)); + node->data = data; + node->hash = hash; + return node; +} + +GraphNode * deleteGNode (GraphNode *node) { + GraphNode *head; + + if (!node) return NULL; + + // delete all edges + while (node->edge) { + node->edge = deleteGEdge (node->edge); + } // while + + // remove node from list + if (node->next) node->next->prev = node->prev; + if (node->prev) node->prev->next = node->next; + + if (node->prev) head = node->prev; else head = node->next; + Safe_free (node); + return head; +} + +GraphEdge *addGEdge (GraphNode *from, GraphNode *to, unsigned int weight) { + GraphEdge *edge = getGEdge (from, to); + if (edge == NULL) { + edge = newGEdge (from, to, weight); + // insert edge into list + if (from->edge) from->edge->prev = edge; + edge->next = from->edge; + from->edge = edge; + } else + edge->weight += weight; + + assert (edge->src == from && edge->node == to); + return edge; +} + +void addGEdge2 (GraphNode *from, GraphNode *to, unsigned int weight, unsigned int weight_back) { + addGEdge (from, to, weight); + addGEdge (to, from, weight_back); +} + +void remGEdge (GraphNode *from, GraphNode *to) { + GraphEdge *curr = from->edge; + while (curr && curr->node != to) curr = curr->next; + + if (!curr) return; + + if (from->edge == curr) + from->edge = deleteGEdge (curr); + else + deleteGEdge (curr); +} + +GraphEdge *getGEdge (GraphNode *from, GraphNode *to) { + GraphEdge *curr = from->edge; + while (curr && curr->node != to) { + assert (curr->src == from); + curr = curr->next; + } + return curr; +} + +/* === Graph ======================================================== */ + +Graph *newGraph (Graph_compareData *compare) { + Graph *graph = (Graph*)Safe_alloc(sizeof(Graph)); + graph->compare = compare; + if (!compare) graph->compare = default_compare; + + return graph; +} + +void deleteGraph (Graph *graph) { + // remove all nodes + while (graph->node) { + graph->node = deleteGNode (graph->node); + } // while + + Safe_free (graph); +} + +GraphNode *addGNode (Graph *graph, void *data, hash_t hash) { + GraphNode *node = newGNode (data, hash); + if (graph->node) graph->node->prev = node; + node->next = graph->node; + graph->node = node; + return node; +} + +void remGNode (Graph *graph, void *data, hash_t hash) { + GraphNode *curr = graph->node; + while (curr && ((curr->hash != hash) || (!graph->compare(curr->data, data)))) { + curr = curr->next; + } // while + + if (!curr) return; + + if (graph->node == curr) + graph->node = deleteGNode (curr); + else + deleteGNode (curr); +} + +GraphNode *getGNode (Graph *graph, void *data, hash_t hash) { + GraphNode *curr = graph->node; + while (curr && ((curr->hash != hash) || (!graph->compare(curr->data, data)))) { + curr = curr->next; + } // while + + return curr; +} + +GraphNode *getOrAddGNode (Graph *graph, void *data, hash_t hash) { + GraphNode *curr = getGNode (graph, data, hash); + if (!curr) + curr = addGNode (graph, data, hash); + + assert (curr != NULL); + return curr; +} diff --git a/src/pic16/graph.h b/src/pic16/graph.h new file mode 100644 index 0000000..73ccede --- /dev/null +++ b/src/pic16/graph.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + + graph.h - header file for graph.c + + Written By - Raphael Neider (2005) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +/* $Id: graph.h 4781 2007-04-29 20:33:44Z borutr $ */ + +#ifndef __GRAPH_H__ +#define __GRAPH_H__ + +#include "../common.h" + +typedef unsigned int hash_t; + +struct GraphNode; + +typedef struct GraphEdge { + struct GraphNode *src; // starting node of this edge + struct GraphNode *node; // other end of this edge + unsigned int weight; // weight assigned to this edge + struct GraphEdge *prev; // link to previous edge + struct GraphEdge *next; // link to next edge +} GraphEdge; + +typedef struct GraphNode { + void *data; // data stored in this node + hash_t hash; // hash value for "data" + + GraphEdge *edge; // first edge leaving this node + struct GraphNode *prev; // link to previous node + struct GraphNode *next; // link to next edge +} GraphNode; + +// compare function, returns 0 for different items and 1 for equal items +typedef int Graph_compareData(void *item1, void *item2); + +typedef struct { + GraphNode *node; // first node in this graph + Graph_compareData *compare; // function used to compare two data items +} Graph; + +/* Create a new edge from src to dest. + * Returns a pointer to the new edge. */ +GraphEdge *newGEdge (GraphNode *src, GraphNode *dest, unsigned int weight); + +/* Delete an edge and remove it from the containing list. + * Returns a pointer to the previous edge or (if there is NULL) to its successor. */ +GraphEdge *deleteGEdge (GraphEdge *edge); + + + +/* Create a new node. */ +GraphNode *newGNode (void *data, hash_t hash); + +/* Delete a node and all its edges. this also removes the node + * from its containing list. + * Returns the previous node in the list or (if there is NULL) + * its successor. */ +GraphNode *deleteGNode (GraphNode *node); + +/* Adds an edge with the given weight. If the edge already exists, + * its weight its increased instead! */ +GraphEdge *addGEdge (GraphNode *from, GraphNode *to, unsigned int weight); + +/* Adds the edges (from,to) and (to,from) with the specified weights. */ +void addGEdge2 (GraphNode *from, GraphNode *to, unsigned int weight, unsigned int weight_back); + +/* Remove an edge from the node. This deletes the edge and updates the + * list of edges attached to the "from" node. */ +void remGEdge (GraphNode *from, GraphNode *to); + +/* Returns the edge (from,to) or NULL if no such edge exists. */ +GraphEdge *getGEdge (GraphNode *from, GraphNode *to); + + + +/* Create a new graph which uses the given compare function to test + * its nodes' data for equality. */ +Graph *newGraph (Graph_compareData *compare); + +/* Delete a graph, all its contained nodes and their edges. */ +void deleteGraph (Graph *graph); + +/* Add a node to the graph. */ +GraphNode *addGNode (Graph *graph, void *data, hash_t hash); + +/* Remove a node from the graph. This also deletes the node and all + * its associated (outbound) edges. */ +void remGNode (Graph *graph, void *data, hash_t hash); + +/* Returns the specified node or NULL if no such node exists. */ +GraphNode *getGNode (Graph *graph, void *data, hash_t hash); + +/* Returns the specified node (after creating it if neccessary). */ +GraphNode *getOrAddGNode (Graph *graph, void *data, hash_t hash); + +#endif diff --git a/src/pic16/main.c b/src/pic16/main.c new file mode 100644 index 0000000..61d9cfb --- /dev/null +++ b/src/pic16/main.c @@ -0,0 +1,1428 @@ +/*------------------------------------------------------------------------- + + main.c - pic16 specific general functions. + + Written by - Scott Dattalo scott@dattalo.com + Ported to PIC16 by - Martin Dubuc m.debuc@rogers.com + + Note that mlh prepended _pic16_ on the static functions. Makes + it easier to set a breakpoint using the debugger. + + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "dbuf_string.h" + +#include "main.h" +#include "ralloc.h" +#include "device.h" +#include "glue.h" +#include "pcode.h" +#include "SDCCargs.h" +#include "dbuf_string.h" + + +static char _defaultRules[] = +{ +#include "peeph.rul" +}; + +/* list of key words used by pic16 */ +static char *_pic16_keywords[] = +{ + "at", + "code", + "critical", + "register", + "data", + "far", + "interrupt", + "near", + //"pdata", + "reentrant", + "sfr", + "sfr16", + "using", + "_data", + "_code", + "_generic", + "_near", + //"_pdata", + "_naked", + "shadowregs", + "wparam", + "prodlp", + "prodhp", + "fsr0lp", + "fixed16x16", + +// "bit", +// "idata", +// "sbit", +// "xdata", +// "_xdata", +// "_idata", + NULL +}; + + +pic16_sectioninfo_t pic16_sectioninfo; +int has_xinst_config = 0; + +static int regParmFlg = 0; /* determine if we can register a parameter */ + +pic16_options_t pic16_options; +pic16_config_options_t *pic16_config_options; + +extern set *includeDirsSet; +extern set *dataDirsSet; +extern set *libFilesSet; + +/* Also defined in gen.h, but the #include is commented out */ +/* for an unknowned reason. - EEP */ +void pic16_emitDebuggerSymbol (const char *); + +extern void pic16_emitConfigRegs (FILE *of); +extern void pic16_emitIDRegs (FILE *of); + + +static void +_pic16_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); + pic16_pCodeInitRegisters(); + maxInterrupts = 2; + memset(&pic16_options, 0, sizeof(pic16_options)); +} + +static void +_pic16_reset_regparm (struct sym_link *funcType) +{ + regParmFlg = 0; +} + +static int +_pic16_regparm (sym_link * l, bool reentrant) +{ + /* force all parameters via SEND/RECEIVE */ + if(0 /*pic16_options.ip_stack*/) { + /* for this processor it is simple + * can pass only the first parameter in a register */ + if(regParmFlg)return 0; + regParmFlg++; + return 1; //regParmFlg; + } else { + /* otherwise pass all arguments in registers via SEND/RECEIVE */ + regParmFlg++;// = 1; + return regParmFlg; + } +} + + +int initsfpnt=0; /* set to 1 if source provides a pragma for stack + * so glue() later emits code to initialize stack/frame pointers */ +set *absSymSet; + +set *sectNames=NULL; /* list of section listed in pragma directives */ +set *sectSyms=NULL; /* list of symbols set in a specific section */ +set *wparamList=NULL; + +#if 0 +/* This is an experimental code for #pragma inline + and is temporarily disabled for 2.5.0 release */ +set *asmInlineMap=NULL; +#endif /* 0 */ + +struct { + unsigned ignore: 1; + unsigned want_libc: 1; + unsigned want_libm: 1; + unsigned want_libio: 1; + unsigned want_libdebug: 1; +} libflags = { 0, 0, 0, 0, 0 }; + + +enum { + P_STACK = 1, + P_CODE, + P_UDATA, + P_LIBRARY, + P_CONFIG +}; + +static int +do_pragma (int id, const char *name, const char *cp) +{ + struct pragma_token_s token; + int err = 0; + int processed = 1; + + init_pragma_token(&token); + + switch (id) + { + /* #pragma stack [stack-position] [stack-len] */ + case P_STACK: + { + unsigned int stackPos, stackLen; + reg_info *reg; + symbol *sym; + + cp = get_pragma_token (cp, &token); + if (TOKEN_INT != token.type) + { + err = 1; + break; + } + stackPos = token.val.int_val; + + cp = get_pragma_token (cp, &token); + if (TOKEN_INT != token.type) + { + err = 1; + break; + } + stackLen = token.val.int_val; + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + if (stackLen < 1) { + stackLen = 64; + fprintf (stderr, "%s:%d: warning: setting stack to default size %d (0x%04x)\n", + filename, lineno, stackLen, stackLen); + } + + /* check sanity of stack */ + if ((stackPos >> 8) != ((stackPos + stackLen - 1) >> 8)) { + fprintf (stderr, "%s:%u: warning: stack [0x%03X,0x%03X] crosses memory bank boundaries (not fully tested)\n", + filename, lineno, stackPos, stackPos + stackLen - 1); + } + + if (pic16) { + if (stackPos < pic16->acsSplitOfs) { + fprintf (stderr, "%s:%u: warning: stack [0x%03X, 0x%03X] intersects with the access bank [0x000,0x%03x] -- this is highly discouraged!\n", + filename, lineno, stackPos, stackPos + stackLen - 1, pic16->acsSplitOfs); + } + + if (stackPos+stackLen > 0xF00 + pic16->acsSplitOfs) { + fprintf (stderr, "%s:%u: warning: stack [0x%03X,0x%03X] intersects with special function registers [0x%03X,0xFFF]-- this is highly discouraged!\n", + filename, lineno, stackPos, stackPos + stackLen - 1, 0xF00 + pic16->acsSplitOfs); + } + + if (stackPos+stackLen > pic16->RAMsize) { + fprintf (stderr, "%s:%u: error: stack [0x%03X,0x%03X] is placed outside available memory [0x000,0x%03X]!\n", + filename, lineno, stackPos, stackPos + stackLen - 1, pic16->RAMsize-1); + err = 1; + break; + } + } + + reg = newReg (REG_SFR, PO_SFR_REGISTER, stackPos, "_stack", stackLen-1, 0, NULL); + addSet (&pic16_fix_udata, reg); + + reg = newReg (REG_SFR, PO_SFR_REGISTER, stackPos + stackLen-1, "_stack_end", 1, 0, NULL); + addSet (&pic16_fix_udata, reg); + + sym = newSymbol ("stack", 0); + SNPRINTF(sym->rname, sizeof(sym->rname), "_%s", sym->name); + addSet (&publics, sym); + + sym = newSymbol ("stack_end", 0); + SNPRINTF(sym->rname, sizeof(sym->rname), "_%s", sym->name); + addSet (&publics, sym); + + initsfpnt = 1; // force glue() to initialize stack/frame pointers */ + } + break; + + /* #pragma code [symbol] [location] */ + case P_CODE: + { + absSym *absS; + + cp = get_pragma_token (cp, &token); + if (TOKEN_STR != token.type) + goto code_err; + + absS = Safe_alloc(sizeof(absSym)); + SNPRINTF(absS->name, sizeof(absS->name), "_%s", get_pragma_string(&token)); + + cp = get_pragma_token (cp, &token); + if (TOKEN_INT != token.type) + { + code_err: + //fprintf (stderr, "%s:%d: #pragma code [symbol] [location] -- symbol or location missing\n", filename, lineno); + err = 1; + break; + } + absS->address = token.val.int_val; + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + if ((absS->address % 2) != 0) { + absS->address--; + fprintf (stderr, "%s:%d: warning: code memory locations should be word aligned, will locate to 0x%06x instead\n", + filename, lineno, absS->address); + } + + addSet (&absSymSet, absS); +// fprintf(stderr, "%s:%d symbol %s will be placed in location 0x%06x in code memory\n", +// __FILE__, __LINE__, symname, absS->address); + } + break; + + /* #pragma udata [section-name] [symbol] */ + case P_UDATA: + { + char *sectname; + const char *symname; + symbol *nsym; + sectSym *ssym; + sectName *snam; + int found = 0; + + cp = get_pragma_token (cp, &token); + if (TOKEN_STR == token.type) + sectname = Safe_strdup (get_pragma_string (&token)); + else + { + err = 1; + break; + } + + cp = get_pragma_token (cp, &token); + if (TOKEN_STR == token.type) + symname = get_pragma_string (&token); + else + { + //fprintf (stderr, "%s:%d: #pragma udata [section-name] [symbol] -- section-name or symbol missing!\n", filename, lineno); + err = 1; + symname = NULL; + } + + while (symname) + { + size_t len = strlen(symname) + 2; + + ssym = Safe_alloc(sizeof(sectSym)); + ssym->name = Safe_alloc(len); + SNPRINTF(ssym->name, len, "%s%s", port->fun_prefix, symname); + ssym->reg = NULL; + + addSet (§Syms, ssym); + + nsym = newSymbol ((char *)symname, 0); + strcpy(nsym->rname, ssym->name); + +#if 0 + checkAddSym (&publics, nsym); +#endif + + found = 0; + for (snam = setFirstItem (sectNames); snam; snam = setNextItem (sectNames)) + { + if (!strcmp (sectname, snam->name)) + { + found=1; + break; + } + } + + if(!found) + { + snam = Safe_alloc(sizeof(sectName)); + snam->name = Safe_strdup (sectname); + snam->regsSet = NULL; + + addSet (§Names, snam); + } + + ssym->section = snam; + +#if 0 + fprintf (stderr, "%s:%d placing symbol %s at section %s (%p)\n", __FILE__, __LINE__, + ssym->name, snam->name, snam); +#endif + + cp = get_pragma_token (cp, &token); + if (TOKEN_STR == token.type) + symname = get_pragma_string (&token); + else if (TOKEN_EOL == token.type) + symname = NULL; + else + { + err = 1; + symname = NULL; + } + } + + Safe_free (sectname); + } + break; + + /* #pragma library library_module */ + case P_LIBRARY: + { + const char *lmodule; + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + lmodule = get_pragma_string (&token); + + /* lmodule can be: + * c link the C library + * math link the math library + * io link the IO library + * debug link the debug libary + * anything else, will link as-is */ + + if(!strcmp (lmodule, "c")) + libflags.want_libc = 1; + else if(!strcmp (lmodule, "math")) + libflags.want_libm = 1; + else if(!strcmp (lmodule, "io")) + libflags.want_libio = 1; + else if(!strcmp (lmodule, "debug")) + libflags.want_libdebug = 1; + else if(!strcmp (lmodule, "ignore")) + libflags.ignore = 1; + else + { + if(!libflags.ignore) + { + fprintf (stderr, "link library %s\n", lmodule); + addSetHead (&libFilesSet, (char *)lmodule); + } + } + } + else + { + err = 1; + break; + } + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + } + break; + + case P_CONFIG: + { + const char *begin; + struct dbuf_s dbuf; + bool first = TRUE; + + token.type = TOKEN_EOL; /* just to make the final error test happy */ + + dbuf_init (&dbuf, 128); + dbuf_append_str (&dbuf, "CONFIG\t"); + + do + { + int isXINST = 0; + + if (first) + first = FALSE; + else + { + if (',' == *cp) + ++cp; + else + goto error; + + dbuf_append_char (&dbuf, ','); + } + + + while (isspace (*cp)) + ++cp; + + if (*cp == '_' || isalpha (*cp)) + { + begin = cp++; + while (*cp == '_' || isalnum (*cp)) + ++cp; + if ((5 == (cp - begin)) && (0 == strncmp("XINST", begin, 5))) + { + /* Keep warning if we have XINST=ON ... */ + isXINST = 1; + } // if + dbuf_append (&dbuf, begin, cp - begin); + } + else + goto error; + + while (isspace (*cp)) + ++cp; + + if ('=' == *cp) + dbuf_append_char (&dbuf, *cp++); + else + goto error; + + while (isspace (*cp)) + ++cp; + + if (*cp == '_' || isalnum (*cp)) + { + begin = cp++; + while (*cp == '_' || isalnum (*cp)) + ++cp; + if (isXINST && (3 == cp - begin) && (0 == strncmp("OFF", begin, 3))) + { + /* Suppress warning in glue.c if #pragma config XINST=OFF is present. */ + has_xinst_config = 1; + } // if + dbuf_append (&dbuf, begin, cp - begin); + } + else + goto error; + + while (isspace (*cp)) + ++cp; + } + while ('\0' != *cp); + + /* append to the config options list */ + if (!pic16_config_options) + { + pic16_config_options = malloc (sizeof (pic16_config_options_t)); + memset (pic16_config_options, 0, sizeof (pic16_config_options_t)); + pic16_config_options->config_str = dbuf_detach_c_str (&dbuf); + } + else + { + pic16_config_options_t *p; + + for (p = pic16_config_options; p->next; p = p->next) + ; + p->next = malloc (sizeof (pic16_config_options_t)); + memset (p->next, 0, sizeof (pic16_config_options_t)); + p->next->config_str = dbuf_detach_c_str (&dbuf); + } + break; + + error: + dbuf_destroy(&dbuf); + err = 1; + } + break; + +#if 0 + /* This is an experimental code for #pragma inline + and is temporarily disabled for 2.5.0 release */ + case P_INLINE: + { + char *tmp = strtok ((char *)NULL, WHITECOMMA); + + while (tmp) + { + addSet (&asmInlineMap, Safe_strdup ( tmp )); + tmp = strtok ((char *)NULL, WHITECOMMA); + } + + { + char *s; + + for (s = setFirstItem (asmInlineMap); s ; s = setNextItem (asmInlineMap)) + { + debugf ("inline asm: `%s'\n", s); + } + } + } + break; +#endif /* 0 */ + + default: + processed = 0; + break; + } + + get_pragma_token (cp, &token); + + if (1 == err || token.type != TOKEN_EOL) + werror (W_BAD_PRAGMA_ARGUMENTS, name); + + free_pragma_token (&token); + return processed; +} + +static struct pragma_s pragma_tbl[] = { + { "stack", P_STACK, 0, do_pragma }, + { "code", P_CODE, 0, do_pragma }, + { "udata", P_UDATA, 0, do_pragma }, + { "library", P_LIBRARY, 0, do_pragma }, + { "config", P_CONFIG, 0, do_pragma }, +/*{ "inline", P_INLINE, 0, do_pragma }, */ + { NULL, 0, 0, NULL }, + }; + +static int +_process_pragma (const char *s) +{ + return process_pragma_tbl(pragma_tbl, s); +} + +#define REP_UDATA "--preplace-udata-with=" + +#define STACK_MODEL "--pstack-model=" +#define OPT_BANKSEL "--obanksel=" + +#define ALT_ASM "--asm=" +#define ALT_LINK "--link=" + +#define IVT_LOC "--ivt-loc=" +#define NO_DEFLIBS "--nodefaultlibs" +#define MPLAB_COMPAT "--mplab-comp" + +#define USE_CRT "--use-crt=" + +#define OFMSG_LRSUPPORT "--flr-support" + +#define NO_OPTIMIZE_GOTO "--no-optimize-goto" +#define OPTIMIZE_CMP "--optimize-cmp" +#define OPTIMIZE_DF "--optimize-df" + +char *alt_asm = NULL; +char *alt_link = NULL; + +int pic16_mplab_comp = 0; +extern int pic16_debug_verbose; +extern int pic16_ralloc_debug; +extern int pic16_pcode_verbose; + +int pic16_enable_peeps = 0; + +OPTION pic16_optionsTable[]= { + /* code generation options */ + { 0, STACK_MODEL, NULL, "use stack model 'small' (default) or 'large'"}, +#if XINST + { 'y', "--extended", &pic16_options.xinst, "enable Extended Instruction Set/Literal Offset Addressing mode"}, +#endif + { 0, "--pno-banksel", &pic16_options.no_banksel, "do not generate BANKSEL assembler directives"}, + + /* optimization options */ + { 0, OPT_BANKSEL, &pic16_options.opt_banksel, "set banksel optimization level (default=0 no)", CLAT_INTEGER }, + { 0, "--denable-peeps", &pic16_enable_peeps, "explicit enable of peepholes"}, + { 0, NO_OPTIMIZE_GOTO, NULL, "do NOT use (conditional) BRA instead of GOTO"}, + { 0, OPTIMIZE_CMP, NULL, "try to optimize some compares"}, + { 0, OPTIMIZE_DF, NULL, "thoroughly analyze data flow (memory and time intensive!)"}, + + /* assembling options */ + { 0, ALT_ASM, &alt_asm, "Use alternative assembler", CLAT_STRING}, + { 0, MPLAB_COMPAT, &pic16_mplab_comp, "enable compatibility mode for MPLAB utilities (MPASM/MPLINK)"}, + + /* linking options */ + { 0, ALT_LINK, &alt_link, "Use alternative linker", CLAT_STRING }, + { 0, REP_UDATA, &pic16_sectioninfo.at_udata, "Place udata variables at another section: udata_acs, udata_ovr, udata_shr", CLAT_STRING }, + { 0, IVT_LOC, NULL, "Set address of interrupt vector table."}, + { 0, NO_DEFLIBS, &pic16_options.nodefaultlibs, "do not link default libraries when linking"}, + { 0, USE_CRT, NULL, "use run-time initialization module"}, + { 0, "--no-crt", &pic16_options.no_crt, "do not link any default run-time initialization module"}, + + /* debugging options */ + { 0, "--debug-xtra", &pic16_debug_verbose, "show more debug info in assembly output"}, + { 0, "--debug-ralloc", &pic16_ralloc_debug, "dump register allocator debug file *.d"}, + { 0, "--pcode-verbose", &pic16_pcode_verbose, "dump pcode related info"}, + { 0, "--calltree", &pic16_options.dumpcalltree, "dump call tree in .calltree file"}, + { 0, "--gstack", &pic16_options.gstack, "trace stack pointer push/pop to overflow"}, + { 0, "--no-warn-non-free", &pic16_options.no_warn_non_free, "suppress warning on absent --use-non-free option" }, + { 0, NULL, NULL, NULL} +}; + + +#define ISOPT(str) !strncmp(argv[ *i ], str, strlen(str) ) + +static bool +_pic16_parseOptions (int *pargc, char **argv, int *i) +{ + int j=0; + char *stkmodel; + + /* TODO: allow port-specific command line options to specify + * segment names here. + */ + + /* check for arguments that have associated an integer variable */ + while(pic16_optionsTable[j].pparameter) { + if(ISOPT( pic16_optionsTable[j].longOpt )) { + (*(int *)pic16_optionsTable[j].pparameter)++; + return TRUE; + } + j++; + } + + if(ISOPT(STACK_MODEL)) { + stkmodel = getStringArg(STACK_MODEL, argv, i, *pargc); + if(!STRCASECMP(stkmodel, "small"))pic16_options.stack_model = 0; + else if(!STRCASECMP(stkmodel, "large"))pic16_options.stack_model = 1; + else { + fprintf(stderr, "Unknown stack model: %s", stkmodel); + exit(EXIT_FAILURE); + } + return TRUE; + } + + if(ISOPT(IVT_LOC)) { + pic16_options.ivt_loc = getIntArg(IVT_LOC, argv, i, *pargc); + fprintf(stderr, "%s:%d setting interrupt vector addresses 0x%x\n", __FILE__, __LINE__, pic16_options.ivt_loc); + return TRUE; + } + + if(ISOPT(USE_CRT)) { + pic16_options.no_crt = 0; + pic16_options.crt_name = Safe_strdup( getStringArg(USE_CRT, argv, i, *pargc) ); + + return TRUE; + } + +#if 0 + if(ISOPT(OFMSG_LRSUPPORT)) { + pic16_options.opt_flags |= OF_LR_SUPPORT; + return TRUE; + } +#endif + + if (ISOPT(NO_OPTIMIZE_GOTO)) { + pic16_options.opt_flags |= OF_NO_OPTIMIZE_GOTO; + return TRUE; + } + + if(ISOPT(OPTIMIZE_CMP)) { + pic16_options.opt_flags |= OF_OPTIMIZE_CMP; + return TRUE; + } + + if (ISOPT(OPTIMIZE_DF)) { + pic16_options.opt_flags |= OF_OPTIMIZE_DF; + return TRUE; + } + + + return FALSE; +} + +extern void pic16_init_pic(const char *name); + +static void +_pic16_initPaths (void) +{ + pic16_init_pic(port->processor); +} + +extern set *linkOptionsSet; +char *msprintf(hTab *pvals, const char *pformat, ...); + +/* forward declarations */ +extern const char *pic16_linkCmd[]; +extern const char *pic16_asmCmd[]; +extern set *asmOptionsSet; + +/* custom function to link objects */ +static void +_pic16_linkEdit (void) +{ + /* + * link command format: + * {linker} {incdirs} {lflags} -o {outfile} {spec_ofiles} {ofiles} {libs} + * + */ +#define LFRM "{linker} {incdirs} {lflags} -w -r -o {outfile} {user_ofile} {ofiles} {spec_ofiles} {libs}" + hTab *linkValues = NULL; + char *lcmd; + set *tSet = NULL; + int ret; + + shash_add (&linkValues, "linker", pic16_linkCmd[0]); + + mergeSets (&tSet, libPathsSet); + mergeSets (&tSet, libDirsSet); + + shash_add (&linkValues, "incdirs", joinStrSet (processStrSet(tSet, "-I", NULL, shell_escape))); + shash_add (&linkValues, "lflags", joinStrSet (linkOptionsSet)); + + { + char *s = shell_escape (fullDstFileName ? fullDstFileName : dstFileName); + + shash_add (&linkValues, "outfile", s); + Safe_free (s); + } + + if (fullSrcFileName) + { + struct dbuf_s dbuf; + char *s; + + dbuf_init (&dbuf, 128); + + dbuf_append_str (&dbuf, fullDstFileName ? fullDstFileName : dstFileName); + dbuf_append (&dbuf, ".o", 2); + s = shell_escape (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + shash_add (&linkValues, "user_ofile", s); + Safe_free (s); + } + + if (!pic16_options.no_crt) + { + char *s = shell_escape (pic16_options.crt_name); + + shash_add (&linkValues, "spec_ofiles", s); + Safe_free (s); + } + + shash_add (&linkValues, "ofiles", joinStrSet (processStrSet (relFilesSet, NULL, NULL, shell_escape))); + + if (!libflags.ignore) + { + if (libflags.want_libc) + addSet (&libFilesSet, Safe_strdup ("libc18f.lib")); + + if (libflags.want_libm) + addSet (&libFilesSet, Safe_strdup ("libm18f.lib")); + + if (libflags.want_libio) + { + /* build libio18f452.lib name */ + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + + dbuf_append (&dbuf, "libio", sizeof ("libio") - 1); + dbuf_append_str (&dbuf, pic16->name[1]); + dbuf_append (&dbuf, ".lib", sizeof (".lib") - 1); + addSet (&libFilesSet, dbuf_detach_c_str (&dbuf)); + } + + if (libflags.want_libdebug) + addSet(&libFilesSet, Safe_strdup ("libdebug.lib")); + } + + shash_add (&linkValues, "libs", joinStrSet (processStrSet (libFilesSet, NULL, NULL, shell_escape))); + + lcmd = msprintf(linkValues, LFRM); + ret = sdcc_system (lcmd); + Safe_free (lcmd); + + if (ret) + exit (1); +} + + +static void +_pic16_finaliseOptions (void) +{ + struct dbuf_s dbuf; + + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + + /* peepholes are disabled for the time being */ + options.nopeep = 1; + + /* explicit enable peepholes for testing */ + if (pic16_enable_peeps) + options.nopeep = 0; + + options.all_callee_saves = 1; // always callee saves + +#if 0 + options.float_rent = 1; + options.intlong_rent = 1; +#endif + + dbuf_init (&dbuf, 128); + + dbuf_set_length (&dbuf, 0); + dbuf_append (&dbuf, "-D__", sizeof ("-D__") - 1); + dbuf_append_str (&dbuf, pic16->name[1]); + addSet (&preArgvSet, Safe_strdup (dbuf_c_str (&dbuf))); + + { + char *upperProc, *p1, *p2; + int len; + + dbuf_set_length (&dbuf, 0); + len = strlen (port->processor); + upperProc = Safe_malloc (len); + for (p1 = port->processor, p2 = upperProc; *p1; ++p1, ++p2) + { + *p2 = toupper (*p1); + } + dbuf_append (&dbuf, "-D__SDCC_PIC", sizeof ("-D__SDCC_PIC") - 1); + dbuf_append (&dbuf, upperProc, len); + addSet (&preArgvSet, dbuf_detach_c_str (&dbuf)); + } + if (!pic16_options.nodefaultlibs) + { + /* now add the library for the device */ + dbuf_set_length (&dbuf, 0); + dbuf_printf (&dbuf, "libdev%s.lib", pic16->name[1]); /* e.g., libdev18f452.lib */ + addSet (&libFilesSet, Safe_strdup (dbuf_c_str (&dbuf))); + + /* add the internal SDCC library */ + addSet (&libFilesSet, Safe_strdup ("libsdcc.lib" )); + } + + if (alt_asm && alt_asm[0] != '\0') + { + pic16_asmCmd[0] = alt_asm; + } + + if (alt_link && alt_link[0] != '\0') + { + pic16_linkCmd[0] = alt_link; + } + + if (!pic16_options.no_crt) + { + pic16_options.omit_ivt = 1; + pic16_options.leave_reset = 0; + } + + if (options.model == MODEL_SMALL) + { + addSet (&asmOptionsSet, Safe_strdup ("-DSDCC_MODEL_SMALL")); + } + else if (options.model == MODEL_LARGE) + { + char *s, *p; + + addSet (&asmOptionsSet, Safe_strdup ("-DSDCC_MODEL_LARGE")); + + dbuf_printf (&dbuf, "-D%s -D__%s", pic16->name[2], pic16->name[1]); + s = Safe_strdup (dbuf_c_str (&dbuf)); + /* TODO: borut - why only the first 'f' is converted to upper case? + * What if there is an other letter instead 'f'? + */ + if (NULL != (p = strrchr (s, 'f'))) + *p = 'F'; + addSet (&asmOptionsSet, s); + } + + if (STACK_MODEL_LARGE) + { + addSet (&preArgvSet, Safe_strdup ("-D__STACK_MODEL_LARGE")); + addSet (&asmOptionsSet, Safe_strdup ("-D__STACK_MODEL_LARGE")); + } + else + { + addSet (&preArgvSet, Safe_strdup ("-D__STACK_MODEL_SMALL")); + addSet (&asmOptionsSet, Safe_strdup ("-D__STACK_MODEL_SMALL")); + } + + if (!pic16_options.no_warn_non_free && !options.use_non_free) + { + fprintf(stderr, + "WARNING: Command line option --use-non-free not present.\n" + " When compiling for PIC14/PIC16, please provide --use-non-free\n" + " to get access to device headers and libraries.\n" + " If you do not use these, you may provide --no-warn-non-free\n" + " to suppress this warning (not recommended).\n"); + } // if + + dbuf_destroy (&dbuf); +} + + +static void +_pic16_setDefaultOptions (void) +{ + options.stackAuto = 0; /* implicit declaration */ + /* port is not capable yet to allocate separate registers + * dedicated for passing certain parameters */ + + /* initialize to defaults section locations, names and addresses */ + pic16_sectioninfo.at_udata = "udata"; + + /* set pic16 port options to defaults */ + pic16_options.no_banksel = 0; + pic16_options.opt_banksel = 0; + pic16_options.omit_configw = 0; + pic16_options.omit_ivt = 0; + pic16_options.leave_reset = 0; + pic16_options.stack_model = 0; /* 0 for 'small', 1 for 'large' */ + pic16_options.ivt_loc = 0x000000; + pic16_options.nodefaultlibs = 0; + pic16_options.dumpcalltree = 0; + pic16_options.crt_name = "crt0iz.o"; /* the default crt to link */ + pic16_options.no_crt = 0; /* use crt by default */ + pic16_options.ip_stack = 1; /* set to 1 to enable ipop/ipush for stack */ + pic16_options.gstack = 0; + pic16_options.debgen = 0; + pic16_options.no_warn_non_free = 0; +} + +static const char * +_pic16_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + + +#if 1 +static const char * +_pic16_mangleFunctionName (const char *sz) +{ +// fprintf(stderr, "mangled function name: %s\n", sz); + + return sz; +} +#endif + + +static void +_pic16_genAssemblerPreamble (FILE * of) +{ + const char *name = pic16_processor_base_name(); + + if (!name) + { + name = "p18f452"; + fprintf(stderr,"WARNING: No Pic has been selected, defaulting to %s\n",name); + } + + fprintf (of, "\tlist\tp=%s\n", &name[1]); + fprintf (of, "\tradix\tdec\n"); + + if (pic16_mplab_comp) + { + // provide ACCESS macro used during SFR accesses + fprintf (of, "\tinclude \n", &name[1]); + } + + if(!pic16_options.omit_configw) { + pic16_emitConfigRegs(of); + fprintf(of, "\n"); + pic16_emitIDRegs(of); + } +} + +/* Generate interrupt vector table. */ +static int +_pic16_genIVT (struct dbuf_s * oBuf, symbol ** interrupts, int maxInterrupts) +{ +#if 1 + /* PIC18F family has only two interrupts, the high and the low + * priority interrupts, which reside at 0x0008 and 0x0018 respectively - VR */ + + if((!pic16_options.omit_ivt) || (pic16_options.omit_ivt && pic16_options.leave_reset)) { + dbuf_printf(oBuf, "; RESET vector\n"); + dbuf_printf(oBuf, "\tgoto\t__sdcc_gsinit_startup\n"); + } + + if(!pic16_options.omit_ivt) { + dbuf_printf(oBuf, "\tres 4\n"); + + + dbuf_printf(oBuf, "; High priority interrupt vector 0x0008\n"); + if(interrupts[1]) { + dbuf_printf(oBuf, "\tgoto\t%s\n", interrupts[1]->rname); + dbuf_printf(oBuf, "\tres\t12\n"); + } else { + dbuf_printf(oBuf, "\tretfie\n"); + dbuf_printf(oBuf, "\tres\t14\n"); + } + + dbuf_printf(oBuf, "; Low priority interrupt vector 0x0018\n"); + if(interrupts[2]) { + dbuf_printf(oBuf, "\tgoto\t%s\n", interrupts[2]->rname); + } else { + dbuf_printf(oBuf, "\tretfie\n"); + } + } +#endif + return TRUE; +} + +/* return True if the port can handle the type, + * False to convert it to function call */ +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + //fprintf(stderr,"checking for native mult for %c (size: %d)\n", ic->op, getSize(OP_SYMBOL(IC_RESULT(ic))->type)); + int symL, symR, symRes, sizeL = 0, sizeR = 0, sizeRes = 0; + + /* left/right are symbols? */ + symL = IS_SYMOP(IC_LEFT(ic)); + symR = IS_SYMOP(IC_RIGHT(ic)); + symRes = IS_SYMOP(IC_RESULT(ic)); + + /* --> then determine their sizes */ + sizeL = symL ? getSize(OP_SYM_TYPE(IC_LEFT(ic))) : 4; + sizeR = symR ? getSize(OP_SYM_TYPE(IC_RIGHT(ic))) : 4; + sizeRes = symRes ? getSize(OP_SYM_TYPE(IC_RESULT(ic))) : 4; + + /* Checks to enable native multiplication. + * PICs do not offer native division at all... + * + * Ideas: + * ( i) if result is just one byte, use native MUL + * (regardless of the operands) + * ( ii) if left and right are unsigned 8-bit operands, + * use native MUL + * (iii) if left or right is a literal in the range of [-128..256) + * and the other is an unsigned byte, use native MUL + */ + if (ic->op == '*') + { + /* use native mult for `*: x --> {u8_t, s8_t}' */ + if (sizeRes == 1) { return TRUE; } + + /* use native mult for `u8_t x u8_t --> { u16_t, s16_t }' */ + if (sizeL == 1 && symL /*&& SPEC_USIGN(OP_SYM_TYPE(IC_LEFT(ic)))*/) { + sizeL = 1; + } else { + //printf( "%s: left too large (%u) / signed (%u)\n", __FUNCTION__, sizeL, symL && !SPEC_USIGN(OP_SYM_TYPE(IC_LEFT(ic)))); + sizeL = 4; + } + if (sizeR == 1 && symR /*&& SPEC_USIGN(OP_SYM_TYPE(IC_RIGHT(ic)))*/) { + sizeR = 1; + } else { + //printf( "%s: right too large (%u) / signed (%u)\n", __FUNCTION__, sizeR, symR && !SPEC_USIGN(OP_SYM_TYPE(IC_RIGHT(ic)))); + sizeR = 4; + } + + /* also allow literals [-128..256) for left/right operands */ + if (IS_VALOP(IC_LEFT(ic))) + { + long l = (long) ulFromVal ( OP_VALUE( IC_LEFT(ic) ) ); + sizeL = 4; + //printf( "%s: val(left) = %ld\n", __FUNCTION__, l ); + if (l >= -128 && l < 256) + { + sizeL = 1; + } else { + //printf( "%s: left value %ld outside [-128..256)\n", __FUNCTION__, l ); + } + } + if (IS_VALOP( IC_RIGHT(ic) )) + { + long l = (long) ulFromVal ( OP_VALUE( IC_RIGHT(ic) ) ); + sizeR = 4; + //printf( "%s: val(right) = %ld\n", __FUNCTION__, l ); + if (l >= -128 && l < 256) + { + sizeR = 1; + } else { + //printf( "%s: right value %ld outside [-128..256)\n", __FUNCTION__, l ); + } + } + + /* use native mult iff left and right are (unsigned) 8-bit operands */ + if (sizeL == 1 && sizeR == 1) { return TRUE; } + } + + if (ic->op == '/' || ic->op == '%') + { + /* We must catch /: {u8_t,s8_t} x {u8_t,s8_t} --> {u8_t,s8_t}, + * because SDCC will call 'divuchar' even for u8_t / s8_t. + * Example: 128 / -2 becomes 128 / 254 = 0 != -64... */ + if (sizeL == 1 && sizeR == 1) return TRUE; + + /* What about literals? */ + if (IS_VALOP( IC_LEFT(ic) )) + { + long l = (long) ulFromVal ( OP_VALUE( IC_LEFT(ic) ) ); + sizeL = 4; + //printf( "%s: val(left) = %ld\n", __FUNCTION__, l ); + if (l >= -128 && l < 256) + { + sizeL = 1; + } else { + //printf( "%s: left value %ld outside [-128..256)\n", __FUNCTION__, l ); + } + } + if (IS_VALOP( IC_RIGHT(ic) )) + { + long l = (long) ulFromVal ( OP_VALUE( IC_RIGHT(ic) ) ); + sizeR = 4; + //printf( "%s: val(right) = %ld\n", __FUNCTION__, l ); + if (l >= -128 && l < 256) + { + sizeR = 1; + } else { + //printf( "%s: right value %ld outside [-128..256)\n", __FUNCTION__, l ); + } + } + if (sizeL == 1 && sizeR == 1) { return TRUE; } + } + + return FALSE; +} + + +#if 0 +/* Do CSE estimation */ +static bool cseCostEstimation (iCode *ic, iCode *pdic) +{ +// operand *result = IC_RESULT(ic); +// sym_link *result_type = operandType(result); + + + /* VR -- this is an adhoc. Put here after conversation + * with Erik Epetrich */ + + if(ic->op == '<' + || ic->op == '>' + || ic->op == EQ_OP) { + + fprintf(stderr, "%d %s\n", __LINE__, __FUNCTION__); + return 0; + } + +#if 0 + /* if it is a pointer then return ok for now */ + if (IC_RESULT(ic) && IS_PTR(result_type)) return 1; + + /* if bitwise | add & subtract then no since mcs51 is pretty good at it + so we will cse only if they are local (i.e. both ic & pdic belong to + the same basic block */ + if (IS_BITWISE_OP(ic) || ic->op == '+' || ic->op == '-') { + /* then if they are the same Basic block then ok */ + if (ic->eBBlockNum == pdic->eBBlockNum) return 1; + else return 0; + } +#endif + + /* for others it is cheaper to do the cse */ + return 1; +} +#endif + + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == RRC + || op == RLC + || op == GETABIT + /* || op == GETHBIT */ /* GETHBIT doesn't look complete for PIC */ + ) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + /* The IN_FARSPACE test is compatible with historical behaviour, */ + /* but I don't think it is applicable to PIC. If so, please feel */ + /* free to remove this test -- EEP */ + if (IN_FARSPACE(oclass)) + return 1; + + return 0; +} + +/** $1 is the input object file (PIC16 specific) // >>always the basename<<. + $2 is always the output file. + $3 -L path and -l libraries + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +const char *pic16_linkCmd[] = +{ + "gplink", "$l", "-w", "-r", "-o", "$2", "$1","$3", NULL +}; + +/** $1 is always the basename. + $2 is always the output file. + $3 varies (nothing currently) + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +const char *pic16_asmCmd[] = +{ + "gpasm", "$l", "$3", "-o", "$2", "-c", "$1.asm", NULL +}; + +/* Globals */ +PORT pic16_port = +{ + TARGET_ID_PIC16, + "pic16", + "MCU PIC16", /* Target name */ + "18f452", /* Processor */ + { + pic16glue, + TRUE, /* Emit glue around main */ + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { + pic16_asmCmd, /* assembler command and arguments */ + NULL, /* alternate macro based form */ + "-g", /* arguments for debug mode */ + NULL, /* arguments for normal mode */ + 0, /* print externs as global */ + ".asm", /* assembler file extension */ + NULL /* no do_assemble function */ + }, + { + NULL, // pic16_linkCmd, /* linker command and arguments */ + NULL, /* alternate macro based form */ + _pic16_linkEdit, //NULL, /* no do_link function */ + ".o", /* extension for object files */ + 0 /* no need for linker file */ + }, + { /* Peephole optimizer */ + _defaultRules + }, + { + /* Sizes */ + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 2, /* near ptr */ + 3, /* far ptr, far pointers (see Microchip) */ + 3, /* gptr */ + 2, /* func ptr */ + 3, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + + /* generic pointer tags */ + { + 0x00, /* far */ + 0x80, /* near */ + 0x00, /* xstack */ + 0x00 /* code */ + }, + + { + "XSEG (XDATA)", // xstack + "STACK (DATA)", // istack + "CSEG (CODE)", // code + "DSEG (DATA)", // data + "ISEG (DATA)", // idata + "PSEG (DATA)", // pdata + "XSEG (XDATA)", // xdata + "BSEG (BIT)", // bit + "RSEG (DATA)", // reg + "GSINIT (CODE)", // static + "OSEG (OVR,DATA)", // overlay + "GSFINAL (CODE)", // post static + "HOME (CODE)", // home + NULL, // xidata + NULL, // xinit + "CONST (CODE)", // const_name - const data (code or not) + "CABS (ABS,CODE)", // cabs_name - const absolute data (code or not) + "XABS (ABS,XDATA)", // xabs_name - absolute xdata + "IABS (ABS,DATA)", // iabs_name - absolute data + NULL, // name of segment for initialized variables + NULL, // name of segment for copies of initialized variables in code space + NULL, // default location for auto vars + NULL, // default location for global vars + 1, // code is read only 1=yes + 1 // No fancy alignments supported. + }, + { + NULL, /* genExtraAreaDeclaration */ + NULL /* genExatrAreaLinkOptions */ + }, + { + /* stack related information */ + -1, /* -1 stack grows downwards, +1 upwards */ + 1, /* extra overhead when calling between banks */ + 4, /* extra overhead when the function is an ISR */ + 1, /* extra overhead for a function call */ + 1, /* re-entrant space */ + 0, /* 'banked' call overhead, mild overlap with bank_overhead */ + 1 /* sp is offset by 1 from last item pushed */ + }, + { + -1, FALSE + }, + { + pic16_emitDebuggerSymbol + }, + { + 255/3, /* maxCount */ + 3, /* sizeofElement */ + /* The rest of these costs are bogus. They approximate */ + /* the behavior of src/SDCCicode.c 1.207 and earlier. */ + {4,4,4}, /* sizeofMatchJump[] */ + {0,0,0}, /* sizeofRangeCompare[] */ + 0, /* sizeofSubtract */ + 3, /* sizeofDispatch */ + }, + "_", + _pic16_init, + _pic16_parseOptions, + pic16_optionsTable, + _pic16_initPaths, + _pic16_finaliseOptions, + _pic16_setDefaultOptions, + pic16_assignRegisters, + _pic16_getRegName, + 0, + NULL, + _pic16_keywords, + _pic16_genAssemblerPreamble, + NULL, /* no genAssemblerEnd */ + _pic16_genIVT, + NULL, // _pic16_genXINIT + NULL, /* genInitStartup */ + _pic16_reset_regparm, + _pic16_regparm, + _process_pragma, /* process a pragma */ + _pic16_mangleFunctionName, /* mangles function name */ + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + FALSE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* No array initializer support. */ + 0, //cseCostEstimation, /* !!!no CSE cost estimation yet */ + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local static allowed */ + 0, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; diff --git a/src/pic16/main.h b/src/pic16/main.h new file mode 100644 index 0000000..e0fb90e --- /dev/null +++ b/src/pic16/main.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + + main.h - header file for pic16 specific general functions. + + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + Copyright (C) 2002, Martin Dubuc m.debuc@rogers.com + + Written by - Scott Dattalo scott@dattalo.com + Ported to PIC16 by - Martin Dubuc m.debuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef MAIN_INCLUDE +#define MAIN_INCLUDE + +#include "ralloc.h" + +bool x_parseOptions (char **argv, int *pargc); +void x_setDefaultOptions (void); +void x_finaliseOptions (void); + + +typedef struct { + char *at_udata; +} pic16_sectioninfo_t; + +typedef struct absSym { + char name[SDCC_SYMNAME_MAX+1]; + unsigned int address; +} absSym; + +typedef struct sectName { + char *name; + set *regsSet; +} sectName; + +typedef struct sectSym { + sectName *section; + char *name; + reg_info *reg; +} sectSym; + +extern set *absSymSet; +extern set *sectNames; +extern set *sectSyms; +extern set *wparamList; + +extern int pic16_mplab_comp; + +#endif diff --git a/src/pic16/pcode.c b/src/pic16/pcode.c new file mode 100644 index 0000000..5f45b45 --- /dev/null +++ b/src/pic16/pcode.c @@ -0,0 +1,12278 @@ +/*------------------------------------------------------------------------- + pcode.c - post code generation + + Copyright (C) 2000, Scott Dattalo scott@dattalo.com + PIC16 port: + Copyright (C) 2002, Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" + + +#include "main.h" +#include "pcode.h" +#include "pcodeflow.h" +#include "ralloc.h" +#include "device.h" + +extern char *pic16_aopGet (struct asmop *aop, int offset, bool bit16, bool dname); + +#if defined(__BORLANDC__) || defined(_MSC_VER) +#define inline +#endif + +#define DUMP_DF_GRAPHS 0 + +/****************************************************************/ +/****************************************************************/ + +static peepCommand peepCommands[] = { + + {NOTBITSKIP, "_NOTBITSKIP_"}, + {BITSKIP, "_BITSKIP_"}, + {INVERTBITSKIP, "_INVERTBITSKIP_"}, + + {-1, NULL} +}; + + + +// Eventually this will go into device dependent files: +pCodeOpReg pic16_pc_status = {{PO_STATUS, "STATUS"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_intcon = {{PO_INTCON, "INTCON"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pcl = {{PO_PCL, "PCL"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pclath = {{PO_PCLATH, "PCLATH"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_pclatu = {{PO_PCLATU, "PCLATU"}, -1, NULL,0,NULL}; // patch 14 +pCodeOpReg pic16_pc_wreg = {{PO_WREG, "WREG"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_bsr = {{PO_BSR, "BSR"}, -1, NULL,0,NULL}; + +pCodeOpReg pic16_pc_tosl = {{PO_SFR_REGISTER, "TOSL"}, -1, NULL,0,NULL}; // patch 14 +pCodeOpReg pic16_pc_tosh = {{PO_SFR_REGISTER, "TOSH"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tosu = {{PO_SFR_REGISTER, "TOSU"}, -1, NULL,0,NULL}; // patch 14 + +pCodeOpReg pic16_pc_tblptrl = {{PO_SFR_REGISTER, "TBLPTRL"}, -1, NULL,0,NULL}; // patch 15 +pCodeOpReg pic16_pc_tblptrh = {{PO_SFR_REGISTER, "TBLPTRH"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tblptru = {{PO_SFR_REGISTER, "TBLPTRU"}, -1, NULL,0,NULL}; // +pCodeOpReg pic16_pc_tablat = {{PO_SFR_REGISTER, "TABLAT"}, -1, NULL,0,NULL}; // patch 15 + +//pCodeOpReg pic16_pc_fsr0 = {{PO_FSR0, "FSR0"}, -1, NULL,0,NULL}; //deprecated ! + +pCodeOpReg pic16_pc_fsr0l = {{PO_FSR0, "FSR0L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr0h = {{PO_FSR0, "FSR0H"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr1l = {{PO_FSR0, "FSR1L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr1h = {{PO_FSR0, "FSR1H"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr2l = {{PO_FSR0, "FSR2L"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_fsr2h = {{PO_FSR0, "FSR2H"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf0 = {{PO_INDF0, "INDF0"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc0 = {{PO_INDF0, "POSTINC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec0 = {{PO_INDF0, "POSTDEC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc0 = {{PO_INDF0, "PREINC0"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw0 = {{PO_INDF0, "PLUSW0"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf1 = {{PO_INDF0, "INDF1"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc1 = {{PO_INDF0, "POSTINC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec1 = {{PO_INDF0, "POSTDEC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc1 = {{PO_INDF0, "PREINC1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw1 = {{PO_INDF0, "PLUSW1"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_indf2 = {{PO_INDF0, "INDF2"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_postinc2 = {{PO_INDF0, "POSTINC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_postdec2 = {{PO_INDF0, "POSTDEC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_preinc2 = {{PO_INDF0, "PREINC2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_plusw2 = {{PO_INDF0, "PLUSW2"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_prodl = {{PO_PRODL, "PRODL"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_prodh = {{PO_PRODH, "PRODH"}, -1, NULL, 0, NULL}; + +/* EEPROM registers */ +pCodeOpReg pic16_pc_eecon1 = {{PO_SFR_REGISTER, "EECON1"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eecon2 = {{PO_SFR_REGISTER, "EECON2"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eedata = {{PO_SFR_REGISTER, "EEDATA"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_eeadr = {{PO_SFR_REGISTER, "EEADR"}, -1, NULL, 0, NULL}; + +pCodeOpReg pic16_pc_kzero = {{PO_GPR_REGISTER, "KZ"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_wsave = {{PO_GPR_REGISTER, "WSAVE"}, -1, NULL,0,NULL}; +pCodeOpReg pic16_pc_ssave = {{PO_GPR_REGISTER, "SSAVE"}, -1, NULL,0,NULL}; + +pCodeOpReg *pic16_stackpnt_lo; +pCodeOpReg *pic16_stackpnt_hi; +pCodeOpReg *pic16_stack_postinc; +pCodeOpReg *pic16_stack_postdec; +pCodeOpReg *pic16_stack_preinc; +pCodeOpReg *pic16_stack_plusw; + +pCodeOpReg *pic16_framepnt_lo; +pCodeOpReg *pic16_framepnt_hi; +pCodeOpReg *pic16_frame_postinc; +pCodeOpReg *pic16_frame_postdec; +pCodeOpReg *pic16_frame_preinc; +pCodeOpReg *pic16_frame_plusw; + +pCodeOpReg pic16_pc_gpsimio = {{PO_GPR_REGISTER, "GPSIMIO"}, -1, NULL, 0, NULL}; +pCodeOpReg pic16_pc_gpsimio2 = {{PO_GPR_REGISTER, "GPSIMIO2"}, -1, NULL, 0, NULL}; + +static const char *OPT_TYPE_STR[] = { "begin", "end", "jumptable_begin", "jumptable_end" }; +const char *LR_TYPE_STR[] = { "entry begin", "entry end", "exit begin", "exit end" }; + + +static int mnemonics_initialized = 0; + + +static hTab *pic16MnemonicsHash = NULL; +static hTab *pic16pCodePeepCommandsHash = NULL; + +static pFile *the_pFile = NULL; +static pBlock *pb_dead_pcodes = NULL; + +/* Hardcoded flags to change the behavior of the PIC port */ +static int peepOptimizing = 1; /* run the peephole optimizer if nonzero */ +static int functionInlining = 1; /* inline functions if nonzero */ +int pic16_debug_verbose = 0; /* Set true to inundate .asm file */ + +int pic16_pcode_verbose = 0; + +//static int GpCodeSequenceNumber = 1; +static int GpcFlowSeq = 1; + +extern void pic16_RemoveUnusedRegisters(void); +extern void pic16_RegsUnMapLiveRanges(void); +extern void pic16_BuildFlowTree(pBlock *pb); +extern void pic16_pCodeRegOptimizeRegUsage(int level); + +/****************************************************************/ +/* Forward declarations */ +/****************************************************************/ + +void pic16_unlinkpCode(pCode *pc); +#if 0 +static void genericAnalyze(pCode *pc); +static void AnalyzeGOTO(pCode *pc); +static void AnalyzeSKIP(pCode *pc); +static void AnalyzeRETURN(pCode *pc); +#endif + +static void genericDestruct(pCode *pc); +static void genericPrint(FILE *of,pCode *pc); + +static void pCodePrintLabel(FILE *of, pCode *pc); +static void pCodePrintFunction(FILE *of, pCode *pc); +static void pCodeOpPrint(FILE *of, pCodeOp *pcop); +static char *pic16_get_op_from_instruction( pCodeInstruction *pcc); +char *pic16_get_op(pCodeOp *pcop,char *buff,size_t buf_size); +int pCodePeepMatchLine(pCodePeep *peepBlock, pCode *pcs, pCode *pcd); +int pic16_pCodePeepMatchRule(pCode *pc); +static void pBlockStats(FILE *of, pBlock *pb); +static pBlock *newpBlock(void); +extern void pic16_pCodeInsertAfter(pCode *pc1, pCode *pc2); +extern pCodeOp *pic16_popCopyReg(pCodeOpReg *pc); +pCodeOp *pic16_popCopyGPR2Bit(pCodeOp *pc, int bitval); +void pic16_pCodeRegMapLiveRanges(pBlock *pb); +void OptimizeLocalRegs(void); +pCodeOp *pic16_popGet2p(pCodeOp *src, pCodeOp *dst); + +const char *dumpPicOptype(PIC_OPTYPE type); + +pCodeOp *pic16_popGetLit2(int, pCodeOp *); +pCodeOp *pic16_popGetLit(int); +pCodeOp *pic16_popGetWithString(const char *); +int isBanksel(pCode *pc); +extern int inWparamList(char *s); + +/** data flow optimization helpers **/ +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 +static void pic16_vcg_dump (FILE *of, pBlock *pb); +static void pic16_vcg_dump_default (pBlock *pb); +#endif +static int pic16_pCodeIsAlive (pCode *pc); +static void pic16_df_stats (); +static void pic16_createDF (pBlock *pb); +static int pic16_removeUnusedRegistersDF (); +static void pic16_destructDF (pBlock *pb); +static void releaseStack (void); + +/****************************************************************/ +/* PIC Instructions */ +/****************************************************************/ + +pCodeInstruction pic16_pciADDWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDWF, + "ADDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDFW, + "ADDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDWFC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDWFC, + "ADDWFC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDFWC = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDFWC, + "ADDWFC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciADDLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ADDLW, + "ADDLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDLW, + "ANDLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDWF, + "ANDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciANDFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_ANDFW, + "ANDWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BC, + "BC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_C), // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBCF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BCF, + "BCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BSF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_REGISTER, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBN = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BN, + "BN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_N), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNC = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNC, + "BNC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_C), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNN = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNN, + "BNN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_N), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNOV = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNOV, + "BNOV", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_OV), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BNZ, + "BNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_Z), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBOV = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BOV, + "BOV", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_OV), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBRA = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BRA, + "BRA", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBSF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BSF, + "BSF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BCF, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTFSC = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_BTFSC, + "BTFSC", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BTFSS, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_EXAMINE_PCOP, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTFSS = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_BTFSS, + "BTFSS", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_BTFSC, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + PCC_EXAMINE_PCOP, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBTG = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BTG, + "BTG", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + TRUE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER | PCC_EXAMINE_PCOP), // inCond + (PCC_REGISTER | PCC_EXAMINE_PCOP), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciBZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_BZ, + "BZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REL_ADDR | PCC_Z), // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCALL = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CALL, + "CALL", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCOMF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_COMF, + "COMF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCOMFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_COMFW, + "COMF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCLRF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CLRF, + "CLRF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + (PCC_REGISTER | PCC_Z), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCLRWDT = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CLRWDT, + "CLRWDT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSEQ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSEQ, + "CPFSEQ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSGT = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSGT, + "CPFSGT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciCPFSLT = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_CPFSLT, + "CPFSLT", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDAW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DAW, + "DAW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + FALSE, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_W, // inCond + (PCC_W | PCC_C), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDCFSNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DCFSNZ, + "DCFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDCFSNZW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DCFSNZW, + "DCFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DECF, + "DECF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_DECFW, + "DECF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFSZ = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_DECFSZ, + "DECFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciDECFSZW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_DECFSZW, + "DECFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciGOTO = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeGOTO, + genericDestruct, + genericPrint}, + POC_GOTO, + "GOTO", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_INCF, + "INCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_INCFW, + "INCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_STATUS) , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFSZ = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INCFSZ, + "INCFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INFSNZ, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINCFSZW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INCFSZW, + "INCFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INFSNZW, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINFSNZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INFSNZ, + "INFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INCFSZ, + PCC_REGISTER, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciINFSNZW = { // vrokas - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeSKIP, + genericDestruct, + genericPrint}, + POC_INFSNZW, + "INFSNZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_INCFSZW, + PCC_REGISTER, // inCond + PCC_W , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORWF, + "IORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORFW, + "IORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciIORLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_IORLW, + "IORLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciLFSR = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_LFSR, + "LFSR", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + TRUE, // second literal operand + POC_NOP, + PCC_LITERAL, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVF, + "MOVF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVFW, + "MOVF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_N | PCC_Z), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVFF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVFF, + "MOVFF", + 4, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + TRUE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_REGISTER2, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVLB = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLB, + "MOVLB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_NONE | PCC_LITERAL), // inCond + PCC_REGISTER, // outCond - BSR + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MOVLW, + "MOVLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_NONE | PCC_LITERAL), // inCond + PCC_W, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMOVWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_MOVWF, + "MOVWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_W, // inCond + PCC_REGISTER, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMULLW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MULLW, + "MULLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + PCC_NONE, // outCond - PROD + PCI_MAGIC +}; + +pCodeInstruction pic16_pciMULWF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_MULWF, + "MULWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + PCC_REGISTER, // outCond - PROD + PCI_MAGIC +}; + +pCodeInstruction pic16_pciNEGF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_NEGF, + "NEGF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciNOP = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_NOP, + "NOP", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciPOP = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_POP, + "POP", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciPUSH = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_PUSH, + "PUSH", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRCALL = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_RCALL, + "RCALL", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REL_ADDR, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETFIE = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETFIE, + "RETFIE", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond (not true... affects the GIE bit too) + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETLW, + "RETLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_LITERAL, // inCond + PCC_W, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRETURN = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // AnalyzeRETURN, + genericDestruct, + genericPrint}, + POC_RETURN, + "RETURN", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + TRUE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRLCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLCF, + "RLCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRLCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLCFW, + "RLCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRLNCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLNCF, + "RLNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRLNCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RLNCFW, + "RLNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRCF, + "RRCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRCFW, + "RRCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_C | PCC_REGISTER), // inCond + (PCC_W | PCC_C | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; +pCodeInstruction pic16_pciRRNCF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRNCF, + "RRNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciRRNCFW = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_RRNCFW, + "RRNCF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSETF = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SETF, + "SETF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_REGISTER , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBLW, + "SUBLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWF, + "SUBWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFW, + "SUBWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB_D1 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB_D1, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBFWB_D0 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBFWB_D0, + "SUBFWB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWFB_D1 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWFB_D1, + "SUBWFB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_REGISTER | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSUBWFB_D0 = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SUBWFB_D0, + "SUBWFB", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER | PCC_C), // inCond + (PCC_W | PCC_STATUS), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSWAPF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SWAPF, + "SWAPF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER), // inCond + (PCC_REGISTER), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciSWAPFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_SWAPFW, + "SWAPF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_REGISTER), // inCond + (PCC_W), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD, + "TBLRD*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_POSTINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_POSTINC, + "TBLRD*+", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_POSTDEC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_POSTDEC, + "TBLRD*-", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLRD_PREINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLRD_PREINC, + "TBLRD+*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT, + "TBLWT*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_POSTINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_POSTINC, + "TBLWT*+", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_POSTDEC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_POSTDEC, + "TBLWT*-", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTBLWT_PREINC = { // patch 15 + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_TBLWT_PREINC, + "TBLWT+*", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE , // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciTSTFSZ = { // mdubuc - New + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_TSTFSZ, + "TSTFSZ", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 2, // num ops + FALSE, // dest + FALSE, // bit instruction + TRUE, // branch + TRUE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_REGISTER, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORWF = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORWF, + "XORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + TRUE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_REGISTER | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORFW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORFW, + "XORWF", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 3, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_REGISTER), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + +pCodeInstruction pic16_pciXORLW = { + {PC_OPCODE, NULL, NULL, 0, NULL, + // genericAnalyze, + genericDestruct, + genericPrint}, + POC_XORLW, + "XORLW", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 1, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + TRUE, // literal operand + TRUE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + (PCC_W | PCC_LITERAL), // inCond + (PCC_W | PCC_Z | PCC_N), // outCond + PCI_MAGIC +}; + + +pCodeInstruction pic16_pciBANKSEL = { + {PC_OPCODE, NULL, NULL, 0, NULL, + genericDestruct, + genericPrint}, + POC_BANKSEL, + "BANKSEL", + 2, + NULL, // from branch + NULL, // to branch + NULL, // label + NULL, // operand + NULL, // flow block + NULL, // C source + 0, // num ops + FALSE, // dest + FALSE, // bit instruction + FALSE, // branch + FALSE, // skip + FALSE, // literal operand + FALSE, // RAM access bit + FALSE, // fast call/return mode select bit + FALSE, // second memory operand + FALSE, // second literal operand + POC_NOP, + PCC_NONE, // inCond + PCC_NONE, // outCond + PCI_MAGIC +}; + + +#define MAX_PIC16MNEMONICS 100 +pCodeInstruction *pic16Mnemonics[MAX_PIC16MNEMONICS]; + +extern set *externs; +extern reg_info *pic16_allocProcessorRegister(int rIdx, char * name, short po_type, int alias); +extern reg_info *pic16_allocInternalRegister(int rIdx, char * name, short po_type, int alias); + +void pic16_pCodeInitRegisters(void) +{ + static int initialized=0; + + if(initialized) + return; + + initialized = 1; + + pic16_pc_status.r = pic16_allocProcessorRegister(IDX_STATUS,"STATUS", PO_STATUS, 0x80); + pic16_pc_pcl.r = pic16_allocProcessorRegister(IDX_PCL,"PCL", PO_PCL, 0x80); + pic16_pc_pclath.r = pic16_allocProcessorRegister(IDX_PCLATH,"PCLATH", PO_PCLATH, 0x80); + pic16_pc_pclatu.r = pic16_allocProcessorRegister(IDX_PCLATU,"PCLATU", PO_PCLATU, 0x80); + pic16_pc_intcon.r = pic16_allocProcessorRegister(IDX_INTCON,"INTCON", PO_INTCON, 0x80); + pic16_pc_wreg.r = pic16_allocProcessorRegister(IDX_WREG,"WREG", PO_WREG, 0x80); + pic16_pc_bsr.r = pic16_allocProcessorRegister(IDX_BSR,"BSR", PO_BSR, 0x80); + + pic16_pc_tosl.r = pic16_allocProcessorRegister(IDX_TOSL,"TOSL", PO_SFR_REGISTER, 0x80); + pic16_pc_tosh.r = pic16_allocProcessorRegister(IDX_TOSH,"TOSH", PO_SFR_REGISTER, 0x80); + pic16_pc_tosu.r = pic16_allocProcessorRegister(IDX_TOSU,"TOSU", PO_SFR_REGISTER, 0x80); + + pic16_pc_tblptrl.r = pic16_allocProcessorRegister(IDX_TBLPTRL,"TBLPTRL", PO_SFR_REGISTER, 0x80); + pic16_pc_tblptrh.r = pic16_allocProcessorRegister(IDX_TBLPTRH,"TBLPTRH", PO_SFR_REGISTER, 0x80); + pic16_pc_tblptru.r = pic16_allocProcessorRegister(IDX_TBLPTRU,"TBLPTRU", PO_SFR_REGISTER, 0x80); + pic16_pc_tablat.r = pic16_allocProcessorRegister(IDX_TABLAT,"TABLAT", PO_SFR_REGISTER, 0x80); + + pic16_pc_fsr0l.r = pic16_allocProcessorRegister(IDX_FSR0L, "FSR0L", PO_FSR0, 0x80); + pic16_pc_fsr0h.r = pic16_allocProcessorRegister(IDX_FSR0H, "FSR0H", PO_FSR0, 0x80); + pic16_pc_fsr1l.r = pic16_allocProcessorRegister(IDX_FSR1L, "FSR1L", PO_FSR0, 0x80); + pic16_pc_fsr1h.r = pic16_allocProcessorRegister(IDX_FSR1H, "FSR1H", PO_FSR0, 0x80); + pic16_pc_fsr2l.r = pic16_allocProcessorRegister(IDX_FSR2L, "FSR2L", PO_FSR0, 0x80); + pic16_pc_fsr2h.r = pic16_allocProcessorRegister(IDX_FSR2H, "FSR2H", PO_FSR0, 0x80); + + pic16_stackpnt_lo = &pic16_pc_fsr1l; + pic16_stackpnt_hi = &pic16_pc_fsr1h; + pic16_stack_postdec = &pic16_pc_postdec1; + pic16_stack_postinc = &pic16_pc_postinc1; + pic16_stack_preinc = &pic16_pc_preinc1; + pic16_stack_plusw = &pic16_pc_plusw1; + + pic16_framepnt_lo = &pic16_pc_fsr2l; + pic16_framepnt_hi = &pic16_pc_fsr2h; + pic16_frame_postdec = &pic16_pc_postdec2; + pic16_frame_postinc = &pic16_pc_postinc2; + pic16_frame_preinc = &pic16_pc_preinc2; + pic16_frame_plusw = &pic16_pc_plusw2; + + pic16_pc_indf0.r = pic16_allocProcessorRegister(IDX_INDF0,"INDF0", PO_INDF0, 0x80); + pic16_pc_postinc0.r = pic16_allocProcessorRegister(IDX_POSTINC0, "POSTINC0", PO_INDF0, 0x80); + pic16_pc_postdec0.r = pic16_allocProcessorRegister(IDX_POSTDEC0, "POSTDEC0", PO_INDF0, 0x80); + pic16_pc_preinc0.r = pic16_allocProcessorRegister(IDX_PREINC0, "PREINC0", PO_INDF0, 0x80); + pic16_pc_plusw0.r = pic16_allocProcessorRegister(IDX_PLUSW0, "PLUSW0", PO_INDF0, 0x80); + + pic16_pc_indf1.r = pic16_allocProcessorRegister(IDX_INDF1,"INDF1", PO_INDF0, 0x80); + pic16_pc_postinc1.r = pic16_allocProcessorRegister(IDX_POSTINC1, "POSTINC1", PO_INDF0, 0x80); + pic16_pc_postdec1.r = pic16_allocProcessorRegister(IDX_POSTDEC1, "POSTDEC1", PO_INDF0, 0x80); + pic16_pc_preinc1.r = pic16_allocProcessorRegister(IDX_PREINC1, "PREINC1", PO_INDF0, 0x80); + pic16_pc_plusw1.r = pic16_allocProcessorRegister(IDX_PLUSW1, "PLUSW1", PO_INDF0, 0x80); + + pic16_pc_indf2.r = pic16_allocProcessorRegister(IDX_INDF2,"INDF2", PO_INDF0, 0x80); + pic16_pc_postinc2.r = pic16_allocProcessorRegister(IDX_POSTINC2, "POSTINC2", PO_INDF0, 0x80); + pic16_pc_postdec2.r = pic16_allocProcessorRegister(IDX_POSTDEC2, "POSTDEC2", PO_INDF0, 0x80); + pic16_pc_preinc2.r = pic16_allocProcessorRegister(IDX_PREINC2, "PREINC2", PO_INDF0, 0x80); + pic16_pc_plusw2.r = pic16_allocProcessorRegister(IDX_PLUSW2, "PLUSW2", PO_INDF0, 0x80); + + pic16_pc_prodl.r = pic16_allocProcessorRegister(IDX_PRODL, "PRODL", PO_PRODL, 0x80); + pic16_pc_prodh.r = pic16_allocProcessorRegister(IDX_PRODH, "PRODH", PO_PRODH, 0x80); + + + pic16_pc_eecon1.r = pic16_allocProcessorRegister(IDX_EECON1, "EECON1", PO_SFR_REGISTER, 0x80); + pic16_pc_eecon2.r = pic16_allocProcessorRegister(IDX_EECON2, "EECON2", PO_SFR_REGISTER, 0x80); + pic16_pc_eedata.r = pic16_allocProcessorRegister(IDX_EEDATA, "EEDATA", PO_SFR_REGISTER, 0x80); + pic16_pc_eeadr.r = pic16_allocProcessorRegister(IDX_EEADR, "EEADR", PO_SFR_REGISTER, 0x80); + + + pic16_pc_status.rIdx = IDX_STATUS; + pic16_pc_intcon.rIdx = IDX_INTCON; + pic16_pc_pcl.rIdx = IDX_PCL; + pic16_pc_pclath.rIdx = IDX_PCLATH; + pic16_pc_pclatu.rIdx = IDX_PCLATU; + pic16_pc_wreg.rIdx = IDX_WREG; + pic16_pc_bsr.rIdx = IDX_BSR; + + pic16_pc_tosl.rIdx = IDX_TOSL; + pic16_pc_tosh.rIdx = IDX_TOSH; + pic16_pc_tosu.rIdx = IDX_TOSU; + + pic16_pc_tblptrl.rIdx = IDX_TBLPTRL; + pic16_pc_tblptrh.rIdx = IDX_TBLPTRH; + pic16_pc_tblptru.rIdx = IDX_TBLPTRU; + pic16_pc_tablat.rIdx = IDX_TABLAT; + + pic16_pc_fsr0l.rIdx = IDX_FSR0L; + pic16_pc_fsr0h.rIdx = IDX_FSR0H; + pic16_pc_fsr1l.rIdx = IDX_FSR1L; + pic16_pc_fsr1h.rIdx = IDX_FSR1H; + pic16_pc_fsr2l.rIdx = IDX_FSR2L; + pic16_pc_fsr2h.rIdx = IDX_FSR2H; + pic16_pc_indf0.rIdx = IDX_INDF0; + pic16_pc_postinc0.rIdx = IDX_POSTINC0; + pic16_pc_postdec0.rIdx = IDX_POSTDEC0; + pic16_pc_preinc0.rIdx = IDX_PREINC0; + pic16_pc_plusw0.rIdx = IDX_PLUSW0; + pic16_pc_indf1.rIdx = IDX_INDF1; + pic16_pc_postinc1.rIdx = IDX_POSTINC1; + pic16_pc_postdec1.rIdx = IDX_POSTDEC1; + pic16_pc_preinc1.rIdx = IDX_PREINC1; + pic16_pc_plusw1.rIdx = IDX_PLUSW1; + pic16_pc_indf2.rIdx = IDX_INDF2; + pic16_pc_postinc2.rIdx = IDX_POSTINC2; + pic16_pc_postdec2.rIdx = IDX_POSTDEC2; + pic16_pc_preinc2.rIdx = IDX_PREINC2; + pic16_pc_plusw2.rIdx = IDX_PLUSW2; + pic16_pc_prodl.rIdx = IDX_PRODL; + pic16_pc_prodh.rIdx = IDX_PRODH; + + pic16_pc_kzero.r = pic16_allocInternalRegister(IDX_KZ,"KZ",PO_GPR_REGISTER,0); + pic16_pc_ssave.r = pic16_allocInternalRegister(IDX_SSAVE,"SSAVE", PO_GPR_REGISTER, 0); + pic16_pc_wsave.r = pic16_allocInternalRegister(IDX_WSAVE,"WSAVE", PO_GPR_REGISTER, 0); + + pic16_pc_kzero.rIdx = IDX_KZ; + pic16_pc_wsave.rIdx = IDX_WSAVE; + pic16_pc_ssave.rIdx = IDX_SSAVE; + + pic16_pc_eecon1.rIdx = IDX_EECON1; + pic16_pc_eecon2.rIdx = IDX_EECON2; + pic16_pc_eedata.rIdx = IDX_EEDATA; + pic16_pc_eeadr.rIdx = IDX_EEADR; + + + pic16_pc_gpsimio.r = pic16_allocProcessorRegister(IDX_GPSIMIO, "GPSIMIO", PO_GPR_REGISTER, 0x80); + pic16_pc_gpsimio2.r = pic16_allocProcessorRegister(IDX_GPSIMIO2, "GPSIMIO2", PO_GPR_REGISTER, 0x80); + + pic16_pc_gpsimio.rIdx = IDX_GPSIMIO; + pic16_pc_gpsimio2.rIdx = IDX_GPSIMIO2; + + /* probably should put this in a separate initialization routine */ + pb_dead_pcodes = newpBlock(); + +} + +/*-----------------------------------------------------------------*/ +/* mnem2key - convert a pic mnemonic into a hash key */ +/* (BTW - this spreads the mnemonics quite well) */ +/* */ +/*-----------------------------------------------------------------*/ + +static int mnem2key(unsigned char const *mnem) +{ + int key = 0; + + if(!mnem) + return 0; + + while(*mnem) { + + key += toupper(*mnem++) +1; + + } + + return (key & 0x1f); + +} + +void pic16initMnemonics(void) +{ + int i = 0; + int key; + // char *str; + pCodeInstruction *pci; + + if(mnemonics_initialized) + return; + + // NULL out the array before making the assignments + // since we check the array contents below this initialization. + + for (i = 0; i < MAX_PIC16MNEMONICS; i++) { + pic16Mnemonics[i] = NULL; + } + + pic16Mnemonics[POC_ADDLW] = &pic16_pciADDLW; + pic16Mnemonics[POC_ADDWF] = &pic16_pciADDWF; + pic16Mnemonics[POC_ADDFW] = &pic16_pciADDFW; + pic16Mnemonics[POC_ADDWFC] = &pic16_pciADDWFC; + pic16Mnemonics[POC_ADDFWC] = &pic16_pciADDFWC; + pic16Mnemonics[POC_ANDLW] = &pic16_pciANDLW; + pic16Mnemonics[POC_ANDWF] = &pic16_pciANDWF; + pic16Mnemonics[POC_ANDFW] = &pic16_pciANDFW; + pic16Mnemonics[POC_BC] = &pic16_pciBC; + pic16Mnemonics[POC_BCF] = &pic16_pciBCF; + pic16Mnemonics[POC_BN] = &pic16_pciBN; + pic16Mnemonics[POC_BNC] = &pic16_pciBNC; + pic16Mnemonics[POC_BNN] = &pic16_pciBNN; + pic16Mnemonics[POC_BNOV] = &pic16_pciBNOV; + pic16Mnemonics[POC_BNZ] = &pic16_pciBNZ; + pic16Mnemonics[POC_BOV] = &pic16_pciBOV; + pic16Mnemonics[POC_BRA] = &pic16_pciBRA; + pic16Mnemonics[POC_BSF] = &pic16_pciBSF; + pic16Mnemonics[POC_BTFSC] = &pic16_pciBTFSC; + pic16Mnemonics[POC_BTFSS] = &pic16_pciBTFSS; + pic16Mnemonics[POC_BTG] = &pic16_pciBTG; + pic16Mnemonics[POC_BZ] = &pic16_pciBZ; + pic16Mnemonics[POC_CALL] = &pic16_pciCALL; + pic16Mnemonics[POC_CLRF] = &pic16_pciCLRF; + pic16Mnemonics[POC_CLRWDT] = &pic16_pciCLRWDT; + pic16Mnemonics[POC_COMF] = &pic16_pciCOMF; + pic16Mnemonics[POC_COMFW] = &pic16_pciCOMFW; + pic16Mnemonics[POC_CPFSEQ] = &pic16_pciCPFSEQ; + pic16Mnemonics[POC_CPFSGT] = &pic16_pciCPFSGT; + pic16Mnemonics[POC_CPFSLT] = &pic16_pciCPFSLT; + pic16Mnemonics[POC_DAW] = &pic16_pciDAW; + pic16Mnemonics[POC_DCFSNZ] = &pic16_pciDCFSNZ; + pic16Mnemonics[POC_DECF] = &pic16_pciDECF; + pic16Mnemonics[POC_DECFW] = &pic16_pciDECFW; + pic16Mnemonics[POC_DECFSZ] = &pic16_pciDECFSZ; + pic16Mnemonics[POC_DECFSZW] = &pic16_pciDECFSZW; + pic16Mnemonics[POC_GOTO] = &pic16_pciGOTO; + pic16Mnemonics[POC_INCF] = &pic16_pciINCF; + pic16Mnemonics[POC_INCFW] = &pic16_pciINCFW; + pic16Mnemonics[POC_INCFSZ] = &pic16_pciINCFSZ; + pic16Mnemonics[POC_INCFSZW] = &pic16_pciINCFSZW; + pic16Mnemonics[POC_INFSNZ] = &pic16_pciINFSNZ; + pic16Mnemonics[POC_INFSNZW] = &pic16_pciINFSNZW; + pic16Mnemonics[POC_IORWF] = &pic16_pciIORWF; + pic16Mnemonics[POC_IORFW] = &pic16_pciIORFW; + pic16Mnemonics[POC_IORLW] = &pic16_pciIORLW; + pic16Mnemonics[POC_LFSR] = &pic16_pciLFSR; + pic16Mnemonics[POC_MOVF] = &pic16_pciMOVF; + pic16Mnemonics[POC_MOVFW] = &pic16_pciMOVFW; + pic16Mnemonics[POC_MOVFF] = &pic16_pciMOVFF; + pic16Mnemonics[POC_MOVLB] = &pic16_pciMOVLB; + pic16Mnemonics[POC_MOVLW] = &pic16_pciMOVLW; + pic16Mnemonics[POC_MOVWF] = &pic16_pciMOVWF; + pic16Mnemonics[POC_MULLW] = &pic16_pciMULLW; + pic16Mnemonics[POC_MULWF] = &pic16_pciMULWF; + pic16Mnemonics[POC_NEGF] = &pic16_pciNEGF; + pic16Mnemonics[POC_NOP] = &pic16_pciNOP; + pic16Mnemonics[POC_POP] = &pic16_pciPOP; + pic16Mnemonics[POC_PUSH] = &pic16_pciPUSH; + pic16Mnemonics[POC_RCALL] = &pic16_pciRCALL; + pic16Mnemonics[POC_RETFIE] = &pic16_pciRETFIE; + pic16Mnemonics[POC_RETLW] = &pic16_pciRETLW; + pic16Mnemonics[POC_RETURN] = &pic16_pciRETURN; + pic16Mnemonics[POC_RLCF] = &pic16_pciRLCF; + pic16Mnemonics[POC_RLCFW] = &pic16_pciRLCFW; + pic16Mnemonics[POC_RLNCF] = &pic16_pciRLNCF; + pic16Mnemonics[POC_RLNCFW] = &pic16_pciRLNCFW; + pic16Mnemonics[POC_RRCF] = &pic16_pciRRCF; + pic16Mnemonics[POC_RRCFW] = &pic16_pciRRCFW; + pic16Mnemonics[POC_RRNCF] = &pic16_pciRRNCF; + pic16Mnemonics[POC_RRNCFW] = &pic16_pciRRNCFW; + pic16Mnemonics[POC_SETF] = &pic16_pciSETF; + pic16Mnemonics[POC_SUBLW] = &pic16_pciSUBLW; + pic16Mnemonics[POC_SUBWF] = &pic16_pciSUBWF; + pic16Mnemonics[POC_SUBFW] = &pic16_pciSUBFW; + pic16Mnemonics[POC_SUBWFB_D0] = &pic16_pciSUBWFB_D0; + pic16Mnemonics[POC_SUBWFB_D1] = &pic16_pciSUBWFB_D1; + pic16Mnemonics[POC_SUBFWB_D0] = &pic16_pciSUBFWB_D0; + pic16Mnemonics[POC_SUBFWB_D1] = &pic16_pciSUBFWB_D1; + pic16Mnemonics[POC_SWAPF] = &pic16_pciSWAPF; + pic16Mnemonics[POC_SWAPFW] = &pic16_pciSWAPFW; + pic16Mnemonics[POC_TBLRD] = &pic16_pciTBLRD; + pic16Mnemonics[POC_TBLRD_POSTINC] = &pic16_pciTBLRD_POSTINC; + pic16Mnemonics[POC_TBLRD_POSTDEC] = &pic16_pciTBLRD_POSTDEC; + pic16Mnemonics[POC_TBLRD_PREINC] = &pic16_pciTBLRD_PREINC; + pic16Mnemonics[POC_TBLWT] = &pic16_pciTBLWT; + pic16Mnemonics[POC_TBLWT_POSTINC] = &pic16_pciTBLWT_POSTINC; + pic16Mnemonics[POC_TBLWT_POSTDEC] = &pic16_pciTBLWT_POSTDEC; + pic16Mnemonics[POC_TBLWT_PREINC] = &pic16_pciTBLWT_PREINC; + pic16Mnemonics[POC_TSTFSZ] = &pic16_pciTSTFSZ; + pic16Mnemonics[POC_XORLW] = &pic16_pciXORLW; + pic16Mnemonics[POC_XORWF] = &pic16_pciXORWF; + pic16Mnemonics[POC_XORFW] = &pic16_pciXORFW; + pic16Mnemonics[POC_BANKSEL] = &pic16_pciBANKSEL; + + for(i=0; imnemonic), pic16Mnemonics[i]); + pci = hTabFirstItem(pic16MnemonicsHash, &key); + + while(pci) { + DFPRINTF((stderr, "element %d key %d, mnem %s\n",i++,key,pci->mnemonic)); + pci = hTabNextItem(pic16MnemonicsHash, &key); + } + + mnemonics_initialized = 1; +} + +int pic16_getpCodePeepCommand(const char *cmd); + +int pic16_getpCode(const char *mnem,unsigned dest) +{ + pCodeInstruction *pci; + int key = mnem2key((unsigned char *)mnem); + + if(!mnemonics_initialized) + pic16initMnemonics(); + + pci = hTabFirstItemWK(pic16MnemonicsHash, key); + + while(pci) { + + if(STRCASECMP(pci->mnemonic, mnem) == 0) { + if((pci->num_ops <= 1) + || (pci->isModReg == dest) + || (pci->isBitInst) + || (pci->num_ops <= 2 && pci->isAccess) + || (pci->num_ops <= 2 && pci->isFastCall) + || (pci->num_ops <= 2 && pci->is2MemOp) + || (pci->num_ops <= 2 && pci->is2LitOp) ) + return(pci->op); + } + + pci = hTabNextItemWK (pic16MnemonicsHash); + + } + + return -1; +} + +/*-----------------------------------------------------------------* + * pic16initpCodePeepCommands + * + *-----------------------------------------------------------------*/ +void pic16initpCodePeepCommands(void) +{ + int key, i; + peepCommand *pcmd; + + i = 0; + do { + hTabAddItem(&pic16pCodePeepCommandsHash, + mnem2key((const unsigned char *)peepCommands[i].cmd), &peepCommands[i]); + i++; + } while (peepCommands[i].cmd); + + pcmd = hTabFirstItem(pic16pCodePeepCommandsHash, &key); + + while(pcmd) { + //fprintf(stderr, "peep command %s key %d\n",pcmd->cmd,pcmd->id); + pcmd = hTabNextItem(pic16pCodePeepCommandsHash, &key); + } +} + +/*----------------------------------------------------------------- + * + * + *-----------------------------------------------------------------*/ + +int pic16_getpCodePeepCommand(const char *cmd) +{ + peepCommand *pcmd; + int key = mnem2key((unsigned char *)cmd); + + + pcmd = hTabFirstItemWK(pic16pCodePeepCommandsHash, key); + + while(pcmd) { + // fprintf(stderr," comparing %s to %s\n",pcmd->cmd,cmd); + if(STRCASECMP(pcmd->cmd, cmd) == 0) { + return pcmd->id; + } + + pcmd = hTabNextItemWK (pic16pCodePeepCommandsHash); + + } + + return -1; +} + +static char getpBlock_dbName(pBlock *pb) +{ + if(!pb) + return 0; + + if(pb->cmemmap) + return pb->cmemmap->dbName; + + return pb->dbName; +} +void pic16_pBlockConvert2ISR(pBlock *pb) +{ + if(!pb)return; + + if(pb->cmemmap)pb->cmemmap = NULL; + + pb->dbName = 'I'; + + if(pic16_pcode_verbose) + fprintf(stderr, "%s:%d converting to 'I'interrupt pBlock\n", __FILE__, __LINE__); +} + +void pic16_pBlockConvert2Absolute(pBlock *pb) +{ + if(!pb)return; + if(pb->cmemmap)pb->cmemmap = NULL; + + pb->dbName = 'A'; + + if(pic16_pcode_verbose) + fprintf(stderr, "%s:%d converting to 'A'bsolute pBlock\n", __FILE__, __LINE__); +} + +/*-----------------------------------------------------------------*/ +/* pic16_movepBlock2Head - given the dbname of a pBlock, move all */ +/* instances to the front of the doubly linked */ +/* list of pBlocks */ +/*-----------------------------------------------------------------*/ + +void pic16_movepBlock2Head(char dbName) +{ + pBlock *pb; + + + /* this can happen in sources without code, + * only variable definitions */ + if(!the_pFile)return; + + pb = the_pFile->pbHead; + + while(pb) { + + if(getpBlock_dbName(pb) == dbName) { + pBlock *pbn = pb->next; + pb->next = the_pFile->pbHead; + the_pFile->pbHead->prev = pb; + the_pFile->pbHead = pb; + + if(pb->prev) + pb->prev->next = pbn; + + // If the pBlock that we just moved was the last + // one in the link of all of the pBlocks, then we + // need to point the tail to the block just before + // the one we moved. + // Note: if pb->next is NULL, then pb must have + // been the last pBlock in the chain. + + if(pbn) + pbn->prev = pb->prev; + else + the_pFile->pbTail = pb->prev; + + pb = pbn; + + } else + pb = pb->next; + + } +} + +void pic16_copypCode(FILE *of, char dbName) +{ + pBlock *pb; + + if(!of || !the_pFile) + return; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(getpBlock_dbName(pb) == dbName) { +// fprintf(stderr, "%s:%d: output of pb= 0x%p\n", __FILE__, __LINE__, pb); + pBlockStats(of,pb); + pic16_printpBlock(of,pb); + } + } + +} +void pic16_pcode_test(void) +{ + DFPRINTF((stderr,"pcode is alive!\n")); + + //initMnemonics(); + + if (the_pFile) { + pBlock *pb; + FILE *pFile; + char buffer[100]; + + /* create the file name */ + SNPRINTF(buffer, sizeof(buffer), "%s.p", dstFileName); + + if(!(pFile = fopen(buffer, "w" ))) { + werror(E_FILE_OPEN_ERR,buffer); + exit(1); + } + + fprintf(pFile,"pcode dump\n\n"); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(pFile,"\n\tNew pBlock\n\n"); + if(pb->cmemmap) + fprintf(pFile,"%s",pb->cmemmap->sname); + else + fprintf(pFile,"internal pblock"); + + fprintf(pFile,", dbName =%c\n",getpBlock_dbName(pb)); + pic16_printpBlock(pFile,pb); + } + fclose(pFile); + } +} + + +unsigned long pic16_countInstructions(void) +{ + pBlock *pb; + pCode *pc; + unsigned long isize=0; + + if(!the_pFile)return -1; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCI(pc) || isPCAD(pc))isize += PCI(pc)->isize; + } + } + return (isize); +} + + +/*-----------------------------------------------------------------*/ +/* int RegCond(pCodeOp *pcop) - if pcop points to the STATUS reg- */ +/* ister, RegCond will return the bit being referenced. */ +/* */ +/* fixme - why not just OR in the pcop bit field */ +/*-----------------------------------------------------------------*/ + +static int RegCond(const pCodeOp *pcop) +{ + if(!pcop) + return 0; + + if(!pcop->name)return 0; + + if(pcop->type == PO_GPR_BIT && !strcmp(pcop->name, pic16_pc_status.pcop.name)) { + switch(PCORB(pcop)->bit) { + case PIC_C_BIT: + return PCC_C; + case PIC_DC_BIT: + return PCC_DC; + case PIC_Z_BIT: + return PCC_Z; + } + } + + return 0; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_newpCode - create and return a newly initialized pCode */ +/* */ +/* fixme - rename this */ +/* */ +/* The purpose of this routine is to create a new Instruction */ +/* pCode. This is called by gen.c while the assembly code is being */ +/* generated. */ +/* */ +/* Inouts: */ +/* PIC_OPCODE op - the assembly instruction we wish to create. */ +/* (note that the op is analogous to but not the */ +/* same thing as the opcode of the instruction.) */ +/* pCdoeOp *pcop - pointer to the operand of the instruction. */ +/* */ +/* Outputs: */ +/* a pointer to the new malloc'd pCode is returned. */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +pCode *pic16_newpCode (PIC_OPCODE op, pCodeOp *pcop) +{ + pCodeInstruction *pci ; + + if(!mnemonics_initialized) + pic16initMnemonics(); + + pci = Safe_alloc(sizeof(pCodeInstruction)); + + if((op>=0) && (op < MAX_PIC16MNEMONICS) && pic16Mnemonics[op]) { + memcpy(pci, pic16Mnemonics[op], sizeof(pCodeInstruction)); + pci->pcop = pcop; + + if(pci->inCond & PCC_EXAMINE_PCOP) + pci->inCond |= RegCond(pcop); + + if(pci->outCond & PCC_EXAMINE_PCOP) + pci->outCond |= RegCond(pcop); + + pci->pc.prev = pci->pc.next = NULL; + return (pCode *)pci; + } + + fprintf(stderr, "pCode mnemonic error %s,%d\n",__FUNCTION__,__LINE__); + exit(1); + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeWild - create a "wild" as in wild card pCode */ +/* */ +/* Wild pcodes are used during the peep hole optimizer to serve */ +/* as place holders for any instruction. When a snippet of code is */ +/* compared to a peep hole rule, the wild card opcode will match */ +/* any instruction. However, the optional operand and label are */ +/* additional qualifiers that must also be matched before the */ +/* line (of assembly code) is declared matched. Note that the */ +/* operand may be wild too. */ +/* */ +/* Note, a wild instruction is specified just like a wild var: */ +/* %4 ; A wild instruction, */ +/* See the peeph.def file for additional examples */ +/* */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeWild(int pCodeID, pCodeOp *optional_operand, pCodeOp *optional_label) +{ + pCodeWild *pcw; + + pcw = Safe_alloc(sizeof(pCodeWild)); + + pcw->pci.pc.type = PC_WILD; + pcw->pci.pc.prev = pcw->pci.pc.next = NULL; + pcw->pci.from = pcw->pci.to = pcw->pci.label = NULL; + pcw->pci.pc.pb = NULL; + + // pcw->pci.pc.analyze = genericAnalyze; + pcw->pci.pc.destruct = genericDestruct; + pcw->pci.pc.print = genericPrint; + + pcw->id = pCodeID; // this is the 'n' in %n + pcw->operand = optional_operand; + pcw->label = optional_label; + + pcw->mustBeBitSkipInst = FALSE; + pcw->mustNotBeBitSkipInst = FALSE; + pcw->invertBitSkipInst = FALSE; + + return ((pCode *)pcw); +} + + /*-----------------------------------------------------------------*/ +/* newPcodeInlineP - create a new pCode from a char string */ +/*-----------------------------------------------------------------*/ + + +pCode *pic16_newpCodeInlineP(const char *cP) +{ + pCodeComment *pcc; + + pcc = Safe_alloc(sizeof(pCodeComment)); + + pcc->pc.type = PC_INLINE; + pcc->pc.prev = pcc->pc.next = NULL; + //pcc->pc.from = pcc->pc.to = pcc->pc.label = NULL; + pcc->pc.pb = NULL; + + // pcc->pc.analyze = genericAnalyze; + pcc->pc.destruct = genericDestruct; + pcc->pc.print = genericPrint; + + pcc->comment = (cP != NULL) ? Safe_strdup(cP) : NULL; + + return ((pCode *)pcc); +} + +/*-----------------------------------------------------------------*/ +/* newPcodeCharP - create a new pCode from a char string */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeCharP(const char *cP) +{ + pCodeComment *pcc; + + pcc = Safe_alloc(sizeof(pCodeComment)); + + pcc->pc.type = PC_COMMENT; + pcc->pc.prev = pcc->pc.next = NULL; + //pcc->pc.from = pcc->pc.to = pcc->pc.label = NULL; + pcc->pc.pb = NULL; + + // pcc->pc.analyze = genericAnalyze; + pcc->pc.destruct = genericDestruct; + pcc->pc.print = genericPrint; + + pcc->comment = (cP != NULL) ? Safe_strdup(cP) : NULL; + + return ((pCode *)pcc); +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeFunction - */ +/*-----------------------------------------------------------------*/ + + +pCode *pic16_newpCodeFunction(const char *mod, const char *f) +{ + pCodeFunction *pcf; + + pcf = Safe_alloc(sizeof(pCodeFunction)); + + pcf->pc.type = PC_FUNCTION; + pcf->pc.prev = pcf->pc.next = NULL; + //pcf->pc.from = pcf->pc.to = pcf->pc.label = NULL; + pcf->pc.pb = NULL; + + //pcf->pc.analyze = genericAnalyze; + pcf->pc.destruct = genericDestruct; + pcf->pc.print = pCodePrintFunction; + + pcf->ncalled = 0; + pcf->absblock = FALSE; + + pcf->modname = (mod != NULL) ? Safe_strdup(mod) : NULL; + pcf->fname = (f != NULL) ? Safe_strdup(f) : NULL; + + pcf->stackusage = 0; + + return ((pCode *)pcf); +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeFlow */ +/*-----------------------------------------------------------------*/ +static void destructpCodeFlow(pCode *pc) +{ + if(!pc || !isPCFL(pc)) + return; + +/* + if(PCFL(pc)->from) + if(PCFL(pc)->to) +*/ + pic16_unlinkpCode(pc); + + deleteSet(&PCFL(pc)->registers); + deleteSet(&PCFL(pc)->from); + deleteSet(&PCFL(pc)->to); + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + +// Safe_free(pc); +} + +pCode *pic16_newpCodeFlow(void) +{ + pCodeFlow *pcflow; + + //_ALLOC(pcflow,sizeof(pCodeFlow)); + pcflow = Safe_alloc(sizeof(pCodeFlow)); + + pcflow->pc.type = PC_FLOW; + pcflow->pc.prev = pcflow->pc.next = NULL; + pcflow->pc.pb = NULL; + + // pcflow->pc.analyze = genericAnalyze; + pcflow->pc.destruct = destructpCodeFlow; + pcflow->pc.print = genericPrint; + + pcflow->pc.seq = GpcFlowSeq++; + + pcflow->from = pcflow->to = NULL; + + pcflow->inCond = PCC_NONE; + pcflow->outCond = PCC_NONE; + + pcflow->firstBank = -1; + pcflow->lastBank = -1; + + pcflow->FromConflicts = 0; + pcflow->ToConflicts = 0; + + pcflow->end = NULL; + + pcflow->registers = newSet(); + + return ((pCode *)pcflow); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeFlowLink *pic16_newpCodeFlowLink(pCodeFlow *pcflow) +{ + pCodeFlowLink *pcflowLink; + + pcflowLink = Safe_alloc(sizeof(pCodeFlowLink)); + + pcflowLink->pcflow = pcflow; + pcflowLink->bank_conflict = 0; + + return pcflowLink; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeCSource - create a new pCode Source Symbol */ +/*-----------------------------------------------------------------*/ + +pCode *pic16_newpCodeCSource(int ln, const char *f, const char *l) +{ + pCodeCSource *pccs; + + pccs = Safe_alloc(sizeof(pCodeCSource)); + + pccs->pc.type = PC_CSOURCE; + pccs->pc.prev = pccs->pc.next = NULL; + pccs->pc.pb = NULL; + + pccs->pc.destruct = genericDestruct; + pccs->pc.print = genericPrint; + + pccs->line_number = ln; + pccs->line = (l != NULL) ? Safe_strdup(l) : NULL; + pccs->file_name = (f != NULL) ? Safe_strdup(f) : NULL; + + return ((pCode *)pccs); +} + + +/*******************************************************************/ +/* pic16_newpCodeAsmDir - create a new pCode Assembler Directive */ +/* added by VR 6-Jun-2003 */ +/*******************************************************************/ + +pCode *pic16_newpCodeAsmDir(const char *asdir, const char *argfmt, ...) +{ + pCodeAsmDir *pcad; + va_list ap; + char buffer[512]; + char *lbp=buffer; + + pcad = Safe_alloc(sizeof(pCodeAsmDir)); + pcad->pci.pc.type = PC_ASMDIR; + pcad->pci.pc.prev = pcad->pci.pc.next = NULL; + pcad->pci.pc.pb = NULL; + pcad->pci.isize = 2; + pcad->pci.pc.destruct = genericDestruct; + pcad->pci.pc.print = genericPrint; + + if(asdir && *asdir) { + while(isspace((const unsigned char)*asdir)) asdir++; // strip any white space from the beginning + + pcad->directive = Safe_strdup(asdir); + } + + va_start(ap, argfmt); + + memset(buffer, 0, sizeof(buffer)); + if(argfmt && *argfmt) + vsprintf(buffer, argfmt, ap); + + va_end(ap); + + while(isspace((unsigned char)*lbp)) lbp++; + + if(lbp && *lbp) + pcad->arg = Safe_strdup(lbp); + + return ((pCode *)pcad); +} + +/*-----------------------------------------------------------------*/ +/* pCodeLabelDestruct - free memory used by a label. */ +/*-----------------------------------------------------------------*/ +static void pCodeLabelDestruct(pCode *pc) +{ + if(!pc) + return; + + pic16_unlinkpCode(pc); + +// if((pc->type == PC_LABEL) && PCL(pc)->label) +// Safe_free(PCL(pc)->label); + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + +// Safe_free(pc); +} + +pCode *pic16_newpCodeLabel(const char *name, int key) +{ + const char *s; + pCodeLabel *pcl; + + pcl = Safe_alloc(sizeof(pCodeLabel)); + + pcl->pc.type = PC_LABEL; + pcl->pc.prev = pcl->pc.next = NULL; + //pcl->pc.from = pcl->pc.to = pcl->pc.label = NULL; + pcl->pc.pb = NULL; + + // pcl->pc.analyze = genericAnalyze; + pcl->pc.destruct = pCodeLabelDestruct; + pcl->pc.print = pCodePrintLabel; + + pcl->key = key; + pcl->force = FALSE; + + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",key); + s = buffer; + } else + s = name; + + pcl->label = (s != NULL) ? Safe_strdup(s) : NULL; + +// if(pic16_pcode_verbose) +// fprintf(stderr, "%s:%d label name: %s\n", __FILE__, __LINE__, pcl->label); + + return ((pCode *)pcl); +} + +pCode *pic16_newpCodeLabelFORCE(const char *name, int key) +{ + pCodeLabel *pcl = (pCodeLabel *)pic16_newpCodeLabel(name, key); + + pcl->force = TRUE; + + return ((pCode *)pcl); +} + +pCode *pic16_newpCodeInfo(INFO_TYPE type, pCodeOp *pcop) +{ + pCodeInfo *pci; + + pci = Safe_alloc(sizeof(pCodeInfo)); + pci->pci.pc.type = PC_INFO; + pci->pci.pc.prev = pci->pci.pc.next = NULL; + pci->pci.pc.pb = NULL; + pci->pci.label = NULL; + + pci->pci.pc.destruct = genericDestruct; + pci->pci.pc.print = genericPrint; + + pci->type = type; + pci->oper1 = pcop; + + return ((pCode *)pci); +} + + +/*-----------------------------------------------------------------*/ +/* newpBlock - create and return a pointer to a new pBlock */ +/*-----------------------------------------------------------------*/ +static pBlock *newpBlock(void) +{ + pBlock *PpB; + + PpB = Safe_alloc(sizeof(pBlock)); + PpB->next = PpB->prev = NULL; + + PpB->function_entries = PpB->function_exits = PpB->function_calls = NULL; + PpB->tregisters = NULL; + PpB->visited = FALSE; + PpB->FlowTree = NULL; + + return PpB; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeChain - create a new chain of pCodes */ +/*-----------------------------------------------------------------* + * + * This function will create a new pBlock and the pointer to the + * pCode that is passed in will be the first pCode in the block. + *-----------------------------------------------------------------*/ + + +pBlock *pic16_newpCodeChain(memmap *cm,char c, pCode *pc) +{ + pBlock *pB = newpBlock(); + + pB->pcHead = pB->pcTail = pc; + pB->cmemmap = cm; + pB->dbName = c; + + return pB; +} + +/*-----------------------------------------------------------------*/ +/* pic16_newpCodeOpLabel - Create a new label given the key */ +/* Note, a negative key means that the label is part of wild card */ +/* (and hence a wild card label) used in the pCodePeep */ +/* optimizations). */ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOpLabel(const char *name, int key) +{ + static int label_key = -1; + + const char *s; + + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLabel)); + pcop->type = PO_LABEL; + + if(key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",key); + s = buffer; + } + else { + key = label_key--; + s = name; + } + + pcop->name = (s != NULL) ? Safe_strdup(s) : NULL; + + ((pCodeOpLabel *)pcop)->key = key; + + //fprintf(stderr,"pic16_newpCodeOpLabel: key=%d, name=%s\n",key,((s)?s:"")); + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLit(int lit) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLit)); + pcop->type = PO_LITERAL; + + //if(lit>=0) + SNPRINTF(buffer, sizeof(buffer), "0x%02x", (unsigned char)lit); + //else + // SNPRINTF(buffer, sizeof(buffer), "%i", lit); + + pcop->name = Safe_strdup(buffer); + + ((pCodeOpLit *)pcop)->lit = lit; + + return pcop; +} + +/* Allow for 12 bit literals, required for LFSR */ +pCodeOp *pic16_newpCodeOpLit12(int lit) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLit)); + pcop->type = PO_LITERAL; + + //if(lit>=0) + SNPRINTF(buffer, sizeof(buffer), "0x%03x", ((unsigned int)lit) & 0x0fff); + //else + // SNPRINTF(buffer, sizeof(buffer), "%i", lit); + + pcop->name = Safe_strdup(buffer); + + ((pCodeOpLit *)pcop)->lit = lit; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLit2(int lit, pCodeOp *arg2) +{ + char tbuf[256], *tb = tbuf; + pCodeOp *pcop; + + tb = pic16_get_op(arg2, NULL, 0); + pcop = Safe_alloc(sizeof(pCodeOpLit2)); + pcop->type = PO_LITERAL; + + //pcop->name = NULL; + //if(lit>=0) { + SNPRINTF(buffer, sizeof(buffer), "0x%02x, %s", (unsigned char)lit, tb); + pcop->name = Safe_strdup(buffer); + //} + + ((pCodeOpLit2 *)pcop)->lit = lit; + ((pCodeOpLit2 *)pcop)->arg2 = arg2; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpImmd(const char *name, int offset, int index, int code_space) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpImmd)); + pcop->type = PO_IMMEDIATE; + if(name) { + reg_info *r = pic16_dirregWithName(name); + pcop->name = Safe_strdup(name); + PCOI(pcop)->r = r; + + if(r) { +// fprintf(stderr, "%s:%d %s reg %s exists (r: %p)\n",__FILE__, __LINE__, __FUNCTION__, name, r); + PCOI(pcop)->rIdx = r->rIdx; + } else { +// fprintf(stderr, "%s:%d %s reg %s doesn't exist\n", __FILE__, __LINE__, __FUNCTION__, name); + PCOI(pcop)->rIdx = -1; + } +// fprintf(stderr,"%s %s %d\n",__FUNCTION__,name,offset); + } else { + pcop->name = NULL; + PCOI(pcop)->rIdx = -1; + } + + PCOI(pcop)->index = index; + PCOI(pcop)->offset = offset; + PCOI(pcop)->_const = code_space; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpWild(int id, pCodeWildBlock *pcwb, pCodeOp *subtype) +{ + pCodeOp *pcop; + + if(!pcwb || !subtype) { + fprintf(stderr, "Wild opcode declaration error: %s-%d\n",__FILE__,__LINE__); + exit(1); + } + + pcop = Safe_alloc(sizeof(pCodeOpWild)); + pcop->type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id); + pcop->name = Safe_strdup(buffer); + + PCOW(pcop)->id = id; + PCOW(pcop)->pcwb = pcwb; + PCOW(pcop)->subtype = subtype; + PCOW(pcop)->matched = NULL; + + PCOW(pcop)->pcop2 = NULL; + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpWild2(int id, int id2, pCodeWildBlock *pcwb, pCodeOp *subtype, pCodeOp *subtype2) +{ + pCodeOp *pcop; + + if(!pcwb || !subtype || !subtype2) { + fprintf(stderr, "Wild opcode declaration error: %s-%d\n",__FILE__,__LINE__); + exit(1); + } + + pcop = Safe_alloc(sizeof(pCodeOpWild)); + pcop->type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id); + pcop->name = Safe_strdup(buffer); + + PCOW(pcop)->id = id; + PCOW(pcop)->pcwb = pcwb; + PCOW(pcop)->subtype = subtype; + PCOW(pcop)->matched = NULL; + + PCOW(pcop)->pcop2 = Safe_alloc(sizeof(pCodeOpWild)); + + if(!subtype2->name) { + PCOW(pcop)->pcop2 = Safe_alloc(sizeof(pCodeOpWild)); + PCOW2(pcop)->pcop.type = PO_WILD; + SNPRINTF(buffer, sizeof(buffer), "%%%d", id2); + PCOW2(pcop)->pcop.name = Safe_strdup(buffer); + PCOW2(pcop)->id = id2; + PCOW2(pcop)->subtype = subtype2; + +// fprintf(stderr, "%s:%d %s [wild,wild] for name: %s (%d)\tname2: %s (%d)\n", __FILE__, __LINE__, __FUNCTION__, +// pcop->name, id, PCOW2(pcop)->pcop.name, id2); + } else { + PCOW2(pcop)->pcop2 = pic16_pCodeOpCopy(subtype2); + +// fprintf(stderr, "%s:%d %s [wild,str] for name: %s (%d)\tname2: %s (%d)\n", __FILE__, __LINE__, __FUNCTION__, +// pcop->name, id, PCOW2(pcop)->pcop.name, id2); + } + + return pcop; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpBit(const char *s, int bit, int inBitSpace, PIC_OPTYPE subt) +{ + pCodeOp *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpRegBit)); + pcop->type = PO_GPR_BIT; + pcop->name = (s != NULL) ? Safe_strdup(s) : NULL; + PCORB(pcop)->bit = bit; + PCORB(pcop)->inBitSpace = inBitSpace; + PCORB(pcop)->subtype = subt; + + /* pCodeOpBit is derived from pCodeOpReg. We need to init this too */ + PCOR(pcop)->r = pic16_regWithName(s); //NULL; +// fprintf(stderr, "%s:%d %s for reg: %s\treg= %p\n", __FILE__, __LINE__, __FUNCTION__, s, PCOR(pcop)->r); +// PCOR(pcop)->rIdx = 0; + return pcop; +} + +pCodeOp *pic16_newpCodeOpBit_simple (struct asmop *op, int offs, int bit) +{ + return pic16_newpCodeOpBit (pic16_aopGet(op,offs,FALSE,FALSE), + bit, 0, PO_GPR_REGISTER); +} + + +/*-----------------------------------------------------------------* + * pCodeOp *pic16_newpCodeOpReg(int rIdx) - allocate a new register + * + * If rIdx >=0 then a specific register from the set of registers + * will be selected. If rIdx <0, then a new register will be searched + * for. + *-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOpReg(int rIdx) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + + pcop->name = NULL; + + if(rIdx >= 0) { + r = pic16_regWithIdx(rIdx); + if(!r) + r = pic16_allocWithIdx(rIdx); + } else { + r = pic16_findFreeReg(REG_GPR); + + if(!r) { + fprintf(stderr, "%s:%d Could not find a free GPR register\n", + __FUNCTION__, __LINE__); + exit(EXIT_FAILURE); + } + } + + PCOR(pcop)->rIdx = rIdx; + PCOR(pcop)->r = r; + pcop->type = PCOR(pcop)->r->pc_type; + + return pcop; +} + +pCodeOp *pic16_newpCodeOpRegNotVect(bitVect *bv) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + pcop->name = NULL; + + r = pic16_findFreeReg(REG_GPR); + + while(r) { + if(!bitVectBitValue(bv, r->rIdx)) { + PCOR(pcop)->r = r; + PCOR(pcop)->rIdx = r->rIdx; + pcop->type = r->pc_type; + return (pcop); + } + + r = pic16_findFreeRegNext(REG_GPR, r); + } + + return NULL; +} + + + +pCodeOp *pic16_newpCodeOpRegFromStr(const char *name) +{ + pCodeOp *pcop; + reg_info *r; + + pcop = Safe_alloc(sizeof(pCodeOpReg)); + PCOR(pcop)->r = r = pic16_allocRegByName(name, 1, NULL); + PCOR(pcop)->rIdx = PCOR(pcop)->r->rIdx; + pcop->type = PCOR(pcop)->r->pc_type; + pcop->name = PCOR(pcop)->r->name; + +// if(pic16_pcode_verbose) { +// fprintf(stderr, "%s:%d %s allocates register %s rIdx:0x%02x\n", +// __FILE__, __LINE__, __FUNCTION__, r->name, r->rIdx); +// } + + return pcop; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpOpt(OPT_TYPE type, const char *key) +{ + pCodeOpOpt *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpOpt)); + + pcop->type = type; + pcop->key = Safe_strdup(key); + + return (PCOP(pcop)); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_newpCodeOpLocalRegs(LR_TYPE type) +{ + pCodeOpLocalReg *pcop; + + pcop = Safe_alloc(sizeof(pCodeOpLocalReg)); + + pcop->type = type; + + return (PCOP(pcop)); +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_newpCodeOp(const char *name, PIC_OPTYPE type) +{ + pCodeOp *pcop; + + switch(type) { + case PO_BIT: + case PO_GPR_BIT: + pcop = pic16_newpCodeOpBit(name, -1,0, type); + break; + + case PO_LITERAL: + pcop = pic16_newpCodeOpLit(-1); + break; + + case PO_LABEL: + pcop = pic16_newpCodeOpLabel(NULL,-1); + break; + + case PO_GPR_TEMP: + pcop = pic16_newpCodeOpReg(-1); + break; + + case PO_GPR_REGISTER: + pcop = (name != NULL) ? pic16_newpCodeOpRegFromStr(name) : pic16_newpCodeOpReg(-1); + break; + + case PO_TWO_OPS: + assert( !"Cannot create PO_TWO_OPS from string!" ); + pcop = NULL; + break; + + default: + pcop = Safe_alloc(sizeof(pCodeOp)); + pcop->type = type; + pcop->name = (name != NULL) ? Safe_strdup(name) : NULL; + } + + return pcop; +} + +pCodeOp *pic16_newpCodeOp2(pCodeOp *src, pCodeOp *dst) +{ + pCodeOp2 *pcop2 = Safe_alloc(sizeof(pCodeOp2)); + pcop2->pcop.type = PO_TWO_OPS; + pcop2->pcopL = src; + pcop2->pcopR = dst; + return PCOP(pcop2); +} + +/* This is a multiple of two as gpasm pads DB directives to even length, + * thus the data would be interleaved with \0 bytes... + * This is a multiple of three in order to have arrays of 3-byte pointers + * continuously in memory (without 0-padding at the lines' end). + * This is rather 12 than 6 in order not to split up 4-byte data types + * in arrays right in the middle of a 4-byte word. */ +#define DB_ITEMS_PER_LINE 12 + +typedef struct DBdata + { + int count; + char buffer[512]; + } DBdata; + +struct DBdata DBd; +static int DBd_init = -1; + +/*-----------------------------------------------------------------*/ +/* Initialiase "DB" data buffer */ +/*-----------------------------------------------------------------*/ +void pic16_initDB(void) +{ + DBd_init = -1; +} + + +/*-----------------------------------------------------------------*/ +/* Flush pending "DB" data to a pBlock */ +/* */ +/* ptype - type of p pointer, 'f' file pointer, 'p' pBlock pointer */ +/*-----------------------------------------------------------------*/ +void pic16_flushDB(char ptype, void *p) +{ + if (DBd.count>0) { + if(ptype == 'p') + pic16_addpCode2pBlock(((pBlock *)p),pic16_newpCodeAsmDir("DB", "%s", DBd.buffer)); + else + if(ptype == 'f') + fprintf(((FILE *)p), "\tdb\t%s\n", DBd.buffer); + else { + /* sanity check */ + fprintf(stderr, "PIC16 port error: could not emit initial value data\n"); + } + + DBd.count = 0; + DBd.buffer[0] = '\0'; + } +} + + +/*-----------------------------------------------------------------*/ +/* Add "DB" directives to a pBlock */ +/*-----------------------------------------------------------------*/ +void pic16_emitDB(int c, char ptype, void *p) +{ + size_t l; + + if (DBd_init < 0) { + // we need to initialize + DBd_init = 0; + DBd.count = 0; + DBd.buffer[0] = '\0'; + } + + l = strlen(DBd.buffer); + + if (sizeof(DBd.buffer) <= l) { + fprintf(stderr, "%s() -- Error: Size of DBd.buffer too small. (%zu <= %zu)\n", __func__, sizeof(DBd.buffer), l); + exit(1); + } + + SNPRINTF(DBd.buffer + l, sizeof(DBd.buffer) - l, "%s0x%02x", ((DBd.count > 0) ? ", " : ""), c & 0xff); + +// fprintf(stderr, "%s:%d DBbuffer: '%s'\n", __FILE__, __LINE__, DBd.buffer); + + DBd.count++; + if (DBd.count >= DB_ITEMS_PER_LINE) + pic16_flushDB(ptype, p); +} + +void pic16_emitDS(const char *s, char ptype, void *p) +{ + size_t l; + + if (DBd_init < 0) { + // we need to initialize + DBd_init = 0; + DBd.count = 0; + DBd.buffer[0] = '\0'; + } + + l = strlen(DBd.buffer); + + if (sizeof(DBd.buffer) <= l) { + fprintf(stderr, "%s() -- Error: Size of DBd.buffer too small. (%zu <= %zu)\n", __func__, sizeof(DBd.buffer), l); + exit(1); + } + + SNPRINTF(DBd.buffer + l, sizeof(DBd.buffer) - l, "%s%s", ((DBd.count > 0) ? ", " : ""), s); + +// fprintf(stderr, "%s:%d DBbuffer: '%s'\n", __FILE__, __LINE__, DBd.buffer); + + DBd.count++; //=strlen(s); + if (DBd.count >= DB_ITEMS_PER_LINE) + pic16_flushDB(ptype, p); +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +void pic16_pCodeConstString(const char *name, const char *value, unsigned length) +{ + pBlock *pb; + char *item; + static set *emittedSymbols = NULL; + + if(!name || !value) + return; + + /* keep track of emitted symbols to avoid multiple definition of str_ */ + if (emittedSymbols) { + /* scan set for name */ + for (item = setFirstItem (emittedSymbols); item; item = setNextItem (emittedSymbols)) + { + if (!strcmp (item,name)) { + //fprintf (stderr, "%s already emitted\n", name); + return; + } // if + } // for + } // if + addSet (&emittedSymbols, Safe_strdup (name)); + + //fprintf(stderr, " %s %s %s\n",__FUNCTION__,name,value); + + pb = pic16_newpCodeChain(NULL, 'P',pic16_newpCodeCharP("; Starting pCode block")); + + pic16_addpBlock(pb); + +// SNPRINTF(buffer, sizeof(buffer), "; %s = %s", name, value); +// fputs(buffer, stderr); + +// pic16_addpCode2pBlock(pb,pic16_newpCodeCharP(buffer)); + pic16_addpCode2pBlock(pb,pic16_newpCodeLabel(name,-1)); + + while (length--) + pic16_emitDB(*value++, 'p', (void *)pb); + + pic16_flushDB('p', (void *)pb); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void pCodeReadCodeTable(void) +{ + pBlock *pb; + + fprintf(stderr, " %s\n",__FUNCTION__); + + pb = pic16_newpCodeChain(NULL, 'P',pic16_newpCodeCharP("; Starting pCode block")); + + pic16_addpBlock(pb); + + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; ReadCodeTable - built in function")); + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; Inputs: temp1,temp2 = code pointer")); + pic16_addpCode2pBlock(pb,pic16_newpCodeCharP("; Outpus: W (from RETLW at temp2:temp1)")); + pic16_addpCode2pBlock(pb,pic16_newpCodeLabel("ReadCodeTable:",-1)); + + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVFW,pic16_newpCodeOpRegFromStr("temp2"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVWF,pic16_newpCodeOpRegFromStr("PCLATH"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVFW,pic16_newpCodeOpRegFromStr("temp1"))); + pic16_addpCode2pBlock(pb,pic16_newpCode(POC_MOVWF,pic16_newpCodeOpRegFromStr("PCL"))); + + +} +#endif +/*-----------------------------------------------------------------*/ +/* pic16_addpCode2pBlock - place the pCode into the pBlock linked list */ +/*-----------------------------------------------------------------*/ +void pic16_addpCode2pBlock(pBlock *pb, pCode *pc) +{ + if(!pc) + return; + + if(!pb->pcHead) { + /* If this is the first pcode to be added to a block that + * was initialized with a NULL pcode, then go ahead and + * make this pcode the head and tail */ + pb->pcHead = pb->pcTail = pc; + } else { + // if(pb->pcTail) + pb->pcTail->next = pc; + + pc->prev = pb->pcTail; + pc->pb = pb; + + pb->pcTail = pc; + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_addpBlock - place a pBlock into the pFile */ +/*-----------------------------------------------------------------*/ +void pic16_addpBlock(pBlock *pb) +{ + // fprintf(stderr," Adding pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + if(!the_pFile) { + /* First time called, we'll pass through here. */ + //_ALLOC(the_pFile,sizeof(pFile)); + the_pFile = Safe_alloc(sizeof(pFile)); + the_pFile->pbHead = the_pFile->pbTail = pb; + the_pFile->functions = NULL; + return; + } + + the_pFile->pbTail->next = pb; + pb->prev = the_pFile->pbTail; + pb->next = NULL; + the_pFile->pbTail = pb; +} + +/*-----------------------------------------------------------------*/ +/* removepBlock - remove a pBlock from the pFile */ +/*-----------------------------------------------------------------*/ +static void removepBlock(pBlock *pb) +{ + pBlock *pbs; + + if(!the_pFile) + return; + + //fprintf(stderr," Removing pBlock: dbName =%c\n",getpBlock_dbName(pb)); + + for(pbs = the_pFile->pbHead; pbs; pbs = pbs->next) { + if(pbs == pb) { + + if(pbs == the_pFile->pbHead) + the_pFile->pbHead = pbs->next; + + if (pbs == the_pFile->pbTail) + the_pFile->pbTail = pbs->prev; + + if(pbs->next) + pbs->next->prev = pbs->prev; + + if(pbs->prev) + pbs->prev->next = pbs->next; + + return; + + } + } + + fprintf(stderr, "Warning: call to %s:%s didn't find pBlock\n",__FILE__,__FUNCTION__); +} + +/*-----------------------------------------------------------------*/ +/* printpCode - write the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ +static void printpCode(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + if(pc->print) { + pc->print(of,pc); + return; + } + + fprintf(of,"warning - unable to print pCode\n"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_printpBlock - write the contents of a pBlock to a file */ +/*-----------------------------------------------------------------*/ +void pic16_printpBlock(FILE *of, pBlock *pb) +{ + pCode *pc; + + if(!pb)return; + + if(!of)of=stderr; + + for(pc = pb->pcHead; pc; pc = pc->next) { + if(isPCF(pc) && PCF(pc)->fname) { + fprintf(of, "S_%s_%s\tcode", PCF(pc)->modname, PCF(pc)->fname); + if(pb->dbName == 'A') { + absSym *ab; + for(ab=setFirstItem(absSymSet); ab; ab=setNextItem(absSymSet)) { +// fprintf(stderr, "%s:%d testing %s <-> %s\n", __FILE__, __LINE__, PCF(pc)->fname, ab->name); + if(!strcmp(ab->name, PCF(pc)->fname)) { +// fprintf(stderr, "%s:%d address = %x\n", __FILE__, __LINE__, ab->address); + if(ab->address != -1) + fprintf(of, "\t0X%06X", ab->address); + break; + } + } + } + fprintf(of, "\n"); + } + printpCode(of,pc); + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/* pCode processing */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextInstruction(pCode *pci); +pCode * pic16_findPrevInstruction(pCode *pci); + +void pic16_unlinkpCode(pCode *pc) +{ + pCode *prev; + + if(pc) { +#ifdef PCODE_DEBUG + fprintf(stderr,"Unlinking: "); + printpCode(stderr, pc); +#endif + if(pc->prev) { + pc->prev->next = pc->next; + } else if (pc->pb && (pc->pb->pcHead == pc)) { + pc->pb->pcHead = pc->next; + } + if(pc->next) { + pc->next->prev = pc->prev; + } else if (pc->pb && (pc->pb->pcTail == pc)) { + pc->pb->pcTail = pc->prev; + } + + /* move C source line down (or up) */ + if (isPCI(pc) && PCI(pc)->cline) { + prev = pic16_findNextInstruction (pc->next); + if (prev && isPCI(prev) && !PCI(prev)->cline) { + PCI(prev)->cline = PCI(pc)->cline; + } else { + prev = pic16_findPrevInstruction (pc->prev); + if (prev && isPCI(prev) && !PCI(prev)->cline) + PCI(prev)->cline = PCI(pc)->cline; + } + } + pc->prev = pc->next = NULL; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void genericDestruct(pCode *pc) +{ + pic16_unlinkpCode(pc); + + if(isPCI(pc)) { + /* For instructions, tell the register (if there's one used) + * that it's no longer needed */ + reg_info *reg = pic16_getRegFromInstruction(pc); + if(reg) + deleteSetItem (&(reg->reglives.usedpCodes),pc); + + if(PCI(pc)->is2MemOp) { + reg = pic16_getRegFromInstruction2(pc); + if(reg) + deleteSetItem(&(reg->reglives.usedpCodes), pc); + } + } + + /* Instead of deleting the memory used by this pCode, mark + * the object as bad so that if there's a pointer to this pCode + * dangling around somewhere then (hopefully) when the type is + * checked we'll catch it. + */ + + pc->type = PC_BAD; + pic16_addpCode2pBlock(pb_dead_pcodes, pc); + + //Safe_free(pc); +} + + +void DEBUGpic16_emitcode (char *inst,char *fmt, ...); +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +/* modifiers for constant immediate */ + +static const char *immdmod[3] = {"LOW", "HIGH", "UPPER"}; + +char *pic16_get_op(pCodeOp *pcop, char *buffer, size_t size) +{ + reg_info *r; + static char b[128]; + char *s; + int use_buffer = 1; // copy the string to the passed buffer pointer + + if(!buffer) { + buffer = b; + size = sizeof(b); + use_buffer = 0; // Don't bother copying the string to the buffer. + } + + if(pcop) { + + switch(pcop->type) { + case PO_W: + case PO_WREG: + case PO_PRODL: + case PO_PRODH: + case PO_INDF0: + case PO_FSR0: + if(use_buffer) { + SNPRINTF(buffer,size,"%s",PCOR(pcop)->r->name); + return (buffer); + } + return (PCOR(pcop)->r->name); + break; + case PO_GPR_TEMP: + r = pic16_regWithIdx(PCOR(pcop)->r->rIdx); + if(use_buffer) { + SNPRINTF(buffer,size,"%s",r->name); + return (buffer); + } + return (r->name); + break; + + case PO_IMMEDIATE: + s = buffer; + if(PCOI(pcop)->offset && PCOI(pcop)->offset<4) { + if(PCOI(pcop)->index) { + SNPRINTF(s,size, "%s(%s + %d)", + immdmod[ PCOI(pcop)->offset ], + pcop->name, + PCOI(pcop)->index); + } else { + SNPRINTF(s,size,"%s(%s)", + immdmod[ PCOI(pcop)->offset ], + pcop->name); + } + } else { + if(PCOI(pcop)->index) { + SNPRINTF(s,size, "%s(%s + %d)", + immdmod[ 0 ], + pcop->name, + PCOI(pcop)->index); + } else { + SNPRINTF(s,size, "%s(%s)", + immdmod[ 0 ], + pcop->name); + } + } + return (buffer); + break; + + case PO_GPR_REGISTER: + case PO_DIR: + s = buffer; + //size = sizeof(buffer); + if( PCOR(pcop)->instance) { + SNPRINTF(s,size,"(%s + %d)", + pcop->name, + PCOR(pcop)->instance ); + } else { + SNPRINTF(s,size,"%s",pcop->name); + } + return (buffer); + break; + + case PO_GPR_BIT: + s = buffer; + if(PCORB(pcop)->subtype == PO_GPR_TEMP) { + SNPRINTF(s, size, "%s", pcop->name); + } else { + if(PCORB(pcop)->pcor.instance) + SNPRINTF(s, size, "(%s + %d)", pcop->name, PCORB(pcop)->pcor.instance); + else + SNPRINTF(s, size, "%s", pcop->name); + } + return (buffer); + break; + + case PO_TWO_OPS: + return (pic16_get_op( PCOP2(pcop)->pcopL, use_buffer ? buffer : NULL, size )); + break; + + default: + if(pcop->name) { + if(use_buffer) { + SNPRINTF(buffer,size,"%s",pcop->name); + return (buffer); + } + return (pcop->name); + } + + } + return ("unhandled type for op1"); + } + + return ("NO operand1"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_get_op2 - variant to support two memory operand commands */ +/*-----------------------------------------------------------------*/ +char *pic16_get_op2(pCodeOp *pcop, char *buffer, size_t size) +{ + if(pcop && pcop->type == PO_TWO_OPS) { + return pic16_get_op(PCOP2(pcop)->pcopR, buffer, size); + } + + return "NO operand2"; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static char *pic16_get_op_from_instruction( pCodeInstruction *pcc) +{ + if(pcc) + return pic16_get_op(pcc->pcop,NULL,0); + + /* gcc 3.2: warning: concatenation of string literals with __FUNCTION__ is deprecated + * return ("ERROR Null: "__FUNCTION__); + */ + return ("ERROR Null: pic16_get_op_from_instruction"); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodeOpPrint(FILE *of, pCodeOp *pcop) +{ + fprintf(of,"pcodeopprint- not implemented\n"); +} + +/*-----------------------------------------------------------------*/ +/* pic16_pCode2str - convert a pCode instruction to string */ +/*-----------------------------------------------------------------*/ +char *pic16_pCode2str(char *str, size_t size, pCode *pc) +{ + char *s = str; + reg_info *r; + size_t len; + +#if 0 + if(isPCI(pc) && (PCI(pc)->pci_magic != PCI_MAGIC)) { + fprintf(stderr, "%s:%d: pCodeInstruction initialization error in instruction %s, magic is %x (defaut: %x)\n", + __FILE__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pci_magic, PCI_MAGIC); + // exit(EXIT_FAILURE); + } +#endif + + switch(pc->type) { + + case PC_OPCODE: + SNPRINTF(s, size, "\t%s\t", PCI(pc)->mnemonic); + len = strlen(s); + size -= len; + s += len; + + if( (PCI(pc)->num_ops >= 1) && (PCI(pc)->pcop)) { + + if (PCI(pc)->pcop->type == PO_TWO_OPS) + { + /* split into two phases due to static buffer in pic16_get_op() */ + SNPRINTF(s, size, "%s", pic16_get_op((PCI(pc)->pcop), NULL, 0)); + len = strlen(s); + size -= len; + s += len; + SNPRINTF(s, size, ", %s", pic16_get_op2((PCI(pc)->pcop), NULL, 0)); + break; + } + + if(PCI(pc)->is2LitOp) { + SNPRINTF(s,size, "%s", PCOP(PCI(pc)->pcop)->name); + break; + } + + if(PCI(pc)->isBitInst) { + if(PCI(pc)->pcop->type != PO_GPR_BIT) { + if( (((pCodeOpRegBit *)(PCI(pc)->pcop))->inBitSpace) ) + SNPRINTF(s,size,"(%s >> 3), (%s & 7)", PCI(pc)->pcop->name, PCI(pc)->pcop->name); + else + SNPRINTF(s,size,"%s,%d", pic16_get_op_from_instruction(PCI(pc)), + (((pCodeOpRegBit *)(PCI(pc)->pcop))->bit)); + + } else if(PCI(pc)->pcop->type == PO_GPR_BIT) { + SNPRINTF(s,size,"%s, %d", pic16_get_op_from_instruction(PCI(pc)),PCORB(PCI(pc)->pcop)->bit); + } else + SNPRINTF(s,size,"%s,0 ; ?bug", pic16_get_op_from_instruction(PCI(pc))); + } else { + + if(PCI(pc)->pcop->type == PO_GPR_BIT) { + if(PCI(pc)->num_ops == 3) + SNPRINTF(s,size,"(%s >> 3),%c",pic16_get_op_from_instruction(PCI(pc)),((PCI(pc)->isModReg) ? 'F':'W')); + else + SNPRINTF(s,size,"(1 << (%s & 7))",pic16_get_op_from_instruction(PCI(pc))); + } else { + SNPRINTF(s,size,"%s", pic16_get_op_from_instruction(PCI(pc))); + } + } + + if(PCI(pc)->num_ops == 3 || ((PCI(pc)->num_ops == 2) && (PCI(pc)->isAccess))) { + len = strlen(s); + size -= len; + s += len; + if(PCI(pc)->num_ops == 3 && !PCI(pc)->isBitInst) { + SNPRINTF(s,size,", %c", ( (PCI(pc)->isModReg) ? 'F':'W')); + len = strlen(s); + size -= len; + s += len; + } + + r = pic16_getRegFromInstruction(pc); + + if(PCI(pc)->isAccess) { + static char *bank_spec[2][2] = { + { "", ", ACCESS" }, /* gpasm uses access bank by default */ + { ", B", ", BANKED" }/* MPASM (should) use BANKED by default */ + }; + + SNPRINTF(s,size,"%s", bank_spec[(r && !isACCESS_BANK(r)) ? 1 : 0][pic16_mplab_comp ? 1 : 0]); + } + } + } + break; + + case PC_COMMENT: + /* assuming that comment ends with a \n */ + SNPRINTF(s,size,";%s", ((pCodeComment *)pc)->comment); + break; + + case PC_INFO: + SNPRINTF(s,size,"; info ==>"); + len = strlen(s); + size -= len; + s += len; + switch( PCINF(pc)->type ) { + case INF_OPTIMIZATION: + SNPRINTF(s,size, " [optimization] %s\n", OPT_TYPE_STR[ PCOO(PCINF(pc)->oper1)->type ]); + break; + case INF_LOCALREGS: + SNPRINTF(s,size, " [localregs] %s\n", LR_TYPE_STR[ PCOLR(PCINF(pc)->oper1)->type ]); + break; + }; break; + + case PC_INLINE: + /* assuming that inline code ends with a \n */ + SNPRINTF(s,size,"%s", ((pCodeComment *)pc)->comment); + break; + + case PC_LABEL: + SNPRINTF(s,size,";label=%s, key=%d\n",PCL(pc)->label,PCL(pc)->key); + break; + case PC_FUNCTION: + SNPRINTF(s,size,";modname=%s,function=%s: id=%d\n",PCF(pc)->modname,PCF(pc)->fname); + break; + case PC_WILD: + SNPRINTF(s,size,";\tWild opcode: id=%d\n",PCW(pc)->id); + break; + case PC_FLOW: + SNPRINTF(s,size,";\t--FLOW change.\n"); + break; + case PC_CSOURCE: + SNPRINTF(s,size,"%s\t.line\t%d; %s\t%s\n", ((pic16_mplab_comp || !options.debug)?";":""), + PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + break; + case PC_ASMDIR: + if(PCAD(pc)->directive) { + SNPRINTF(s,size,"\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else + if(PCAD(pc)->arg) { + /* special case to handle inline labels without a tab */ + SNPRINTF(s,size,"%s\n", PCAD(pc)->arg); + } + break; + + case PC_BAD: + SNPRINTF(s,size,";A bad pCode is being used.\n"); + break; + } + + return str; +} + +/*-----------------------------------------------------------------*/ +/* genericPrint - the contents of a pCode to a file */ +/*-----------------------------------------------------------------*/ +static void genericPrint(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + switch(pc->type) { + case PC_COMMENT: +// fputs(((pCodeComment *)pc)->comment, of); + fprintf(of,"; %s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_INFO: + { + pBranch *pbl = PCI(pc)->label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + + if(pic16_pcode_verbose) { + fprintf(of, "; info ==>"); + switch(((pCodeInfo *)pc)->type) { + case INF_OPTIMIZATION: + fprintf(of, " [optimization] %s\n", OPT_TYPE_STR[ PCOO(PCINF(pc)->oper1)->type ]); + break; + case INF_LOCALREGS: + fprintf(of, " [localregs] %s\n", LR_TYPE_STR[ PCOLR(PCINF(pc)->oper1)->type ]); + break; + } + } + + break; + + case PC_INLINE: + fprintf(of,"%s\n", ((pCodeComment *)pc)->comment); + break; + + case PC_OPCODE: + // If the opcode has a label, print that first + { + pBranch *pbl = PCI(pc)->label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + + if(PCI(pc)->cline) + genericPrint(of,PCODE(PCI(pc)->cline)); + + { + char str[256]; + + pic16_pCode2str(str, sizeof(str), pc); + + fprintf(of,"%s",str); + /* Debug */ + if(pic16_debug_verbose) { + fprintf(of, "\t;key=%03x",pc->seq); + if(PCI(pc)->pcflow) + fprintf(of,", flow seq=%03x",PCI(pc)->pcflow->pc.seq); + } + } + fprintf(of, "\n"); + break; + + case PC_WILD: + fprintf(of,";\tWild opcode: id=%d\n",PCW(pc)->id); + if(PCW(pc)->pci.label) + pCodePrintLabel(of, PCW(pc)->pci.label->pc); + + if(PCW(pc)->operand) { + fprintf(of,";\toperand "); + pCodeOpPrint(of,PCW(pc)->operand ); + } + break; + + case PC_FLOW: + if(pic16_debug_verbose) { + fprintf(of,";<>Start of new flow, seq=0x%x",pc->seq); + if(PCFL(pc)->ancestor) + fprintf(of," ancestor = 0x%x", PCODE(PCFL(pc)->ancestor)->seq); + fprintf(of,"\n"); + + } + break; + + case PC_CSOURCE: +// fprintf(of,";#CSRC\t%s %d\t\t%s\n", PCCS(pc)->file_name, PCCS(pc)->line_number, PCCS(pc)->line); + fprintf(of,"%s\t.line\t%d; %s\t%s\n", ((pic16_mplab_comp || !options.debug)?";":""), + PCCS(pc)->line_number, PCCS(pc)->file_name, PCCS(pc)->line); + + break; + + case PC_ASMDIR: + { + pBranch *pbl = PCAD(pc)->pci.label; + while(pbl && pbl->pc) { + if(pbl->pc->type == PC_LABEL) + pCodePrintLabel(of, pbl->pc); + pbl = pbl->next; + } + } + if(PCAD(pc)->directive) { + fprintf(of, "\t%s%s%s\n", PCAD(pc)->directive, PCAD(pc)->arg?"\t":"", PCAD(pc)->arg?PCAD(pc)->arg:""); + } else + if(PCAD(pc)->arg) { + /* special case to handle inline labels without tab */ + fprintf(of, "%s\n", PCAD(pc)->arg); + } + break; + + case PC_LABEL: + default: + fprintf(of,"unknown pCode type %d\n",pc->type); + } +} + +/*-----------------------------------------------------------------*/ +/* pCodePrintFunction - prints function begin/end */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintFunction(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + +#if 0 + if( ((pCodeFunction *)pc)->modname) + fprintf(of,"F_%s",((pCodeFunction *)pc)->modname); +#endif + + if(!PCF(pc)->absblock) { + if(PCF(pc)->fname) { + pBranch *exits = PCF(pc)->to; + int i=0; + + fprintf(of,"%s:", PCF(pc)->fname); + + if(pic16_pcode_verbose) + fprintf(of, "\t;Function start."); + + fprintf(of, "\n"); + + while(exits) { + i++; + exits = exits->next; + } + //if(i) i--; + + if(pic16_pcode_verbose) + fprintf(of,"; %d exit point%c\n",i, ((i==1) ? ' ':'s')); + + } else { + if((PCF(pc)->from && + PCF(pc)->from->pc->type == PC_FUNCTION && + PCF(PCF(pc)->from->pc)->fname) ) { + + if(pic16_pcode_verbose) + fprintf(of,"; exit point of %s\n",PCF(PCF(pc)->from->pc)->fname); + } else { + if(pic16_pcode_verbose) + fprintf(of,"; exit point [can't find entry point]\n"); + } + fprintf(of, "\n"); + } + } +} +/*-----------------------------------------------------------------*/ +/* pCodePrintLabel - prints label */ +/*-----------------------------------------------------------------*/ + +static void pCodePrintLabel(FILE *of, pCode *pc) +{ + if(!pc || !of) + return; + + if(PCL(pc)->label) + fprintf(of,"%s:\n",PCL(pc)->label); + else if (PCL(pc)->key >=0) + fprintf(of,"_%05d_DS_:\n",PCL(pc)->key); + else + fprintf(of,";wild card label: id=%d\n",-PCL(pc)->key); + +} +/*-----------------------------------------------------------------*/ +/* unlinkpCodeFromBranch - Search for a label in a pBranch and */ +/* remove it if it is found. */ +/*-----------------------------------------------------------------*/ +static void unlinkpCodeFromBranch(pCode *pcl , pCode *pc) +{ + pBranch *b, *bprev; + + bprev = NULL; + + if(pcl->type == PC_OPCODE || pcl->type == PC_INLINE || pcl->type == PC_ASMDIR) + b = PCI(pcl)->label; + else { + fprintf(stderr, "LINE %d. can't unlink from non opcode.\n",__LINE__); + exit(1); + } + + //fprintf (stderr, "%s \n",__FUNCTION__); + //pcl->print(stderr,pcl); + //pc->print(stderr,pc); + while(b) { + if(b->pc == pc) { + //fprintf (stderr, "found label\n"); + //pc->print(stderr, pc); + + /* Found a label */ + if(bprev) { + bprev->next = b->next; /* Not first pCode in chain */ +// Safe_free(b); + } else { + pc->destruct(pc); + PCI(pcl)->label = b->next; /* First pCode in chain */ +// Safe_free(b); + } + return; /* A label can't occur more than once */ + } + bprev = b; + b = b->next; + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +pBranch * pic16_pBranchAppend(pBranch *h, pBranch *n) +{ + pBranch *b; + + if(!h) + return n; + + if(h == n) + return n; + + b = h; + while(b->next) + b = b->next; + + b->next = n; + + return h; +} + +/*-----------------------------------------------------------------*/ +/* pBranchLink - given two pcodes, this function will link them */ +/* together through their pBranches */ +/*-----------------------------------------------------------------*/ +static void pBranchLink(pCodeFunction *f, pCodeFunction *t) +{ + pBranch *b; + + // Declare a new branch object for the 'from' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(t); // The link to the 'to' pCode. + b->next = NULL; + + f->to = pic16_pBranchAppend(f->to,b); + + // Now do the same for the 'to' pCode. + + //_ALLOC(b,sizeof(pBranch)); + b = Safe_alloc(sizeof(pBranch)); + b->pc = PCODE(f); + b->next = NULL; + + t->from = pic16_pBranchAppend(t->from,b); +} + +#if 1 +/*-----------------------------------------------------------------*/ +/* pBranchFind - find the pBranch in a pBranch chain that contains */ +/* a pCode */ +/*-----------------------------------------------------------------*/ +static pBranch *pBranchFind(pBranch *pb,pCode *pc) +{ + while(pb) { + + if(pb->pc == pc) + return pb; + + pb = pb->next; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_pCodeUnlink - Unlink the given pCode from its pCode chain. */ +/*-----------------------------------------------------------------*/ +void pic16_pCodeUnlink(pCode *pc) +{ + pBranch *pb1,*pb2; + pCode *pc1; + + if (!pc) { + return; + } + + /* Remove the branches */ + + pb1 = PCI(pc)->from; + while(pb1) { + pc1 = pb1->pc; /* Get the pCode that branches to the + * one we're unlinking */ + + /* search for the link back to this pCode (the one we're + * unlinking) */ + if((pb2 = pBranchFind(PCI(pc1)->to,pc))) { + pb2->pc = PCI(pc)->to->pc; // make the replacement + + /* if the pCode we're unlinking contains multiple 'to' + * branches (e.g. this a skip instruction) then we need + * to copy these extra branches to the chain. */ + if(PCI(pc)->to->next) + pic16_pBranchAppend(pb2, PCI(pc)->to->next); + } + + pb1 = pb1->next; + } + + pic16_unlinkpCode (pc); +} +#endif +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void genericAnalyze(pCode *pc) +{ + switch(pc->type) { + case PC_WILD: + case PC_COMMENT: + return; + case PC_LABEL: + case PC_FUNCTION: + case PC_OPCODE: + { + // Go through the pCodes that are in pCode chain and link + // them together through the pBranches. Note, the pCodes + // are linked together as a contiguous stream like the + // assembly source code lines. The linking here mimics this + // except that comments are not linked in. + // + pCode *npc = pc->next; + while(npc) { + if(npc->type == PC_OPCODE || npc->type == PC_LABEL) { + pBranchLink(pc,npc); + return; + } else + npc = npc->next; + } + /* reached the end of the pcode chain without finding + * an instruction we could link to. */ + } + break; + case PC_FLOW: + fprintf(stderr,"analyze PC_FLOW\n"); + + return; + case PC_BAD: + fprintf(stderr,,";A bad pCode is being used\n"); + + } +} +#endif + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int compareLabel(const pCode *pc, const pCodeOpLabel *pcop_label) +{ + pBranch *pbr; + + if(pc->type == PC_LABEL) { + if(((pCodeLabel *)pc)->key == pcop_label->key) + return TRUE; + } + + if((pc->type == PC_OPCODE) || (pc->type == PC_ASMDIR)) { + pbr = PCI(pc)->label; + while(pbr) { + if(pbr->pc->type == PC_LABEL) { + if(((pCodeLabel *)(pbr->pc))->key == pcop_label->key) + return TRUE; + } + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int checkLabel(const pCode *pc) +{ + pBranch *pbr; + + if(pc && isPCI(pc)) { + pbr = PCI(pc)->label; + while(pbr) { + if(isPCL(pbr->pc) && (PCL(pbr->pc)->key >= 0)) + return TRUE; + + pbr = pbr->next; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* findLabelinpBlock - Search the pCode for a particular label */ +/*-----------------------------------------------------------------*/ +static pCode *findLabelinpBlock(pBlock *pb, const pCodeOpLabel *pcop_label) +{ + pCode *pc; + + if(!pb) + return NULL; + + for(pc = pb->pcHead; pc; pc = pc->next) + if(compareLabel(pc,pcop_label)) + return pc; + + return NULL; +} +#if 0 +/*-----------------------------------------------------------------*/ +/* findLabel - Search the pCode for a particular label */ +/*-----------------------------------------------------------------*/ +static pCode *findLabel(const pCodeOpLabel *pcop_label) +{ + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return NULL; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if((pc = findLabelinpBlock(pb,pcop_label)) != NULL) + return pc; + } + + fprintf(stderr,"Couldn't find label %s", pcop_label->pcop.name); + return NULL; +} +#endif +/*-----------------------------------------------------------------*/ +/* pic16_findNextpCode - given a pCode, find the next of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) + return pc; + + pc = pc->next; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* findPrevpCode - given a pCode, find the previous of type 'pct' */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +static pCode * findPrevpCode(pCode *pc, PC_TYPE pct) +{ + + while(pc) { + if(pc->type == pct) + return pc; + + pc = pc->prev; + } + + return NULL; +} + + +//#define PCODE_DEBUG +/*-----------------------------------------------------------------*/ +/* pic16_findNextInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findNextInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + if((pc->type == PC_OPCODE) + || (pc->type == PC_WILD) + || (pc->type == PC_ASMDIR) + ) + return pc; + +#ifdef PCODE_DEBUG + fprintf(stderr,"pic16_findNextInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->next; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_findPrevInstruction - given a pCode, find the next instruction */ +/* in the linked list */ +/*-----------------------------------------------------------------*/ +pCode * pic16_findPrevInstruction(pCode *pci) +{ + pCode *pc = pci; + + while(pc) { + + if((pc->type == PC_OPCODE) || (pc->type == PC_WILD) || (pc->type == PC_ASMDIR)) + return pc; + +#ifdef PCODE_DEBUG + fprintf(stderr,"pic16_findPrevInstruction: "); + printpCode(stderr, pc); +#endif + pc = pc->prev; + } + + //fprintf(stderr,"Couldn't find instruction\n"); + return NULL; +} + +#undef PCODE_DEBUG + +#if 0 +/*-----------------------------------------------------------------*/ +/* findFunctionEnd - given a pCode find the end of the function */ +/* that contains it */ +/*-----------------------------------------------------------------*/ +static pCode * findFunctionEnd(pCode *pc) +{ + while(pc) { + if(pc->type == PC_FUNCTION && !(PCF(pc)->fname)) + return pc; + + pc = pc->next; + } + + fprintf(stderr,"Couldn't find function end\n"); + return NULL; +} +#endif +#if 0 +/*-----------------------------------------------------------------*/ +/* AnalyzeLabel - if the pCode is a label, then merge it with the */ +/* instruction with which it is associated. */ +/*-----------------------------------------------------------------*/ +static void AnalyzeLabel(pCode *pc) +{ + pic16_pCodeUnlink(pc); +} +#endif + +#if 0 +static void AnalyzeGOTO(pCode *pc) +{ + pBranchLink(pc,findLabel( (pCodeOpLabel *) (PCI(pc)->pcop) )); +} + +static void AnalyzeSKIP(pCode *pc) +{ + pBranchLink(pc,pic16_findNextInstruction(pc->next)); + pBranchLink(pc,pic16_findNextInstruction(pc->next->next)); +} + +static void AnalyzeRETURN(pCode *pc) +{ + // branch_link(pc,findFunctionEnd(pc->next)); +} + +#endif + +/*-------------------------------------------------------------------*/ +/* pic16_getRegFrompCodeOp - extract the register from a pCodeOp */ +/* if one is present. This is the common */ +/* part of pic16_getRegFromInstruction(2) */ +/*-------------------------------------------------------------------*/ + +reg_info * pic16_getRegFrompCodeOp (pCodeOp *pcop) +{ + if (!pcop) return NULL; + + switch(pcop->type) { + case PO_PRODL: + case PO_PRODH: + case PO_INDF0: + case PO_FSR0: + case PO_W: + case PO_WREG: + case PO_STATUS: + case PO_INTCON: + case PO_PCL: + case PO_PCLATH: + case PO_PCLATU: + case PO_BSR: + return PCOR(pcop)->r; + + case PO_SFR_REGISTER: + //fprintf (stderr, "%s - SFR\n", __FUNCTION__); + return PCOR(pcop)->r; + + case PO_BIT: + case PO_GPR_TEMP: +// fprintf(stderr, "pic16_getRegFromInstruction - bit or temp\n"); + return PCOR(pcop)->r; + + case PO_IMMEDIATE: +// return pic16_dirregWithName(PCOI(pcop)->r->name); +/* if(PCOI(pcop)->r) + return (PCOI(pcop)->r); + else + return NULL;*/ + return (PCOI(pcop)->r); + + case PO_GPR_BIT: + return PCOR(pcop)->r; + + case PO_GPR_REGISTER: + case PO_DIR: +// fprintf(stderr, "pic16_getRegFromInstruction - dir\n"); + return PCOR(pcop)->r; + + case PO_LITERAL: + //fprintf(stderr, "pic16_getRegFromInstruction - literal\n"); + break; + + case PO_REL_ADDR: + case PO_LABEL: + //fprintf (stderr, "%s - label or address: %d (%s)\n", __FUNCTION__, pcop->type, dumpPicOptype(pcop->type)); + break; + + case PO_CRY: + case PO_STR: + /* this should never turn up */ + //fprintf (stderr, "%s - unused pCodeOp->type: %d (%s)\n", __FUNCTION__, pcop->type, dumpPicOptype(pcop->type)); + break; + + case PO_WILD: + break; + + case PO_TWO_OPS: + return pic16_getRegFrompCodeOp( PCOP2(pcop)->pcopL ); + break; + + default: + fprintf(stderr, "pic16_getRegFrompCodeOp - unknown reg type %d (%s)\n",pcop->type, dumpPicOptype (pcop->type)); +// assert( 0 ); + break; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +reg_info * pic16_getRegFromInstruction(pCode *pc) +{ + if(!pc || + !isPCI(pc) || + !PCI(pc)->pcop || + PCI(pc)->num_ops == 0 || + (PCI(pc)->num_ops == 1 && PCI(pc)->isFastCall)) + return NULL; + +#if 0 + fprintf(stderr, "pic16_getRegFromInstruction - reg type %s (%d)\n", + dumpPicOptype( PCI(pc)->pcop->type), PCI(pc)->pcop->type); +#endif + + return( pic16_getRegFrompCodeOp (PCI(pc)->pcop) ); +} + +/*-------------------------------------------------------------------------------*/ +/* pic16_getRegFromInstruction2 - variant to support two memory operand commands */ +/*-------------------------------------------------------------------------------*/ +reg_info * pic16_getRegFromInstruction2(pCode *pc) +{ + if(!pc || + !isPCI(pc) || + !PCI(pc)->pcop || + PCI(pc)->num_ops == 0 || + (PCI(pc)->num_ops == 1)) // accept only 2 operand commands + return NULL; + + if (PCI(pc)->pcop->type != PO_TWO_OPS) + return NULL; + +#if 0 + fprintf(stderr, "pic16_getRegFromInstruction2 - reg type %s (%d)\n", + dumpPicOptype( PCI(pc)->pcop->type), PCI(pc)->pcop->type); +#endif + + return pic16_getRegFrompCodeOp (PCOP2(PCI(pc)->pcop)->pcopR); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static void AnalyzepBlock(pBlock *pb) +{ + pCode *pc; + + if(!pb) + return; + + /* Find all of the registers used in this pBlock + * by looking at each instruction and examining it's + * operands + */ + for(pc = pb->pcHead; pc; pc = pc->next) { + + /* Is this an instruction with operands? */ + if(pc->type == PC_OPCODE && PCI(pc)->pcop) { + + if(PCI(pc)->pcop->type == PO_GPR_TEMP) { + + /* Loop through all of the registers declared so far in + this block and see if we find this one there */ + + reg_info *r = setFirstItem(pb->tregisters); + + while(r) { + if(r->rIdx == PCOR(PCI(pc)->pcop)->r->rIdx) { + PCOR(PCI(pc)->pcop)->r = r; + break; + } + r = setNextItem(pb->tregisters); + } + + if(!r) { + /* register wasn't found */ + //r = Safe_alloc(sizeof(regs)); + //memcpy(r,PCOR(PCI(pc)->pcop)->r, sizeof(regs)); + //addSet(&pb->tregisters, r); + addSet(&pb->tregisters, PCOR(PCI(pc)->pcop)->r); + //PCOR(PCI(pc)->pcop)->r = r; + //fprintf(stderr,"added register to pblock: reg %d\n",r->rIdx); + }/* else + fprintf(stderr,"found register in pblock: reg %d\n",r->rIdx); + */ + } + if(PCI(pc)->pcop->type == PO_GPR_REGISTER) { + if(PCOR(PCI(pc)->pcop)->r) { + pic16_allocWithIdx(PCOR(PCI(pc)->pcop)->r->rIdx); /* FIXME! - VR */ + DFPRINTF((stderr,"found register in pblock: reg 0x%x\n",PCOR(PCI(pc)->pcop)->r->rIdx)); + } else { + if(PCI(pc)->pcop->name) + fprintf(stderr,"ERROR: %s is a NULL register\n",PCI(pc)->pcop->name ); + else + fprintf(stderr,"ERROR: NULL register\n"); + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ +#define PCI_HAS_LABEL(x) ((x) && (PCI(x)->label != NULL)) + +static void InsertpFlow(pCode *pc, pCode **pflow) +{ + if(*pflow) + PCFL(*pflow)->end = pc; + + if(!pc || !pc->next) + return; + + *pflow = pic16_newpCodeFlow(); + pic16_pCodeInsertAfter(pc, *pflow); +} + +/*-----------------------------------------------------------------*/ +/* pic16_BuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* + * pic16_BuildFlow inserts pCodeFlow objects into the pCode chain at each + * point the instruction flow changes. + */ +/*-----------------------------------------------------------------*/ +void pic16_BuildFlow(pBlock *pb) +{ + pCode *pc; + pCode *last_pci=NULL; + pCode *pflow=NULL; + int seq = 0; + + if(!pb) + return; + + //fprintf (stderr,"build flow start seq %d ",GpcFlowSeq); + /* Insert a pCodeFlow object at the beginning of a pBlock */ + + InsertpFlow(pb->pcHead, &pflow); + + //pflow = pic16_newpCodeFlow(); /* Create a new Flow object */ + //pflow->next = pb->pcHead; /* Make the current head the next object */ + //pb->pcHead->prev = pflow; /* let the current head point back to the flow object */ + //pb->pcHead = pflow; /* Make the Flow object the head */ + //pflow->pb = pb; + + for( pc = pic16_findNextInstruction(pb->pcHead); + pc != NULL; + pc=pic16_findNextInstruction(pc)) { + + pc->seq = seq++; + PCI(pc)->pcflow = PCFL(pflow); + + //fprintf(stderr," build: "); + //pflow->print(stderr,pflow); + + if (checkLabel(pc)) { + + /* This instruction marks the beginning of a + * new flow segment */ + + pc->seq = 0; + seq = 1; + + /* If the previous pCode is not a flow object, then + * insert a new flow object. (This check prevents + * two consecutive flow objects from being insert in + * the case where a skip instruction preceeds an + * instruction containing a label.) */ + + if(last_pci && (PCI(last_pci)->pcflow == PCFL(pflow))) + InsertpFlow(pic16_findPrevInstruction(pc->prev), &pflow); + + PCI(pc)->pcflow = PCFL(pflow); + + } + + if( PCI(pc)->isSkip) { + + /* The two instructions immediately following this one + * mark the beginning of a new flow segment */ + + while(pc && PCI(pc)->isSkip) { + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = seq-1; + seq = 1; + + InsertpFlow(pc, &pflow); + pc=pic16_findNextInstruction(pc->next); + } + + seq = 0; + + if(!pc) + break; + + PCI(pc)->pcflow = PCFL(pflow); + pc->seq = 0; + InsertpFlow(pc, &pflow); + + } else if ( PCI(pc)->isBranch && !checkLabel(pic16_findNextInstruction(pc->next))) { + + InsertpFlow(pc, &pflow); + seq = 0; + + } + last_pci = pc; + pc = pc->next; + } + + //fprintf (stderr,",end seq %d",GpcFlowSeq); + if(pflow) + PCFL(pflow)->end = pb->pcTail; +} + +/*-------------------------------------------------------------------*/ +/* unBuildFlow(pBlock *pb) - examine the code in a pBlock and build */ +/* the flow blocks. */ +/* + * unBuildFlow removes pCodeFlow objects from a pCode chain + */ +/*-----------------------------------------------------------------*/ +static void unBuildFlow(pBlock *pb) +{ + pCode *pc,*pcnext; + + if(!pb) + return; + + pc = pb->pcHead; + + while(pc) { + pcnext = pc->next; + + if(isPCI(pc)) { + + pc->seq = 0; + if(PCI(pc)->pcflow) { + //Safe_free(PCI(pc)->pcflow); + PCI(pc)->pcflow = NULL; + } + + } else if(isPCFL(pc) ) + pc->destruct(pc); + + pc = pcnext; + } +} +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void dumpCond(int cond) +{ + static const char *pcc_str[] = { + //"PCC_NONE", + "PCC_REGISTER", + "PCC_C", + "PCC_Z", + "PCC_DC", + "PCC_OV", + "PCC_N", + "PCC_W", + "PCC_EXAMINE_PCOP", + "PCC_LITERAL", + "PCC_REL_ADDR" + }; + + int ncond = sizeof(pcc_str) / sizeof(char *); + int i,j; + + fprintf(stderr, "0x%04X\n",cond); + + for(i=0,j=1; ipc.seq); + + pc = pic16_findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + fprintf(stderr, " FlowStats - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + fprintf(stderr, " FlowStats inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FlowStats outCond: "); + dumpCond(pcflow->outCond); +} +#endif +/*-----------------------------------------------------------------* + * int isBankInstruction(pCode *pc) - examine the pCode *pc to determine + * if it affects the banking bits. + * + * return: -1 == Banking bits are unaffected by this pCode. + * + * return: > 0 == Banking bits are affected. + * + * If the banking bits are affected, then the returned value describes + * which bits are affected and how they're affected. The lower half + * of the integer maps to the bits that are affected, the upper half + * to whether they're set or cleared. + * + *-----------------------------------------------------------------*/ + +static int isBankInstruction(pCode *pc) +{ + reg_info *reg; + + if(!isPCI(pc)) + return FALSE; + + if((PCI(pc)->op == POC_MOVLB) || + (((reg = pic16_getRegFromInstruction(pc)) != NULL) && isBSR_REG(reg))) { + } + + return TRUE; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void FillFlow(pCodeFlow *pcflow) +{ + pCode *pc; + + if(!isPCFL(pcflow)) + return; + + // fprintf(stderr, " FillFlow - flow block (seq=%d)\n", pcflow->pc.seq); + + pc = pic16_findNextpCode(PCODE(pcflow), PC_OPCODE); + + if(!pc) { + //fprintf(stderr, " FillFlow - empty flow (seq=%d)\n", pcflow->pc.seq); + return; + } + + do { + isBankInstruction(pc); + pc = pc->next; + } while (pc && (pc != pcflow->end) && !isPCFL(pc)); + +/* + if(!pc ) { + fprintf(stderr, " FillFlow - Bad end of flow\n"); + } else { + fprintf(stderr, " FillFlow - Ending flow with\n "); + pc->print(stderr,pc); + } + + fprintf(stderr, " FillFlow inCond: "); + dumpCond(pcflow->inCond); + fprintf(stderr, " FillFlow outCond: "); + dumpCond(pcflow->outCond); +*/ +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void LinkFlow_pCode(pCodeInstruction *from, pCodeInstruction *to) +{ + pCodeFlowLink *fromLink, *toLink; + + if(!from || !to || !to->pcflow || !from->pcflow) + return; + + fromLink = pic16_newpCodeFlowLink(from->pcflow); + toLink = pic16_newpCodeFlowLink(to->pcflow); + + addSetIfnotP(&(from->pcflow->to), toLink); //to->pcflow); + addSetIfnotP(&(to->pcflow->from), fromLink); //from->pcflow); +} + +static pCode *pic16_getJumptabpCode (pCode *pc) +{ + pCode *pcinf; + + //fprintf (stderr, "%s - start for %p in %p", __FUNCTION__, pc, isPCI(pc) ? PCI(pc)->pcflow : NULL); + //pc->print (stderr, pc); + pcinf = pc; + while (pcinf) { + if (isPCI(pcinf) && PCI(pcinf)->op != POC_GOTO) return NULL; + if (pcinf->type == PC_INFO && PCINF(pcinf)->type == INF_OPTIMIZATION) { + switch (PCOO(PCINF(pcinf)->oper1)->type) { + case OPT_JUMPTABLE_BEGIN: + /* leading begin of jump table -- in one */ + pcinf = pic16_findPrevInstruction (pcinf); + return pcinf; + break; + + case OPT_JUMPTABLE_END: + /* leading end of jumptable -- not in one */ + return NULL; + break; + + default: + /* ignore all other PCInfos */ + break; + } + } + pcinf = pcinf->prev; + } + + /* no PCInfo found -- not in a jumptable */ + return NULL; +} + +/*-----------------------------------------------------------------* + * void LinkFlow(pBlock *pb) + * + * In pic16_BuildFlow, the PIC code has been partitioned into contiguous + * non-branching segments. In LinkFlow, we determine the execution + * order of these segments. For example, if one of the segments ends + * with a skip, then we know that there are two possible flow segments + * to which control may be passed. + *-----------------------------------------------------------------*/ +static void LinkFlow(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcflow; + pCode *pct; + pCode *jumptab_pre = NULL; + + //fprintf(stderr,"linkflow \n"); + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) + fprintf(stderr, "LinkFlow - pcflow is not a flow object "); + + //fprintf(stderr," link: "); + //pcflow->print(stderr,pcflow); + + //FillFlow(PCFL(pcflow)); + + pc = PCFL(pcflow)->end; + + //fprintf(stderr, "LinkFlow - flow block (seq=%d) ", pcflow->seq); + if(isPCI_SKIP(pc)) { +// fprintf(stderr, "ends with skip\n"); +// pc->print(stderr,pc); + + pct=pic16_findNextInstruction(pc->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + pct=pic16_findNextInstruction(pct->next); + LinkFlow_pCode(PCI(pc),PCI(pct)); + continue; + } + + if(isPCI_BRANCH(pc)) { + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + + /* handle GOTOs in jumptables */ + if ((jumptab_pre = pic16_getJumptabpCode (pc)) != NULL) { + /* link to previous flow */ + //fprintf (stderr, "linked jumptable GOTO to predecessor %p\n", PCI(jumptab_pre)->pcflow); + LinkFlow_pCode (PCI(jumptab_pre), PCI(pc)); + } + + switch (PCI(pc)->op) { + case POC_GOTO: + case POC_BRA: + case POC_RETURN: + case POC_RETLW: + case POC_RETFIE: + /* unconditional branches -- do not link to next instruction */ + //fprintf (stderr, "%s: flow ended by unconditional branch\n", __FUNCTION__); + break; + + case POC_CALL: + case POC_RCALL: + /* unconditional calls -- link to next instruction */ + //fprintf (stderr, "%s: flow ended by CALL\n", __FUNCTION__); + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + break; + + case POC_BC: + case POC_BN: + case POC_BNC: + case POC_BNN: + case POC_BNOV: + case POC_BNZ: + case POC_BOV: + case POC_BZ: + /* conditional branches -- also link to next instruction */ + //fprintf (stderr, "%s: flow ended by conditional branch\n", __FUNCTION__); + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + break; + + default: + fprintf (stderr, "%s: unhandled op %u (%s)\n", __FUNCTION__, PCI(pc)->op , PCI(pc)->mnemonic); + assert (0 && "unhandled branching instruction"); + break; + } + + //fprintf(stderr, "ends with branch\n "); + //pc->print(stderr,pc); + + if(!(pcol && isPCOLAB(pcol))) { + if((PCI(pc)->op != POC_RETLW) + && (PCI(pc)->op != POC_RETURN) && (PCI(pc)->op != POC_CALL) && (PCI(pc)->op != POC_RCALL) && (PCI(pc)->op != POC_RETFIE) ) { + + /* continue if label is '$' which assembler knows how to parse */ + if(((PCI(pc)->pcop->type == PO_STR) && !strcmp(PCI(pc)->pcop->name, "$")))continue; + + if(pic16_pcode_verbose) { + pc->print(stderr,pc); + fprintf(stderr, "ERROR: %s, branch instruction doesn't have label\n",__FUNCTION__); + } + } + continue; + } + + if( (pct = findLabelinpBlock(pb,pcol)) != NULL) + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pct))); + else + fprintf(stderr, "ERROR: %s, couldn't find label. key=%d,lab=%s\n", + __FUNCTION__,pcol->key,((PCOP(pcol)->name)?PCOP(pcol)->name:"-")); + +// fprintf(stderr,"pic16_newpCodeOpLabel: key=%d, name=%s\n",pcol->key,(PCOP(pcol)->name)?(PCOP(pcol)->name):""); + + continue; + } + + if(isPCI(pc)) { + //fprintf(stderr, "ends with non-branching instruction:\n"); + //pc->print(stderr,pc); + + LinkFlow_pCode(PCI(pc),PCI(pic16_findNextInstruction(pc->next))); + + continue; + } + + if(pc) { + //fprintf(stderr, "ends with unknown\n"); + //pc->print(stderr,pc); + continue; + } + + //fprintf(stderr, "ends with nothing: ERROR\n"); + } +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +int pic16_isPCinFlow(const pCode *pc, const pCode *pcflow) +{ + if(!pc || !pcflow) + return FALSE; + + if((!isPCI(pc) && !isPCAD(pc)) || !PCI(pc)->pcflow || !isPCFL(pcflow) ) + return FALSE; + + if( PCI(pc)->pcflow->pc.seq == pcflow->seq) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* insertBankSwitch - inserts a bank switch statement in the */ +/* assembly listing */ +/* */ +/* position == 0: insert before */ +/* position == 1: insert after pc */ +/* position == 2: like 0 but previous was a skip instruction */ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_popGetLabel(unsigned int key); +extern int pic16_labelOffset; + +static void insertBankSwitch(unsigned char position, pCode *pc) +{ + pCode *new_pc; + + if(!pc) + return; + + /* emit BANKSEL [symbol] */ + + + new_pc = pic16_newpCodeAsmDir("BANKSEL", "%s", pic16_get_op_from_instruction(PCI(pc))); + +// position = 0; // position is always before (sanity check!) + +#if 0 + fprintf(stderr, "%s:%d: inserting bank switch (pos: %d)\n", __FUNCTION__, __LINE__, position); + pc->print(stderr, pc); +#endif + + switch(position) { + case 1: { + /* insert the bank switch after this pc instruction */ + pCode *pcnext = pic16_findNextInstruction(pc); + + pic16_pCodeInsertAfter(pc, new_pc); + if(pcnext)pc = pcnext; + }; break; + + case 0: + /* insert the bank switch BEFORE this pc instruction */ + pic16_pCodeInsertAfter(pc->prev, new_pc); + break; + + case 2: { + symbol *tlbl; + pCode *pcnext, *pcprev, *npci, *ppc; + PIC_OPCODE ipci; + int ofs1=0, ofs2=0; + + /* just like 0, but previous was a skip instruction, + * so some care should be taken */ + + pic16_labelOffset += 10000; + tlbl = newiTempLabel(NULL); + + /* invert skip instruction */ + pcprev = pic16_findPrevInstruction(pc->prev); + ipci = PCI(pcprev)->inverted_op; + npci = pic16_newpCode(ipci, PCI(pcprev)->pcop); + +// fprintf(stderr, "%s:%d old OP: %d\tnew OP: %d\n", __FILE__, __LINE__, PCI(pcprev)->op, ipci); + + /* copy info from old pCode */ + ofs1 = ofs2 = sizeof( pCode ) + sizeof(PIC_OPCODE); + ofs1 += strlen( PCI(pcprev)->mnemonic) + 1; + ofs2 += strlen( PCI(npci)->mnemonic) + 1; + memcpy(&PCI(npci)->from, &PCI(pcprev)->from, (char *)(&(PCI(npci)->pci_magic)) - (char *)(&(PCI(npci)->from))); + PCI(npci)->op = PCI(pcprev)->inverted_op; + + /* unlink old pCode */ + ppc = pcprev->prev; + ppc->next = pcprev->next; + pcprev->next->prev = ppc; + pic16_pCodeInsertAfter(ppc, npci); + + /* extra instructions to handle invertion */ + pcnext = pic16_newpCode(POC_BRA, pic16_popGetLabel(tlbl->key)); + pic16_pCodeInsertAfter(npci, pcnext); + pic16_pCodeInsertAfter(pc->prev, new_pc); + + pcnext = pic16_newpCodeLabel(NULL,tlbl->key+100+pic16_labelOffset); + pic16_pCodeInsertAfter(pc, pcnext); + }; break; + } + + + /* Move the label, if there is one */ + if(PCI(pc)->label) { +// fprintf(stderr, "%s:%d: moving label due to bank switch directive src= 0x%p dst= 0x%p\n", +// __FILE__, __LINE__, pc, new_pc); + PCAD(new_pc)->pci.label = PCI(pc)->label; + PCI(pc)->label = NULL; + } +} + + +#if 0 +/*-----------------------------------------------------------------*/ +/*int compareBankFlow - compare the banking requirements between */ +/* flow objects. */ +/*-----------------------------------------------------------------*/ +static int compareBankFlow(pCodeFlow *pcflow, pCodeFlowLink *pcflowLink, int toORfrom) +{ + + if(!pcflow || !pcflowLink || !pcflowLink->pcflow) + return FALSE; + + if(!isPCFL(pcflow) || !isPCFL(pcflowLink->pcflow)) + return FALSE; + + if(pcflow->firstBank == -1) + return FALSE; + + if(pcflowLink->pcflow->firstBank == -1) { + pCodeFlowLink *pctl = setFirstItem( toORfrom ? + pcflowLink->pcflow->to : + pcflowLink->pcflow->from); + return compareBankFlow(pcflow, pctl, toORfrom); + } + + if(toORfrom) { + if(pcflow->lastBank == pcflowLink->pcflow->firstBank) + return FALSE; + + pcflowLink->bank_conflict++; + pcflowLink->pcflow->FromConflicts++; + pcflow->ToConflicts++; + } else { + + if(pcflow->firstBank == pcflowLink->pcflow->lastBank) + return FALSE; + + pcflowLink->bank_conflict++; + pcflowLink->pcflow->ToConflicts++; + pcflow->FromConflicts++; + + } + /* + fprintf(stderr,"compare flow found conflict: seq %d from conflicts %d, to conflicts %d\n", + pcflowLink->pcflow->pc.seq, + pcflowLink->pcflow->FromConflicts, + pcflowLink->pcflow->ToConflicts); + */ + return TRUE; +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void DumpFlow(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcflow; + pCodeFlowLink *pcfl; + + fprintf(stderr,"Dump flow \n"); + pb->pcHead->print(stderr, pb->pcHead); + + pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow->print(stderr,pcflow); + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) { + fprintf(stderr, "DumpFlow - pcflow is not a flow object "); + continue; + } + fprintf(stderr,"dumping: "); + pcflow->print(stderr,pcflow); + FlowStats(PCFL(pcflow)); + + for(pcfl = setFirstItem(PCFL(pcflow)->to); pcfl; pcfl=setNextItem(PCFL(pcflow)->to)) { + + pc = PCODE(pcfl->pcflow); + + fprintf(stderr, " from seq %d:\n",pc->seq); + if(!isPCFL(pc)) { + fprintf(stderr,"oops dumpflow - from is not a pcflow\n"); + pc->print(stderr,pc); + } + } + + for(pcfl = setFirstItem(PCFL(pcflow)->to); pcfl; pcfl=setNextItem(PCFL(pcflow)->to)) { + + pc = PCODE(pcfl->pcflow); + + fprintf(stderr, " to seq %d:\n",pc->seq); + if(!isPCFL(pc)) { + fprintf(stderr,"oops dumpflow - to is not a pcflow\n"); + pc->print(stderr,pc); + } + } + } +} +#endif +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepBlock(pBlock *pb) +{ + pCode *pc, *pcprev; + int matches =0; + + if(!pb || !peepOptimizing) + return 0; + + DFPRINTF((stderr," Optimizing pBlock: %c\n",getpBlock_dbName(pb))); +/* + for(pc = pb->pcHead; pc; pc = pc->next) + matches += pic16_pCodePeepMatchRule(pc); +*/ + + pc = pic16_findNextInstruction(pb->pcHead); + if(!pc) + return 0; + + pcprev = pc->prev; + do { + if(pic16_pCodePeepMatchRule(pc)) { + matches++; + + if(pcprev) + pc = pic16_findNextInstruction(pcprev->next); + else + pc = pic16_findNextInstruction(pb->pcHead); + } else + pc = pic16_findNextInstruction(pc->next); + } while(pc); + + if(matches) + DFPRINTF((stderr," Optimizing pBlock: %c - matches=%d\n",getpBlock_dbName(pb),matches)); + + return matches; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static pCode * findInstructionUsingLabel(pCodeLabel *pcl, pCode *pcs) +{ + pCode *pc; + + for(pc = pcs; pc; pc = pc->next) { + + if(((pc->type == PC_OPCODE) || (pc->type == PC_INLINE) || (pc->type == PC_ASMDIR)) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL) && + (PCOLAB(PCI(pc)->pcop)->key == pcl->key)) + return pc; + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void exchangeLabels(pCodeLabel *pcl, pCode *pc) +{ + const char *s; + + if(isPCI(pc) && + (PCI(pc)->pcop) && + (PCI(pc)->pcop->type == PO_LABEL)) { + + pCodeOpLabel *pcol = PCOLAB(PCI(pc)->pcop); + +// fprintf(stderr,"changing label key from %d to %d\n",pcol->key, pcl->key); +// if(pcol->pcop.name) +// Safe_free(pcol->pcop.name); + + /* If the key is negative, then we (probably) have a label to + * a function and the name is already defined */ + + if(pcl->key>0) { + SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_", pcl->key); + s = buffer; + } + else + s = pcl->label; + + //SNPRINTF(buffer, sizeof(buffer), "_%05d_DS_",pcl->key); + if(!s) { + fprintf(stderr, "ERROR %s:%d function label is null\n",__FUNCTION__,__LINE__); + } + pcol->pcop.name = Safe_strdup(s); + pcol->key = pcl->key; + //pc->print(stderr,pc); + } +} + +/*-----------------------------------------------------------------*/ +/* pBlockRemoveUnusedLabels - remove the pCode labels from the */ +/* pCode chain if they're not used. */ +/*-----------------------------------------------------------------*/ +static void pBlockRemoveUnusedLabels(pBlock *pb) +{ + pCode *pc; + pCodeLabel *pcl; + + if(!pb || !pb->pcHead) + return; + + for(pc = pb->pcHead; (pc=pic16_findNextInstruction(pc->next)) != NULL; ) { + + pBranch *pbr = PCI(pc)->label; + if(pbr && pbr->next) { + pCode *pcd = pb->pcHead; + +// fprintf(stderr, "multiple labels\n"); +// pc->print(stderr,pc); + + pbr = pbr->next; + while(pbr) { + + while ( (pcd = findInstructionUsingLabel(PCL(PCI(pc)->label->pc), pcd)) != NULL) { + //fprintf(stderr,"Used by:\n"); + //pcd->print(stderr,pcd); + + exchangeLabels(PCL(pbr->pc),pcd); + + pcd = pcd->next; + } + pbr = pbr->next; + } + } + } + + for(pc = pb->pcHead; pc; pc = pc->next) { + + if(isPCL(pc)) // pc->type == PC_LABEL) + pcl = PCL(pc); + else if (isPCI(pc) && PCI(pc)->label) //((pc->type == PC_OPCODE) && PCI(pc)->label) + pcl = PCL(PCI(pc)->label->pc); + else continue; + +// fprintf(stderr," found A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + /* This pCode is a label, so search the pBlock to see if anyone + * refers to it */ + + if((pcl->key>0) && (!findInstructionUsingLabel(pcl, pb->pcHead)) + && (!pcl->force)) { + //if( !findInstructionUsingLabel(pcl, pb->pcHead)) { + /* Couldn't find an instruction that refers to this label + * So, unlink the pCode label from it's pCode chain + * and destroy the label */ +// fprintf(stderr," removed A LABEL !!! key = %d, %s\n", pcl->key,pcl->label); + + DFPRINTF((stderr," !!! REMOVED A LABEL !!! key = %d, %s\n", pcl->key,pcl->label)); + if(pc->type == PC_LABEL) { + pic16_unlinkpCode(pc); + pCodeLabelDestruct(pc); + } else { + unlinkpCodeFromBranch(pc, PCODE(pcl)); + /*if(pc->label->next == NULL && pc->label->pc == NULL) { + Safe_free(pc->label); + }*/ + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_pBlockMergeLabels - remove the pCode labels from the pCode */ +/* chain and put them into pBranches that are */ +/* associated with the appropriate pCode */ +/* instructions. */ +/*-----------------------------------------------------------------*/ +void pic16_pBlockMergeLabels(pBlock *pb) +{ + pBranch *pbr; + pCode *pc, *pcnext=NULL; + + if(!pb) + return; + + /* First, Try to remove any unused labels */ + //pBlockRemoveUnusedLabels(pb); + + /* Now loop through the pBlock and merge the labels with the opcodes */ + + pc = pb->pcHead; + // for(pc = pb->pcHead; pc; pc = pc->next) { + + while(pc) { + pCode *pcn = pc->next; + + if(pc->type == PC_LABEL) { + +// fprintf(stderr," checking merging label %s\n",PCL(pc)->label); +// fprintf(stderr,"Checking label key = %d\n",PCL(pc)->key); + + if((pcnext = pic16_findNextInstruction(pc) )) { + +// pcnext->print(stderr, pcnext); + + // Unlink the pCode label from it's pCode chain + pic16_unlinkpCode(pc); + +// fprintf(stderr,"Merged label key = %d\n",PCL(pc)->key); + // And link it into the instruction's pBranch labels. (Note, since + // it's possible to have multiple labels associated with one instruction + // we must provide a means to accomodate the additional labels. Thus + // the labels are placed into the singly-linked list "label" as + // opposed to being a single member of the pCodeInstruction.) + + //_ALLOC(pbr,sizeof(pBranch)); +#if 1 + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc; + pbr->next = NULL; + + PCI(pcnext)->label = pic16_pBranchAppend(PCI(pcnext)->label,pbr); +#endif + } else { + if(pic16_pcode_verbose) + fprintf(stderr, "WARNING: couldn't associate label %s with an instruction\n",PCL(pc)->label); + } + } else if(pc->type == PC_CSOURCE) { + + /* merge the source line symbolic info into the next instruction */ + if((pcnext = pic16_findNextInstruction(pc) )) { + + // Unlink the pCode label from it's pCode chain + pic16_unlinkpCode(pc); + PCI(pcnext)->cline = PCCS(pc); + //fprintf(stderr, "merging CSRC\n"); + //genericPrint(stderr,pcnext); + } + } + pc = pcn; + } + + pBlockRemoveUnusedLabels(pb); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int OptimizepCode(char dbName) +{ +#define MAX_PASSES 4 + + int matches = 0; + int passes = 0; + pBlock *pb; + + if(!the_pFile) + return 0; + + DFPRINTF((stderr," Optimizing pCode\n")); + + do { + matches = 0; + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) + matches += OptimizepBlock(pb); + } + } + while(matches && ++passes < MAX_PASSES); + + return matches; +} + +const char *pic16_pCodeOpType(pCodeOp *pcop); +const char *pic16_pCodeOpSubType(pCodeOp *pcop); + +/*-----------------------------------------------------------------*/ +/* pic16_popCopyGPR2Bit - copy a pcode operator */ +/*-----------------------------------------------------------------*/ + +pCodeOp *pic16_popCopyGPR2Bit(pCodeOp *pc, int bitval) +{ + pCodeOp *pcop=NULL; + +// fprintf(stderr, "%s:%d pc type: %s\tname: %s\n", __FILE__, __LINE__, pic16_pCodeOpType(pc), pc->name); + + if(pc->name) { + pcop = pic16_newpCodeOpBit(pc->name, bitval, 0, pc->type); + } else { + if(PCOR(pc)->r)pcop = pic16_newpCodeOpBit(PCOR(pc)->r->name, bitval, 0, pc->type); + } + + assert(pcop != NULL); + + if( !( (pcop->type == PO_LABEL) || + (pcop->type == PO_LITERAL) || + (pcop->type == PO_STR) )) + PCOR(pcop)->r = PCOR(pc)->r; /* This is dangerous... */ + PCOR(pcop)->r->wasUsed = 1; + PCOR(pcop)->instance = PCOR(pc)->instance; + + return pcop; +} + + +/*----------------------------------------------------------------------* + * pic16_areRegsSame - check to see if the names of two registers match * + *----------------------------------------------------------------------*/ + +#if 0 +static int pic16_areRegsSame(const reg_info *r1, const reg_info *r2) +{ + return ((strcmp(r1->name, r2->name) == 0) ? TRUE : FALSE); +} +#endif + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pic16_FixRegisterBanking(pBlock *pb) +{ + pCode *pc=NULL; + pCode *pcprev=NULL; + reg_info *reg, *prevreg; + unsigned char flag=0; + + if(!pb) + return; + + pc = pic16_findNextpCode(pb->pcHead, PC_OPCODE); + if(!pc)return; + + /* loop through all of the flow blocks with in one pblock */ + +// fprintf(stderr,"%s:%d: Register banking\n", __FUNCTION__, __LINE__); + + prevreg = NULL; + do { + /* at this point, pc should point to a PC_FLOW object */ + /* for each flow block, determine the register banking + * requirements */ + + + /* if label, then might come from other point, force banksel */ + if(isPCL(pc))prevreg = NULL; + + if(!isPCI(pc))goto loop; + + if(PCI(pc)->label)prevreg = NULL; + + if(PCI(pc)->is2MemOp)goto loop; + + /* if goto, then force banksel */ +// if(PCI(pc)->op == POC_GOTO)prevreg = NULL; + + reg = pic16_getRegFromInstruction(pc); + +#if 0 + pc->print(stderr, pc); + fprintf(stderr, "reg = %p\n", reg); + + if(reg) { + fprintf(stderr, "%s:%d: %s %d\n",__FUNCTION__, __LINE__, reg->name, reg->rIdx); + fprintf(stderr, "addr = 0x%03x, bit=%d\tfix=%d\n", + reg->address,reg->isBitField, reg->isFixed); + } +#endif + + /* now make some tests to make sure that instruction needs bank switch */ + + /* if no register exists, and if not a bit opcode goto loop */ + if(!reg) { + if(!(PCI(pc)->pcop && PCI(pc)->pcop->type == PO_GPR_BIT))goto loop; + } + + if(isPCI_SKIP(pc)) { +// fprintf(stderr, "instruction is SKIP instruction\n"); +// prevreg = NULL; + } + if(reg && isACCESS_BANK(reg))goto loop; + + if(!isBankInstruction(pc))goto loop; + + if(isPCI_LIT(pc))goto loop; + + if(PCI(pc)->op == POC_CALL)goto loop; + + /* Examine the instruction before this one to make sure it is + * not a skip type instruction */ + pcprev = findPrevpCode(pc->prev, PC_OPCODE); + + flag = 0; /* add before this instruction */ + + /* if previous instruction is a skip one, then set flag + * to 2 and call insertBankSwitch */ + if(pcprev && isPCI_SKIP(pcprev)) { + flag=2; //goto loop +// prevreg = NULL; + } + + if(pic16_options.opt_banksel>0) { + char op1[128], op2[128]; + + if(prevreg) { + strncpy(op1, pic16_get_op_from_instruction(PCI(pc)), sizeof(op1) - 1); + op1[sizeof(op1) - 1] = '\0'; + + strncpy(op2, pic16_get_op_from_instruction(PCI(pcprev)), sizeof(op2) - 1); + op2[sizeof(op2) - 1] = '\0'; + + if(!strcmp(op1, op2))goto loop; + } + } + prevreg = reg; + insertBankSwitch(flag, pc); + +// fprintf(stderr, "BANK SWITCH inserted\n"); + +loop: + pcprev = pc; + pc = pc->next; + } while (pc); +} + +/** ADDITIONS BY RAPHAEL NEIDER, 2004-11-16: GOTO OPTIMIZATIONS **/ + +/* Returns the (maximum of the) number of bytes used by the specified pCode. */ +static int instrSize (pCode *pc) +{ + if (!pc) return 0; + + if (isPCAD(pc)) { + if (!PCAD(pc)->directive || strlen (PCAD(pc)->directive) < 3) return 0; + return 4; // assumes only regular instructions using <= 4 bytes + } + + if (isPCI(pc)) return PCI(pc)->isize; + + return 0; +} + +/* Returns 1 if pc is referenced by the given label (either + * pc is the label itself or is an instruction with an attached + * label). + * Returns 0 if pc is not preceeded by the specified label. + */ +static int isLabel (const pCode *pc, const char *label) +{ + if (!pc) return FALSE; + + // label attached to the pCode? + if (isPCI(pc) || isPCAD(pc) || isPCW(pc) || pc->type == PC_INFO) { + pBranch *lab = NULL; + lab = PCI(pc)->label; + + while (lab) { + if (isPCL(lab->pc) && strcmp(PCL(lab->pc)->label, label) == 0) { + return TRUE; + } + lab = lab->next; + } + } + + // is inline assembly label? + if (isPCAD(pc) && PCAD(pc)->directive == NULL && PCAD(pc)->arg) { + // do not compare trailing ':' + if (strncmp(PCAD(pc)->arg, label, strlen(label)) == 0) { + return TRUE; + } + } + + // is pCodeLabel? + if (isPCL(pc)) { + if (strcmp(PCL(pc)->label, label) == 0) { + return TRUE; + } + } + + // no label/no label attached/wrong label(s) + return FALSE; +} + +/* Returns the distance to the given label in terms of words. + * Labels are searched only within -max .. max words from pc. + * Returns max if the label could not be found or + * its distance from pc in (-max..+max). + */ +static int findpCodeLabel (pCode *pc, char *label, int max, pCode **target) { + int dist = instrSize(pc); + pCode *curr = pc; + + // search backwards + while (dist < max && curr && !isLabel (curr, label)) { + curr = curr->prev; + dist += instrSize(curr); // sizeof (instruction) + } // while + if (curr && dist < max) { + if (target != NULL) *target = curr; + return -dist; + } + + dist = 0; + curr = pic16_findNextInstruction (pc->next); + //search forwards + while (dist < max && curr && !isLabel (curr, label)) { + dist += instrSize(curr); // sizeof (instruction) + curr = curr->next; + } // while + if (curr && dist < max) { + if (target != NULL) *target = curr; + return dist; + } + + if (target != NULL) *target = NULL; + return max; +} + +/* Returns -1 if pc does NOT denote an instruction like + * BTFS[SC] STATUS,i + * Otherwise we return + * (a) 0x10 + i for BTFSS + * (b) 0x00 + i for BTFSC + */ +static int isSkipOnStatus (const pCode *pc) +{ + int res = -1; + pCodeOp *pcop; + + if (!pc || !isPCI(pc)) return -1; + + if (PCI(pc)->op == POC_BTFSS) res = 0x10; + else if (PCI(pc)->op == POC_BTFSC) res = 0x00; + else return -1; + + pcop = PCI(pc)->pcop; + + if (pcop->type == PO_STATUS || (pcop->type == PO_GPR_BIT && strcmp(pcop->name, "STATUS") == 0)) { + return (res + ((pCodeOpRegBit *)pcop)->bit); + } + + return -1; +} + +/* Returns 1 if pc is one of BC, BZ, BOV, BN, BNC, BNZ, BNOV or BNN, + * returns 0 otherwise. */ +static int isConditionalBranch (const pCode *pc) +{ + if (!pc || !isPCI_BRANCH(pc)) return FALSE; + + switch (PCI(pc)->op) { + case POC_BC: + case POC_BZ: + case POC_BOV: + case POC_BN: + case POC_BNC: + case POC_BNZ: + case POC_BNOV: + case POC_BNN: + return TRUE; + + default: + break; + } // switch + + return FALSE; +} + +/* Returns 1 if pc has a label attached to it. + * This can be either a label stored in the pCode itself (.label) + * or a label making up its own pCode preceding this pc. + * Returns 0 if pc cannot be reached directly via a label. + */ +const int hasNoLabel (const pCode *pc) +{ + pCode *prev; + + if (!pc) return TRUE; + + // are there any label pCodes between pc and the previous instruction? + prev = pic16_findPrevInstruction (pc->prev); + while (pc && pc != prev) { + // pCode with attached label? + if ((isPCI(pc) || isPCAD(pc) || isPCW(pc) || pc->type == PC_INFO) + && PCI(pc)->label) { + return FALSE; + } + // is inline assembly label? + if (isPCAD(pc) && PCAD(pc)->directive == NULL) return FALSE; + if (isPCW(pc) && PCW(pc)->label) return FALSE; + + // pCodeLabel? + if (isPCL(pc)) return FALSE; + + pc = pc->prev; + } // if + + // no label found + return TRUE; +} + +static void pic16_InsertCommentAfter (pCode *pc, const char *fmt, ...) { + char buf[512]; + va_list va; + + va_start (va, fmt); + vsprintf (buf, fmt, va); + va_end (va); + + pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP(buf)); +} + +/* Replaces the old pCode with the new one, moving the labels, + * C source line and probably flow information to the new pCode. + */ +void pic16_pCodeReplace (pCode *oldPC, pCode *newPC) { + if (!oldPC || !newPC || !isPCI(oldPC) || !isPCI(newPC)) + return; + + /* first move all labels from old to new */ + PCI(newPC)->label = pic16_pBranchAppend (PCI(oldPC)->label, PCI(newPC)->label); + PCI(oldPC)->label = NULL; + +#if 0 + /* move C source line (if possible) */ + if (PCI(oldPC)->cline && !PCI(newPC)->cline) + PCI(newPC)->cline = PCI(oldPC)->cline; +#endif + + /* keep flow information intact */ + newPC->seq = oldPC->seq; + PCI(newPC)->pcflow = PCI(oldPC)->pcflow; + if (PCI(newPC)->pcflow && PCI(newPC)->pcflow->end == oldPC) { + PCI(newPC)->pcflow->end = newPC; + } + + /* insert a comment stating which pCode has been replaced */ +#if 1 + if (pic16_pcode_verbose || pic16_debug_verbose) { + char pc_str[256]; + + pic16_pCode2str (pc_str, sizeof(pc_str), oldPC); + pic16_InsertCommentAfter (oldPC->prev, "%s: replaced %s", __FUNCTION__, pc_str); + } +#endif + + /* insert new pCode into pBlock */ + pic16_pCodeInsertAfter (oldPC, newPC); + pic16_unlinkpCode (oldPC); + + /* destruct replaced pCode */ + oldPC->destruct (oldPC); +} + +/* Returns the inverted conditional branch (if any) or NULL. + * pcop must be set to the new jump target. + */ +static pCode *getNegatedBcc (const pCode *bcc, pCodeOp *pcop) +{ + pCode *newBcc; + + if (!bcc || !isPCI(bcc)) return NULL; + + switch (PCI(bcc)->op) { + case POC_BC: newBcc = pic16_newpCode (POC_BNC , pcop); break; + case POC_BZ: newBcc = pic16_newpCode (POC_BNZ , pcop); break; + case POC_BOV: newBcc = pic16_newpCode (POC_BNOV, pcop); break; + case POC_BN: newBcc = pic16_newpCode (POC_BNN , pcop); break; + case POC_BNC: newBcc = pic16_newpCode (POC_BC , pcop); break; + case POC_BNZ: newBcc = pic16_newpCode (POC_BZ , pcop); break; + case POC_BNOV: newBcc = pic16_newpCode (POC_BOV , pcop); break; + case POC_BNN: newBcc = pic16_newpCode (POC_BN , pcop); break; + default: + newBcc = NULL; + } + return newBcc; +} + +#define MAX_DIST_GOTO 0x7FFFFFFF +#define MAX_DIST_BRA 1020 // maximum offset (in bytes) possible with BRA +#define MAX_DIST_BCC 120 // maximum offset (in bytes) possible with Bcc +#define MAX_JUMPCHAIN_DEPTH 16 // number of GOTOs to follow in resolveJumpChain() (to prevent endless loops) +#define IS_GOTO(arg) ((arg) && isPCI(arg) && (PCI(arg)->op == POC_GOTO || PCI(arg)->op == POC_BRA)) + +/* Follows GOTO/BRA instructions to their target instructions, stores the + * final destination (not a GOTO or BRA instruction) in target and returns + * the distance from the original pc to *target. + */ +int resolveJumpChain (pCode *pc, pCode **target, pCodeOp **pcop) { + pCode *curr = pc; + pCode *last = NULL; + pCodeOp *lastPCOP = NULL; + int dist = 0; + int depth = 0; + + //fprintf (stderr, "%s:%d: -=-", __FUNCTION__, __LINE__); + + /* only follow unconditional branches, except for the initial pCode (which may be a conditional branch) */ + while (curr && (last != curr) && (depth++ < MAX_JUMPCHAIN_DEPTH) && isPCI(curr) + && (PCI(curr)->op == POC_GOTO || PCI(curr)->op == POC_BRA || (curr == pc && isConditionalBranch(curr)))) { + last = curr; + lastPCOP = PCI(curr)->pcop; + dist = findpCodeLabel (pc, PCI(curr)->pcop->name, MAX_DIST_GOTO, &curr); + //fprintf (stderr, "last:%p, curr:%p, label:%s\n", last, curr, PCI(last)->pcop->name); + } // while + + if (target) *target = last; + if (pcop) *pcop = lastPCOP; + return dist; +} + +/* Returns pc if it is not a OPT_JUMPTABLE_BEGIN INFO pCode. + * Otherwise the first pCode after the jumptable (after + * the OPT_JUMPTABLE_END tag) is returned. + */ +pCode *skipJumptables (pCode *pc, int *isJumptable) +{ + *isJumptable = 0; + + while (pc && pc->type == PC_INFO && PCINF(pc)->type == INF_OPTIMIZATION && PCOO(PCINF(pc)->oper1)->type == OPT_JUMPTABLE_BEGIN) { + *isJumptable = 1; + //fprintf (stderr, "SKIPPING jumptable\n"); + do { + //pc->print(stderr, pc); + pc = pc->next; + } while (pc && (pc->type != PC_INFO || PCINF(pc)->type != INF_OPTIMIZATION + || PCOO(PCINF(pc)->oper1)->type != OPT_JUMPTABLE_END)); + //fprintf (stderr, "<next; + } // while + + return pc; +} + +pCode *pic16_findNextInstructionSkipJumptables (pCode *pc, int *isJumptable) +{ + int isJumptab; + + *isJumptable = 0; + while (pc && !isPCI(pc) && !isPCAD(pc) && !isPCW(pc)) { + // set pc to the first pCode after a jumptable, leave pc untouched otherwise + pc = skipJumptables (pc, &isJumptab); + if (isJumptab) { + // pc is the first pCode after the jumptable + *isJumptable = 1; + } else { + // pc has not been changed by skipJumptables() + pc = pc->next; + } + } // while + + return pc; +} + +/* Turn GOTOs into BRAs if distance between GOTO and label + * is less than 1024 bytes. + * + * This method is especially useful if GOTOs after BTFS[SC] + * can be turned into BRAs as GOTO would cost another NOP + * if skipped. + */ +void pic16_OptimizeJumps (void) +{ + pCode *pc; + pCode *pc_prev = NULL; + pCode *pc_next = NULL; + pBlock *pb; + pCode *target; + int change, iteration, isJumptab; + int isHandled = 0; + char *label; + int opt=0, toofar=0, opt_cond = 0, cond_toofar=0, opt_reorder = 0, opt_gotonext = 0, opt_gotochain = 0; + + if (!the_pFile) return; + + //fprintf (stderr, "%s:%d: %s\n", __FILE__, __LINE__, __FUNCTION__); + + for (pb = the_pFile->pbHead; pb != NULL; pb = pb->next) { + int matchedInvertRule = 1; + iteration = 1; + do { + //fprintf (stderr, "%s:%d: iterating over pBlock %p\n", __FUNCTION__, __LINE__, pb); + change = 0; + pc = pic16_findNextInstruction (pb->pcHead); + + while (pc) { + pc_next = pic16_findNextInstructionSkipJumptables (pc->next, &isJumptab); + if (isJumptab) { + // skip jumptable, i.e. start over with no pc_prev! + pc_prev = NULL; + pc = pc_next; + continue; + } // if + + /* (1) resolve chained jumps + * Do not perform this until pattern (4) is no longer present! Otherwise we will + * (a) leave dead code in and + * (b) skip over the dead code with an (unneccessary) jump. + */ + if (!matchedInvertRule && (IS_GOTO(pc) || isConditionalBranch(pc))) { + pCodeOp *lastTargetOp = NULL; + int newDist = resolveJumpChain (pc, &target, &lastTargetOp); + int maxDist = MAX_DIST_BCC; + if (PCI(pc)->op == POC_BRA) maxDist = MAX_DIST_BRA; + if (PCI(pc)->op == POC_GOTO) maxDist = MAX_DIST_GOTO; + + /* be careful NOT to make the jump instruction longer (might break previously shortened jumps!) */ + if (lastTargetOp && newDist <= maxDist && lastTargetOp != PCI(pc)->pcop + && strcmp (lastTargetOp->name, PCI(pc)->pcop->name) != 0) { + //fprintf (stderr, "(1) ");pc->print(stderr, pc); fprintf (stderr, " --> %s\n", lastTargetOp->name); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("(1) jump chain resolved")); } + PCI(pc)->pcop->name = lastTargetOp->name; + change++; + opt_gotochain++; + } // if + } // if + + + if (IS_GOTO(pc)) { + int dist; + int condBraType = isSkipOnStatus(pc_prev); + label = PCI(pc)->pcop->name; + dist = findpCodeLabel(pc, label, MAX_DIST_BRA, &target); + if (dist < 0) dist = -dist; + //fprintf (stderr, "distance: %d (", dist); pc->print(stderr, pc);fprintf (stderr, ")\n"); + isHandled = 0; + + + /* (2) remove "GOTO label; label:" */ + if (isLabel (pc_next, label)) { + //fprintf (stderr, "(2) GOTO next instruction: ");pc->print(stderr, pc);fprintf (stderr, " --> ");pc_next->print(stderr, pc_next); fprintf(stderr, "\n"); + // first remove all preceeding SKIP instructions + while (pc_prev && isPCI_SKIP(pc_prev)) { + // attach labels on this instruction to pc_next + //fprintf (stderr, "(2) preceeding SKIP removed: ");pc_prev->print(stderr, pc_prev);fprintf(stderr, "\n"); + PCI(pc_next)->label = pic16_pBranchAppend (PCI(pc_prev)->label, PCI(pc_next)->label); + PCI(pc_prev)->label = NULL; + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("(2) SKIP removed")); } + pic16_unlinkpCode (pc_prev); + pc_prev = pic16_findPrevInstruction (pc); + } // while + // now remove the redundant goto itself + PCI(pc_next)->label = pic16_pBranchAppend (PCI(pc)->label, PCI(pc_next)->label); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP("(2) GOTO next instruction removed")); } + pic16_unlinkpCode (pc); + pc = pic16_findPrevInstruction(pc_next->prev); + isHandled = 1; // do not perform further optimizations + opt_gotonext++; + change++; + } // if + + + /* (3) turn BTFSx STATUS,i; GOTO label into Bcc label if possible */ + if (!isHandled && condBraType != -1 && hasNoLabel(pc)) { + if (dist < MAX_DIST_BCC) { + pCode *bcc = NULL; + switch (condBraType) { + case 0x00: bcc = pic16_newpCode (POC_BC, PCI(pc)->pcop);break; + // no BDC on DIGIT CARRY available + case 0x02: bcc = pic16_newpCode (POC_BZ, PCI(pc)->pcop);break; + case 0x03: bcc = pic16_newpCode (POC_BOV, PCI(pc)->pcop);break; + case 0x04: bcc = pic16_newpCode (POC_BN, PCI(pc)->pcop);break; + case 0x10: bcc = pic16_newpCode (POC_BNC, PCI(pc)->pcop);break; + // no BNDC on DIGIT CARRY available + case 0x12: bcc = pic16_newpCode (POC_BNZ, PCI(pc)->pcop);break; + case 0x13: bcc = pic16_newpCode (POC_BNOV, PCI(pc)->pcop);break; + case 0x14: bcc = pic16_newpCode (POC_BNN, PCI(pc)->pcop);break; + default: + // no replacement possible + bcc = NULL; + break; + } // switch + if (bcc) { + // ATTENTION: keep labels attached to BTFSx! + // HINT: GOTO is label free (checked above) + //fprintf (stderr, "%s:%d: (3) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(bcc)->mnemonic, label); + isHandled = 1; // do not perform further optimizations + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc_prev->prev, pic16_newpCodeCharP("(3) conditional branch introduced")); } + pic16_pCodeReplace (pc_prev, bcc); + pc->destruct(pc); + pc = bcc; + opt_cond++; + change++; + } // if + } else { + //fprintf (stderr, "(%d, too far for Bcc)\n", dist); + cond_toofar++; + } // if + } // if + + if (!isHandled) { + // (4) eliminate the following (common) tripel: + // ; + // labels1: Bcc label2; + // GOTO somewhere; ; <-- instruction referenced by pc + // label2: + // and replace it by + // labels1: B#(cc) somewhere; ; #(cc) is the negated condition cc + // label2: + // ATTENTION: all labels pointing to "Bcc label2" must be attached + // to instead + // ATTENTION: This optimization is only valid if is + // not a skip operation! + // ATTENTION: somewhere must be within MAX_DIST_BCC bytes! + // ATTENTION: no label may be attached to the GOTO instruction! + if (isConditionalBranch(pc_prev) + && (!isPCI_SKIP(pic16_findPrevInstruction(pc_prev->prev))) + && (dist < MAX_DIST_BCC) + && isLabel(pc_next,PCI(pc_prev)->pcop->name) + && hasNoLabel(pc)) { + pCode *newBcc = getNegatedBcc (pc_prev, PCI(pc)->pcop); + + if (newBcc) { + //fprintf (stderr, "%s:%d: (4) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(newBcc)->mnemonic, label); + isHandled = 1; // do not perform further optimizations + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc_prev->prev, pic16_newpCodeCharP("(4) conditional skipping branch inverted")); } + pic16_pCodeReplace (pc_prev, newBcc); + pc->destruct(pc); + pc = newBcc; + opt_reorder++; + change++; + matchedInvertRule++; + } + } + } + + /* (5) now just turn GOTO into BRA */ + if (!isHandled && (PCI(pc)->op == POC_GOTO)) { + if (dist < MAX_DIST_BRA) { + pCode *newBra = pic16_newpCode (POC_BRA, PCI(pc)->pcop); + //fprintf (stderr, "%s:%d: (5) turning %s %s into %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, label, PCI(newBra)->mnemonic, label); + if (pic16_pcode_verbose) { pic16_pCodeInsertAfter(pc->prev, pic16_newpCodeCharP("(5) GOTO replaced by BRA")); } + pic16_pCodeReplace (pc, newBra); + pc = newBra; + opt++; + change++; + } else { + //fprintf (stderr, "(%d, too far for BRA)\n", dist); + toofar++; + } + } // if (!isHandled) + } // if + + pc_prev = pc; + pc = pc_next; + } // while (pc) + + pBlockRemoveUnusedLabels (pb); + + // This line enables goto chain resolution! + if (matchedInvertRule > 1) matchedInvertRule = 1; else matchedInvertRule = 0; + + iteration++; + } while (change); /* fixpoint iteration per pBlock */ + } // for (pb) + + // emit some statistics concerning goto-optimization +#if 0 + if (pic16_debug_verbose || pic16_pcode_verbose) { + fprintf (stderr, "optimize-goto:\n" + "\t%5d GOTO->BRA; (%d GOTOs too far)\n" + "\t%5d BTFSx, GOTO->Bcc (%d too far)\n" + "\t%5d conditional \"skipping\" jumps inverted\n" + "\t%5d GOTOs to next instruction removed\n" + "\t%5d chained GOTOs resolved\n", + opt, toofar, opt_cond, cond_toofar, opt_reorder, opt_gotonext, opt_gotochain); + } // if +#endif + //fprintf (stderr, "%s:%d: %s\n", __FILE__, __LINE__, __FUNCTION__); +} + +#undef IS_GOTO +#undef MAX_JUMPCHAIN_DEPTH +#undef MAX_DIST_GOTO +#undef MAX_DIST_BRA +#undef MAX_DIST_BCC + +/** END OF RAPHAEL NEIDER'S ADDITIONS **/ + +static void pBlockDestruct(pBlock *pb) +{ + if(!pb) + return; + +// Safe_free(pb); +} + +/*-----------------------------------------------------------------*/ +/* void mergepBlocks(char dbName) - Search for all pBlocks with the*/ +/* name dbName and combine them */ +/* into one block */ +/*-----------------------------------------------------------------*/ +static void mergepBlocks(char dbName) +{ + pBlock *pb, *pbmerged = NULL,*pbn; + + pb = the_pFile->pbHead; + + //fprintf(stderr," merging blocks named %c\n",dbName); + while(pb) { + + pbn = pb->next; + //fprintf(stderr,"looking at %c\n",getpBlock_dbName(pb)); + if( getpBlock_dbName(pb) == dbName) { + + //fprintf(stderr," merged block %c\n",dbName); + + if(!pbmerged) { + pbmerged = pb; + } else { + pic16_addpCode2pBlock(pbmerged, pb->pcHead); + /* pic16_addpCode2pBlock doesn't handle the tail: */ + pbmerged->pcTail = pb->pcTail; + + pb->prev->next = pbn; + if(pbn) + pbn->prev = pb->prev; + + pBlockDestruct(pb); + } + //pic16_printpBlock(stderr, pbmerged); + } + pb = pbn; + } +} + +/*-----------------------------------------------------------------*/ +/* AnalyzeFlow - Examine the flow of the code and optimize */ +/* */ +/* level 0 == minimal optimization */ +/* optimize registers that are used only by two instructions */ +/* level 1 == maximal optimization */ +/* optimize by looking at pairs of instructions that use the */ +/* register. */ +/*-----------------------------------------------------------------*/ + +static void AnalyzeFlow(int level) +{ + static int times_called=0; + pBlock *pb; + + if(!the_pFile) { + /* remove unused allocated registers before exiting */ + pic16_RemoveUnusedRegisters(); + return; + } + + /* if this is not the first time this function has been called, + * then clean up old flow information */ + if(times_called++) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); + pic16_RegsUnMapLiveRanges(); + } + GpcFlowSeq = 1; + + /* Phase 2 - Flow Analysis - Register Banking + * + * In this phase, the individual flow blocks are examined + * and register banking is fixed. + */ + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_FixRegisterBanking(pb); +#endif + + /* Phase 2 - Flow Analysis + * + * In this phase, the pCode is partition into pCodeFlow + * blocks. The flow blocks mark the points where a continuous + * stream of instructions changes flow (e.g. because of + * a call or goto or whatever). + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_BuildFlow(pb); + + /* Phase 2 - Flow Analysis - linking flow blocks + * + * In this phase, the individual flow blocks are examined + * to determine their order of excution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + LinkFlow(pb); + +#if 1 + if (pic16_options.opt_flags & OF_OPTIMIZE_DF) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pic16_createDF (pb); +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 + pic16_vcg_dump_default (pb); +#endif + //pic16_destructDF (pb); + } + + pic16_df_stats (); + if (0) releaseStack (); // releasing is costly... + } +#endif + + /* Phase 3 - Flow Analysis - Flow Tree + * + * In this phase, the individual flow blocks are examined + * to determine their order of execution. + */ + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_BuildFlowTree(pb); + + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + FixBankFlow(pb); +#endif + + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_pCodeRegMapLiveRanges(pb); + + pic16_RemoveUnusedRegisters(); + pic16_removeUnusedRegistersDF (); + + // for(pb = the_pFile->pbHead; pb; pb = pb->next) + pic16_pCodeRegOptimizeRegUsage(level); + + +#if 0 + if(!options.nopeep) + OptimizepCode('*'); +#endif + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) + DumpFlow(pb); +#endif + + /* debug stuff */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = pic16_findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + FillFlow(PCFL(pcflow)); + } + } + +#if 0 + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + (pcflow = pic16_findNextpCode(pcflow, PC_FLOW)) != NULL; + pcflow = pcflow->next) { + FlowStats(PCFL(pcflow)); + } + } +#endif +} + +/* VR -- no need to analyze banking in flow, but left here : + * 1. because it may be used in the future for other purposes + * 2. because if omitted we'll miss some optimization done here + * + * Perhaps I should rename it to something else + */ + +/*-----------------------------------------------------------------*/ +/* pic16_AnalyzeBanking - Called after the memory addresses have been */ +/* assigned to the registers. */ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_AnalyzeBanking(void) +{ + pBlock *pb; + + /* Phase x - Flow Analysis - Used Banks + * + * In this phase, the individual flow blocks are examined + * to determine the Register Banks they use + */ + + AnalyzeFlow(0); + AnalyzeFlow(1); + + if(!options.nopeep) + OptimizepCode('*'); + + + if(!the_pFile)return; + + if(!pic16_options.no_banksel) { + for(pb = the_pFile->pbHead; pb; pb = pb->next) { +// fprintf(stderr, "%s:%d: Fix register banking in pb= 0x%p\n", __FILE__, __LINE__, pb); + pic16_FixRegisterBanking(pb); + } + } +} + +/*-----------------------------------------------------------------*/ +/* buildCallTree - Look at the flow and extract all of the calls. */ +/*-----------------------------------------------------------------*/ +#if 0 +static set *register_usage(pBlock *pb); +#endif + +static void buildCallTree(void) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + reg_info *r; + + if(!the_pFile) + return; + + + + /* Now build the call tree. + First we examine all of the pCodes for functions. + Keep in mind that the function boundaries coincide + with pBlock boundaries. + + The algorithm goes something like this: + We have two nested loops. The outer loop iterates + through all of the pBlocks/functions. The inner + loop iterates through all of the pCodes for + a given pBlock. When we begin iterating through + a pBlock, the variable pc_fstart, pCode of the start + of a function, is cleared. We then search for pCodes + of type PC_FUNCTION. When one is encountered, we + initialize pc_fstart to this and at the same time + associate a new pBranch object that signifies a + branch entry. If a return is found, then this signifies + a function exit point. We'll link the pCodes of these + returns to the matching pc_fstart. + + When we're done, a doubly linked list of pBranches + will exist. The head of this list is stored in + `the_pFile', which is the meta structure for all + of the pCode. Look at the pic16_printCallTree function + on how the pBranches are linked together. + + */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + pCode *pc_fstart=NULL; + for(pc = pb->pcHead; pc; pc = pc->next) { + + if(isPCI(pc) && pc_fstart) { + if(PCI(pc)->is2MemOp) { + r = pic16_getRegFromInstruction2(pc); + if(r && !strcmp(r->name, "POSTDEC1")) + PCF(pc_fstart)->stackusage++; + } else { + r = pic16_getRegFromInstruction(pc); + if(r && !strcmp(r->name, "PREINC1")) + PCF(pc_fstart)->stackusage--; + } + } + + if(isPCF(pc)) { + if (PCF(pc)->fname) { + char buf[16]; + + SNPRINTF(buf, sizeof(buf), "%smain", port->fun_prefix); + if(STRCASECMP(PCF(pc)->fname, buf) == 0) { + //fprintf(stderr," found main \n"); + pb->cmemmap = NULL; /* FIXME do we need to free ? */ + pb->dbName = 'M'; + } + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pc_fstart = pc; + pbr->next = NULL; + + the_pFile->functions = pic16_pBranchAppend(the_pFile->functions,pbr); + + // Here's a better way of doing the same: + addSet(&pb->function_entries, pc); + + } else { + // Found an exit point in a function, e.g. return + // (Note, there may be more than one return per function) + if(pc_fstart) + pBranchLink(PCF(pc_fstart), PCF(pc)); + + addSet(&pb->function_exits, pc); + } + } else if(isCALL(pc)) { + addSet(&pb->function_calls,pc); + } + } + } + + +#if 0 + /* This is not needed because currently all register used + * by a function are stored in stack -- VR */ + + /* Re-allocate the registers so that there are no collisions + * between local variables when one function call another */ + + // this is weird... + // pic16_deallocateAllRegs(); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if(!pb->visited) + register_usage(pb); + } +#endif + +} + +/*-----------------------------------------------------------------*/ +/* pic16_AnalyzepCode - parse the pCode that has been generated and form */ +/* all of the logical connections. */ +/* */ +/* Essentially what's done here is that the pCode flow is */ +/* determined. */ +/*-----------------------------------------------------------------*/ + +void pic16_AnalyzepCode(char dbName) +{ + pBlock *pb; + int i,changes; + + if(!the_pFile) + return; + + mergepBlocks('D'); + + + /* Phase 1 - Register allocation and peep hole optimization + * + * The first part of the analysis is to determine the registers + * that are used in the pCode. Once that is done, the peep rules + * are applied to the code. We continue to loop until no more + * peep rule optimizations are found (or until we exceed the + * MAX_PASSES threshold). + * + * When done, the required registers will be determined. + * + */ + i = 0; + do { + + DFPRINTF((stderr," Analyzing pCode: PASS #%d\n",i+1)); + //fprintf(stderr," Analyzing pCode: PASS #%d\n",i+1); + + /* First, merge the labels with the instructions */ + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + if('*' == dbName || getpBlock_dbName(pb) == dbName) { + + DFPRINTF((stderr," analyze and merging block %c\n",dbName)); + //fprintf(stderr," analyze and merging block %c\n",dbName); + pic16_pBlockMergeLabels(pb); + AnalyzepBlock(pb); + } else { + DFPRINTF((stderr," skipping block analysis dbName=%c blockname=%c\n",dbName,getpBlock_dbName)); + } + } + + if(!options.nopeep) + changes = OptimizepCode(dbName); + else changes = 0; + + } while(changes && (i++ < MAX_PASSES)); + + + buildCallTree(); +} + + +/* convert a series of movff's of local regs to stack, with a single call to + * a support functions which does the same thing via loop */ +static void pic16_convertLocalRegs2Support(pCode *pcstart, pCode *pcend, int count, reg_info *r, int entry) +{ + pBranch *pbr; + pCode *pc, *pct; + char *fname[]={"__lr_store", "__lr_restore"}; + +// pc = pic16_newpCode(POC_CALL, pic16_popGetFromString( (entry?fname[0]:fname[1]) )); + + pct = pic16_findNextInstruction(pcstart->next); + do { + pc = pct; + pct = pc->next; //pic16_findNextInstruction(pc->next); +// pc->print(stderr, pc); + if(isPCI(pc) && PCI(pc)->label) { + pbr = PCI(pc)->label; + while(pbr && pbr->pc) { + PCI(pcstart)->label = pic16_pBranchAppend(PCI(pcstart)->label, pbr); + pbr = pbr->next; + } + +// pc->print(stderr, pc); + /* unlink pCode */ + pc->prev->next = pct; + pct->prev = pc->prev; +// pc->next = NULL; +// pc->prev = NULL; + } + } while (pc != pcend); + + /* unlink movff instructions */ + pcstart->next = pcend; + pcend->prev = pcstart; + + pc = pcstart; +// if(!entry) { +// pic16_pCodeInsertAfter(pc, pct = pic16_newpCode(POC_MOVFF, pic16_popGet2p( +// pic16_popCopyReg(&pic16_pc_fsr0l), pic16_popCopyReg(pic16_framepnt_lo)))); pc = pct; +// } + + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_LFSR, pic16_popGetLit2(0, pic16_popGetWithString(r->name)))); pc = pct; + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_MOVLW, pic16_popGetLit( count ))); pc = pct; + pic16_pCodeInsertAfter(pc, pct=pic16_newpCode(POC_CALL, pic16_popGetWithString( fname[ (entry==1?0:1) ] ))); pc = pct; + +// if(!entry) { +// pic16_pCodeInsertAfter(pc, pct = pic16_newpCode(POC_MOVFF, pic16_popGet2p( +// pic16_popCopyReg(pic16_framepnt_lo), pic16_popCopyReg(&pic16_pc_fsr0l)))); pc = pct; +// } + + + { + symbol *sym; + + sym = newSymbol( fname[ entry?0:1 ], 0 ); + strcpy(sym->rname, fname[ entry?0:1 ]); + checkAddSym(&externs, sym); + +// fprintf(stderr, "%s:%d adding extern symbol %s in externs\n", __FILE__, __LINE__, fname[ entry?0:1 ]); + } +} + +/*-----------------------------------------------------------------*/ +/* OptimizeLocalRegs - turn sequence of MOVFF instructions for */ +/* local registers to a support function call */ +/*-----------------------------------------------------------------*/ +void pic16_OptimizeLocalRegs(void) +{ + pBlock *pb; + pCode *pc; + pCodeInfo *pci; + pCodeOpLocalReg *pclr; + int regCount=0; + int inRegCount=0; + reg_info *r, *lastr=NULL, *firstr=NULL; + pCode *pcstart=NULL, *pcend=NULL; + int inEntry=0; + char *curFunc=NULL; + + /* Overview: + * local_regs begin mark + * MOVFF r0x01, POSTDEC1 + * MOVFF r0x02, POSTDEC1 + * ... + * ... + * MOVFF r0x0n, POSTDEC1 + * local_regs end mark + * + * convert the above to the below: + * MOVLW starting_register_index + * MOVWF PRODL + * MOVLW register_count + * call __save_registers_in_stack + */ + + if(!the_pFile) + return; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + inRegCount = regCount = 0; + firstr = lastr = NULL; + for(pc = pb->pcHead; pc; pc = pc->next) { + + /* hold current function name */ + if(pc && isPCF(pc))curFunc = PCF(pc)->fname; + + if(pc && (pc->type == PC_INFO)) { + pci = PCINF(pc); + + if(pci->type == INF_LOCALREGS) { + pclr = PCOLR(pci->oper1); + + if((pclr->type == LR_ENTRY_BEGIN) + || (pclr->type == LR_ENTRY_END))inEntry = 1; + else inEntry = 0; + + switch(pclr->type) { + case LR_ENTRY_BEGIN: + case LR_EXIT_BEGIN: + inRegCount = 1; regCount = 0; + pcstart = pc; //pic16_findNextInstruction(pc->next); + firstr = lastr = NULL; + break; + + case LR_ENTRY_END: + case LR_EXIT_END: + inRegCount = -1; + pcend = pc; //pic16_findPrevInstruction(pc->prev); + +#if 1 + if(curFunc && inWparamList(curFunc+1)) { + fprintf(stderr, "sdcc: %s: warning: disabling lr-support for function %s\n", + filename, curFunc); + } else { + if(regCount>2) { + pic16_convertLocalRegs2Support(pcstart, pcend, regCount, + firstr, inEntry); + } + } +#endif + firstr = lastr = NULL; + break; + } + + if(inRegCount == -1) { +// fprintf(stderr, "%s:%d registers used [%s] %d\n", __FILE__, __LINE__, inEntry?"entry":"exit", regCount); + regCount = 0; + inRegCount = 0; + } + } + } else { + if(isPCI(pc) && (PCI(pc)->op == POC_MOVFF) && (inRegCount == 1)) { + if(inEntry) + r = pic16_getRegFromInstruction(pc); + else + r = pic16_getRegFromInstruction2(pc); + if(r && (r->type == REG_GPR) && (r->pc_type == PO_GPR_TEMP)) { + if(!firstr)firstr = r; + regCount++; +// fprintf(stderr, "%s:%d\t%s\t%i\t%d/%d\n", __FILE__, __LINE__, r->name, r->rIdx); + } + } + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* ispCodeFunction - returns true if *pc is the pCode of a */ +/* function */ +/*-----------------------------------------------------------------*/ +static bool ispCodeFunction(pCode *pc) +{ + + if(pc && pc->type == PC_FUNCTION && PCF(pc)->fname) + return 1; + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* findFunction - Search for a function by name (given the name) */ +/* in the set of all functions that are in a pBlock */ +/* (note - I expect this to change because I'm planning to limit */ +/* pBlock's to just one function declaration */ +/*-----------------------------------------------------------------*/ +static pCode *findFunction(char *fname) +{ + pBlock *pb; + pCode *pc; + if(!fname) + return NULL; + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_entries); + while(pc) { + + if((pc->type == PC_FUNCTION) && + (PCF(pc)->fname) && + (strcmp(fname, PCF(pc)->fname)==0)) + return pc; + + pc = setNextItem(pb->function_entries); + + } + + } + return NULL; +} + +#if 0 +static void MarkUsedRegisters(set *regset) +{ + + regs *r1,*r2; + + for(r1=setFirstItem(regset); r1; r1=setNextItem(regset)) { +// fprintf(stderr, "marking register = %s\t", r1->name); + r2 = pic16_regWithIdx(r1->rIdx); +// fprintf(stderr, "to register = %s\n", r2->name); + r2->isFree = 0; + r2->wasUsed = 1; + } +} +#endif + +static void pBlockStats(FILE *of, pBlock *pb) +{ + pCode *pc; + reg_info *r; + + if(!pic16_pcode_verbose)return; + + fprintf(of,";***\n; pBlock Stats: dbName = %c\n;***\n",getpBlock_dbName(pb)); + + // for now just print the first element of each set + pc = setFirstItem(pb->function_entries); + if(pc) { + fprintf(of,";entry: "); + pc->print(of,pc); + } + pc = setFirstItem(pb->function_exits); + if(pc) { + fprintf(of,";has an exit\n"); + //pc->print(of,pc); + } + + pc = setFirstItem(pb->function_calls); + if(pc) { + fprintf(of,";functions called:\n"); + + while(pc) { + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + fprintf(of,"; %s\n",pic16_get_op_from_instruction(PCI(pc))); + } + pc = setNextItem(pb->function_calls); + } + } + + r = setFirstItem(pb->tregisters); + if(r) { + int n = elementsInSet(pb->tregisters); + + fprintf(of,";%d compiler assigned register%c:\n",n, ( (n!=1) ? 's' : ' ')); + + while (r) { + fprintf(of, "; %s\n",r->name); + r = setNextItem(pb->tregisters); + } + } + + fprintf(of, "; uses %d bytes of stack\n", 1+ elementsInSet(pb->tregisters)); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +#if 0 +static void sequencepCode(void) +{ + pBlock *pb; + pCode *pc; + + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pb->seq = GpCodeSequenceNumber+1; + + for( pc = pb->pcHead; pc; pc = pc->next) + pc->seq = ++GpCodeSequenceNumber; + } +} +#endif + +#if 0 +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static set *register_usage(pBlock *pb) +{ + pCode *pc,*pcn; + set *registers=NULL; + set *registersInCallPath = NULL; + + /* check recursion */ + + pc = setFirstItem(pb->function_entries); + + if(!pc) + return registers; + + pb->visited = TRUE; + + if(pc->type != PC_FUNCTION) + fprintf(stderr,"%s, first pc is not a function???\n",__FUNCTION__); + + pc = setFirstItem(pb->function_calls); + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + char *dest = pic16_get_op_from_instruction(PCI(pc)); + + pcn = findFunction(dest); + if(pcn) + registersInCallPath = register_usage(pcn->pb); + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + +#ifdef PCODE_DEBUG + pBlockStats(stderr,pb); // debug +#endif + + // Mark the registers in this block as used. + + MarkUsedRegisters(pb->tregisters); + if(registersInCallPath) { + /* registers were used in the functions this pBlock has called */ + /* so now, we need to see if these collide with the ones we are */ + /* using here */ + + regs *r1,*r2, *newreg; + + DFPRINTF((stderr,"comparing registers\n")); + + r1 = setFirstItem(registersInCallPath); + while(r1) { + + r2 = setFirstItem(pb->tregisters); + + while(r2 && (r1->type != REG_STK)) { + + if(r2->rIdx == r1->rIdx) { + newreg = pic16_findFreeReg(REG_GPR); + + + if(!newreg) { + DFPRINTF((stderr,"Bummer, no more registers.\n")); + exit(1); + } + + DFPRINTF((stderr,"Cool found register collision nIdx=%d moving to %d\n", + r1->rIdx, newreg->rIdx)); + r2->rIdx = newreg->rIdx; + //if(r2->name) Safe_free(r2->name); + if(newreg->name) + r2->name = Safe_strdup(newreg->name); + else + r2->name = NULL; + newreg->isFree = 0; + newreg->wasUsed = 1; + } + r2 = setNextItem(pb->tregisters); + } + + r1 = setNextItem(registersInCallPath); + } + + /* Collisions have been resolved. Now free the registers in the call path */ + r1 = setFirstItem(registersInCallPath); + while(r1) { + if(r1->type != REG_STK) { + newreg = pic16_regWithIdx(r1->rIdx); + newreg->isFree = 1; + } + r1 = setNextItem(registersInCallPath); + } + + }// else + // MarkUsedRegisters(pb->registers); + + registers = unionSets(pb->tregisters, registersInCallPath, THROW_NONE); +#ifdef PCODE_DEBUG + if(registers) + DFPRINTF((stderr,"returning regs\n")); + else + DFPRINTF((stderr,"not returning regs\n")); + + DFPRINTF((stderr,"pBlock after register optim.\n")); + pBlockStats(stderr,pb); // debug +#endif + + return registers; +} +#endif + +/*-----------------------------------------------------------------*/ +/* pct2 - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ +static void pct2(FILE *of,pBlock *pb,int indent,int usedstack) +{ + pCode *pc,*pcn; + int i; + // set *registersInCallPath = NULL; + + if(!of) + return; + + if(indent > 10) { + fprintf(of, "recursive function\n"); + return; //recursion ? + } + + pc = setFirstItem(pb->function_entries); + + if(!pc) + return; + + pb->visited = FALSE; + + for(i=0;itype == PC_FUNCTION) { + usedstack += PCF(pc)->stackusage; + fprintf(of,"%s (stack: %i)\n",PCF(pc)->fname, usedstack); + } else return; // ??? + + + pc = setFirstItem(pb->function_calls); + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) { + char *dest = pic16_get_op_from_instruction(PCI(pc)); + + pcn = findFunction(dest); + if(pcn) + pct2(of,pcn->pb,indent+1, usedstack); // + PCF(pcn)->stackusage); + } else + fprintf(of,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } +} + + +/*-----------------------------------------------------------------*/ +/* pic16_printCallTree - writes the call tree to a file */ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_printCallTree(FILE *of) +{ + pBranch *pbr; + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!of) + of = stderr; + + fprintf(of, "\npBlock statistics\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next ) + pBlockStats(of,pb); + + + fprintf(of,"Call Tree\n"); + pbr = the_pFile->functions; + while(pbr) { + if(pbr->pc) { + pc = pbr->pc; + if(!ispCodeFunction(pc)) + fprintf(of,"bug in call tree"); + + + fprintf(of,"Function: %s\n", PCF(pc)->fname); + + while(pc->next && !ispCodeFunction(pc->next)) { + pc = pc->next; + if(pc->type == PC_OPCODE && PCI(pc)->op == POC_CALL) + fprintf(of,"\t%s\n",pic16_get_op_from_instruction(PCI(pc))); + } + } + + pbr = pbr->next; + } + + + fprintf(of,"\n**************\n\na better call tree\n"); + for(pb = the_pFile->pbHead; pb; pb = pb->next) { +// if(pb->visited) + pct2(of,pb,0,0); + } + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + fprintf(of,"block dbname: %c\n", getpBlock_dbName(pb)); + } +} + + + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +static void InlineFunction(pBlock *pb) +{ + pCode *pc; + pCode *pc_call; + + if(!pb) + return; + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(pic16_get_op_from_instruction(PCI(pc))); + pCode *pct; + pCode *pce; + + pBranch *pbr; + + if(pcn && isPCF(pcn) && (PCF(pcn)->ncalled == 0)) { /* change 0 to 1 to enable inlining */ + + //fprintf(stderr,"Cool can inline:\n"); + //pcn->print(stderr,pcn); + + //fprintf(stderr,"recursive call Inline\n"); + InlineFunction(pcn->pb); + //fprintf(stderr,"return from recursive call Inline\n"); + + /* + At this point, *pc points to a CALL mnemonic, and + *pcn points to the function that is being called. + + To in-line this call, we need to remove the CALL + and RETURN(s), and link the function pCode in with + the CALLee pCode. + + */ + + + /* Remove the CALL */ + pc_call = pc; + pc = pc->prev; + + /* remove callee pBlock from the pBlock linked list */ + removepBlock(pcn->pb); + + pce = pcn; + while(pce) { + pce->pb = pb; + pce = pce->next; + } + + /* Remove the Function pCode */ + pct = pic16_findNextInstruction(pcn->next); + + /* Link the function with the callee */ + pc->next = pcn->next; + pcn->next->prev = pc; + + /* Convert the function name into a label */ + + pbr = Safe_alloc(sizeof(pBranch)); + pbr->pc = pic16_newpCodeLabel(PCF(pcn)->fname, -1); + pbr->next = NULL; + PCI(pct)->label = pic16_pBranchAppend(PCI(pct)->label,pbr); + PCI(pct)->label = pic16_pBranchAppend(PCI(pct)->label,PCI(pc_call)->label); + + /* turn all of the return's except the last into goto's */ + /* check case for 2 instruction pBlocks */ + pce = pic16_findNextInstruction(pcn->next); + while(pce) { + pCode *pce_next = pic16_findNextInstruction(pce->next); + + if(pce_next == NULL) { + /* found the last return */ + pCode *pc_call_next = pic16_findNextInstruction(pc_call->next); + + //fprintf(stderr,"found last return\n"); + //pce->print(stderr,pce); + pce->prev->next = pc_call->next; + pc_call->next->prev = pce->prev; + PCI(pc_call_next)->label = pic16_pBranchAppend(PCI(pc_call_next)->label, + PCI(pce)->label); + } + + pce = pce_next; + } + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + } +} + +/*-----------------------------------------------------------------*/ +/* */ +/*-----------------------------------------------------------------*/ + +void pic16_InlinepCode(void) +{ + pBlock *pb; + pCode *pc; + + if(!the_pFile) + return; + + if(!functionInlining) + return; + + /* Loop through all of the function definitions and count the + * number of times each one is called */ + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) { + + pc = setFirstItem(pb->function_calls); + + for( ; pc; pc = setNextItem(pb->function_calls)) { + + if(isCALL(pc)) { + pCode *pcn = findFunction(pic16_get_op_from_instruction(PCI(pc))); + if(pcn && isPCF(pcn)) { + PCF(pcn)->ncalled++; + } + } else + fprintf(stderr,"BUG? pCode isn't a POC_CALL %d\n",__LINE__); + + } + } + + //fprintf(stderr,"inlining %d\n",__LINE__); + + /* Now, Loop through the function definitions again, but this + * time inline those functions that have only been called once. */ + + InlineFunction(the_pFile->pbHead); + //fprintf(stderr,"inlining %d\n",__LINE__); + + for(pb = the_pFile->pbHead; pb; pb = pb->next) + unBuildFlow(pb); +} + +static const char *pic_optype_names[] = { + "PO_NONE", // No operand e.g. NOP + "PO_W", // The working register (as a destination) + "PO_WREG", // The working register (as a file register) + "PO_STATUS", // The 'STATUS' register + "PO_BSR", // The 'BSR' register + "PO_FSR0", // The "file select register" (in PIC18 family it's one + // of three) + "PO_INDF0", // The Indirect register + "PO_INTCON", // Interrupt Control register + "PO_GPR_REGISTER", // A general purpose register + "PO_GPR_BIT", // A bit of a general purpose register + "PO_GPR_TEMP", // A general purpose temporary register + "PO_SFR_REGISTER", // A special function register (e.g. PORTA) + "PO_PCL", // Program counter Low register + "PO_PCLATH", // Program counter Latch high register + "PO_PCLATU", // Program counter Latch upper register + "PO_PRODL", // Product Register Low + "PO_PRODH", // Product Register High + "PO_LITERAL", // A constant + "PO_REL_ADDR", // A relative address + "PO_IMMEDIATE", // (8051 legacy) + "PO_DIR", // Direct memory (8051 legacy) + "PO_CRY", // bit memory (8051 legacy) + "PO_BIT", // bit operand. + "PO_STR", // (8051 legacy) + "PO_LABEL", + "PO_WILD", // Wild card operand in peep optimizer + "PO_TWO_OPS" // combine two operands +}; + + +const char *dumpPicOptype(PIC_OPTYPE type) +{ + assert( type >= 0 && type < sizeof(pic_optype_names)/sizeof( char *) ); + return (pic_optype_names[ type ]); +} + + +/*** BEGIN of stuff belonging to the BANKSEL optimization ***/ +#include "graph.h" + +#define MAX_COMMON_BANK_SIZE 32 +#define FIRST_PSEUDO_BANK_NR 1000 + +hTab *sym2bank = NULL; // --> +hTab *bank2sym = NULL; // --> +hTab *coerce = NULL; // --> <&PSEUDOBANK> +Graph *adj = NULL; + +typedef enum { INVALID_BANK = -1, UNKNOWN_BANK = -2, FIXED_BANK = -3 } pseudoBankNr; + +typedef struct { + pseudoBankNr bank; // number assigned to this pseudoBank + unsigned int size; // number of operands assigned to this bank + unsigned int ref; // number of symbols referring to this pseudoBank (for garbage collection) +} pseudoBank; + +/*----------------------------------------------------------------------*/ +/* hashSymbol - hash function used to map SYMBOLs (or operands) to ints */ +/*----------------------------------------------------------------------*/ +unsigned int hashSymbol (const char *str) +{ + unsigned int res = 0; + if (!str) return 0; + + while (*str) { + res ^= (*str); + res = (res << 4) | (res >> (8 * sizeof(unsigned int) - 4)); + str++; + } // while + + return res; +} + +/*-----------------------------------------------------------------------*/ +/* compareSymbol - return 1 iff sym1 equals sym2 */ +/*-----------------------------------------------------------------------*/ +int compareSymbol (const void *sym1, const void *sym2) +{ + char *s1 = (char*) sym1; + char *s2 = (char*) sym2; + + return (strcmp (s1,s2) == 0); +} + +/*-----------------------------------------------------------------------*/ +/* comparePre - return 1 iff p1 == p2 */ +/*-----------------------------------------------------------------------*/ +int comparePtr (const void *p1, const void *p2) +{ + return (p1 == p2); +} + +/*----------------------------------------------------------*/ +/* getSymbolFromOperand - return a pointer to the symbol in */ +/* the given operand and its length */ +/*----------------------------------------------------------*/ +char *getSymbolFromOperand (char *op, int *len) +{ + char *sym, *curr; + *len = 0; + + if (!op) return NULL; + + // we recognize two forms of operands: SYMBOL and (SYMBOL + offset) + sym = op; + if (*sym == '(') sym++; + + curr = sym; + while (((*curr >= 'A') && (*curr <= 'Z')) + || ((*curr >= 'a') && (*curr <= 'z')) + || ((curr != sym) && (*curr >= '0') && (*curr <= '9')) + || (*curr == '_')) { + // find end of symbol [A-Za-z_]?[A-Za-z0-9]* + curr++; + (*len)++; + } // while + + return sym; +} + +/*--------------------------------------------------------------------------*/ +/* getSymFromBank - get (one) name of a symbol assigned to the given bank */ +/*--------------------------------------------------------------------------*/ +char *getSymFromBank (pseudoBankNr bank) +{ + assert (bank2sym); + + if (bank < 0) return ""; + return hTabFindByKey (bank2sym, bank % bank2sym->size, (void *) bank, &comparePtr); +} + +/*-----------------------------------------------------------------------*/ +/* getPseudoBsrFromOperand - maps a string to its corresponding pseudo */ +/* bank number (uses hTab sym2bank), if the */ +/* symbol is not yet assigned a pseudo bank it */ +/* is assigned one here */ +/*-----------------------------------------------------------------------*/ +pseudoBankNr getPseudoBankNrFromOperand (const char *op) +{ + static pseudoBankNr next_bank = FIRST_PSEUDO_BANK_NR; + pseudoBankNr bank; + unsigned int hash; + + assert (sym2bank); + + hash = hashSymbol (op) % sym2bank->size; + bank = (pseudoBankNr) hTabFindByKey (sym2bank, hash, op, &compareSymbol); + if (bank == (pseudoBankNr)NULL) bank = UNKNOWN_BANK; + + if (bank == UNKNOWN_BANK) { + // create a pseudo bank for the operand + bank = next_bank++; + hTabAddItemLong (&sym2bank, hash, (char *)op, (void *)bank); + hTabAddItemLong (&bank2sym, bank, (void *) bank, (void *)op); + getOrAddGNode (adj, NULL, bank); // adds the node if it does not exist yet + //fprintf (stderr, "%s:%d: adding %s with hash %u in bank %u\n", __FUNCTION__, __LINE__, op, hash, bank); + } else { + //fprintf (stderr, "%s:%d: found %s with hash %u in bank %u\n", __FUNCTION__, __LINE__, op, hash, bank); + } // if + + assert (bank >= 0); + + return bank; +} + +/*--------------------------------------------------------------------*/ +/* isBanksel - check whether the given pCode is a BANKSEL instruction */ +/*--------------------------------------------------------------------*/ +int isBanksel (pCode *pc) +{ + if (!pc) return 0; + + if (isPCI(pc) && (PCI(pc)->op == POC_BANKSEL || PCI(pc)->op == POC_MOVLB)) { + // BANKSEL or MOVLB + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + return 1; + } + + // check for inline assembler BANKSELs + if (isPCAD(pc) && PCAD(pc)->directive && (STRCASECMP(PCAD(pc)->directive,"BANKSEL") == 0 || + STRCASECMP(PCAD(pc)->directive,"MOVLB") == 0)) { + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + return 1; + } + + // assume pc is no BANKSEL instruction + return 0; +} + +/*---------------------------------------------------------------------------------*/ +/* invalidatesBSR - check whether the pCodeInstruction passed in modifies the BSR */ +/* This method can not guarantee to find all modifications of the */ +/* BSR (e.g. via INDirection registers) but covers all compiler */ +/* generated plus some cases. */ +/*---------------------------------------------------------------------------------*/ +int invalidatesBSR(pCode *pc) +{ + // assembler directives invalidate BSR (well, they might, we don't know) + if (isPCAD(pc)) return 1; + + // only ASMDIRs and pCodeInstructions can invalidate BSR + if (!isPCI(pc)) return 0; + + // we have a pCodeInstruction + + // check for BSR modifying instructions + switch (PCI(pc)->op) { + case POC_CALL: + case POC_RCALL: + case POC_MOVLB: + case POC_RETFIE: // might be used as CALL replacement + case POC_RETLW: // might be used as CALL replacement + case POC_RETURN: // might be used as CALL replacement + case POC_BANKSEL: + return 1; + break; + + default: // other instruction do not change BSR unless BSR is an explicit operand! + // TODO: check for BSR as an explicit operand (e.g. INCF BSR,F), which should be rather unlikely...! + break; + } // switch + + // no change of BSR possible/probable + return 0; +} + +/*------------------------------------------------------------*/ +/* getBankFromBanksel - return the pseudo bank nr assigned to */ +/* the symbol referenced in this BANKSEL */ +/*------------------------------------------------------------*/ +pseudoBankNr getBankFromBanksel (pCode *pc) +{ + char *sym; + int data = 0; + + if (!pc) return INVALID_BANK; + + if (isPCAD(pc) && PCAD(pc)->directive) { + if (STRCASECMP(PCAD(pc)->directive,"BANKSEL") == 0) { + // get symbolname from PCAD(pc)->arg + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + sym = PCAD(pc)->arg; + data = getPseudoBankNrFromOperand (sym); + //fprintf (stderr, "symbol: %s, data=%i\n", sym, data); + } else if (STRCASECMP(PCAD(pc)->directive,"MOVLB")) { + // get (literal) bank number from PCAD(pc)->arg + fprintf (stderr, "%s:%d: MOVLB found: %s %s\n", __FUNCTION__, __LINE__, PCAD(pc)->directive, PCAD(pc)->arg); + assert (0 && "not yet implemented - turn off banksel optimization for now"); + } + } else if (isPCI(pc)) { + if (PCI(pc)->op == POC_BANKSEL) { + // get symbolname from PCI(pc)->pcop->name (?) + //fprintf (stderr, "%s:%d: BANKSEL found: %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pcop->name); + sym = PCI(pc)->pcop->name; + data = getPseudoBankNrFromOperand (sym); + //fprintf (stderr, "symbol: %s, data=%i\n", sym, data); + } else if (PCI(pc)->op == POC_MOVLB) { + // get (literal) bank number from PCI(pc)->pcop->name + fprintf (stderr, "%s:%d: MOVLB found: %s %s\n", __FUNCTION__, __LINE__, PCI(pc)->mnemonic, PCI(pc)->pcop->name); + assert (0 && "not yet implemented - turn off banksel optimization for now"); + } + } + + if (data == 0) + // no assigned bank could be found + return UNKNOWN_BANK; + else + return data; +} + +/*------------------------------------------------------------------------------*/ +/* getEffectiveBank - resolves the currently assigned effective pseudo bank nr */ +/*------------------------------------------------------------------------------*/ +pseudoBankNr getEffectiveBank (pseudoBankNr bank) +{ + pseudoBank *data; + + if (bank < FIRST_PSEUDO_BANK_NR) return bank; + + do { + //fprintf (stderr, "%s:%d: bank=%d\n", __FUNCTION__, __LINE__, bank); + data = (pseudoBank *) hTabFindByKey (coerce, bank % coerce->size, (void *) bank, &comparePtr); + if (data) { + if (data->bank != bank) + bank = data->bank; + else + data = NULL; + } + } while (data); + + //fprintf (stderr, "%s:%d: effective bank=%d\n", __FUNCTION__, __LINE__, bank); + return bank; +} + +/*------------------------------------------------------------------*/ +/* attachBsrInfo2pBlock - create a look-up table as to which pseudo */ +/* bank is selected at a given pCode */ +/*------------------------------------------------------------------*/ + +/* Create a graph with pseudo banks as its nodes and switches between + * these as edges (with the edge weight representing the absolute + * number of BANKSELs from one to the other). + * Removes redundand BANKSELs instead iff mod == 1. + * BANKSELs update the pseudo BSR, labels invalidate the current BSR + * value (setting it to 0=UNNKOWN), (R)CALLs also invalidate the + * pseudo BSR. + * TODO: check ALL instructions operands if they modify BSR directly... + * + * pb - the pBlock to annotate + * mod - select either graph creation (0) or BANKSEL removal (1) + */ +unsigned int attachBsrInfo2pBlock (pBlock *pb, int mod) +{ + pCode *pc, *pc_next; + unsigned int prevBSR = UNKNOWN_BANK, pseudoBSR = UNKNOWN_BANK; + int isBankselect = 0; + unsigned int banksels=0; + + if (!pb) return 0; + + pc = pic16_findNextInstruction(pb->pcHead); + while (pc) { + isBankselect = isBanksel (pc); + pc_next = pic16_findNextInstruction (pc->next); + + if (!hasNoLabel (pc)) { + // we don't know our predecessors -- assume different BSRs + prevBSR = UNKNOWN_BANK; + pseudoBSR = UNKNOWN_BANK; + //fprintf (stderr, "invalidated by label at "); pc->print (stderr, pc); + } // if + + // check if this is a BANKSEL instruction + if (isBankselect) { + pseudoBSR = getEffectiveBank (getBankFromBanksel(pc)); + //fprintf (stderr, "BANKSEL via "); pc->print (stderr, pc); + if (mod) { + if (prevBSR == pseudoBSR && pseudoBSR >= 0) { + //fprintf (stderr, "removing redundant "); pc->print (stderr, pc); + if (1 || pic16_pcode_verbose) pic16_pCodeInsertAfter (pc->prev, pic16_newpCodeCharP("removed redundant BANKSEL")); + pic16_unlinkpCode (pc); + banksels++; + } + } else { + addGEdge2 (getOrAddGNode (adj, NULL, prevBSR), getOrAddGNode (adj, NULL, pseudoBSR), 1, 0); + banksels++; + } + } // if + + if (!isBankselect && invalidatesBSR(pc)) { + // check if this instruction invalidates the pseudoBSR + pseudoBSR = UNKNOWN_BANK; + //fprintf (stderr, "invalidated via "); pc->print (stderr, pc); + } // if + + prevBSR = pseudoBSR; + pc = pc_next; + } // while + + return banksels; +} + +/*------------------------------------------------------------------------------------*/ +/* assignToSameBank - returns 0 on success or an error code */ +/* 1 - common bank would be too large */ +/* 2 - assignment to fixed (absolute) bank not performed */ +/* */ +/* This functions assumes that unsplittable operands are already assigned to the same */ +/* bank (e.g. all objects being referenced as (SYMBOL + offset) must be in the same */ +/* bank so that we can make sure the bytes are laid out sequentially in memory) */ +/* TODO: Symbols with an abslute address must be handled specially! */ +/*------------------------------------------------------------------------------------*/ +int assignToSameBank (int bank0, int bank1, int doAbs, int force) +{ + int eff0, eff1, dummy; + pseudoBank *pbank0, *pbank1; + hashtItem *hitem; + + eff0 = getEffectiveBank (bank0); + eff1 = getEffectiveBank (bank1); + + //fprintf (stderr, "%s:%d: bank0=%d/%d, bank1=%d/%d, doAbs=%d\n", __FUNCTION__, __LINE__, bank0, eff0, bank1, eff1, doAbs); + + // nothing to do if already same bank + if (eff0 == eff1) return 0; + + if (!doAbs && (eff0 < FIRST_PSEUDO_BANK_NR || eff1 < FIRST_PSEUDO_BANK_NR)) + return 2; + + // ensure eff0 < eff1 + if (eff0 > eff1) { + // swap eff0 and eff1 + dummy = eff0; + eff0 = eff1; + eff1 = dummy; + dummy = bank0; + bank0 = bank1; + bank1 = dummy; + } // if + + // now assign bank eff1 to bank eff0 + pbank0 = (pseudoBank *) hTabFindByKey (coerce, eff0 % coerce->size, (void *)((char*)0+eff0), &comparePtr); + if (!pbank0) { + pbank0 = Safe_alloc(sizeof(pseudoBank)); + pbank0->bank = eff0; + pbank0->size = 1; + pbank0->ref = 1; + hTabAddItemLong (&coerce, eff0 % coerce->size, (void *)((char*)0+eff0), (void *) pbank0); + } // if + + pbank1 = NULL; + hitem = hTabSearch (coerce, eff1 % coerce->size); + while (hitem && hitem->pkey != (void *)((char*)0+eff1)) + hitem = hitem->next; + + if (hitem) pbank1 = (pseudoBank *) hitem->item; + +#if 0 + fprintf (stderr, "bank #%d/%d & bank #%d/%d --> bank #%d: %u (%s & %s)\n", bank0, eff0, bank1, eff1, + pbank0->bank, pbank0->size, + getSymFromBank (eff0), getSymFromBank (eff1)); +#endif + + if (pbank1) { + if (!force && (pbank0->size + pbank1->size > MAX_COMMON_BANK_SIZE)) { +#if 0 + fprintf (stderr, "bank #%d: %u, bank #%d: %u --> bank #%d': %u > %u (%s,%s)\n", + pbank0->bank, pbank0->size, pbank1->bank, pbank1->size, + pbank0->bank, pbank0->size + pbank1->size, MAX_COMMON_BANK_SIZE, + getSymFromBank (pbank0->bank), getSymFromBank (pbank1->bank)); +#endif + return 1; + } // if + pbank0->size += pbank1->size; + pbank1->ref--; + if (pbank1->ref == 0) Safe_free (pbank1); + } else { + pbank0->size++; + } // if + + if (hitem) + hitem->item = pbank0; + else + hTabAddItemLong (&coerce, eff1 % coerce->size, (void *)((char*)0+eff1), (void *) pbank0); + pbank0->ref++; + + //fprintf (stderr, "%s:%d: leaving.\n", __FUNCTION__, __LINE__); + + return 0; +} + +/*----------------------------------------------------------------*/ +/* mergeGraphNodes - combines two nodes into one and modifies all */ +/* edges to and from the nodes accordingly */ +/* This method needs complete backedges, i.e. if (A,B) is an edge */ +/* then also (B,A) must be an edge (possibly with weight 0). */ +/*----------------------------------------------------------------*/ +void mergeGraphNodes (GraphNode *node1, GraphNode *node2) +{ + GraphEdge *edge, *backedge, *nextedge; + GraphNode *node; + int backweight; + + assert (node1 && node2); + assert (node1 != node2); + + // add all edges starting at node2 to node1 + edge = node2->edge; + while (edge) { + nextedge = edge->next; + node = edge->node; + backedge = getGEdge (node, node2); + if (backedge) + backweight = backedge->weight; + else + backweight = 0; + // insert edges (node1,node) and (node,node1) + addGEdge2 (node1, node, edge->weight, backweight); + // remove edges (node, node2) and (node2, node) + remGEdge (node2, node); + remGEdge (node, node2); + edge = nextedge; + } // while + + // now node2 should not be referenced by any other GraphNode... + //remGNode (adj, node2->data, node2->hash); +} + +/*----------------------------------------------------------------*/ +/* showGraph - dump the current BANKSEL graph as a node/edge list */ +/*----------------------------------------------------------------*/ +void showGraph (Graph *g) +{ + GraphNode *node; + GraphEdge *edge; + pseudoBankNr bankNr; + pseudoBank *pbank; + unsigned int size; + + node = g->node; + while (node) { + edge = node->edge; + bankNr = getEffectiveBank (node->hash); + assert (bankNr >= 0); + pbank = (pseudoBank *) hTabFindByKey (coerce, bankNr % coerce->size, (void *) bankNr, &comparePtr); + if (pbank) { + bankNr = pbank->bank; + size = pbank->size; + } else { + size = 1; + } + + fprintf (stderr, "edges from %s (bank %u, size %u) to:\n", getSymFromBank (node->hash), bankNr, size); + + while (edge) { + if (edge->weight > 0) + fprintf (stderr, " %4u x %s\n", edge->weight, getSymFromBank (edge->node->hash)); + edge = edge->next; + } // while (edge) + node = node->next; + } // while (node) +} + +/*---------------------------------------------------------------*/ +/* pic16_OptimizeBanksel - remove redundant BANKSEL instructions */ +/*---------------------------------------------------------------*/ +void pic16_OptimizeBanksel () +{ + GraphNode *node, *node1, *node1next; + +#if 0 + // needed for more effective bank assignment (needs adjusted pic16_emit_usection()) + GraphEdge *edge, *backedge; + GraphEdge *max; + int maxWeight, weight, mergeMore, absMaxWeight; + pseudoBankNr curr0, curr1; +#endif + pseudoBank *pbank; + pseudoBankNr bankNr; + char *base_symbol0, *base_symbol1; + int len0, len1; + pBlock *pb; + set *set; + reg_info *reg; + unsigned int bankselsTotal = 0, bankselsRemoved = 0; + + //fprintf (stderr, "%s:%s:%d: entered.\n", __FILE__, __FUNCTION__, __LINE__); + + if (!the_pFile || !the_pFile->pbHead) return; + + adj = newGraph (NULL); + sym2bank = newHashTable ( 255 ); + bank2sym = newHashTable ( 255 ); + coerce = newHashTable ( 255 ); + + // create graph of BANKSEL relationships (node = operands, edge (A,B) iff BANKSEL B follows BANKSEL A) + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + bankselsTotal += attachBsrInfo2pBlock (pb, 0); + } // for pb + +#if 1 + // assign symbols with absolute addresses to their respective bank nrs + set = pic16_fix_udata; + for (reg = setFirstItem (set); reg; reg = setNextItem (set)) { + bankNr = reg->address >> 8; + node = getOrAddGNode (adj, NULL, bankNr); + bankNr = (pseudoBankNr) getEffectiveBank (getPseudoBankNrFromOperand(reg->name)); + assignToSameBank (node->hash, bankNr, 1, 1); + + assert (bankNr >= 0); + pbank = (pseudoBank *) hTabFindByKey (coerce, bankNr % coerce->size, (void *) bankNr, &comparePtr); + if (!pbank) { + pbank = Safe_alloc(sizeof(pseudoBank)); + pbank->bank = reg->address >> 8; //FIXED_BANK; + pbank->size = 1; + pbank->ref = 1; + hTabAddItemLong (&coerce, bankNr % coerce->size, (void *) bankNr, pbank); + } else { + assert (pbank->bank == (reg->address >> 8)); + pbank->bank = reg->address >> 8; //FIXED_BANK; + pbank->size++; + } + //fprintf (stderr, "ABS: %s (%d bytes) at %x in bank %u\n", reg->name, reg->size, reg->address, bankNr); + } // for reg +#endif + +#if 1 + // assign operands referring to the same symbol (which is not given an absolute address) to the same bank + //fprintf (stderr, "assign operands with the same symbol to the same bank\n"); + node = adj->node; + while (node) { + if (node->hash < 0) { node = node->next; continue; } + base_symbol0 = getSymbolFromOperand (getSymFromBank (getEffectiveBank(node->hash)), &len0); + node1 = node->next; + while (node1) { + if (node1->hash < 0) { node1 = node1->next; continue; } + node1next = node1->next; + base_symbol1 = getSymbolFromOperand (getSymFromBank (getEffectiveBank (node1->hash)), &len1); + if (len0 == len1 && len0 > 0 && strncmp (base_symbol0, base_symbol1, len0) == 0) { + int res; + // TODO: check for symbols with absolute addresses -- these might be placed across bank boundaries! + //fprintf (stderr, "merging %s and %s\n", getSymFromBank (getEffectiveBank(node->hash)), getSymFromBank (getEffectiveBank(node1->hash))); + if (0 != (res = assignToSameBank (node->hash, node1->hash, 0, 1))) { + fprintf (stderr, "%s(%d) == %s(%d), res=%d\n", base_symbol0, len0, base_symbol1, len1, res); + assert (0 && "Could not assign a symbol to a bank!"); + } + mergeGraphNodes (node, node1); + /* + if (node->hash < node1->hash) + mergeGraphNodes (node, node1); + else + mergeGraphNodes (node1, node); // this removes node so node->next will fail... + */ + } // if + node1 = node1next; + } // while (node1) + node = node->next; + } // while (node) +#endif + +#if 0 + // >>> THIS ALSO NEEDS AN UPDATED pic16_emit_usection() TO REFLECT THE BANK ASSIGNMENTS <<< + // assign tightly coupled operands to the same (pseudo) bank + //fprintf (stderr, "assign tightly coupled operands to the same bank\n"); + mergeMore = 1; + absMaxWeight = 0; + while (mergeMore) { + node = adj->node; + max = NULL; + maxWeight = 0; + while (node) { + curr0 = getEffectiveBank (node->hash); + if (curr0 < 0) { node = node->next; continue; } + edge = node->edge; + while (edge) { + assert (edge->src == node); + backedge = getGEdge (edge->node, edge->src); + weight = edge->weight + (backedge ? backedge->weight : 0); + curr1 = getEffectiveBank (edge->node->hash); + if (curr1 < 0) { edge = edge->next; continue; } + + // merging is only useful if the items are not assigned to the same bank already... + if (curr0 != curr1 && weight > maxWeight) { + if (maxWeight > absMaxWeight) absMaxWeight = maxWeight; + maxWeight = weight; + max = edge; + } // if + edge = edge->next; + } // while + node = node->next; + } // while + + if (maxWeight > 0) { +#if 0 + fprintf (stderr, "%s:%d: merging (%4u) %d(%s) and %d(%s)\n", __FUNCTION__, __LINE__, maxWeight, + max->src->hash, getSymFromBank (max->src->hash), + max->node->hash, getSymFromBank (max->node->hash)); +#endif + + node = getGNode (adj, max->src->data, max->src->hash); + node1 = getGNode (adj, max->node->data, max->node->hash); + + if (0 == assignToSameBank (max->src->hash, max->node->hash, 0, 0)) { + if (max->src->hash < max->node->hash) + mergeGraphNodes (node, node1); + else + mergeGraphNodes (node1, node); + } else { + remGEdge (node, node1); + remGEdge (node1, node); + //mergeMore = 0; + } + + } else { + mergeMore = 0; + } + } // while +#endif + +#if 1 + // remove redundant BANKSELs + //fprintf (stderr, "removing redundant BANKSELs\n"); + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + bankselsRemoved += attachBsrInfo2pBlock (pb, 1); + } // for pb +#endif + +#if 0 + fprintf (stderr, "display graph\n"); + showGraph (); +#endif + + deleteGraph (adj); + //fprintf (stderr, "%s:%s:%d: leaving, %u/%u BANKSELs removed...\n", __FILE__, __FUNCTION__, __LINE__, bankselsRemoved, bankselsTotal); +} + +/*** END of stuff belonging to the BANKSEL optimization ***/ + + + +/*** BEGIN of helpers for pCode dataflow optimizations ***/ + +typedef unsigned int symbol_t; +typedef unsigned int valnum_t; +//typedef unsigned int hash_t; + +#ifndef INT_TO_PTR +#define INT_TO_PTR(x) (((char *) 0) + (x)) +#endif + +#ifndef PTR_TO_INT +#define PTR_TO_INT(x) (((char *)(x)) - ((char *) 0)) +#endif + +static int pic16_regIsLocal (reg_info *r); +static int pic16_safepCodeRemove (pCode *pc, char *comment); + +/* statistics */ +static unsigned int pic16_df_removed_pcodes = 0; +static unsigned int pic16_df_saved_bytes = 0; +static unsigned int df_findall_sameflow = 0; +static unsigned int df_findall_otherflow = 0; +static unsigned int df_findall_in_vals = 0; + +static void pic16_df_stats () { + return; + if (pic16_debug_verbose || pic16_pcode_verbose) { + fprintf (stderr, "PIC16: dataflow analysis removed %u instructions (%u bytes)\n", pic16_df_removed_pcodes, pic16_df_saved_bytes); + fprintf (stderr, "findAll: same flow %u (%u in_vals), other flow %u\n", df_findall_sameflow, df_findall_in_vals, df_findall_otherflow); + //pic16_df_removed_pcodes = pic16_df_saved_bytes = 0; + } +} + +/* Remove a pCode iff possible: + * - previous pCode is no SKIP + * - pc has no label + * Returns 1 iff the pCode has been removed, 0 otherwise. */ +static int pic16_safepCodeUnlink (pCode *pc, char *comment) { + pCode *pcprev, *pcnext; + char buf[256], *total=NULL; + int len; + + if (!comment) comment = "=DF= pCode removed by pic16_safepCodeUnlink"; + + pcprev = pic16_findPrevInstruction (pc->prev); + pcnext = pic16_findNextInstruction (pc->next); + + /* move labels to next instruction (if possible) */ + if (PCI(pc)->label && !pcnext) return 0; + + /* if this is a SKIP with side-effects -- do not remove */ + /* XXX: might try to replace this one with the side-effect only version */ + if (isPCI_SKIP(pc) + && ((PCI(pc)->outCond & (PCC_REGISTER | PCC_W)) != 0)) + { + pCode *newpc; + switch (PCI(pc)->op) + { + case POC_INCFSZ: + case POC_INFSNZ: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_INCFSZW: + newpc = pic16_newpCode(POC_INCFW, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_DECFSZ: + case POC_DCFSNZ: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + case POC_DECFSZW: + newpc = pic16_newpCode(POC_INCF, pic16_pCodeOpCopy( PCI(pc)->pcop ) ); + pic16_pCodeReplace( pc, newpc ); + return 1; + break; + default: + return 0; + } + return 0; + } + + /* if previous instruction is a skip -- do not remove */ + if (pcprev && isPCI_SKIP(pcprev)) { + if (!pic16_safepCodeUnlink (pcprev, "=DF= removed now unused SKIP")) { + /* preceeding SKIP could not be removed -- keep this instruction! */ + return 0; + } + } + + if (PCI(pc)->label) { + //fprintf (stderr, "%s: moving label(s)\n", __FUNCTION__); + //pc->print (stderr, pc); + PCI(pcnext)->label = pic16_pBranchAppend (PCI(pc)->label, PCI(pcnext)->label); + PCI(pc)->label = NULL; + } + + /* update statistics */ + pic16_df_removed_pcodes++; + if (isPCI(pc)) pic16_df_saved_bytes += PCI(pc)->isize; + + /* remove the pCode */ + pic16_pCode2str (buf, 256, pc); + //fprintf (stderr, "%s: removing pCode: %s\n", __FUNCTION__, buf); + if (0 || pic16_debug_verbose || pic16_pcode_verbose) { + len = strlen (buf) + strlen (comment) + 10; + total = (char *) Safe_malloc (len); + SNPRINTF (total, len, "%s: %s", comment, buf); + pic16_pCodeInsertAfter (pc, pic16_newpCodeCharP(total)); + Safe_free (total); + } + + /* actually unlink it from the pBlock -- also remove from to/from lists */ + pic16_pCodeUnlink (pc); + + /* remove the pCode -- release registers */ + pc->destruct (pc); + + /* report success */ + return 1; +} + + +/* ======================================================================== */ +/* === SYMBOL HANDLING ==================================================== */ +/* ======================================================================== */ + +static hTab *map_strToSym = NULL; /** (char *) --> symbol_t */ +static hTab *map_symToStr = NULL; /** symbol_t -> (char *) */ +static symbol_t nextSymbol = 0x2000; /** next symbol_t assigned to the next generated symbol */ + +/** Calculate a hash for a given string. + * If len == 0 the string is assumed to be NUL terminated. */ +static hash_t symbolHash (const char *str, unsigned int len) { + hash_t hash = 0; + if (!len) { + while (*str) { + hash = (hash << 2) ^ *str; + str++; + } // while + } else { + while (len--) { + hash = (hash << 2) ^ *str; + str++; + } + } + return hash; +} + +/** Return 1 iff strings v1 and v2 are identical. */ +static int symcmp (const void *v1, const void *v2) { + return !strcmp ((const char *) v1, (const char *) v2); +} + +/** Return 1 iff pointers v1 and v2 are identical. */ +static int ptrcmp (const void *v1, const void *v2) { + return (v1 == v2); +} + +enum { SPO_WREG=0x1000, + SPO_STATUS, + SPO_PRODL, + SPO_PRODH, + SPO_INDF0, + SPO_POSTDEC0, + SPO_POSTINC0, + SPO_PREINC0, + SPO_PLUSW0, + SPO_INDF1, + SPO_POSTDEC1, + SPO_POSTINC1, + SPO_PREINC1, + SPO_PLUSW1, + SPO_INDF2, + SPO_POSTDEC2, + SPO_POSTINC2, + SPO_PREINC2, + SPO_PLUSW2, + SPO_STKPTR, + SPO_TOSL, + SPO_TOSH, + SPO_TOSU, + SPO_BSR, + SPO_FSR0L, + SPO_FSR0H, + SPO_FSR1L, + SPO_FSR1H, + SPO_FSR2L, + SPO_FSR2H, + SPO_PCL, + SPO_PCLATH, + SPO_PCLATU, + SPO_TABLAT, + SPO_TBLPTRL, + SPO_TBLPTRH, + SPO_TBLPTRU, + SPO_LAST +}; + +/* Return the unique symbol_t for the given string. */ +static symbol_t symFromStr (const char *str) { + hash_t hash; + char *res; + symbol_t sym; + + if (!map_symToStr) { + int i; + struct { char *name; symbol_t sym; } predefsyms[] = { + {"WREG", SPO_WREG}, + {"STATUS", SPO_STATUS}, + {"PRODL", SPO_PRODL}, + {"PRODH", SPO_PRODH}, + {"INDF0", SPO_INDF0}, + {"POSTDEC0", SPO_POSTDEC0}, + {"POSTINC0", SPO_POSTINC0}, + {"PREINC0", SPO_PREINC0}, + {"PLUSW0", SPO_PLUSW0}, + {"INDF1", SPO_INDF1}, + {"POSTDEC1", SPO_POSTDEC1}, + {"POSTINC1", SPO_POSTINC1}, + {"PREINC1", SPO_PREINC1}, + {"PLUSW1", SPO_PLUSW1}, + {"INDF2", SPO_INDF2}, + {"POSTDEC2", SPO_POSTDEC2}, + {"POSTINC2", SPO_POSTINC2}, + {"PREINC2", SPO_PREINC2}, + {"PLUSW2", SPO_PLUSW2}, + {"STKPTR", SPO_STKPTR}, + {"TOSL", SPO_TOSL}, + {"TOSH", SPO_TOSH}, + {"TOSU", SPO_TOSU}, + {"BSR", SPO_BSR}, + {"FSR0L", SPO_FSR0L}, + {"FSR0H", SPO_FSR0H}, + {"FSR1L", SPO_FSR1L}, + {"FSR1H", SPO_FSR1H}, + {"FSR2L", SPO_FSR2L}, + {"FSR2H", SPO_FSR2H}, + {"PCL", SPO_PCL}, + {"PCLATH", SPO_PCLATH}, + {"PCLATU", SPO_PCLATU}, + {"TABLAT", SPO_TABLAT}, + {"TBLPTRL", SPO_TBLPTRL}, + {"TBLPTRH", SPO_TBLPTRH}, + {"TBLPTRU", SPO_TBLPTRU}, + {NULL, 0} + }; + + map_strToSym = newHashTable (128); + map_symToStr = newHashTable (128); + + for (i=0; predefsyms[i].name; i++) { + char *name; + + /* enter new symbol */ + sym = predefsyms[i].sym; + name = predefsyms[i].name; + res = Safe_strdup (name); + hash = symbolHash (name, 0); + + hTabAddItemLong (&map_strToSym, hash, res, INT_TO_PTR(sym)); + hTabAddItemLong (&map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), res); + } // for i + } + + hash = symbolHash (str, 0) % map_strToSym->size; + + /* find symbol in table */ + sym = PTR_TO_INT(hTabFindByKey (map_strToSym, hash, str, &symcmp)); + if (sym) { + //fprintf (stderr, "found symbol %x for %s\n", sym, str); + return sym; + } + + /* enter new symbol */ + sym = nextSymbol++; + res = Safe_strdup (str); + + hTabAddItemLong (&map_strToSym, hash, res, INT_TO_PTR(sym)); + hTabAddItemLong (&map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), res); + + //fprintf (stderr, "created symbol %x for %s\n", sym, res); + + return sym; +} + +#if 1 +static const char *strFromSym (symbol_t sym) { + return (const char *) hTabFindByKey (map_symToStr, sym % map_symToStr->size, INT_TO_PTR(sym), &ptrcmp); +} +#endif + +/* ======================================================================== */ +/* === DEFINITION MAP HANDLING ============================================ */ +/* ======================================================================== */ + +/* A defmap provides information about which symbol is defined by which pCode. + * The most recent definitions are prepended to the list, so that the most + * recent definition can be found by forward scanning the list. + * pc2: MOVFF r0x00, r0x01 + * pc1: INCF r0x01 + * head --> ("r0x01",pc1,42) --> ("STATUS",pc1,44) --> ("r0x01",pc2,28) --> NULL + * + * We attach one defmap to each flow object, and each pCode will occur at + * least once in its flow's defmap (maybe defining the 0 symbol). This can be + * used to find definitions for a pCode in its own defmap that precede pCode. + */ + +typedef struct defmap_s { + symbol_t sym; /** symbol this item refers to */ + union { + struct { + unsigned int in_mask:8; /** mask leaving in accessed bits */ + unsigned int mask:8; /** mask leaving in modified bits (if isWrite) */ + int isRead:1; /** sym/mask is read */ + int isWrite:1; /** sym/mask is written */ + } access; + int accessmethod; + } acc; + pCode *pc; /** pCode this symbol is refrenced at */ + valnum_t in_val; /** valnum_t of symbol's previous value (the one read at pc) */ + valnum_t val; /** new unique number for this value (if isWrite) */ + struct defmap_s *prev, *next; /** link to previous an next definition */ +} defmap_t; + +static defmap_t *defmap_free = NULL; /** list of unused defmaps */ +static int defmap_free_count = 0; /** number of released defmap items */ + +/* Returns a defmap_t with the specified data; this will be the new list head. + * next - pointer to the current list head */ +static defmap_t *newDefmap (symbol_t sym, int in_mask, int mask, int isRead, int isWrite, pCode *pc, valnum_t val, defmap_t *next) { + defmap_t *map; + + if (defmap_free) { + map = defmap_free; + defmap_free = map->next; + --defmap_free_count; + } else { + map = (defmap_t *)Safe_alloc(sizeof(defmap_t)); + } + map->sym = sym; + map->acc.access.in_mask = (isRead ? (in_mask ? in_mask : 0xFF) : 0x00); + map->acc.access.mask = (isWrite ? (mask ? mask : 0xFF) : 0x00); + map->acc.access.isRead = (isRead != 0); + map->acc.access.isWrite = (isWrite != 0); + map->pc = pc; + map->in_val = 0; + map->val = (isWrite ? val : 0); + map->prev = NULL; + map->next = next; + if (next) next->prev = map; + + return map; +} + +/* Returns a copy of the single defmap item. */ +static defmap_t *copyDefmap (defmap_t *map) { + defmap_t *res = (defmap_t *) Safe_malloc (sizeof (defmap_t)); + memcpy (res, map, sizeof (defmap_t)); + res->next = NULL; + res->prev = NULL; + return res; +} + +/* Insert a defmap item after the specified one. */ +static int defmapInsertAfter (defmap_t *ref, defmap_t *newItem) { + if (!ref || !newItem) return 1; + + newItem->next = ref->next; + newItem->prev = ref; + ref->next = newItem; + if (newItem->next) newItem->next->prev = newItem; + + return 0; +} + +/* Check whether item (or an identical one) is already in the chain and add it if neccessary. + * item is copied before insertion into chain and therefore left untouched. + * Returns 1 iff the item has been inserted into the list, 0 otherwise. */ +static int defmapAddCopyIfNew (defmap_t **head, defmap_t *item) { + defmap_t *dummy; + dummy = *head; + while (dummy && (dummy->sym != item->sym + || dummy->pc != item->pc + || dummy->acc.accessmethod != item->acc.accessmethod + || dummy->val != item->val + || dummy->in_val != item->in_val)) { + dummy = dummy->next; + } // while + + /* item already present? */ + if (dummy) return 0; + + /* otherwise: insert copy of item */ + dummy = copyDefmap (item); + dummy->next = *head; + if (*head) (*head)->prev = dummy; + *head = dummy; + + return 1; +} + +/* Releases a defmap. This also removes the map from its chain -- update the head manually! */ +static void deleteDefmap (defmap_t *map) { + if (!map) return; + + /* unlink from chain -- fails for the first item (head is not updated!) */ + if (map->next) map->next->prev = map->prev; + if (map->prev) map->prev->next = map->next; + + /* clear map */ + memset (map, 0, sizeof (defmap_t)); + + /* save for future use */ + map->next = defmap_free; + defmap_free = map; + ++defmap_free_count; +} + +/* Release all defmaps referenced from map. */ +static void deleteDefmapChain (defmap_t **_map) { + defmap_t *map, *next; + + if (!_map) return; + + map = *_map; + + /* find list head */ + while (map && map->prev) map = map->prev; + + /* delete all items */ + while (map) { + next = map->next; + deleteDefmap (map); + map = next; + } // while + + *_map = NULL; +} + +/* Free all defmap items. */ +static void freeDefmap (defmap_t **_map) { + defmap_t *next; + defmap_t *map; + + if (!_map) return; + + map = (*_map); + + /* find list head */ + while (map->prev) map = map->prev; + + /* release all items */ + while (map) { + next = map->next; + Safe_free (map); + map = next; + } + + (*_map) = NULL; +} + +/* Returns the most recent definition for the given symbol preceeding pc. + * If no definition is found, NULL is returned. + * If pc == NULL the whole list is scanned. */ +static defmap_t *defmapFindDef (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *curr = map; + + if (pc) { + /* skip all definitions up to pc */ + while (curr && (curr->pc != pc)) curr = curr->next; + + /* pc not in the list -- scan the whole list for definitions */ + if (!curr) { + fprintf (stderr, "pc %p not found in defmap -- scanning whole list for symbol '%s'\n", pc, strFromSym (sym)); + curr = map; + } else { + /* skip all definitions performed by pc */ + while (curr && (curr->pc == pc)) curr = curr->next; + } + } // if (pc) + + /* find definition for sym */ + while (curr && (!curr->acc.access.isWrite || (curr->sym != sym))) { + curr = curr->next; + } + + return curr; +} + +#if 0 +/* Returns the first use (read) of the given symbol AFTER pc. + * If no such use is found, NULL is returned. + * If pc == NULL the whole list is scanned. */ +static defmap_t *defmapFindUse (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *curr = map, *prev = NULL; + + if (pc) { + /* skip all definitions up to pc */ + while (curr && (curr->pc != pc)) { prev = curr; curr = curr->next; } + + /* pc not in the list -- scan the whole list for definitions */ + if (!curr) { + //fprintf (stderr, "pc %p not found in defmap -- scanning whole list for symbol '%s'\n", pc, strFromSym (sym)); + curr = prev; + } + } else { + /* find end of list */ + while (curr && curr->next) curr = curr->next; + } // if (pc) + + /* find use of sym (scan list backwards) */ + while (curr && (!curr->acc.access.isRead || (curr->sym != sym))) curr = curr->prev; + + return curr; +} +#endif + +/* Return the defmap entry for sym AT pc. + * If none is found, NULL is returned. + * If more than one entry is found an assertion is triggered. */ +static defmap_t *defmapCurr (defmap_t *map, symbol_t sym, pCode *pc) { + defmap_t *res = NULL; + + /* find entries for pc */ + while (map && map->pc != pc) map = map->next; + + /* find first entry for sym @ pc */ + while (map && map->pc == pc && map->sym != sym) map = map->next; + + /* no entry found */ + if (!map) return NULL; + + /* check for more entries */ + res = map; + map = map->next; + while (map && map->pc == pc) { + /* more than one entry for sym @ pc found? */ + assert (map->sym != sym); + map = map->next; + } + + /* return single entry for sym @ pc */ + return res; +} + +/* Modifies the definition of sym at pCode to newval. + * Returns 0 on success, 1 if no definition of sym in pc has been found. + */ +static int defmapUpdate (defmap_t *map, symbol_t sym, pCode *pc, valnum_t newval) { + defmap_t *m = map; + + /* find definitions of pc */ + while (m && m->pc != pc) m = m->next; + + /* find definition of sym at pc */ + while (m && m->pc == pc && (!m->acc.access.isWrite || (m->sym != sym))) m = m->next; + + /* no definition found */ + if (!m) return 1; + + /* redefine */ + m->val = newval; + + /* update following uses of sym */ + while (m && m->pc == pc) m = m->prev; + while (m) { + if (m->sym == sym) { + m->in_val = newval; + if (m->acc.access.isWrite) m = NULL; + } // if + if (m) m = m->prev; + } // while + + return 0; +} + +/* ======================================================================== */ +/* === STACK ROUTINES ===================================================== */ +/* ======================================================================== */ + +typedef struct stack_s { + void *data; + struct stack_s *next; +} stackitem_t; + +typedef stackitem_t *dynstack_t; +static stackitem_t *free_stackitems = NULL; + +/* Create a stack with one item. */ +static dynstack_t *newStack () { + dynstack_t *s = (dynstack_t *) Safe_malloc (sizeof (dynstack_t)); + *s = NULL; + return s; +} + +/* Remove a stack -- its items are only marked free. */ +static void deleteStack (dynstack_t *s) { + stackitem_t *i; + + while (*s) { + i = *s; + *s = (*s)->next; + i->next = free_stackitems; + free_stackitems = i; + } // while + Safe_free (s); +} + +/* Release all stackitems. */ +static void releaseStack () { + stackitem_t *i; + + while (free_stackitems) { + i = free_stackitems->next; + Safe_free(free_stackitems); + free_stackitems = i; + } // while +} + +static void stackPush (dynstack_t *stack, void *data) { + stackitem_t *i; + + if (free_stackitems) { + i = free_stackitems; + free_stackitems = free_stackitems->next; + } else { + i = (stackitem_t *)Safe_alloc(sizeof(stackitem_t)); + } + i->data = data; + i->next = *stack; + *stack = i; +} + +static void *stackPop (dynstack_t *stack) { + void *data; + stackitem_t *i; + + if (stack && *stack) { + data = (*stack)->data; + i = *stack; + *stack = (*stack)->next; + i->next = free_stackitems; + free_stackitems = i; + return data; + } else { + return NULL; + } +} + +#if 0 +static int stackContains (dynstack_t *s, void *data) { + stackitem_t *i; + if (!s) return 0; + i = *s; + while (i) { + if (i->data == data) return 1; + i = i->next; + } // while + + /* not found */ + return 0; +} +#endif + +static int stackIsEmpty (dynstack_t *s) { + return (*s == NULL); +} + + +typedef struct { + pCodeFlow *flow; + defmap_t *lastdef; +} state_t; + +static state_t *newState (pCodeFlow *flow, defmap_t *lastdef) { + state_t *s = (state_t *)Safe_alloc(sizeof(state_t)); + s->flow = flow; + s->lastdef = lastdef; + return s; +} + +static void deleteState (state_t *s) { + Safe_free (s); +} + +static int stateIsNew (state_t *state, dynstack_t *todo, dynstack_t *done) { + stackitem_t *i; + + /* scan working list for state */ + if (todo) { + i = *todo; + while (i) { + /* is i == state? -- state not new */ + if ((((state_t *) (i->data))->flow == state->flow) && (((state_t *) (i->data))->lastdef == state->lastdef)) return 0; + i = i->next; + } // while + } + + if (done) { + i = *done; + while (i) { + /* is i == state? -- state not new */ + if ((((state_t *) (i->data))->flow == state->flow) && (((state_t *) (i->data))->lastdef == state->lastdef)) return 0; + i = i->next; + } // while + } + + /* not found -- state is new */ + return 1; +} + +static inline valnum_t newValnum (); + +const char *pic16_pBlockGetFunctionName (pBlock *pb) { + pCode *pc; + + if (!pb) return ""; + + pc = pic16_findNextpCode (pb->pcHead, PC_FUNCTION); + if (pc && isPCF(pc)) return PCF(pc)->fname; + else return ""; +} + +static defmap_t *pic16_pBlockAddInval (pBlock *pb, symbol_t sym) { + defmap_t *map; + pCodeFlow *pcfl; + + assert(pb); + + pcfl = PCI(pic16_findNextInstruction (pb->pcHead))->pcflow; + + /* find initial value (assigning pc == NULL) */ + map = PCFL(pcfl)->in_vals; + while (map && map->sym != sym) map = map->next; + + /* initial value already present? */ + if (map) { + //fprintf (stderr, "found init value for sym %s (%x): %u\n", strFromSym(sym), sym, map->val); + return map; + } + + /* create a new initial value */ + map = newDefmap (sym, 0x00, 0xff, 0, 1, NULL, newValnum(), PCFL(pcfl)->in_vals); + PCFL(pcfl)->in_vals = map; + //fprintf (stderr, "Created init value for sym %s (%x): %u\n", strFromSym(sym), sym, map->val); + return map; + +#if 0 + /* insert map as last item in pcfl's defmap */ + if (!prev) prev = PCFL(pcfl)->defmap; + if (!prev) { + PCFL(pcfl)->defmap = map; + } else { + while (prev->next) prev = prev->next; + prev->next = map; + map->prev = prev; + } + + return map; +#endif +} + +/* Find all reaching definitions for sym at pc. + * A new (!) list of definitions is returned. + * Returns the number of reaching definitions found. + * The defining defmap entries are returned in *chain. + */ +static int defmapFindAll (symbol_t sym, pCode *pc, defmap_t **chain) { + defmap_t *map; + defmap_t *res; + + pCodeFlow *curr; + pCodeFlowLink *succ; + state_t *state; + dynstack_t *todo; /** stack of state_t */ + dynstack_t *done; /** stack of state_t */ + + int n_defs; + + assert (pc && isPCI(pc) && PCI(pc)->pcflow); + assert (chain); + + /* initialize return list */ + *chain = NULL; + + /* wildcard symbol? */ + if (!sym) return 0; + + //fprintf (stderr, "Searching definition of sym %s(%x) @ pc %p(%p)\n", strFromSym(sym), sym, pc, pc->pb); + + map = PCI(pc)->pcflow->defmap; + + res = defmapFindDef (map, sym, pc); + //if (res) fprintf (stderr, "found def in own flow @ pc %p\n", res->pc); + +#define USE_PRECALCED_INVALS 1 +#if USE_PRECALCED_INVALS + if (!res && PCI(pc)->pcflow->in_vals) { + res = defmapFindDef (PCI(pc)->pcflow->in_vals, sym, NULL); + if (res) { + //fprintf (stderr, "found def in init values\n"); + df_findall_in_vals++; + } + } +#endif + + if (res) { + // found a single definition (in pc's flow) + //fprintf (stderr, "unique definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + defmapAddCopyIfNew (chain, res); + df_findall_sameflow++; + return 1; + } + +#if USE_PRECALCED_INVALS + else { + defmapAddCopyIfNew (chain, pic16_pBlockAddInval (pc->pb, sym)); + return 1; + } + +#endif + +#define FORWARD_FLOW_ANALYSIS 1 +#if defined FORWARD_FLOW_ANALYSIS && FORWARD_FLOW_ANALYSIS + /* no definition found in pc's flow preceeding pc */ + todo = newStack (); + done = newStack (); + n_defs = 0; + stackPush (todo, newState (PCI(pic16_findNextInstruction(pc->pb->pcHead))->pcflow, res)); + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + stackPush (done, state); + curr = state->flow; + res = state->lastdef; + //fprintf (stderr, "searching def of sym %s in pcFlow %p (lastdef %x @ %p)\n", strFromSym(sym), curr, res ? res->val : 0, res ? res->pc : NULL); + + /* there are no definitions BEFORE pc in pc's flow (see above) */ + if (curr == PCI(pc)->pcflow) { + if (!res) { + //fprintf (stderr, "symbol %s(%x) might be used uninitialized at %p\n", strFromSym(sym), sym, pc); + res = pic16_pBlockAddInval (pc->pb, sym); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + res = NULL; + } else { + //fprintf (stderr, "reaching definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + } + } + + /* save last definition of sym in this flow as initial def in successors */ + res = defmapFindDef (curr->defmap, sym, NULL); + if (!res) res = state->lastdef; + + /* add successors to working list */ + state = newState (NULL, NULL); + succ = (pCodeFlowLink *) setFirstItem (curr->to); + while (succ) { + //fprintf (stderr, " %p --> %p with %x\n", curr, succ->pcflow, res ? res->val : 0); + state->flow = succ->pcflow; + state->lastdef = res; + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + state = newState (NULL, NULL); + } // if + succ = (pCodeFlowLink *) setNextItem (curr->to); + } // while + deleteState (state); + } // while + +#else // !FORWARD_FLOW_ANALYSIS + + { + int firstState = 1; + + /* no definition found in pc's flow preceeding pc */ + todo = newStack (); + done = newStack (); + n_defs = 0; + stackPush (todo, newState (PCI(pc)->pcflow, res)); + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + curr = state->flow; + + if (firstState) { + firstState = 0; + /* only check predecessor flows */ + } else { + /* get (last) definition of sym in this flow */ + res = defmapFindDef (curr->defmap, sym, NULL); + } + + if (res) { + /* definition found */ + //fprintf (stderr, "reaching definition for %s @ %p found @ %p (val: %x)\n", strFromSym(sym), pc, res->pc, res->val); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + } else { + /* no definition found -- check predecessor flows */ + state = newState (NULL, NULL); + succ = (pCodeFlowLink *) setFirstItem (curr->from); + + /* if no flow predecessor available -- sym might be uninitialized */ + if (!succ) { + //fprintf (stder, "sym %s might be used uninitialized at %p\n", strFromSym (sym), pc); + res = newDefmap (sym, 0xff, 0, 1, NULL, 0, *chain); + if (defmapAddCopyIfNew (chain, res)) n_defs++; + deleteDefmap (res); res = NULL; + } + + while (succ) { + //fprintf (stderr, " %p --> %p with %x\n", curr, succ->pcflow, res ? res->val : 0); + state->flow = succ->pcflow; + state->lastdef = res; + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + state = newState (NULL, NULL); + } // if + succ = (pCodeFlowLink *) setNextItem (curr->from); + } // while + deleteState (state); + } + } // while + } + +#endif + + /* clean up done stack */ + while (!stackIsEmpty(done)) { + deleteState ((state_t *) stackPop (done)); + } // while + deleteStack (done); + + /* return number of items in result set */ + if (n_defs == 0) { + //fprintf (stderr, "sym %s might be used uninitialized at %p\n", strFromSym (sym), pc); + } else if (n_defs == 1) { + assert (*chain); + //fprintf (stderr, "sym %s at %p always defined as %x @ %p\n", strFromSym(sym), pc, (*chain)->val, (*chain)->pc); + } else if (n_defs > 0) { + //fprintf (stderr, "%u definitions for sym %s at %p found:\n", n_defs, strFromSym(sym), pc); +#if 0 + res = *chain; + while (res) { + fprintf (stderr, " as %4x @ %p\n", res->val, res->pc); + res = res->next; + } // while +#endif + } + //fprintf (stderr, "%u definitions for sym %s at %p found\n", n_defs, strFromSym(sym), pc); + df_findall_otherflow++; + return n_defs; +} + +/* ======================================================================== */ +/* === VALUE NUMBER HANDLING ============================================== */ +/* ======================================================================== */ + +static valnum_t nextValnum = 0x1000; +static hTab *map_symToValnum = NULL; + +/** Return a new value number. */ +static inline valnum_t newValnum () { + return (nextValnum += 4); +} + +static valnum_t valnumFromStr (const char *str) { + symbol_t sym; + valnum_t val; + void *res; + + sym = symFromStr (str); + + if (!map_symToValnum) { + map_symToValnum = newHashTable (128); + } // if + + /* literal already known? */ + res = hTabFindByKey (map_symToValnum, sym % map_symToValnum->size, INT_TO_PTR(sym), &ptrcmp); + + /* return existing valnum */ + if (res) return (valnum_t) PTR_TO_INT(res); + + /* create new valnum */ + val = newValnum(); + hTabAddItemLong (&map_symToValnum, sym % map_symToValnum->size, INT_TO_PTR(sym), INT_TO_PTR(val)); + //fprintf (stderr, "NEW VALNUM %x for symbol %s\n", val, str); + return val; +} + +/* Create a valnum for a literal. */ +static valnum_t valnumFromLit (unsigned int lit) { + return ((valnum_t) 0x100 + (lit & 0x0FF)); +} + +/* Return the (positive) literal value represented by val + * or -1 iff val is no known literal's valnum. */ +static int litFromValnum (valnum_t val) { + if (val >= 0x100 && val < 0x200) { + /* valnum is a (known) literal */ + return val & 0x00FF; + } else { + /* valnum is not a known literal */ + return -1; + } +} + +#if 0 +/* Sanity check - all flows in a block must be reachable from initial flow. */ +static int verifyAllFlowsReachable (pBlock *pb) { + set *reached; + set *flowInBlock; + set *checked; + pCode *pc; + pCodeFlow *pcfl; + pCodeFlowLink *succ; + int res; + + //fprintf (stderr, "%s - started for %s.\n" ,__FUNCTION__, pic16_pBlockGetFunctionName (pb)); + + reached = NULL; + flowInBlock = NULL; + checked = NULL; + /* mark initial flow as reached (and "not needs to be reached") */ + pc = pic16_findNextpCode (pb->pcHead, PC_FLOW); + assert (pc); + addSetHead (&reached, pc); + addSetHead (&checked, pc); + + /* mark all further flows in block as "need to be reached" */ + pc = pb->pcHead; + do { + if (isPCI(pc)) addSetIfnotP (&flowInBlock, PCI(pc)->pcflow); + pc = pic16_findNextInstruction (pc->next); + } while (pc); + + while (reached && (pcfl = (pCodeFlow *)indexSet (reached, 0)) != NULL) { + /* mark as reached and "not need to be reached" */ + deleteSetItem (&reached, pcfl); + //fprintf (stderr, "%s - checking %p\n" ,__FUNCTION__, pcfl); + + /* flow is no longer considered unreachable */ + deleteSetItem (&flowInBlock, pcfl); + + for (succ = setFirstItem (pcfl->to); succ; succ = setNextItem (pcfl->to)) { + if (!isinSet (checked, succ->pcflow)) { + /* flow has never been reached before */ + addSetHead (&reached, succ->pcflow); + addSetHead (&checked, succ->pcflow); + } // if + } // for succ + } // while + + //fprintf (stderr, "%s - finished\n", __FUNCTION__); + + /* by now every flow should have been reached + * --> flowInBlock should be empty */ + res = (flowInBlock == NULL); + +#if 1 + if (flowInBlock) { + fprintf (stderr, "not all flows reached in %s:\n", pic16_pBlockGetFunctionName (pb)); + while (flowInBlock) { + pcfl = indexSet (flowInBlock, 0); + fprintf (stderr, "not reached: flow %p\n", pcfl); + deleteSetItem (&flowInBlock, pcfl); + } // while + } +#endif + + /* clean up */ + deleteSet (&reached); + deleteSet (&flowInBlock); + deleteSet (&checked); + + /* if we reached every flow, succ is NULL by now... */ + //assert (res); // will fire on unreachable code... + return (res); +} +#endif + +/* Checks a flow for accesses to sym AFTER pc. + * + * Returns -1 if the symbol is read in this flow (before redefinition), + * returns 0 if the symbol is redefined in this flow or + * returns a mask [0x01 -- 0xFF] indicating the bits still alive after this flow. + */ +int pic16_isAliveInFlow (symbol_t sym, int mask, pCodeFlow *pcfl, pCode *pc) { + defmap_t *map, *mappc; + + /* find pc or start of definitions */ + map = pcfl->defmap; + while (map && (map->pc != pc) && map->next) map = map->next; + /* if we found pc -- ignore it */ + while (map && map->pc == pc) map = map->prev; + + /* scan list backwards (first definition first) */ + while (map && mask) { +// if (map->sym == sym) { + //fprintf (stderr, "%s: accessing sym %s in pc %p/map %p\n", __FUNCTION__, strFromSym(sym), map->pc, map); + mappc = map; + /* scan list for reads at this pc first */ + while (map && map->pc == mappc->pc) { + /* is the symbol (partially) read? */ + if ((map->sym == sym) && (map->acc.access.isRead && ((map->acc.access.in_mask & mask) != 0))) { + //if (sym != SPO_STATUS) fprintf (stderr, "%s: symbol %s read at pc %p\n", __FUNCTION__, strFromSym (sym), map->pc); + return -1; + } + map = map->prev; + } // while + map = mappc; + + while (map && map->pc == mappc->pc) { + /* honor (partial) redefinitions of sym */ + if ((map->sym == sym) && (map->acc.access.isWrite)) { + mask &= ~map->acc.access.mask; + //if (sym != SPO_STATUS) fprintf (stderr, "%s: symbol %s redefined at pc %p, alive mask: %x\n", __FUNCTION__, strFromSym (sym), map->pc, mask); + } + map = map->prev; + } // while +// } // if + /* map already points to the first defmap for the next pCode */ + //map = mappc->prev; + } // while + + /* the symbol is not completely redefined in this flow and not accessed -- symbol + * is still alive; return the appropriate mask of alive bits */ + return mask; +} + +/* Check whether a symbol is alive (AFTER pc). */ +static int pic16_isAlive (symbol_t sym, pCode *pc) { + int mask, visit; + dynstack_t *todo, *done; + state_t *state; + pCodeFlow *pcfl; + pCodeFlowLink *succ; + + mask = 0x00ff; + + assert (isPCI(pc)); + pcfl = PCI(pc)->pcflow; + + todo = newStack (); + done = newStack (); + + state = newState (pcfl, (defmap_t *) INT_TO_PTR(mask)); + stackPush (todo, state); + visit = 0; + + while (!stackIsEmpty (todo)) { + state = (state_t *) stackPop (todo); + pcfl = state->flow; + mask = PTR_TO_INT(state->lastdef); + if (visit) stackPush (done, state); else deleteState(state); + //fprintf (stderr, "%s: checking flow %p for symbol %s (%x)/%x\n", __FUNCTION__, pcfl, strFromSym(sym), sym, mask); + // make sure flows like A(i1,i2,pc,i3,...) --> A with pc reading and writing sym are handled correctly! + mask = pic16_isAliveInFlow (sym, mask, pcfl, visit == 0 ? pc : NULL); + visit++; + + /* symbol is redefined in flow before use -- not alive in this flow (maybe in others?) */ + if (mask == 0) continue; + + /* symbol is (partially) read before redefinition in flow */ + if (mask == -1) break; + + /* symbol is neither read nor completely redefined -- check successor flows */ + for (succ = setFirstItem(pcfl->to); succ; succ = setNextItem (pcfl->to)) { + state = newState (succ->pcflow, (defmap_t *) INT_TO_PTR(mask)); + if (stateIsNew (state, todo, done)) { + stackPush (todo, state); + } else { + deleteState (state); + } + } // for + } // while + + while (!stackIsEmpty (todo)) deleteState ((state_t *) stackPop (todo)); + while (!stackIsEmpty (done)) deleteState ((state_t *) stackPop (done)); + + /* symbol is read in at least one flow -- is alive */ + if (mask == -1) return 1; + + /* symbol is read in no flow */ + return 0; +} + +/* Returns whether access to the given symbol has side effects. */ +static int pic16_symIsSpecial (symbol_t sym) { + //fprintf (stderr, "%s: sym=%x\n", __FUNCTION__, sym); + switch (sym) { + case SPO_INDF0: + case SPO_PLUSW0: + case SPO_POSTINC0: + case SPO_POSTDEC0: + case SPO_PREINC0: + case SPO_INDF1: + case SPO_PLUSW1: + case SPO_POSTINC1: + case SPO_POSTDEC1: + case SPO_PREINC1: + case SPO_INDF2: + case SPO_PLUSW2: + case SPO_POSTINC2: + case SPO_POSTDEC2: + case SPO_PREINC2: + case SPO_PCL: + return 1; + default: + /* no special effects known */ + return 0; + } // switch + + return 0; +} + +/* Check whether a register should be considered local (to the current function) or not. */ +static int pic16_regIsLocal (reg_info *r) { + symbol_t sym; + if (r) { + if (r->type == REG_TMP) return 1; + + sym = symFromStr (r->name); + switch (sym) { + case SPO_WREG: + case SPO_FSR0L: // used in ptrget/ptrput + case SPO_FSR0H: // ... as well + case SPO_FSR1L: // used as stack pointer... (so not really local but shared among function calls) + case SPO_FSR1H: // ... as well + case SPO_FSR2L: // used as frame pointer + case SPO_FSR2H: // ... as well + case SPO_PRODL: // used to return values from functions + case SPO_PRODH: // ... as well + /* these registers (and some more...) are considered local */ + return 1; + break; + default: + /* for unknown regs: check is marked local, leave if not */ + if (r->isLocal) { + return 1; + } else { + //fprintf (stderr, "%s: non-local reg used: %s\n", __FUNCTION__, r->name); + return 0; + } + } // switch + } // if + + /* if in doubt, assume non-local... */ + return 0; +} + +/* Check all symbols touched by pc whether their newly assigned values are read. + * Returns 0 if no symbol is used later on, 1 otherwise. */ +static int pic16_pCodeIsAlive (pCode *pc) { + pCodeInstruction *pci; + defmap_t *map; + reg_info *checkreg; + + /* we can only handle PCIs */ + if (!isPCI(pc)) return 1; + + //pc->print (stderr, pc); + + pci = PCI(pc); + assert (pci && pci->pcflow && pci->pcflow->defmap); + + /* NEVER remove instructions with implicit side effects */ + switch (pci->op) { + case POC_TBLRD: + case POC_TBLRD_POSTINC: /* modify TBLPTRx */ + case POC_TBLRD_POSTDEC: + case POC_TBLRD_PREINC: + case POC_TBLWT: /* modify program memory */ + case POC_TBLWT_POSTINC: /* modify TBLPTRx */ + case POC_TBLWT_POSTDEC: + case POC_TBLWT_PREINC: + case POC_CLRWDT: /* clear watchdog timer */ + case POC_PUSH: /* should be safe to remove though... */ + case POC_POP: /* should be safe to remove though... */ + case POC_CALL: + case POC_RCALL: + case POC_RETFIE: + case POC_RETURN: + //fprintf (stderr, "%s: instruction with implicit side effects not removed: %s\n", __FUNCTION__, pci->mnemonic); + return 1; + + default: + /* no special instruction */ + break; + } // switch + + /* prevent us from removing assignments to non-local variables */ + checkreg = NULL; + if (PCI(pc)->outCond & PCC_REGISTER) checkreg = pic16_getRegFromInstruction (pc); + else if (PCI(pc)->outCond & PCC_REGISTER2) checkreg = pic16_getRegFromInstruction2(pc); + + if ((PCI(pc)->outCond & (PCC_REGISTER | PCC_REGISTER2)) && !checkreg) { + /* assignment to DIRECT operand like "BSF (_global + 1),6" */ + //fprintf (stderr, "%s: assignment to register detected, but register not available!\n", __FUNCTION__); + //pc->print (stderr, pc); + return 1; + } + if ((PCI(pc)->outCond & (PCC_REGISTER | PCC_REGISTER2)) && !pic16_regIsLocal (checkreg)) { + //fprintf (stderr, "%s: dest-reg not local %s\n", __FUNCTION__, checkreg ? checkreg->name : ""); + return 1; + } + +#if 1 + /* OVERKILL: prevent us from removing reads from non-local variables + * THIS IS HERE TO AVOID PROBLEMS WITH VOLATILE OPERANDS ONLY! + * Once registers get a "isVolatile" field this might be handled more efficiently... */ + checkreg = NULL; + if (PCI(pc)->inCond & PCC_REGISTER) checkreg = pic16_getRegFromInstruction (pc); + else if (PCI(pc)->inCond & PCC_REGISTER2) checkreg = pic16_getRegFromInstruction2(pc); + + if ((PCI(pc)->inCond & (PCC_REGISTER | PCC_REGISTER2)) && !checkreg) { + /* read from DIRECT operand like "BTFSS (_global + 1),6" -- might be volatile */ + //fprintf (stderr, "%s: read from register detected, but register not available!\n", __FUNCTION__); + //pc->print (stderr, pc); + return 1; + } + if ((PCI(pc)->inCond & (PCC_REGISTER | PCC_REGISTER2)) && !pic16_regIsLocal (checkreg)) { + //fprintf (stderr, "%s: src-reg not local: %s\n", __FUNCTION__, checkreg ? checkreg->name : ""); + return 1; + } +#endif + + /* now check that the defined symbols are not used */ + map = pci->pcflow->defmap; + + /* find items for pc */ + while (map && map->pc != pc) map = map->next; + + /* no entries found? something is fishy with DF analysis... -- play safe */ + if (!map) { + if (pic16_pcode_verbose) { + fprintf (stderr, "%s: defmap not found\n", __FUNCTION__); + } + return 1; + } + + /* check all symbols being modified by pc */ + while (map && map->pc == pc) { + if (map->sym == 0) { map = map->next; continue; } + + /* keep pc if it references special symbols (like POSTDEC0) */ +#if 0 + { + char buf[256]; + pic16_pCode2str (buf, sizeof(buf), pc); + fprintf (stderr, "%s: checking for sym %x(%s) at pc %p (%s)\n", __FUNCTION__, map->sym, strFromSym (map->sym), pc, buf); + } +#endif + if (pic16_symIsSpecial (map->sym)) { + //fprintf (stderr, "%s: special sym\n", __FUNCTION__); + return 1; + } + if (map->acc.access.isWrite) { + if (pic16_isAlive (map->sym, pc)) { + //fprintf (stderr, "%s(%s): pCode is alive (sym %s still used)\n", __FUNCTION__, pic16_pBlockGetFunctionName (pc->pb),strFromSym (map->sym)); + return 1; + } + } + map = map->next; + } // while + + /* no use for any of the pc-assigned symbols found -- pCode is dead and can be removed */ +#if 0 + { + char buf[256]; + pic16_pCode2str (buf, sizeof(buf), pc); + fprintf (stderr, "%s: pCode %p (%s) is dead.\n", __FUNCTION__, pc, buf); + } +#endif + return 0; +} + +/* Adds implied operands to the list. + * sym - operand being accessed in the pCode + * list - list to append the operand + * isRead - set to 1 iff sym is read in pCode + * listRead - set to 1 iff all operands being read are to be listed + * + * Returns 0 for "normal" operands, 1 for special operands. + */ +static int fixupSpecialOperands (symbol_t sym, int in_mask, int mask, pCode *pc, valnum_t val, defmap_t **list, int isRead, int isWrite) { + /* check whether accessing REG accesses other REGs as well */ + switch (sym) { + case SPO_INDF0: + /* reads FSR0x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW0: + /* reads FSR0x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC0: + case SPO_POSTINC0: + case SPO_PREINC0: + /* reads/modifies FSR0x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR0H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_INDF1: + /* reads FSR1x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW1: + /* reads FSR1x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC1: + case SPO_POSTINC1: + case SPO_PREINC1: + /* reads/modifies FSR1x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR1L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR1H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_INDF2: + /* reads FSR2x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_PLUSW2: + /* reads FSR2x and WREG */ + *list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, *list); + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 0, pc, 0, *list); + break; + + case SPO_POSTDEC2: + case SPO_POSTINC2: + case SPO_PREINC2: + /* reads/modifies FSR2x */ + *list = newDefmap (sym, 0xff, 0xff, 0, 0, pc, 0, *list); + *list = newDefmap (SPO_FSR2L, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_FSR2H, 0xff, 0xff, 1, 1, pc, newValnum (), *list); + break; + + case SPO_PCL: + /* modifies PCLATH and PCLATU */ + *list = newDefmap (SPO_PCL, 0xff, 0xff, isRead, isWrite, pc, newValnum (), *list); + if (isRead) { + /* reading PCL updates PCLATx */ + *list = newDefmap (SPO_PCLATH, 0xff, 0xff, 0, 1, pc, newValnum (), *list); + *list = newDefmap (SPO_PCLATU, 0xff, 0xff, 0, 1, pc, newValnum (), *list); + } + if (isWrite) { + /* writing PCL implicitly reads PCLATx (computed GOTO) */ + *list = newDefmap (SPO_PCLATH, 0xff, 0xff, 1, 0, pc, 0, *list); + *list = newDefmap (SPO_PCLATU, 0xff, 0xff, 1, 0, pc, 0, *list); + } + break; + + default: + *list = newDefmap (sym, in_mask, mask, isRead, isWrite, pc, val, *list); + /* nothing special */ + return 0; + break; + } + + /* has been a special operand */ + return 1; +} + +static symbol_t pic16_fsrsym_idx[][2] = { + {SPO_FSR0L, SPO_FSR0H}, + {SPO_FSR1L, SPO_FSR1H}, + {SPO_FSR2L, SPO_FSR2H} +}; + +/* Merge multiple defmap entries for the same symbol for list's pCode. */ +static void mergeDefmapSymbols (defmap_t *list) { + defmap_t *ref, *curr, *temp; + + /* now make sure that each symbol occurs at most once per pc */ + ref = list; + while (ref && (ref->pc == list->pc)) { + curr = ref->next; + while (curr && (curr->pc == list->pc)) { + if (curr->sym == ref->sym) { + //fprintf (stderr, "Merging defmap entries for symbol %s\n", strFromSym (ref->sym)); + /* found a symbol occuring twice... merge the two */ + if (curr->acc.access.isRead) { + //if (ref->acc.access.isRead) fprintf (stderr, "symbol %s was marked twice as read at pc %p\n", strFromSym (ref->sym), ref->pc); + ref->acc.access.isRead = 1; + ref->acc.access.in_mask |= curr->acc.access.in_mask; + } + if (curr->acc.access.isWrite) { + //if (ref->acc.access.isWrite) fprintf (stderr, "symbol %s was marked twice as written at pc %p\n", strFromSym (ref->sym), ref->pc); + ref->acc.access.isWrite = 1; + ref->acc.access.mask |= curr->acc.access.mask; + } + temp = curr; + curr = curr->next; + deleteDefmap (temp); + continue; // do not skip curr! + } // if + curr = curr->next; + } // while + ref = ref->next; + } // while +} + +/** Prepend list with the reads and definitions performed by pc. */ +static defmap_t *createDefmap (pCode *pc, defmap_t *list) { + pCodeInstruction *pci; + int cond, inCond, outCond; + int mask = 0xff, smask; + symbol_t sym, sym2; + char *name; + + if (isPCAD(pc)) { + /* make sure there is at least one entry for each pc (needed by list traversal routines) */ + /* TODO: mark this defmap node as an ASMDIR -- any values might be read/modified */ + fprintf (stderr, "ASMDIRs not supported by data flow analysis!\n"); + list = newDefmap (0, 0xff, 0xff, 0, 0, pc, 0, list); + return list; + } + assert (isPCI(pc)); + pci = PCI(pc); + + /* handle bit instructions */ + if (pci->isBitInst) { + assert (pci->pcop->type == PO_GPR_BIT); + mask = 1U << (PCORB(PCI(pc)->pcop)->bit); + } + + /* handle (additional) implicit arguments */ + switch (pci->op) { + case POC_LFSR: + { + int lit; + valnum_t val; + lit = PCOL(pci->pcop)->lit; + assert (lit >= 0 && lit < 3); + //fprintf (stderr, "LFSR: %s // %s\n", pci->pcop->name, pic16_get_op(((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)); + val = valnumFromStr (pic16_get_op(((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)); + //fprintf (stderr, "LFSR lit=%u, symval=%4x\n", lit, val); + list = newDefmap (pic16_fsrsym_idx[lit][0], 0x00, 0xff, 0, 1, pc, val, list); + list = newDefmap (pic16_fsrsym_idx[lit][1], 0x00, 0xff, 0, 1, pc, val+1, list); // val+1 is guaranteed not be used as a valnum... + } + break; + + case POC_MOVLB: // BSR + case POC_BANKSEL: // BSR + list = newDefmap (SPO_BSR, 0x00, 0xff, 0, 1, pc, valnumFromStr (pic16_get_op (((pCodeOpLit2 *)(pci->pcop))->arg2, NULL, 0)), list); + break; + + case POC_MULWF: // PRODx + case POC_MULLW: // PRODx + list = newDefmap (SPO_PRODH, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODL, 0x00, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_POP: // TOS, STKPTR + list = newDefmap (SPO_STKPTR, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSL, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSH, 0x00, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSU, 0x00, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_PUSH: // STKPTR + list = newDefmap (SPO_STKPTR, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSL, 0xff, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSH, 0xff, 0xff, 0, 1, pc, newValnum (), list); + list = newDefmap (SPO_TOSU, 0xff, 0xff, 0, 1, pc, newValnum (), list); + break; + + case POC_CALL: // return values (and arguments?): WREG, PRODx, FSR0L + case POC_RCALL: // return values (and arguments?): WREG, PRODx, FSR0L + list = newDefmap (SPO_WREG, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODL, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_PRODH, 0xff, 0xff, 1, 1, pc, newValnum (), list); + list = newDefmap (SPO_FSR0L, 0xff, 0xff, 1, 1, pc, newValnum (), list); + + /* needs correctly set-up stack pointer */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_RETLW: // return values: WREG, PRODx, FSR0L + /* pseudo read on (possible) return values */ + // WREG is handled below via outCond + list = newDefmap (SPO_PRODL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR0L, 0xff, 0x00, 1, 0, pc, 0, list); + + /* caller's stack pointers must be restored */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_RETURN: // return values; WREG, PRODx, FSR0L + case POC_RETFIE: // return value: WREG, PRODx, FSR0L + /* pseudo read on (possible) return values */ + list = newDefmap (SPO_WREG, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_PRODH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR0L, 0xff, 0x00, 1, 0, pc, 0, list); + + /* caller's stack pointers must be restored */ + list = newDefmap (SPO_FSR1L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR1H, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2L, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_FSR2H, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_TBLRD: + list = newDefmap (SPO_TBLPTRL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TABLAT, 0x00, 0xff, 0, 1, pc, newValnum(), list); + break; + + case POC_TBLRD_POSTINC: + case POC_TBLRD_POSTDEC: + case POC_TBLRD_PREINC: + list = newDefmap (SPO_TBLPTRL, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TABLAT, 0x00, 0xff, 0, 1, pc, newValnum(), list); + break; + + case POC_TBLWT: + list = newDefmap (SPO_TBLPTRL, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0x00, 1, 0, pc, 0, list); + list = newDefmap (SPO_TABLAT, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + case POC_TBLWT_POSTINC: + case POC_TBLWT_POSTDEC: + case POC_TBLWT_PREINC: + list = newDefmap (SPO_TBLPTRL, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRH, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TBLPTRU, 0xff, 0xff, 1, 1, pc, newValnum(), list); + list = newDefmap (SPO_TABLAT, 0xff, 0x00, 1, 0, pc, 0, list); + break; + + default: + /* many instruction implicitly read BSR... -- THIS IS IGNORED! */ + break; + } // switch + + /* handle explicit arguments */ + inCond = pci->inCond; + outCond = pci->outCond; + cond = inCond | outCond; + if (cond & PCC_W) { + list = newDefmap (symFromStr ("WREG"), mask, mask, inCond & PCC_W, outCond & PCC_W, pc, newValnum (), list); + } // if + + /* keep STATUS read BEFORE STATUS write in the list (still neccessary?) */ + if (inCond & PCC_STATUS) { + smask = 0; + if (inCond & PCC_C) smask |= 1U << PIC_C_BIT; + if (inCond & PCC_DC) smask |= 1U << PIC_DC_BIT; + if (inCond & PCC_Z) smask |= 1U << PIC_Z_BIT; + if (inCond & PCC_OV) smask |= 1U << PIC_OV_BIT; + if (inCond & PCC_N) smask |= 1U << PIC_N_BIT; + + list = newDefmap (symFromStr ("STATUS"), smask, 0x00, 1, 0, pc, 0, list); + //fprintf (stderr, "pc %p: def STATUS & %02x\n", pc, smask); + } // if + + if (outCond & PCC_STATUS) { + smask = 0; + if (outCond & PCC_C) smask |= 1U << PIC_C_BIT; + if (outCond & PCC_DC) smask |= 1U << PIC_DC_BIT; + if (outCond & PCC_Z) smask |= 1U << PIC_Z_BIT; + if (outCond & PCC_OV) smask |= 1U << PIC_OV_BIT; + if (outCond & PCC_N) smask |= 1U << PIC_N_BIT; + + list = newDefmap (symFromStr ("STATUS"), 0x00, smask, 0, 1, pc, newValnum (), list); + //fprintf (stderr, "pc %p: def STATUS & %02x\n", pc, smask); + } // if + + sym = sym2 = 0; + if (cond & PCC_REGISTER) { + name = pic16_get_op (pci->pcop, NULL, 0); + sym = symFromStr (name); + fixupSpecialOperands (sym, mask, mask, pc, newValnum(), &list, inCond & PCC_REGISTER, outCond & PCC_REGISTER); + //fprintf (stderr, "pc %p: def REG %s(%x) & %02x\n", pc, name, sym, mask); + } + + if (cond & PCC_REGISTER2) { + name = pic16_get_op2 (pci->pcop, NULL, 0); + sym2 = symFromStr (name); + fixupSpecialOperands (sym2, mask, mask, pc, newValnum(), &list, inCond & PCC_REGISTER2, outCond & PCC_REGISTER2); + //fprintf (stderr, "pc %p: def REG2 %s(%x) & %02x\n", pc, name, sym2, mask); + } + + + /* make sure there is at least one entry for each pc (needed by list traversal routines) */ + list = newDefmap (0, 0x00, 0x00, 0, 0, pc, 0, list); + + mergeDefmapSymbols (list); + + return list; +} + +#if 0 +static void printDefmap (defmap_t *map) { + defmap_t *curr; + + curr = map; + fprintf (stderr, "defmap @ %p:\n", curr); + while (curr) { + fprintf (stderr, "%s%s: %4x|%4x / %02x|%02x, sym %s(%x) @ pc %p\n", + curr->acc.access.isRead ? "R" : " ", + curr->acc.access.isWrite ? "W": " ", + curr->in_val, curr->val, + curr->acc.access.in_mask, curr->acc.access.mask, + strFromSym(curr->sym), curr->sym, + curr->pc); + curr = curr->next; + } // while + fprintf (stderr, "\n"); +} +#endif + +/* Add "additional" definitions to uniq. + * This can be used to merge the in_values and the flow's defmap to create an in_value-list for the flow's successors. + * This can also be used to create a uniq (out)list from a flow's defmap by passing *uniq==NULL. + * + * If symbols defined in additional are not present in uniq, a definition is created. + * Otherwise the present definition is altered to reflect the newer assignments. + * + * flow: --> assign1 --> assign2 --> assign3 --> ... --> + * before `------- noted in additional --------' after + * + * I assume that each symbol occurs AT MOST ONCE in uniq. + * + */ +static int defmapUpdateUniqueSym (defmap_t **uniq, defmap_t *additional) { + defmap_t *curr; + defmap_t *old; + int change = 0; + + //fprintf (stderr, "%s: merging %p & %p\n", __FUNCTION__, *uniq, additional); + /* find tail of additional list (holds the first assignment) */ + curr = additional; + while (curr && curr->next) curr = curr->next; + + /* update uniq */ + do { + /* find next assignment in additionals */ + while (curr && !curr->acc.access.isWrite) curr = curr->prev; + + if (!curr) break; + + /* find item in uniq */ + old = *uniq; + //printDefmap (*uniq); + while (old && (old->sym != curr->sym)) old = old->next; + + if (old) { + /* definition found -- replace */ + if (old->val != curr->val) { + old->val = curr->val; + change++; + } // if + } else { + /* new definition */ + *uniq = newDefmap (curr->sym, 0x00, 0xff, 0, 1, NULL, curr->val, *uniq); + change++; + } + + curr = curr->prev; + } while (1); + + /* return 0 iff uniq remained unchanged */ + return change; +} + +/* Creates the in_value list of a flow by (iteratively) merging the out_value + * lists of its predecessor flows. + * Initially *combined should be NULL, alt_in will be copied to combined. + * If *combined != NULL, combined will be altered: + * - for symbols defined in *combined but not in alt_in, + * *combined is altered to 0 (value unknown, either *combined or INIT). + * - for symbols defined in alt_in but not in *combined, + * a 0 definition is created (value unknown, either INIT or alt). + * - for symbols defined in both, *combined is: + * > left unchanged if *combined->val == alt_in->val or + * > modified to 0 otherwise (value unknown, either alt or *combined). + * + * I assume that each symbol occurs AT MOST ONCE in each list! + */ +static int defmapCombineFlows (defmap_t **combined, defmap_t *alt_in, pBlock *pb) { + defmap_t *curr; + defmap_t *old; + int change = 0; + valnum_t val; + + //fprintf (stderr, "%s: merging %p & %p\n", __FUNCTION__, *combined, alt_in); + + if (!(*combined)) { + return defmapUpdateUniqueSym (combined, alt_in); + } // if + + /* merge the two */ + curr = alt_in; + while (curr) { + /* find symbols definition in *combined */ + old = *combined; + while (old && (old->sym != curr->sym)) old = old->next; + + if (old) { + /* definition found */ + if (old->val && (old->val != curr->val)) { + old->val = 0; /* value unknown */ + change++; + } + } else { + /* no definition found -- can be either INIT or alt_in's value */ + val = pic16_pBlockAddInval (pb, curr->sym)->val; + *combined = newDefmap (curr->sym, 0x00, 0xff, 0, 1, NULL, (val == curr->val) ? val : 0, *combined); + if (val != curr->val) change++; + } + + curr = curr->next; + } // while (curr) + + /* update symbols from *combined that are NOT defined in alt_in -- can be either *combined's value or INIT */ + old = *combined; + while (old) { + if (old->val != 0) { + /* find definition in alt_in */ + curr = alt_in; + while (curr && curr->sym != old->sym) curr = curr->next; + if (!curr) { + /* symbol defined in *combined only -- can be either INIT or *combined */ + val = pic16_pBlockAddInval (pb, old->sym)->val; + if (old->val != val) { + old->val = 0; + change++; + } + } // if + } // if + + old = old->next; + } // while + + return change; +} + +static int defmapCompareUnique (defmap_t *map1, defmap_t *map2) { + defmap_t *curr1, *curr2; + symbol_t sym; + + /* identical maps are equal */ + if (map1 == map2) return 0; + + if (!map1) return -1; + if (!map2) return 1; + + //fprintf (stderr, "%s: comparing %p & %p\n", __FUNCTION__, map1, map2); + + /* check length */ + curr1 = map1; + curr2 = map2; + while (curr1 && curr2) { + curr1 = curr1->next; + curr2 = curr2->next; + } // while + + /* one of them longer? */ + if (curr1) return 1; + if (curr2) return -1; + + /* both lists are of equal length -- compare (in O(n^2)) */ + curr1 = map1; + while (curr1) { + sym = curr1->sym; + curr2 = map2; + while (curr2 && curr2->sym != sym) curr2 = curr2->next; + if (!curr2) return 1; // symbol not found in curr2 + if (curr2->val != curr1->val) return 1; // values differ + + /* compare next symbol */ + curr1 = curr1->next; + } // while + + /* no difference found */ + return 0; +} + + +/* Prepare a list of all reaching definitions per flow. + * This is done using a forward dataflow analysis. + */ +static void createReachingDefinitions (pBlock *pb) { + defmap_t *out_vals, *in_vals; + pCode *pc; + pCodeFlow *pcfl; + pCodeFlowLink *link; + set *todo; + set *blacklist; + + if (!pb) return; + + /* initialize out_vals to unique'fied defmaps per pCodeFlow */ + for (pc = pic16_findNextInstruction (pb->pcHead); pc; pc = pic16_findNextInstruction (pc->next)) { + if (isPCFL(pc)) { + deleteDefmapChain (&PCFL(pc)->in_vals); + deleteDefmapChain (&PCFL(pc)->out_vals); + defmapUpdateUniqueSym (&PCFL(pc)->out_vals, PCFL(pc)->defmap); + } // if + } // for + + pc = pic16_findNextInstruction (pb->pcHead); + if (!pc) { + // empty function, avoid NULL pointer dereference + return; + } // if + todo = NULL; blacklist = NULL; + addSetHead (&todo, PCI(pc)->pcflow); + + //fprintf (stderr, "%s: function %s()\n", __FUNCTION__, pic16_pBlockGetFunctionName (pb)); + while (elementsInSet (todo)) { + //fprintf (stderr, "%u items in todo-set\n", elementsInSet (todo)); + pcfl = PCFL(indexSet (todo, 0)); + deleteSetItem (&todo, pcfl); + //fprintf (stderr, "%s: checking %p\n", __FUNCTION__, pcfl); + in_vals = NULL; + out_vals = NULL; + + if (isinSet (blacklist, pcfl)) { + fprintf (stderr, "ignoring blacklisted flow\n"); + continue; + } + + /* create in_vals from predecessors out_vals */ + link = setFirstItem (pcfl->from); + while (link) { + defmapCombineFlows (&in_vals, link->pcflow->out_vals, pb); + link = setNextItem (pcfl->from); + } // while + + //printDefmap (in_vals); + //printDefmap (pcfl->in_vals); + + if (!pcfl->in_vals || !pcfl->out_vals || defmapCompareUnique (in_vals, pcfl->in_vals)) { + //fprintf (stderr, "in_vals changed\n"); + /* in_vals changed -- update out_vals */ + deleteDefmapChain (&pcfl->in_vals); + pcfl->in_vals = in_vals; + + /* create out_val from in_val and defmap */ + out_vals = NULL; + defmapUpdateUniqueSym (&out_vals, in_vals); + defmapUpdateUniqueSym (&out_vals, pcfl->defmap); + + /* is out_vals different from pcfl->out_vals */ + if (!pcfl->out_vals || defmapCompareUnique (out_vals, pcfl->out_vals)) { + //fprintf (stderr, "out_vals changed\n"); + deleteDefmapChain (&pcfl->out_vals); + pcfl->out_vals = out_vals; + + if (pcfl->out_vals == NULL && pcfl->in_vals == NULL) { + addSet (&blacklist, pcfl); + } // if + + /* reschedule all successors */ + link = setFirstItem (pcfl->to); + while (link) { + //fprintf (stderr, " %p --> %p\n", pcfl, link->pcflow); + addSetIfnotP (&todo, link->pcflow); + link = setNextItem (pcfl->to); + } // while + } else { + deleteDefmapChain (&out_vals); + }// if + } else { + deleteDefmapChain (&in_vals); + } // if + } // while +} + +#if 0 +static void showAllDefs (symbol_t sym, pCode *pc) { + defmap_t *map; + int count; + + assert (isPCI(pc)); + count = defmapFindAll (sym, pc, &map); + + fprintf (stderr, "sym %s(%x) @ %p defined as (val@pc): ", strFromSym(sym), sym, pc); + while (map) { +#if 1 + fprintf (stderr, "(%x @ %p) ", map->val, map->pc); +#else + { + char buf[256]; + + pic16_pCode2str (buf, sizeof(buf), map->pc); + fprintf (stderr, "\n (%x @ %p(%s)) ", map->val, map->pc, buf); + } +#endif + map = map->next; + } + deleteDefmapChain (&map); +} +#endif + +/* safepCodeUnlink and remove pc from defmap. */ +static int pic16_safepCodeRemove (pCode *pc, char *comment) { + defmap_t *map, *next, **head; + int res; + + map = isPCI(pc) ? PCI(pc)->pcflow->defmap : NULL; + head = isPCI(pc) ? &PCI(pc)->pcflow->defmap : NULL; + res = pic16_safepCodeUnlink (pc, comment); + + if (res && map) { + /* remove pc from defmap */ + while (map) { + next = map->next; + if (map->pc == pc) { + if (!map->prev && head) *head = map->next; + deleteDefmap (map); + } // if + map = next; + } + } + + return res; +} + +void pic16_fixDefmap (pCode *pc, pCode *newpc) { + defmap_t *map; + /* This breaks the defmap chain's references to pCodes... fix it! */ + map = PCI(pc)->pcflow->defmap; + + while (map && map->pc != pc) map = map->next; + + while (map && map->pc == pc) { + map->pc = newpc; + map = map->next; + } // while +} + +/* Replace a defmap entry for sym with newsym for read accesses (isRead == 1) or + * write accesses (isRead == 0). */ +void defmapReplaceSymRef (pCode *pc, symbol_t sym, symbol_t newsym, int isRead) { + defmap_t *map, *map_start; + defmap_t *copy; + if (!isPCI(pc)) return; + if (sym == newsym) return; + + map = PCI(pc)->pcflow->defmap; + + while (map && map->pc != pc) map = map->next; + map_start = map; + while (map && map->pc == pc) { + if (map->sym == sym) { + assert ((isRead && map->acc.access.isRead) || ((!isRead) && (map->acc.access.isWrite))); + if (!(map->acc.access.isRead && map->acc.access.isWrite)) { + /* only one kind of access handled... this is easy */ + map->sym = newsym; + } else { + /* must copy defmap entry before replacing symbol... */ + copy = copyDefmap (map); + if (isRead) { + map->acc.access.isRead = 0; + copy->acc.access.isWrite = 0; + } else { + map->acc.access.isWrite = 0; + copy->acc.access.isRead = 0; + } + copy->sym = newsym; + /* insert copy into defmap chain */ + defmapInsertAfter (map, copy); + } + } + map = map->next; + } // while + + /* as this might introduce multiple defmap entries for newsym... */ + mergeDefmapSymbols (map_start); +} + +/* Assign "better" valnums to results. */ +static void assignValnums (pCode *pc) { + pCodeInstruction *pci; + pCode *newpc; + symbol_t sym1, sym2; + int cond, isSpecial1, isSpecial2, count, mask, lit; + defmap_t *list, *val, *oldval, *dummy; + reg_info *reg1 = NULL, *reg2 = NULL; + valnum_t litnum; + + /* only works for pCodeInstructions... */ + if (!isPCI(pc)) return; + + pci = PCI(pc); + cond = pci->inCond | pci->outCond; + list = pci->pcflow->defmap; + sym1 = sym2 = isSpecial1 = isSpecial2 = 0; + + if (cond & PCC_REGISTER) { + sym1 = symFromStr (pic16_get_op (pci->pcop, NULL, 0)); + reg1 = pic16_getRegFromInstruction (pc); + isSpecial1 = pic16_symIsSpecial (sym1); + } + if (cond & PCC_REGISTER2) { + sym2 = symFromStr (pic16_get_op2 (pci->pcop, NULL, 0)); + reg2 = pic16_getRegFromInstruction (pc); + isSpecial2 = pic16_symIsSpecial (sym2); + } + + /* determine input values */ + val = list; + while (val && val->pc != pc) val = val->next; + //list = val; /* might save some time later... */ + while (val && val->pc == pc) { + val->in_val = 0; + if (val->sym != 0 && (1 || val->acc.access.isRead)) { + /* get valnum for sym */ + count = defmapFindAll (val->sym, pc, &oldval); + //fprintf (stderr, "%d defs for sym %s\n", count, strFromSym (val->sym)); + if (count == 1) { + if ((val->acc.access.in_mask & oldval->acc.access.mask) == val->acc.access.in_mask) { + val->in_val = oldval->val; + } else { + val->in_val = 0; + } + } else if (count == 0) { + /* no definition found */ + val->in_val = 0; + } else { + /* multiple definition(s) found -- value not known (unless always the same valnum) */ + assert (oldval); + dummy = oldval->next; + mask = oldval->acc.access.mask; + val->in_val = oldval->val; + while (dummy && (dummy->val == val->in_val)) { + mask &= dummy->acc.access.mask; + dummy = dummy->next; + } // while + + /* found other values or to restictive mask */ + if (dummy || ((mask & val->acc.access.in_mask) != val->acc.access.in_mask)) { + val->in_val = 0; + } + } + if (count > 0) deleteDefmapChain (&oldval); + } // if + val = val->next; + } + + /* handle valnum assignment */ + switch (pci->op) { + case POC_CLRF: /* modifies STATUS (Z) */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + oldval = defmapCurr (list, sym1, pc); + if (oldval && (litFromValnum (oldval->in_val) == 0)) { + //fprintf (stderr, "%s: REG (%s) already set up correctly (%x)\n", pci->mnemonic, strFromSym(sym1), oldval->in_val); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant CLRF removed"); + } + defmapUpdate (list, sym1, pc, valnumFromLit(0)); + } + break; + + case POC_SETF: /* SETF does not touch STATUS */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + oldval = defmapCurr (list, sym1, pc); + if (oldval && (litFromValnum (oldval->in_val) == 0x00FF)) { + //fprintf (stderr, "%s: REG (%s) already set up correctly (%x)\n", pci->mnemonic, strFromSym(sym1), oldval->in_val); + pic16_safepCodeRemove (pc, "=DF= redundant SETF removed"); + } + defmapUpdate (list, sym1, pc, valnumFromLit (0x00FF)); + } + break; + + case POC_MOVLW: /* does not touch STATUS */ + oldval = defmapCurr (list, SPO_WREG, pc); + if (pci->pcop->type == PO_LITERAL) { + //fprintf (stderr, "MOVLW: literal %u\n", PCOL(pci->pcop)->lit); + litnum = valnumFromLit ((unsigned char)PCOL(pci->pcop)->lit); + } else { + //fprintf (stderr, "MOVLW: %s\n", pic16_get_op (pci->pcop, NULL, 0)); + litnum = valnumFromStr (pic16_get_op (pci->pcop, NULL, 0)); + } + if (oldval && oldval->in_val == litnum) { + //fprintf (stderr, "%s: W already set up correctly (%x)\n", PCI(pc)->mnemonic, oldval->in_val); + pic16_safepCodeRemove (pc, "=DF= redundant MOVLW removed"); + } + defmapUpdate (list, SPO_WREG, pc, litnum); + break; + + case POC_ANDLW: /* modifies STATUS (Z,N) */ + case POC_IORLW: /* modifies STATUS (Z,N) */ + case POC_XORLW: /* modifies STATUS (Z,N) */ + /* can be optimized iff WREG contains a known literal (0x100 - 0x1FF) */ + if (pci->pcop->type == PO_LITERAL) { + int vallit = -1; + lit = (unsigned char) PCOL(pci->pcop)->lit; + val = defmapCurr (list, SPO_WREG, pc); + if (val) vallit = litFromValnum (val->in_val); + if (vallit != -1) { + /* xxxLW , WREG contains a known literal */ + //fprintf (stderr, "%s 0x%02x, WREG: 0x%x\n", pci->mnemonic, lit, vallit); + if (pci->op == POC_ANDLW) { + lit &= vallit; + } else if (pci->op == POC_IORLW) { + lit |= vallit; + } else if (pci->op == POC_XORLW) { + lit ^= vallit; + } else { + assert (0 && "invalid operation"); + } + if (vallit == lit) { + //fprintf (stderr, "%s: W already set up correctly (%x = val %x)\n", pci->mnemonic, vallit, val->in_val); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant ANDLW/IORLW/XORLW removed"); + } + defmapUpdate (list, SPO_WREG, pc, valnumFromLit (lit)); + } // if + } + break; + + case POC_LFSR: + { + /* check if old value matches new value */ + int lit; + int ok = TRUE; + assert (pci->pcop->type == PO_LITERAL); + + lit = PCOL(pci->pcop)->lit; + + val = defmapCurr (list, pic16_fsrsym_idx[lit][0], pc); + + if (val && (val->in_val != 0) && (val->in_val == val->val)) { + //fprintf (stderr, "FSR%dL already set up correctly at %p (%x)\n", lit, pc, val->val); + } else { + /* cannot remove this LFSR */ + ok = FALSE; + } // if + + val = defmapCurr (list, pic16_fsrsym_idx[lit][1], pc); + if (val && (val->in_val != 0) && (val->in_val == val->val)) { + //fprintf (stderr, "FSR%dH already set up correctly at %p (%x)\n", lit, pc, val->val); + } else { + ok = FALSE; + } // if + + if (ok) { + pic16_safepCodeRemove (pc, "=DF= redundant LFSR removed"); + } + } + break; + + case POC_MOVWF: /* does not touch flags */ + /* find value of WREG */ + val = defmapCurr (list, SPO_WREG, pc); + oldval = defmapCurr (list, sym1, pc); + if (val) lit = litFromValnum (val->in_val); + else lit = -1; + //fprintf (stderr, "MOVWF: lit: %i (%x, %x)\n", lit, lit, val->in_val); + + if ((lit == 0 || lit == 0x0ff) && !pic16_isAlive (SPO_STATUS, pc)) { + /* might replace with CLRF/SETF (will possibly make previous MOVLW 0x00/0xff unneccessary --> dead code elimination) */ + //fprintf (stderr, "replacing MOVWF with CLRF/SETF\n"); + if (lit == 0) { + newpc = pic16_newpCode (POC_CLRF, pic16_pCodeOpCopy (pci->pcop)); + } else { + assert (lit == 0x0ff); + newpc = pic16_newpCode (POC_SETF, pic16_pCodeOpCopy (pci->pcop)); + } + if (pic16_debug_verbose || pic16_pcode_verbose) pic16_InsertCommentAfter (pc->prev, "=DF= MOVWF: replaced by CLRF/SETF"); + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, SPO_WREG, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + + /* This breaks the defmap chain's references to pCodes... fix it! */ + if (!val->prev) PCI(pc)->pcflow->defmap = val->next; + if (!val->acc.access.isWrite) { + deleteDefmap (val); // delete reference to WREG as in value + val = NULL; + } else { + val->acc.access.isRead = 0; // delete reference to WREG as in value + } + oldval = PCI(pc)->pcflow->defmap; + while (oldval) { + if (oldval->pc == pc) oldval->pc = newpc; + oldval = oldval->next; + } // while + } else if (!isSpecial1 && pic16_regIsLocal (reg1) && val && oldval && (val->in_val != 0) && (val->in_val == oldval->in_val)) { + //fprintf (stderr, "MOVWF: F (%s) already set up correctly (%x) at %p\n", strFromSym (sym1), oldval->in_val, pc); + pic16_safepCodeRemove (pc, "=DF= redundant MOVWF removed"); + } + if (val) defmapUpdate (list, sym1, pc, val->in_val); + break; + + case POC_MOVFW: /* modifies STATUS (Z,N) */ + /* find value of REG */ + if (!isSpecial1 && pic16_regIsLocal (reg1)) { + val = defmapCurr (list, sym1, pc); + oldval = defmapCurr (list, SPO_WREG, pc); + if (val && oldval && (val->in_val != 0) && (val->in_val == oldval->in_val)) { + //fprintf (stderr, "MOVFW: W already set up correctly (%x) at %p\n", oldval->in_val, pc); + if (!pic16_isAlive (SPO_STATUS, pc)) pic16_safepCodeRemove (pc, "=DF= redundant MOVFW removed"); + } else { + defmap_t *pred, *predpred; + /* Optimize MOVLW immd; MOVWF reg1; [...]; MOVFW reg1 + * into MOVLW immd; MOVWF reg1; [...]; MOVLW immd + * This might allow removal of the first two assignments. */ + pred = defmapFindDef (list, sym1, pc); + predpred = pred ? defmapFindDef (list, SPO_WREG, pred->pc) : NULL; + if (pred && predpred && (PCI(pred->pc)->op == POC_MOVWF) && (PCI(predpred->pc)->op == POC_MOVLW) + && !pic16_isAlive (SPO_STATUS, pc)) + { + newpc = pic16_newpCode (POC_MOVLW, pic16_pCodeOpCopy (PCI(predpred->pc)->pcop)); + + if (pic16_debug_verbose || pic16_pcode_verbose) { + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFW: replaced last of MOVLW;MOVWF;MOVFW by MOVLW"); + } // if + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, sym1, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + + /* This breaks the defmap chain's references to pCodes... fix it! */ + if (!val->prev) PCI(pc)->pcflow->defmap = val->next; + if (!val->acc.access.isWrite) { + deleteDefmap (val); // delete reference to reg1 as in value + val = NULL; + } else { + val->acc.access.isRead = 0; // delete reference to reg1 as in value + } + oldval = PCI(pc)->pcflow->defmap; + while (oldval) { + if (oldval->pc == pc) oldval->pc = newpc; + oldval = oldval->next; + } // while + } // if + } + if (val) defmapUpdate (list, SPO_WREG, pc, val->in_val); + } + break; + + case POC_MOVFF: /* does not touch STATUS */ + /* find value of REG */ + val = defmapCurr (list, sym1, pc); + oldval = defmapCurr (list, sym2, pc); + if (val) lit = litFromValnum (val->in_val); + else lit = -1; + newpc = NULL; + if (!isSpecial1 && pic16_regIsLocal (reg1) && val && oldval && !pic16_isAlive (SPO_STATUS, pc)) { + //pc->print (stderr, pc); fprintf (stderr, "lit: %d (%x, %x)\n", lit, lit, val->in_val); + if (lit == 0) { + newpc = pic16_newpCode (POC_CLRF, PCOP2(pci->pcop)->pcopR); + } else if (lit == 0x00ff) { + newpc = pic16_newpCode (POC_SETF, PCOP2(pci->pcop)->pcopR); + } else { + newpc = NULL; + } + if (newpc) { + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: replaced by CRLF/SETF"); + pic16_df_saved_bytes += PCI(pc)->isize - PCI(newpc)->isize; + pic16_pCodeReplace (pc, newpc); + defmapReplaceSymRef (pc, sym1, 0, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + break; // do not process instruction as MOVFF... + } + } else if (!isSpecial1 && !isSpecial2 + && pic16_regIsLocal (reg1) && pic16_regIsLocal (reg2) + && val && oldval && (val->in_val != 0)) { + if (val->in_val == oldval->in_val) { + //fprintf (stderr, "MOVFF: F2 (%s) already set up correctly (%x) at %p\n", strFromSym (sym2), oldval->in_val, pc); + pic16_safepCodeRemove (pc, "=DF= redundant MOVFF removed"); + } else { + if (!pic16_isAlive (sym1, pc)) { + defmap_t *copy = NULL; + /* If there is another symbol S storing sym1's value we should assign from S thus shortening the liferange of sym1. + * This should help eliminate + * MOVFF A,B + * + * MOVFF B,C + * + * and turn it into + * + * MOVFF A,C + */ + + /* scan defmap for symbols storing sym1's value */ + while (oldval && (oldval->pc == pc || oldval->in_val != val->in_val)) oldval = oldval->next; + if (oldval && (oldval->sym != sym1) && defmapFindAll (oldval->sym, pc, ©) == 1) { + /* unique reaching definition for sym found */ + if (copy->val && copy->val == val->in_val) { + //fprintf (stderr, "found replacement symbol for %s (val %x) <-- %s (assigned %x @ %p)\n", strFromSym(sym1), val->in_val, strFromSym(copy->sym), copy->val, copy->pc); + if (copy->sym == SPO_WREG) { + newpc = pic16_newpCode (POC_MOVWF, pic16_pCodeOpCopy (PCOP2(pci->pcop)->pcopR)); + } else { + pCodeOp *pcop = NULL; + /* the code below fails if we try to replace + * MOVFF PRODL, r0x03 + * MOVFF r0x03, PCLATU + * with + * MOVFF PRODL, PCLATU + * as copy(PRODL) contains has pc==NULL, by name fails... + */ + if (!copy->pc || !PCI(copy->pc)->pcop) break; + + if (copy->pc && PCI(copy->pc)->pcop) + pcop = PCI(copy->pc)->pcop; +#if 0 + /* This code is broken--see above. */ + else + { + const char *symname = strFromSym(copy->sym); + + assert( symname ); + pic16_InsertCommentAfter (pc->prev, "BUG-ME"); + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: newpCodeOpregFromStr(%s)", (char *)symname); + //pcop = pic16_newpCodeOpRegFromStr((char *)symname); + } +#endif + assert( pcop ); + newpc = pic16_newpCode(POC_MOVFF, pic16_popGet2p( + pcop, + pic16_pCodeOpCopy (PCOP2(pci->pcop)->pcopR))); + } + pic16_InsertCommentAfter (pc->prev, "=DF= MOVFF: SRC op %s replaced by %s", strFromSym(sym1), strFromSym(copy->sym)); + pic16_df_saved_bytes += PCI(pc)->isize - PCI(newpc)->isize; + pic16_pCodeReplace (pc, newpc); + assert (val->sym == sym1 && val->acc.access.isRead && !val->acc.access.isWrite); + defmapReplaceSymRef (pc, sym1, copy->sym, 1); + pic16_fixDefmap (pc, newpc); + pc = newpc; + } + } + deleteDefmapChain (©); + } + } + if (val) defmapUpdate (list, sym2, pc, val->in_val); + } + break; + + default: + /* cannot optimize */ + break; + } // switch +} + +static void pic16_destructDF (pBlock *pb) { + pCode *pc, *next; + + if (!pb) return; + + /* remove old defmaps */ + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (isPCI(pc) || isPCAD(pc)); + assert (PCI(pc)->pcflow); + deleteDefmapChain (&PCI(pc)->pcflow->defmap); + deleteDefmapChain (&PCI(pc)->pcflow->in_vals); + deleteDefmapChain (&PCI(pc)->pcflow->out_vals); + + pc = next; + } // while + + if (defmap_free || defmap_free_count) { + //fprintf (stderr, "released defmaps: %u -- freeing up memory\n", defmap_free_count); + freeDefmap (&defmap_free); + defmap_free_count = 0; + } +} + +/* Checks whether a pBlock contains ASMDIRs. */ +static int pic16_pBlockHasAsmdirs (pBlock *pb) { + pCode *pc; + + if (!pb) return FALSE; + + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + if (isPCAD(pc)) return TRUE; + + pc = pic16_findNextInstruction (pc->next); + } // while + + /* no PCADs found */ + return FALSE; +} + +#if 1 +/* Remove MOVFF r0x??, POSTDEC1 and MOVFF PREINC1, r0x?? for otherwise unused registers. */ +static int pic16_removeUnusedRegistersDF () { + pCode *pc, *pc2; + pBlock *pb; + reg_info *reg1, *reg2, *reg3; + set *seenRegs = NULL; + int cond, i; + int islocal, change = 0; + + /* no pBlocks? */ + if (!the_pFile || !the_pFile->pbHead) return 0; + + for (pb = the_pFile->pbHead; pb; pb = pb->next) { + //fprintf (stderr, "%s: examining function %s\n", __FUNCTION__, pic16_pBlockGetFunctionName (pb)); +#if 1 + /* find set of using pCodes per register */ + for (pc = pic16_findNextInstruction (pb->pcHead); pc; + pc = pic16_findNextInstruction(pc->next)) { + + cond = PCI(pc)->inCond | PCI(pc)->outCond; + reg1 = reg2 = NULL; + if (cond & PCC_REGISTER) reg1 = pic16_getRegFromInstruction (pc); + if (cond & PCC_REGISTER2) reg2 = pic16_getRegFromInstruction2 (pc); + + if (reg1) { + if (!isinSet (seenRegs, reg1)) reg1->reglives.usedpCodes = NULL; + addSetIfnotP (&seenRegs, reg1); + addSetIfnotP (®1->reglives.usedpCodes, pc); + } + if (reg2) { + if (!isinSet (seenRegs, reg2)) reg2->reglives.usedpCodes = NULL; + addSetIfnotP (&seenRegs, reg2); + addSetIfnotP (®2->reglives.usedpCodes, pc); + } + } // for pc +#endif + for (reg1 = setFirstItem (seenRegs); reg1; reg1 = setNextItem (seenRegs)) { + /* may not use pic16_regIsLocal() here -- in interrupt routines + * WREG, PRODx, FSR0x must be saved */ + islocal = (reg1->isLocal || reg1->rIdx == pic16_framepnt_lo->rIdx || reg1->rIdx == pic16_framepnt_hi->rIdx); + if (islocal && elementsInSet (reg1->reglives.usedpCodes) == 2) { + pc = pc2 = NULL; + for (i=0; i < 2; i++) { + pc = (pCode *) indexSet(reg1->reglives.usedpCodes, i); + if (!pc2) pc2 = pc; + if (!isPCI(pc) || !(PCI(pc)->op == POC_MOVFF)) continue; + reg2 = pic16_getRegFromInstruction (pc); + reg3 = pic16_getRegFromInstruction2 (pc); + if (!reg2 || !reg3 + || (reg2->rIdx != pic16_stack_preinc->rIdx + && reg3->rIdx != pic16_stack_postdec->rIdx)) break; + if (i == 1) { + /* both pCodes are MOVFF R,POSTDEC1 / MOVFF PREINC1,R */ + //fprintf (stderr, "%s: removing local register %s from %s\n", __FUNCTION__, reg1->name, pic16_pBlockGetFunctionName (pb)); + pic16_safepCodeRemove (pc, "removed unused local reg IN"); + pic16_safepCodeRemove (pc2, "removed unused local reg OUT"); + } + } // for + } // if + deleteSet (®1->reglives.usedpCodes); + } // for reg1 + + deleteSet (&seenRegs); + } // for pb + + return change; +} +#endif + +/* Set up pCodeFlow's defmap_ts. + * Needs correctly set up to/from fields. */ +static void pic16_createDF (pBlock *pb) { + pCode *pc, *next; + int change=0; + + if (!pb) return; + + //fprintf (stderr, "creating DF for pb %p (%s)\n", pb, pic16_pBlockGetFunctionName (pb)); + + pic16_destructDF (pb); + + /* check pBlock: do not analyze pBlocks with ASMDIRs (for now...) */ + if (pic16_pBlockHasAsmdirs (pb)) { + //fprintf (stderr, "%s: pBlock contains ASMDIRs -- data flow analysis not performed!\n", __FUNCTION__); + return; + } + + /* integrity check -- we need to reach all flows to guarantee + * correct data flow analysis (reaching definitions, aliveness) */ +#if 0 + if (!verifyAllFlowsReachable (pb)) { + fprintf (stderr, "not all flows reachable -- aborting dataflow analysis for %s!\n", pic16_pBlockGetFunctionName (pb)); + return; + } +#endif + + /* establish new defmaps */ + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (PCI(pc)->pcflow); + PCI(pc)->pcflow->defmap = createDefmap (pc, PCI(pc)->pcflow->defmap); + + pc = next; + } // while + + //fprintf (stderr, "%s: creating reaching definitions...\n", __FUNCTION__); + createReachingDefinitions (pb); + +#if 1 + /* assign better valnums */ + //fprintf (stderr, "assigning valnums for pb %p\n", pb); + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + assert (PCI(pc)->pcflow); + assignValnums (pc); + + pc = next; + } // while +#endif + +#if 1 + /* remove dead pCodes */ + //fprintf (stderr, "removing dead pCodes in %p (%s)\n", pb, pic16_pBlockGetFunctionName (pb)); + do { + change = 0; + pc = pic16_findNextInstruction (pb->pcHead); + while (pc) { + next = pic16_findNextInstruction (pc->next); + + if (isPCI(pc) && !isPCI_BRANCH(pc) && !pic16_pCodeIsAlive (pc)) { + change += pic16_safepCodeRemove (pc, "=DF= removed dead pCode"); + } + + pc = next; + } // while + } while (change); +#endif +} + +/* ======================================================================== */ +/* === VCG DUMPER ROUTINES ================================================ */ +/* ======================================================================== */ +#if defined (DUMP_DF_GRAPHS) && DUMP_DF_GRAPHS > 0 +hTab *dumpedNodes = NULL; + +/** Dump VCG header into of. */ +static void pic16_vcg_init (FILE *of) { + /* graph defaults */ + fprintf (of, "graph:{\n"); + fprintf (of, "title:\"graph1\"\n"); + fprintf (of, "label:\"graph1\"\n"); + fprintf (of, "color:white\n"); + fprintf (of, "textcolor:black\n"); + fprintf (of, "bordercolor:black\n"); + fprintf (of, "borderwidth:1\n"); + fprintf (of, "textmode:center\n"); + + fprintf (of, "layoutalgorithm:dfs\n"); + fprintf (of, "late_edge_labels:yes\n"); + fprintf (of, "display_edge_labels:yes\n"); + fprintf (of, "dirty_edge_labels:yes\n"); + fprintf (of, "finetuning:yes\n"); + fprintf (of, "ignoresingles:no\n"); + fprintf (of, "straight_phase:yes\n"); + fprintf (of, "priority_phase:yes\n"); + fprintf (of, "manhattan_edges:yes\n"); + fprintf (of, "smanhattan_edges:no\n"); + fprintf (of, "nearedges:no\n"); + fprintf (of, "node_alignment:center\n"); // bottom|top|center + fprintf (of, "port_sharing:no\n"); + fprintf (of, "arrowmode:free\n"); // fixed|free + fprintf (of, "crossingphase2:yes\n"); + fprintf (of, "crossingoptimization:yes\n"); + fprintf (of, "edges:yes\n"); + fprintf (of, "nodes:yes\n"); + fprintf (of, "splines:no\n"); + + /* node defaults */ + fprintf (of, "node.color:lightyellow\n"); + fprintf (of, "node.textcolor:black\n"); + fprintf (of, "node.textmode:center\n"); + fprintf (of, "node.shape:box\n"); + fprintf (of, "node.bordercolor:black\n"); + fprintf (of, "node.borderwidth:1\n"); + + /* edge defaults */ + fprintf (of, "edge.textcolor:black\n"); + fprintf (of, "edge.color:black\n"); + fprintf (of, "edge.thickness:1\n"); + fprintf (of, "edge.arrowcolor:black\n"); + fprintf (of, "edge.backarrowcolor:black\n"); + fprintf (of, "edge.arrowsize:15\n"); + fprintf (of, "edge.backarrowsize:15\n"); + fprintf (of, "edge.arrowstyle:line\n"); // none|solid|line + fprintf (of, "edge.backarrowstyle:none\n"); // none|solid|line + fprintf (of, "edge.linestyle:continuous\n"); // continuous|solid|dotted|dashed|invisible + + fprintf (of, "\n"); + + /* prepare data structures */ + if (dumpedNodes) { + hTabDeleteAll (dumpedNodes); + dumpedNodes = NULL; + } + dumpedNodes = newHashTable (128); +} + +/** Dump VCG footer into of. */ +static void pic16_vcg_close (FILE *of) { + fprintf (of, "}\n"); +} + +#define BUF_SIZE 128 +#define pcTitle(pc) (SNPRINTF (buf, BUF_SIZE, "n_%p, %p/%u", PCODE(pc), isPCI(pc) ? PCI(pc)->pcflow : NULL, PCODE(pc)->seq), &buf[0]) + +#if 0 +static int ptrcmp (const void *p1, const void *p2) { + return p1 == p2; +} +#endif + +/** Dump a pCode node as VCG to of. */ +static void pic16_vcg_dumpnode (pCode *pc, FILE *of) { + char buf[BUF_SIZE]; + + if (hTabFindByKey (dumpedNodes, (((char *) pc - (char *) 0)>>2) % 128, pc, ptrcmp)) { + // dumped already + return; + } + hTabAddItemLong (&dumpedNodes, (((char *) pc - (char *) 0)>>2) % 128, pc, pc); + //fprintf (stderr, "dumping %p\n", pc); + + /* only dump pCodeInstructions and Flow nodes */ + if (!isPCI(pc) && !isPCAD(pc) && !isPCFL(pc)) return; + + /* emit node */ + fprintf (of, "node:{"); + fprintf (of, "title:\"%s\" ", pcTitle(pc)); + fprintf (of, "label:\"%s\n", pcTitle(pc)); + if (isPCFL(pc)) { + fprintf (of, ""); + } else if (isPCI(pc) || isPCAD(pc)) { + pc->print (of, pc); + } else { + fprintf (of, ""); + } + fprintf (of, "\" "); + fprintf (of, "}\n"); + + if (1 && isPCFL(pc)) { + defmap_t *map, *prev; + unsigned int i; + map = PCFL(pc)->defmap; + i=0; + while (map) { + if (map->sym != 0) { + i++; + + /* emit definition node */ + fprintf (of, "node:{title:\"%s_def%u\" ", pcTitle(pc), i); + fprintf (of, "label:\""); + + prev = map; + do { + fprintf (of, "%s%c%c: val %4x|%4x & %02x|%02x, sym %s", (prev == map) ? "" : "\n", map->acc.access.isRead ? 'R' : ' ', map->acc.access.isWrite ? 'W' : ' ', map->in_val, map->val, map->acc.access.in_mask, map->acc.access.mask, strFromSym (map->sym)); + prev = map; + map = map->next; + } while (map && prev->pc == map->pc); + map = prev; + + fprintf (of, "\" "); + + fprintf (of, "color:green "); + fprintf (of, "}\n"); + + /* emit edge to previous definition */ + fprintf (of, "edge:{sourcename:\"%s_def%u\" ", pcTitle(pc), i); + if (i == 1) { + fprintf (of, "targetname:\"%s\" ", pcTitle(pc)); + } else { + fprintf (of, "targetname:\"%s_def%u\" ", pcTitle(pc), i-1); + } + fprintf (of, "color:green "); + fprintf (of, "}\n"); + + if (map->pc) { + pic16_vcg_dumpnode (map->pc, of); + fprintf (of, "edge:{sourcename:\"%s_def%u\" ", pcTitle(pc), i); + fprintf (of, "targetname:\"%s\" linestyle:dashed color:lightgreen}\n", pcTitle(map->pc)); + } + } + map = map->next; + } // while + } + + /* emit additional nodes (e.g. operands) */ +} + +/** Dump a pCode's edges (control flow/data flow) as VCG to of. */ +static void pic16_vcg_dumpedges (pCode *pc, FILE *of) { + char buf[BUF_SIZE]; + pCodeInstruction *pci; + pBranch *curr; + int i; + + if (1 && isPCFL(pc)) { + /* emit edges to flow successors */ + void *pcfl; + //fprintf (stderr, "PCFLOWe @ %p\n", pc); + pcfl = setFirstItem (PCFL(pc)->to); + while (pcfl) { + pcfl = ((pCodeFlowLink *) (pcfl))->pcflow; + pic16_vcg_dumpnode (pc, of); + pic16_vcg_dumpnode ((pCode *) pcfl, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:lightred linestyle:dashed}\n", pcTitle(pcfl)); + pcfl = setNextItem (PCFL(pc)->to); + } // while + } // if + + if (!isPCI(pc) && !isPCAD(pc)) return; + + pci = PCI(pc); + + /* emit control flow edges (forward only) */ + curr = pci->to; + i=0; + while (curr) { + pic16_vcg_dumpnode (curr->pc, of); + fprintf (of, "edge:{"); + fprintf (of, "sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" ", pcTitle(curr->pc)); + fprintf (of, "color:red "); + fprintf (of, "}\n"); + curr = curr->next; + } // while + +#if 1 + /* dump "flow" edge (link pCode according to pBlock order) */ + { + pCode *pcnext; + pcnext = pic16_findNextInstruction (pc->next); + if (pcnext) { + pic16_vcg_dumpnode (pcnext, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:red linestyle:solid}\n", pcTitle(pcnext)); + } + } +#endif + +#if 0 + /* emit flow */ + if (pci->pcflow) { + pic16_vcg_dumpnode (&pci->pcflow->pc, of); + fprintf (of, "edge:{sourcename:\"%s\" ", pcTitle(pc)); + fprintf (of, "targetname:\"%s\" color:lightblue linestyle:dashed}\n", pcTitle (pci->pcflow)); + } +#endif + + /* emit data flow edges (backward only) */ + /* TODO: gather data flow information... */ +} + +static void pic16_vcg_dump (FILE *of, pBlock *pb) { + pCode *pc; + + if (!pb) return; + + /* check pBlock: do not analyze pBlocks with ASMDIRs (for now...) */ + if (pic16_pBlockHasAsmdirs (pb)) { + //fprintf (stderr, "%s: pBlock contains ASMDIRs -- data flow analysis not performed!\n", __FUNCTION__); + return; + } + + for (pc=pb->pcHead; pc; pc = pc->next) { + pic16_vcg_dumpnode (pc, of); + } // for pc + + for (pc=pb->pcHead; pc; pc = pc->next) { + pic16_vcg_dumpedges (pc, of); + } // for pc +} + +static void pic16_vcg_dump_default (pBlock *pb) { + FILE *of; + char buf[BUF_SIZE]; + pCode *pc; + + if (!pb) return; + + /* get function name */ + pc = pb->pcHead; + while (pc && !isPCF(pc)) pc = pc->next; + if (pc) { + SNPRINTF (buf, BUF_SIZE, "%s_%s.vcg", PCF(pc)->modname, PCF(pc)->fname); + } else { + SNPRINTF (buf, BUF_SIZE, "pb_%p.vcg", pb); + } + + //fprintf (stderr, "now dumping %s\n", buf); + of = fopen (buf, "w"); + pic16_vcg_init (of); + pic16_vcg_dump (of, pb); + pic16_vcg_close (of); + fclose (of); +} +#endif + +/*** END of helpers for pCode dataflow optimizations ***/ diff --git a/src/pic16/pcode.h b/src/pic16/pcode.h new file mode 100644 index 0000000..e29bef6 --- /dev/null +++ b/src/pic16/pcode.h @@ -0,0 +1,1141 @@ +/*------------------------------------------------------------------------- + + pcode.h - post code generation + Written By - Scott Dattalo scott@dattalo.com + Ported to PIC16 By - Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +//#include "ralloc.h" +struct reg_info; + +/* + Post code generation + + The post code generation is an assembler optimizer. The assembly code + produced by all of the previous steps is fully functional. This step + will attempt to analyze the flow of the assembly code and agressively + optimize it. The peep hole optimizer attempts to do the same thing. + As you may recall, the peep hole optimizer replaces blocks of assembly + with more optimal blocks (e.g. removing redundant register loads). + However, the peep hole optimizer has to be somewhat conservative since + an assembly program has implicit state information that's unavailable + when only a few instructions are examined. + Consider this example: + + example1: + movwf t1 + movf t1,w + + The movf seems redundant since we know that the W register already + contains the same value of t1. So a peep hole optimizer is tempted to + remove the "movf". However, this is dangerous since the movf affects + the flags in the status register (specifically the Z flag) and subsequent + code may depend upon this. Look at these two examples: + + example2: + movwf t1 + movf t1,w ; Can't remove this movf + skpz + return + + example3: + movwf t1 + movf t1,w ; This movf can be removed + xorwf t2,w ; since xorwf will over write Z + skpz + return + +*/ + + +#ifndef __PCODE_H__ +#define __PCODE_H__ + +/*********************************************************************** + * debug stuff + * + * The DFPRINTF macro will call fprintf if PCODE_DEBUG is defined. + * The macro is used like: + * + * DPRINTF(("%s #%d\n","test", 1)); + * + * The double parenthesis (()) are necessary + * + ***********************************************************************/ +//#define PCODE_DEBUG + +#ifdef PCODE_DEBUG +#define DFPRINTF(args) (fprintf args) +#else +#define DFPRINTF(args) ; +#endif + + +#ifdef WORDS_BIGENDIAN + #define _ENDIAN(x) (3-x) +#else + #define _ENDIAN(x) (x) +#endif + + +#define BYTE_IN_LONG(x,b) ((x>>(8*_ENDIAN(b)))&0xff) + + +/*********************************************************************** + * Extended Instruction Set/Indexed Literal Offset Mode * + * Set this macro to enable code generation with the extended * + * instruction set and the new Indexed Literal Offset Mode * + ***********************************************************************/ +#define XINST 1 + +/*********************************************************************** + * PIC status bits - this will move into device dependent headers + ***********************************************************************/ +#define PIC_C_BIT 0 +#define PIC_DC_BIT 1 +#define PIC_Z_BIT 2 +#define PIC_OV_BIT 3 +#define PIC_N_BIT 4 +#define PIC_IRP_BIT 7 /* Indirect register page select */ + +/*********************************************************************** + * PIC INTCON bits - this will move into device dependent headers + ***********************************************************************/ +#define PIC_RBIF_BIT 0 /* Port B level has changed flag */ +#define PIC_INTF_BIT 1 /* Port B bit 0 interrupt on edge flag */ +#define PIC_T0IF_BIT 2 /* TMR0 has overflowed flag */ +#define PIC_RBIE_BIT 3 /* Port B level has changed - Interrupt Enable */ +#define PIC_INTE_BIT 4 /* Port B bit 0 interrupt on edge - Int Enable */ +#define PIC_T0IE_BIT 5 /* TMR0 overflow Interrupt Enable */ +#define PIC_PIE_BIT 6 /* Peripheral Interrupt Enable */ +#define PIC_GIE_BIT 7 /* Global Interrupt Enable */ + +/*********************************************************************** + * PIC bank definitions + ***********************************************************************/ +#define PIC_BANK_FIRST 0 +#define PIC_BANK_LAST 0xf + + +/*********************************************************************** + * Operand types + ***********************************************************************/ +#define POT_RESULT 0 +#define POT_LEFT 1 +#define POT_RIGHT 2 + + +/*********************************************************************** + * + * PIC_OPTYPE - Operand types that are specific to the PIC architecture + * + * If a PIC assembly instruction has an operand then here is where we + * associate a type to it. For example, + * + * movf reg,W + * + * The movf has two operands: 'reg' and the W register. 'reg' is some + * arbitrary general purpose register, hence it has the type PO_GPR_REGISTER. + * The W register, which is the PIC's accumulator, has the type PO_W. + * + ***********************************************************************/ + + + +typedef enum +{ + PO_NONE=0, // No operand e.g. NOP + PO_W, // The working register (as a destination) + PO_WREG, // The working register (as a file register) + PO_STATUS, // The 'STATUS' register + PO_BSR, // The 'BSR' register + PO_FSR0, // The "file select register" (in PIC18 family it's one + // of three) + PO_INDF0, // The Indirect register + PO_INTCON, // Interrupt Control register + PO_GPR_REGISTER, // A general purpose register + PO_GPR_BIT, // A bit of a general purpose register + PO_GPR_TEMP, // A general purpose temporary register + PO_SFR_REGISTER, // A special function register (e.g. PORTA) + PO_PCL, // Program counter Low register + PO_PCLATH, // Program counter Latch high register + PO_PCLATU, // Program counter Latch upper register + PO_PRODL, // Product Register Low + PO_PRODH, // Product Register High + PO_LITERAL, // A constant + PO_REL_ADDR, // A relative address + PO_IMMEDIATE, // (8051 legacy) + PO_DIR, // Direct memory (8051 legacy) + PO_CRY, // bit memory (8051 legacy) + PO_BIT, // bit operand. + PO_STR, // (8051 legacy) + PO_LABEL, + PO_WILD, // Wild card operand in peep optimizer + PO_TWO_OPS // combine two operands +} PIC_OPTYPE; + + +/*********************************************************************** + * + * PIC_OPCODE + * + * This is not a list of the PIC's opcodes per se, but instead + * an enumeration of all of the different types of pic opcodes. + * + ***********************************************************************/ + +typedef enum +{ + POC_WILD=-1, /* Wild card - used in the pCode peep hole optimizer + * to represent ANY pic opcode */ + POC_ADDLW=0, + POC_ADDWF, + POC_ADDFW, + POC_ADDFWC, + POC_ADDWFC, + POC_ANDLW, + POC_ANDWF, + POC_ANDFW, + POC_BC, + POC_BCF, + POC_BN, + POC_BNC, + POC_BNN, + POC_BNOV, + POC_BNZ, + POC_BOV, + POC_BRA, + POC_BSF, + POC_BTFSC, + POC_BTFSS, + POC_BTG, + POC_BZ, + POC_CALL, + POC_CLRF, + POC_CLRWDT, + POC_COMF, + POC_COMFW, + POC_CPFSEQ, + POC_CPFSGT, + POC_CPFSLT, + POC_DAW, + POC_DCFSNZ, + POC_DCFSNZW, + POC_DECF, + POC_DECFW, + POC_DECFSZ, + POC_DECFSZW, + POC_GOTO, + POC_INCF, + POC_INCFW, + POC_INCFSZ, + POC_INCFSZW, + POC_INFSNZ, + POC_INFSNZW, + POC_IORWF, + POC_IORFW, + POC_IORLW, + POC_LFSR, + POC_MOVF, + POC_MOVFW, + POC_MOVFF, + POC_MOVLB, + POC_MOVLW, + POC_MOVWF, + POC_MULLW, + POC_MULWF, + POC_NEGF, + POC_NOP, + POC_POP, + POC_PUSH, + POC_RCALL, + POC_RETFIE, + POC_RETLW, + POC_RETURN, + POC_RLCF, + POC_RLCFW, + POC_RLNCF, + POC_RLNCFW, + POC_RRCF, + POC_RRCFW, + POC_RRNCF, + POC_RRNCFW, + POC_SETF, + POC_SUBLW, + POC_SUBFWB, + POC_SUBWF, + POC_SUBFW, + POC_SUBWFB_D0, + POC_SUBWFB_D1, + POC_SUBFWB_D0, + POC_SUBFWB_D1, + POC_SWAPF, + POC_SWAPFW, + POC_TBLRD, + POC_TBLRD_POSTINC, + POC_TBLRD_POSTDEC, + POC_TBLRD_PREINC, + POC_TBLWT, + POC_TBLWT_POSTINC, + POC_TBLWT_POSTDEC, + POC_TBLWT_PREINC, + POC_TSTFSZ, + POC_XORLW, + POC_XORWF, + POC_XORFW, + + POC_BANKSEL + + /* pseudo-instructions */ +} PIC_OPCODE; + + +/*********************************************************************** + * PC_TYPE - pCode Types + ***********************************************************************/ + +typedef enum +{ + PC_COMMENT=0, /* pCode is a comment */ + PC_INLINE, /* user's inline code */ + PC_OPCODE, /* PORT dependent opcode */ + PC_LABEL, /* assembly label */ + PC_FLOW, /* flow analysis */ + PC_FUNCTION, /* Function start or end */ + PC_WILD, /* wildcard - an opcode place holder used + * in the pCode peep hole optimizer */ + PC_CSOURCE, /* C-Source Line */ + PC_ASMDIR, /* Assembler directive */ + PC_BAD, /* Mark the pCode object as being bad */ + PC_INFO /* pCode information node, used primarily in optimizing */ +} PC_TYPE; + + +/*********************************************************************** + * INFO_TYPE - information node types + ***********************************************************************/ + +typedef enum +{ + INF_OPTIMIZATION, /* structure contains optimization information */ + INF_LOCALREGS /* structure contains local register information */ +} INFO_TYPE; + + + +/*********************************************************************** + * OPT_TYPE - optimization node types + ***********************************************************************/ + +typedef enum +{ + OPT_BEGIN, /* mark beginning of optimization block */ + OPT_END, /* mark ending of optimization block */ + OPT_JUMPTABLE_BEGIN, /* mark beginning of a jumptable */ + OPT_JUMPTABLE_END /* mark end of jumptable */ +} OPT_TYPE; + +/*********************************************************************** + * LR_TYPE - optimization node types + ***********************************************************************/ + +typedef enum +{ + LR_ENTRY_BEGIN, /* mark beginning of optimization block */ + LR_ENTRY_END, /* mark ending of optimization block */ + LR_EXIT_BEGIN, + LR_EXIT_END +} LR_TYPE; + + +/************************************************/ +/*************** Structures ********************/ +/************************************************/ +/* These are here as forward references - the + * full definition of these are below */ +struct pCode; +struct pCodeWildBlock; +struct pCodeRegLives; + +/************************************************* + pBranch + + The first step in optimizing pCode is determining + the program flow. This information is stored in + single-linked lists in the for of 'from' and 'to' + objects with in a pcode. For example, most instructions + don't involve any branching. So their from branch + points to the pCode immediately preceding them and + their 'to' branch points to the pcode immediately + following them. A skip instruction is an example of + a pcode that has multiple (in this case two) elements + in the 'to' branch. A 'label' pcode is an where there + may be multiple 'from' branches. + *************************************************/ + +typedef struct pBranch +{ + struct pCode *pc; // Next pCode in a branch + struct pBranch *next; /* If more than one branch + * the next one is here */ + +} pBranch; + +/************************************************* + pCodeOp + + pCode Operand structure. + For those assembly instructions that have arguments, + the pCode will have a pCodeOp in which the argument + can be stored. For example + + movf some_register,w + + 'some_register' will be stored/referenced in a pCodeOp + + *************************************************/ + +typedef struct pCodeOp +{ + PIC_OPTYPE type; + char *name; + +} pCodeOp; + +#if 0 +typedef struct pCodeOpBit +{ + pCodeOp pcop; + int bit; + unsigned int inBitSpace: 1; /* True if in bit space, else + just a bit of a register */ +} pCodeOpBit; +#endif + +typedef struct pCodeOpLit +{ + pCodeOp pcop; + int lit; + pCodeOp *arg2; /* needed as pCodeOpLit and pCodeOpLit2 are not separable via their type (PO_LITERAL) */ +} pCodeOpLit; + +typedef struct pCodeOpLit2 +{ + pCodeOp pcop; + int lit; + pCodeOp *arg2; +} pCodeOpLit2; + + +typedef struct pCodeOpImmd +{ + pCodeOp pcop; + int offset; /* low,high or upper byte of immediate value */ + int index; /* add this to the immediate value */ + unsigned _const:1; /* is in code space */ + + int rIdx; /* If this immd points to a register */ + struct reg_info *r; /* then this is the reg. */ + +} pCodeOpImmd; + +typedef struct pCodeOpLabel +{ + pCodeOp pcop; + int key; +} pCodeOpLabel; + +typedef struct pCodeOpReg +{ + pCodeOp pcop; // Can be either GPR or SFR + int rIdx; // Index into the register table + struct reg_info *r; + int instance; // byte # of Multi-byte registers + struct pBlock *pb; +} pCodeOpReg; + +typedef struct pCodeOp2 +{ + pCodeOp pcop; // describes this pCodeOp + pCodeOp *pcopL; // reference to left pCodeOp (src) + pCodeOp *pcopR; // reference to right pCodeOp (dest) +} pCodeOp2; + +typedef struct pCodeOpRegBit +{ + pCodeOpReg pcor; // The Register containing this bit + int bit; // 0-7 bit number. + PIC_OPTYPE subtype; // The type of this register. + unsigned int inBitSpace: 1; /* True if in bit space, else + just a bit of a register */ +} pCodeOpRegBit; + + +typedef struct pCodeOpWild +{ + pCodeOp pcop; + + struct pCodeWildBlock *pcwb; + + int id; /* index into an array of char *'s that will match + * the wild card. The array is in *pcp. */ + pCodeOp *subtype; /* Pointer to the Operand type into which this wild + * card will be expanded */ + pCodeOp *matched; /* When a wild matches, we'll store a pointer to the + * opcode we matched */ + + pCodeOp *pcop2; /* second operand if exists */ + +} pCodeOpWild; + + +typedef struct pCodeOpOpt +{ + pCodeOp pcop; + + OPT_TYPE type; /* optimization node type */ + + char *key; /* key by which a block is identified */ +} pCodeOpOpt; + +typedef struct pCodeOpLocalReg +{ + pCodeOp pcop; + + LR_TYPE type; +} pCodeOpLocalReg; + +/************************************************* + pCode + + Here is the basic build block of a PIC instruction. + Each pic instruction will get allocated a pCode. + A linked list of pCodes makes a program. + +**************************************************/ + +typedef struct pCode +{ + PC_TYPE type; + + struct pCode *prev; // The pCode objects are linked together + struct pCode *next; // in doubly linked lists. + + int seq; // sequence number + + struct pBlock *pb; // The pBlock that contains this pCode. + + /* "virtual functions" + * The pCode structure is like a base class + * in C++. The subsequent structures that "inherit" + * the pCode structure will initialize these function + * pointers to something useful */ + // void (*analyze) (struct pCode *_this); + void (*destruct)(struct pCode *_this); + void (*print) (FILE *of,struct pCode *_this); + +} pCode; + + +/************************************************* + pCodeComment +**************************************************/ + +typedef struct pCodeComment +{ + pCode pc; + + char *comment; + +} pCodeComment; + + +/************************************************* + pCodeCSource +**************************************************/ + +typedef struct pCodeCSource +{ + pCode pc; + + int line_number; + char *line; + char *file_name; + +} pCodeCSource; + + +/************************************************* + pCodeAsmDir +**************************************************/ + +/************************************************* + pCodeFlow + + The Flow object is used as marker to separate + the assembly code into contiguous chunks. In other + words, everytime an instruction cause or potentially + causes a branch, a Flow object will be inserted into + the pCode chain to mark the beginning of the next + contiguous chunk. + +**************************************************/ +struct defmap_s; // defined in pcode.c + +typedef struct pCodeFlow +{ + pCode pc; + + pCode *end; /* Last pCode in this flow. Note that + the first pCode is pc.next */ + + /* set **uses; * map the pCode instruction inCond and outCond conditions + * in this array of set's. The reason we allocate an + * array of pointers instead of declaring each type of + * usage is because there are port dependent usage definitions */ + //int nuses; /* number of uses sets */ + + set *from; /* flow blocks that can send control to this flow block */ + set *to; /* flow blocks to which this one can send control */ + struct pCodeFlow *ancestor; /* The most immediate "single" pCodeFlow object that + * executes prior to this one. In many cases, this + * will be just the previous */ + + int inCond; /* Input conditions - stuff assumed defined at entry */ + int outCond; /* Output conditions - stuff modified by flow block */ + + int firstBank; /* The first and last bank flags are the first and last */ + int lastBank; /* register banks used within one flow object */ + + int FromConflicts; + int ToConflicts; + + set *registers;/* Registers used in this flow */ + + struct defmap_s *defmap; /* chronologically ordered list of definitions performed + in this flow (most recent at the front) */ + struct defmap_s *in_vals; /* definitions of all symbols reaching this flow + * symbols with multiple different definitions are stored + * with an assigned value of 0. */ + struct defmap_s *out_vals; /* definitions valid AFTER thie flow */ + +} pCodeFlow; + +/************************************************* + pCodeFlowLink + + The Flow Link object is used to record information + about how consecutive excutive Flow objects are related. + The pCodeFlow objects demarcate the pCodeInstructions + into contiguous chunks. The FlowLink records conflicts + in the discontinuities. For example, if one Flow object + references a register in bank 0 and the next Flow object + references a register in bank 1, then there is a discontinuity + in the banking registers. + +*/ +typedef struct pCodeFlowLink +{ + pCodeFlow *pcflow; /* pointer to linked pCodeFlow object */ + + int bank_conflict; /* records bank conflicts */ + +} pCodeFlowLink; + +/************************************************* + pCodeInstruction + + Here we describe all the facets of a PIC instruction + (expansion for the 18cxxx is also provided). + +**************************************************/ + +typedef struct pCodeInstruction +{ + pCode pc; + + PIC_OPCODE op; // The opcode of the instruction. + + char const * const mnemonic; // Pointer to mnemonic string + + char isize; // pCode instruction size + + pBranch *from; // pCodes that execute before this one + pBranch *to; // pCodes that execute after + pBranch *label; // pCode instructions that have labels + + pCodeOp *pcop; /* Operand, if this instruction has one */ + pCodeFlow *pcflow; /* flow block to which this instruction belongs */ + pCodeCSource *cline; /* C Source from which this instruction was derived */ + + unsigned int num_ops; /* Number of operands (0,1,2 for mid range pics) */ + unsigned int isModReg: 1; /* If destination is W or F, then 1==F */ + unsigned int isBitInst: 1; /* e.g. BCF */ + unsigned int isBranch: 1; /* True if this is a branching instruction */ + unsigned int isSkip: 1; /* True if this is a skip instruction */ + unsigned int isLit: 1; /* True if this instruction has an literal operand */ + unsigned int isAccess: 1; /* True if this instruction has an access RAM operand */ + unsigned int isFastCall: 1; /* True if this instruction has a fast call/return mode select operand */ + unsigned int is2MemOp: 1; /* True is second operand is a memory operand VR - support for MOVFF */ + unsigned int is2LitOp: 1; /* True if instruction takes 2 literal operands VR - support for LFSR */ + + PIC_OPCODE inverted_op; /* Opcode of instruction that's the opposite of this one */ + unsigned int inCond; // Input conditions for this instruction + unsigned int outCond; // Output conditions for this instruction + +#define PCI_MAGIC 0x6e12 + unsigned int pci_magic; // sanity check for pci initialization +} pCodeInstruction; + + + +/************************************************* + pCodeAsmDir +**************************************************/ + +typedef struct pCodeAsmDir +{ + pCodeInstruction pci; + + char *directive; + char *arg; +} pCodeAsmDir; + + +/************************************************* + pCodeLabel +**************************************************/ + +typedef struct pCodeLabel +{ + pCode pc; + + char *label; + int key; + int force; /* label cannot be optimized out */ + +} pCodeLabel; + +/************************************************* + pCodeFunction +**************************************************/ + +typedef struct pCodeFunction +{ + pCode pc; + + char *modname; + char *fname; /* If NULL, then this is the end of + a function. Otherwise, it's the + start and the name is contained + here */ + + pBranch *from; // pCodes that execute before this one + pBranch *to; // pCodes that execute after + pBranch *label; // pCode instructions that have labels + + int ncalled; /* Number of times function is called */ + + int absblock; /* hack to emulate a block pCodes in absolute position + but not inside a function */ + int stackusage; /* stack positions used in function */ + +} pCodeFunction; + + +/************************************************* + pCodeWild +**************************************************/ + +typedef struct pCodeWild +{ + pCodeInstruction pci; + + int id; /* Index into the wild card array of a peepBlock + * - this wild card will get expanded into that pCode + * that is stored at this index */ + + /* Conditions on wild pcode instruction */ + int mustBeBitSkipInst:1; + int mustNotBeBitSkipInst:1; + int invertBitSkipInst:1; + + pCodeOp *operand; // Optional operand + pCodeOp *label; // Optional label + +} pCodeWild; + + +/************************************************* + pInfo + + Here are stored generic informaton +*************************************************/ +typedef struct pCodeInfo +{ + pCodeInstruction pci; + + INFO_TYPE type; /* info node type */ + + pCodeOp *oper1; /* info node arguments */ +} pCodeInfo; + + +/************************************************* + pBlock + + Here are PIC program snippets. There's a strong + correlation between the eBBlocks and pBlocks. + SDCC subdivides a C program into managable chunks. + Each chunk becomes a eBBlock and ultimately in the + PIC port a pBlock. + +**************************************************/ + +typedef struct pBlock +{ + memmap *cmemmap; /* The snippet is from this memmap */ + char dbName; /* if cmemmap is NULL, then dbName will identify the block */ + pCode *pcHead; /* A pointer to the first pCode in a link list of pCodes */ + pCode *pcTail; /* A pointer to the last pCode in a link list of pCodes */ + + struct pBlock *next; /* The pBlocks will form a doubly linked list */ + struct pBlock *prev; + + set *function_entries; /* dll of functions in this pblock */ + set *function_exits; + set *function_calls; + set *tregisters; + + set *FlowTree; + unsigned visited:1; /* set true if traversed in call tree */ + + unsigned seq; /* sequence number of this pBlock */ + +} pBlock; + +/************************************************* + pFile + + The collection of pBlock program snippets are + placed into a linked list that is implemented + in the pFile structure. + + The pcode optimizer will parse the pFile. + +**************************************************/ + +typedef struct pFile +{ + pBlock *pbHead; /* A pointer to the first pBlock */ + pBlock *pbTail; /* A pointer to the last pBlock */ + + pBranch *functions; /* A SLL of functions in this pFile */ + +} pFile; + + + +/************************************************* + pCodeWildBlock + + The pCodeWildBlock object keeps track of the wild + variables, operands, and opcodes that exist in + a pBlock. +**************************************************/ +typedef struct pCodeWildBlock { + pBlock *pb; + struct pCodePeep *pcp; // pointer back to ... I don't like this... + + int nvars; // Number of wildcard registers in target. + char **vars; // array of pointers to them + + int nops; // Number of wildcard operands in target. + pCodeOp **wildpCodeOps; // array of pointers to the pCodeOp's. + + int nwildpCodes; // Number of wildcard pCodes in target/replace + pCode **wildpCodes; // array of pointers to the pCode's. + +} pCodeWildBlock; + +/************************************************* + pCodePeep + + The pCodePeep object mimics the peep hole optimizer + in the main SDCC src (e.g. SDCCpeeph.c). Essentially + there is a target pCode chain and a replacement + pCode chain. The target chain is compared to the + pCode that is generated by gen.c. If a match is + found then the pCode is replaced by the replacement + pCode chain. +**************************************************/ +typedef struct pCodePeep { + pCodeWildBlock target; // code we'd like to optimize + pCodeWildBlock replace; // and this is what we'll optimize it with. + + //pBlock *target; + //pBlock replace; // and this is what we'll optimize it with. + + + + /* (Note: a wildcard register is a place holder. Any register + * can be replaced by the wildcard when the pcode is being + * compared to the target. */ + + /* Post Conditions. A post condition is a condition that + * must be either true or false before the peep rule is + * accepted. For example, a certain rule may be accepted + * if and only if the Z-bit is not used as an input to + * the subsequent instructions in a pCode chain. + */ + unsigned int postFalseCond; + unsigned int postTrueCond; + +} pCodePeep; + +/************************************************* + + pCode peep command definitions + + Here are some special commands that control the +way the peep hole optimizer behaves + +**************************************************/ + +enum peepCommandTypes{ + NOTBITSKIP = 0, + BITSKIP, + INVERTBITSKIP, + _LAST_PEEP_COMMAND_ +}; + +/************************************************* + peepCommand structure stores the peep commands. + +**************************************************/ + +typedef struct peepCommand { + int id; + char *cmd; +} peepCommand; + +/************************************************* + pCode Macros + +**************************************************/ +#define PCODE(x) ((pCode *)(x)) +#define PCI(x) ((pCodeInstruction *)(x)) +#define PCL(x) ((pCodeLabel *)(x)) +#define PCF(x) ((pCodeFunction *)(x)) +#define PCFL(x) ((pCodeFlow *)(x)) +#define PCFLINK(x)((pCodeFlowLink *)(x)) +#define PCW(x) ((pCodeWild *)(x)) +#define PCCS(x) ((pCodeCSource *)(x)) +#define PCAD(x) ((pCodeAsmDir *)(x)) +#define PCINF(x) ((pCodeInfo *)(x)) + +#define PCOP(x) ((pCodeOp *)(x)) +#define PCOP2(x) ((pCodeOp2 *)(x)) +//#define PCOB(x) ((pCodeOpBit *)(x)) +#define PCOL(x) ((pCodeOpLit *)(x)) +#define PCOI(x) ((pCodeOpImmd *)(x)) +#define PCOLAB(x) ((pCodeOpLabel *)(x)) +#define PCOR(x) ((pCodeOpReg *)(x)) +//#define PCOR2(x) ((pCodeOpReg2 *)(x)) +#define PCORB(x) ((pCodeOpRegBit *)(x)) +#define PCOO(x) ((pCodeOpOpt *)(x)) +#define PCOLR(x) ((pCodeOpLocalReg *)(x)) +#define PCOW(x) ((pCodeOpWild *)(x)) +#define PCOW2(x) (PCOW(PCOW(x)->pcop2)) +#define PBR(x) ((pBranch *)(x)) + +#define PCWB(x) ((pCodeWildBlock *)(x)) + + +/* + macros for checking pCode types +*/ +#define isPCI(x) ((PCODE(x)->type == PC_OPCODE)) +#define isPCI_BRANCH(x) ((PCODE(x)->type == PC_OPCODE) && PCI(x)->isBranch) +#define isPCI_SKIP(x) ((PCODE(x)->type == PC_OPCODE) && PCI(x)->isSkip) +#define isPCI_LIT(x) ((PCODE(x)->type == PC_OPCODE) && PCI(x)->isLit) +#define isPCI_BITSKIP(x)((PCODE(x)->type == PC_OPCODE) && PCI(x)->isSkip && PCI(x)->isBitInst) +#define isPCFL(x) ((PCODE(x)->type == PC_FLOW)) +#define isPCF(x) ((PCODE(x)->type == PC_FUNCTION)) +#define isPCL(x) ((PCODE(x)->type == PC_LABEL)) +#define isPCW(x) ((PCODE(x)->type == PC_WILD)) +#define isPCCS(x) ((PCODE(x)->type == PC_CSOURCE)) +#define isPCAD(x) ((PCODE(x)->type == PC_ASMDIR)) +#define isPCINFO(x) ((PCODE(x)->type == PC_INFO)) + +#define isCALL(x) ((isPCI(x)) && (PCI(x)->op == POC_CALL)) +#define isSTATUS_REG(r) ((r)->pc_type == PO_STATUS) +#define isBSR_REG(r) ((r)->pc_type == PO_BSR) +#define isACCESS_BANK(r) (r->accessBank) + + + +#define isPCOLAB(x) ((PCOP(x)->type) == PO_LABEL) + +/*-----------------------------------------------------------------* + * pCode functions. + *-----------------------------------------------------------------*/ + +pCode *pic16_newpCode (PIC_OPCODE op, pCodeOp *pcop); // Create a new pCode given an operand +pCode *pic16_newpCodeCharP(const char *cP); // Create a new pCode given a char * +pCode *pic16_newpCodeInlineP(const char *cP); // Create a new pCode given a char * +pCode *pic16_newpCodeFunction(const char *g, const char *f); // Create a new function +pCode *pic16_newpCodeLabel(const char *name,int key); // Create a new label given a key +pCode *pic16_newpCodeLabelFORCE(const char *name, int key); // Same as newpCodeLabel but label cannot be optimized out +pCode *pic16_newpCodeCSource(int ln, const char *f, const char *l); // Create a new symbol line +pBlock *pic16_newpCodeChain(memmap *cm,char c, pCode *pc); // Create a new pBlock +void pic16_printpBlock(FILE *of, pBlock *pb); // Write a pBlock to a file +void pic16_addpCode2pBlock(pBlock *pb, pCode *pc); // Add a pCode to a pBlock +void pic16_addpBlock(pBlock *pb); // Add a pBlock to a pFile +void pic16_copypCode(FILE *of, char dbName); // Write all pBlocks with dbName to *of +void pic16_movepBlock2Head(char dbName); // move pBlocks around +void pic16_AnalyzepCode(char dbName); +void pic16_OptimizeLocalRegs(void); +void pic16_AssignRegBanks(void); +void pic16_printCallTree(FILE *of); +void pCodePeepInit(void); +void pic16_pBlockConvert2ISR(pBlock *pb); +void pic16_pBlockConvert2Absolute(pBlock *pb); +void pic16_initDB(void); +void pic16_emitDB(int c, char ptype, void *p); // Add DB directives to a pBlock +void pic16_emitDS(const char *s, char ptype, void *p); +void pic16_flushDB(char ptype, void *p); // Add pending DB data to a pBlock + +pCode *pic16_newpCodeAsmDir(const char *asdir, const char *argfmt, ...); + +pCodeOp *pic16_newpCodeOpLabel(const char *name, int key); +pCodeOp *pic16_newpCodeOpImmd(const char *name, int offset, int index, int code_space); +pCodeOp *pic16_newpCodeOpLit(int lit); +pCodeOp *pic16_newpCodeOpLit12(int lit); +pCodeOp *pic16_newpCodeOpLit2(int lit, pCodeOp *arg2); +pCodeOp *pic16_newpCodeOpBit(const char *name, int bit,int inBitSpace, PIC_OPTYPE subt); +pCodeOp *pic16_newpCodeOpBit_simple (struct asmop *op, int offs, int bit); +pCodeOp *pic16_newpCodeOpRegFromStr(const char *name); +pCodeOp *pic16_newpCodeOpReg(int rIdx); +pCodeOp *pic16_newpCodeOp(const char *name, PIC_OPTYPE p); +pCodeOp *pic16_newpCodeOp2(pCodeOp *src, pCodeOp *dst); +pCodeOp *pic16_newpCodeOpRegNotVect(bitVect *bv); +pCodeOp *pic16_pCodeOpCopy(pCodeOp *pcop); + +pCode *pic16_newpCodeInfo(INFO_TYPE type, pCodeOp *pcop); +pCodeOp *pic16_newpCodeOpOpt(OPT_TYPE type, const char *key); +pCodeOp *pic16_newpCodeOpLocalRegs(LR_TYPE type); +pCodeOp *pic16_newpCodeOpReg(int rIdx); + +pCode * pic16_findNextInstruction(pCode *pci); +pCode * pic16_findNextpCode(pCode *pc, PC_TYPE pct); +int pic16_isPCinFlow(const pCode *pc, const pCode *pcflow); +struct reg_info * pic16_getRegFromInstruction(pCode *pc); +struct reg_info * pic16_getRegFromInstruction2(pCode *pc); +char *pic16_get_op(pCodeOp *pcop,char *buffer, size_t size); +char *pic16_get_op2(pCodeOp *pcop,char *buffer, size_t size); +const char *dumpPicOptype(PIC_OPTYPE type); + +extern void pic16_pcode_test(void); +extern int pic16_debug_verbose; +extern int pic16_pcode_verbose; + +extern const char *LR_TYPE_STR[]; + + +#ifndef debugf +//#define debugf(frm, rest...) _debugf(__FILE__, __LINE__, frm, rest) +#define debugf(frm, rest) _debugf(__FILE__, __LINE__, frm, rest) +#define debugf2(frm, arg1, arg2) _debugf(__FILE__, __LINE__, frm, arg1, arg2) +#define debugf3(frm, arg1, arg2, arg3) _debugf(__FILE__, __LINE__, frm, arg1, arg2, arg3) + +#endif + +extern void _debugf(char *f, int l, char *frm, ...); + + +/*-----------------------------------------------------------------* + * pCode objects. + *-----------------------------------------------------------------*/ + +extern pCodeOpReg pic16_pc_status; +extern pCodeOpReg pic16_pc_intcon; +extern pCodeOpReg pic16_pc_pcl; +extern pCodeOpReg pic16_pc_pclath; +extern pCodeOpReg pic16_pc_pclatu; +extern pCodeOpReg pic16_pc_wreg; +extern pCodeOpReg pic16_pc_tosl; +extern pCodeOpReg pic16_pc_tosh; +extern pCodeOpReg pic16_pc_tosu; +extern pCodeOpReg pic16_pc_tblptrl; +extern pCodeOpReg pic16_pc_tblptrh; +extern pCodeOpReg pic16_pc_tblptru; +extern pCodeOpReg pic16_pc_tablat; +extern pCodeOpReg pic16_pc_bsr; +extern pCodeOpReg pic16_pc_fsr0; +extern pCodeOpReg pic16_pc_fsr0l; +extern pCodeOpReg pic16_pc_fsr0h; +extern pCodeOpReg pic16_pc_fsr1l; +extern pCodeOpReg pic16_pc_fsr1h; +extern pCodeOpReg pic16_pc_fsr2l; +extern pCodeOpReg pic16_pc_fsr2h; +extern pCodeOpReg pic16_pc_indf0; +extern pCodeOpReg pic16_pc_postinc0; +extern pCodeOpReg pic16_pc_postdec0; +extern pCodeOpReg pic16_pc_preinc0; +extern pCodeOpReg pic16_pc_plusw0; +extern pCodeOpReg pic16_pc_indf1; +extern pCodeOpReg pic16_pc_postinc1; +extern pCodeOpReg pic16_pc_postdec1; +extern pCodeOpReg pic16_pc_preinc1; +extern pCodeOpReg pic16_pc_plusw1; +extern pCodeOpReg pic16_pc_indf2; +extern pCodeOpReg pic16_pc_postinc2; +extern pCodeOpReg pic16_pc_postdec2; +extern pCodeOpReg pic16_pc_preinc2; +extern pCodeOpReg pic16_pc_plusw2; +extern pCodeOpReg pic16_pc_prodl; +extern pCodeOpReg pic16_pc_prodh; + +extern pCodeOpReg pic16_pc_eecon1; +extern pCodeOpReg pic16_pc_eecon2; +extern pCodeOpReg pic16_pc_eedata; +extern pCodeOpReg pic16_pc_eeadr; + +extern pCodeOpReg pic16_pc_kzero; +extern pCodeOpReg pic16_pc_wsave; /* wsave and ssave are used to save W and the Status */ +extern pCodeOpReg pic16_pc_ssave; /* registers during an interrupt */ + +extern pCodeOpReg *pic16_stackpnt_lo; +extern pCodeOpReg *pic16_stackpnt_hi; +extern pCodeOpReg *pic16_stack_postinc; +extern pCodeOpReg *pic16_stack_postdec; +extern pCodeOpReg *pic16_stack_preinc; +extern pCodeOpReg *pic16_stack_plusw; + +extern pCodeOpReg *pic16_framepnt_lo; +extern pCodeOpReg *pic16_framepnt_hi; +extern pCodeOpReg *pic16_frame_postinc; +extern pCodeOpReg *pic16_frame_postdec; +extern pCodeOpReg *pic16_frame_preinc; +extern pCodeOpReg *pic16_frame_plusw; + +extern pCodeOpReg pic16_pc_gpsimio; +extern pCodeOpReg pic16_pc_gpsimio2; + +#endif // __PCODE_H__ diff --git a/src/pic16/pcodeflow.c b/src/pic16/pcodeflow.c new file mode 100644 index 0000000..fc8ec86 --- /dev/null +++ b/src/pic16/pcodeflow.c @@ -0,0 +1,351 @@ +/*------------------------------------------------------------------------- + + pcodeflow.c - post code generation flow analysis + + Written By - Scott Dattalo scott@dattalo.com + Ported to PIC16 By - Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ +/* + pcodeflow.c + + The purpose of the code in this file is to analyze the flow of the + pCode. + +*/ + +#include + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" +#include "ralloc.h" +#include "device.h" +#include "pcode.h" +#include "pcoderegs.h" +#include "pcodeflow.h" + +#if 0 + +/* + In the section that follows, an exhaustive flow "tree" is built. + It's not a tree that's really built, but instead every possible + flow path is constructed. + + This is really overkill... +*/ + +static set *FlowTree=NULL; + +void dbg_dumpFlowTree(set *FlowTree) +{ + set *segment; + pCodeFlow *pcflow; + + fprintf(stderr,"Flow Tree: \n"); + + for(segment = setFirstItem(FlowTree); segment; segment=setNextItem(FlowTree)) { + + fprintf(stderr,"Segment:\n"); + + for(pcflow=PCFL(setFirstItem(segment)); pcflow; pcflow=PCFL(setNextItem(segment))) { + fprintf(stderr, " 0x%03x",pcflow->pc.seq); + } + fprintf(stderr,"\n"); + } + +} + + + + +/*-----------------------------------------------------------------* + * void BuildFlowSegment(set *segment, pCodeFlow *pcflow) + *-----------------------------------------------------------------*/ +static void BuildFlowSegment(set *segment, pCodeFlow *pcflow) +{ + + int nNextFlow=0; + pCodeFlowLink *pcflowLink=NULL; + + /* Add this flow to the set if it's not in there already */ + if(isinSet(segment, pcflow)) { + addSetHead(&FlowTree, segment); + return; + } + + addSetHead(&segment, pcflow); + + /* Continue to add contiguous flows */ + + while( pcflow->to && ((nNextFlow = elementsInSet(pcflow->to)) == 1)) { + pcflowLink = (pCodeFlowLink *)(setFirstItem(pcflow->to)); + pcflow = pcflowLink->pcflow; + + if(isinSet(segment, pcflow)) { + addSetIfnotP(&FlowTree, segment); + return; + } + + addSetIfnotP(&segment, pcflow); + + } + + /* Branch: for each branch, duplicate the set and recursively call */ + if(pcflow->to && nNextFlow>=2) { + pCodeFlow *pcflow_to; + + set *branch_segment=NULL; + + pcflowLink = (pCodeFlowLink *)(setFirstItem(pcflow->to)); + pcflow_to = pcflowLink->pcflow; + + addSetIfnotP(&segment, pcflow); + + pcflowLink = (pCodeFlowLink *)(setNextItem(pcflow->to)); + + while(pcflowLink) { + + branch_segment = setFromSet(segment); + BuildFlowSegment(setFromSet(segment),pcflowLink->pcflow); + pcflowLink = (pCodeFlowLink *)(setNextItem(pcflow->to)); + } + + /* add the first branch to this segment */ + BuildFlowSegment(segment,pcflow_to); + + } + + addSetIfnotP(&FlowTree, segment); + + /* done */ +} + +/*-----------------------------------------------------------------* + * void pic16_BuildFlowTree(pBlock *pb) + *-----------------------------------------------------------------*/ +void pic16_BuildFlowTree(pBlock *pb) +{ + pCodeFlow *pcflow; + set *segment; + + FlowTree = newSet(); + + /* Start with the first flow object of this pBlock */ + + pcflow = PCFL(pic16_findNextpCode(pb->pcHead, PC_FLOW)); + + segment = newSet(); + BuildFlowSegment(segment, pcflow); + + pb->FlowTree = FlowTree; + + dbg_dumpFlowTree(FlowTree); +} + +static void dbg_dumpFlow(pBlock *pb) +{ + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) + fprintf(stderr, "LinkFlow - pcflow is not a flow object "); + + fprintf(stderr, "Flow: 0x%x",pcflow->seq); + if(PCFL(pcflow) && PCFL(pcflow)->ancestor) + fprintf(stderr,", ancestor 0x%x\n", + PCFL(pcflow)->ancestor->pc.seq); + else { + pCodeFlowLink *from = (PCFL(pcflow)->from) ? (PCFLINK(setFirstItem(PCFL(pcflow)->from))) : NULL; + fprintf(stderr," no ancestor"); + while(from) { + fprintf(stderr," (0x%x)",from->pcflow->pc.seq); + from = setNextItem(PCFL(pcflow)->from); + } + fprintf(stderr,"\n"); + } + } +} +#endif + +#if 0 +/*-----------------------------------------------------------------* + * void BuildFlowSegment(set *segment, pCodeFlow *pcflow) + *-----------------------------------------------------------------*/ +static void BuildFlowSegment(pCodeFlow *pcflow) +{ + static int recursion=0; + pCodeFlow *pcflow_other; + set *flowset; + + if(!pcflow) + return; + + if(recursion++ > 200) { + fprintf(stderr, " exceeded recursion\n"); + return; + } + + fprintf(stderr,"examining 0x%x\n",pcflow->pc.seq); + + if(pcflow->from) { + + + flowset = pcflow->from; + + if(flowset && flowset->next == NULL) { + + /* + There is a flow before this one. In fact, there's + exactly one flow before this one (because ->next is + NULL). That means all children of this node pass + through both this node and the node immediately + before this one; i.e. they have the same ancestor. + */ + + if( (NULL == (pcflow_other = PCFLINK(flowset->item)->pcflow)) || + (NULL == pcflow_other)) { + fprintf(stderr,"2 error in flow link\n"); + exit(1); + } + pcflow->ancestor = pcflow_other->ancestor ; + + fprintf(stderr,"Assigning ancestor 0x%x from flow 0x%x\n", + pcflow->ancestor->pc.seq, pcflow_other->pc.seq); + + } else { + + if(flowset == NULL) { + + /* There are no flows before this one. + * If this is not the first flow object in the pBlock then + * there's an error */ + + if(!pcflow->ancestor) { + fprintf(stderr,"error in flow link\n"); + exit(1); + + } + + } else { + + /* Flow passes to this flow from multiple flows. Let's + look at just one of these. If the one we look at has + an ancestor, then that's our ancestor too. If the one + we look at doesn't have an ancestor, then that means + we haven't traversed that branch of the call tree + yet - but we will */ + + pcflow_other = PCFLINK(flowset->item)->pcflow; + if(pcflow_other) { + fprintf(stderr, "coming from 0x%x\n",pcflow_other->pc.seq); + if( pcflow_other->ancestor) + pcflow->ancestor = pcflow_other->ancestor; + } + } + + + } + + } else { + /* there are no nodes before this one */ + if(!pcflow->ancestor) + fprintf(stderr,"3 Error in flow link\n"); + } + + /* Now let's recursively expand the call tree */ + + if(pcflow->ancestor && pcflow->to) { + flowset = pcflow->to; + while(flowset) { + BuildFlowSegment(PCFLINK(flowset->item)->pcflow); + flowset = flowset->next; + } + } + +} +#endif + +void pic16_BuildFlowTree(pBlock *pb) +{ + pCodeFlow *first_pcflow, *pcflow; + + + // fprintf(stderr,"pic16_BuildFlowTree \n"); + + first_pcflow = PCFL(pic16_findNextpCode(pb->pcHead, PC_FLOW)); + if(!first_pcflow) + return; + + /* The very first node is like Adam, it's its own ancestor (i.e. + * there are no other flows in this pBlock prior to the first one). + */ + + first_pcflow->ancestor = first_pcflow; + + /* For each flow that has only one predecessor, it's easy to + identify the ancestor */ + pcflow = PCFL(pic16_findNextpCode(first_pcflow->pc.next, PC_FLOW)); + + while(pcflow) { + if(elementsInSet(pcflow->from) == 1) { + pCodeFlowLink *from = PCFLINK(setFirstItem(pcflow->from)); + + pcflow->ancestor = from->pcflow; + /* + fprintf(stderr,"Assigning ancestor 0x%x to flow 0x%x\n", + pcflow->ancestor->pc.seq, pcflow->pc.seq); + */ + } + + pcflow = PCFL(pic16_findNextpCode(pcflow->pc.next, PC_FLOW)); + + } + + pcflow = PCFL(pic16_findNextpCode(first_pcflow->pc.next, PC_FLOW)); + + while(pcflow) { + if(elementsInSet(pcflow->from) > 1) { + pCodeFlow *min_pcflow; + pCodeFlowLink *from = PCFLINK(setFirstItem(pcflow->from)); + + min_pcflow = from->pcflow; + + while( (from = setNextItem(pcflow->from)) != NULL) { + if(from->pcflow->pc.seq < min_pcflow->pc.seq) + min_pcflow = from->pcflow; + } + + pcflow->ancestor = min_pcflow; + /* + fprintf(stderr,"Assigning ancestor 0x%x to flow 0x%x from multiple\n", + pcflow->ancestor->pc.seq, pcflow->pc.seq); + */ + + } + + pcflow = PCFL(pic16_findNextpCode(pcflow->pc.next, PC_FLOW)); + + } + + // BuildFlowSegment(pcflow); + + //dbg_dumpFlow(pb); + +} diff --git a/src/pic16/pcodeflow.h b/src/pic16/pcodeflow.h new file mode 100644 index 0000000..46cd38d --- /dev/null +++ b/src/pic16/pcodeflow.h @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- + + pcode.h - post code generation + + Written By - Scott Dattalo scott@dattalo.com + PIC16 port - Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +#ifndef __PCODEFLOW_H__ +#define __PCODEFLOW_H__ + +/************************************************* + * pCode conditions: + * + * The "conditions" are bit-mapped flags that describe + * input and/or output conditions that are affected by + * the instructions. For example: + * + * MOVF SOME_REG,W + * + * This instruction depends upon 'SOME_REG'. Consequently + * it has the input condition PCC_REGISTER set to true. + * + * In addition, this instruction affects the Z bit in the + * status register and affects W. Thus the output conditions + * are the logical or: + * PCC_ZERO_BIT | PCC_W + * + * The conditions are intialized when the pCode for an + * instruction is created. They're subsequently used + * by the pCode optimizer determine state information + * in the program flow. + *************************************************/ + +#define PCC_NONE 0 +#define PCC_REGISTER (1<<0) +#define PCC_REGISTER2 (1<<1) +#define PCC_C (1<<2) +#define PCC_Z (1<<3) +#define PCC_DC (1<<4) +#define PCC_OV (1<<5) +#define PCC_N (1<<6) +#define PCC_W (1<<7) +#define PCC_EXAMINE_PCOP (1<<8) +#define PCC_LITERAL (1<<9) +#define PCC_REL_ADDR (1<<10) + +#define PCC_STATUS (PCC_C | PCC_Z | PCC_DC | PCC_OV | PCC_N) + +/*------------------------------------------------------------*/ + +void BuildFlowAncestry(pBlock *pb); + +#endif // __PCODEFLOW_H__ diff --git a/src/pic16/pcodepeep.c b/src/pic16/pcodepeep.c new file mode 100644 index 0000000..d0eb74e --- /dev/null +++ b/src/pic16/pcodepeep.c @@ -0,0 +1,2515 @@ +/*------------------------------------------------------------------------- + + pcodepeep.c - post code generation + + Written By - Scott Dattalo scott@dattalo.com + Ported to PIC16 By - Martin Dubuc m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include +#include + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" + +//#define PCODE_DEBUG + +#include "pcode.h" +#include "pcodeflow.h" +#include "ralloc.h" + +pCodeOp *pic16_popCopyGPR2Bit(pCodeOpReg *pc, int bitval); + +pCodeOp *pic16_newpCodeOpWild(int id, pCodeWildBlock *pcwb, pCodeOp *subtype); +pCodeOp *pic16_newpCodeOpWild2(int id, int id2, pCodeWildBlock *pcwb, pCodeOp *subtype, pCodeOp *subtype2); +pCode *pic16_newpCodeWild(int pCodeID, pCodeOp *optional_operand, pCodeOp *optional_label); +pCode * pic16_findNextInstruction(pCode *pc); +int pic16_getpCode(char *mnem,int dest); +int pic16_getpCodePeepCommand(char *cmd); +void pic16_pBlockMergeLabels(pBlock *pb); +char *pic16_pCode2str(char *str, int size, pCode *pc); +//char *pic16_get_op(pCodeOp *pcop,char *buf, size_t buf_size); +pCodeOp *pic16_popCombine2(pCodeOp *, pCodeOp *, int); + +extern pCodeInstruction *pic16Mnemonics[]; +static int parsing_peeps=1; + +#define IS_PCCOMMENT(x) ( x && (x->type==PC_COMMENT)) + +/****************************************************************/ +/* + * rootRules - defined in SDCCpeep.c + * This is a pointer to the (parsed) peephole rules that are + * defined in peep.def. + */ + +//extern peepRule *rootRules; + + + + +/****************************************************************/ +/****************************************************************/ +typedef struct DLList { + struct DLList *prev; + struct DLList *next; + // void *data; +} DLList; + + +typedef struct pCodePeepSnippets +{ + DLList dll; + pCodePeep *peep; +} pCodePeepSnippets; + + +/****************************************************************/ +/* */ +/* peepSnippets - */ +/* */ +/****************************************************************/ + +static pCodePeepSnippets *peepSnippets=NULL; + +/****************************************************************/ +/* */ +/* curPeep */ +/* */ +/****************************************************************/ + +//static pCodePeep *curPeep=NULL; + +/****************************************************************/ +/* */ +/* curBlock */ +/* */ +/****************************************************************/ + +//static pBlock *curBlock=NULL; + + +/****************************************************************/ +/* */ +/* max wild cards in a peep rule */ +/* */ +/****************************************************************/ + +//static int sMaxWildVar = 0; +//static int sMaxWildMnem = 0; + + +typedef struct pCodeToken +{ + int tt; // token type; + union { + char c; // character + int n; // number + char *s; // string + } tok; + +} pCodeToken; + +pCodeToken tokArr[50]; +static unsigned tokIdx=0; + + +typedef enum { + PCT_NULL=0, + PCT_SPACE=1, + PCT_PERCENT, + PCT_LESSTHAN, + PCT_GREATERTHAN, + PCT_COLON, + PCT_COMMA, + PCT_COMMENT, + PCT_STRING, + PCT_NUMBER + +} pCodeTokens; + + +typedef struct parsedPattern { + struct pcPattern *pcp; + pCodeToken *pct; +} parsedPattern; + +#define MAX_PARSEDPATARR 50 +parsedPattern parsedPatArr[MAX_PARSEDPATARR]; +//static unsigned int parsedPatIdx=0; + + +typedef enum { + PCP_LABEL=1, + PCP_NUMBER, + PCP_STR, + PCP_WILDVAR, + PCP_WILDSTR, + PCP_COMMA, + PCP_COMMENT +} pCodePatterns; + +static char pcpat_label[] = {PCT_PERCENT, PCT_NUMBER, PCT_COLON, 0}; +static char pcpat_number[] = {PCT_NUMBER, 0}; +static char pcpat_string[] = {PCT_STRING, 0}; +static char pcpat_wildString[] = {PCT_PERCENT, PCT_STRING, 0}; +static char pcpat_wildVar[] = {PCT_PERCENT, PCT_NUMBER, 0}; +static char pcpat_comma[] = {PCT_COMMA, 0}; +static char pcpat_comment[] = {PCT_COMMENT, 0}; + + +typedef struct pcPattern { + char pt; // Pattern type + char *tokens; // list of tokens that describe the pattern + void * (*f) (void *,pCodeWildBlock *); +} pcPattern; + +static pcPattern pcpArr[] = { + {PCP_LABEL, pcpat_label, NULL}, + {PCP_WILDSTR, pcpat_wildString, NULL}, + {PCP_STR, pcpat_string, NULL}, + {PCP_WILDVAR, pcpat_wildVar, NULL}, + {PCP_COMMA, pcpat_comma, NULL}, + {PCP_COMMENT, pcpat_comment, NULL}, + {PCP_NUMBER, pcpat_number, NULL} +}; + +#define PCPATTERNS (sizeof(pcpArr)/sizeof(pcPattern)) + +// Assembly Line Token +typedef enum { + ALT_LABEL=1, + ALT_COMMENT, + ALT_MNEM0, + ALT_MNEM0A, + ALT_MNEM1, + ALT_MNEM1A, + ALT_MNEM1B, + ALT_MNEM2, + ALT_MNEM2A, + ALT_MNEM2B, + ALT_MNEM3, + ALT_MNEM4, + ALT_MNEM4a +} altPatterns; + +static char alt_comment[] = { PCP_COMMENT, 0}; +static char alt_label[] = { PCP_LABEL, 0}; +static char alt_mnem0[] = { PCP_STR, 0}; +static char alt_mnem0a[] = { PCP_WILDVAR, 0}; +static char alt_mnem1[] = { PCP_STR, PCP_STR, 0}; +static char alt_mnem1a[] = { PCP_STR, PCP_WILDVAR, 0}; +static char alt_mnem1b[] = { PCP_STR, PCP_NUMBER, 0}; +static char alt_mnem2[] = { PCP_STR, PCP_STR, PCP_COMMA, PCP_STR, 0}; +static char alt_mnem2a[] = { PCP_STR, PCP_WILDVAR, PCP_COMMA, PCP_STR, 0}; +static char alt_mnem2b[] = { PCP_STR, PCP_WILDVAR, PCP_COMMA, PCP_WILDVAR, 0}; +static char alt_mnem3[] = { PCP_STR, PCP_STR, PCP_COMMA, PCP_NUMBER, 0}; +static char alt_mnem4[] = { PCP_STR, PCP_NUMBER, PCP_COMMA, PCP_STR, 0}; // for lfsr 0 , name +static char alt_mnem4a[] = { PCP_STR, PCP_NUMBER, PCP_COMMA, PCP_NUMBER, 0}; // for lfsr 0 , value + +static void * cvt_altpat_label(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_comment(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem0(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem0a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem1b(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem2(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem2a(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem2b(void *pp, pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem3(void *pp,pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem4(void *pp, pCodeWildBlock *pcwb); +static void * cvt_altpat_mnem4a(void *pp, pCodeWildBlock *pcwb); + +/* NOTE: Order is important in the following table */ +static pcPattern altArr[] = { + {ALT_LABEL, alt_label, cvt_altpat_label}, + {ALT_COMMENT, alt_comment,cvt_altpat_comment}, + {ALT_MNEM4a, alt_mnem4a, cvt_altpat_mnem4a}, + {ALT_MNEM4, alt_mnem4, cvt_altpat_mnem4}, + {ALT_MNEM3, alt_mnem3, cvt_altpat_mnem3}, + {ALT_MNEM2B, alt_mnem2b, cvt_altpat_mnem2b}, + {ALT_MNEM2A, alt_mnem2a, cvt_altpat_mnem2a}, + {ALT_MNEM2, alt_mnem2, cvt_altpat_mnem2}, + {ALT_MNEM1B, alt_mnem1b, cvt_altpat_mnem1b}, + {ALT_MNEM1A, alt_mnem1a, cvt_altpat_mnem1a}, + {ALT_MNEM1, alt_mnem1, cvt_altpat_mnem1}, + {ALT_MNEM0A, alt_mnem0a, cvt_altpat_mnem0a}, + {ALT_MNEM0, alt_mnem0, cvt_altpat_mnem0}, + +}; + +#define ALTPATTERNS (sizeof(altArr)/sizeof(pcPattern)) + +// forward declarations +static void * DLL_append(DLList *list, DLList *next); + +/*-----------------------------------------------------------------*/ +/* cvt_extract_destination - helper function extracts the register */ +/* destination from a parsedPattern. */ +/* */ +/*-----------------------------------------------------------------*/ +static int cvt_extract_destination(parsedPattern *pp) +{ + + if(pp->pct[0].tt == PCT_STRING) { + + // just check first letter for now + + if(toupper((unsigned char)*pp->pct[0].tok.s) == 'F') + return 1; + + } else if (pp->pct[0].tt == PCT_NUMBER) { + + if(pp->pct[0].tok.n) + return 1; + } + + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* pCodeOp *cvt_extract_status(char *reg, char *bit) */ +/* if *reg is the "status" register and *bit is one of the */ +/* status bits, then this function will create a new pCode op */ +/* containing the status register. */ +/*-----------------------------------------------------------------*/ + +static pCodeOp *cvt_extract_status(const char *reg, char *bit) +{ + int len; + + if(STRCASECMP(reg, pic16_pc_status.pcop.name)) + return NULL; + + len = strlen(bit); + + if(len == 1) { + // check C,Z + if(toupper((unsigned char)*bit) == 'C') + return PCOP(pic16_popCopyGPR2Bit(&pic16_pc_status,PIC_C_BIT)); + if(toupper((unsigned char)*bit) == 'Z') + return PCOP(pic16_popCopyGPR2Bit(&pic16_pc_status,PIC_Z_BIT)); + } + + // Check DC + if(len ==2 && toupper((unsigned char)bit[0]) == 'D' && toupper((unsigned char)bit[1]) == 'C') + return PCOP(pic16_popCopyGPR2Bit(&pic16_pc_status,PIC_DC_BIT)); + + return NULL; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_label - convert assembly line type to a pCode label */ +/* INPUT: pointer to the parsedPattern */ +/* */ +/* pp[0] - label */ +/* */ +/* label pattern => '%' number ':' */ +/* at this point, we wish to extract only the 'number' */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_label(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_label with ID = %d\n",p->pct[1].tok.n)); + return pic16_newpCodeLabel(NULL,-p->pct[1].tok.n); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_comment - convert assembly line type to a comment */ +/* INPUT: pointer to the parsedPattern */ +/* */ +/* pp[0] - comment */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_comment(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_comment = %s\n",p->pct[0].tok.s)); + return pic16_newpCodeCharP(p->pct[0].tok.s); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem0 - convert assembly line type to a wild pCode */ +/* instruction */ +/* */ +/* pp[0] - str */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem0(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + + DFPRINTF((stderr,"altpat_mnem0 %s\n", p->pct[0].tok.s)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + + if(opcode < 0) { + /* look for special command strings like _NOTBITSKIP_ */ + + //fprintf(stderr, "Bad mnemonic\n"); + + opcode = pic16_getpCodePeepCommand(p->pct[0].tok.s); + //if(opcode > 0) + // fprintf(stderr," but valid peep command: %s, key = %d\n",p->pct[0].tok.s,opcode); + return NULL; + } + + pci = PCI(pic16_newpCode(opcode, NULL)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem0a - convert assembly line type to a wild pCode */ +/* instruction */ +/* */ +/* pp[0] - wild var */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem0a(void *pp, pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + + DFPRINTF((stderr,"altpat_mnem0a wild mnem # %d\n", p[0].pct[1].tok.n)); + + /* Save the index of the maximum wildcard mnemonic */ + + //if(p[0].pct[1].tok.n > sMaxWildVar) + // sMaxWildMnem = p[0].pct[1].tok.n; + + if(p[0].pct[1].tok.n > pcwb->nwildpCodes) + pcwb->nwildpCodes = p[0].pct[1].tok.n; + + return pic16_newpCodeWild(p[0].pct[1].tok.n,NULL,NULL); + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem1 - convert assembly line type to a pCode */ +/* instruction with 1 operand. */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - Operand */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1(void *pp,pCodeWildBlock *pcwb) +{ + + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + DFPRINTF((stderr,"altpat_mnem1 %s var %s\n", p->pct[0].tok.s,p[1].pct[0].tok.s)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + //fprintf(stderr, "Bad mnemonic\n"); + opcode = pic16_getpCodePeepCommand(p->pct[0].tok.s); + //if(opcode > 0) + //fprintf(stderr," but valid peep command: %s, key = %d\n",p->pct[0].tok.s,opcode); + + return NULL; + } + + if(pic16Mnemonics[opcode]->isBitInst) + pcosubtype = pic16_newpCodeOp(p[1].pct[0].tok.s,PO_BIT); + else { +// fprintf(stderr, "%s:%d tok.s= %s\n", __FILE__, __LINE__, p[1].pct[0].tok.s); + pcosubtype = pic16_newpCodeOp(p[1].pct[0].tok.s,PO_STR); //GPR_REGISTER); + } + + + pci = PCI(pic16_newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem1a - convert assembly line type to a pCode */ +/* instruction with 1 wild operand. */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - wild var */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1a(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + DFPRINTF((stderr,"altpat_mnem1a %s var %d\n", p->pct[0].tok.s,p[1].pct[1].tok.n)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + int cmd_id = pic16_getpCodePeepCommand(p->pct[0].tok.s); + pCode *pc=NULL; + + if(cmd_id<0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(p[0].pct[1].tok.n > pcwb->nwildpCodes) + pcwb->nwildpCodes = p[0].pct[1].tok.n; + + pc = pic16_newpCodeWild(p[1].pct[1].tok.n,NULL,NULL); + + switch(cmd_id) { + case NOTBITSKIP: + PCW(pc)->mustNotBeBitSkipInst = TRUE; + break; + case BITSKIP: + PCW(pc)->mustBeBitSkipInst = TRUE; + break; + case INVERTBITSKIP: + PCW(pc)->invertBitSkipInst = TRUE; + } + return pc; + } + + if(pic16Mnemonics[opcode]->isBitInst) + pcosubtype = pic16_newpCodeOpBit(NULL,-1,0, PO_GPR_REGISTER); + else + pcosubtype = pic16_newpCodeOp(NULL,PO_GPR_REGISTER); + + + pci = PCI(pic16_newpCode(opcode, + pic16_newpCodeOpWild(p[1].pct[1].tok.n, pcwb, pcosubtype))); + + /* Save the index of the maximum wildcard variable */ + //if(p[1].pct[1].tok.n > sMaxWildVar) + // sMaxWildVar = p[1].pct[1].tok.n; + + if(p[1].pct[1].tok.n > pcwb->nvars) + pcwb->nvars = p[1].pct[1].tok.n; + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem1b(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + + DFPRINTF((stderr,"altpat_mnem1b %s var %d\n", p->pct[0].tok.s,p[1].pct[0].tok.n)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + pci = PCI(pic16_newpCode(opcode, pic16_newpCodeOpLit(p[1].pct[0].tok.n) )); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + + return pci; +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mnem2 */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - var */ +/* pp[2] - comma */ +/* pp[3] - destination */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem2(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + int dest; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype; + + dest = cvt_extract_destination(&p[3]); + + DFPRINTF((stderr,"altpat_mnem2 %s var %s destination %s(%d)\n", + p->pct[0].tok.s, + p[1].pct[0].tok.s, + p[3].pct[0].tok.s, + dest)); + + opcode = pic16_getpCode(p->pct[0].tok.s,dest); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(pic16Mnemonics[opcode]->isBitInst) { + pcosubtype = cvt_extract_status(p[1].pct[0].tok.s, p[3].pct[0].tok.s); + if(pcosubtype == NULL) { + fprintf(stderr, "bad operand?\n"); + return NULL; + } + + } else + if(pic16Mnemonics[opcode]->is2MemOp) { + /* support for movff instruction */ + pcosubtype = pic16_popCombine2( + pic16_newpCodeOp(p[1].pct[0].tok.s, PO_GPR_REGISTER), + pic16_newpCodeOp(p[3].pct[0].tok.s, PO_GPR_REGISTER), 0); + } else + pcosubtype = pic16_newpCodeOp(p[1].pct[0].tok.s,PO_GPR_REGISTER); + + + + pci = PCI(pic16_newpCode(opcode,pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem2a - convert assembly line type to a pCode */ +/* instruction with 1 wild operand and a */ +/* destination operand (e.g. w or f) */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - wild var */ +/* pp[2] - comma */ +/* pp[3] - destination */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem2a(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + int dest; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype, *pcosubtype2; + + if(!pcwb) { + fprintf(stderr,"ERROR %s:%d - can't assemble line\n",__FILE__,__LINE__); + return NULL; + } + + dest = cvt_extract_destination(&p[3]); + + DFPRINTF((stderr,"altpat_mnem2a %s var %d destination %s(%d)\n", + p->pct[0].tok.s, + p[1].pct[1].tok.n, + p[3].pct[0].tok.s, + dest)); + +#if 0 + fprintf(stderr,"altpat_mnem2a %s var %d destination %s(%d)\n", + p->pct[0].tok.s, + p[1].pct[1].tok.n, + p[3].pct[0].tok.s, + dest); +#endif + + + opcode = pic16_getpCode(p->pct[0].tok.s,dest); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(pic16Mnemonics[opcode]->isBitInst) { + pcosubtype = pic16_newpCodeOp(NULL,PO_BIT); + pcosubtype2 = NULL; + } else { +#if 0 + if(pic16Mnemonics[opcode]->is2MemOp) { + /* support for movff instruction */ + pcosubtype = pic16_newpCodeOp(NULL, PO_GPR_REGISTER); + pcosubtype2 = pic16_newpCodeOp(p[3].pct[0].tok.s, PO_STR); + } else { +#endif + pcosubtype = pic16_newpCodeOp(NULL,PO_GPR_REGISTER); pcosubtype2 = NULL; } + + + if(!pcosubtype2) + pci = PCI(pic16_newpCode(opcode, + pic16_newpCodeOpWild(p[1].pct[1].tok.n, pcwb, pcosubtype))); + else + pci = PCI(pic16_newpCode(opcode, + pic16_newpCodeOpWild2(p[1].pct[1].tok.n, p[3].pct[1].tok.n, pcwb, pcosubtype, pcosubtype2))); + + /* Save the index of the maximum wildcard variable */ + //if(p[1].pct[1].tok.n > sMaxWildVar) + // sMaxWildVar = p[1].pct[1].tok.n; + + if(p[1].pct[1].tok.n > pcwb->nvars) + pcwb->nvars = p[1].pct[1].tok.n; + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem2b - convert assembly line type to a pCode */ +/* instruction with 2 wild operands */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - wild var */ +/* pp[2] - comma */ +/* pp[3] - wild var */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem2b(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + int dest; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype, *pcosubtype2; + + if(!pcwb) { + fprintf(stderr,"ERROR %s:%d - can't assemble line\n",__FILE__,__LINE__); + return NULL; + } + + dest = cvt_extract_destination(&p[3]); + + DFPRINTF((stderr,"altpat_mnem2b %s src %d dst (%d)\n", + p->pct[0].tok.s, + p[1].pct[1].tok.n, + p[3].pct[1].tok.n)); + +#if 0 + fprintf(stderr,"altpat_mnem2b %s src: %d dst: %d\n", + p->pct[0].tok.s, + p[1].pct[1].tok.n, + p[3].pct[1].tok.n); +#endif + + opcode = pic16_getpCode(p->pct[0].tok.s,dest); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + if(pic16Mnemonics[opcode]->is2MemOp) { + /* support for movff instruction */ + pcosubtype = pic16_newpCodeOp(NULL, PO_GPR_REGISTER); + pcosubtype2 = pic16_newpCodeOp(NULL, PO_GPR_REGISTER); + } else pcosubtype = pcosubtype2 = NULL; + + pci = PCI(pic16_newpCode(opcode, + pic16_newpCodeOpWild2(p[1].pct[1].tok.n, p[3].pct[1].tok.n, + pcwb, pcosubtype, pcosubtype2))); + + /* Save the index of the maximum wildcard variable */ + //if(p[1].pct[1].tok.n > sMaxWildVar) + // sMaxWildVar = p[1].pct[1].tok.n; + + if(p[1].pct[1].tok.n > pcwb->nvars) + pcwb->nvars = p[1].pct[1].tok.n; + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem3 - convert assembly line type to a pCode */ +/* This rule is for bsf/bcf type instructions */ +/* */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - register */ +/* pp[2] - comma */ +/* pp[3] - number */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem3(void *pp,pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype=NULL; + + DFPRINTF((stderr,"altpat_mnem3 %s var %s bit (%d)\n", + p->pct[0].tok.s, + p[1].pct[0].tok.s, + p[3].pct[0].tok.n)); + + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + + + if(pic16Mnemonics[opcode]->isBitInst) { + //pcosubtype = cvt_extract_status(p[1].pct[0].tok.s, p[3].pct[0].tok.s); + + //if(pcosubtype == NULL) { + pcosubtype = pic16_newpCodeOpBit(p[1].pct[0].tok.s,p[3].pct[0].tok.n,0, PO_GPR_REGISTER); + //} + } else + pcosubtype = pic16_newpCodeOp(p[1].pct[0].tok.s,PO_GPR_REGISTER); + + if(pcosubtype == NULL) { + fprintf(stderr, "Bad operand\n"); + return NULL; + } + + pci = PCI(pic16_newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem4 - convert assembly line type to a pCode */ +/* This rule is for lfsr instruction */ +/* */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - number */ +/* pp[2] - comma */ +/* pp[3] - source */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem4(void *pp, pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype=NULL; + + DFPRINTF((stderr,"altpat_mnem4 %s fsr %d source %s\n", + p->pct[0].tok.s, + p[1].pct[0].tok.n, + p[3].pct[0].tok.s)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + DFPRINTF((stderr, "Found mnemonic opcode= %d\n", opcode)); + + if(pic16Mnemonics[opcode]->is2LitOp) { + pcosubtype = pic16_newpCodeOpLit2(p[1].pct[0].tok.n, pic16_newpCodeOp(p[3].pct[0].tok.s, PO_STR)); + } + + if(pcosubtype == NULL) { + fprintf(stderr, "Bad operand\n"); + return NULL; + } + + pci = PCI(pic16_newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* cvt_altpat_mem4a - convert assembly line type to a pCode */ +/* This rule is for lfsr instruction */ +/* */ +/* */ +/* pp[0] - mnem */ +/* pp[1] - number */ +/* pp[2] - comma */ +/* pp[3] - value */ +/* */ +/*-----------------------------------------------------------------*/ +static void * cvt_altpat_mnem4a(void *pp, pCodeWildBlock *pcwb) +{ + parsedPattern *p = pp; + int opcode; + + pCodeInstruction *pci=NULL; + pCodeOp *pcosubtype=NULL; + + DFPRINTF((stderr,"altpat_mnem4a %s fsr %d value 0x%02x\n", + p->pct[0].tok.s, + p[1].pct[0].tok.n, + p[3].pct[0].tok.n)); + + opcode = pic16_getpCode(p->pct[0].tok.s,0); + if(opcode < 0) { + fprintf(stderr, "Bad mnemonic\n"); + return NULL; + } + DFPRINTF((stderr, "Found mnemonic opcode= %d\n", opcode)); + + if(pic16Mnemonics[opcode]->is2LitOp) { + pcosubtype = pic16_newpCodeOpLit2(p[1].pct[0].tok.n, pic16_newpCodeOpLit(p[3].pct[0].tok.n)); + } + + if(pcosubtype == NULL) { + fprintf(stderr, "Bad operand\n"); + return NULL; + } + + pci = PCI(pic16_newpCode(opcode, pcosubtype)); + + if(!pci) + fprintf(stderr,"couldn't find mnemonic\n"); + + return pci; + +} + +/*-----------------------------------------------------------------*/ +/* tokenizeLineNode - Convert a string (of char's) that was parsed */ +/* by SDCCpeeph.c into a string of tokens. */ +/* */ +/* */ +/* The tokenizer is of the classic type. When an item is encounterd*/ +/* it is converted into a token. The token is a structure that */ +/* encodes the item's type and it's value (when appropriate). */ +/* */ +/* Accepted token types: */ +/* SPACE NUMBER STRING % : , ; */ +/* */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ + + +static void tokenizeLineNode(char *ln) +{ + char *lnstart=ln; + tokIdx = 0; // Starting off at the beginning + tokArr[0].tt = PCT_NULL; // and assume invalid character for first token. + + if(!ln || !*ln) + return; + +// fprintf(stderr, "%s:%d: processing %s\n", __FILE__, __LINE__, ln); + + while(*ln) { + if(isspace((unsigned char)*ln)) { + // add a SPACE token and eat the extra spaces. + tokArr[tokIdx++].tt = PCT_SPACE; + while (isspace ((unsigned char)*ln)) + ln++; + continue; + } + + if(isdigit((unsigned char)*ln)) { + + tokArr[tokIdx].tt = PCT_NUMBER; + tokArr[tokIdx++].tok.n = strtol(ln, &ln, 0); + + continue; + + } + + switch(*ln) { + case '%': + tokArr[tokIdx++].tt = PCT_PERCENT; + break; + case '<': + tokArr[tokIdx++].tt = PCT_LESSTHAN; + break; + case '>': + tokArr[tokIdx++].tt = PCT_GREATERTHAN; + break; + case ':': + tokArr[tokIdx++].tt = PCT_COLON; + break; + case ';': + tokArr[tokIdx].tok.s = Safe_strdup(ln); + tokArr[tokIdx++].tt = PCT_COMMENT; + tokArr[tokIdx].tt = PCT_NULL; + return; + case ',': + tokArr[tokIdx++].tt = PCT_COMMA; + break; + + + default: // hack to allow : goto $ + if(isalpha((unsigned char)*ln) || (*ln == '_') || (!parsing_peeps && (*ln == '$'))) { + char buffer[50]; + int i=0; + + while( (isalpha((unsigned char)*ln) || isdigit((unsigned char)*ln) || (*ln == '_') || (*ln == '$')) && i<49) + buffer[i++] = *ln++; + + ln--; + buffer[i] = 0; + + tokArr[tokIdx].tok.s = Safe_strdup(buffer); + tokArr[tokIdx++].tt = PCT_STRING; + + } else { + if(parsing_peeps) { + fprintf(stderr, "Error while parsing peep rules (check peeph.def)\n"); + fprintf(stderr, "Line: %s\n",lnstart); + fprintf(stderr, "Token: '%c'\n",*ln); + exit(1); + } + } + } + + /* Advance to next character in input string . + * Note, if none of the tests passed above, then + * we effectively ignore the `bad' character. + * Since the line has already been parsed by SDCCpeeph, + * chance are that there are no invalid characters... */ + + ln++; + + } + + tokArr[tokIdx].tt = 0; +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + + + +static void dump1Token(pCodeTokens tt) +{ + + switch(tt) { + case PCT_SPACE: + fprintf(stderr, " space "); + break; + case PCT_PERCENT: + fprintf(stderr, " pct %%"); + break; + case PCT_LESSTHAN: + fprintf(stderr, " pct <"); + break; + case PCT_GREATERTHAN: + fprintf(stderr, " pct >"); + break; + case PCT_COLON: + fprintf(stderr, " col :"); + break; + case PCT_COMMA: + fprintf(stderr, " comma , "); + break; + case PCT_COMMENT: + fprintf(stderr, " comment "); + //fprintf(stderr,"%s",tokArr[i].tok.s); + break; + case PCT_STRING: + fprintf(stderr, " str "); + //fprintf(stderr,"%s",tokArr[i].tok.s); + break; + case PCT_NUMBER: + fprintf(stderr, " num "); + //fprintf(stderr,"%d",tokArr[i].tok.n); + break; + case PCT_NULL: + fprintf(stderr, " null "); + + } + +} + + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int pcComparePattern(pCodeToken *pct, const char *pat, int max_tokens) +{ + int i=0; + + if(!pct || !pat || !*pat) + return 0; + + //DFPRINTF((stderr,"comparing against:\n")); + + while(i < max_tokens) { + + if(*pat == 0){ + //DFPRINTF((stderr,"matched\n")); + return (i+1); + } + +// dump1Token(*pat); fputc('\n', stderr); DFPRINTF((stderr,"\n")); + + if(pct->tt != *pat) + return 0; + + + pct++; + pat++; + } + + return 0; + +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int altComparePattern(const char *pct, parsedPattern *pat, int max_tokens) +{ + int i=0; + + if(!pct || !pat || !*pct) + return 0; + + + while(i < max_tokens) { + + if(*pct == 0) { + DFPRINTF((stderr,"matched\n")); + return i; + } + +// dump1Token(*pat); DFPRINTF((stderr,"\n")); + + if( !pat || !pat->pcp ) + return 0; + + if (pat->pcp->pt != *pct) + return 0; + + DFPRINTF((stderr," pct=%d\n",*pct)); + pct++; + pat++; + i++; + } + + return 0; + +} +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ + +static int advTokIdx(int *v, int amt) +{ + + if((unsigned) (*v + amt) > tokIdx) + return 1; + + *v += amt; + return 0; + +} + +/*-----------------------------------------------------------------*/ +/* parseTokens - convert the tokens corresponding to a single line */ +/* of a peep hole assembly into a pCode object. */ +/* */ +/* */ +/* */ +/* */ +/* This is a simple parser that looks for strings of the type */ +/* allowed in the peep hole definition file. Essentially the format*/ +/* is the same as a line of assembly: */ +/* */ +/* label: mnemonic op1, op2, op3 ; comment */ +/* */ +/* Some of these items aren't present. It's the job of the parser */ +/* to determine which are and convert those into the appropriate */ +/* pcode. */ +/*-----------------------------------------------------------------*/ + +static int parseTokens(pCodeWildBlock *pcwb, pCode **pcret) +{ + pCode *pc; + int error = 0; + + if(!tokIdx) + return error; + +#ifdef PCODE_DEBUG + { + unsigned i; + for(i=0; i<=tokIdx; i++) + dump1Token(tokArr[i].tt); + fputc('\n',stderr); + } +#endif + + { + int lparsedPatIdx = 0; + int lpcpIdx; + int ltokIdx = 0; + int matching = 0; + int j = 0; + int k = 0; + + typedef enum { + PS_START, + PS_HAVE_LABEL, + PS_HAVE_MNEM, + PS_HAVE_1OPERAND, + PS_HAVE_COMMA, + PS_HAVE_2OPERANDS + } ParseStates; + + ParseStates state = PS_START; + + do { + + lpcpIdx=0; + matching = 0; + + if( ((tokArr[ltokIdx].tt == PCT_SPACE) ) + && (advTokIdx(<okIdx, 1)) ) // eat space + break; + + do { + j = pcComparePattern(&tokArr[ltokIdx], pcpArr[lpcpIdx].tokens, tokIdx +1); + if( j ) { + + switch(pcpArr[lpcpIdx].pt) { + case PCP_LABEL: + if(state == PS_START){ + DFPRINTF((stderr," label\n")); + state = PS_HAVE_LABEL; + } else + DFPRINTF((stderr," bad state (%d) for label\n",state)); + break; + + case PCP_STR: + DFPRINTF((stderr," %s is",tokArr[ltokIdx].tok.s)); + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + DFPRINTF((stderr," mnem\n")); + state = PS_HAVE_MNEM; + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand\n")); + //pco1 = pic16_newpCodeOp(NULL,PO_GPR_REGISTER); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + DFPRINTF((stderr," error expecting comma\n")); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2 operands\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_WILDVAR: + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + DFPRINTF((stderr," wild mnem\n")); + state = PS_HAVE_MNEM; + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand is wild\n")); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + DFPRINTF((stderr," error expecting comma\n")); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2nd operand is wild\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_NUMBER: + switch(state) { + case PS_START: + case PS_HAVE_LABEL: + fprintf(stderr," ERROR number\n"); + break; + case PS_HAVE_MNEM: + DFPRINTF((stderr," 1st operand is a number\n")); + state = PS_HAVE_1OPERAND; + break; + case PS_HAVE_1OPERAND: + fprintf(stderr," error expecting comma\n"); + break; + case PS_HAVE_COMMA: + DFPRINTF((stderr," 2nd operand is a number\n")); + break; + case PS_HAVE_2OPERANDS: + break; + } + break; + + case PCP_WILDSTR: + break; + case PCP_COMMA: + if(state == PS_HAVE_1OPERAND){ + DFPRINTF((stderr," got a comma\n")); + state = PS_HAVE_COMMA; + } else + fprintf(stderr," unexpected comma\n"); + break; + + } + + matching = 1; + parsedPatArr[lparsedPatIdx].pcp = &pcpArr[lpcpIdx]; + parsedPatArr[lparsedPatIdx].pct = &tokArr[ltokIdx]; + lparsedPatIdx++; + +// dump1Token(tokArr[ltokIdx].tt); + + if(advTokIdx(<okIdx, strlen(pcpArr[lpcpIdx].tokens) ) ) { + DFPRINTF((stderr," reached end \n")); + matching = 0; + //return; + } + } + + + } while ((++lpcpIdx < PCPATTERNS) && !matching); + + } while (matching); + + parsedPatArr[lparsedPatIdx].pcp = NULL; + parsedPatArr[lparsedPatIdx].pct = NULL; + + j=k=0; + do { + int c; + + if( (c=altComparePattern( altArr[k].tokens, &parsedPatArr[j],10) ) ) { + + if( altArr[k].f) { + pc = altArr[k].f(&parsedPatArr[j],pcwb); + //if(pc && pc->print) + // pc->print(stderr,pc); + //if(pc && pc->destruct) pc->destruct(pc); dumps core? + + //if(curBlock && pc) + //pic16_addpCode2pBlock(curBlock, pc); + if(pc) { + if (pcret) { + *pcret = pc; + return 0; // Only accept one line for now. + } else + pic16_addpCode2pBlock(pcwb->pb, pc); + } else + error++; + } + j += c; + } + k++; + } + while(j<=lparsedPatIdx && kf ) + parsedPatArr[j].pcp->f(&parsedPatArr[j]); + DFPRINTF((stderr," %d",parsedPatArr[j].pcp->pt)); + j++; + } + while(jnext) { + + //DFPRINTF((stderr,"%s\n",ln->line)); +// fprintf(stderr, "peep rule : %s\n", ln->line); + + tokenizeLineNode(ln->line); + + if(parseTokens(pcwb,NULL)) { + int i; + fprintf(stderr,"ERROR assembling line:\n%s\n",ln->line); + fprintf(stderr,"Tokens:\n"); + for(i=0; i<8; i++) + dump1Token(tokArr[i].tt); + fputc('\n',stderr); + exit (1); + } + } +} + +/*-----------------------------------------------------------------*/ +/* pic16_AssembleLine - parse line and return the pCode equivalent */ +/* peeps=1 if parsing peep rules, 0 otherwise */ +/*-----------------------------------------------------------------*/ +pCode *pic16_AssembleLine(char *line, int peeps) +{ + pCode *pc=NULL; + + if(!line || !*line) { + fprintf(stderr,"WARNING returning NULL in AssembleLine\n"); + return NULL; + } + + parsing_peeps = peeps; + + tokenizeLineNode(line); + + if(parseTokens(NULL,&pc)) + fprintf(stderr, "WARNING: unable to assemble line:\n%s\n",line); + else { + DFPRINTF((stderr, "pc= %p\n", pc)); +// if(pc)pc->print(stderr, pc); + } + + parsing_peeps = 1; + return pc; + +} + +/*-----------------------------------------------------------------*/ +/* peepRuleCondition */ +/*-----------------------------------------------------------------*/ +static void peepRuleCondition(const char *cond, pCodePeep *pcp) +{ + if(!cond || !pcp) + return; + + //DFPRINTF((stderr,"\nCondition: %s\n",cond)); + /* brute force compares for now */ + + if(STRCASECMP(cond, "NZ") == 0) { + //DFPRINTF((stderr,"found NZ\n")); + pcp->postFalseCond = PCC_Z; + + } + +} + + +static void initpCodeWildBlock(pCodeWildBlock *pcwb) +{ + + // pcwb = Safe_alloc(sizeof(pCodeWildBlock)); + + if(!pcwb) + return; + + pcwb->vars = NULL; + pcwb->wildpCodes = NULL; + pcwb->wildpCodeOps = NULL; + + pcwb->nvars = 0; + pcwb->nwildpCodes = 0; + pcwb->nops = 0; + +} + +static void postinit_pCodeWildBlock(pCodeWildBlock *pcwb) +{ + + if(!pcwb) + return; + + pcwb->nvars+=2; + pcwb->nops = pcwb->nvars; + + pcwb->vars = Safe_calloc(pcwb->nvars, sizeof(char *)); + pcwb->wildpCodeOps = Safe_calloc(pcwb->nvars, sizeof(pCodeOp *)); + + pcwb->nwildpCodes+=2; + pcwb->wildpCodes = Safe_calloc(pcwb->nwildpCodes, sizeof(pCode *)); + +} + +static void initpCodePeep(pCodePeep *pcp) +{ + + // pcwb = Safe_alloc(sizeof(pCodeWildBlock)); + + if(!pcp) + return; + + initpCodeWildBlock(&pcp->target); + pcp->target.pb = pic16_newpCodeChain(NULL, 'W', NULL); + + initpCodeWildBlock(&pcp->replace); + pcp->replace.pb = pic16_newpCodeChain(NULL, 'W', NULL); + +} + +/*-----------------------------------------------------------------*/ +/* peepRules2pCode - parse the "parsed" peep hole rules to generate*/ +/* pCode. */ +/* */ +/* SDCCpeeph parses the peep rules file and extracts variables, */ +/* removes white space, and checks the syntax. This function */ +/* extends that processing to produce pCode objects. You can kind */ +/* think of this function as an "assembler", though instead of */ +/* taking raw text to produce machine code, it produces pCode. */ +/* */ +/*-----------------------------------------------------------------*/ +extern void pic16initpCodePeepCommands(void); + +void pic16_peepRules2pCode(peepRule *rules) +{ + peepRule *pr; + + pCodePeep *currentRule; + pCodePeepSnippets *pcps; + + pic16initpCodePeepCommands(); + + /* The rules are in a linked-list. Each rule has two portions */ + /* There's the `target' and there's the `replace'. The target */ + /* is compared against the SDCC generated code and if it */ + /* matches, it gets replaced by the `replace' block of code. */ + /* */ + /* Here we loop through each rule and convert the target's and*/ + /* replace's into pCode target and replace blocks */ + + for (pr = rules; pr; pr = pr->next) { + + //DFPRINTF((stderr,"\nRule:\n\n")); + + pcps = Safe_alloc(sizeof(pCodePeepSnippets)); + peepSnippets = DLL_append((DLList*)peepSnippets,(DLList*)pcps); + + currentRule = pcps->peep = Safe_alloc(sizeof(pCodePeep)); + initpCodePeep(currentRule); + + /* Convert the target block */ + peepRuleBlock2pCodeBlock(pr->match, ¤tRule->target); + + //DFPRINTF((stderr,"finished target, here it is in pcode form:\n")); +// pic16_printpBlock(stderr, currentRule->target.pb); + + //DFPRINTF((stderr,"target with labels merged:\n")); + //pic16_pBlockMergeLabels(curBlock); + pic16_pBlockMergeLabels(currentRule->target.pb); + //pic16_printpBlock(stderr, currentRule->replace.pb); + + //#ifdef PCODE_DEBUG + // pic16_printpBlock(stderr, curBlock); + //#endif + //DFPRINTF((stderr,"\nReplaced by:\n")); + + + /* Convert the replace block */ + peepRuleBlock2pCodeBlock(pr->replace, ¤tRule->replace); + + //DFPRINTF((stderr,"finished replace block, here it is in pcode form:\n")); + //pic16_printpBlock(stderr, curBlock); + + //DFPRINTF((stderr,"replace with labels merged:\n")); + + pic16_pBlockMergeLabels(currentRule->replace.pb); + //pic16_printpBlock(stderr, currentRule->replace.pb); + + peepRuleCondition(pr->cond,currentRule); + + /* The rule has been converted to pCode. Now allocate + * space for the wildcards */ + + postinit_pCodeWildBlock(¤tRule->target); + postinit_pCodeWildBlock(¤tRule->replace); + + //return; // debug ... don't want to go through all the rules yet + } + + if (0) + { + pCodePeep *peepBlock; + DLList *peeprules; + + peeprules = (DLList *)peepSnippets; + fprintf(stderr,"target rules\n"); + while (peeprules) + { + fprintf(stderr," rule:\n"); + peepBlock = ((pCodePeepSnippets*)peeprules)->peep; + pic16_printpBlock(stderr, peepBlock->target.pb); + peeprules = peeprules->next; + } // while + fprintf(stderr," ... done\n"); + } // if +} +#if 0 +static void printpCodeString(FILE *of, pCode *pc, int max) +{ + int i=0; + + while(pc && (i++print(of,pc); + pc = pc->next; + } +} +#endif +/*-----------------------------------------------------------------*/ +/* DLList * DLL_append */ +/* */ +/* Append a DLList object to the end of a DLList (doubly linked */ +/* list). If The list to which we want to append is non-existant */ +/* then one is created. Other wise, the end of the list is sought */ +/* out and a new DLList object is appended to it. In either case, */ +/* the void *data is added to the newly created DLL object. */ +/*-----------------------------------------------------------------*/ + +static void * DLL_append(DLList *list, DLList *next) +{ + DLList *b; + + + /* If there's no list, then create one: */ + if(!list) { + next->next = next->prev = NULL; + return next; + } + + + /* Search for the end of the list. */ + b = list; + while(b->next) + b = b->next; + + /* Now append the new DLL object */ + b->next = next; + b->next->prev = b; + b = b->next; + b->next = NULL; + + return list; + +} + + +/*----------------------------------------------------------------- + + pCode peephole optimization + + + The pCode "peep hole" optimization is not too unlike the peep hole + optimization in SDCCpeeph.c. The major difference is that here we + use pCode's whereas there we use ASCII strings. The advantage with + pCode's is that we can ascertain flow information in the instructions + being optimized. + + + - elaborate... + + -----------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------*/ +/* pCodeSearchCondition - Search a pCode chain for a 'condition' */ +/* */ +/* return conditions */ +/* 1 - The Condition was found for a pCode's input */ +/* 0 - No matching condition was found for the whole chain */ +/* -1 - The Condition was found for a pCode's output */ +/* */ +/*-----------------------------------------------------------------*/ +int pic16_pCodeSearchCondition(pCode *pc, unsigned int cond) +{ + //fprintf(stderr,"Checking conditions %d\n",cond); + while(pc) { + + /* If we reach a function end (presumably an end since we most + probably began the search in the middle of a function), then + the condition was not found. */ + if(pc->type == PC_FUNCTION) + return 0; + + if(pc->type == PC_OPCODE) { + //fprintf(stderr," checking conditions of: "); + //pc->print(stderr,pc); + //fprintf(stderr,"\t\tinCond=%d\toutCond=%d\n",PCI(pc)->inCond,PCI(pc)->outCond); + if(PCI(pc)->inCond & cond) + return 1; + if(PCI(pc)->outCond & cond) + return -1; + } + + pc = pc->next; + } + + return 0; +} + +/*----------------------------------------------------------------- + * int pCodeOpCompare(pCodeOp *pcops, pCodeOp *pcopd) + * + * Compare two pCodeOp's and return 1 if they're the same + *-----------------------------------------------------------------*/ +static int pCodeOpCompare(pCodeOp *pcops, pCodeOp *pcopd) +{ + char b[1024], *n2; + + if(!pcops || !pcopd) + return 0; + +#if 0 + fprintf(stderr,"%s:%d Comparing operands %s", __FILE__, __LINE__, + pic16_get_op( pcops,NULL,0)); + + fprintf(stderr," to %s\n", + pic16_get_op( pcopd,NULL,0)); +#endif + + if(pcops->type != pcopd->type) { + //fprintf(stderr," - fail - diff types\n"); + return 0; // different types + } + + if(pcops->type == PO_LITERAL) { + + if((PCOL(pcops)->lit >= 0) && (PCOL(pcops)->lit == PCOL(pcopd)->lit)) + return 1; + + return 0; + } + + memset(b, 0, sizeof(b) ); //b[0]=0; + pic16_get_op(pcops,b, sizeof(b) ); + + n2 = pic16_get_op(pcopd,NULL,0); + + if( !n2 || strcmp(b,n2)) { +// fprintf(stderr," - fail - diff names: %s, len=%d, %s, len=%d\n",b,strlen(b), n2, strlen(n2) ); + return 0; // different names + } + + switch(pcops->type) { + case PO_DIR: + if( PCOR(pcops)->instance != PCOR(pcopd)->instance) { +// fprintf(stderr, " - fail different instances\n"); + return 0; + } + break; + default: + break; + } + + //fprintf(stderr," - pass\n"); + + return 1; +} + +static int pCodePeepMatchLabels(pCodePeep *peepBlock, pCode *pcs, pCode *pcd) +{ + int labindex; + + /* Check for a label associated with this wild pCode */ + // If the wild card has a label, make sure the source code does too. + if(PCI(pcd)->label) { + pCode *pcl = PCI(pcd)->label->pc; + +#ifdef PCODE_DEBUG + int li = -PCL(pcl)->key; + + if(peepBlock->target.vars[li] == NULL) { + if(PCI(pcs)->label) { + DFPRINTF((stderr,"first time for a label: %d %s\n",li,PCL(PCI(pcs)->label->pc)->label)); + } + } else { + // DFPRINTF((stderr,"label id = %d \n",PCL(PCI(pcd)->label->pc)->key)); + DFPRINTF((stderr," label id: %d %s\n",li,peepBlock->target.vars[li])); + if(PCI(pcs)->label) { + DFPRINTF((stderr," src %s\n",PCL(PCI(pcs)->label->pc)->label)); + } + } +#endif + + + if(!PCI(pcs)->label) + return 0; + + labindex = -PCL(pcl)->key; + if(peepBlock->target.vars[labindex] == NULL) { + // First time to encounter this label + peepBlock->target.vars[labindex] = PCL(PCI(pcs)->label->pc)->label; + DFPRINTF((stderr,"first time for a label: %d %s\n",labindex,PCL(PCI(pcs)->label->pc)->label)); + + } else { + if(strcmp(peepBlock->target.vars[labindex],PCL(PCI(pcs)->label->pc)->label) != 0) { + DFPRINTF((stderr,"labels don't match dest %s != src %s\n",peepBlock->target.vars[labindex],PCL(PCI(pcs)->label->pc)->label)); + return 0; + } + DFPRINTF((stderr,"matched a label %d %s -hey\n",labindex,peepBlock->target.vars[labindex])); + } + } else { + //DFPRINTF((stderr,"destination doesn't have a label\n")); + + if(PCI(pcs)->label) + return 0; + + //DFPRINTF((stderr,"neither src nor dest have labels\n")); + + } + + return 1; + +} + +/*-----------------------------------------------------------------*/ +/* pCodePeepMatchLine - Compare source and destination pCodes to */ +/* see they're the same. */ +/* */ +/* In this context, "source" refers to the coded generated by gen.c*/ +/* and "destination" refers to a pcode in a peep rule. If the dest-*/ +/* ination has no wild cards, then MatchLine will compare the two */ +/* pcodes (src and dest) for a one-to-one match. If the destination*/ +/* has wildcards, then those get expanded. When a wild card is */ +/* encountered for the first time it autmatically is considered a */ +/* match and the object that matches it is referenced in the */ +/* variables or opcodes array (depending on the type of match). */ +/* */ +/* */ +/* Inputs: */ +/* *peepBlock - A pointer to the peepBlock that contains the */ +/* entire rule to which the destination pcode belongs*/ +/* *pcs - a pointer to the source pcode */ +/* *pcd - a pointer to the destination pcode */ +/* */ +/* Returns: */ +/* 1 - pcodes match */ +/* 0 - pcodes don't match */ +/* */ +/* */ +/*-----------------------------------------------------------------*/ + +static int pCodePeepMatchLine(pCodePeep *peepBlock, pCode *pcs, pCode *pcd) +{ + int index; // index into wild card arrays + int havematch=0; + + /* one-for-one match. Here the source and destination opcodes + * are not wild. However, there may be a label or a wild operand */ + + if(pcs) { + if(PCI(pcs)->label) { + DFPRINTF((stderr,"Match line source label: %s\n",PCL(PCI(pcs)->label->pc)->label)); + } + } + + if(pcs->type == pcd->type) { + + if(pcs->type == PC_OPCODE) { + + /* If the opcodes don't match then the line doesn't match */ + if(PCI(pcs)->op != PCI(pcd)->op) + return 0; + +#ifdef PCODE_DEBUG + DFPRINTF((stderr,"%s comparing\n",__FUNCTION__)); + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); +#endif + + if(!pCodePeepMatchLabels(peepBlock, pcs, pcd)) + return 0; + + /* Compare the operands */ + if(PCI(pcd)->pcop) { + /* assert that optimizations do not touch operations that work on SFRs or INDF registers */ + if ((PCI(pcd)->pcop->type == PO_WILD) && (!(PCI(pcs)->pcop) || ((PCI(pcs)->pcop->type != PO_SFR_REGISTER) && (PCI(pcs)->pcop->type != PO_INDF0)))) { + index = PCOW(PCI(pcd)->pcop)->id; + //DFPRINTF((stderr,"destination is wild\n")); +#ifdef DEBUG_PCODEPEEP + if (index > peepBlock->nops) { + DFPRINTF((stderr,"%s - variables exceeded\n",__FUNCTION__)); + exit(1); + } +#endif + + PCOW(PCI(pcd)->pcop)->matched = PCI(pcs)->pcop; + havematch = -1; + if(!peepBlock->target.wildpCodeOps[index]) { + peepBlock->target.wildpCodeOps[index] = PCI(pcs)->pcop; + + //if(PCI(pcs)->pcop->type == PO_GPR_TEMP) + + } else { + /* + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); + + fprintf(stderr, "comparing operands of these instructions, result %d\n", + pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]) + ); + */ + + havematch = pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]); +// return pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]); + } + + if((havematch==-1) && PCI(pcs)->pcop) { + char *n; + + switch(PCI(pcs)->pcop->type) { + case PO_GPR_TEMP: + case PO_FSR0: + //case PO_INDF0: + n = PCOR(PCI(pcs)->pcop)->r->name; + + break; + default: + assert (PCI(pcs)->pcop != NULL); + n = PCI(pcs)->pcop->name; + } + + if(peepBlock->target.vars[index]) + return (strcmp(peepBlock->target.vars[index],n) == 0); + else { + DFPRINTF((stderr,"first time for a variable: %d, %s\n",index,n)); + peepBlock->target.vars[index] = n; + havematch = 1; +// return 1; + } + } + + /* now check whether the second operand matches */ + /* assert that optimizations do not touch operations that work on SFRs or INDF registers */ + if(PCOW2(PCI(pcd)->pcop) && (PCOP2(PCI(pcd)->pcop)->pcopR->type == PO_WILD) && (!(PCOP2(PCI(pcs)->pcop)->pcopR) || ((PCOP2(PCI(pcs)->pcop)->pcopR->type != PO_SFR_REGISTER) && (PCOP2(PCI(pcs)->pcop)->pcopR) && (PCOP2(PCI(pcs)->pcop)->pcopR->type != PO_INDF0)))) { + +// fprintf(stderr, "%s:%d %s second operand is wild\n", __FILE__, __LINE__, __FUNCTION__); + + index = PCOW2(PCI(pcd)->pcop)->id; + //DFPRINTF((stderr,"destination is wild\n")); +#ifdef DEBUG_PCODEPEEP + if (index > peepBlock->nops) { + DFPRINTF((stderr,"%s - variables exceeded\n",__FUNCTION__)); + exit(1); + } +#endif + + PCOW2(PCI(pcd)->pcop)->matched = PCOP2(PCI(pcs)->pcop)->pcopR; + if(!peepBlock->target.wildpCodeOps[index]) { + peepBlock->target.wildpCodeOps[index] = PCOP2(PCI(pcs)->pcop)->pcopR; + + //if(PCI(pcs)->pcop->type == PO_GPR_TEMP) + + } else { + /* + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); + + fprintf(stderr, "comparing operands of these instructions, result %d\n", + pCodeOpCompare(PCI(pcs)->pcop, peepBlock->target.wildpCodeOps[index]) + ); + */ + + return ((havematch==1) && pCodeOpCompare(PCOP2(PCI(pcs)->pcop)->pcopR, + peepBlock->target.wildpCodeOps[index])); + } + + if(PCOP2(PCI(pcs)->pcop)->pcopR) { + char *n; + + switch(PCOP2(PCI(pcs)->pcop)->pcopR->type) { + case PO_GPR_TEMP: + case PO_FSR0: + //case PO_INDF0: + n = PCOR(PCOP2(PCI(pcs)->pcop)->pcopR)->r->name; + break; + default: + n = PCOP2(PCI(pcs)->pcop)->pcopR->name; + } + + if(peepBlock->target.vars[index]) + return (havematch && (strcmp(peepBlock->target.vars[index],n) == 0)); + else { + DFPRINTF((stderr,"first time for a variable: %d, %s\n",index,n)); + peepBlock->target.vars[index] = n; + return (havematch==1); //&& 1; + } + } + + } else if (PCOW2(PCI(pcd)->pcop) && (PCOP2(PCI(pcd)->pcop)->pcopR->type == PO_WILD) && PCOP2(PCI(pcs)->pcop)->pcopR) + { + return 0; + } else { + return havematch; + } +#if 0 + else if (PCI(pcd)->pcop->type == PO_LITERAL) { + return (havematch && + pCodeOpCompare(PCOR2(PCI(pcs)->pcop)->pcop2, PCOR2(PCI(pcd)->pcop)->pcop2)); + + } +#endif + + } else if (PCI(pcd)->pcop->type == PO_LITERAL) { + return pCodeOpCompare(PCI(pcs)->pcop, PCI(pcd)->pcop); + + } + } + + /* FIXME - need an else to check the case when the destination + * isn't a wild card */ + } else + /* The pcd has no operand. Lines match if pcs has no operand either*/ + return (PCI(pcs)->pcop == NULL); + } + + /* Compare a wild instruction to a regular one. */ + + if((pcd->type == PC_WILD) && (pcs->type == PC_OPCODE)) { + + index = PCW(pcd)->id; +#ifdef PCODE_DEBUG + DFPRINTF((stderr,"%s comparing wild cards\n",__FUNCTION__)); + pcs->print(stderr,pcs); + pcd->print(stderr,pcd); +#endif + peepBlock->target.wildpCodes[PCW(pcd)->id] = pcs; + + if(!pCodePeepMatchLabels(peepBlock, pcs, pcd)) { + DFPRINTF((stderr," Failing because labels don't match\n")); + return 0; + } + + if(PCW(pcd)->mustBeBitSkipInst & !(PCI(pcs)->isBitInst && PCI(pcs)->isSkip)) { + // doesn't match because the wild pcode must be a bit skip + DFPRINTF((stderr," Failing match because bit skip is req\n")); + //pcd->print(stderr,pcd); + //pcs->print(stderr,pcs); + return 0; + } + + if(PCW(pcd)->mustNotBeBitSkipInst & (PCI(pcs)->isBitInst && PCI(pcs)->isSkip)) { + // doesn't match because the wild pcode must *not* be a bit skip + DFPRINTF((stderr," Failing match because shouldn't be bit skip\n")); + //pcd->print(stderr,pcd); + //pcs->print(stderr,pcs); + return 0; + } + + if(PCW(pcd)->operand) { + PCOW(PCI(pcd)->pcop)->matched = PCI(pcs)->pcop; + if(peepBlock->target.vars[index]) { + int i = (strcmp(peepBlock->target.vars[index],PCI(pcs)->pcop->name) == 0); +#ifdef PCODE_DEBUG + + if(i) + DFPRINTF((stderr," (matched)\n")); + else { + DFPRINTF((stderr," (no match: wild card operand mismatch\n")); + DFPRINTF((stderr," peepblock= %s, pcodeop= %s\n", + peepBlock->target.vars[index], + PCI(pcs)->pcop->name)); + } +#endif + return i; + } else { + DFPRINTF((stderr," (matched %s\n",PCI(pcs)->pcop->name)); + peepBlock->target.vars[index] = PCI(pcs)->pcop->name; + return 1; + } + } + + pcs = pic16_findNextInstruction(pcs->next); + if(pcs) { + //DFPRINTF((stderr," (next to match)\n")); + //pcs->print(stderr,pcs); + } else if(pcd->next) { + /* oops, we ran out of code, but there's more to the rule */ + return 0; + } + + return 1; /* wild card matches */ + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodePeepClrVars(pCodePeep *pcp) +{ + + int i; + if(!pcp) + return; +/* + DFPRINTF((stderr," Clearing peep rule vars\n")); + DFPRINTF((stderr," %d %d %d %d %d %d\n", + pcp->target.nvars,pcp->target.nops,pcp->target.nwildpCodes, + pcp->replace.nvars,pcp->replace.nops,pcp->replace.nwildpCodes)); +*/ + for(i=0;itarget.nvars; i++) + pcp->target.vars[i] = NULL; + for(i=0;itarget.nops; i++) + pcp->target.wildpCodeOps[i] = NULL; + for(i=0;itarget.nwildpCodes; i++) + pcp->target.wildpCodes[i] = NULL; + + for(i=0;ireplace.nvars; i++) + pcp->replace.vars[i] = NULL; + for(i=0;ireplace.nops; i++) + pcp->replace.wildpCodeOps[i] = NULL; + for(i=0;ireplace.nwildpCodes; i++) + pcp->replace.wildpCodes[i] = NULL; + + + +} + +/*-----------------------------------------------------------------*/ +/* pCodeInsertAfter - splice in the pCode chain starting with pc2 */ +/* into the pCode chain containing pc1 */ +/*-----------------------------------------------------------------*/ +void pic16_pCodeInsertAfter(pCode *pc1, pCode *pc2) +{ + + if(!pc1 || !pc2) + return; + + pc2->next = pc1->next; + if(pc1->next) + pc1->next->prev = pc2; + + pc2->pb = pc1->pb; + pc2->prev = pc1; + pc1->next = pc2; + +} + +/*-----------------------------------------------------------------*/ +/* pic16_pCodeOpCopy - copy a pcode operator */ +/*-----------------------------------------------------------------*/ +pCodeOp *pic16_pCodeOpCopy(pCodeOp *pcop) +{ + pCodeOp *pcopnew=NULL; + + if(!pcop) + return NULL; + + switch(pcop->type) { + case PO_NONE: + case PO_STR: + case PO_REL_ADDR: + pcopnew = Safe_malloc(sizeof(pCodeOp)); + memcpy(pcopnew, pcop, sizeof(pCodeOp)); + break; + + case PO_W: + case PO_WREG: + case PO_STATUS: + case PO_BSR: + case PO_FSR0: + case PO_INDF0: + case PO_INTCON: + case PO_GPR_REGISTER: + case PO_GPR_TEMP: + case PO_SFR_REGISTER: + case PO_PCL: + case PO_PCLATH: + case PO_PCLATU: + case PO_PRODL: + case PO_PRODH: + case PO_DIR: + //DFPRINTF((stderr,"pCodeOpCopy GPR register\n")); + /* XXX: might also be pCodeOpReg2 -- that's why the two structs are identical */ + pcopnew = Safe_malloc(sizeof(pCodeOpReg)); + memcpy(pcopnew, pcop, sizeof(pCodeOpReg)); + break; + + case PO_LITERAL: + //DFPRINTF((stderr,"pCodeOpCopy lit\n")); + /* XXX: might also be pCodeOpLit2, that's why the two structs are identical... */ + pcopnew = Safe_malloc(sizeof(pCodeOpLit)); + memcpy(pcopnew, pcop, sizeof(pCodeOpLit)); + break; + + case PO_IMMEDIATE: + pcopnew = Safe_malloc(sizeof(pCodeOpImmd)); + memcpy(pcopnew, pcop, sizeof(pCodeOpImmd)); + break; + + case PO_GPR_BIT: + case PO_CRY: + case PO_BIT: + pcopnew = Safe_malloc(sizeof(pCodeOpRegBit)); + memcpy(pcopnew, pcop, sizeof(pCodeOpRegBit)); + break; + + case PO_LABEL: + //DFPRINTF((stderr,"pCodeOpCopy label\n")); + pcopnew = Safe_malloc(sizeof(pCodeOpLabel)); + memcpy(pcopnew, pcop, sizeof(pCodeOpLabel)); + break; + + case PO_WILD: + /* Here we expand the wild card into the appropriate type: */ + /* By recursively calling pCodeOpCopy */ + //DFPRINTF((stderr,"pCodeOpCopy wild\n")); + if(PCOW(pcop)->matched) + pcopnew = pic16_pCodeOpCopy(PCOW(pcop)->matched); + else { + // Probably a label + pcopnew = pic16_pCodeOpCopy(PCOW(pcop)->subtype); + pcopnew->name = Safe_strdup(PCOW(pcop)->pcwb->vars[PCOW(pcop)->id]); + //DFPRINTF((stderr,"copied a wild op named %s\n",pcopnew->name)); + } + return pcopnew; + break; + + case PO_TWO_OPS: + pcopnew = pic16_newpCodeOp2( pic16_pCodeOpCopy( PCOP2(pcop)->pcopL ), + pic16_pCodeOpCopy( PCOP2(pcop)->pcopR ) ); + return pcopnew; + + default: + assert ( !"unhandled pCodeOp type copied" ); + } // switch + + /* strdup pcop->name (prevent access to shared but released memory) */ + if(pcop->name) + pcopnew->name = Safe_strdup(pcop->name); + else + pcopnew->name = NULL; + + return pcopnew; +} + + +/*-----------------------------------------------------------------*/ +/* pCodeCopy - copy a pcode */ +/*-----------------------------------------------------------------*/ +static pCode *pCodeInstructionCopy(pCodeInstruction *pci,int invert) +{ + pCodeInstruction *new_pci; + + if(invert) + new_pci = PCI(pic16_newpCode(pci->inverted_op,pci->pcop)); + else + new_pci = PCI(pic16_newpCode(pci->op,pci->pcop)); + + new_pci->pc.pb = pci->pc.pb; + new_pci->from = pci->from; + new_pci->to = pci->to; + new_pci->label = pci->label; + new_pci->pcflow = pci->pcflow; + + return PCODE(new_pci); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static void pCodeDeleteChain(pCode *f,pCode *t) +{ + pCode *pc; + + + while(f && f!=t) { + DFPRINTF((stderr,"delete pCode:\n")); + pc = f->next; + //f->print(stderr,f); + //f->delete(f); this dumps core... + + f = pc; + + } + +} +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +int pic16_pCodePeepMatchRule(pCode *pc) +{ + pCodePeep *peepBlock; + pCode *pct, *pcin; + pCodeCSource *pc_cline=NULL; + DLList *peeprules; + int matched; + pCode *pcr; + + peeprules = (DLList *)peepSnippets; + + while(peeprules) { + peepBlock = ((pCodePeepSnippets*)peeprules)->peep; + + if(!peepBlock || /*!peepBlock->target ||*/ !peepBlock->target.pb->pcHead) { + fprintf(stderr, "skipping rule because target pb is NULL\n"); + goto next_rule; + } + + pCodePeepClrVars(peepBlock); +/* + pcin = pc; + if(IS_PCCOMMENT(pcin)) + pc = pcin = pic16_findNextInstruction(pcin->next); +*/ + pcin = pc = pic16_findNextInstruction(pc); + + pct = peepBlock->target.pb->pcHead; +#ifdef PCODE_DEBUG + { + pCode *pcr = peepBlock->replace.pb->pcHead; + if(pcr) pct->print(stderr,pcr); + } +#endif + matched = 0; + while(pct && pcin) { + + if(! (matched = pCodePeepMatchLine(peepBlock, pcin,pct))) + break; + + pcin = pic16_findNextInstruction(pcin->next); + pct = pct->next; + //debug: + //DFPRINTF((stderr," matched\n")); + + if(!pcin && pct) { + DFPRINTF((stderr," partial match... no more code\n")); +// fprintf(stderr," partial match... no more code\n"); + matched = 0; + } + if(!pct) { + DFPRINTF((stderr," end of rule\n")); + } + } + + if(matched && pcin) { + + /* So far we matched the rule up to the point of the conditions . + * In other words, all of the opcodes match. Now we need to see + * if the post conditions are satisfied. + * First we check the 'postFalseCond'. This means that we check + * to see if any of the subsequent pCode's in the pCode chain + * following the point just past where we have matched depend on + * the `postFalseCond' as input then we abort the match + */ + DFPRINTF((stderr," matched rule so far, now checking conditions\n")); + //pcin->print(stderr,pcin); + + if (pcin && peepBlock->postFalseCond && + (pic16_pCodeSearchCondition(pcin,peepBlock->postFalseCond) > 0) ) + matched = 0; + +// fprintf(stderr," condition results = %d\n",pic16_pCodeSearchCondition(pcin,peepBlock->postFalseCond)); + + + //if(!matched) fprintf(stderr,"failed on conditions\n"); + } + + if(matched) { + + pCode *pcprev; +// pCode *pcr; + + + /* We matched a rule! Now we have to go through and remove the + inefficient code with the optimized version */ +#ifdef PCODE_DEBUG + DFPRINTF((stderr, "Found a pcode peep match:\nRule:\n")); +// printpCodeString(stderr,peepBlock->target.pb->pcHead,10); + DFPRINTF((stderr,"first thing matched\n")); + pc->print(stderr,pc); + if(pcin) { + DFPRINTF((stderr,"last thing matched\n")); + pcin->print(stderr,pcin); + } +#endif + + + /* Unlink the original code */ + pcprev = pc->prev; + pcprev->next = pcin; + if(pcin) + pcin->prev = pc->prev; + + +#if 1 + { + /* DEBUG */ + /* Converted the deleted pCodes into comments */ + + char buf[1024]; + pCodeCSource *pc_cline2=NULL; + +// buf[0] = ';'; + buf[0] = '#'; + + while(pc && pc!=pcin) { + + if(pc->type == PC_OPCODE && PCI(pc)->cline) { + if(pc_cline) { + pc_cline2->pc.next = PCODE(PCI(pc)->cline); + pc_cline2 = PCCS(pc_cline2->pc.next); + } else { + pc_cline = pc_cline2 = PCI(pc)->cline; + pc_cline->pc.seq = pc->seq; + } + } + + pic16_pCode2str(&buf[1], sizeof( buf )-1, pc); + pic16_pCodeInsertAfter(pcprev, pic16_newpCodeCharP(buf)); + pcprev = pcprev->next; + pc = pc->next; + + } + if(pc_cline2) + pc_cline2->pc.next = NULL; + } +#endif + + if(pcin) + pCodeDeleteChain(pc,pcin); + +// fprintf(stderr, "%s:%d rule matched\n", __FILE__, __LINE__); + + /* Generate the replacement code */ + pc = pcprev; + pcr = peepBlock->replace.pb->pcHead; // This is the replacement code + while (pcr) { + pCodeOp *pcop=NULL; + + /* If the replace pcode is an instruction with an operand, */ + /* then duplicate the operand (and expand wild cards in the process). */ + if(pcr->type == PC_OPCODE) { + if(PCI(pcr)->pcop) { + /* The replacing instruction has an operand. + * Is it wild? */ + if(PCI(pcr)->pcop->type == PO_WILD) { + int index = PCOW(PCI(pcr)->pcop)->id; +// fprintf(stderr, "%s:%d replacing index= %d\n", __FUNCTION__, __LINE__, index); + //DFPRINTF((stderr,"copying wildopcode\n")); + if(peepBlock->target.wildpCodeOps[index]) + pcop = pic16_pCodeOpCopy(peepBlock->target.wildpCodeOps[index]); + else + DFPRINTF((stderr,"error, wildopcode in replace but not source?\n")); + } else + pcop = pic16_pCodeOpCopy(PCI(pcr)->pcop); + } + + if(PCI(pcr)->is2MemOp && PCOP2(PCI(pcr)->pcop)->pcopR) { + /* The replacing instruction has also a second operand. + * Is it wild? */ +// fprintf(stderr, "%s:%d pcop2= %p\n", __FILE__, __LINE__, PCOR2(PCI(pcr)->pcop)->pcop2); + if(PCOP2(PCI(pcr)->pcop)->pcopR->type == PO_WILD) { + int index = PCOW2(PCI(pcr)->pcop)->id; +// fprintf(stderr, "%s:%d replacing index= %d\n", __FUNCTION__, __LINE__, index); + //DFPRINTF((stderr,"copying wildopcode\n")); + if(peepBlock->target.wildpCodeOps[index]) + pcop = pic16_popCombine2(pic16_pCodeOpCopy(pcop), + pic16_pCodeOpCopy(peepBlock->target.wildpCodeOps[index]), 0); + else + DFPRINTF((stderr,"error, wildopcode in replace but not source?\n")); + } else + pcop = pic16_popCombine2(pic16_pCodeOpCopy(pcop), + pic16_pCodeOpCopy(PCOP2(PCI(pcr)->pcop)->pcopR), 0); + + } + + //DFPRINTF((stderr,"inserting pCode\n")); + pic16_pCodeInsertAfter(pc, pic16_newpCode(PCI(pcr)->op,pcop)); + } else if (pcr->type == PC_WILD) { + if(PCW(pcr)->invertBitSkipInst) + DFPRINTF((stderr,"We need to invert the bit skip instruction\n")); + pic16_pCodeInsertAfter(pc, + pCodeInstructionCopy(PCI(peepBlock->target.wildpCodes[PCW(pcr)->id]), + PCW(pcr)->invertBitSkipInst)); + } else if (pcr->type == PC_COMMENT) { + pic16_pCodeInsertAfter(pc, pic16_newpCodeCharP( ((pCodeComment *)(pcr))->comment)); + } + + + pc = pc->next; +#ifdef PCODE_DEBUG + DFPRINTF((stderr," NEW Code:")); + if(pc) pc->print(stderr,pc); +#endif + pcr = pcr->next; + } + + /* We have just replaced the inefficient code with the rule. + * Now, we need to re-add the C-source symbols if there are any */ + pc = pcprev; + while(pc_cline ) { + + pc = pic16_findNextInstruction(pc->next); + PCI(pc)->cline = pc_cline; + pc_cline = PCCS(pc_cline->pc.next); + + } + + return 1; + } + next_rule: + peeprules = peeprules->next; + } + DFPRINTF((stderr," no rule matched\n")); + + return 0; +} diff --git a/src/pic16/pcoderegs.c b/src/pic16/pcoderegs.c new file mode 100644 index 0000000..b1d2bbf --- /dev/null +++ b/src/pic16/pcoderegs.c @@ -0,0 +1,1007 @@ +/*------------------------------------------------------------------------- + + pcoderegs.c - post code generation register optimizations + + Written By - Scott Dattalo scott@dattalo.com + Ported To PIC16 By - m.dubuc@rogers.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +/* + pcoderegs.c + + The purpose of the code in this file is to optimize the register usage. + +*/ +#include + +#include "common.h" // Include everything in the SDCC src directory +#include "newalloc.h" +#include "ralloc.h" +#include "device.h" +#include "pcode.h" +#include "pcoderegs.h" +#include "pcodeflow.h" + + +#define DEBUG_REMOVE1PCODE 0 +#define HAVE_DBGREGUSAGE 0 + + +extern void pic16_pCodeInsertAfter(pCode *pc1, pCode *pc2); +extern pCode * pic16_findPrevInstruction(pCode *pci); +extern pBranch * pic16_pBranchAppend(pBranch *h, pBranch *n); +void pic16_unlinkpCode(pCode *pc); +extern int pic16_pCodeSearchCondition(pCode *pc, unsigned int cond); + +static int total_registers_saved=0; +static int register_optimization=1; + +/*-----------------------------------------------------------------* + * void AddRegToFlow(regs *reg, pCodeFlow *pcfl) + *-----------------------------------------------------------------*/ +/* +void AddRegToFlow(regs *reg, pCodeFlow *pcfl) +{ + + if(!reg || ! pcfl || !isPCFL(pcflow)) + return; + + if(!pcfl->registers) + pcfl->registers = newSet(); + +} +*/ + + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ + +#if HAVE_DBGREGUSAGE +static void dbg_regusage(set *fregs) +{ + reg_info *reg; + pCode *pcfl; + pCode *pc; + + + for (reg = setFirstItem(fregs) ; reg ; + reg = setNextItem(fregs)) { + + if(elementsInSet(reg->reglives.usedpCodes)) { + + fprintf (stderr, "%s addr=0x%03x rIdx=0x%03x", + reg->name, + reg->address, + reg->rIdx); + + pcfl = setFirstItem(reg->reglives.usedpFlows); + if(pcfl) + fprintf(stderr, "\n used in seq"); + + while(pcfl) { + fprintf(stderr," 0x%03x",pcfl->seq); + pcfl = setNextItem(reg->reglives.usedpFlows); + } + + pcfl = setFirstItem(reg->reglives.assignedpFlows); + if(pcfl) + fprintf(stderr, "\n assigned in seq"); + + while(pcfl) { + fprintf(stderr," 0x%03x",pcfl->seq); + pcfl = setNextItem(reg->reglives.assignedpFlows); + } + + pc = setFirstItem(reg->reglives.usedpCodes); + if(pc) + fprintf(stderr, "\n used in instructions "); + + while(pc) { + pcfl = PCODE(PCI(pc)->pcflow); + if(pcfl) + fprintf(stderr," 0x%03x:",pcfl->seq); + fprintf(stderr,"0x%03x",pc->seq); + + pc = setNextItem(reg->reglives.usedpCodes); + } + + fprintf(stderr, "\n"); + } + } +} + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ + +//static +void dbg_dumpregusage(void) +{ + + fprintf(stderr,"*** Register Usage ***\n"); + fprintf(stderr,"InternalRegs:\n"); + dbg_regusage(pic16_dynInternalRegs); + fprintf(stderr,"AllocRegs:\n"); + dbg_regusage(pic16_dynAllocRegs); + fprintf(stderr,"StackRegs:\n"); + dbg_regusage(pic16_dynStackRegs); + fprintf(stderr,"DirectRegs:\n"); + dbg_regusage(pic16_dynDirectRegs); + fprintf(stderr,"DirectBitRegs:\n"); + dbg_regusage(pic16_dynDirectBitRegs); + fprintf(stderr,"ProcessorRegs:\n"); + dbg_regusage(pic16_dynProcessorRegs); + +} +#endif + +/*-----------------------------------------------------------------* + * void pCodeRegMapLiveRangesInFlow(pCodeFlow *pcfl) + *-----------------------------------------------------------------*/ +static void pCodeRegMapLiveRangesInFlow(pCodeFlow *pcfl) +{ + pCode *pc=NULL; + + reg_info *reg; + + if(!pcfl) + return; + + + pc = pic16_findNextInstruction(pcfl->pc.next); + + while (pic16_isPCinFlow(pc,PCODE(pcfl))) + { + reg = pic16_getRegFromInstruction(pc); + + if (reg && (reg->type != REG_TMP)) + { +#if 0 + fprintf(stderr, "reg= %p\n", reg); + fprintf(stderr, "flow seq %d, inst seq %d %s ",PCODE(pcfl)->seq,pc->seq,reg->name); + fprintf(stderr, "addr = 0x%03x, type = %d rIdx=0x%03x ", + reg->address,reg->type,reg->rIdx); + fprintf(stderr, "command = %s\n", PCI(pc)->mnemonic); +#endif + + //fprintf(stderr, "%s:%d: trying to get first operand from pCode reg= %s\n", __FILE__, __LINE__, reg->name); + addSetIfnotP(& (PCFL(pcfl)->registers), reg); + + if ((PCC_REGISTER | PCC_LITERAL) & PCI(pc)->inCond) + addSetIfnotP(& (reg->reglives.usedpFlows), pcfl); + + if (PCC_REGISTER & PCI(pc)->outCond) + addSetIfnotP(& (reg->reglives.assignedpFlows), pcfl); + + addSetIfnotP(& (reg->reglives.usedpCodes), pc); + + //reg->wasUsed=1; + +#if 1 + /* check to see if this pCode has 2 memory operands, + and set up the second operand too */ + if (PCI(pc)->is2MemOp) + { + reg = pic16_getRegFromInstruction2(pc); + if (reg) + { + //fprintf(stderr, "%s:%d: trying to get second operand from pCode reg= %s\n", __FILE__, __LINE__, reg->name); + addSetIfnotP(& (PCFL(pcfl)->registers), reg); + + if ((PCC_REGISTER | PCC_LITERAL) & PCI(pc)->inCond) + addSetIfnotP(& (reg->reglives.usedpFlows), pcfl); + + if ((PCC_REGISTER | PCC_REGISTER2) & PCI(pc)->outCond) + addSetIfnotP(& (reg->reglives.assignedpFlows), pcfl); + + addSetIfnotP(& (reg->reglives.usedpCodes), pc); + + //reg->wasUsed=1; + } // if + } // if +#endif + } // if + + pc = pic16_findNextInstruction(pc->next); + } // while +} + +/*-----------------------------------------------------------------* + * void pic16_pCodeRegMapLiveRanges(pBlock *pb) + *-----------------------------------------------------------------*/ +void pic16_pCodeRegMapLiveRanges(pBlock *pb) +{ + pCode *pcflow; + + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + if(!isPCFL(pcflow)) { + fprintf(stderr, "pCodeRegMapLiveRanges - pcflow is not a flow object "); + continue; + } + pCodeRegMapLiveRangesInFlow(PCFL(pcflow)); + } + +#if 0 + for( pcflow = pic16_findNextpCode(pb->pcHead, PC_FLOW); + pcflow != NULL; + pcflow = pic16_findNextpCode(pcflow->next, PC_FLOW) ) { + + regs *r = setFirstItem(PCFL(pcflow)->registers); + fprintf(stderr,"flow seq %d\n", pcflow->seq); + + while (r) { + fprintf(stderr, " %s\n",r->name); + r = setNextItem(PCFL(pcflow)->registers); + + } + + } +#endif + +#if HAVE_DBGREGUSAGE + dbg_dumpregusage(); +#endif + +} + + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ +static void Remove1pcode(pCode *pc, reg_info *reg) +{ + pCode *pcn=NULL; + + if(!reg || !pc) + return; + + deleteSetItem (&(reg->reglives.usedpCodes),pc); + +#if DEBUG_REMOVE1PCODE + fprintf(stderr,"removing instruction:\n"); + pc->print(stderr,pc); +#endif + + if(PCI(pc)->label) { + pcn = pic16_findNextInstruction(pc->next); + + if(pcn) + PCI(pcn)->label = pic16_pBranchAppend(PCI(pcn)->label,PCI(pc)->label); + } + + if(PCI(pc)->cline) { + if(!pcn) + pcn = pic16_findNextInstruction(pc->next); + + if(pcn) { + if(PCI(pcn)->cline) { + +#if DEBUG_REMOVE1PCODE + fprintf(stderr, "source line has been optimized completely out\n"); + pc->print(stderr,pc); +#endif + + } else { + PCI(pcn)->cline = PCI(pc)->cline; + } + } + } + + pc->destruct(pc); + +} + +/*-----------------------------------------------------------------* + * void RemoveRegsFromSet(set *regset) + * + *-----------------------------------------------------------------*/ +static void RemoveRegsFromSet(set *regset) +{ + reg_info *reg; + int used; + + while(regset) { + reg = regset->item; + regset = regset->next; + + used = elementsInSet(reg->reglives.usedpCodes); + + if(used <= 1) { + +// fprintf(stderr," reg %s isfree=%d, wasused=%d\n",reg->name,reg->isFree,reg->wasUsed); + + if(used == 0) { + +// fprintf(stderr,"%s:%d: getting rid of reg %s\n",__FILE__, __LINE__, reg->name); + + reg->isFree = 1; + reg->wasUsed = 0; + } else { + pCode *pc; + + + pc = setFirstItem(reg->reglives.usedpCodes); + + if(reg->type == REG_SFR) { + fprintf(stderr, "not removing SFR reg %s even though used only once\n", reg->name); + continue; + } + + if(isPCI(pc)) { + if(PCI(pc)->label) { + pCode *pcn = pic16_findNextInstruction(pc->next); + + if(pcn && PCI(pcn)->label) { + //fprintf(stderr,"can't delete instruction with label...\n"); + //pc->print(stderr,pc); + continue; + } + /* Move the label to the next instruction */ + + assert (PCI(pcn) != NULL); + PCI(pcn)->label = PCI(pc)->label; + + } + + if(isPCI_SKIP(pc)) { + reg_info *r = pic16_getRegFromInstruction(pc); + fprintf(stderr, "WARNING, a skip instruction is being optimized out\n"); + pc->print(stderr,pc); + fprintf(stderr,"reg %s, type =%d\n",r->name, r->type); + } + + Remove1pcode(pc, reg); + /* + pic16_unlinkpCode(pc); + deleteSetItem (&(reg->reglives.usedpCodes),pc); + */ + reg->isFree = 1; + reg->wasUsed = 0; + total_registers_saved++; // debugging stats. + } + } + } + + } +} +/*-----------------------------------------------------------------* + * void pic16_RemoveUnusedRegisters(void) + * + *-----------------------------------------------------------------*/ +void pic16_RemoveUnusedRegisters(void) +{ + /* First, get rid of registers that are used only one time */ + + //RemoveRegsFromSet(pic16_dynInternalRegs); + RemoveRegsFromSet(pic16_dynAllocRegs); + RemoveRegsFromSet(pic16_dynStackRegs); + /* + don't do DirectRegs yet - there's a problem with arrays + RemoveRegsFromSet(pic16_dynDirectRegs); + */ + RemoveRegsFromSet(pic16_dynDirectBitRegs); + + if(total_registers_saved && pic16_pcode_verbose) + fprintf(stderr, " *** Saved %d registers ***\n", total_registers_saved); +} + +static int insideLRBlock(pCode *pc) +{ + pCode *pc1; + int t1=-1, t2=-1; + + pc1 = pc->prev; + while(pc1) { + if(isPCINFO(pc1) && (PCINF(pc1)->type == INF_LOCALREGS)) { + t1 = PCOLR (PCINF (pc1)->oper1)->type; + break; + } + pc1 = pc1->prev; + } + + pc1 = pc->next; + while(pc1) { + if(isPCINFO(pc1) && (PCINF(pc1)->type == INF_LOCALREGS)) { + t2 = PCOLR (PCINF (pc1)->oper1)->type; + break; + } + pc1 = pc1->next; + } + + if((t1 == LR_ENTRY_BEGIN && t2 == LR_ENTRY_END) + || (t1 == LR_EXIT_BEGIN && t2 == LR_EXIT_END)) + return 1; + + return 0; +} + + +static void RemoveRegFromLRBlock(reg_info *reg) +{ + if(elementsInSet(reg->reglives.usedpCodes) == 2) { + pCode *pc1; + + /* only continue if there are just 2 uses of the register, + * in in the local *entry* block and one in the local *exit* block */ + + /* search for entry block */ + pc1 = indexSet(reg->reglives.usedpCodes, 1); + + if(insideLRBlock( pc1 )) { + fprintf(stderr, "usedpCodes[0] inside LR block\n"); + deleteSetItem(&pc1->pb->tregisters, PCOR(PCI(pc1)->pcop)->r); + Remove1pcode(pc1, reg); + } + + pc1 = indexSet(reg->reglives.usedpCodes, 0); + if(insideLRBlock( pc1 )) { + fprintf(stderr, "usedpCodes[1] inside LR block\n"); + deleteSetItem(&pc1->pb->tregisters, PCOR(PCI(pc1)->pcop)->r); + Remove1pcode(pc1, reg); + } + + /* remove r0x00 */ + reg->isFree = 1; + reg->wasUsed = 0; + } +} + + + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ +static void Remove2pcodes(pCode *pcflow, pCode *pc1, pCode *pc2, reg_info *reg, int can_free) +{ + if(!reg) + return; + +#if 0 + fprintf(stderr,"removing 2 instructions:\n"); + pc1->print(stderr,pc1); + pc2->print(stderr,pc2); +#endif + + if(pc1) + Remove1pcode(pc1, reg); + + if(pc2) { + Remove1pcode(pc2, reg); + deleteSetItem (&(PCFL(pcflow)->registers), reg); + + if(can_free) { + reg->isFree = 1; + reg->wasUsed = 0; + } + + } + + pCodeRegMapLiveRangesInFlow(PCFL(pcflow)); + +#if 1 +// fprintf(stderr, "register %s is used in %d pCodes, assigned in %d pCodes\n", reg->name, +// elementsInSet(reg->reglives.usedpCodes), +// elementsInSet(reg->reglives.assignedpFlows)); + + RemoveRegFromLRBlock(reg); +#endif + +} + +/*-----------------------------------------------------------------* + * + *-----------------------------------------------------------------*/ +static int regUsedinRange(pCode *pc1, pCode *pc2, reg_info *reg) +{ + int i=0; + reg_info *testreg; + + do { + testreg = pic16_getRegFromInstruction(pc1); + if(testreg && (testreg->rIdx == reg->rIdx)) { + return 1; + } + + if(PCI(pc1)->is2MemOp) { + testreg = pic16_getRegFromInstruction2(pc1); + if(testreg && (testreg->rIdx == reg->rIdx)) { + return 1; + } + } + + pc1 = pic16_findNextInstruction(pc1->next); + + } while (pc1 && (pc1 != pc2) && (i++ < 100)) ; + + if(i >= 100) + fprintf(stderr, "warning, regUsedinRange searched through too many pcodes\n"); + + return 0; +} + +/*-----------------------------------------------------------------* + * void pCodeOptime2pCodes(pCode *pc1, pCode *pc2) + * + * ADHOC pattern checking + * Now look for specific sequences that are easy to optimize. + * Many of these sequences are characteristic of the compiler + * (i.e. it'd probably be a waste of time to apply these adhoc + * checks to hand written assembly.) + * + * + *-----------------------------------------------------------------*/ +static int pCodeOptime2pCodes(pCode *pc1, pCode *pc2, pCode *pcfl_used, reg_info *reg, int can_free, int optimize_level) +{ + pCode *pct1, *pct2; + reg_info *reg1, *reg2; + + int t = total_registers_saved; + + if(reg->type == REG_SFR)return 0; + + if(pc2->seq < pc1->seq) { + pct1 = pc2; + pc2 = pc1; + pc1 = pct1; + } +/* + fprintf(stderr,"pCodeOptime2pCodes\n"); + pc1->print(stderr,pc1); + pc2->print(stderr,pc2); +*/ + if((PCI(pc1)->op == POC_CLRF) && (PCI(pc2)->op == POC_MOVFW) ){ + + /* + clrf reg + stuff... + movf reg,w + + can be replaced with + + stuff... + movlw 0 + */ + + pCode *newpc; + //fprintf(stderr, " CLRF/MOVFW. instruction after MOVFW is:\n"); + pct1 = pic16_findNextInstruction(pc2->next); + + if(PCI(pct1)->op == POC_MOVWF) { + newpc = pic16_newpCode(POC_CLRF, PCI(pct1)->pcop); + pct1->destruct(pct1); + } else { + newpc = pic16_newpCode(POC_MOVLW, pic16_newpCodeOpLit(0)); + } + + pic16_pCodeInsertAfter(pc2, newpc); + PCI(newpc)->pcflow = PCFL(pcfl_used); + newpc->seq = pc2->seq; + + /* take care if register is used after pc2, if yes, then don't delete + * clrf reg, because, reg should be initialized with zero */ + { + pCode *spc; + int maxSeq=0; + + for(spc=setFirstItem(reg->reglives.usedpCodes);spc;spc=setNextItem(reg->reglives.usedpCodes)) { + if(maxSeq < spc->seq)maxSeq = spc->seq; + } + +// fprintf(stderr, "pc1->seq = %d\tpc2->seq = %d\tspc->seq = %d\n", pc1->seq, pc2->seq, maxSeq); + + if(maxSeq > pc2->seq) { + /* this means that a pCode uses register after pc2, then + * we can't delete pc1 pCode */ + Remove2pcodes(pcfl_used, NULL, pc2, reg, can_free); + } else { + /* we can remove both pCodes */ + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + } + } + total_registers_saved++; // debugging stats. + + } else if((PCI(pc1)->op == POC_CLRF) && (PCI(pc2)->op == POC_IORFW) ){ + //fprintf(stderr, " CLRF/IORFW.\n"); + + pct2 = pic16_findNextInstruction(pc2->next); + + if(pic16_pCodeSearchCondition(pct2, PCC_Z) > 0) { + pct2 = pic16_newpCode(POC_IORLW, pic16_newpCodeOpLit(0)); + pct2->seq = pc2->seq; + PCI(pct2)->pcflow = PCFL(pcfl_used); + pic16_pCodeInsertAfter(pc1,pct2); + } + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + total_registers_saved++; // debugging stats. + + } else if(PCI(pc1)->op == POC_MOVWF) { + + reg1 = pic16_getRegFromInstruction(pc1); + + if(reg1->type == REG_SFR)return (total_registers_saved != t); + + pct2 = pic16_findNextInstruction(pc2->next); + + if(PCI(pc2)->op == POC_MOVFW) { + +#if 0 + fprintf(stderr, " MOVWF/MOVFW. instruction after MOVFW is:\n"); + pct2->print(stderr,pct2); +#endif + + if(PCI(pct2)->op == POC_MOVWF) { + /* + Change: + + movwf reg + + stuff... + + movf reg,w + movwf reg2 + + To: + + + */ + reg2 = pic16_getRegFromInstruction(pct2); + if(reg2 && !regUsedinRange(pc1,pc2,reg2) && (reg2->type != REG_SFR)) { +// if(reg2 && !regUsedinRange(pc1,pc2,reg2)) + + if(pic16_pCodeSearchCondition(pct2, PCC_Z) < 1) { + pCode *pct3 = pic16_findNextInstruction(pct2->next); + pct2->seq = pc1->seq; + pic16_unlinkpCode(pct2); + pic16_pCodeInsertAfter(pic16_findPrevInstruction(pc1->prev),pct2); + +#define usesW(x) ((x) && (isPCI(x)) && ( (PCI(x)->inCond & PCC_W) != 0)) + + if(usesW(pct3)) + ; // Remove2pcodes(pcfl_used, pc1, NULL, reg, can_free); + else { + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + total_registers_saved++; // debugging stats. + return 1; + } + } else { +// fprintf(stderr,"didn't optimize because Z bit is used\n"); + } + } +#if 0 + fprintf(stderr, " couldn't optimize\n"); + if(reg2) + fprintf(stderr, " %s is used in range\n", reg2->name); + else + fprintf(stderr, " reg2 is NULL\n"); +#endif + } + } + + pct1 = pic16_findPrevInstruction(pc1->prev); + if(pct1 && (PCI(pct1)->pcflow == PCI(pc1)->pcflow)) { + + if ( (PCI(pct1)->op == POC_MOVFW) && + (PCI(pc2)->op == POC_MOVFW)) { + + reg1 = pic16_getRegFromInstruction(pct1); + if(reg1 && !regUsedinRange(pc1,pc2,reg1)) { + +#if 0 + fprintf(stderr, " MOVF/MOVFW. \n"); + fprintf(stderr, " ...optimizing\n"); +#endif + + /* + Change: + + movf reg1,w + movwf reg + + stuff... + movf reg,w + + To: + + stuff... + + movf reg1,w + + Or, if we're not deleting the register then the "To" is: + + stuff... + + movf reg1,w + movwf reg + + + */ + pct2 = pic16_newpCode(PCI(pc2)->op, PCI(pct1)->pcop); + pic16_pCodeInsertAfter(pc2, pct2); + PCI(pct2)->pcflow = PCFL(pcfl_used); + pct2->seq = pc2->seq; + + if(can_free) { + Remove2pcodes(pcfl_used, pc1, pc2, reg, can_free); + } else { + /* If we're not freeing the register then that means (probably) + * the register is needed somewhere else.*/ + pic16_unlinkpCode(pc1); + pic16_pCodeInsertAfter(pct2, pc1); + + Remove2pcodes(pcfl_used, pc2, NULL, reg, can_free); + } + + Remove2pcodes(pcfl_used, pct1, NULL, reg1, 0); + total_registers_saved++; // debugging stats. + + } + } else if ( (PCI(pct1)->op == POC_MOVWF) && + (PCI(pc2)->op == POC_MOVFW)) { + +// fprintf(stderr,"movwf MOVWF/MOVFW\n"); + + if(optimize_level > 1 && can_free) { + pct2 = pic16_newpCode(POC_MOVFW, PCI(pc1)->pcop); + pic16_pCodeInsertAfter(pc2, pct2); + Remove2pcodes(pcfl_used, pc1, pc2, reg, 1); + total_registers_saved++; // debugging stats. + } + } + + + } + + } + + return (total_registers_saved != t); +} + +/*-----------------------------------------------------------------* + * void pCodeRegOptimeRegUsage(pBlock *pb) + *-----------------------------------------------------------------*/ +static void OptimizeRegUsage(set *fregs, int optimize_multi_uses, int optimize_level) +{ + reg_info *reg; + int used; + pCode *pc1=NULL, *pc2=NULL; + + + while(fregs) { + pCode *pcfl_used, *pcfl_assigned; + + /* Step through the set by directly accessing the 'next' pointer. + * We could also step through by using the set API, but the + * the (debug) calls to print instructions affect the state + * of the set pointers */ + + reg = fregs->item; + fregs = fregs->next; + + if(reg->type == REG_SFR) { +// fprintf(stderr,"skipping SFR: %s\n",reg->name); + continue; + } + + pcfl_used = setFirstItem(reg->reglives.usedpFlows); + pcfl_assigned = setFirstItem(reg->reglives.assignedpFlows); + + used = elementsInSet(reg->reglives.usedpCodes); +// fprintf(stderr, "%s:%d register %s used %d times in pCode\n", __FILE__, __LINE__, reg->name, used); + if(used == 2) { + + /* + * In this section, all registers that are used in only in two + * instructions are examined. If possible, they're optimized out. + */ + +#if 0 + fprintf (stderr, "OptimizeRegUsage: %s addr=0x%03x rIdx=0x%03x type=%d used=%d\n", + reg->name, + reg->address, + reg->rIdx, reg->type, used); +#endif + + pc1 = setFirstItem(reg->reglives.usedpCodes); + pc2 = setNextItem(reg->reglives.usedpCodes); + + if(pcfl_used && pcfl_assigned) { + + /* + expected case - the register has been assigned a value and is + subsequently used + */ + + //fprintf(stderr," used only twice\n"); + if(pcfl_used->seq == pcfl_assigned->seq && !(setNextItem(reg->reglives.usedpFlows)) && !(setNextItem(reg->reglives.assignedpFlows))) { + + //fprintf(stderr, " and used in same flow\n"); + + pCodeOptime2pCodes(pc1, pc2, pcfl_used, reg, 1,optimize_level); + + } else { + // fprintf(stderr, " and used in different flows\n"); + + } + + } else if(pcfl_used) { + + /* + register has been used twice without ever being assigned */ + //fprintf(stderr,"WARNING %s: reg %s used without being assigned\n",__FUNCTION__,reg->name); + + } else { +// fprintf(stderr,"WARNING %s: reg %s assigned without being used\n",__FUNCTION__,reg->name); + Remove2pcodes(pcfl_assigned, pc1, pc2, reg, 1); + total_registers_saved++; // debugging stats. + } + } else { + + /* register has been used either once, or more than twice */ + + if(used && !pcfl_used && pcfl_assigned) { + pCode *pc; + + fprintf(stderr,"WARNING %s: reg %s assigned without being used\n",__FUNCTION__,reg->name); + + pc = setFirstItem(reg->reglives.usedpCodes); + while(pc) { + + pcfl_assigned = PCODE(PCI(pc)->pcflow); + Remove1pcode(pc, reg); + + deleteSetItem (&(PCFL(pcfl_assigned)->registers), reg); + /* + deleteSetItem (&(reg->reglives.usedpCodes),pc); + pc->destruct(pc); + */ + pc = setNextItem(reg->reglives.usedpCodes); + } + + + reg->isFree = 1; + reg->wasUsed = 0; + + total_registers_saved++; // debugging stats. + } else if( (used > 2) && optimize_multi_uses) { + + set *rset1=NULL; + set *rset2=NULL; + int searching=1; + + pCodeFlow *pcfl1=NULL, *pcfl2=NULL; + + /* examine the number of times this register is used */ + + + rset1 = reg->reglives.usedpCodes; + while(rset1 && searching) { + + pc1 = rset1->item; + rset2 = rset1->next; + + if(pc1 && isPCI(pc1) && ( (pcfl1 = PCI(pc1)->pcflow) != NULL) ) { + + //while(rset2 && searching) { + if(rset2) { + + pc2 = rset2->item; + if(pc2 && isPCI(pc2) && ( (pcfl2 = PCI(pc2)->pcflow) != NULL) ) { + if(pcfl2 == pcfl1) { + + if(pCodeOptime2pCodes(pc1, pc2, pcfl_used, reg, 0,optimize_level)) + searching = 0; + } + } + + //rset2 = rset2->next; + + } + } + rset1 = rset1->next; + } + } + } + + } + +} +/*-----------------------------------------------------------------* + * void pic16_pCodeRegOptimeRegUsage(pBlock *pb) + *-----------------------------------------------------------------*/ +void pic16_pCodeRegOptimizeRegUsage(int level) +{ + + int passes; + int saved = 0; + int t = total_registers_saved; + + if(getenv("NO_REG_OPT")) + return; + + if(!register_optimization) + return; + +#define OPT_PASSES 8 + passes = OPT_PASSES; + + do { + saved = total_registers_saved; + + /* Identify registers used in one flow sequence */ + OptimizeRegUsage(pic16_dynAllocRegs,level, (OPT_PASSES-passes)); + OptimizeRegUsage(pic16_dynStackRegs,level, (OPT_PASSES-passes)); + OptimizeRegUsage(pic16_dynDirectRegs,0, (OPT_PASSES-passes)); + + if((total_registers_saved != saved) + && (pic16_pcode_verbose)) + fprintf(stderr, " *** pass %d, Saved %d registers, total saved %d ***\n", + (1+OPT_PASSES-passes),total_registers_saved-saved,total_registers_saved); + + passes--; + + } while( passes && ((total_registers_saved != saved) || (passes==OPT_PASSES-1)) ); + + if(total_registers_saved == t) + if(pic16_debug_verbose) + fprintf(stderr, "No registers saved on this pass\n"); + + +#if 0 + fprintf(stderr,"dynamically allocated regs:\n"); + dbg_regusage(pic16_dynAllocRegs); + fprintf(stderr,"stack regs:\n"); + dbg_regusage(pic16_dynStackRegs); + fprintf(stderr,"direct regs:\n"); + dbg_regusage(pic16_dynDirectRegs); +#endif +} + + +/*-----------------------------------------------------------------* + * void RegsUnMapLiveRanges(set *regset) + * + *-----------------------------------------------------------------*/ +static void RegsSetUnMapLiveRanges(set *regset) +{ + reg_info *reg; + + while(regset) { + reg = regset->item; + regset = regset->next; + + deleteSet(®->reglives.usedpCodes); + deleteSet(®->reglives.usedpFlows); + deleteSet(®->reglives.assignedpFlows); + + } + +} + +void pic16_RegsUnMapLiveRanges(void) +{ + RegsSetUnMapLiveRanges(pic16_dynAllocRegs); + RegsSetUnMapLiveRanges(pic16_dynStackRegs); + RegsSetUnMapLiveRanges(pic16_dynDirectRegs); + RegsSetUnMapLiveRanges(pic16_dynProcessorRegs); + RegsSetUnMapLiveRanges(pic16_dynDirectBitRegs); + RegsSetUnMapLiveRanges(pic16_dynInternalRegs); +} diff --git a/src/pic16/pcoderegs.h b/src/pic16/pcoderegs.h new file mode 100644 index 0000000..6ee056e --- /dev/null +++ b/src/pic16/pcoderegs.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + + pcoderegs.h - post code generation register optimizations + + Written By - Scott Dattalo scott@dattalo.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +-------------------------------------------------------------------------*/ + +#ifndef __PCODEREGS_H__ +#define __PCODEREGS_H__ + +/************************************************* + + pCodeRegLives + + Records the set of registers used in a flow object. + +**************************************************/ + +typedef struct pCodeRegLives { + set *usedpFlows; /* set of pFlow objects that use this register */ + set *assignedpFlows; /* set of pFlow objects that assign values to this register */ + set *usedpCodes; /* set of all instructions that use this register */ + +} pCodeRegLives; + + +#endif // __PCODEREGS_H__ diff --git a/src/pic16/peeph.def b/src/pic16/peeph.def new file mode 100644 index 0000000..dffb192 --- /dev/null +++ b/src/pic16/peeph.def @@ -0,0 +1,308 @@ +// PIC Port Peep rules +// +// +// INTRODUCTION: +// +// The peep hole optimizer searchs the +// the SDCC generated code for small snippets +// that can be optimized. As a user, you have +// control over this optimization process without +// having to learn the SDCC source code. (However +// you'll still need access to the source since +// these rules are compiled into the source.) +// +// The way it works is you specify the target +// snippet that you want replaced with a more +// efficient snippet that you write. Wild card +// variables allow the rules to be parameterized. +// +// In all of the SDCC ports, labels and operands +// can be wild cards. However, in the PIC even the +// instructions can be wild cards. +// +// EXAMPLE: +// +// Consider Peep Rule 1 as an example. This rule +// replaces some code like: +// +// skpz ;i.e. btfss status,Z +// goto lab1 +// clrw +//lab1: +// +// with: +// +// skpnz ;i.e. btfsc status,Z +// clrw +//lab1 +// +// However, the Rule has four wild cards. +// The first allows the btfss instruction operator +// be anything, not just the Z bit in status register. +// The second wild card applies to a label. +// The third wild card is for an instruction - any +// single instruction can be substituted. +// The fourth wild card is also an instruction. It's +// just an instruction place holder associated with +// a label (think of it as the PIC Port author's laziness +// imposed upon the user). +// +// +// CONDITIONS +// +// There are certain instances where a peep rule may not +// be applicable. Consider this subtle example: +// +// movwf R0 +// movf R0,W +// +// It would seem that the second move is unnecessary. But +// be careful! The movf instruction affects the 'Z' bit. +// So if this sequence is followed by a btfsc status,Z, you +// will have to leave the second move in. +// +// To get around this proble, the peep rule can be followed +// by a conditon: "if NZ". Which is to say, apply the rule +// if Z bit is not needed in the code that follows. The optimizer +// is smart enough to look more than one instruction past the +// target block... +// +// Special commands +// +// +// _NOTBITSKIP_ %1 - Creates a wild card instruction that +// will match all instructions except for +// bit skip instructions (btfsc or btfss) +// _BITSKIP_ %1 - Creates a wild card instruction that only +// will match a bit skip instruction (btfsc +// or btfss) +// _INVERTBITSKIP_ %1 - For the wild card instruction %1, invert +// the state of the bit skip. If %1 is not +// a bit skip instruction, then there's an +// error in the peep rule. +// +// +// + + +// Peep 1 +// Converts +// +// btfss reg1,bit +// goto label +// incf reg2,f +//label +// +// Into: +// +// btfsc reg1,bit +// incf reg2,f +//label +// +// Notice that wild cards will allow any instruction +// besides incf to be used in the above. +// +// Also, notice that this snippet is not valid if +// it follows another skip + + +replace restart { + _NOTBITSKIP_ %1 + _BITSKIP_ %2 + goto %3 + %4 +%3: %5 +} by { + ; peep 1 - test/jump to test/skip + %1 + _INVERTBITSKIP_ %2 + %4 +%3: %5 +} + +replace restart { + _NOTBITSKIP_ %1 + _BITSKIP_ %2 + goto %3 +%4: %5 +%3: %6 +} by { + ; peep 1b - test/jump to test/skip + %1 + _INVERTBITSKIP_ %2 +%4: %5 +%3: %6 +} + + +//bogus test for pcode +//replace restart { +// movf %1,w ;comment at end +//%4: movf %1,w +// RETURN +// clrf INDF0 +// movlw 0xa5 +// movf fsr0,w +// incf indf0,f +// %2 +//} by { +// ; peep test remove redundant move +//%4: movf %1,w ;another comment +// %2 +//} if AYBABTU %3 + + +// peep 2 +replace restart { + movwf %1 + movf %1,w +} by { + ; peep 2 - Removed redundant move + movwf %1 +} if NZ + +// peep 3 +//replace restart { +/ decf %1,f +/// movf %1,w +// btfss _STATUS,z +// goto %2 +//} by { +// ; peep 3 - decf/mov/skpz to decfsz +// decfsz %1,f +// goto %2 +//} + + +replace restart { + movf %1,w + movf %1,w +} by { + ; peep 4 - Removed redundant move + movf %1,w +} + + +replace restart { + movlw %1 + movwf %2 + movlw %1 +} by { + ; peep 5 - Removed redundant move + movlw %1 + movwf %2 +} + +replace restart { + movwf %1 + movwf %1 +} by { + ; peep 6 - Removed redundant move + movwf %1 +} + +replace restart { + movlw 0 + iorwf %1,w +} by { + ; peep 7 - Removed redundant move + movf %1,w +} + +replace restart { + movf %1,w + movwf %2 + decf %2,f +} by { + ; peep 8 - Removed redundant move + decf %1,w + movwf %2 +} + +replace restart { + movwf %1 + movf %2,w + xorwf %1,w +} by { + ; peep 9a - Removed redundant move + movwf %1 + xorwf %2,w +} + +replace restart { + movwf %1 + movf %2,w + iorwf %1,w +} by { + ; peep 9b - Removed redundant move + movwf %1 + iorwf %2,w +} + +replace restart { + movf %1,w + movwf %2 + movf %2,w +} by { + ; peep 9c - Removed redundant move + movf %1,w + movwf %2 +} + +replace restart { + movwf %1 + movf %1,w + movwf %2 +} by { + ; peep 9d - Removed redundant move + movwf %1 + movwf %2 +} if NZ + +// From: Frieder Ferlemann + +replace restart { + iorlw 0 +} by { + ; peep 10a - Removed unnecessary iorlw +} if NZ + +// From: Frieder Ferlemann + +replace restart { + xorlw 0 +} by { + ; peep 10b - Removed unnecessary xorlw +} if NZ + +// From: Frieder Ferlemann + +replace restart { + movf %1,w + movwf %1 +} by { + ; peep 11 - Removed redundant move + movf %1,w +} + +replace restart { + movf %1,w + movf %2,w +} by { + ; peep 12 - Removed redundant move + movf %2,w +} + +replace restart { + movf %1,w + xorlw %2 + bz %3 + bra %4 +%3: %5 +} by { + ; peep 101 - test for equality + movlw %2 + cpfseq %1 + bra %4 +%3: %5 +} diff --git a/src/pic16/pic16.vcxproj b/src/pic16/pic16.vcxproj new file mode 100644 index 0000000..8141b7f --- /dev/null +++ b/src/pic16/pic16.vcxproj @@ -0,0 +1,204 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7E09A25E-1C9D-438D-85DB-8535F134890D} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/pic16.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level2 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/pic16.bsc + + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/pic16.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/pic16.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + + \ No newline at end of file diff --git a/src/pic16/pic16.vcxproj.filters b/src/pic16/pic16.vcxproj.filters new file mode 100644 index 0000000..05bbe9c --- /dev/null +++ b/src/pic16/pic16.vcxproj.filters @@ -0,0 +1,85 @@ + + + + + {b613ea3d-3cff-4025-9b3e-8643f2bb4fe6} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {fc0b3ed3-ef96-43d3-a63c-b8e4551b40f6} + h;hpp;hxx;hm;inl + + + {6c387ed2-8c02-44f4-904c-683998af7386} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/pic16/ralloc.c b/src/pic16/ralloc.c new file mode 100644 index 0000000..879cc70 --- /dev/null +++ b/src/pic16/ralloc.c @@ -0,0 +1,4529 @@ +/*------------------------------------------------------------------------ + + ralloc.c - source file for register allocation. PIC16 specific + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + Added Pic Port T.scott Dattalo scott@dattalo.com (2000) + Added Pic16 Port Martin Dubuc m.dubuc@rogers.com (2002) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "ralloc.h" +#include "pcode.h" +#include "gen.h" +#include "device.h" + +#ifndef debugf +#define debugf(frm, rest) _debugf(__FILE__, __LINE__, frm, rest) +#endif +void _debugf(char *f, int l, char *frm, ...); + +#define NEWREG_DEBUG 0 +//#define USE_ONSTACK + + +/*-----------------------------------------------------------------*/ +/* At this point we start getting processor specific although */ +/* some routines are non-processor specific & can be reused when */ +/* targetting other processors. The decision for this will have */ +/* to be made on a routine by routine basis */ +/* routines used to pack registers are most definitely not reusable */ +/* since the pack the registers depending strictly on the MCU */ +/*-----------------------------------------------------------------*/ + +reg_info *pic16_typeRegWithIdx (int idx, int type, int fixed); +extern void genpic16Code (iCode *); + +/* Global data */ +static struct + { + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + short blockSpil; + int slocNum; + bitVect *funcrUsed; /* registers used in a function */ + int stackExtend; + int dataExtend; + } +_G; + +/* Shared with gen.c */ +int pic16_ptrRegReq; /* one byte pointer register required */ + + +set *pic16_dynAllocRegs=NULL; +set *pic16_dynStackRegs=NULL; +set *pic16_dynProcessorRegs=NULL; +set *pic16_dynDirectRegs=NULL; +set *pic16_dynDirectBitRegs=NULL; +set *pic16_dynInternalRegs=NULL; +set *pic16_dynAccessRegs=NULL; + +static hTab *dynDirectRegNames=NULL; +static hTab *dynAllocRegNames=NULL; +static hTab *dynProcRegNames=NULL; +static hTab *dynAccessRegNames=NULL; +//static hTab *regHash = NULL; /* a hash table containing ALL registers */ + +extern set *sectNames; + +set *pic16_rel_udata=NULL; /* relocatable uninitialized registers */ +set *pic16_fix_udata=NULL; /* absolute uninitialized registers */ +set *pic16_equ_data=NULL; /* registers used by equates */ +set *pic16_int_regs=NULL; /* internal registers placed in access bank 0 to 0x7f */ +set *pic16_acs_udata=NULL; /* access bank variables */ + +set *pic16_builtin_functions=NULL; + +static int dynrIdx=0x00; //0x20; // starting temporary register rIdx +static int rDirectIdx=0; + +int pic16_nRegs = 128; // = sizeof (regspic16) / sizeof (regs); + +int pic16_Gstack_base_addr=0; /* The starting address of registers that + * are used to pass and return parameters */ + + +int _inRegAllocator=0; /* flag that marks whther allocReg happens while + * inside the register allocator function */ + + +static void spillThis (symbol *); +int pic16_ralloc_debug = 0; +static FILE *debugF = NULL; +/*-----------------------------------------------------------------*/ +/* debugLog - open a file for debugging information */ +/*-----------------------------------------------------------------*/ +//static void debugLog(char *inst,char *fmt, ...) +static void +debugLog (const char *fmt,...) +{ + static int append = 0; // First time through, open the file without append. + + char buffer[256]; + //char *bufferP=buffer; + va_list ap; + + if (!pic16_ralloc_debug || !dstFileName) + return; + + + if (!debugF) + { + /* create the file name */ + SNPRINTF(buffer, sizeof(buffer), "%s.d", dstFileName); + + if (!(debugF = fopen (buffer, (append ? "a+" : "w")))) + { + werror (E_FILE_OPEN_ERR, buffer); + exit (1); + } + append = 1; // Next time debubLog is called, we'll append the debug info + } + + va_start (ap, fmt); + + vsprintf (buffer, fmt, ap); + + fprintf (debugF, "%s", buffer); + //fprintf (stderr, "%s", buffer); +/* + while (isspace((unsigned char)*bufferP)) bufferP++; + + if (bufferP && *bufferP) + lineCurr = (lineCurr ? + connectLine(lineCurr,newLineNode(lb)) : + (lineHead = newLineNode(lb))); + lineCurr->isInline = _G.inLine; + lineCurr->isDebug = _G.debugLine; + */ + va_end (ap); + +} + +static void +debugNewLine (void) +{ + if(!pic16_ralloc_debug)return; + + if (debugF) + fputc ('\n', debugF); +} +/*-----------------------------------------------------------------*/ +/* debugLogClose - closes the debug log file (if opened) */ +/*-----------------------------------------------------------------*/ +static void +debugLogClose (void) +{ + if (debugF) { + fclose (debugF); + debugF = NULL; + } +} + +#define AOP(op) op->aop + +static char * +debugAopGet (char *str, operand * op) +{ + if(!pic16_ralloc_debug)return NULL; + + if (str) + debugLog (str); + + printOperand (op, debugF); + debugNewLine (); + + return NULL; +} + +char * +pic16_decodeOp (unsigned int op) +{ + if (op < 128 && op > ' ') { + buffer[0] = op & 0xff; + buffer[1] = '\0'; + return buffer; + } + + switch (op) { + case IDENTIFIER: return "IDENTIFIER"; + case TYPE_NAME: return "TYPE_NAME"; + case CONSTANT: return "CONSTANT"; + case STRING_LITERAL: return "STRING_LITERAL"; + case SIZEOF: return "SIZEOF"; + case PTR_OP: return "PTR_OP"; + case INC_OP: return "INC_OP"; + case DEC_OP: return "DEC_OP"; + case LEFT_OP: return "LEFT_OP"; + case RIGHT_OP: return "RIGHT_OP"; + case LE_OP: return "LE_OP"; + case GE_OP: return "GE_OP"; + case EQ_OP: return "EQ_OP"; + case NE_OP: return "NE_OP"; + case AND_OP: return "AND_OP"; + case OR_OP: return "OR_OP"; + case MUL_ASSIGN: return "MUL_ASSIGN"; + case DIV_ASSIGN: return "DIV_ASSIGN"; + case MOD_ASSIGN: return "MOD_ASSIGN"; + case ADD_ASSIGN: return "ADD_ASSIGN"; + case SUB_ASSIGN: return "SUB_ASSIGN"; + case LEFT_ASSIGN: return "LEFT_ASSIGN"; + case RIGHT_ASSIGN: return "RIGHT_ASSIGN"; + case AND_ASSIGN: return "AND_ASSIGN"; + case XOR_ASSIGN: return "XOR_ASSIGN"; + case OR_ASSIGN: return "OR_ASSIGN"; + case TYPEDEF: return "TYPEDEF"; + case EXTERN: return "EXTERN"; + case STATIC: return "STATIC"; + case AUTO: return "AUTO"; + case REGISTER: return "REGISTER"; + case CODE: return "CODE"; + case EEPROM: return "EEPROM"; + case INTERRUPT: return "INTERRUPT"; + case SFR: return "SFR"; + case AT: return "AT"; + case SBIT: return "SBIT"; + case REENTRANT: return "REENTRANT"; + case USING: return "USING"; + case XDATA: return "XDATA"; + case DATA: return "DATA"; + case IDATA: return "IDATA"; + case PDATA: return "PDATA"; + case VAR_ARGS: return "VAR_ARGS"; + case CRITICAL: return "CRITICAL"; + case NONBANKED: return "NONBANKED"; + case BANKED: return "BANKED"; + case SD_CHAR: return "CHAR"; + case SD_SHORT: return "SHORT"; + case SD_INT: return "INT"; + case SD_LONG: return "LONG"; + case SIGNED: return "SIGNED"; + case UNSIGNED: return "UNSIGNED"; + case SD_FLOAT: return "FLOAT"; + case DOUBLE: return "DOUBLE"; + case SD_CONST: return "CONST"; + case VOLATILE: return "VOLATILE"; + case SD_VOID: return "VOID"; + case BIT: return "BIT"; + case STRUCT: return "STRUCT"; + case UNION: return "UNION"; + case ENUM: return "ENUM"; + case RANGE: return "RANGE"; + case SD_FAR: return "FAR"; + case CASE: return "CASE"; + case DEFAULT: return "DEFAULT"; + case IF: return "IF"; + case ELSE: return "ELSE"; + case SWITCH: return "SWITCH"; + case WHILE: return "WHILE"; + case DO: return "DO"; + case FOR: return "FOR"; + case GOTO: return "GOTO"; + case CONTINUE: return "CONTINUE"; + case BREAK: return "BREAK"; + case RETURN: return "RETURN"; + case INLINEASM: return "INLINEASM"; + case IFX: return "IFX"; + case ADDRESS_OF: return "ADDRESS_OF"; + case GET_VALUE_AT_ADDRESS: return "GET_VALUE_AT_ADDRESS"; + case SPIL: return "SPIL"; + case UNSPIL: return "UNSPIL"; + case GETHBIT: return "GETHBIT"; + case BITWISEAND: return "BITWISEAND"; + case UNARYMINUS: return "UNARYMINUS"; + case IPUSH: return "IPUSH"; + case IPOP: return "IPOP"; + case PCALL: return "PCALL"; + case FUNCTION: return "FUNCTION"; + case ENDFUNCTION: return "ENDFUNCTION"; + case JUMPTABLE: return "JUMPTABLE"; + case RRC: return "RRC"; + case RLC: return "RLC"; + case CAST: return "CAST"; + case CALL: return "CALL"; + case PARAM: return "PARAM "; + case NULLOP: return "NULLOP"; + case BLOCK: return "BLOCK"; + case LABEL: return "LABEL"; + case RECEIVE: return "RECEIVE"; + case SEND: return "SEND"; + case DUMMY_READ_VOLATILE: return "DUMMY_READ_VOLATILE"; + } + SNPRINTF(buffer, sizeof(buffer), "unknown op %d %c", op, op & 0xff); + + return buffer; +} + +#if 0 +static const char *decodeRegType(short type) +{ + switch(type) { + case REG_GPR: return "REG_GPR"; + case REG_PTR: return "REG_PTR"; + case REG_CND: return "REG_CNT"; + + default: + return ""; + } +} +#endif + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static const char * +debugLogRegType (short type) +{ + if(!pic16_ralloc_debug)return NULL; + switch (type) { + case REG_GPR: return "REG_GPR"; + case REG_PTR: return "REG_PTR"; + case REG_CND: return "REG_CND"; + } + SNPRINTF(buffer, sizeof(buffer), "unknown reg type %d", type); + + return buffer; +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +static int regname2key(char const *name) +{ + int key = 0; + + if(!name) + return 0; + + while(*name) { + key += (*name++) + 1; + } + + return ((key + (key >> 4) + (key>>8)) & 0x3f); +} + +/*-----------------------------------------------------------------*/ +/* newReg - allocate and init memory for a new register */ +/*-----------------------------------------------------------------*/ +reg_info* newReg(int type, short pc_type, int rIdx, const char *name, unsigned size, int alias, operand *refop) +{ + + reg_info *dReg; + + dReg = Safe_alloc(sizeof(reg_info)); + dReg->type = type; + dReg->pc_type = pc_type; + dReg->rIdx = rIdx; + if(name) + dReg->name = Safe_strdup(name); + else { + if(pic16_options.xinst && pc_type == PO_GPR_TEMP) { + SNPRINTF(buffer, sizeof(buffer), "0x%02x", dReg->rIdx); + } else { + SNPRINTF(buffer, sizeof(buffer), "r0x%02x", dReg->rIdx); + } + + if(type == REG_STK) { + *buffer = 's'; + } + dReg->name = Safe_strdup(buffer); + } + + + dReg->isFree = 0; + dReg->wasUsed = 1; + dReg->isEmitted = 0; + + if(type == REG_SFR) { + dReg->isFixed = 1; + dReg->address = rIdx; + dReg->accessBank = 1; + } else { + dReg->isFixed = 0; + dReg->address = 0; + dReg->accessBank = 0; + } + +#if NEWREG_DEBUG + fprintf(stderr,"newReg @ %p: %s, rIdx = 0x%02x\taccess= %d\tregop= %p\n",dReg, dReg->name,rIdx, dReg->accessBank, refop); +#endif + dReg->size = size; + dReg->alias = alias; + dReg->reg_alias = NULL; + dReg->reglives.usedpFlows = newSet(); + dReg->reglives.assignedpFlows = newSet(); + dReg->regop = refop; + + if(!(type == REG_SFR && alias == 0x80)) + hTabAddItem(&dynDirectRegNames, regname2key(dReg->name), dReg); + + return dReg; +} + +/*-----------------------------------------------------------------*/ +/* regWithIdx - Search through a set of registers that matches idx */ +/*-----------------------------------------------------------------*/ +static reg_info * +regWithIdx (set *dRegs, int idx, unsigned fixed) +{ + reg_info *dReg; + +//#define D(text) text +#define D(text) + + for (dReg = setFirstItem(dRegs) ; dReg ; + dReg = setNextItem(dRegs)) { + + D(fprintf(stderr, "%s:%d testing reg w/rIdx = %d (%d f:%d)\t", __FUNCTION__, __LINE__, dReg->rIdx, idx, fixed)); + if(idx == dReg->rIdx && (fixed == dReg->isFixed)) { + D(fprintf(stderr, "found!\n")); + return dReg; + } else + D(fprintf(stderr, "not found!\n")); + } + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* regFindFree - Search for a free register in a set of registers */ +/*-----------------------------------------------------------------*/ +static reg_info * +regFindFree (set *dRegs) +{ + reg_info *dReg; + + for (dReg = setFirstItem(dRegs) ; dReg ; + dReg = setNextItem(dRegs)) { + +// fprintf(stderr, "%s:%d checking register %s (%p) [rIdx: 0x%02x] if free= %d\n", +// __FILE__, __LINE__, dReg->name, dReg, dReg->rIdx, dReg->isFree); + + if(dReg->isFree) { +// fprintf(stderr, "%s:%d free register found, rIdx = %d\n", __FILE__, __LINE__, dReg->rIdx); + + return dReg; + } + } + + return NULL; +} + +static reg_info * +regFindFreeNext(set *dRegs, reg_info *creg) +{ + reg_info *dReg; + + if(creg) { + /* position at current register */ + for(dReg = setFirstItem(dRegs); dReg != creg; dReg = setNextItem(dRegs)); + } + + for(dReg = setNextItem(dRegs); dReg; dReg = setNextItem(dRegs)) { + if(dReg->isFree) { + return dReg; + } + } + + return NULL; +} + +/*-----------------------------------------------------------------* + *-----------------------------------------------------------------*/ +reg_info * +pic16_allocProcessorRegister(int rIdx, const char * name, short po_type, int alias) +{ + reg_info *reg = newReg(REG_SFR, po_type, rIdx, name, 1, alias, NULL); + +// fprintf(stderr,"%s: %s addr =0x%x\n",__FUNCTION__, name,rIdx); + + reg->wasUsed = 0; // we do not know if they are going to be used at all + reg->accessBank = 1; // implicit add access Bank + + hTabAddItem(&dynProcRegNames, regname2key(reg->name), reg); + + return addSet(&pic16_dynProcessorRegs, reg); +} + +/*-----------------------------------------------------------------* + *-----------------------------------------------------------------*/ + +reg_info * +pic16_allocInternalRegister(int rIdx, const char *name, short po_type, int alias) +{ + reg_info * reg = newReg(REG_GPR, po_type, rIdx, name, 1, alias, NULL); + +// fprintf(stderr,"%s:%d: %s %s addr =0x%x\n",__FILE__, __LINE__, __FUNCTION__, name, rIdx); + + if(reg) { + reg->wasUsed = 0; + return addSet(&pic16_dynInternalRegs, reg); + } + + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* allocReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +static reg_info * +allocReg (short type) +{ + reg_info * reg=NULL; + +#define MAX_P16_NREGS 16 + + +#if 0 + if(dynrIdx > pic16_nRegs) + werror(W_POSSBUG2, __FILE__, __LINE__); + return NULL; +#endif + + /* try to reuse some unused registers */ + reg = regFindFree( pic16_dynAllocRegs ); + + if(reg) { +// fprintf(stderr, "%s: [%s][cf:%p] found FREE register %s, rIdx: %d\n", __FILE__, (_inRegAllocator)?"ralloc":"", currFunc, reg->name, reg->rIdx); + } + + if(!reg) { + reg = newReg(REG_GPR, PO_GPR_TEMP, dynrIdx++, NULL, 1, 0, NULL); +// fprintf(stderr, "%s [%s][cf:%p] allocating NEW register %s, rIdx: %d\n", __FILE__, +// (_inRegAllocator)?"ralloc":"", currFunc, reg->name, reg->rIdx); + +#if 1 + if(_inRegAllocator && (dynrIdx > MAX_P16_NREGS)) { + // debugf("allocating more registers than available\n", 0); + // return (NULL); + } + + addSet(&pic16_dynAllocRegs, reg); + hTabAddItem(&dynAllocRegNames, regname2key(reg->name), reg); +// fprintf(stderr, "%s:%d added reg to pic16_dynAllocRegs = %p\n", __FUNCTION__, __LINE__, pic16_dynAllocRegs); +#endif + } + + debugLog ("%s of type %s for register rIdx: %d (0x%x)\n", __FUNCTION__, debugLogRegType (type), dynrIdx-1, dynrIdx-1); + +#if 0 + fprintf(stderr,"%s:%d: %s\t%s addr= 0x%x\trIdx= 0x%02x isFree: %d\n", + __FILE__, __LINE__, __FUNCTION__, reg->name, reg->address, reg->rIdx, reg->isFree); +#endif + if(reg) { + reg->isFree=0; + reg->accessBank = 1; /* this is a temporary register alloc in accessBank */ + reg->isLocal = 1; /* this is a local frame register */ +// reg->wasUsed = 1; + } + + if (currFunc) { +// fprintf(stderr, "%s:%d adding %s into function %s regsUsed\n", __FUNCTION__, __LINE__, reg->name, currFunc->name); + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, reg->rIdx); + } + + return (reg); // addSet(&pic16_dynAllocRegs,reg); + +} + + +/*-----------------------------------------------------------------*/ +/* pic16_dirregWithName - search for register by name */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_dirregWithName(const char *name) +{ + int hkey; + reg_info *reg; + + if(!name) + return NULL; + + /* hash the name to get a key */ + + hkey = regname2key(name); + +// fprintf(stderr, "%s:%d: name = %s\thash = %d\n", __FUNCTION__, __LINE__, name, hkey); + + reg = hTabFirstItemWK(dynDirectRegNames, hkey); + + while(reg) { + + if(STRCASECMP(reg->name, name) == 0) { +// fprintf(stderr, "%s:%d: FOUND name = %s\thash = %d\n", __FUNCTION__, __LINE__, reg->name, hkey); + return(reg); + } + + reg = hTabNextItemWK (dynDirectRegNames); + + } + + return NULL; // name wasn't found in the hash table +} + +/*-----------------------------------------------------------------*/ +/* pic16_allocregWithName - search for register by name */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_allocregWithName(const char *name) +{ + int hkey; + reg_info *reg; + + if(!name) + return NULL; + + /* hash the name to get a key */ + + hkey = regname2key(name); + + //fprintf(stderr, "%s:%d: name = %s\thash = %d\n", __FUNCTION__, __LINE__, name, hkey); + + reg = hTabFirstItemWK(dynAllocRegNames, hkey); + + while(reg) { + + if(STRCASECMP(reg->name, name) == 0) { + return(reg); + } + + reg = hTabNextItemWK (dynAllocRegNames); + + } + + return NULL; // name wasn't found in the hash table + +} + + +/*-----------------------------------------------------------------*/ +/* pic16_procregWithName - search for register by name */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_procregWithName(const char *name) +{ + int hkey; + reg_info *reg; + + if(!name) + return NULL; + + /* hash the name to get a key */ + + hkey = regname2key(name); + +// fprintf(stderr, "%s:%d: name = %s\thash = %d\n", __FUNCTION__, __LINE__, name, hkey); + + reg = hTabFirstItemWK(dynProcRegNames, hkey); + + while(reg) { + + if(STRCASECMP(reg->name, name) == 0) { + return(reg); + } + + reg = hTabNextItemWK (dynProcRegNames); + + } + + return NULL; // name wasn't found in the hash table + +} + +/*-----------------------------------------------------------------*/ +/* pic16_accessregWithName - search for register by name */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_accessregWithName(const char *name) +{ + int hkey; + reg_info *reg; + + if(!name) + return NULL; + + /* hash the name to get a key */ + + hkey = regname2key(name); + +// fprintf(stderr, "%s:%d: name = %s\thash = %d\n", __FUNCTION__, __LINE__, name, hkey); + + reg = hTabFirstItemWK(dynAccessRegNames, hkey); + + while(reg) { + + if(STRCASECMP(reg->name, name) == 0) { + return(reg); + } + + reg = hTabNextItemWK (dynAccessRegNames); + + } + + return NULL; // name wasn't found in the hash table + +} + +reg_info *pic16_regWithName(const char *name) +{ + reg_info *reg; + + reg = pic16_dirregWithName( name ); + if(reg)return reg; + + reg = pic16_procregWithName( name ); + if(reg)return reg; + + reg = pic16_allocregWithName( name ); + if(reg)return reg; + + reg = pic16_accessregWithName( name ); + if(reg)return reg; + + return NULL; +} + + +/*-----------------------------------------------------------------*/ +/* pic16_allocDirReg - allocates register of given type */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_allocDirReg (operand *op ) +{ + reg_info *reg; + char *name; + + if(!IS_SYMOP(op)) { + debugLog ("%s BAD, op is NULL\n", __FUNCTION__); +// fprintf(stderr, "%s BAD, op is NULL\n", __FUNCTION__); + return NULL; + } + + name = OP_SYMBOL (op)->rname[0] ? OP_SYMBOL (op)->rname : OP_SYMBOL (op)->name; + + + if(!SPEC_OCLS( OP_SYM_ETYPE(op))) { +#if 0 + if(pic16_debug_verbose) + { + fprintf(stderr, "%s:%d symbol %s(r:%s) is not assigned to a memmap\n", __FILE__, __LINE__, + OP_SYMBOL(op)->name, OP_SYMBOL(op)->rname); + } +#endif + return NULL; + } + + if(!IN_DIRSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))) + || !IN_FARSPACE(SPEC_OCLS( OP_SYM_ETYPE(op))) ) { + +#if 0 + if(pic16_debug_verbose) { + fprintf(stderr, "dispace:%d farspace:%d codespace:%d regspace:%d stack:%d eeprom: %d regparm: %d isparm: %d\n", + IN_DIRSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_FARSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_CODESPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_REGSP( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_STACK( OP_SYM_ETYPE(op)), + SPEC_OCLS(OP_SYM_ETYPE(op)) == eeprom, + IS_REGPARM(OP_SYM_ETYPE(op)), + IS_PARM(op)); + + fprintf(stderr, "%s:%d symbol %s NOT in dirspace\n", __FILE__, __LINE__, + OP_SYMBOL(op)->name); + } +#endif + + } + + + + if (IS_CODE ( OP_SYM_ETYPE(op)) ) { +// fprintf(stderr, "%s:%d sym: %s in codespace\n", __FUNCTION__, __LINE__, OP_SYMBOL(op)->name); + return NULL; + } + + if(IS_ITEMP(op))return NULL; + +// if(IS_STATIC(OP_SYM_ETYPE(op)))return NULL; + + if(IN_STACK(OP_SYM_ETYPE(op)))return NULL; + + debugLog ("%s:%d symbol name %s\n", __FUNCTION__, __LINE__, name); +// fprintf(stderr, "%s symbol name %s\tSTATIC:%d\n", __FUNCTION__,name, IS_STATIC(OP_SYM_ETYPE(op))); + + { + if(SPEC_CONST ( OP_SYM_ETYPE(op)) && (IS_CHAR ( OP_SYM_ETYPE(op)) )) { + debugLog(" %d const char\n",__LINE__); + debugLog(" value = %s \n",SPEC_CVAL( OP_SYM_ETYPE(op))); +// fprintf(stderr, " %d const char\n",__LINE__); +// fprintf(stderr, " value = %s \n",SPEC_CVAL( OP_SYM_ETYPE(op))); + } + + + debugLog(" %d storage class %d \n",__LINE__,SPEC_SCLS( OP_SYM_ETYPE(op))); + if (IS_CODE ( OP_SYM_ETYPE(op)) ) + debugLog(" %d code space\n",__LINE__); + + if (IS_INTEGRAL ( OP_SYM_ETYPE(op)) ) + debugLog(" %d integral\n",__LINE__); + + if (IS_LITERAL ( OP_SYM_ETYPE(op)) ) + debugLog(" %d literal\n",__LINE__); + + if (IS_SPEC ( OP_SYM_ETYPE(op)) ) + debugLog(" %d specifier\n",__LINE__); + + debugAopGet(NULL, op); + } + + + reg = pic16_dirregWithName(name); + + if(!reg) { + int regtype = REG_GPR; + + /* if this is at an absolute address, then get the address. */ + if (0 && SPEC_ABSA ( OP_SYM_ETYPE(op)) ) + { + int address = 0; + address = SPEC_ADDR ( OP_SYM_ETYPE(op)); + fprintf(stderr,"reg %s is at an absolute address: 0x%03x\n",name,address); + } + + /* Register wasn't found in hash, so let's create + * a new one and put it in the hash table AND in the + * dynDirectRegNames set */ + if(IS_CODE(OP_SYM_ETYPE(op)) || IN_CODESPACE( SPEC_OCLS( OP_SYM_ETYPE(op)))) { + debugLog("%s:%d sym: %s in codespace\n", __FUNCTION__, __LINE__, OP_SYMBOL(op)->name); + return NULL; + } + + +#if 0 + if(OP_SYMBOL(op)->onStack) { + fprintf(stderr, "%s:%d onStack %s offset: %d\n", __FILE__, __LINE__, + OP_SYMBOL(op)->name, OP_SYMBOL(op)->stack); + } +#endif + + if(!IN_DIRSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))) + || !IN_FARSPACE(SPEC_OCLS( OP_SYM_ETYPE(op))) ) { + +#if 0 + if(pic16_debug_verbose) + { + fprintf(stderr, "dispace:%d farspace:%d codespace:%d regspace:%d stack:%d eeprom: %d\n", + IN_DIRSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_FARSPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_CODESPACE( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_REGSP( SPEC_OCLS( OP_SYM_ETYPE(op))), + IN_STACK( OP_SYM_ETYPE(op)), + SPEC_OCLS(OP_SYM_ETYPE(op)) == eeprom); + + fprintf(stderr, "%s:%d symbol %s NOT in dirspace\n", __FILE__, __LINE__, + OP_SYMBOL(op)->name); + } +#endif + } + + reg = newReg(regtype, PO_DIR, rDirectIdx++, name,getSize (OP_SYMBOL (op)->type),0, op); + debugLog ("%d -- added %s to hash, size = %d\n", __LINE__, name,reg->size); + + if( SPEC_SCLS( OP_SYM_ETYPE( op ) ) == S_REGISTER ) { + fprintf(stderr, "%s:%d symbol %s is declared as register\n", __FILE__, __LINE__, + name); + + reg->accessBank = 1; + checkAddReg(&pic16_dynAccessRegs, reg); + hTabAddItem(&dynAccessRegNames, regname2key(name), reg); + + return (reg); + } + + +// if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { +// fprintf(stderr, " ralloc.c at fixed address: %s - changing to REG_SFR\n",name); +// reg->type = REG_SFR; +// } + + if (IS_BITVAR (OP_SYM_ETYPE(op))) { +// fprintf(stderr, "%s:%d adding %s in bit registers\n", __FILE__, __LINE__, reg->name); + addSet(&pic16_dynDirectBitRegs, reg); + reg->isBitField = 1; + } else { +// fprintf(stderr, "%s:%d adding %s in direct registers\n", __FILE__, __LINE__, reg->name); +// addSet(&pic16_dynDirectRegs, reg); + +#if 1 + if(!(IS_STATIC(OP_SYM_ETYPE(op)) + && OP_SYMBOL(op)->ival + )) +#endif + checkAddReg(&pic16_dynDirectRegs, reg); + } + + } else { +// debugLog (" -- %s is declared at address 0x30000x\n",name); + return (reg); /* This was NULL before, but since we found it + * why not just return it?! */ + } + + if (SPEC_ABSA ( OP_SYM_ETYPE(op)) ) { + reg->isFixed = 1; + reg->address = SPEC_ADDR ( OP_SYM_ETYPE(op)); + + /* work around for user defined registers in access bank */ + if((reg->address>= 0x00 && reg->address < pic16->acsSplitOfs) + || (reg->address >= (0xf00 + pic16->acsSplitOfs) && reg->address <= 0xfff)) + reg->accessBank = 1; + + debugLog (" -- and it is at a fixed address 0x%02x\n",reg->address); + } + + return reg; +} + +/*-----------------------------------------------------------------*/ +/* pic16_allocRegByName - allocates register of given type */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_allocRegByName(const char *name, int size, operand *op) +{ + + reg_info *reg; + + if(!name) { + fprintf(stderr, "%s - allocating a NULL register\n",__FUNCTION__); + exit(1); + } + + /* First, search the hash table to see if there is a register with this name */ + reg = pic16_dirregWithName(name); + + if(!reg) { + + /* Register wasn't found in hash, so let's create + * a new one and put it in the hash table AND in the + * dynDirectRegNames set */ + + //fprintf (stderr,"%s:%d symbol name %s\tregop= %p\n", __FUNCTION__, __LINE__, name, op); + + reg = newReg(REG_GPR, PO_DIR, rDirectIdx++, name,size,0, op); + + debugLog ("%d -- added %s to hash, size = %d\n", __LINE__, name,reg->size); + //fprintf(stderr, " -- added %s to hash, size = %d\n", name,reg->size); + + //hTabAddItem(&dynDirectRegNames, regname2key(name), reg); /* initially commented out */ + addSet(&pic16_dynDirectRegs, reg); + } + + return reg; +} + +/*-----------------------------------------------------------------*/ +/* RegWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info *pic16_typeRegWithIdx (int idx, int type, int fixed) +{ + + reg_info *dReg; + + debugLog ("%s - requesting index = 0x%x\n", __FUNCTION__,idx); +// fprintf(stderr, "%s - requesting index = 0x%x (type = %d [%s])\n", __FUNCTION__, idx, type, decodeRegType(type)); + + switch (type) { + + case REG_GPR: + if( (dReg = regWithIdx ( pic16_dynAllocRegs, idx, fixed)) != NULL) { + + debugLog ("Found a Dynamic Register!\n"); + return dReg; + } + if( (dReg = regWithIdx ( pic16_dynDirectRegs, idx, fixed)) != NULL ) { + debugLog ("Found a Direct Register!\n"); + return dReg; + } + + if( (dReg = regWithIdx ( pic16_dynInternalRegs, idx, fixed)) != NULL ) { + debugLog ("Found an Internal Register!\n"); + return dReg; + } + + break; + case REG_STK: + if( (dReg = regWithIdx ( pic16_dynStackRegs, idx, fixed)) != NULL ) { + debugLog ("Found a Stack Register!\n"); + return dReg; + } + break; + case REG_SFR: + if( (dReg = regWithIdx ( pic16_dynProcessorRegs, idx, 1)) != NULL ) { + debugLog ("Found a Processor Register!\n"); + return dReg; + } + + case REG_CND: + case REG_PTR: + default: + break; + } + + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_regWithIdx - returns pointer to register with index number*/ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_regWithIdx (int idx) +{ + reg_info *dReg; + + if( (dReg = pic16_typeRegWithIdx(idx,REG_GPR,0)) != NULL) + return dReg; + + if( (dReg = pic16_typeRegWithIdx(idx,REG_SFR,0)) != NULL) + return dReg; + +#if 0 + if( (dReg = pic16_typeRegWithIdx(idx,REG_STK,0)) != NULL) + return dReg; +#endif + + return NULL; +} + +/*-----------------------------------------------------------------*/ +/* pic16_regWithIdx - returns pointer to register with index number */ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_allocWithIdx (int idx) +{ + + reg_info *dReg=NULL; + + debugLog ("%s - allocating with index = 0x%x\n", __FUNCTION__,idx); +// fprintf(stderr, "%s - allocating with index = 0x%x\n", __FUNCTION__,idx); + + if( (dReg = regWithIdx ( pic16_dynAllocRegs, idx,0)) != NULL) { + + debugLog ("Found a Dynamic Register!\n"); + } else if( (dReg = regWithIdx ( pic16_dynStackRegs, idx,0)) != NULL ) { + debugLog ("Found a Stack Register!\n"); + } else if( (dReg = regWithIdx ( pic16_dynProcessorRegs, idx,1)) != NULL ) { + debugLog ("Found a Processor Register!\n"); + fprintf(stderr, "Found a processor register! %s\n", dReg->name); + } else if( (dReg = regWithIdx ( pic16_dynInternalRegs, idx,0)) != NULL ) { + debugLog ("Found an Internal Register!\n"); + } else { + + debugLog ("Dynamic Register not found\n"); + +#if 1 + dReg = newReg(REG_GPR, PO_GPR_TEMP, idx, NULL, 1, 0, NULL); + addSet(&pic16_dynAllocRegs, dReg); + hTabAddItem(&dynAllocRegNames, regname2key(dReg->name), dReg); +#endif + + if(!dReg) { +// return (NULL); + //fprintf(stderr,"%s %d - requested register: 0x%x\n",__FUNCTION__,__LINE__,idx); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "allocWithIdx not found"); + exit (1); + } + } + + dReg->wasUsed = 1; + dReg->isFree = 0; + + return dReg; +} +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +reg_info * +pic16_findFreeReg(short type) +{ + // int i; + reg_info* dReg; + + switch (type) { + case REG_GPR: + if((dReg = regFindFree(pic16_dynAllocRegs)) != NULL) + return dReg; +// return (addSet(&pic16_dynAllocRegs,newReg(REG_GPR, PO_GPR_TEMP,dynrIdx++,NULL,1,0, NULL))); + return allocReg( REG_GPR ); + + case REG_STK: + + if((dReg = regFindFree(pic16_dynStackRegs)) != NULL) + return dReg; + + return NULL; + + case REG_PTR: + case REG_CND: + case REG_SFR: + default: + return NULL; + } +} + +reg_info * +pic16_findFreeRegNext(short type, reg_info *creg) +{ + // int i; + reg_info* dReg; + + switch (type) { + case REG_GPR: + if((dReg = regFindFreeNext(pic16_dynAllocRegs, creg)) != NULL) + return dReg; +// return (addSet(&pic16_dynAllocRegs,newReg(REG_GPR, PO_GPR_TEMP,dynrIdx++,NULL,1,0, NULL))); + return (allocReg( REG_GPR ) ); + + case REG_STK: + + if((dReg = regFindFreeNext(pic16_dynStackRegs, creg)) != NULL) + return dReg; + + return NULL; + + case REG_PTR: + case REG_CND: + case REG_SFR: + default: + return NULL; + } +} +/*-----------------------------------------------------------------*/ +/* freeReg - frees a register */ +/*-----------------------------------------------------------------*/ +static void +freeReg (reg_info * reg) +{ + debugLog ("%s\n", __FUNCTION__); +// fprintf(stderr, "%s:%d register %s (%p) is freed\n", __FILE__, __LINE__, reg->name, reg); + reg->isFree = 1; +} + + +/*-----------------------------------------------------------------*/ +/* nFreeRegs - returns number of free registers */ +/*-----------------------------------------------------------------*/ +static int +nFreeRegs (int type) +{ + reg_info *reg; + int nfr=0; + + + /* although I fixed the register allocation/freeing scheme + * the for loop below doesn't give valid results. I do not + * know why yet. -- VR 10-Jan-2003 */ + + return 100; + + + /* dynamically allocate as many as we need and worry about + * fitting them into a PIC later */ + + debugLog ("%s\n", __FUNCTION__); + + for(reg = setFirstItem(pic16_dynAllocRegs); reg; reg=setNextItem(pic16_dynAllocRegs)) + if((reg->type == type) && reg->isFree)nfr++; + + fprintf(stderr, "%s:%d # of free registers= %d\n", __FILE__, __LINE__, nfr); + return nfr; +} + +/*-----------------------------------------------------------------*/ +/* nfreeRegsType - free registers with type */ +/*-----------------------------------------------------------------*/ +static int +nfreeRegsType (int type) +{ + int nfr; + debugLog ("%s\n", __FUNCTION__); + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + return nFreeRegs (REG_GPR); + } + + return nFreeRegs (type); +} +#if 0 +static void writeSetUsedRegs(FILE *of, set *dRegs) +{ + + reg_info *dReg; + + for (dReg = setFirstItem(dRegs) ; dReg ; + dReg = setNextItem(dRegs)) { + + if(dReg->wasUsed) + fprintf (of, "\t%s\n",dReg->name); + } + +} +#endif + +extern void pic16_groupRegistersInSection(set *regset); + +extern void pic16_dump_equates(FILE *of, set *equs); +extern void pic16_dump_access(FILE *of, set *section); +//extern void pic16_dump_map(void); +extern void pic16_dump_usection(FILE *of, set *section, int fix); +extern void pic16_dump_isection(FILE *of, set *section, int fix); +extern void pic16_dump_int_registers(FILE *of, set *section); +extern void pic16_dump_idata(FILE *of, set *idataSymSet); + +extern void pic16_dump_gsection(FILE *of, set *sections); + +static void packBits(set *bregs) +{ + set *regset; + reg_info *breg; + reg_info *bitfield=NULL; + reg_info *relocbitfield=NULL; + int bit_no=0; + int byte_no=-1; + char buffer[20]; + + + for (regset = bregs ; regset ; + regset = regset->next) { + + breg = regset->item; + breg->isBitField = 1; + //fprintf(stderr,"bit reg: %s\n",breg->name); + + if(breg->isFixed) { + //fprintf(stderr,"packing bit at fixed address = 0x%03x\n",breg->address); + + bitfield = pic16_typeRegWithIdx (breg->address >> 3, -1 , 1); + breg->rIdx = breg->address & 7; + breg->address >>= 3; + + if(!bitfield) { + SNPRINTF(buffer, sizeof(buffer), "fbitfield%02x", breg->address); + //fprintf(stderr,"new bit field\n"); + bitfield = newReg(REG_SFR, PO_GPR_BIT,breg->address,buffer,1,0, NULL); + bitfield->isBitField = 1; + bitfield->isFixed = 1; + bitfield->address = breg->address; + addSet(&pic16_dynDirectRegs,bitfield); + //hTabAddItem(&dynDirectRegNames, regname2key(buffer), bitfield); + } else { + //fprintf(stderr," which is occupied by %s (addr = %d)\n",bitfield->name,bitfield->address); + ; + } + breg->reg_alias = bitfield; + bitfield = NULL; + + } else { + if(!relocbitfield || bit_no >7) { + byte_no++; + bit_no=0; + SNPRINTF(buffer, sizeof(buffer), "bitfield%d", byte_no); + //fprintf(stderr,"new relocatable bit field\n"); + relocbitfield = newReg(REG_GPR, PO_GPR_BIT,rDirectIdx++,buffer,1,0, NULL); + relocbitfield->isBitField = 1; + addSet(&pic16_dynDirectRegs,relocbitfield); + //hTabAddItem(&dynDirectRegNames, regname2key(buffer), relocbitfield); + + } + + breg->reg_alias = relocbitfield; + breg->address = rDirectIdx; /* byte_no; */ + breg->rIdx = bit_no++; + } + } + +} + +void pic16_writeUsedRegs(FILE *of) +{ + packBits(pic16_dynDirectBitRegs); + + pic16_groupRegistersInSection(pic16_dynAllocRegs); + pic16_groupRegistersInSection(pic16_dynInternalRegs); + pic16_groupRegistersInSection(pic16_dynStackRegs); + pic16_groupRegistersInSection(pic16_dynDirectRegs); + pic16_groupRegistersInSection(pic16_dynDirectBitRegs); + pic16_groupRegistersInSection(pic16_dynProcessorRegs); + pic16_groupRegistersInSection(pic16_dynAccessRegs); + + /* dump equates */ + pic16_dump_equates(of, pic16_equ_data); + +// pic16_dump_esection(of, pic16_rel_eedata, 0); +// pic16_dump_esection(of, pic16_fix_eedata, 0); + + /* dump access bank symbols */ + pic16_dump_access(of, pic16_acs_udata); + + /* dump initialised data */ + pic16_dump_isection(of, rel_idataSymSet, 0); + pic16_dump_isection(of, fix_idataSymSet, 1); + + if(!pic16_options.xinst) { + /* dump internal registers */ + pic16_dump_int_registers(of, pic16_int_regs); + } + + /* dump generic section variables */ + pic16_dump_gsection(of, sectNames); + + /* dump other variables */ + pic16_dump_usection(of, pic16_rel_udata, 0); + pic16_dump_usection(of, pic16_fix_udata, 1); +} + + +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + debugLog ("%s\n", __FUNCTION__); + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = + bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = + bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + return spillable; + +} + +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return (SYM_SPIL_LOC (sym) ? 0 : 1); +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return (SYM_SPIL_LOC (sym) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* directSpilLoc - will return 1 if the splilocation is in direct */ +/*-----------------------------------------------------------------*/ +static int +directSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + if (SYM_SPIL_LOC (sym) && + (IN_DIRSPACE (SPEC_OCLS (SYM_SPIL_LOC (sym)->etype)))) + return 1; + else + return 0; +} + +/*-----------------------------------------------------------------*/ +/* hasSpilLocnoUptr - will return 1 if the symbol has spil location */ +/* but is not used as a pointer */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLocnoUptr (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return ((SYM_SPIL_LOC (sym) && !sym->uptr) ? 1 : 0); +} + +/*-----------------------------------------------------------------*/ +/* rematable - will return 1 if the remat flag is set */ +/*-----------------------------------------------------------------*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return sym->remat; +} + +/*-----------------------------------------------------------------*/ +/* notUsedInRemaining - not used or defined in remain of the block */ +/*-----------------------------------------------------------------*/ +static int +notUsedInRemaining (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return ((usedInRemaining (operandFromSymbol (sym), ic) ? 0 : 1) && + allDefsOutOfRange (sym->defs, ebp->fSeq, ebp->lSeq)); +} + +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + debugLog ("%s\n", __FUNCTION__); + return 1; +} + +/*-----------------------------------------------------------------*/ +/* liveRangesWith - applies function to a given set of live range */ +/*-----------------------------------------------------------------*/ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), + eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + debugLog ("%s\n", __FUNCTION__); + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + addSetHead (&rset, sym); + } + + return rset; +} + + +/*-----------------------------------------------------------------*/ +/* leastUsedLR - given a set determines which is the least used */ +/*-----------------------------------------------------------------*/ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + debugLog ("%s\n", __FUNCTION__); + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + + /* if usage is the same then prefer + the spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} + +/*-----------------------------------------------------------------*/ +/* noOverLap - will iterate through the list looking for over lap */ +/*-----------------------------------------------------------------*/ +static int +noOverLap (set * itmpStack, symbol * fsym) +{ + symbol *sym; + debugLog ("%s\n", __FUNCTION__); + + + for (sym = setFirstItem (itmpStack); sym; + sym = setNextItem (itmpStack)) + { + if (sym->liveTo > fsym->liveFrom) + return 0; + + } + + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + debugLog ("%s\n", __FUNCTION__); + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && + noOverLap (sym->usl.itmpStack, fsym) && + getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* spillLRWithPtrReg :- will spil those live ranges which use PTR */ +/*-----------------------------------------------------------------*/ +static void +spillLRWithPtrReg (symbol * forSym) +{ + symbol *lrsym; + reg_info *r0, *r1; + int k; + + debugLog ("%s\n", __FUNCTION__); + if (!_G.regAssigned || + bitVectIsZero (_G.regAssigned)) + return; + + r0 = pic16_regWithIdx (R0_IDX); + r1 = pic16_regWithIdx (R1_IDX); + + /* for all live ranges */ + for (lrsym = hTabFirstItem (liveRanges, &k); lrsym; + lrsym = hTabNextItem (liveRanges, &k)) + { + int j; + + /* if no registers assigned to it or + spilt */ + /* if it does not overlap with this then + not need to spill it */ + + if (lrsym->isspilt || !lrsym->nRegs || + (lrsym->liveTo < forSym->liveFrom)) + continue; + + /* go thru the registers : if it is either + r0 or r1 then spil it */ + for (j = 0; j < lrsym->nRegs; j++) + if (lrsym->regs[j] == r0 || + lrsym->regs[j] == r1) + { + spillThis (lrsym); + break; + } + } + +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + int useXstack, model, noOverlay; + char slocBuffer[120]; + + debugLog ("%s\n", __FUNCTION__); + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym)) + { + /* found a free one : just update & return */ + SYM_SPIL_LOC (sym) = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + return sym; + } + + SNPRINTF(slocBuffer, sizeof(slocBuffer), "sloc%d", _G.slocNum++); + sloc = newiTemp(slocBuffer); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_DATA; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + + /* we don't allow it to be allocated` + onto the external stack since : so we + temporarily turn it off ; we also + turn off memory model to prevent + the spil from going to the external storage + and turn off overlaying + */ + + useXstack = options.useXstack; + model = options.model; + noOverlay = options.noOverlay; + options.noOverlay = 1; + options.model = options.useXstack = 0; + + allocLocal (sloc); + + options.useXstack = useXstack; + options.model = model; + options.noOverlay = noOverlay; + sloc->isref = 1; /* to prevent compiler warning */ + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + _G.dataExtend += getSize (sloc->type); + + /* add it to the _G.stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + SYM_SPIL_LOC (sym) = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* isSpiltOnStack - returns true if the spil location is on stack */ +/*-----------------------------------------------------------------*/ +static bool +isSpiltOnStack (symbol * sym) +{ + sym_link *etype; + + debugLog ("%s\n", __FUNCTION__); + if (!sym) + return FALSE; + + if (!sym->isspilt) + return FALSE; + +/* if (sym->_G.stackSpil) */ +/* return TRUE; */ + + if (!SYM_SPIL_LOC (sym)) + return FALSE; + + etype = getSpec (SYM_SPIL_LOC (sym)->type); + if (IN_STACK (etype)) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +static void +spillThis (symbol * sym) +{ + int i; + debugLog ("%s : %s\n", __FUNCTION__, sym->rname); + + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || SYM_SPIL_LOC (sym))) + createStackSpil (sym); + + + /* mark it has spilt & put it in the spilt set */ + sym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + + if (sym->regs[i]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to some + LIVE ranges */ + if (!pic16_ptrRegReq && isSpiltOnStack (sym)) + { + pic16_ptrRegReq++; + spillLRWithPtrReg (sym); + } + + if (SYM_SPIL_LOC (sym) && !sym->remat) + SYM_SPIL_LOC (sym)->allocreq = 1; + return; +} + +/*-----------------------------------------------------------------*/ +/* selectSpil - select a iTemp to spil : rather a simple procedure */ +/*-----------------------------------------------------------------*/ +static symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + debugLog ("%s\n", __FUNCTION__); + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* get all live ranges that are rematerizable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + + /* return the least used of these */ + return leastUsedLR (selectS); + } + + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strcpy (sym->rname, (SYM_SPIL_LOC (sym)->rname[0] ? + SYM_SPIL_LOC (sym)->rname : + SYM_SPIL_LOC (sym)->name)); + sym->spildir = 1; + /* mark it as allocation required */ + SYM_SPIL_LOC (sym)->allocreq = 1; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that not + used in the remainder of the block */ + if (!_G.blockSpil && + !isiCodeInFunctionCall (ic) && + (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (!sym->remat) + { + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + SYM_SPIL_LOC (sym)->allocreq = 1; + return sym; + } + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + + sym = leastUsedLR (selectS); + SYM_SPIL_LOC (sym)->allocreq = 1; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack , for which one? the least + used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + SYM_SPIL_LOC (sym)->allocreq = 1; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + spillThis (forSym); + return forSym; + +} + +/*-----------------------------------------------------------------*/ +/* spilSomething - spil some variable & mark registers as free */ +/*-----------------------------------------------------------------*/ +static bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + debugLog ("%s\n", __FUNCTION__); + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!pic16_ptrRegReq && isSpiltOnStack (ssym)) + { + pic16_ptrRegReq++; + spillLRWithPtrReg (ssym); + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? + ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* getRegPtr - will try for PTR if not a GPR type if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegPtr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + + debugLog ("%s\n", __FUNCTION__); +tryAgain: + /* try for a ptr type */ + if ((reg = allocReg (REG_PTR))) + return reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j=0; j<=sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* getRegGpr - will try for GPR if not spil */ +/*-----------------------------------------------------------------*/ +static reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + + debugLog ("%s\n", __FUNCTION__); +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + return reg; + + if (!pic16_ptrRegReq) + if ((reg = allocReg (REG_PTR))) + return reg; + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + return NULL; + + /* make sure partially assigned registers aren't reused */ + for (j=0; j<=sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} + +/*-----------------------------------------------------------------*/ +/* symHasReg - symbol has a given register */ +/*-----------------------------------------------------------------*/ +static bool +symHasReg (symbol *sym, reg_info *reg) +{ + int i; + + debugLog ("%s\n", __FUNCTION__); + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* deassignLRs - check the live to and if they have registers & are */ +/* not spilt then free up the registers */ +/*-----------------------------------------------------------------*/ +static void +deassignLRs (iCode * ic, eBBlock * ebp) +{ + symbol *sym; + int k; + symbol *result; + + debugLog ("%s\n", __FUNCTION__); + for (sym = hTabFirstItem (liveRanges, &k); sym; + sym = hTabNextItem (liveRanges, &k)) + { + + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + SYM_SPIL_LOC (sym)->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case for shifting: there is a case where shift count + * can be allocated in the same register as the result, so do not + * free right registers if same as result registers, cause genShiftLeft + * will fail -- VR */ + if(ic->op == LEFT_OP) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && + ic->prev->op == IPOP && + !ic->prev->parmPush && + !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (IC_RESULT (ic) && + !(SKIP_IC2 (ic) || /* not a special icode */ + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + ic->op == RETURN || + POINTER_SET (ic)) && + (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && + !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + + sym->nRegs) >= result->nRegs) + ) + { + + for (i = 0; i < result->nRegs; i++) + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + + } + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + + +/*-----------------------------------------------------------------*/ +/* reassignLR - reassign this to registers */ +/*-----------------------------------------------------------------*/ +static void +reassignLR (operand * op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + debugLog ("%s\n", __FUNCTION__); + /* not spilt any more */ + sym->isspilt = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +/*-----------------------------------------------------------------*/ +/* willCauseSpill - determines if allocating will cause a spill */ +/*-----------------------------------------------------------------*/ +static int +willCauseSpill (int nr, int rt) +{ + debugLog ("%s\n", __FUNCTION__); + /* first check if there are any avlb registers + of te type required */ + if (rt == REG_PTR) + { + /* special case for pointer type + if pointer type not avlb then + check for type gpr */ + if (nFreeRegs (rt) >= nr) + return 0; + if (nFreeRegs (REG_GPR) >= nr) + return 0; + } + else + { + if (pic16_ptrRegReq) + { + if (nFreeRegs (rt) >= nr) + return 0; + } + else + { + if (nFreeRegs (REG_PTR) + + nFreeRegs (REG_GPR) >= nr) + return 0; + } + } + + debugLog (" ... yep it will (cause a spill)\n"); + /* it will cause a spil */ + return 1; +} + +/*-----------------------------------------------------------------*/ +/* positionRegs - the allocator can allocate same registers to res- */ +/* ult and operand, if this happens make sure they are in the same */ +/* position as the operand otherwise chaos results */ +/*-----------------------------------------------------------------*/ +static void +positionRegs (symbol * result, symbol * opsym, int lineno) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + + debugLog ("%s\n", __FUNCTION__); + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return; +again: + shared = 0; + /* first make sure that they actually share */ + for (i = 0; i < count; i++) + { + for (j = 0; j < count; j++) + { + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = 1; + goto xchgPositions; + } + } + } +xchgPositions: + if (shared) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + goto again; + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode * ic) +{ + symbol * sym; + + if (!op) return; + if (!IS_ITEMP (op)) return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) return; + if (!sym->nRegs) return; + if (sym->regs[0]) return; + + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, + sym->prereqv ? sym->prereqv->name : sym->name); + spillThis (sym); +} + + +/*-----------------------------------------------------------------*/ +/* serialRegAssign - serially allocate registers to the variables */ +/*-----------------------------------------------------------------*/ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + iCode *ic; + + debugLog ("%s\n", __FUNCTION__); + /* for all blocks */ + for (i = 0; i < count; i++) + { + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* of all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + + debugLog (" op: %s\n", pic16_decodeOp (ic->op)); + + if(IC_RESULT(ic) && !IS_ITEMP( IC_RESULT(ic))) + pic16_allocDirReg(IC_RESULT(ic)); + + if(IC_LEFT(ic) && !IS_ITEMP( IC_LEFT(ic))) + pic16_allocDirReg(IC_LEFT(ic)); + + if(IC_RIGHT(ic) && !IS_ITEMP( IC_RIGHT(ic))) + pic16_allocDirReg(IC_RIGHT(ic)); + + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + reassignLR (IC_LEFT (ic)); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && + IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq = 1; + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || + ic->op == IFX || + ic->op == IPUSH || + ic->op == IPOP || + (IC_RESULT (ic) && POINTER_SET (ic))) + continue; + + /* now we need to allocate registers + only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j; + int ptrRegSet = 0; + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && SYM_SPIL_LOC (sym) && + !SYM_SPIL_LOC (sym)->allocreq) + { + SYM_SPIL_LOC (sym)->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || + bitVectBitValue (_G.regAssigned, sym->key) || + sym->liveTo <= ic->seq) + continue; + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + spillThis (sym); + continue; + } + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + willCS = willCauseSpill (sym->nRegs, sym->regType); + + /* explicit turn off register spilling */ + willCS = 0; + + spillable = computeSpillable (ic); + if (sym->remat || + (willCS && bitVectIsZero (spillable))) + { + + spillThis (sym); + continue; + + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + spillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) { + if (SYM_SPIL_LOC (sym)) { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) { + spillThis (sym); + continue; + } + } else { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith(spillable,noSpilLoc,ebbs[i],ic)) { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) { + spillThis (sym); + continue; + } + } + } + } + + if (ic->op == RECEIVE) + debugLog ("When I get clever, I'll optimize the receive logic\n"); + + if(POINTER_GET(ic) && IS_BITFIELD(getSpec(operandType(IC_RESULT(ic)))) + && (SPEC_BLEN(getSpec(operandType(IC_RESULT(ic))))==1) + && (ic->next->op == IFX) + && (OP_LIVETO(IC_RESULT(ic)) == ic->next->seq)) { + + /* skip register allocation since none will be used */ + for(j=0;jnRegs;j++) + sym->regs[j] = newReg(REG_TMP, PO_GPR_TEMP, 0, "bad", 1, 0, NULL); +// OP_SYMBOL(IC_RESULT(ic))->nRegs = 0; + + continue; + } + + /* if we need ptr regs for the right side + then mark it */ + if (POINTER_GET (ic) && IS_SYMOP( IC_LEFT(ic) ) && getSize (OP_SYMBOL (IC_LEFT (ic))->type) + <= (unsigned) NEARPTRSIZE) + { + pic16_ptrRegReq++; + ptrRegSet = 1; + } + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + + if(debugF) + bitVectDebugOn(_G.regAssigned, debugF); + + for (j = 0; j < sym->nRegs; j++) + { + if (sym->regType == REG_PTR) + sym->regs[j] = getRegPtr (ic, ebbs[i], sym); + else + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + + /* if the allocation falied which means + this was spilt then break */ + if (!sym->regs[j]) + break; + } + debugLog (" %d - \n", __LINE__); + + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->nRegs && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_LEFT (ic)), ic->lineno); + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->nRegs && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), + OP_SYMBOL (IC_RIGHT (ic)), ic->lineno); + + debugLog (" %d - \n", __LINE__); + if (ptrRegSet) + { + debugLog (" %d - \n", __LINE__); + pic16_ptrRegReq--; + ptrRegSet = 0; + } + + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } + +} + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +static bitVect * +rUmaskForOp (operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + debugLog ("%s\n", __FUNCTION__); + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (pic16_nRegs); + + for (j = 0; j < sym->nRegs; j++) + { + rumask = bitVectSetBit (rumask, + sym->regs[j]->rIdx); + } + + return rumask; +} + +/*-----------------------------------------------------------------*/ +/* regsUsedIniCode :- returns bit vector of registers used in iCode */ +/*-----------------------------------------------------------------*/ +static bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (pic16_nRegs); + + debugLog ("%s\n", __FUNCTION__); + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, + rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, + rUmaskForOp (IC_JTCOND (ic))); + + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, + rUmaskForOp (IC_LEFT (ic))); + + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, + rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, + rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/*-----------------------------------------------------------------*/ +/* createRegMask - for each instruction will determine the regsUsed */ +/*-----------------------------------------------------------------*/ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + debugLog ("%s\n", __FUNCTION__); + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && + (ebbs[i]->entryLabel != entryLabel && + ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (pic16_nRegs + 1); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, + "createRegMask cannot find live range"); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + if (sym->regs[k]) + ic->rMask = + bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static symbol * +rematStr (symbol * sym) +{ + iCode *ic = sym->rematiCode; + symbol *psym = NULL; + int offset = 0; + + debugLog ("%s\n", __FUNCTION__); + + while (ic->op == '+' || ic->op == '-') { + /* if plus or minus print the right hand side */ + + offset += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } // while + + psym = newSymbol (OP_SYMBOL (IC_LEFT (ic))->rname, 1); + psym->offset = offset; + return psym; +} + +#if 0 +/*-----------------------------------------------------------------*/ +/* rematStr - returns the rematerialized string for a remat var */ +/*-----------------------------------------------------------------*/ +static char * +rematStr (symbol * sym) +{ + char *s = buffer; + iCode *ic = sym->rematiCode; + + debugLog ("%s\n", __FUNCTION__); + while (1) + { + + printf ("%s\n", s); + /* if plus or minus print the right hand side */ +/* + if (ic->op == '+' || ic->op == '-') { + sprintf(s,"0x%04x %c ",(int) operandLitValue(IC_RIGHT(ic)), + ic->op ); + s += strlen(s); + ic = OP_SYMBOL(IC_LEFT(ic))->rematiCode; + continue ; + } + */ + if (ic->op == '+' || ic->op == '-') + { + iCode *ric = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + sprintf (s, "(%s %c 0x%04x)", + OP_SYMBOL (IC_LEFT (ric))->rname, + ic->op, + (int) operandLitValue (IC_RIGHT (ic))); + + //s += strlen(s); + //ic = OP_SYMBOL(IC_LEFT(ic))->rematiCode; + //continue ; + //fprintf(stderr, "ralloc.c:%d OOPS %s\n",__LINE__,s); + return buffer; + } + + /* we reached the end */ + sprintf (s, "%s", OP_SYMBOL (IC_LEFT (ic))->rname); + break; + } + + printf ("%s\n", buffer); + return buffer; +} +#endif + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum () +{ + symbol *sym; + int k; + iCode *ic; + + debugLog ("%s\n", __FUNCTION__); + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) { + + debugLog (" %d - %s\n", __LINE__, sym->rname); + //fprintf(stderr," %d - %s\n", __LINE__, sym->rname); + + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0) + continue; + + /* if the live range is a temporary */ + if (sym->isitmp) { + + debugLog (" %d - itemp register\n", __LINE__); + + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + debugLog (" %d - no reg needed - used as a return\n", __LINE__); + continue; + } + + /* if the symbol has only one definition & + that definition is a get_pointer and the + pointer we are getting is rematerializable and + in "data" space */ + + if (bitVectnBitsOn (sym->defs) == 1 && + (ic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (sym->defs))) && + POINTER_GET (ic) && + !IS_BITVAR (sym->etype) && + (aggrToPtrDclType (operandType (IC_LEFT (ic)), FALSE) == POINTER)) { + +// continue; /* FIXME -- VR */ + if (ptrPseudoSymSafe (sym, ic)) { + + symbol *psym; + + debugLog (" %d - \n", __LINE__); + + /* create a psuedo symbol & force a spil */ + //X symbol *psym = newSymbol (rematStr (OP_SYMBOL (IC_LEFT (ic))), 1); + psym = rematStr (OP_SYMBOL (IC_LEFT (ic))); + psym->type = sym->type; + psym->etype = sym->etype; + psym->psbase = ptrBaseRematSym (OP_SYMBOL (IC_LEFT (ic))); + strcpy (psym->rname, psym->name); + sym->isspilt = 1; + SYM_SPIL_LOC (sym) = psym; + continue; + } + + /* if in data space or idata space then try to + allocate pointer register */ + + } + + /* if not then we require registers */ + sym->nRegs = ((IS_AGGREGATE (sym->type) || sym->isptr) ? + getSize (sym->type = aggrToPtr (sym->type, FALSE)) : + getSize (sym->type)); + + +#if 0 + if(IS_PTR_CONST (sym->type)) { +#else + if(IS_CODEPTR (sym->type)) { +#endif + // what IS this ???? (HJD) + debugLog (" %d const pointer type requires %d registers, changing to 3\n",__LINE__,sym->nRegs); // patch 14 + sym->nRegs = 3; // patch 14 + } + + if (sym->nRegs > 4) { + fprintf (stderr, "allocated more than 4 or 0 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + if (sym->nRegs == 1 && + IS_PTR (sym->type) && + sym->uptr) + sym->regType = REG_PTR; + else + sym->regType = REG_GPR; + + + debugLog (" reg name %s, reg type %s\n", sym->rname, debugLogRegType (sym->regType)); + + } + else + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + sym->nRegs = 0; + + } + +} +static DEFSETFUNC (markRegFree) +{ + ((reg_info *)item)->isFree = 1; +// ((regs *)item)->wasUsed = 0; + + return 0; +} + +DEFSETFUNC (pic16_deallocReg) +{ + fprintf(stderr,"deallocting register %s\n",((reg_info *)item)->name); + ((reg_info *)item)->isFree = 1; + ((reg_info *)item)->wasUsed = 0; + + return 0; +} +/*-----------------------------------------------------------------*/ +/* freeAllRegs - mark all registers as free */ +/*-----------------------------------------------------------------*/ +void +pic16_freeAllRegs () +{ + debugLog ("%s\n", __FUNCTION__); + + applyToSet(pic16_dynAllocRegs,markRegFree); + applyToSet(pic16_dynStackRegs,markRegFree); +} + +/*-----------------------------------------------------------------*/ +/*-----------------------------------------------------------------*/ +void +pic16_deallocateAllRegs () +{ + debugLog ("%s\n", __FUNCTION__); + + applyToSet(pic16_dynAllocRegs,pic16_deallocReg); +} + + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + debugLog ("%s\n", __FUNCTION__); + deallocLocal (sym); + return 0; +} + +/*-----------------------------------------------------------------*/ +/* farSpacePackable - returns the packable icode for far variables */ +/*-----------------------------------------------------------------*/ +static iCode * +farSpacePackable (iCode * ic) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + /* go thru till we find a definition for the + symbol on the right */ + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if the definition is a call then no */ + if ((dic->op == CALL || dic->op == PCALL) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + return NULL; + } + + /* if shift by unknown amount then not */ + if ((dic->op == LEFT_OP || dic->op == RIGHT_OP) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + return NULL; + + /* if pointer get and size > 1 */ + if (POINTER_GET (dic) && + getSize (aggrToPtr (operandType (IC_LEFT (dic)), FALSE)) > 1) + return NULL; + + if (POINTER_SET (dic) && + getSize (aggrToPtr (operandType (IC_RESULT (dic)), FALSE)) > 1) + return NULL; + + /* if any three is a true symbol in far space */ + if (IC_RESULT (dic) && + IS_TRUE_SYMOP (IC_RESULT (dic)) && + isOperandInFarSpace (IC_RESULT (dic))) + return NULL; + + if (IC_RIGHT (dic) && + IS_TRUE_SYMOP (IC_RIGHT (dic)) && + isOperandInFarSpace (IC_RIGHT (dic)) && + !isOperandEqual (IC_RIGHT (dic), IC_RESULT (ic))) + return NULL; + + if (IC_LEFT (dic) && + IS_TRUE_SYMOP (IC_LEFT (dic)) && + isOperandInFarSpace (IC_LEFT (dic)) && + !isOperandEqual (IC_LEFT (dic), IC_RESULT (ic))) + return NULL; + + if (isOperandEqual (IC_RIGHT (ic), IC_RESULT (dic))) + { + if ((dic->op == LEFT_OP || + dic->op == RIGHT_OP || + dic->op == '-') && + IS_OP_LITERAL (IC_RIGHT (dic))) + return NULL; + else + return dic; + } + } + + return NULL; +} + +#if 0 +static int packRegsForPointerGet(iCode *ic, eBBlock *ebp) +{ + iCode *dic, *sic; + + debugLog ("%d\t%s\n", __LINE__, __FUNCTION__); + debugLog ("ic->op = %s\n", pic16_decodeOp( ic->op ) ); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + + dic = ic->prev; + if((dic->op == '=') + && ( +} +#endif + + +void replaceOperandWithOperand(eBBlock *ebp, iCode *ic, operand *src, iCode *dic, operand *dst); + +/*-----------------------------------------------------------------*/ +/* packRegsForAssign - register reduction for assignment */ +/*-----------------------------------------------------------------*/ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + debugLog ("%d\t%s\n", __LINE__, __FUNCTION__); + debugLog ("ic->op = %s\n", pic16_decodeOp( ic->op ) ); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + +// fprintf(stderr, "%s:%d symbol = %s\n", __FILE__, __LINE__, OP_SYMBOL( IC_RESULT(ic))->name); + + debugLog(" %d - actuall processing\n", __LINE__ ); + + if (!IS_ITEMP (IC_RESULT (ic))) { + pic16_allocDirReg(IC_RESULT (ic)); + debugLog (" %d - result is not temp\n", __LINE__); + } + +// if(IS_VALOP(IC_RIGHT(ic)))return 0; + +/* See BUGLOG0001 - VR */ +#if 1 + if (!IS_ITEMP (IC_RIGHT (ic)) /*&& (!IS_PARM(IC_RESULT(ic)))*/) { + debugLog (" %d - not packing - right is not temp\n", __LINE__); + pic16_allocDirReg(IC_RIGHT (ic)); + return 0; + } +#endif + + if (OP_SYMBOL (IC_RIGHT (ic))->isind || + OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + { + debugLog (" %d - not packing - right side fails \n", __LINE__); + return 0; + } + + /* if the true symbol is defined in far space or on stack + then we should not since this will increase register pressure */ + if (isOperandInFarSpace (IC_RESULT (ic))) + { + if ((dic = farSpacePackable (ic))) + goto pack; + else + return 0; + + } + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol before we find the definition then + we cannot pack */ + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if there is a function call and this is + a parameter & not my parameter then don't pack it */ + if ((dic->op == CALL || dic->op == PCALL) && + (OP_SYMBOL (IC_RESULT (ic))->_isparm && + !OP_SYMBOL (IC_RESULT (ic))->ismyparm)) + { + debugLog (" %d - \n", __LINE__); + dic = NULL; + break; + } + + + if (SKIP_IC2 (dic)) + continue; + + debugLog("%d\tSearching for iTempNN\n", __LINE__); + + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && + IS_OP_VOLATILE (IC_RESULT (dic))) + { + debugLog (" %d - dic is VOLATILE \n", __LINE__); + dic = NULL; + break; + } + +#if 1 + if( IS_SYMOP( IC_RESULT(dic)) && + IS_BITFIELD( OP_SYMBOL(IC_RESULT(dic))->etype ) ) { + + debugLog (" %d - result is bitfield\n", __LINE__); + dic = NULL; + break; + } +#endif + + if (IS_SYMOP (IC_RESULT (dic)) && + IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + /* A previous result was assigned to the same register - we'll our definition */ + debugLog (" %d - dic result key == ic right key -- pointer set=%c\n", + __LINE__, ((POINTER_SET (dic)) ? 'Y' : 'N')); + if (POINTER_SET (dic)) + dic = NULL; + + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || + IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + debugLog (" %d - dic right key == ic rightor result key\n", __LINE__); + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || + IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + debugLog (" %d - dic left key == ic rightor result key\n", __LINE__); + dic = NULL; + break; + } + + if (POINTER_SET (dic) && + IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + debugLog (" %d - dic result key == ic result key -- pointer set=Y\n", + __LINE__); + dic = NULL; + break; + } + } + + if (!dic) + return 0; /* did not find */ + +#if 1 + /* This code is taken from the hc08 port. Do not know + * if it fits for pic16, but I leave it here just in case */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) { + sym_link *etype = operandType (IC_RESULT (dic)); + + if (IS_BITFIELD (etype)) { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) { + debugLog(" %d bitfields\n"); + return 0; + } + } + } +#endif + + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || + (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || + (IC_RIGHT (dic) && + IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + debugLog (" packing. removing %s\n", OP_SYMBOL (IC_RIGHT (ic))->rname); + debugLog (" replacing with %s\n", OP_SYMBOL (IC_RESULT (dic))->rname); + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + + + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + + debugLog(" %d\n", __LINE__ ); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + + +#if 1 + +#define NO_packRegsForAccUse +#define NO_packRegsForSupport +#define NO_packRegsForOneuse +#define NO_cast_peep + +#endif + + +#ifndef NO_packRegsForSupport +/*-----------------------------------------------------------------*/ +/* findAssignToSym : scanning backwards looks for first assig found */ +/*-----------------------------------------------------------------*/ +static iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if definition by assignment */ + if (dic->op == '=' && + !POINTER_SET (dic) && + IC_RESULT (dic)->key == op->key +/* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + ) + { + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then return + true */ + if (!IS_SYMOP (IC_RIGHT (dic))) + break; + + /* if the symbol is in far space then + we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && + OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && + IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + return NULL; + } + + break; + + } + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && + IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; + + +} +#endif + + +#ifndef NO_packRegsForSupport +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + int change = 0; + + debugLog ("%s\n", __FUNCTION__); + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + if (IS_ITEMP (IC_LEFT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_LEFT (ic), ic); + iCode *sic; + + if (!dic) + goto right; + + debugAopGet ("removing left:", IC_LEFT (ic)); + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_LEFT (ic)->key); + + IC_LEFT (ic)->operand.symOperand = + IC_RIGHT (dic)->operand.symOperand; + IC_LEFT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + /* do the same for the right operand */ +right: + if (!change && + IS_ITEMP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + iCode *sic; + + if (!dic) + return change; + + /* if this is a subtraction & the result + is a true symbol in far space then don't pack */ + if (ic->op == '-' && IS_TRUE_SYMOP (IC_RESULT (dic))) + { + sym_link *etype = getSpec (operandType (IC_RESULT (dic))); + if (IN_FARSPACE (SPEC_OCLS (etype))) + return change; + } + + debugAopGet ("removing right:", IC_RIGHT (ic)); + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_RIGHT (ic)->key); + + IC_RIGHT (ic)->operand.symOperand = + IC_RIGHT (dic)->operand.symOperand; + IC_RIGHT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + change++; + } + + return change; +} +#endif + + +#ifndef NO_packRegsForOneuse +/*-----------------------------------------------------------------*/ +/* packRegsForOneuse : - will reduce some registers for single Use */ +/*-----------------------------------------------------------------*/ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + return NULL; + + debugLog ("%s\n", __FUNCTION__); + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + if(OP_SYMBOL(op)->remat || OP_SYMBOL(op)->ruonly) + return NULL; + + /* only upto 2 bytes since we cannot predict + the usage of b, & acc */ + if (getSize (operandType (op)) > (pic16_fReturnSizePic - 1) + && ic->op != RETURN + && ic->op != SEND + && !POINTER_SET(ic) + && !POINTER_GET(ic) + ) + return NULL; + + /* this routine will mark the a symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition && + that definition is either a return value from a + function or does not contain any variables in + far space */ + +#if 0 + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; +#endif + +#if 1 + if (bitVectnBitsOn (OP_USES (op)) > 1) + return NULL; +#endif + + /* if it has only one defintion */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = + hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || + dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from + a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && + !POINTER_SET(ic) && !POINTER_GET(ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + else + { + + + /* otherwise check that the definition does + not contain any symbols in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + IS_OP_RUONLY (IC_LEFT (ic)) || + IS_OP_RUONLY (IC_RIGHT (ic))) + { + return NULL; + } + + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + } + + sic = dic; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (dic = dic->next; dic && dic != ic; dic = dic->next) + { + + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_RESULT (dic)), FALSE))) + return NULL; + + if (POINTER_GET (dic) && + !IS_DATA_PTR (aggrToPtr (operandType (IC_LEFT (dic)), FALSE))) + return NULL; + + /* if address of & the result is remat then okay */ + if (dic->op == ADDRESS_OF && + OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if operand has size of three or more & this + operation is a '*','/' or '%' then 'b' may + cause a problem */ + if ((dic->op == '%' || dic->op == '/' || dic->op == '*') && + getSize (operandType (op)) >= 2) + return NULL; + + /* if left or right or result is in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + isOperandInFarSpace (IC_RESULT (dic)) || + IS_OP_RUONLY (IC_LEFT (dic)) || + IS_OP_RUONLY (IC_RIGHT (dic)) || + IS_OP_RUONLY (IC_RESULT (dic))) + { + return NULL; + } + } + + OP_SYMBOL (op)->ruonly = 1; + return sic; + +} +#endif + + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *ltype = getSpec (operandType (IC_LEFT (ic))); + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + debugLog ("%s\n", __FUNCTION__); + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype) || + (IS_BITVAR (ltype) && IN_BITSPACE (SPEC_OCLS (ltype)))) + return TRUE; + else + return FALSE; +} + + +#ifndef NO_packRegsForAccUse + +/*-----------------------------------------------------------------*/ +/* packRegsForAccUse - pack registers for acc use */ +/*-----------------------------------------------------------------*/ +static void +packRegsForAccUse (iCode * ic) +{ + iCode *uic; + + debugLog ("%s\n", __FUNCTION__); + + /* if this is an aggregate, e.g. a one byte char array */ + if (IS_AGGREGATE(operandType(IC_RESULT(ic)))) { + return; + } + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + + /* if + or - then it has to be one byte result */ + if ((ic->op == '+' || ic->op == '-') + && getSize (operandType (IC_RESULT (ic))) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if shift operation make sure right side is not a literal */ + if (ic->op == RIGHT_OP && + (isOperandLiteral (IC_RIGHT (ic)) || + getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (ic->op == LEFT_OP && + (isOperandLiteral (IC_RIGHT (ic)) || + getSize (operandType (IC_RESULT (ic))) > 1)) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (IS_BITWISE_OP (ic) && + getSize (operandType (IC_RESULT (ic))) > 1) + return; + + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* has only one use */ + if (bitVectnBitsOn (OP_USES (IC_RESULT (ic))) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (ic->next != uic) + return; + + /* if it is a conditional branch then we definitely can */ + if (uic->op == IFX) + goto accuse; + + if (uic->op == JUMPTABLE) + return; + + /* if the usage is not is an assignment + or an arithmetic / bitwise / shift operation then not */ + if (POINTER_SET (uic) && + getSize (aggrToPtr (operandType (IC_RESULT (uic)), FALSE)) > 1) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + if (uic->op != '=' && + !IS_ARITHMETIC_OP (uic) && + !IS_BITWISE_OP (uic) && + uic->op != LEFT_OP && + uic->op != RIGHT_OP) + return; + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if used in ^ operation then make sure right is not a + literl */ + if (uic->op == '^' && isOperandLiteral (IC_RIGHT (uic))) + return; + + /* if shift operation make sure right side is not a literal */ + if (uic->op == RIGHT_OP && + (isOperandLiteral (IC_RIGHT (uic)) || + getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + if (uic->op == LEFT_OP && + (isOperandLiteral (IC_RIGHT (uic)) || + getSize (operandType (IC_RESULT (uic))) > 1)) + return; + + /* make sure that the result of this icode is not on the + stack, since acc is used to compute stack offset */ + if (IS_TRUE_SYMOP (IC_RESULT (uic)) && + OP_SYMBOL (IC_RESULT (uic))->onStack) + return; + + /* if either one of them in far space then we cannot */ + if ((IS_TRUE_SYMOP (IC_LEFT (uic)) && + isOperandInFarSpace (IC_LEFT (uic))) || + (IS_TRUE_SYMOP (IC_RIGHT (uic)) && + isOperandInFarSpace (IC_RIGHT (uic)))) + return; + + /* if the usage has only one operand then we can */ + if (IC_LEFT (uic) == NULL || + IC_RIGHT (uic) == NULL) + goto accuse; + + /* make sure this is on the left side if not + a '+' since '+' is commutative */ + if (ic->op != '+' && + IC_LEFT (uic)->key != IC_RESULT (ic)->key) + return; + +#if 1 + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if one of them is a literal then we can */ + if ( ((IC_LEFT (uic) && IS_OP_LITERAL (IC_LEFT (uic))) || + (IC_RIGHT (uic) && IS_OP_LITERAL (IC_RIGHT (uic)))) && + (getSize (operandType (IC_RESULT (uic))) <= 1)) + { + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + return; + } +#endif + + debugLog (" %s:%d\n", __FUNCTION__,__LINE__); + /* if the other one is not on stack then we can */ + if (IC_LEFT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_RIGHT (uic)) || + (IS_TRUE_SYMOP (IC_RIGHT (uic)) && + !OP_SYMBOL (IC_RIGHT (uic))->onStack))) + goto accuse; + + if (IC_RIGHT (uic)->key == IC_RESULT (ic)->key && + (IS_ITEMP (IC_LEFT (uic)) || + (IS_TRUE_SYMOP (IC_LEFT (uic)) && + !OP_SYMBOL (IC_LEFT (uic))->onStack))) + goto accuse; + + return; + +accuse: + debugLog ("%s - Yes we are using the accumulator\n", __FUNCTION__); + OP_SYMBOL (IC_RESULT (ic))->accuse = 1; + + +} +#endif + + +/*-----------------------------------------------------------------*/ +/* packForPush - hueristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForReceive (iCode * ic, eBBlock * ebp) +{ + iCode *dic; + + debugLog ("%s\n", __FUNCTION__); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + + if (!ic->next) + return; + + for (dic = ic->next; dic; dic = dic->next) + { + if (IC_LEFT (dic) && (IC_RESULT (ic)->key == IC_LEFT (dic)->key)) + debugLog (" used on left\n"); + if (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key) + debugLog (" used on right\n"); + if (IC_RESULT (dic) && IC_RESULT (ic)->key == IC_RESULT (dic)->key) + debugLog (" used on result\n"); + + if ((IC_LEFT (dic) && (IC_RESULT (ic)->key == IC_LEFT (dic)->key)) || + (IC_RESULT (dic) && IC_RESULT (ic)->key == IC_RESULT (dic)->key)) + return; + } + + debugLog (" hey we can remove this unnecessary assign\n"); +} +/*-----------------------------------------------------------------*/ +/* packForPush - hueristics to reduce iCode for pushing */ +/*-----------------------------------------------------------------*/ +static void +packForPush (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *lic; + const char *iLine; + bitVect *dbv; + int disallowHiddenAssignment = 0; + + debugLog ("%s\n", __FUNCTION__); + if (ic->op != IPUSH || !IS_ITEMP (IC_LEFT (ic))) + return; + +#if 0 + { + int n1, n2; + + n1 = bitVectnBitsOn( OP_DEFS(IC_LEFT(ic))); + n2 = bitVectnBitsOn( OP_USES(IC_LEFT(ic))); + iLine = printILine(ic); + debugf3("defs: %d\tuses: %d\t%s\n", n1, n2, printILine(ic)); + dbuf_free(iLine); + debugf2("IC_LEFT(ic): from %d to %d\n", OP_LIVEFROM(IC_LEFT(ic)), OP_LIVETO(IC_LEFT(ic))); + } +#endif + + /* must have only definition & one usage */ + if (bitVectnBitsOn (OP_DEFS (IC_LEFT (ic))) != 1 || + bitVectnBitsOn (OP_USES (IC_LEFT (ic))) != 1) + return; + + /* find the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, + bitVectFirstBit (OP_DEFS (IC_LEFT (ic)))))) + return; + + /* if definition is not assignment, + * or is not pointer (because pointer might have changed) */ + if (dic->op != '=' || POINTER_SET (dic)) + return; + + /* we must ensure that we can use the delete the assignment, + * because the source might have been modified in between. + * Until I know how to fix this, I'll use the adhoc fix + * to check the liveranges */ + if((OP_LIVEFROM(IC_RIGHT(dic))==0) || (OP_LIVETO(IC_RIGHT(dic))==0)) + return; +// debugf2("IC_RIGHT(dic): from %d to %d\n", OP_LIVEFROM(IC_RIGHT(dic)), OP_LIVETO(IC_RIGHT(dic))); + + /* If the defining iCode is outside of this block, we need to recompute */ + /* ebp (see the mcs51 version of packForPush), but we weren't passed */ + /* enough data to do that. Just bail out instead if that happens. */ + if (dic->seq < ebp->fSeq) + return; + + if (IS_SYMOP (IC_RIGHT (dic))) + { + if (IC_RIGHT (dic)->isvolatile) + return; + + if (OP_SYMBOL (IC_RIGHT (dic))->addrtaken || isOperandGlobal (IC_RIGHT (dic))) + disallowHiddenAssignment = 1; + + /* make sure the right side does not have any definitions + inbetween */ + dbv = OP_DEFS (IC_RIGHT (dic)); + for (lic = ic; lic && lic != dic; lic = lic->prev) + { + if (bitVectBitValue (dbv, lic->key)) + return; + if (disallowHiddenAssignment && (lic->op == CALL || lic->op == PCALL || POINTER_SET (lic))) + return; + } + /* make sure they have the same type */ + if (IS_SPEC (operandType (IC_LEFT (ic)))) + { + sym_link *itype = operandType (IC_LEFT (ic)); + sym_link *ditype = operandType (IC_RIGHT (dic)); + + if (SPEC_USIGN (itype) != SPEC_USIGN (ditype) || SPEC_LONG (itype) != SPEC_LONG (ditype)) + return; + } + } + + + /* + * The following code causes segfaults, e.g., + * #2496919 Internal error with pic16 sdcc + * and is thus disabled for now. + */ + if (0) + { + /* we now we know that it has one & only one def & use + and the that the definition is an assignment */ + + /* extend the live range of replaced operand if needed */ + if (IS_SYMOP (IC_RIGHT (dic)) && OP_SYMBOL (IC_RIGHT (dic))->liveTo < ic->seq) + { + OP_SYMBOL (IC_RIGHT (dic))->liveTo = ic->seq; + } + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + if (IS_ITEMP (IC_RIGHT (dic))) + OP_USES (IC_RIGHT (dic)) = bitVectSetBit (OP_USES (IC_RIGHT (dic)), ic->key); + + IC_LEFT (ic) = IC_RIGHT (dic); + + iLine = printILine(dic); + debugf("remiCodeFromeBBlock: %s\n", iLine); + dbuf_free(iLine); + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + } // if +} + +static void printSymType(char * str, sym_link *sl) +{ + if(!pic16_ralloc_debug)return; + + debugLog (" %s Symbol type: ",str); + printTypeChain (sl, debugF); + debugLog ("\n"); +} + +/*-----------------------------------------------------------------*/ +/* some debug code to print the symbol S_TYPE. Note that + * the function checkSClass in src/SDCCsymt.c dinks with + * the S_TYPE in ways the PIC port doesn't fully like...*/ +/*-----------------------------------------------------------------*/ +static void isData(sym_link *sl) +{ + FILE *of = stderr; + + if(!pic16_ralloc_debug)return; + + if(!sl)return; + + if(debugF) + of = debugF; + + for ( ; sl; sl=sl->next) { + if(!IS_DECL(sl) ) { + switch (SPEC_SCLS(sl)) { + case S_DATA: fprintf (of, "data "); break; + case S_XDATA: fprintf (of, "xdata "); break; + case S_SFR: fprintf (of, "sfr "); break; + case S_SBIT: fprintf (of, "sbit "); break; + case S_CODE: fprintf (of, "code "); break; + case S_IDATA: fprintf (of, "idata "); break; + case S_PDATA: fprintf (of, "pdata "); break; + case S_LITERAL: fprintf (of, "literal "); break; + case S_STACK: fprintf (of, "stack "); break; + case S_XSTACK: fprintf (of, "xstack "); break; + case S_BIT: fprintf (of, "bit "); break; + case S_EEPROM: fprintf (of, "eeprom "); break; + default: break; + } + } + } +} + + +/*--------------------------------------------------------------------*/ +/* pic16_packRegisters - does some transformations to reduce */ +/* register pressure */ +/* */ +/*--------------------------------------------------------------------*/ +static void +pic16_packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + debugLog ("%s\n", __FUNCTION__); + + while (1) { + + change = 0; + + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { +// debugLog("%d\n", __LINE__); + /* find assignment of the form TrueSym := iTempNN:1 */ + if ( (ic->op == '=') && !POINTER_SET (ic) ) // patch 11 + change += packRegsForAssign (ic, ebp); + /* debug stuff */ + if (ic->op == '=') + { + if (POINTER_SET (ic)) + debugLog ("pointer is set\n"); + debugAopGet (" result:", IC_RESULT (ic)); + debugAopGet (" left:", IC_LEFT (ic)); + debugAopGet (" right:", IC_RIGHT (ic)); + } + + } + + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) { + + if(IS_SYMOP ( IC_LEFT(ic))) { + sym_link *etype = getSpec (operandType (IC_LEFT (ic))); + + debugAopGet ("x left:", IC_LEFT (ic)); +#if 0 + if(IS_PTR_CONST(OP_SYMBOL(IC_LEFT(ic))->type)) +#else + if(IS_CODEPTR(OP_SYMBOL(IC_LEFT(ic))->type)) +#endif + debugLog (" is a pointer\n"); + + if(IS_PTR(OP_SYMBOL(IC_LEFT(ic))->type)) + debugLog (" is a ptr\n"); + + if(IS_OP_VOLATILE(IC_LEFT(ic))) + debugLog (" is volatile\n"); + + isData(etype); + + if(IS_OP_VOLATILE(IC_LEFT(ic))) { + debugLog (" %d - left is not temp, allocating\n", __LINE__); + pic16_allocDirReg(IC_LEFT (ic)); + } + + printSymType("c ", OP_SYMBOL(IC_LEFT(ic))->type); + } + + if(IS_SYMOP ( IC_RIGHT(ic))) { + debugAopGet (" right:", IC_RIGHT (ic)); + printSymType(" ", OP_SYMBOL(IC_RIGHT(ic))->type); + } + + if(IS_SYMOP ( IC_RESULT(ic))) { + debugAopGet (" result:", IC_RESULT (ic)); + printSymType(" ", OP_SYMBOL(IC_RESULT(ic))->type); + } + + if(IS_TRUE_SYMOP ( IC_RIGHT(ic))) { + debugAopGet (" right:", IC_RIGHT (ic)); + printSymType(" ", OP_SYMBOL(IC_RIGHT(ic))->type); +// pic16_allocDirReg(IC_RIGHT(ic)); + } + + if(IS_TRUE_SYMOP ( IC_RESULT(ic))) { + debugAopGet (" result:", IC_RESULT (ic)); + printSymType(" ", OP_SYMBOL(IC_RESULT(ic))->type); +// pic16_allocDirReg(IC_RESULT(ic)); + } + + + if (POINTER_SET (ic)) + debugLog (" %d - Pointer set\n", __LINE__); + + /* Look for two subsequent iCodes with */ + /* iTemp := _c; */ + /* _c = iTemp & op; */ + /* and replace them by */ + /* iTemp := _c; */ + /* _c = _c & op; */ + if ((ic->op == BITWISEAND || ic->op == '|' || ic->op == '^') + && ic->prev + && ic->prev->op == '=' + && IS_ITEMP (IC_LEFT (ic)) + && IC_LEFT (ic) == IC_RESULT (ic->prev) + && isOperandEqual (IC_RESULT(ic), IC_RIGHT(ic->prev))) + { + iCode* ic_prev = ic->prev; + symbol* prev_result_sym = OP_SYMBOL (IC_RESULT (ic_prev)); + + ReplaceOpWithCheaperOp (&IC_LEFT (ic), IC_RESULT (ic)); + if (IC_RESULT (ic_prev) != IC_RIGHT (ic)) { + bitVectUnSetBit (OP_USES (IC_RESULT (ic_prev)), ic->key); + if (/*IS_ITEMP (IC_RESULT (ic_prev)) && */ + prev_result_sym->liveTo == ic->seq) + { + prev_result_sym->liveTo = ic_prev->seq; + } + } + bitVectSetBit (OP_USES (IC_RESULT (ic)), ic->key); + + bitVectSetBit (ic->rlive, IC_RESULT (ic)->key); + + if (bitVectIsZero (OP_USES (IC_RESULT (ic_prev)))) { + bitVectUnSetBit (ic->rlive, IC_RESULT (ic)->key); + bitVectUnSetBit (OP_DEFS (IC_RESULT (ic_prev)), ic_prev->key); + remiCodeFromeBBlock (ebp, ic_prev); + hTabDeleteItem (&iCodehTab, ic_prev->key, ic_prev, DELETE_ITEM, NULL); + } + } + + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && + IS_TRUE_SYMOP (IC_LEFT (ic)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + + debugLog (" %d - %s. result is rematerializable\n", __LINE__,__FUNCTION__); + + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + SPIL_LOC (IC_RESULT (ic)) = NULL; + + } + + /* if straight assignment then carry remat flag if + this is the only definition */ + if (ic->op == '=' && + !POINTER_SET (ic) && + IS_SYMOP (IC_RIGHT (ic)) && + OP_SYMBOL (IC_RIGHT (ic))->remat && + bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) <= 1 && + !isOperandGlobal (IC_RESULT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + debugLog (" %d - %s. straight rematerializable\n", __LINE__,__FUNCTION__); + + OP_SYMBOL (IC_RESULT (ic))->remat = + OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = + OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + OP_SYMBOL (IC_LEFT (ic))->remat && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && + IS_OP_LITERAL (IC_RIGHT (ic)))) + { + debugLog (" %d - %s. rematerializable because op is +/-\n", __LINE__,__FUNCTION__); + //int i = + operandLitValue (IC_RIGHT (ic)); + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + SPIL_LOC (IC_RESULT (ic)) = NULL; + } + + +#if 0 + /* try to optimize FSR0 usage when reading data memory pointers */ + + if(getenv("OPTIMIZE_NEAR_POINTER_GET")) { + static int fsr0usage=0; + static iCode *usic; + + if(POINTER_GET(ic) /* this is a memory read */ + && ic->loop /* this is in a loop */ + ) { + fprintf(stderr, "might optimize FSR0 usage\n"); + } + } +#endif + + /* mark the pointer usages */ + if (POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->uptr = 1; + debugLog (" marking as a pointer (set) =>"); + debugAopGet (" result:", IC_RESULT (ic)); + + } + + if (POINTER_GET (ic)) + { + if(IS_SYMOP(IC_LEFT(ic))) { + OP_SYMBOL (IC_LEFT (ic))->uptr = 1; + debugLog (" marking as a pointer (get) =>"); + debugAopGet (" left:", IC_LEFT (ic)); + } + + if(getenv("OPTIMIZE_BITFIELD_POINTER_GET")) { + if(IS_ITEMP(IC_LEFT(ic)) && IS_BITFIELD(OP_SYM_ETYPE(IC_LEFT(ic)))) { + iCode *dic = ic->prev; + + fprintf(stderr, "%s:%d might give opt POINTER_GET && IS_BITFIELD(IC_LEFT)\n", __FILE__, __LINE__); + + if(dic && dic->op == '=' + && isOperandEqual(IC_RESULT(dic), IC_LEFT(ic))) { + + fprintf(stderr, "%s:%d && prev is '=' && prev->result == ic->left\n", __FILE__, __LINE__); + + + /* replace prev->left with ic->left */ + IC_LEFT(ic) = IC_RIGHT(dic); + IC_RIGHT(ic->prev) = NULL; + + /* remove ic->prev iCode (assignment) */ + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,ic->key); + + + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + } + } + } + } + + //debugLog(" %d %s\n", __LINE__, __FUNCTION__); + + if (!SKIP_IC2 (ic)) + { + //debugLog(" %d %s\n", __LINE__, __FUNCTION__ ); + /* if we are using a symbol on the stack + then we should say pic16_ptrRegReq */ + if (ic->op == IFX && IS_SYMOP (IC_COND (ic))) + pic16_ptrRegReq += ((OP_SYMBOL (IC_COND (ic))->onStack || + OP_SYMBOL (IC_COND (ic))->iaccess) ? 1 : 0); + else if (ic->op == JUMPTABLE && IS_SYMOP (IC_JTCOND (ic))) + pic16_ptrRegReq += ((OP_SYMBOL (IC_JTCOND (ic))->onStack || + OP_SYMBOL (IC_JTCOND (ic))->iaccess) ? 1 : 0); + else + { + + //debugLog(" %d %s\n", __LINE__, __FUNCTION__ ); + if (IS_SYMOP (IC_LEFT (ic))) + pic16_ptrRegReq += ((OP_SYMBOL (IC_LEFT (ic))->onStack || + OP_SYMBOL (IC_LEFT (ic))->iaccess) ? 1 : 0); + if (IS_SYMOP (IC_RIGHT (ic))) + pic16_ptrRegReq += ((OP_SYMBOL (IC_RIGHT (ic))->onStack || + OP_SYMBOL (IC_RIGHT (ic))->iaccess) ? 1 : 0); + if (IS_SYMOP (IC_RESULT (ic))) + pic16_ptrRegReq += ((OP_SYMBOL (IC_RESULT (ic))->onStack || + OP_SYMBOL (IC_RESULT (ic))->iaccess) ? 1 : 0); + } + + debugLog (" %d - pointer reg req = %d\n", __LINE__,pic16_ptrRegReq); + + } + + /* if the condition of an if instruction + is defined in the previous instruction then + mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + ((ic->op == BITWISEAND || + ic->op == '|' || + ic->op == '^') && + isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && + OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + + debugLog (" %d\n", __LINE__); + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + debugLog(" %d\n", __LINE__); + +#ifndef NO_packRegsForSupport + /* reduce for support function calls */ + if (ic->supportRtn || ic->op == '+' || ic->op == '-') + packRegsForSupport (ic, ebp); +#endif + + /* if a parameter is passed, it's in W, so we may not + need to place a copy in a register */ + if (ic->op == RECEIVE) + packForReceive (ic, ebp); + +#ifndef NO_packRegsForOneuse + /* some cases the redundant moves can + can be eliminated for return statements */ + if ((ic->op == RETURN || ic->op == SEND) && + !isOperandInFarSpace (IC_LEFT (ic)) && + !options.model) + packRegsForOneuse (ic, IC_LEFT (ic), ebp); +#endif + +#ifndef NO_packRegsForOneuse + /* if pointer set & left has a size more than + one and right is not in far space */ + if (POINTER_SET (ic) && + !isOperandInFarSpace (IC_RIGHT (ic)) && + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && + getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + + packRegsForOneuse (ic, IC_RESULT (ic), ebp); +#endif + +#ifndef NO_packRegsForOneuse + /* if pointer get */ + if (POINTER_GET (ic) && + !isOperandInFarSpace (IC_RESULT (ic)) && + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && + getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + debugLog("%d - return from packRegsForOneuse\n", __LINE__); +#endif + +#ifndef NO_cast_peep + /* if this is cast for intergral promotion then + check if only use of the definition of the + operand being casted/ if yes then replace + the result of that arithmetic operation with + this result and get rid of the cast */ + if (ic->op == CAST) { + + sym_link *fromType = operandType (IC_RIGHT (ic)); + sym_link *toType = operandType (IC_LEFT (ic)); + + debugLog (" %d - casting\n", __LINE__); + + if (IS_INTEGRAL (fromType) && IS_INTEGRAL (toType) && + getSize (fromType) != getSize (toType)) { + + + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) { + + if (IS_ARITHMETIC_OP (dic)) { + debugLog(" %d %s\n", __LINE__, __FUNCTION__ ); + + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + remiCodeFromeBBlock (ebp, ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } else + + OP_SYMBOL (IC_RIGHT (ic))->ruonly = 0; + } + } else { + + /* if the type from and type to are the same + then if this is the only use then packit */ + if (compareType (operandType (IC_RIGHT (ic)), + operandType (IC_LEFT (ic))) == 1) { + + iCode *dic = packRegsForOneuse (ic, IC_RIGHT (ic), ebp); + if (dic) { + + debugLog(" %d\n", __LINE__); + + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(dic))->defs,dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + bitVectUnSetBit(OP_SYMBOL(IC_RESULT(ic))->defs,ic->key); + remiCodeFromeBBlock (ebp, ic); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + ic = ic->prev; + } + } + } + } +#endif + +#if 1 + /* there are some problems with packing variables + * it seems that the live range estimator doesn't + * estimate correctly the liveranges of some symbols */ + + /* pack for PUSH + iTempNN := (some variable in farspace) V1 + push iTempNN ; + ------------- + push V1 + */ + if (ic->op == IPUSH) + { + packForPush (ic, ebp); + } +#endif + +#ifndef NO_packRegsForAccUse + /* pack registers for accumulator use, when the + result of an arithmetic or bit wise operation + has only one use, that use is immediately following + the defintion and the using iCode has only one + operand or has two operands but one is literal & + the result of that operation is not on stack then + we can leave the result of this operation in acc:b + combination */ + if ((IS_ARITHMETIC_OP (ic) + + || IS_BITWISE_OP (ic) + + || ic->op == LEFT_OP || ic->op == RIGHT_OP + + ) && + IS_ITEMP (IC_RESULT (ic)) && + getSize (operandType (IC_RESULT (ic))) <= 1) + + packRegsForAccUse (ic); +#endif + + } +} + +static void +dumpEbbsToDebug (eBBlock ** ebbs, int count) +{ + int i; + + if (!pic16_ralloc_debug || !debugF) + return; + + for (i = 0; i < count; i++) + { + fprintf (debugF, "\n----------------------------------------------------------------\n"); + fprintf (debugF, "Basic Block %s : loop Depth = %d noPath = %d , lastinLoop = %d\n", + ebbs[i]->entryLabel->name, + ebbs[i]->depth, + ebbs[i]->noPath, + ebbs[i]->isLastInLoop); + fprintf (debugF, "depth 1st num %d : bbnum = %d 1st iCode = %d , last iCode = %d\n", + ebbs[i]->dfnum, + ebbs[i]->bbnum, + ebbs[i]->fSeq, + ebbs[i]->lSeq); + fprintf (debugF, "visited %d : hasFcall = %d\n", + ebbs[i]->visited, + ebbs[i]->hasFcall); + + fprintf (debugF, "\ndefines bitVector :"); + bitVectDebugOn (ebbs[i]->defSet, debugF); + fprintf (debugF, "\nlocal defines bitVector :"); + bitVectDebugOn (ebbs[i]->ldefs, debugF); + fprintf (debugF, "\npointers Set bitvector :"); + bitVectDebugOn (ebbs[i]->ptrsSet, debugF); + fprintf (debugF, "\nin pointers Set bitvector :"); + bitVectDebugOn (ebbs[i]->inPtrsSet, debugF); + fprintf (debugF, "\ninDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->inDefs, debugF); + fprintf (debugF, "\noutDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->outDefs, debugF); + fprintf (debugF, "\nusesDefs Set bitvector :"); + bitVectDebugOn (ebbs[i]->usesDefs, debugF); + fprintf (debugF, "\n----------------------------------------------------------------\n"); + printiCChain (ebbs[i]->sch, debugF); + } +} + +void dbg_dumpregusage(void); + +/*-----------------------------------------------------------------*/ +/* pic16_assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +pic16_assignRegisters (ebbIndex * ebbi) +{ + eBBlock ** ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + debugLog ("<><><><><><><><><><><><><><><><><>\nstarting\t%s:%s", __FILE__, __FUNCTION__); + debugLog ("\nebbs before optimizing:\n"); + dumpEbbsToDebug (ebbs, count); + + _inRegAllocator = 1; + + pic16_freeAllRegs(); +#if 0 + dbg_dumpregusage(); + /* clear whats left over from peephole parser */ + pic16_dynAllocRegs= newSet(); //NULL; +// pic16_dynStackRegs= newSet(); //NULL; +// pic16_dynProcessorRegs=newSet(); //NULL; +// pic16_dynDirectRegs=newSet(); //NULL; +// pic16_dynDirectBitRegs=newSet(); //NULL; +// pic16_dynInternalRegs=newSet(); //NULL; +// pic16_dynAccessRegs=newSet(); //NULL; + +// dynDirectRegNames=NULL; + dynAllocRegNames=NULL; +// dynProcRegNames=NULL; +// dynAccessRegNames=NULL; +#endif + + setToNull ((void *) &_G.funcrUsed); + pic16_ptrRegReq = _G.stackExtend = _G.dataExtend = 0; + + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + pic16_packRegisters (ebbs[i]); + + + if (0) + { + reg_info *reg; + int hkey; + + debugLog("dir registers allocated so far:\n"); + reg = hTabFirstItem(dynDirectRegNames, &hkey); + + while(reg) { + debugLog(" -- #%d reg = %s key %d, rIdx = %d, size %d\n",i++,reg->name,hkey, reg->rIdx,reg->size); + // fprintf(stderr, " -- #%d reg = %s key %d, rIdx = %d, size %d\n",i++,reg->name,hkey, reg->rIdx,reg->size); + reg = hTabNextItem(dynDirectRegNames, &hkey); + } + } + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* start counting function temporary registers from zero */ + /* XXX: Resetting dynrIdx breaks register allocation, + * see #1489055, #1483693 (?), and #1445850! */ + //dynrIdx = 0; + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + +#if 0 + debugLog ("ebbs after serialRegAssign:\n"); + dumpEbbsToDebug (ebbs, count); +#endif + + //pic16_freeAllRegs(); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* redo that offsets for stacked automatic variables */ + redoStackOffsets (); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + +// dumpLR(ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + debugLog ("ebbs after optimizing:\n"); + dumpEbbsToDebug (ebbs, count); + + + _inRegAllocator = 0; + + genpic16Code (ic); + + /* free up any _G.stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + pic16_freeAllRegs (); + + + debugLog ("leaving\n<><><><><><><><><><><><><><><><><>\n"); + debugLogClose (); + return; +} diff --git a/src/pic16/ralloc.h b/src/pic16/ralloc.h new file mode 100644 index 0000000..b98263f --- /dev/null +++ b/src/pic16/ralloc.h @@ -0,0 +1,194 @@ +/*------------------------------------------------------------------------- + + ralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + PIC port - T. Scott Dattalo scott@dattalo.com (2000) + PIC16 port - Martin Dubuc m.dubuc@rogers.com (2002) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#include "pcoderegs.h" + +enum + { + R2_IDX = 0, R3_IDX, R4_IDX, + R5_IDX, R6_IDX, R7_IDX, + R0_IDX, R1_IDX, X8_IDX, + X9_IDX, X10_IDX, X11_IDX, + X12_IDX, CND_IDX + }; + +enum { + REG_PTR=1, + REG_GPR, + REG_CND, + REG_SFR, + REG_STK, + REG_TMP +}; +//#define REG_PTR 0x01 +//#define REG_GPR 0x02 +//#define REG_CND 0x04 +//#define REG_SFR 0x08 +//#define REG_STK 0x10 /* Use a register as a psuedo stack */ +//#define REG_TMP 0x20 + +/* definition for the registers */ +typedef struct reg_info + { + short type; /* can have value + * REG_GPR, REG_PTR or REG_CND + * This like the "meta-type" */ + short pc_type; /* pcode type */ + short rIdx; /* index into register table */ + // short otype; + char *name; /* name */ + + unsigned isFree:1; /* is currently unassigned */ + unsigned wasUsed:1; /* becomes true if register has been used */ + unsigned isFixed:1; /* True if address can't change */ +// unsigned isMapped:1; /* The Register's address has been mapped to physical RAM */ + unsigned isBitField:1; /* True if reg is type bit OR is holder for several bits */ + unsigned isEmitted:1; /* True if the reg has been written to a .asm file */ + unsigned accessBank:1; /* True if the reg is explicit placed in access bank */ + unsigned isLocal:1; /* True if the reg is allocated in function's local frame */ + unsigned address; /* reg's address if isFixed | isMapped is true */ + unsigned size; /* 0 for byte, 1 for int, 4 for long */ + unsigned alias; /* Alias mask if register appears in multiple banks */ + struct reg_info *reg_alias; /* If more than one register share the same address + * then they'll point to each other. (primarily for bits)*/ + operand *regop; /* reference to the operand used to create the register */ + pCodeRegLives reglives; /* live range mapping */ + } +reg_info; +extern reg_info regspic16[]; +extern int pic16_nRegs; +extern int pic16_Gstack_base_addr; + +/* + As registers are created, they're added to a set (based on the + register type). Here are the sets of registers that are supported + in the PIC port: +*/ +extern set *pic16_dynAllocRegs; +extern set *pic16_dynStackRegs; +extern set *pic16_dynProcessorRegs; +extern set *pic16_dynDirectRegs; +extern set *pic16_dynDirectBitRegs; +extern set *pic16_dynInternalRegs; + +extern set *pic16_builtin_functions; + +extern set *pic16_rel_udata; +extern set *pic16_fix_udata; +extern set *pic16_equ_data; +extern set *pic16_int_regs; +extern set *pic16_acs_udata; + +void pic16_assignRegisters(ebbIndex *ebbi); +reg_info *pic16_regWithIdx(int); +reg_info *pic16_typeRegWithIdx(int, int, int); +reg_info *pic16_dirregWithName(const char *name); +reg_info *pic16_allocregWithName(const char *name); +reg_info *pic16_regWithName(const char *name); +void pic16_freeAllRegs(void); +void pic16_deallocateAllRegs(void); +reg_info *pic16_findFreeReg(short type); +reg_info *pic16_findFreeRegNext(short type, reg_info *creg); +reg_info *pic16_allocWithIdx(int idx); + +reg_info *pic16_allocDirReg (operand *op); +reg_info *pic16_allocRegByName (const char *name, int size, operand *op); +extern char *pic16_decodeOp(unsigned int op); + +reg_info* newReg(int type, short pc_type, int rIdx, const char *name, unsigned size, int alias, operand *refop); + +/* Define register address that are constant across PIC16 family */ +#define IDX_TMR0 0xfd6 +#define IDX_STATUS 0xfd8 +#define IDX_INTCON 0xff2 +#define IDX_WREG 0xfe8 +#define IDX_BSR 0xfe0 + +#define IDX_PCL 0xff9 +#define IDX_PCLATH 0xffa +#define IDX_PCLATU 0xffb + +#define IDX_TOSL 0xffd +#define IDX_TOSH 0xffe +#define IDX_TOSU 0xfff + +#define IDX_TBLPTRL 0xff6 +#define IDX_TBLPTRH 0xff7 +#define IDX_TBLPTRU 0xff8 +#define IDX_TABLAT 0xff5 + +#define IDX_FSR0 0xfe9 +#define IDX_FSR0L 0xfe9 +#define IDX_FSR0H 0xfea + +#define IDX_FSR1 0xfe1 +#define IDX_FSR1L 0xfe1 +#define IDX_FSR1H 0xfe2 + +#define IDX_FSR2 0xfd9 +#define IDX_FSR2L 0xfd9 +#define IDX_FSR2H 0xfda + +#define IDX_INDF0 0xfef +#define IDX_POSTINC0 0xfee +#define IDX_POSTDEC0 0xfed +#define IDX_PREINC0 0xfec +#define IDX_PLUSW0 0xfeb + +#define IDX_INDF1 0xfe7 +#define IDX_POSTINC1 0xfe6 +#define IDX_POSTDEC1 0xfe5 +#define IDX_PREINC1 0xfe4 +#define IDX_PLUSW1 0xfe3 + +#define IDX_INDF2 0xfdf +#define IDX_POSTINC2 0xfde +#define IDX_POSTDEC2 0xfdd +#define IDX_PREINC2 0xfdc +#define IDX_PLUSW2 0xfdb + +#define IDX_PRODL 0xff3 +#define IDX_PRODH 0xff4 + +/* EEPROM registers */ +#define IDX_EECON1 0xfa6 +#define IDX_EECON2 0xfa7 +#define IDX_EEDATA 0xfa8 +#define IDX_EEADR 0xfa9 + +#define IDX_KZ 0x7fff /* Known zero - actually just a general purpose reg. */ +#define IDX_WSAVE 0x7ffe +#define IDX_SSAVE 0x7ffd + +#define IDX_GPSIMIO 0xf7f +#define IDX_GPSIMIO2 0xf7e + +#endif diff --git a/src/port-clean.mk b/src/port-clean.mk new file mode 100644 index 0000000..205d28f --- /dev/null +++ b/src/port-clean.mk @@ -0,0 +1,5 @@ +clean: + rm -f $(LIB) *.o *~ port.a *.lst *.asm *.sym *~ *.cdb *.dep *.rul + +distclean: clean + rm -f Makefile \ No newline at end of file diff --git a/src/port.h b/src/port.h new file mode 100644 index 0000000..6f11b58 --- /dev/null +++ b/src/port.h @@ -0,0 +1,488 @@ +/** @file port.h + Definitions for what a port must provide. + All ports are referenced in SDCCmain.c. + */ +#ifndef PORT_INCLUDE +#define PORT_INCLUDE + +#include "SDCCicode.h" +#include "SDCCargs.h" +#include "SDCCpeeph.h" +#include "dbuf.h" + +#define TARGET_ID_MCS51 1 +#define TARGET_ID_GBZ80 2 +#define TARGET_ID_Z80 3 +#define TARGET_ID_AVR 4 +#define TARGET_ID_DS390 5 +#define TARGET_ID_PIC14 6 +#define TARGET_ID_PIC16 7 +#define TARGET_ID_DS400 10 +#define TARGET_ID_HC08 11 +#define TARGET_ID_Z180 12 +#define TARGET_ID_R2K 13 +#define TARGET_ID_R3KA 14 +#define TARGET_ID_S08 15 +#define TARGET_ID_STM8 16 +#define TARGET_ID_TLCS90 17 +#define TARGET_ID_EZ80_Z80 18 +#define TARGET_ID_PDK13 19 +#define TARGET_ID_PDK14 20 +#define TARGET_ID_PDK15 21 +#define TARGET_ID_PDK16 22 + +/* Macro to test the target we are compiling for. + Can only be used after SDCCmain has defined the port + */ +#define TARGET_IS_MCS51 (port->id == TARGET_ID_MCS51) +#define TARGET_IS_AVR (port->id == TARGET_ID_AVR) +#define TARGET_IS_DS390 (port->id == TARGET_ID_DS390) +#define TARGET_IS_DS400 (port->id == TARGET_ID_DS400) +#define TARGET_IS_PIC14 (port->id == TARGET_ID_PIC14) +#define TARGET_IS_PIC16 (port->id == TARGET_ID_PIC16) +#define TARGET_IS_Z80 (port->id == TARGET_ID_Z80) +#define TARGET_IS_Z180 (port->id == TARGET_ID_Z180) +#define TARGET_IS_R2K (port->id == TARGET_ID_R2K) +#define TARGET_IS_R3KA (port->id == TARGET_ID_R3KA) +#define TARGET_IS_GBZ80 (port->id == TARGET_ID_GBZ80) +#define TARGET_IS_TLCS90 (port->id == TARGET_ID_TLCS90) +#define TARGET_IS_EZ80_Z80 (port->id == TARGET_ID_EZ80_Z80) +#define TARGET_IS_HC08 (port->id == TARGET_ID_HC08) +#define TARGET_IS_S08 (port->id == TARGET_ID_S08) +#define TARGET_IS_STM8 (port->id == TARGET_ID_STM8) +#define TARGET_IS_PDK13 (port->id == TARGET_ID_PDK13) +#define TARGET_IS_PDK14 (port->id == TARGET_ID_PDK14) +#define TARGET_IS_PDK15 (port->id == TARGET_ID_PDK15) +#define TARGET_IS_PDK16 (port->id == TARGET_ID_PDK16) + +#define TARGET_MCS51_LIKE (TARGET_IS_MCS51 || TARGET_IS_DS390 || TARGET_IS_DS400) +#define TARGET_Z80_LIKE (TARGET_IS_Z80 || TARGET_IS_Z180 || TARGET_IS_GBZ80 || TARGET_IS_R2K || TARGET_IS_R3KA || TARGET_IS_TLCS90 || TARGET_IS_EZ80_Z80) +#define TARGET_IS_RABBIT (TARGET_IS_R2K || TARGET_IS_R3KA) +#define TARGET_HC08_LIKE (TARGET_IS_HC08 || TARGET_IS_S08) +#define TARGET_PIC_LIKE (TARGET_IS_PIC14 || TARGET_IS_PIC16) +#define TARGET_PDK_LIKE (TARGET_IS_PDK13 || TARGET_IS_PDK14 || TARGET_IS_PDK15 || TARGET_IS_PDK16) + +/* is using sdas / sdld assembler / linker */ +#define IS_SDASLD (TARGET_Z80_LIKE || TARGET_MCS51_LIKE || TARGET_HC08_LIKE) + +#define MAX_BUILTIN_ARGS 16 +/* definition of builtin functions */ +typedef struct builtins +{ + char *name; /* name of builtin function */ + char *rtype; /* return type as string : see typeFromStr */ + int nParms; /* number of parms : max 8 */ + char *parm_types[MAX_BUILTIN_ARGS]; /* each parm type as string : see typeFromStr */ +} builtins; + +struct ebbIndex; + +/* pragma structure */ +struct pragma_s +{ + const char *name; + int id; + char deprecated; + int (*func) (int id, const char *name, const char *cp); +}; + +/* defined in SDCClex.lex */ +int process_pragma_tbl (const struct pragma_s *pragma_tbl, const char *s); + +/* Processor specific names */ +typedef struct +{ + /** Unique id for this target */ + const int id; + /** Target name used for -m */ + const char *const target; + + /** Target name string, used for --help */ + const char *const target_name; + + /** Specific processor for the given target family. specified by -p */ + char *processor; + + struct + { + /** Pointer to glue function */ + void (*do_glue) (void); + /** TRUE if all types of glue functions should be inserted into + the file that also defines main. + We dont want this in cases like the z80 where the startup + code is provided by a seperate module. + */ + bool glue_up_main; + /* OR of MODEL_* */ + int supported_models; + int default_model; + /** return the model string, used as library destination; + port->target is used as model string if get_model is NULL */ + const char *(*get_model) (void); + /** Pointer to GAS (GNU assembler) glue function. + * If none is available, it means there is no support + * for this port yet. */ + void (*gas_glue)(void); + } + general; + + /* assembler related information */ + struct + { + /** Command to run and arguments (eg as-z80) */ + const char **cmd; + /** Alternate macro based form. */ + const char *mcmd; + /** Arguments for debug mode. */ + const char *debug_opts; + /** Arguments for normal assembly mode. */ + const char *plain_opts; + /* print externs as global */ + int externGlobal; + /* assembler file extension */ + const char *file_ext; + /** If non-null will be used to execute the assembler. */ + void (*do_assemble) (set *); + } + assembler; + + /* linker related info */ + struct + { + /** Command to run (eg link-z80) */ + const char **cmd; + /** Alternate macro based form. */ + const char *mcmd; + /** If non-null will be used to execute the link. */ + void (*do_link) (void); + /** Extension for object files (.rel, .obj, ...) */ + const char *rel_ext; + /** 1 if port needs the .lnk file, 0 otherwise */ + const int needLinkerScript; + const char *const *crt; + const char *const *libs; + } + linker; + + /** Default peephole rules */ + struct + { + char *default_rules; + int (*getSize) (lineNode * line); + bitVect *(*getRegsRead) (lineNode * line); + bitVect *(*getRegsWritten) (lineNode * line); + bool (*deadMove) (const char *reg, lineNode * currPl, lineNode * head); + bool (*notUsed) (const char *reg, lineNode * currPl, lineNode * head); + bool (*canAssign) (const char *op1, const char *op2, const char *op3); + bool (*notUsedFrom) (const char *reg, const char *label, lineNode *head); + bool (*symmParmStack) (void); + } + peep; + + /** Basic type sizes */ + struct + { + int char_size; + int short_size; + int int_size; + int long_size; + int longlong_size; + int near_ptr_size; // __near + int far_ptr_size; // __far + int ptr_size; // generic + int funcptr_size; + int banked_funcptr_size; + int bit_size; + int float_size; + } + s; + + /** tags for far, near, xstack, code generic pointers */ + struct + { + int tag_far; + int tag_near; + int tag_xstack; + int tag_code; + } + gp_tags; + + /** memory regions related stuff */ + struct + { + const char *const xstack_name; + const char *const istack_name; + /* + * The following 2 items can't be const pointers + * due to ugly implementation in gbz80 target; + * this should be fixed in src/z80/main.c (borutr) + */ + const char *code_name; + const char *data_name; + const char *const idata_name; + const char *const pdata_name; + const char *const xdata_name; + const char *const bit_name; + const char *const reg_name; + const char *const static_name; + const char *const overlay_name; + const char *const post_static_name; + const char *const home_name; + const char *const xidata_name; // initialized xdata + const char *const xinit_name; // a code copy of xidata + const char *const const_name; // const data (code or not) + const char *const cabs_name; // const absolute data (code or not) + const char *const xabs_name; // absolute xdata/pdata + const char *const iabs_name; // absolute idata/data + const char *const initialized_name; // Initialized global (and static local) variables. + const char *const initializer_name; // A code copy of initialized_name (to be copied for fast initialization). + struct memmap *default_local_map; // default location for auto vars + struct memmap *default_globl_map; // default location for globl vars + int code_ro; // code space read-only 1=yes + unsigned int maxextalign; // maximum extended alignment supported, nonnegative power of 2 (C11 standard, section 6.2.8). + } + mem; + + struct + { + void (*genExtraAreaDeclaration) (FILE *, bool); + void (*genExtraAreaLinkOptions) (FILE *); + } + extraAreas; + + /* stack related information */ + struct + { + /** -1 for grows down (z80), +1 for grows up (mcs51) */ + int direction; + /** Extra overhead when calling between banks */ + int bank_overhead; + /** Extra overhead when the function is an ISR */ + int isr_overhead; + /** Standard overhead for a function call */ + int call_overhead; + /** Re-enterant space */ + int reent_overhead; + /** 'banked' call overhead. + Mild overlap with bank_overhead */ + int banked_overhead; + /** 0 if sp points to last item pushed, 1 if sp points to next location to use */ + int offset; + } + stack; + + struct + { + /** Size of the biggest shift the port can handle. -1 if port can handle shifts of arbitrary size. */ + signed int shift; + + /* Has support routines for int x int -> long multiplication and unsigned int x unsigned int -> unsigned long multiplication */ + bool has_mulint2long; + } + support; + + struct + { + void (*emitDebuggerSymbol) (const char *); + struct + { + int (*regNum) (const struct reg_info *); + bitVect *cfiSame; + bitVect *cfiUndef; + int addressSize; + int regNumRet; + int regNumSP; + int regNumBP; + int offsetSP; + } + dwarf; + } + debugger; + + struct + { + int maxCount; + int sizeofElement; + int sizeofMatchJump[3]; + int sizeofRangeCompare[3]; + int sizeofSubtract; + int sizeofDispatch; + } + jumptableCost; + + /** Prefix to add to a C function (eg "_") */ + const char *fun_prefix; + + /** Called once the processor target has been selected. + First chance to initalise and set any port specific variables. + 'port' is set before calling this. May be NULL. + */ + void (*init) (void); + /** Parses one option + its arguments */ + bool (*parseOption) (int *pargc, char **argv, int *i); + /** Optional list of automatically parsed options. Should be + implemented to at least show the help text correctly. */ + OPTION *poptions; + /** Initialise port spectific paths */ + void (*initPaths) (void); + /** Called after all the options have been parsed. */ + void (*finaliseOptions) (void); + /** Called after the port has been selected but before any + options are parsed. */ + void (*setDefaultOptions) (void); + /** Does the dirty work. */ + void (*assignRegisters) (struct ebbIndex *); + + /** Returns the register name of a symbol. + Used so that 'reg_info' can be an incomplete type. */ + const char *(*getRegName) (const struct reg_info *reg); + + int (*getRegByName) (const char *name); + + /** Try to keep track of register contents. */ + bool (*rtrackUpdate)(const char* line); + + /* list of keywords that are used by this + target (used by lexer) */ + char **keywords; + + /* Write any port specific assembler output. */ + void (*genAssemblerPreamble) (FILE * of); + /* invoked at end assembler file */ + void (*genAssemblerEnd) (FILE * of); + + /* Write the port specific IVT. If genIVT is NULL or if + * it returns zero, default (8051) IVT generation code + * will be used. + */ + int (*genIVT) (struct dbuf_s * oBuf, symbol ** intTable, int intCount); + + void (*genXINIT) (FILE * of); + + /* Write port specific startup code */ + void (*genInitStartup) (FILE * of); + + /* parameter passing in register related functions */ + void (*reset_regparms) (struct sym_link *); /* reset the register count */ + int (*reg_parm) (struct sym_link *, bool reentrant); /* will return 1 if can be passed in register */ + + /** Process the pragma string 'sz'. Returns 0 if recognised and + processed, 1 otherwise. May be NULL. + */ + int (*process_pragma) (const char *sz); + + /** Mangles a support function name to reflect the calling model. + */ + const char *(*getMangledFunctionName) (const char *szOrginial); + + /** Returns true if the port can multiply the two types nativly + without using support functions. + */ + bool (*hasNativeMulFor) (iCode *ic, sym_link *left, sym_link *right); + + /** Returns true if the port has implemented certain bit + manipulation iCodes (RRC, RLC, SWAP, GETHBIT, GETABIT, GETBYTE, GETWORD) + */ + bool (*hasExtBitOp) (int op, int size); + + /** Returns the relative expense of accessing a particular output + storage class. Larger values indicate higher expense. + */ + int (*oclsExpense) (struct memmap * oclass); + + /** If TRUE, then tprintf and !dw will be used for some initalisers + */ + bool use_dw_for_init; + + /** TRUE for targets with little endian byte ordering, FALSE for + targets with big endian byte ordering. + */ + bool little_endian; + + /* condition transformations */ + bool lt_nge; /* transform (a < b) to !(a >= b) */ + bool gt_nle; /* transform (a > b) to !(a <= b) */ + bool le_ngt; /* transform (a <= b) to !(a > b) */ + bool ge_nlt; /* transform (a >= b) to !(a < b) */ + bool ne_neq; /* transform a != b --> ! (a == b) */ + bool eq_nne; /* transform a == b --> ! (a != b) */ + + bool arrayInitializerSuppported; + bool (*cseOk) (iCode * ic, iCode * pdic); + builtins *builtintable; /* table of builtin functions */ + int unqualified_pointer; /* unqualified pointers type is */ + int reset_labelKey; /* reset Label no 1 at the start of a function */ + int globals_allowed; /* global & static locals not allowed ? 0 ONLY TININative */ + + int num_regs; /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + +#define PORT_MAGIC 0xAC32 + /** Used at runtime to detect if this structure has been completely filled in. */ + int magic; +} +PORT; + +extern PORT *port; + +#if !OPT_DISABLE_MCS51 +extern PORT mcs51_port; +#endif +#if !OPT_DISABLE_Z80 +extern PORT z80_port; +#endif +#if !OPT_DISABLE_Z180 +extern PORT z180_port; +#endif +#if !OPT_DISABLE_R2K +extern PORT r2k_port; /* Rabbit 2000/3000 */ +#endif +#if !OPT_DISABLE_R3KA +extern PORT r3ka_port; /* Rabbit 3000A */ +#endif +#if !OPT_DISABLE_GBZ80 +extern PORT gbz80_port; +#endif +#if !OPT_DISABLE_TLCS90 +extern PORT tlcs90_port; +#endif +#if !OPT_DISABLE_EZ80_Z80 +extern PORT ez80_z80_port; +#endif +#if !OPT_DISABLE_AVR +extern PORT avr_port; +#endif +#if !OPT_DISABLE_DS390 +extern PORT ds390_port; +#endif +#if !OPT_DISABLE_PIC14 +extern PORT pic_port; +#endif +#if !OPT_DISABLE_PIC16 +extern PORT pic16_port; +#endif +#if !OPT_DISABLE_TININative +extern PORT tininative_port; +#endif +#if !OPT_DISABLE_DS400 +extern PORT ds400_port; +#endif +#if !OPT_DISABLE_HC08 +extern PORT hc08_port; +#endif +#if !OPT_DISABLE_S08 +extern PORT s08_port; +#endif +#if !OPT_DISABLE_STM8 +extern PORT stm8_port; +#endif +#if !OPT_DISABLE_PDK13 +extern PORT pdk13_port; +#endif +#if !OPT_DISABLE_PDK14 +extern PORT pdk14_port; +#endif +#if !OPT_DISABLE_PDK15 +extern PORT pdk15_port; +#endif + +#endif /* PORT_INCLUDE */ diff --git a/src/port.mk b/src/port.mk new file mode 100644 index 0000000..5253e8b --- /dev/null +++ b/src/port.mk @@ -0,0 +1,52 @@ +# Common Makefile for all ports. +# Input: SOURCES - list of C files included in the project +# SPECIAL - list of special files that should be included in dependencies +# PEEPRULES - list of all peephole rules (.rul) derrived files +# PREBUILD - list of special files to build before deps. + +# Ports are always located in sdcc/src/ + +# Output +LIB = port.a +# Include the sdcc/src directory +INCLUDEFLAGS = -I$(srcdir)/.. -I.. + +# If the sources aren't specified, assume all in this directory. +ifndef SOURCES +SOURCES = $(sort $(notdir $(wildcard $(srcdir)/*.c))) +endif +ifndef CXXSOURCES +CXXSOURCES = $(sort $(notdir $(wildcard $(srcdir)/*.cc))) +endif + +# If the peephole rules aren't specified, assume all. +ifndef PEEPRULES +PEEPDEFS = $(notdir $(wildcard $(srcdir)/*.def)) +PEEPRULES = $(PEEPDEFS:.def=.rul) +endif + +PREBUILD += $(PEEPRULES) + +all: $(PREBUILD) dep $(LIB) + +include $(top_builddir)/Makefile.common + +$(LIB): $(OBJ) + rm -f $(LIB) + $(AR) rc $(LIB) $(OBJ) + $(RANLIB) $(LIB) + +%.rul: %.def + $(AWK) -f $(srcdir)/../SDCCpeeph.awk $< > $@ + +dep: Makefile.dep + +Makefile.dep: $(PREBUILD) Makefile $(SOURCES) $(CXXSOURCES) $(SPECIAL) + $(MAKEDEP) $(CPPFLAGS) $(filter %.c %.cc,$^) >Makefile.dep + +# don't include Makefile.dep for the listed targets: +ifeq "$(findstring $(MAKECMDGOALS),clean distclean)" "" + -include Makefile.dep +endif + +include $(srcdir)/../port-clean.mk diff --git a/src/regression/Makefile b/src/regression/Makefile new file mode 100644 index 0000000..26a7ff3 --- /dev/null +++ b/src/regression/Makefile @@ -0,0 +1,226 @@ +# Regression testing Makefile for Pic Port of SDCC +# +# note that this regression suite was started before +# the one in sdcc/regression. The regression suite in +# sdcc/regression is better suited for testing mature +# ports. +# +# GPL'd +# +# T. Scott Dattalo scott@dattalo.com +# +# This makefile provides a means by which the output +# of the SDCC Compiler can be tested. This version +# is unique to the PIC (as in Microchip PIC) port. +# As such it requires the following software: +# +# gpasm version 0.11.1 (or greater) +# gpsim version 0.20.7 (or greater) +# +# Usage: +# +# make +# - without any options the whole regression test is +# performed. The results are placed into a log file +# (defined by $LOGFILE). +# +# make asm +# - Creates .asm files by compiling the .c files +# +# make cod +# - Creates .cod files by assembling the .asm files +# (.cod files are symbolic files compatible with +# MPASM, Microchip's assembler) +# +# make stc +# - Creates .stc files which are script files for +# gpsim. +# +# make clean +# - removes all of the intermediate files created +# +# make cleancod +# make cleanasm +# make cleanstc +# make cleano +# - removes either the .stc, .asm, .cod or .o files + +# verboseness +#Q ?= # be verbose +Q ?= @ # be quiet + +SDCC_SRC=../.. +SDCC_BIN=../.. + +CC = $(SDCC_BIN)/bin/sdcc +LINKER = gplink +USE_PIC16 ?= 0 +ifeq ($(strip $(filter 1 yes,$(USE_PIC16))),) +TARGETPIC = 16f877 +#TARGETPIC = 16f84 +CFLAGS = -mpic14 -p$(TARGETPIC) +DIR = pic14 +else +TARGETPIC = 18f452 +CFLAGS = -mpic16 -p$(TARGETPIC) +DIR = pic16 +endif +CFLAGS += -Wl,-q --no-warn-non-free +CFLAGS += -Wl,--map +CFLAGS += -I $(SDCC_SRC)/device/include/$(DIR) -I $(SDCC_SRC)/device/non-free/include/$(DIR) +CFLAGS += -L $(SDCC_BIN)/device/lib/build/$(DIR) -L $(SDCC_BIN)/device/non-free/lib/build/$(DIR) +#CFLAGS += --no-pcode-opt +#CFLAGS += -V + +.SUFFIXES: .asm .c .cod .stc +.NOTPARALLEL: + +# Results of the test are placed here: +LOGFILE = test.log + +# Script file for creating gpsim scripts +CREATESTC = create_stc + +# Script file for invoking gpsim +SIMULATE = simulate + +# List the C files to be test here: +SRC = add.c \ + add2.c \ + add3.c \ + add4.c \ + and1.c \ + and2.c \ + arrays.c \ + b.c \ + bank1.c \ + bool1.c \ + bool2.c \ + bool3.c \ + call1.c \ + compare.c \ + compare10.c \ + compare2.c \ + compare3.c \ + compare4.c \ + compare5.c \ + compare6.c \ + compare7.c \ + compare8.c \ + compare9.c \ + configword.c \ + empty.c \ + for.c \ + init0.c \ + inline.c \ + mult1.c \ + nestfor.c \ + or1.c \ + pcodeopt.c \ + pointer1.c \ + ptrarg.c \ + ptrfunc.c \ + rotate1.c \ + rotate2.c \ + rotate3.c \ + rotate4.c \ + rotate5.c \ + rotate6.c \ + rotate7.c \ + string1.c \ + struct1.c \ + sub.c \ + sub2.c \ + switch1.c \ + while.c \ + xor.c + +COD := $(patsubst %.c, %.cod, $(SRC)) +ASM := $(patsubst %.c, %.asm, $(SRC)) +O := $(patsubst %.c, %.o, $(SRC)) +P := $(patsubst %.c, %.p, $(SRC)) +STC := $(patsubst %.c, %.stc, $(SRC)) +HEX := $(patsubst %.c, %.hex, $(SRC)) +LST := $(patsubst %.c, %.lst, $(SRC)) +MAP := $(patsubst %.c, %.map, $(SRC)) + +all: test + + +# The cod files are generated by sdcc +.c.cod: + $(Q)-$(CC) $(CFLAGS) $*.c + +# The .stc files are script files for gpsim +.cod.stc: + $(Q)-./$(CREATESTC) $*.cod $*.stc + $(Q)-./$(SIMULATE) $*.stc $(LOGFILE) + +# this will also make .stc files +#%.stc : %.cod +# ./create_stc $^ $@ + +# now for the dependencies + +cod : $(COD) + +o : $(O) + +asm : $(ASM) + +stc : $(STC) + echo $(STC) + +test: $(STC) + $(Q)echo "Done - Results are in $(LOGFILE)" + +cleancod: + files="$(COD)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleano: + files="$(O)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanp: + files="$(P)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanasm: + files="$(ASM)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanstc: + files="$(STC)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanhex: + files="$(HEX)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanlst: + files="$(LST)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +cleanmap: + files="$(MAP)" ; \ + for f in $$files ; do \ + if [ -f $$f ]; then rm $$f; fi \ + done ; \ + +clean: cleancod cleanasm cleanstc cleano cleanp cleanhex cleanlst cleanmap + if [ -f "$(LOGFILE)" ]; then rm $(LOGFILE); fi diff --git a/src/regression/add.c b/src/regression/add.c new file mode 100644 index 0000000..99fce7a --- /dev/null +++ b/src/regression/add.c @@ -0,0 +1,191 @@ +#include "gpsim_assert.h" + +// Addition tests + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + +/* Some compilers that support bit types do not support bit arithmetic + * (like bitx = bity + bitz;) */ +#define SUPPORT_BIT_ARITHMETIC 1 + +unsigned char failures=0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; +unsigned char achar3 = 0; +unsigned char *acharP = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void add_lit2uchar(void) +{ + + achar0 = achar0 + 5; + + if(achar0 != 5) + failures++; + + achar0 += 10; + + if(achar0 != 15) + failures++; + + achar0 = achar0 +1; // Should be an increment + if(achar0 != 16) + failures++; + + for(achar1 = 0; achar1 < 100; achar1++) + achar0 += 2; + + if(achar0 != 216) + failures++; + +} + +void add_uchar2uchar(void) +{ + + achar1 = achar1 + achar0; + + if(achar1 != 16) + failures++; + + for(achar2 = 0; achar2<7; achar2++) + achar1 += achar0; + + if(achar1 != 128) + failures++; + +} + +// assumes +// achar0 = 0 +// achar1 = 32 +// achar2, achar3 can be anything. + +void add_uchar2uchar2(void) +{ + achar0++; + achar0 = achar0 + 1; + achar0 = achar0 + 2; + achar0 = achar0 + 3; + if(achar0 != 7) + failures++; + + achar1 += achar0; + if(achar1 != 39) + failures++; + + achar2 = achar1 + achar0; + if(achar2 != 46) + failures++; + + achar3 = achar2 + achar1 + achar0; + if(achar3 != 92) + failures++; +} + +#if SUPPORT_BIT_TYPES +void add_bits(void) +{ + bit1 = bit0; + + bit0 = 1; + + if(bit1 != 0) + failures++; + + bit1 = bit1+bit0; + if(bit1 != 1) + failures++; + +#if SUPPORT_BIT_ARITHMETIC + bit2 = bit1+bit3; + if(!bit2) + failures++; + + bit3 = bit4+bit5+bit6+bit7+bit0; + if(!bit3) + failures++; +#endif +} +#endif + +/* add_bit2uchar(void) - assumes bit0 = 1, achar0 = 7 */ + +#if SUPPORT_BIT_TYPES +void add_bit2uchar(void) +{ + achar0 += bit0; + + if(achar0 != 8) + failures++; + + if(achar0 == bit0) + failures++; +} + +void add_bit2uint(void) +{ + if(aint0 != bit11) + failures++; + + aint0 += bit0; + if(aint0!=1) + failures++; +} +#endif + +void main(void) +{ + add_lit2uchar(); + ASSERT(MANGLE(failures) == 0); + + achar0=16; + achar1=0; + add_uchar2uchar(); + ASSERT(MANGLE(failures) == 0); + + + achar0 = 0; + achar1 = 32; + add_uchar2uchar2(); + ASSERT(MANGLE(failures) == 0); + +#if SUPPORT_BIT_TYPES + add_bits(); + + add_bit2uchar(); + add_bit2uint(); +#endif + ASSERT(MANGLE(failures) == 0); + + done(); +} diff --git a/src/regression/add2.c b/src/regression/add2.c new file mode 100644 index 0000000..f9536e4 --- /dev/null +++ b/src/regression/add2.c @@ -0,0 +1,326 @@ +#include "gpsim_assert.h" + +// Addition tests - mostly int's + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + + +unsigned char failures=0; + + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned int aint2 = 0; +unsigned int aint3 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; +unsigned char achar3 = 0; +unsigned char *acharP = 0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void add_lit2uint(void) +{ + + aint0 = aint0 + 5; + + if(aint0 != 5) + failures++; + + aint0 += 10; + + if(aint0 != 15) + failures++; + + aint0 = aint0 +1; // Should be an increment + if(aint0 != 16) + failures++; + + for(aint1 = 0; aint1 < 100; aint1++) + aint0 += 2; + + if(aint0 != 216) + failures++; + +} + +void add_uint2uint (void) +{ + + aint1 = aint1 + aint0; + + if(aint1 != 16) + failures++; + + for(aint2 = 0; aint2<7; aint2++) + aint1 += aint0; + + if(aint1 != 128) + failures++; + +} + +// assumes +// aint0 = 0 +// aint1 = 32 +// aint2, aint3 can be anything. + +void add_uint2uint2(void) +{ + + + aint0++; + aint0 = aint0 + 1; + aint0 = aint0 + 2; + aint0 = aint0 + 3; + if(aint0 != 7) + failures++; + + aint1 += aint0; + if(aint1 != 0x27) + failures++; + + aint2 = aint1 + aint0; + if(aint2 != 0x2e) + failures++; + + aint3 = aint2 + aint1 + aint0; + if(aint3 != 0x5c) + failures++; + + aint3 += 0xa0; + if(aint3 != 0xfc) + failures++; + + aint3 += aint0; + if(aint3 != 0x103) + failures++; + + aint1 += 0xffc0; + if(aint1 != 0xffe7) + failures++; + + aint3 = aint2 + aint1 + aint0; + if(aint3 != 0x1c) + failures++; + + +} + +#if SUPPORT_BIT_TYPES +void add_bits(void) +{ + + bit1 = bit0; + + bit0 = 1; + + if(bit1 != 0) + failures++; + + bit1 = bit1+bit0; + if(bit1 != 1) + failures++; + + bit2 = bit1+bit3; + if(!bit2) + failures++; + + bit3 = bit4+bit5+bit6+bit7+bit0; + if(!bit3) + failures++; +} +#endif + +/* add_bit2uchar(void) - assumes bit0 = 1, aint0 = 7 */ + +#if SUPPORT_BIT_TYPES +void add_bit2uchar(void) +{ + + achar0 += bit0; + + if(achar0 != 8) + failures++; + + if(achar0 == bit0) + failures++; + +} + +void add_bit2uint(void) +{ + + if(aint0 != bit11) + failures++; + + aint0 += bit0; + if(aint0!=1) + failures++; + +} +#endif + +/***********************************/ + +void addlits(void) +{ + aint0 += 0x0001; + + if(aint0 != 1) + failures++; + + aint0 += 0x00; + + if(aint0 != 1) + failures++; + + aint0 += 0x00fe; + if(aint0 != 0x00ff) + failures++; + + aint0 += 0x0001; + + if(aint0 != 0x0100) + failures++; + + aint0++; + if(aint0 != 0x0101) + failures++; + + aint0 += 0x00ff; + if(aint0 != 0x0200) + failures++; + + aint0 += 0x00a0; + if(aint0 != 0x02a0) + failures++; + + aint0 += 0x0061; + if(aint0 != 0x0301) + failures++; + + aint0 += 0x0100; + if(aint0 != 0x0401) + failures++; + + aint0 += 0x0101; + if(aint0 != 0x0502) + failures++; + + aint0 += 0x00fd; + if(aint0 != 0x05ff) + failures++; + + aint0 += 0x0101; + if(aint0 != 0x0700) + failures++; + + aint0 += 0x01ff; + if(aint0 != 0x08ff) + failures++; + + aint0 += 0x01ff; + if(aint0 != 0x0afe) + failures++; + + aint0 += 0xff02; + if(aint0 != 0x0a00) + failures++; + + aint0 += 0xffff; + if(aint0 != 0x09ff) + failures++; + + aint0 += 0xff01; + if(aint0 != 0x0900) + failures++; + + aint0 += 0xff00; + if(aint0 != 0x0800) + failures++; + + aint0 += 0xff01; + if(aint0 != 0x0701) + failures++; + + aint0 += 0x0300; + if(aint0 != 0x0a01) + failures++; + + aint0 += 0x03ff; + if(aint0 != 0x0e00) + failures++; + + aint0 += 0x0301; + if(aint0 != 0x1101) + failures++; + + aint0 += 0x03fe; + if(aint0 != 0x14ff) + failures++; + + aint0 += 0x0301; + if(aint0 != 0x1800) + failures++; + +} + + +void main(void) +{ + + add_lit2uint(); + + aint0=16; + aint1=0; + add_uint2uint(); + + + aint0 = 0; + aint1 = 32; + aint2 = 0; + add_uint2uint2(); + +#if SUPPORT_BIT_TYPES + add_bits(); + + achar0 = 7; + add_bit2uchar(); + + aint0 = 0; + bit0 = 1; + add_bit2uint(); +#endif + + aint0 = 0; + addlits(); + + done(); +} diff --git a/src/regression/add3.c b/src/regression/add3.c new file mode 100644 index 0000000..a1990af --- /dev/null +++ b/src/regression/add3.c @@ -0,0 +1,218 @@ +#include "gpsim_assert.h" + +// Addition tests - mostly int's + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + + +unsigned char failures=0; + + +char char0 = 0; +char char1 = 0; +char char2 = 0; +int int0 = 0; +int int1 = 0; +long long0 = 0; +long long1 = 0; +unsigned long ulong0 = 0; +unsigned long ulong1 = 0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void add_char2char(void) +{ + if(char0 != 4) + failures++; + if(char1 != 5) + failures++; + + char0 = char0 + char1; + + if(char0 != 9) + failures++; + + char0 += 127; + if(char0 > 0) + failures++; + + if(char0 != -0x78) + failures++; + + +} + +void add_compound_char(void) +{ + char0 = char1+5; + + if(char0 != 9) + failures++; + + if((char0+char1) != 13) + failures++; +} + +void add_int2int(void) +{ + if(int0 != 4) + failures++; + if(int1 != 5) + failures++; + + int0 += int1; + if(int0 != 9) + failures++; + + int0 += 0x7fff; + if(int0 != -0x7ff8) + failures++; + +} + +void add_compound_int(void) +{ + int0 = int1+5; + + if(int0 != 9) + failures++; + + if((int0+int1) != 13) + failures++; +} + + +void add_lit2long(void) +{ + + if(long0 != 0) + failures++; + + long0++; + + if(long0 != 1) + failures++; + + long0 = long0 + 0xff; + + if(long0 != 0x100) + failures++; + + long0 = long0 + 0x100; + if(long0 != 0x200) + failures++; + + + long0 = long0 + 0xfe00; + if(long0 != 0x10000) + failures++; + + long0 = long0 + 0xff0000; + if(long0 != 0x1000000) + failures++; + + long0 = long0 + 0x7e000000; + if(long0 != 0x7f000000) + failures++; + + /* wrap around zero */ + long0 = long0 + 0x2000000; + if(long0 != -0x7f000000) + failures++; + + long0 = long0 + 0x7f000000; + if(long0 != 0) + failures++; + +} + +void add_lit2ulong(void) +{ + + if(ulong0 != 0) + failures++; + + ulong0++; + + if(ulong0 != 1) + failures++; + + ulong0 = ulong0 + 0xff; + + if(ulong0 != 0x100) + failures++; + + ulong0 = ulong0 + 0x100; + if(ulong0 != 0x200) + failures++; + + + ulong0 = ulong0 + 0xfe00; + if(ulong0 != 0x10000) + failures++; + + ulong0 = ulong0 + 0xff0000; + if(ulong0 != 0x1000000) + failures++; + + ulong0 = ulong0 + 0x7e000000; + if(ulong0 != 0x7f000000) + failures++; + + ulong0 = ulong0 + 0x2000000; + if(ulong0 != 0x81000000) + failures++; + + /* wrap around zero */ + ulong0 = ulong0 + 0x7f000000; + if(ulong0) + failures++; + +} + +void main(void) +{ + char0=4; + char1 = char0 + 1; + add_char2char(); + + char1=4; + add_compound_char(); + + int0 = 4; + int1 = int0 + 1; + add_int2int(); + + int1=4; + add_compound_int(); + + add_lit2long(); + add_lit2ulong(); + + done(); +} diff --git a/src/regression/add4.c b/src/regression/add4.c new file mode 100644 index 0000000..ca370c5 --- /dev/null +++ b/src/regression/add4.c @@ -0,0 +1,82 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +//bit bit0 = 0; +int int0 = 0; +int int1 = 0; +char char0 = 0; +char char1 = 0; +long long0 = 0; +long long1 = 0; +unsigned long ulong0 = 0; +unsigned long ulong1 = 0; +#define NULL 0 +char *cP0=NULL; +char *cP1=NULL; +int *iP0=NULL; +int *iP1=NULL; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +/* pointer to char arithmetic */ + +void pc_add(void) +{ + + if(*cP1) + failures++; + + *cP1 += 1; + if(*cP1 != 1) + failures++; + + if(char0 != 1) + failures++; + + char0++; + + if(*cP1 != 2) + failures++; + + char1 = char0 + *cP1; + + if(char1 != 4) + failures++; +} + +/* pointer to integer arithmetic */ +void pi_add(void) +{ + if(*iP0) + failures++; + + *iP0 += 1; + + if(*iP0 != 1) + failures++; + + if(int0 != 1) + failures++; + + int1 = int0 + *iP0; + if(int1 != 2) + failures++; +} + +void main(void) +{ + + cP1 = &char0; + pc_add(); + + iP0 = &int0; + pi_add(); + + done(); +} diff --git a/src/regression/and1.c b/src/regression/and1.c new file mode 100644 index 0000000..bf9ecae --- /dev/null +++ b/src/regression/and1.c @@ -0,0 +1,141 @@ +#include "gpsim_assert.h" + +unsigned char failures=0; + + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +unsigned long ulong0 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// uchar0 = 0xff; +void and_lit2uchar(void) +{ + + if(uchar0 != 0xff) + failures++; + + uchar0 &= 0x7f; + + if(uchar0 != 0x7f) + failures++; + + uchar0 &= 0x3f; + + if(uchar0 != 0x3f) + failures++; + + uchar0 &= 0xdf; + + if(uchar0 != 0x1f) + failures++; +} + +void and_lit2uint(void) +{ + if(uint0 != 0xffff) + failures++; + + uint0 &= 0x7fff; + + if(uint0 != 0x7fff) + failures++; + + uint0 &= 0x3fff; + + if(uint0 != 0x3fff) + failures++; + + uint0 &= 0xdfff; + + if(uint0 != 0x1fff) + failures++; + + + uint0 &= 0xff7f; + + if(uint0 != 0x1f7f) + failures++; + + uint0 &= 0x0f0f; + + if(uint0 != 0x0f0f) + failures++; + + uint0 &= 0xfefe; + + if(uint0 != 0x0e0e) + failures++; + + uint0 &= 0xf0f0; + + if(uint0 != 0) + failures++; +} + +void and_lit2ulong(void) +{ + + if(ulong0 != 0xffffffff) + failures++; + + ulong0 &= 0x7fffffff; + + if(ulong0 != 0x7fffffff) + failures++; + + ulong0 &= 0xff00ffff; + + if(ulong0 != 0x7f00ffff) + failures++; + + ulong0 &= 0xfeff00ff; + + if(ulong0 != 0x7e0000ff) + failures++; +} + +/*-----------*/ +void and_uchar2uchar(void) +{ + + uchar0 &= uchar1; + + if(uchar0 != 0x0f) + failures++; + + uchar1 &= 0xf7; + + uchar0 = uchar1 & 0xfe; + + if(uchar0 != 0x06) + failures++; + +} + +void main(void) +{ + + uchar0 = 0xff; + and_lit2uchar(); + + uint0 = 0xffff; + and_lit2uint(); + + ulong0 = 0xffffffff; + and_lit2ulong(); + + uchar0 = 0xff; + uchar1 = 0x0f; + and_uchar2uchar(); + + done(); +} diff --git a/src/regression/and2.c b/src/regression/and2.c new file mode 100644 index 0000000..c667bad --- /dev/null +++ b/src/regression/and2.c @@ -0,0 +1,115 @@ +#include "gpsim_assert.h" + +unsigned char failures=0; + + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +unsigned long ulong0 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// uchar0 = 0x13; +void and_compound1(void) +{ + uchar0 = (uchar0 + 1) & 0x0f; + if(uchar0 != 4) + failures++; +} + + +// uchar1 = 0x42; +void and_compound2(void) +{ + uchar0 = (uchar1 + 1) & 0x0f; + if(uchar0 != 3) + failures++; + + if(uchar1 != 0x42) + failures++; +} + +// uchar0 = 0x13; +void or_compound1(void) +{ + uchar0 = (uchar0 + 0xe) | 0x0f; + if(uchar0 != 0x2f) + failures++; +} + + +// uchar1 = 0x47; +void or_compound2(void) +{ + uchar0 = (uchar1 + 0xf) | 0x0f; + if(uchar0 != 0x5f) + failures++; + + if(uchar1 != 0x47) + failures++; +} + + + +// uchar0 = 0x13; +void xor_compound1(void) +{ + uchar0 = (uchar0 + 1) ^ 0x0f; + if(uchar0 != 0x1b) + failures++; +} + + +// uchar1 = 0x47; +void xor_compound2(void) +{ + uchar0 = (uchar1 + 0xf) ^ 0x0f; + if(uchar0 != 0x59) + failures++; + + if(uchar1 != 0x47) + failures++; +} + +// uchar0 = 0x13; +void neg_compound1(void) +{ + uchar0 = ~(uchar0 + 1); + if(uchar0 != 0xeb) + failures++; +} + + +void main(void) +{ + + uchar0 = 0x13; + and_compound1(); + + uchar1 = 0x42; + and_compound2(); + + uchar0 = 0x13; + or_compound1(); + + uchar1 = 0x47; + or_compound2(); + + uchar0 = 0x13; + xor_compound1(); + + uchar1 = 0x47; + xor_compound2(); + + uchar0 = 0x13; + neg_compound1(); + + done(); +} diff --git a/src/regression/arrays.c b/src/regression/arrays.c new file mode 100644 index 0000000..8877692 --- /dev/null +++ b/src/regression/arrays.c @@ -0,0 +1,145 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +bool_or1 (void) +{ + + if ((achar0 > 0) || (achar1 > 0)) + failures++; +} + +void +bool_or2 (void) +{ + + if (achar0 || achar1) + failures++; +} + +void +bool_test1 (void) +{ + + if ((achar0 == 0) || achar1) + failures++; +} + + +void +bool_test2 (void) +{ + + if ((achar0 == 0) || aint0) + failures++; +} + +void +bool_and1 (void) +{ + + if (achar0 && achar1) + failures++; +} + +void +bin_or1 (void) +{ + + char t; + + t = achar0 | achar1; + if (t) + failures++; +} + +void +bin_xor1 (void) +{ + + if (achar0 ^ achar1) + failures++; +} + + +void +bool_test3 (void) +{ + + if ((achar0 == 0x42) || (achar1 == 42)) + failures++; +} + + +void +bool_or_lit1 (void) +{ + + achar0 |= 0x0f; + + if (achar0 > 0x10) + failures++; + + if ((achar0 | 0x10) > 0xf0) + failures++; + +} + +void +bool_and_lit1 (void) +{ + + achar0 &= 0xf0; + + if (achar0 > 0x10) + failures++; + + if ((achar0 & 0x10) > 0xf0) + failures++; + + achar0 &= 0xef; + +} + +void +main (void) +{ + + bool_or1 (); + bool_or2 (); + bool_and1 (); + bin_or1 (); + bin_xor1 (); + + achar0++; + bool_and1 (); + bool_test1 (); + bool_test2 (); + bool_test3 (); + + + achar0--; + achar1++; + bool_and1 (); + + achar0 = 0; + achar1 = 0; + + bool_or_lit1 (); + bool_and_lit1 (); + + done (); +} diff --git a/src/regression/b.c b/src/regression/b.c new file mode 100644 index 0000000..dd45f6c --- /dev/null +++ b/src/regression/b.c @@ -0,0 +1,64 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +unsigned char uchar0=0; +unsigned char uchar1=0; +unsigned char uchar2=0; + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +/* +void incptr(unsigned char *ucP) +{ + *ucP = *ucP + 1; +} +*/ + +void inc(unsigned char k) +{ + uchar0 = uchar0 + k; +} + +void f1(void) +{ + + uchar2++; +} + +void nested_call(unsigned char u) +{ + + f1(); + uchar1 = uchar1 + u; + inc(uchar1); + +} + // uchar1 = uchar1 + uchar0; + // uchar2 = uchar1 + k; + +void main(void) +{ + + uchar0=1; + //incptr(&uchar0); + inc(uchar0); + if(uchar0 !=2) + failures++; + + uchar0 = 2; + uchar1 = 1; + uchar2 = 1; + nested_call(uchar2); + + if(uchar0 !=4) + failures++; + + done(); +} diff --git a/src/regression/bank1.c b/src/regression/bank1.c new file mode 100644 index 0000000..3c58189 --- /dev/null +++ b/src/regression/bank1.c @@ -0,0 +1,51 @@ +#include "gpsim_assert.h" + +#if SUPPORT_BIT_TYPES +# define bit bit +#else +# define bit unsigned char +#endif + +unsigned char success=0; +unsigned char failures=0; +unsigned char dummy=0; +unsigned bit bit1; + +typedef unsigned char byte; + +byte d2; + +unsigned char uchar0 = 0xa5; + +__data __at (0xa0) unsigned char uc_bank1_temp = 0x42; +__data __at (0xa2) unsigned int ui_bank1_temp = 3; + +void +done(void) +{ + dummy++; + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void main(void) +{ + dummy = 0; + ui_bank1_temp = 0; + uc_bank1_temp = 0; + + bit1 = 0; + + uchar0 = (uchar0<<4) | (uchar0>>4); + + if(uchar0 > 7) { + dummy = 8; + uc_bank1_temp = failures; + } + + if(uc_bank1_temp > 3) + bit1 = 1; + + success = failures; + done(); +} diff --git a/src/regression/bool1.c b/src/regression/bool1.c new file mode 100644 index 0000000..46857bf --- /dev/null +++ b/src/regression/bool1.c @@ -0,0 +1,133 @@ +#include "gpsim_assert.h" + +unsigned char failures=0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void bool_or1(void) +{ + + if( (achar0 >0) || (achar1 >0 )) + failures++; +} + +void bool_or2(void) +{ + + if( achar0 || achar1) + failures++; +} + +void bool_test1(void) +{ + + if( (achar0==0) || achar1) + failures++; +} + + +void bool_test2(void) +{ + + if( (achar0==0) || aint0) + failures++; +} + +void bool_and1(void) +{ + + if( achar0 && achar1) + failures++; +} + +void bin_or1(void) +{ + + char t; + + t = achar0 | achar1; + if(t) + failures++; +} + +void bin_xor1(void) +{ + + if(achar0 ^ achar1) + failures++; +} + + +void bool_test3(void) +{ + + if((achar0 == 0x42) || (achar1 == 42)) + failures++; +} + + +void bool_or_lit1(void) +{ + + achar0 |= 0x0f; + + if(achar0 > 0x10) + failures++; + + if( (achar0 | 0x10) > 0xf0) + failures++; + +} + +void bool_and_lit1(void) +{ + + achar0 &= 0xf0; + + if(achar0 > 0x10) + failures++; + + if( (achar0 & 0x10) > 0xf0) + failures++; + + achar0 &= 0xef; + +} + +void main(void) +{ + + bool_or1(); + bool_or2(); + bool_and1(); + bin_or1(); + bin_xor1(); + + achar0++; + bool_and1(); + bool_test1(); + bool_test2(); + bool_test3(); + + + achar0--; achar1++; + bool_and1(); + + achar0=0; + achar1=0; + + bool_or_lit1(); + bool_and_lit1(); + + done(); +} diff --git a/src/regression/bool2.c b/src/regression/bool2.c new file mode 100644 index 0000000..3289d7c --- /dev/null +++ b/src/regression/bool2.c @@ -0,0 +1,50 @@ +#include "gpsim_assert.h" + +unsigned char failures=0; + +#if SUPPORT_BIT_TYPES +# define bit bit +#else +# define bit unsigned char +#endif + +bit bit0 = 0; +bit bit1 = 0; +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void bit_invert(void) +{ + + bit0 = !bit0; + bit1 = !bit1; + + if((bit0 != bit1) || (bit0 == 0)) + failures++; + +} + +void bit_copy(void) +{ + + bit0 = !bit0; + bit1 = bit0; +} + +void main(void) +{ + + bit_invert(); + bit_copy(); + + done(); +} diff --git a/src/regression/bool3.c b/src/regression/bool3.c new file mode 100644 index 0000000..d3092da --- /dev/null +++ b/src/regression/bool3.c @@ -0,0 +1,65 @@ +#include "gpsim_assert.h" + + +/* Compound comparisons */ + + +unsigned char failures=0; + +unsigned int ui0 = 0; +unsigned int ui1 = 0; +unsigned char uc0 = 0; +unsigned char uc1 = 0; +unsigned long uL0 = 0; +unsigned long uL1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void compound_compare_uc(void) +{ + + failures += (uc0 != uc1); + +} + + +void compound_compare_ui(void) +{ + + failures += (ui0 != ui1); + +} + +void compound_compare_ul(void) +{ + + failures += (uL0 != uL1); + +} + +void compound_compare_uc_lit(void) +{ + + failures += (uc0 != 0xff); + failures += (uc0 != 0xff); + failures += (uc0 == 0); + +} + +void main(void) +{ + + compound_compare_uc(); + compound_compare_ui(); + compound_compare_ul(); + + uc0 = 0xff; + compound_compare_uc_lit(); + + done(); +} diff --git a/src/regression/call1.c b/src/regression/call1.c new file mode 100644 index 0000000..f64a026 --- /dev/null +++ b/src/regression/call1.c @@ -0,0 +1,157 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; + +unsigned char call3 (void); + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +call1 (unsigned char uc0) +{ + if (uc0) + failures++; +} + +void +call2 (unsigned int ui0) +{ + if (ui0) + failures++; + +} + +unsigned char +call3 (void) +{ + if (uchar0) + failures++; + + return (failures); + +} + +unsigned int +call4 (void) +{ + unsigned int i = 0; + + if (uint0) + i++; + + return (i); + +} + +unsigned int +call5 (unsigned int k) +{ + + if (k) + failures++; + + return (k); + +} + +unsigned char +call6a(unsigned char uc) +{ + + if(uc>uchar1) + return 1; + else + return 0; + +} + +unsigned char +call6(unsigned char uc) +{ + + return(call6a(uc)); +} + +unsigned int +call7a(unsigned int ui) +{ + if(ui) + return 1; + else + return 0; + +} + +unsigned int +call7(unsigned int ui) +{ + + return(call7a(ui)); +} + +unsigned char +call8(unsigned char uc1,unsigned char uc2) +{ + + return uc1+uc2; +} + +void call9(unsigned int ui1, unsigned int ui2) +{ + + if(ui1 != 0x1234) + failures++; + if(ui2 != 0x5678) + failures++; + +} + +void +main (void) +{ + + call1 (uchar0); + call2 (uint0); + uchar1 = call3 (); + uint1 = call4 (); + if (uint1) + failures++; + + uint1 = call5 (uint0); + if (uint1) + failures++; + + if(call6(uchar0)) + failures++; + + + if(call7(0)) + failures++; + + if(!call7(1)) + failures++; + + if(!call7(0xff00)) + failures++; + + uchar0=4; + uchar1=3; + uchar0 = call8(uchar0,uchar1); + + if(uchar0 != 7) + failures++; + + call9(0x1234,0x5678); + + done (); +} diff --git a/src/regression/compare.c b/src/regression/compare.c new file mode 100644 index 0000000..29843f4 --- /dev/null +++ b/src/regression/compare.c @@ -0,0 +1,186 @@ +#include "gpsim_assert.h" +/* + + PIC PORT Test code + + */ + +/* + compare.c test compare + + */ + +unsigned char failures = 0; + +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned int aint0 = 0; +unsigned int aint1 = 0; + +char schar0 = 0; +char schar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +/* achar0 should be zero */ + +void +compare_char_to_lits1 (void) +{ + + if (achar0) + failures++; + + if (achar0 == 1) + failures++; + + if (achar0 == 7) + failures++; + + if (achar0 != 0) + failures++; +} + +/* achar0 should be `5' */ +void +compare_char_to_lits2 (void) +{ + + if (!achar0) + failures++; + + if (achar0 == 1) + failures++; + + if (achar0 == 7) + failures++; + + if (achar0 != 5) + failures++; +} + + +/* achar0 should equal achar1 */ +void +compare_char_to_char1 (void) +{ + + if (achar0 != achar1) + failures++; + + if (schar0 != schar1) + failures++; +} + +/* achar0 should be different than achar1 */ +void +compare_char_to_char2 (void) +{ + + if (achar0 == achar1) + failures++; + +} + +/* aint0 should be zero */ + +void +compare_int_to_lits1 (void) +{ + + if (aint0) + failures++; + + if (aint0 == 1) + failures++; + + if (aint0 == 7) + failures++; + + if (aint0 != 0) + failures++; +} + +/* aint0 should be `5' */ +void +compare_int_to_lits2 (void) +{ + + if (!aint0) + failures++; + + if (aint0 == 1) + failures++; + + if (aint0 == 7) + failures++; + + if (aint0 != 5) + failures++; +} + +/* aint0 should be `0x1234' */ +void +compare_int_to_lits3 (void) +{ + + if (!aint0) + failures++; + + if (aint0 == 1) + failures++; + + if (aint0 == 7) + failures++; + + if (aint0 != 0x1234) + failures++; +} + +/* aint0 should equal aint1 */ +void +compare_int_to_int1 (void) +{ + + if (aint0 != aint1) + failures++; + +} + +/* aint0 should be different than aint1 */ +void +compare_int_to_int2 (void) +{ + + if (aint0 == aint1) + failures++; + +} + +void +main (void) +{ + + compare_char_to_lits1 (); + compare_char_to_char1 (); + achar0 = 5; + compare_char_to_lits2 (); + compare_char_to_char2 (); + + + compare_int_to_lits1 (); + aint0 = 5; + compare_int_to_lits2 (); + aint0 = 0x1234; + compare_int_to_lits3 (); + compare_int_to_int2 (); + aint0 = 0; + compare_int_to_int1 (); + + done (); +} diff --git a/src/regression/compare10.c b/src/regression/compare10.c new file mode 100644 index 0000000..b3ed5cb --- /dev/null +++ b/src/regression/compare10.c @@ -0,0 +1,322 @@ +#include "gpsim_assert.h" +#include "picregs.h" + +// Signed comparisons of the form: (variable>=LIT) +// +// This regression test exercises all of the boundary +// conditions in literal less than comparisons. There +// are numerous opportunities to optimize these comparison +// and each one has an astonishing capability of failing +// a boundary condition. + +unsigned char failures = 0; +unsigned char result = 0; + +//bit bit0 = 0; +int int0 = 0; +int int1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +signed char char0 = 0; +signed char char1 = 0; +char long0 = 0; +char long1 = 0; + +/* *** NOTE *** This particular test takes quite a while to run + * ~ 10,000,000 instruction cycles. (2.5 seconds on a 20Mhz PIC). + * The WDT will reset the CPU if it's enabled. So disable it... +*/ + +typedef unsigned int word; + +//word at 0x2007 CONFIG = wdt_off & pwrte_on; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + + +void c_char_gte_lit1(unsigned char expected_result) +{ + result = 0; + + if(char0 >= -0x7f) + result |= 1; + + + if(char0 >= -1) + result |= 2; + + if(char0 >= 0) + result |= 4; + + if(char0 >= 1) + result |= 8; + + if(char0 >= 0x7e) + result |= 0x10; + + if(char0 >= 0x7f) + result |= 0x20; + + if(result != expected_result) + failures++; +} + + +void char_compare(void) +{ + char0 = 0x7f; + c_char_gte_lit1(0x3f); + + char0 = 0x7e; + c_char_gte_lit1(0x1f); + + char0 = 0x40; + c_char_gte_lit1(0x0f); + + char0 = 0x2; + c_char_gte_lit1(0x0f); + + char0 = 0x1; + c_char_gte_lit1(0x0f); + + char0 = 0; + c_char_gte_lit1(0x07); + + char0 = -1; + c_char_gte_lit1(0x03); + + char0 = -2; + c_char_gte_lit1(0x01); + + char0 = -0x40; + c_char_gte_lit1(0x01); + + char0 = -0x7e; + c_char_gte_lit1(0x01); + + char0 = -0x7f; + c_char_gte_lit1(0x01); + + char0 = 0x80; + c_char_gte_lit1(0x00); + + + /* Now test entire range */ + + for(char0=1; char0 != 0x7e; char0++) + c_char_gte_lit1(0x0f); + + for(char0=-0x7f; char0 != -1; char0++) + c_char_gte_lit1(0x01); + +} + + +void c_int_gte_lit1(unsigned char expected_result) +{ + result = 0; + + if(int0 >= 0) + result |= 1; + + if(int0 >= 1) + result |= 2; + + + if(int0 >= 0xff) + result |= 4; + + if(int0 >= 0x100) + result |= 8; + + if(int0 >= 0x0101) + result |= 0x10; + + if(int0 >= 0x01ff) + result |= 0x20; + + if(int0 >= 0x0200) + result |= 0x40; + + if(int0 >= 0x0201) + result |= 0x80; + + if(result != expected_result) + failures=1; + +} + + +void int_compare1(void) +{ + int0 = -1; + c_int_gte_lit1(0x00); + + int0 = 0; + c_int_gte_lit1(0x01); + + int0 = 1; + c_int_gte_lit1(0x03); + + int0 = 2; + c_int_gte_lit1(0x03); + + int0 = 0xfe; + c_int_gte_lit1(0x03); + + int0 = 0xff; + c_int_gte_lit1(0x07); + + int0 = 0x100; + c_int_gte_lit1(0x0f); + + int0 = 0x101; + c_int_gte_lit1(0x1f); + + int0 = 0x102; + c_int_gte_lit1(0x1f); + + int0 = 0x1fe; + c_int_gte_lit1(0x1f); + + int0 = 0x1ff; + c_int_gte_lit1(0x3f); + + int0 = 0x200; + c_int_gte_lit1(0x7f); + + int0 = 0x201; + c_int_gte_lit1(0xff); + + int0 = 0x7f00; + c_int_gte_lit1(0xff); + + /* now check contiguous ranges */ + + for(int0 = -0x7fff; int0 != -1; int0++) + c_int_gte_lit1(0x00); + + for(int0 = 1; int0 != 0xff; int0++) + c_int_gte_lit1(0x03); + + for(int0 = 0x201; int0 != 0x7fff; int0++) + c_int_gte_lit1(0xff); + +} + + +void c_int_gte_lit2(unsigned char expected_result) +{ + result = 0; + + if(int0 >= -0x7fff) + result |= 1; + + if(int0 >= -0x7f00) + result |= 2; + + if(int0 >= -0x7eff) + result |= 4; + + if(int0 >= -0x7e00) + result |= 8; + + if(int0 >= -0x0101) + result |= 0x10; + + if(int0 >= -0x0100) + result |= 0x20; + + if(int0 >= -0xff) + result |= 0x40; + + if(int0 >= -1) + result |= 0x80; + + if(result != expected_result) + failures=1; +} + +void int_compare2(void) +{ + int0 = -0x7fff; + c_int_gte_lit2(0x01); + + int0 = -0x7f00; + c_int_gte_lit2(0x03); + + int0 = -0x7eff; + c_int_gte_lit2(0x07); + + int0 = -0x7e00; + c_int_gte_lit2(0x0f); + + int0 = -0x7dff; + c_int_gte_lit2(0x0f); + + int0 = -0x4567; + c_int_gte_lit2(0x0f); + + int0 = -0x200; + c_int_gte_lit2(0x0f); + + int0 = -0x102; + c_int_gte_lit2(0x0f); + + int0 = -0x101; + c_int_gte_lit2(0x1f); + + int0 = -0x100; + c_int_gte_lit2(0x3f); + + int0 = -0xff; + c_int_gte_lit2(0x7f); + + int0 = -0x02; + c_int_gte_lit2(0x7f); + + int0 = -0x01; + c_int_gte_lit2(0xff); + + int0 = 0; + c_int_gte_lit2(0xff); + + int0 = 1; + c_int_gte_lit2(0xff); + + int0 = 0x7fff; + c_int_gte_lit2(0xff); + + /* now check contiguous ranges */ + + for(int0 = -0x7fff; int0 != -0x7f00; int0++) + c_int_gte_lit2(0x01); + + for(int0 = -0x7e00; int0 != -0x101; int0++) + c_int_gte_lit2(0x0f); + + for(int0 = -1; int0 != 0x7fff; int0++) + c_int_gte_lit2(0xff); + +} + + + + +void +main (void) +{ + char_compare(); + __asm clrwdt __endasm; + int_compare1(); + __asm clrwdt __endasm; + int_compare2(); + __asm clrwdt __endasm; + + done (); +} diff --git a/src/regression/compare2.c b/src/regression/compare2.c new file mode 100644 index 0000000..d14aeb0 --- /dev/null +++ b/src/regression/compare2.c @@ -0,0 +1,362 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; +unsigned char dummy = 0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +char_lt_char (void) +{ + if (achar0 < achar1) + failures++; +} + +void +char_gt_char (void) +{ + if (achar1 > achar0) + failures++; +} + +void +char_lte_char (void) +{ + if (achar0 <= achar1) + failures++; +} + +void +char_gte_char (void) +{ + if (achar1 >= achar0) + failures++; +} + +void +char_lt_lit (void) +{ + if (achar1 < 0x10) + failures++; +} + +void +char_gt_lit (void) +{ + if (achar1 > 0x10) + failures++; +} + +void +char_lte_lit (void) +{ + if (achar1 <= 0x0f) + failures++; +} + +void +char_gte_lit (void) +{ + if (achar1 >= 0x11) + failures++; +} + + +/* now repeat test using negative logic */ +void +char_lt_char_else (void) +{ + if (achar0 >= achar1) + dummy++; + else + failures++; +} + +void +char_gt_char_else (void) +{ + if (achar1 <= achar0) + dummy++; + else + failures++; +} + +void +char_lte_char_else (void) +{ + if (achar0 > achar1) + dummy++; + else + failures++; +} + +void +char_gte_char_else (void) +{ + if (achar1 < achar0) + dummy++; + else + failures++; +} + +void +char_lt_lit_else (void) +{ + if (achar1 >= 0x10) + dummy++; + else + failures++; +} + +void +char_gt_lit_else (void) +{ + if (achar1 <= 0x10) + dummy++; + else + failures++; +} + +void +char_lte_lit_else (void) +{ + if (achar1 > 0x0f) + dummy++; + else + failures++; +} + +void +char_gte_lit_else (void) +{ + if (achar1 < 0x11) + dummy++; + else + failures++; +} + + +/* ints */ + +void +int_lt_int (void) +{ + if (aint0 < aint1) + failures++; +} + +void +int_gt_int (void) +{ + if (aint1 > aint0) + failures++; +} + +void +int_lte_int (void) +{ + if (aint0 <= aint1) + failures++; +} + +void +int_gte_int (void) +{ + if (aint1 >= aint0) + failures++; +} + +void +int_lt_lit (void) +{ + if (aint1 < 0x10) + failures++; +} + +void +int_gt_lit (void) +{ + if (aint1 > 0x10) + failures++; +} + +void +int_lte_lit (void) +{ + if (aint1 <= 0x0f) + failures++; +} + +void +int_gte_lit (void) +{ + if (aint1 >= 0x11) + failures++; +} + + + + +/* now repeat int comparisons using negative logic */ + +void +int_lt_int_else (void) +{ + if (aint0 >= aint1) + dummy++; + else + failures++; +} + +void +int_gt_int_else (void) +{ + if (aint1 <= aint0) + dummy++; + else + failures++; +} + +void +int_lte_int_else (void) +{ + if (aint0 > aint1) + dummy++; + else + failures++; +} + +void +int_gte_int_else (void) +{ + if (aint1 < aint0) + dummy++; + else + failures++; +} + +void +int_lt_lit_else (void) +{ + if (aint1 >= 0x10) + dummy++; + else + failures++; +} + +void +int_gt_lit_else (void) +{ + if (aint1 <= 0x10) + dummy++; + else + failures++; +} + +void +int_lte_lit_else (void) +{ + if (aint1 > 0x0f) + dummy++; + else + failures++; +} + +void +int_gte_lit_else (void) +{ + if (aint1 < 0x11) + dummy++; + else + failures++; +} + + + + + + +void +main (void) +{ + + char_lt_char (); + char_gt_char (); + + achar0++; + char_lt_char (); + char_gt_char (); + char_gte_char (); + char_lte_char (); + + achar1 = 0x10; + char_lt_lit (); + char_gt_lit (); + char_lte_lit (); + char_gte_lit (); + + + achar0 = 0; + achar1 = 0; + + char_lt_char_else (); + char_gt_char_else (); + + achar0++; + char_lt_char_else (); + char_gt_char_else (); + char_gte_char_else (); + char_lte_char_else (); + + achar1 = 0x10; + char_lt_lit_else (); + char_gt_lit_else (); + char_lte_lit_else (); + char_gte_lit_else (); + + + + int_lt_int (); + int_gt_int (); + + aint0++; + int_lt_int (); + int_gt_int (); + int_gte_int (); + int_lte_int (); + + aint1 = 0x10; + int_lt_lit (); + int_gt_lit (); + int_lte_lit (); + int_gte_lit (); + + aint0=0; + aint1=0; + int_lt_int_else (); + int_gt_int_else (); + + aint0++; + int_lt_int_else (); + int_gt_int_else (); + int_gte_int_else (); + int_lte_int_else (); + + aint1 = 0x10; + int_lt_lit_else (); + int_gt_lit_else (); + int_lte_lit_else (); + int_gte_lit_else (); + + done (); +} diff --git a/src/regression/compare3.c b/src/regression/compare3.c new file mode 100644 index 0000000..480eceb --- /dev/null +++ b/src/regression/compare3.c @@ -0,0 +1,247 @@ +#include "gpsim_assert.h" +// +// compare3.c +// regression testing program for comparing literals to variables +// + + +unsigned char failures = 0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// compare to 0 +// assumes +// achar0 == 0 +// achar1 != 0 +// aint0 == 0 +// aint1 != 0 + +void c_0(void) +{ + + if(achar0 != 0) + failures++; + + if(achar0) + failures++; + + if(achar1 == 0) + failures++; + + if(!achar1) + failures++; + + if(aint0 != 0) + failures++; + + if(aint0) + failures++; + + if(aint1 == 0) + failures++; + + if(!aint1) + failures++; + +} + +// compare to 1 +// assumes +// achar0 != 1 +// achar1 == 1 +// aint0 != 1 +// aint1 == 1 + +void c_1(void) +{ + + if(achar0 == 1) + failures++; + + if(achar1 != 1) + failures++; + + if(aint0 == 1) + failures++; + + if(aint1 != 1) + failures++; + +} + +// compare to 2 +// assumes +// achar0 == 2 +// aint0 == 2 + +void c_2(void) +{ + + if(achar0 != 2) + failures++; + + if(aint0 != 2) + failures++; + +} + +// compare to 0xff +// assumes +// achar0 == 0xff +// aint0 == 0xff + +void c_ff(void) +{ + + if(achar0 != 0xff) + failures++; + + if(aint0 != 0xff) + failures++; + + if(aint0 == 0xfe) + failures++; + + if(aint0 == 0xff00) + failures++; + +} + + +// compare to 0x00a5 +// assumes +// achar0 == 0xa5 +// aint0 == 0x00a5 + +void c_a5(void) +{ + + if(achar0 != 0xa5) + failures++; + + if(aint0 != 0xa5) + failures++; + + if(aint0 == 0xa4) + failures++; + + if(aint0 == 0xa500) + failures++; + +} + +// compare to 0xa500 +// assumes +// achar0 == 0xa5 +// aint0 == 0xa500 + +void c_a500(void) +{ + + if(achar0 == 0xa500) + failures++; + + if(aint0 != 0xa500) + failures++; + + if(aint0 == 0xa400) + failures++; + + if(aint0 == 0x00a5) + failures++; + +} + +// compare to 0xabcd +// assumes +// achar0 == 0xa5 +// aint0 == 0xabcd + +void c_abcd(void) +{ + + if(achar0 == 0xabcd) + failures++; + + if(aint0 != 0xabcd) + failures++; + + if(aint0 == 0xab00) + failures++; + + if(aint0 == 0x00cd) + failures++; + + if(aint0 == 0x05cd) + failures++; + + if(aint0 == 0xab05) + failures++; + + if(aint0 == 0xab01) + failures++; + + if(aint0 == 0x01cd) + failures++; + + // if(aint0 == 0x1234abcd) + // failures++; + +} + +// assumes achar1 == 0 +void c_ifelse1(void) +{ + + if(achar0) + achar0 = achar1; + else + achar0 = 0; + + if(achar0) + failures++; +} + +void +main (void) +{ + + aint1 = 1; + achar1 = 1; + c_0(); + c_1(); + + aint0 = 2; + achar0 = 2; + c_2(); + + aint0 = 0xff; + achar0 = 0xff; + c_ff(); + + aint0 = 0xa5; + achar0 = 0xa5; + c_a5(); + + aint0 = 0xabcd; + c_abcd(); + + achar0 = 0; + achar1 = 0; + c_ifelse1(); + + achar0 = 1; + c_ifelse1(); + + done (); +} diff --git a/src/regression/compare4.c b/src/regression/compare4.c new file mode 100644 index 0000000..98c22ca --- /dev/null +++ b/src/regression/compare4.c @@ -0,0 +1,317 @@ +#include "gpsim_assert.h" +// +// compare4.c +// regression testing program for comparing signed chars and ints +// + +//#define COMPARE_OUT_OF_RANGE 1 + +unsigned char failures = 0; + +int int0 = 0; +int int1 = 0; +char char0 = 0; +char char1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// compare to 0 +// assumes +// char0 == 0 +// char1 != 0 +// int0 == 0 +// int1 != 0 + +void c_0(void) +{ + + if(char0 != 0) + failures++; + + if(char0) + failures++; + + if(char1 == 0) + failures++; + + if(!char1) + failures++; + + if(int0 != 0) + failures++; + + if(int0) + failures++; + + if(int1 == 0) + failures++; + + if(!int1) + failures++; + + if(char0>0) + failures++; + + if(int0>0) + failures++; + + if(char0<0) + failures++; + + if(int0<0) + failures++; + +} + +// compare to 1 +// assumes +// char0 != 1 +// char1 == 1 +// int0 != 1 +// int1 == 1 + +void c_1(void) +{ + + if(char0 == 1) + failures++; + + if(char1 != 1) + failures++; + + if(int0 == 1) + failures++; + + if(int1 != 1) + failures++; + + if(char0 < 0) + failures++; + + if(int0 < 0) + failures++; + +} + +// compare to 2 +// assumes +// achar0 == 2 +// aint0 == 2 + +void c_2(void) +{ + + if(char0 != 2) + failures++; + + if(int0 != 2) + failures++; + +} + +// compare to 0xff +// assumes +// achar0 == 0xff +// aint0 == 0xff + +void c_ff(void) +{ + + if(char0 != (char)0xff) + failures++; + + if(int0 != 0xff) + failures++; + + if(int0 == 0xfe) + failures++; + + if(int0 == 0xff00) + failures++; + +} + + +// compare to 0x00a5 +// assumes +// char0 == 0xa5 +// int0 == 0x00a5 + +void c_a5(void) +{ + + if(char0 != (char)0xa5) + failures++; + + if(int0 != 0xa5) + failures++; + + if(int0 == 0xa4) + failures++; + + if(int0 == 0xa500) + failures++; + +} + +// compare to 0xa500 +// assumes +// char0 == 0xa5 +// int0 == 0xa500 + +void c_a500(void) +{ + +#ifdef COMPARE_OUT_OF_RANGE + if(char0 == 0xa500) + failures++; +#endif + + if(int0 != 0xa500) + failures++; + + if(int0 != 0x44) + int0 = 0x28; + + if(int0 == 0xa400) + failures++; + + if(int0 == 0x00a5) + failures++; + +} + +// compare to 0xabcd +// assumes +// char0 == 0xa5 +// int0 == 0xabcd + +void c_abcd(void) +{ +#ifdef COMPARE_OUT_OF_RANGE + if(char0 == 0xabcd) + failures++; +#endif +/* + if(int0 != 0xabcd) + failures++; +*/ + if(int0 == 0xab00) + failures++; + + if(int0 == 0x00cd) + failures++; + + if(int0 == 0x05cd) + failures++; + + if(int0 == 0xab05) + failures++; + + if(int0 == 0xab01) + failures++; + + if(int0 == 0x01cd) + failures++; + + if(int0 > 0) + failures++; + +#ifdef COMPARE_OUT_OF_RANGE + if(int0 == 0x1234abcd) + failures++; +#endif + +} + +// assumes char1 == 0 +void c_ifelse1(void) +{ + + if(char0) + char0 = char1; + else + char0 = 0; + + if(char0) + failures++; +} + +// assumes char0 = -1 +// assumes int0 = -1 + +void c_minus1(void) +{ + if(char0 != -1) + failures++; + + if(int0 != -1) + failures++; + + if(char0 != int0) + failures++; + + if(char0>0) + failures++; + + if(int0>0) + failures++; +} + +void c_c0gtc1(void) +{ + + if(char0 < char1) + failures++; +} + +void +main (void) +{ + + int1 = 1; + char1 = 1; + c_0(); + c_1(); + + int0 = 2; + char0 = 2; + c_2(); + + int0 = 0xff; + char0 = 0xff; + c_ff(); + + int0 = 0xa5; + char0 = 0xa5; + c_a5(); + + int0 = 0xabcd; + //c_abcd(); + + char0 = 0; + char1 = 0; + c_ifelse1(); + + char0 = 1; + c_ifelse1(); + + char0 = -1; + int0 = -1; + c_minus1(); + + + char0 = 5; + char1 = 3; + c_c0gtc1(); + + char1 = -3; + c_c0gtc1(); + + done (); +} diff --git a/src/regression/compare5.c b/src/regression/compare5.c new file mode 100644 index 0000000..238755a --- /dev/null +++ b/src/regression/compare5.c @@ -0,0 +1,429 @@ +#include "gpsim_assert.h" +// +// compare5.c +// regression testing program for comparing longs +// + +#define COMPARE_OUT_OF_RANGE 1 + +unsigned char failures = 0; + +int int0 = 0; +int int1 = 0; +char char0 = 0; +char char1 = 0; +long long0 = 0; +long long1 = 0; +unsigned long ulong0 = 0; +unsigned long ulong1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// compare to 0 +// assumes +// long0 == 0 +// ulong0 == 0 + +void c_0(void) +{ + + if(long0 != 0) + failures++; + + if(long0 > 0) + failures++; + + if(ulong0 != 0) + failures++; + + if(ulong0 > 0) + failures++; + +} + +// compare to 1 +// assumes +// long1 == 1 +// ulong1 == 1 + +void c_1(void) +{ + + if(long0 == 1) + failures++; + + if(long1 != 1) + failures++; + + if(ulong0 == 1) + failures++; + + if(ulong1 != 1) + failures++; + + if(long1 < 0) + failures++; + + if(long1 < 1) + failures++; + + if(ulong1 < 1) + failures++; + + if(long1 > 1) + failures++; + + if(ulong1 > 1) + failures++; +} + +// compare to 2 +// assumes +// long0 == 2 +// ulong0 == 2 + +void c_2(void) +{ + + if(long0 != 2) + failures++; + + if(ulong0 != 2) + failures++; + + if(long1 == 2) + failures++; + + if(ulong1 == 2) + failures++; + +} + +// compare to 0xff +// assumes +// achar0 == 0xff +// aint0 == 0xff + +void c_ff(void) +{ + + if(long0 != 0xff) + failures++; + + if(ulong0 != 0xff) + failures++; + + if(long1 == 0xff) + failures++; + + if(ulong1 == 0xff) + failures++; + + +} + +// compare to 0x200 +// assumes +// achar0 == 0x200 +// aint0 == 0x200 + +void c_200(void) +{ + + if(long0 != 0x200) + failures++; + + if(ulong0 != 0x200) + failures++; + + if(long1 == 0x200) + failures++; + + if(ulong1 == 0x200) + failures++; + + +} + +// compare to 0x20000 +// assumes +// long0 == 0x20000 +// ulong0 == 0x20000 +// long1 != 0x20000 +// ulong1 != 0x20000 + +void c_20000(void) +{ + + if(long0 != 0x20000) + failures++; + + if(ulong0 != 0x20000) + failures++; + + if(long1 == 0x20000) + failures++; + + if(ulong1 == 0x20000) + failures++; + + if(long0 <= 0x10000) + failures++; + + if(long0 < 0x10000) + failures++; + +/* if(long0 < 0x12345) + failures++; +*/ + if(long0 == 0) + failures++; +} + +// compare to 0x00a5 +// assumes +// char0 == 0xa5 +// int0 == 0x00a5 + +void c_a5(void) +{ + + if(char0 != 0xa5) + failures++; + + if(int0 != 0xa5) + failures++; + + if(int0 == 0xa4) + failures++; + + if(int0 == 0xa500) + failures++; + +} + +// compare to 0xa500 +// assumes +// char0 == 0xa5 +// int0 == 0xa500 + +void c_a500(void) +{ + +#ifdef COMPARE_OUT_OF_RANGE + if(char0 == 0xa500) + failures++; +#endif + + if(int0 != 0xa500) + failures++; + + if(int0 == 0xa400) + failures++; + + if(int0 == 0x00a5) + failures++; + +} + +// compare to 0xabcd +// assumes +// char0 == 0xa5 +// int0 == 0xabcd + +void c_abcd(void) +{ +#ifdef COMPARE_OUT_OF_RANGE + if(char0 == 0xabcd) + failures++; +#endif + + if(int0 != 0xabcd) + failures++; + + if(int0 == 0xab00) + failures++; + + if(int0 == 0x00cd) + failures++; + + if(int0 == 0x05cd) + failures++; + + if(int0 == 0xab05) + failures++; + + if(int0 == 0xab01) + failures++; + + if(int0 == 0x01cd) + failures++; + + if(int0 > 0) + failures++; + +#ifdef COMPARE_OUT_OF_RANGE + if(int0 == 0x1234abcd) + failures++; +#endif + +} + +// assumes char1 == 0 +void c_ifelse1(void) +{ + + if(char0) + char0 = char1; + else + char0 = 0; + + if(char0) + failures++; +} + +// assumes char0 = -1 +// assumes int0 = -1 + +void c_minus1(void) +{ + if(long0 != -1) + failures++; + + if(long0 > 0) + failures++; + + if(long1 < 0) + failures++; +/* + if(long1 < 2) + failures++; +*/ +} + +// assumes +// long0 = long1 = ulong0 = ulong1 == 0 + +void c_long2long_eq(void) +{ + + if(long0 != long1) + failures++; + + if(ulong0 != ulong1) + failures++; + + if(long0 != ulong1) + failures++; + + if(long0 > long1) + failures++; + + if(long0 < long1) + failures++; + + if(long0 > ulong0) + failures++; + + if(long0 < ulong0) + failures++; +} + +// assumes +// long0 = ulong0 == 0 +// long1 = ulong1 == 1 + +void c_long2long_neq(void) +{ + + if(long0 == long1) + failures++; + + if(ulong0 == ulong1) + failures++; + + if(long1 != ulong1) + failures++; + + if(long1 < long0) + failures++; + + if(long1 <= long0) + failures++; + + if(ulong1 < ulong0) + failures++; + + if(ulong1 <= ulong0) + failures++; + +} + + +// long0=-100; +// long1=-1000; +void +c_long2neglit(void) +{ + + if(long0>0) + failures++; + if(long1>0) + failures++; + + if(long1 > long0) + failures++; + + if(long1 > 100) + failures++; + + if(long0 > -50) + failures++; + + if(long1 < -5000) + failures++; +} + +void +main (void) +{ + + c_0(); + + c_long2long_eq(); + + long1 = 1; + ulong1 = 1; + c_1(); + c_long2long_neq(); + + long0 = 2; + ulong0 = 2; + c_2(); + + long0 = 0xff; + ulong0 = 0xff; + c_ff(); + + long0 = 0x200; + ulong0 = 0x200; + c_200(); + + long0 = 0x20000; + ulong0 = 0x20000; + c_20000(); + + long0 = -1; + c_minus1(); + + long0=-100; + long1=-1000; + c_long2neglit(); + + done (); +} diff --git a/src/regression/compare6.c b/src/regression/compare6.c new file mode 100644 index 0000000..f189ba8 --- /dev/null +++ b/src/regression/compare6.c @@ -0,0 +1,184 @@ +#include "gpsim_assert.h" +// Compound comparisons + +unsigned char success = 0; +unsigned char failures = 0; +unsigned char dummy = 0; + +//bit bit0 = 0; +int int0 = 0; +int int1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +char char0 = 0; +char char1 = 0; +char long0 = 0; +char long1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +c_char(void) +{ + + if(char0 || char1) + failures++; + + if(char0 && char1) + failures++; + + if(char0 > char1) + failures++; + + if((char0+1) < char1) + failures++; + + if((char0+5) >= (char1+9)) + failures++; + + char0++; + + if(char0 && char1) + failures++; + + if(char0 != (char1+1) ) + failures++; + + if(!char0) + failures++; + + if(char1 || !char0) + failures++; + + if((char0 >5 ) && (char0 < 10)) + failures++; + + char0 +=5; /* char0 = 6 now */ + + + if(!((char0 >5 ) && (char0 < 10))) + failures++; + +} + + +void +c_int(void) +{ + + if(int0 || int1) + failures++; + + if(int0 && int1) + failures++; + + if(int0 > int1) + failures++; + + if((int0+1) < int1) + failures++; + + if((int0+5) >= (int1+9)) + failures++; + + int0++; + + if(int0 && int1) + failures++; + + if(int0 != (int1+1) ) + failures++; + + if(!int0) + failures++; + + if(int1 || !int0) + failures++; + + if((int0 >5 ) && (int0 < 10)) + failures++; + + int0 +=5; /* int0 = 6 now */ + + + if(!((int0 >5 ) && (int0 < 10))) + failures++; + +} + + +void +c_long(void) +{ + + if(long0 || long1) + failures++; + + if(long0 && long1) + failures++; + + if(long0 > long1) + failures++; + + if((long0+1) < long1) + failures++; + + if((long0+5) >= (long1+9)) + failures++; + + long0++; + + if(long0 && long1) + failures++; + + if(long0 != (long1+1) ) + failures++; + + if(!long0) + failures++; + + if(long1 || !long0) + failures++; + + if((long0 >5 ) && (long0 < 10)) + failures++; + + long0 +=5; /* long0 = 6 now */ + + + if(!((long0 >5 ) && (long0 < 10))) + failures++; + +} + +void +c_uminus(void) +{ + + int1 = -int0; + if(int1 < 0) + failures++; +} + +void +main (void) +{ + + c_char(); + c_int(); + c_long(); + + int0 = -1; + c_uminus(); + if(int1 != 1) + failures++; + + success = failures; + done (); +} + diff --git a/src/regression/compare7.c b/src/regression/compare7.c new file mode 100644 index 0000000..1c025c6 --- /dev/null +++ b/src/regression/compare7.c @@ -0,0 +1,311 @@ +#include "gpsim_assert.h" +#include "picregs.h" +// Signed comparisons of the form: (variableLIT) +// +// This regression test exercises all of the boundary +// conditions in literal less than comparisons. There +// are numerous opportunities to optimize these comparison +// and each one has an astonishing capability of failing +// a boundary condition. + +unsigned char failures = 0; +unsigned char result = 0; + +//bit bit0 = 0; +int int0 = 0; +int int1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +signed char char0 = 0; +signed char char1 = 0; +char long0 = 0; +char long1 = 0; + +/* *** NOTE *** This particular test takes quite a while to run + * ~ 10,000,000 instruction cycles. (2.5 seconds on a 20Mhz PIC). + * The WDT will reset the CPU if it's enabled. So disable it... +*/ + +typedef unsigned int word; + +//word at 0x2007 CONFIG = wdt_off & pwrte_on; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void c_char_gt_lit1(unsigned char expected_result) +{ + result = 0; + + if(char0 > -0x7f) + result |= 1; + + + if(char0 > -1) + result |= 2; + + if(char0 > 0) + result |= 4; + + if(char0 > 1) + result |= 8; + + if(char0 > 0x7e) + result |= 0x10; + + if(char0 > 0x7f) + result |= 0x20; + + if(result != expected_result) + failures++; +} + + +void char_compare(void) +{ + char0 = 0x7f; + c_char_gt_lit1(0x1f); + + char0 = 0x7e; + c_char_gt_lit1(0x0f); + + char0 = 0x40; + c_char_gt_lit1(0x0f); + + char0 = 0x2; + c_char_gt_lit1(0x0f); + + char0 = 0x1; + c_char_gt_lit1(0x07); + + char0 = 0; + c_char_gt_lit1(0x03); + + char0 = -1; + c_char_gt_lit1(0x01); + + char0 = -2; + c_char_gt_lit1(0x01); + + char0 = -0x40; + c_char_gt_lit1(0x01); + + char0 = -0x7e; + c_char_gt_lit1(0x01); + + char0 = -0x7f; + c_char_gt_lit1(0x00); + + char0 = 0x80; + c_char_gt_lit1(0x00); + + + /* Now test entire range */ + + for(char0=2; char0 != 0x7f; char0++) + c_char_gt_lit1(0x0f); + + + for(char0=-0x7e; char0 != -1; char0++) + c_char_gt_lit1(0x01); + + +} + + +void c_int_gt_lit1(unsigned char expected_result) +{ + result = 0; + + if(int0 > 0) + result |= 1; + + if(int0 > 1) + result |= 2; + + + if(int0 > 0xff) + result |= 4; + + if(int0 > 0x100) + result |= 8; + + if(int0 > 0x0101) + result |= 0x10; + + if(int0 > 0x01ff) + result |= 0x20; + + if(int0 > 0x0200) + result |= 0x40; + + if(int0 > 0x0201) + result |= 0x80; + + if(result != expected_result) + failures=1; + +} + + +void int_compare1(void) +{ + int0 = -1; + c_int_gt_lit1(0x00); + + int0 = 0; + c_int_gt_lit1(0x00); + + int0 = 1; + c_int_gt_lit1(0x01); + + int0 = 2; + c_int_gt_lit1(0x03); + + int0 = 0xfe; + c_int_gt_lit1(0x03); + + int0 = 0xff; + c_int_gt_lit1(0x03); + + int0 = 0x100; + c_int_gt_lit1(0x07); + + int0 = 0x101; + c_int_gt_lit1(0x0f); + + int0 = 0x102; + c_int_gt_lit1(0x1f); + + int0 = 0x1fe; + c_int_gt_lit1(0x1f); + + int0 = 0x1ff; + c_int_gt_lit1(0x1f); + + int0 = 0x200; + c_int_gt_lit1(0x3f); + + int0 = 0x201; + c_int_gt_lit1(0x7f); + + int0 = 0x7f00; + c_int_gt_lit1(0xff); + + /* now check contiguous ranges */ + + for(int0 = -0x7fff; int0 != -1; int0++) + c_int_gt_lit1(0x00); + + for(int0 = 2; int0 != 0xff; int0++) + c_int_gt_lit1(0x03); + + for(int0 = 0x202; int0 != 0x7fff; int0++) + c_int_gt_lit1(0xff); + +} + + +void c_int_gt_lit2(unsigned char expected_result) +{ + result = 0; + + if(int0 > -0x7fff) + result |= 1; + + if(int0 > -0x7f00) + result |= 2; + + if(int0 > -0x7eff) + result |= 4; + + if(int0 > -0x7e00) + result |= 8; + + if(int0 > -0x0101) + result |= 0x10; + + if(int0 > -0x0100) + result |= 0x20; + + if(int0 > -0xff) + result |= 0x40; + + if(int0 > -1) + result |= 0x80; + + if(result != expected_result) + failures=1; +} + +void int_compare2(void) +{ + int0 = -0x7fff; + c_int_gt_lit2(0x00); + + int0 = -0x7f00; + c_int_gt_lit2(0x01); + + int0 = -0x7eff; + c_int_gt_lit2(0x03); + + int0 = -0x7e00; + c_int_gt_lit2(0x07); + + int0 = -0x7dff; + c_int_gt_lit2(0x0f); + + int0 = -0x4567; + c_int_gt_lit2(0x0f); + + int0 = -0x200; + c_int_gt_lit2(0x0f); + + int0 = -0x102; + c_int_gt_lit2(0x0f); + + int0 = -0x101; + c_int_gt_lit2(0x0f); + + int0 = -0x100; + c_int_gt_lit2(0x1f); + + int0 = -0xff; + c_int_gt_lit2(0x3f); + + int0 = -0x02; + c_int_gt_lit2(0x7f); + + int0 = -0x01; + c_int_gt_lit2(0x7f); + + int0 = 0; + c_int_gt_lit2(0xff); + + int0 = 1; + c_int_gt_lit2(0xff); + + int0 = 0x7fff; + c_int_gt_lit2(0xff); + + /* now check contiguous ranges */ + + for(int0 = -0x7ffe; int0 != -0x7f01; int0++) + c_int_gt_lit2(0x01); + + for(int0 = -0x7dff; int0 != -0x101; int0++) + c_int_gt_lit2(0x0f); + + for(int0 = 0; int0 != 0x7fff; int0++) + c_int_gt_lit2(0xff); + +} + + + + +void +main (void) +{ + char_compare(); + __asm clrwdt __endasm; + int_compare1(); + __asm clrwdt __endasm; + int_compare2(); + __asm clrwdt __endasm; + + done (); +} diff --git a/src/regression/compare9.c b/src/regression/compare9.c new file mode 100644 index 0000000..d8caad0 --- /dev/null +++ b/src/regression/compare9.c @@ -0,0 +1,305 @@ +#include "gpsim_assert.h" +#include "picregs.h" +// Signed comparisons of the form: (variable<=LIT) +// +// This regression test exercises all of the boundary +// conditions in literal less than or equal comparisons. There +// are numerous opportunities to optimize these comparison +// and each one has an astonishing capability of failing +// a boundary condition. + +unsigned char failures = 0; +unsigned char result = 0; + +int int0 = 0; +int int1 = 0; +signed char char0 = 0; +signed char char1 = 0; + +/* *** NOTE *** This particular test takes quite a while to run + * ~ 10,000,000 instruction cycles. (2.5 seconds on a 20Mhz PIC). + * The WDT will reset the CPU if it's enabled. So disable it... +*/ + +typedef unsigned int word; + +//word at 0x2007 CONFIG = wdt_off & pwrte_on; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void c_char_lte_lit1(unsigned char expected_result) +{ + result = 0; + + if(char0 <= -0x7f) + result |= 1; + + if(char0 <= -1) + result |= 2; + + if(char0 <= 0) + result |= 4; + + if(char0 <= 1) + result |= 8; + + if(char0 <= 0x7f) + result |= 0x10; + + if(result != expected_result) + failures++; +} + + +void char_compare(void) +{ + char0 = 0x7f; + c_char_lte_lit1(0x10); + + char0 = 0x7e; + c_char_lte_lit1(0x10); + + char0 = 0x40; + c_char_lte_lit1(0x10); + + char0 = 0x2; + c_char_lte_lit1(0x10); + + char0 = 0x1; + c_char_lte_lit1(0x18); + + char0 = 0; + c_char_lte_lit1(0x1c); + + char0 = -1; + c_char_lte_lit1(0x1e); + + char0 = -2; + c_char_lte_lit1(0x1e); + + char0 = -0x40; + c_char_lte_lit1(0x1e); + + char0 = -0x7e; + c_char_lte_lit1(0x1e); + + char0 = -0x7f; + c_char_lte_lit1(0x1f); + + char0 = 0x80; + // c_char_lte_lit1(0x1f); + + + /* Now test entire range */ + + for(char0=2; char0 != 0x7f; char0++) + c_char_lte_lit1(0x10); + + + for(char0=-0x7e; char0 != 0; char0++) + c_char_lte_lit1(0x1e); + + +} + +void c_int_lte_lit1(unsigned char expected_result) +{ + result = 0; + + if(int0 <= 0) + result |= 1; + + if(int0 <= 1) + result |= 2; + + + if(int0 <= 0xff) + result |= 4; + + if(int0 <= 0x100) + result |= 8; + + if(int0 <= 0x0101) + result |= 0x10; + + if(int0 <= 0x01ff) + result |= 0x20; + + if(int0 <= 0x0200) + result |= 0x40; + + if(int0 <= 0x0201) + result |= 0x80; + + if(result != expected_result) + failures=1; + +} + + +void int_compare1(void) +{ + int0 = -1; + c_int_lte_lit1(0xff); + + int0 = 0; + c_int_lte_lit1(0xff); + + int0 = 1; + c_int_lte_lit1(0xfe); + + int0 = 2; + c_int_lte_lit1(0xfc); + + int0 = 0xfe; + c_int_lte_lit1(0xfc); + + int0 = 0xff; + c_int_lte_lit1(0xfc); + + int0 = 0x100; + c_int_lte_lit1(0xf8); + + int0 = 0x101; + c_int_lte_lit1(0xf0); + + int0 = 0x1fe; + c_int_lte_lit1(0xe0); + + int0 = 0x1ff; + c_int_lte_lit1(0xe0); + + int0 = 0x200; + c_int_lte_lit1(0xc0); + + int0 = 0x201; + c_int_lte_lit1(0x80); + + int0 = 0x7f00; + c_int_lte_lit1(0x0); + + /* now check contiguous ranges */ + + for(int0 = -0x7fff; int0 != 1; int0++) + c_int_lte_lit1(0xff); + + for(int0 = 2; int0 != 0xff; int0++) + c_int_lte_lit1(0xfc); + + for(int0 = 0x202; int0 != 0x7fff; int0++) + c_int_lte_lit1(0); + +} + + + +void c_int_lte_lit2(unsigned char expected_result) +{ + result = 0; + + if(int0 <= -0x7fff) + result |= 1; + + if(int0 <= -0x7f00) + result |= 2; + + if(int0 <= -0x7eff) + result |= 4; + + if(int0 <= -0x7e00) + result |= 8; + + if(int0 <= -0x0101) + result |= 0x10; + + if(int0 <= -0x0100) + result |= 0x20; + + if(int0 <= -0xff) + result |= 0x40; + + if(int0 <= -1) + result |= 0x80; + + if(result != expected_result) + failures=1; +} + +void int_compare2(void) +{ + int0 = -0x7fff; + c_int_lte_lit2(0xff); + + int0 = -0x7f00; + c_int_lte_lit2(0xfe); + + int0 = -0x7eff; + c_int_lte_lit2(0xfc); + + int0 = -0x7e00; + c_int_lte_lit2(0xf8); + + int0 = -0x4567; + c_int_lte_lit2(0xf0); + + int0 = -0x200; + c_int_lte_lit2(0xf0); + + int0 = -0x102; + c_int_lte_lit2(0xf0); + + int0 = -0x101; + c_int_lte_lit2(0xf0); + + int0 = -0x100; + c_int_lte_lit2(0xe0); + + int0 = -0xff; + c_int_lte_lit2(0xc0); + + int0 = -0x02; + c_int_lte_lit2(0x80); + + int0 = -0x01; + c_int_lte_lit2(0x80); + + int0 = 0; + c_int_lte_lit2(0x00); + + int0 = 1; + c_int_lte_lit2(0x00); + + int0 = 0x7fff; + c_int_lte_lit2(0x00); + + /* now check contiguous ranges */ + + for(int0 = -0x7ffe; int0 != -0x7f00; int0++) + c_int_lte_lit2(0xfe); + + for(int0 = -0x7dff; int0 != -0x101; int0++) + c_int_lte_lit2(0xf0); + + for(int0 = 0; int0 != 0x7fff; int0++) + c_int_lte_lit2(0); + +} + + +void +main (void) +{ + char_compare(); + __asm clrwdt __endasm; + int_compare1(); + __asm clrwdt __endasm; + int_compare2(); + __asm clrwdt __endasm; + + done (); +} diff --git a/src/regression/configword.c b/src/regression/configword.c new file mode 100644 index 0000000..fb4728a --- /dev/null +++ b/src/regression/configword.c @@ -0,0 +1,33 @@ +#include "gpsim_assert.h" +#include "picregs.h" + +/* configword.c - illustrates how the configuration word can + * be assigned */ + +unsigned char failures=0; +unsigned char dummy; + +#ifdef __pic14 +typedef unsigned int word; +static word __at(0x2007) _config = _WDT_OFF & _PWRTE_ON; +#else /* !__pic14 */ +static __code char __at(__CONFIG2L) _conf2l = _PUT_ON_2L; +static __code char __at(__CONFIG2H) _conf2h = _WDT_OFF_2H; +#endif /* !__pic14 */ + +/* TODO -- write a test that puts the PIC to sleep, + * and verify that the WDT wakes it up */ + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void main(void) +{ + dummy = 0; + + done(); +} diff --git a/src/regression/create_stc b/src/regression/create_stc new file mode 100755 index 0000000..4e59f4a --- /dev/null +++ b/src/regression/create_stc @@ -0,0 +1,15 @@ +# Create gpsim script file +# + +USAGE="Usage: `basename $0` COD_FILE GPSIM_SCRIPT" + +if [ $# -lt 2 ] ; then + echo "$USAGE" + exit 1 +fi + + +echo "load $1" > $2 +echo "run" >> $2 +echo "symbol _failures" >> $2 +echo "quit" >> $2 diff --git a/src/regression/empty.c b/src/regression/empty.c new file mode 100644 index 0000000..fb4fd26 --- /dev/null +++ b/src/regression/empty.c @@ -0,0 +1,22 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +void foo(void) { +} + +void bar(int arg0) { + (void)arg0; +} + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void main(void) { + done(); +} + diff --git a/src/regression/for.c b/src/regression/for.c new file mode 100644 index 0000000..9c9314e --- /dev/null +++ b/src/regression/for.c @@ -0,0 +1,108 @@ +#include "gpsim_assert.h" + +unsigned char failures=0; + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void for1(void) +{ + unsigned char i=0; + + for(i=0; i<10; i++) + uchar0++; + + if(uchar0 != 10) + failures++; + +} + +void for2(void) +{ + unsigned char i=0; + unsigned char j; + + for(i=0; i<10; i++) + uchar0++; + + j = (volatile)i; + + if(j != 10) + failures++; + +} + +void for3(void) +{ + unsigned int i=0; + volatile unsigned int j; + + for(i=0; i<10; i++) + uint0++; + + j = i; + if(j != 10) + failures++; + +} + +void for4(void) +{ + + for(uint0=1; uint0<10; uint0++) + uchar0++; + + if(uchar0 != 9) + failures++; + +} + +void for5(void) +{ + + for(uint0=1; uint0<=10; uint0++) + uchar0++; + + if(uchar0 != 10) + failures++; + +} + +void inc_uchar0(void) +{ + uchar0++; +} + +void for6(void) +{ + uchar0 = 0; + for(uint0=1; uint0<=10; uint0++) + inc_uchar0(); + +} + +void main(void) +{ + for1(); + for2(); + for3(); + uchar0 = 0; + for4(); + uchar0 = 0; + for5(); + + for6(); + if(uchar0 != 10) + failures++; + + done(); +} diff --git a/src/regression/gpsim_assert.h b/src/regression/gpsim_assert.h new file mode 100644 index 0000000..8e39858 --- /dev/null +++ b/src/regression/gpsim_assert.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------------ + + gpsim_assert.h - assertion macros generating gpsim breakpoints + + Copyright (c) 2006 Borut Razem + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. + +------------------------------------------------------------------------------*/ + + +#ifndef __PIC_ASSERT_H +#define __PIC_ASSERT_H + +#define MANGLE(e) _##e +#define STRINGIFY(e) #e + +#define ASSERT(e) \ + __asm \ + nop \ + __endasm; \ + __asm \ + .direct "a", STRINGIFY(e) \ + __endasm; \ + __asm \ + nop \ + __endasm; + +#define PASSED() \ + __asm \ + nop \ + __endasm; \ + __asm \ + .direct "a", "\"PASSED\"" \ + __endasm; \ + __asm \ + nop \ + __endasm; + +#define FAILED() \ + __asm \ + nop \ + __endasm; \ + __asm \ + .direct "a", "\"===> FAILED\"" \ + __endasm; \ + __asm \ + nop \ + __endasm; + +#endif diff --git a/src/regression/init0.c b/src/regression/init0.c new file mode 100644 index 0000000..fc711e4 --- /dev/null +++ b/src/regression/init0.c @@ -0,0 +1,45 @@ +#include "gpsim_assert.h" + +unsigned failures = 0; + +void +done(void) +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +typedef void (void_void_f)(void); + +void +foo(void) { + failures--; +} + +void +bar(void) { + failures -= 2; +} + +static void_void_f * +funcs[] = { + &foo, + &bar, + (void *)0 +}; + +void +main(void) +{ + void_void_f **ptr; + failures = 3; + + ptr = &funcs[0]; + while (*ptr) { + (**ptr)(); + ptr++; + } // while + + done(); +} + diff --git a/src/regression/inline.c b/src/regression/inline.c new file mode 100644 index 0000000..bb2cb3b --- /dev/null +++ b/src/regression/inline.c @@ -0,0 +1,58 @@ +#include "gpsim_assert.h" +#include "picregs.h" + +#pragma preproc_asm - + +unsigned char failures=0; + +unsigned char test_tris=0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +delay_1ms(void) +{ + unsigned char cnt1m = 2; + unsigned char cnt500u = 249; + + do { + do { + __asm + nop + nop + __endasm; + } while (--cnt500u > 0); + } while (--cnt1m > 0); +} + + +void main(void) +{ + TRISA = 0x0f; + +#if defined(__pic14) + __asm + BSF STATUS,RP0 + MOVF TRISA,W + BCF STATUS,RP0 + MOVWF _test_tris + __endasm; +#else // !defined(__pic14) + __asm + BANKSEL _TRISA + MOVF _TRISA,W + BANKSEL _test_tris + MOVWF _test_tris + __endasm; +#endif // !defined(__pic14) + + if(test_tris != 0x0f) + failures++; + + done(); +} diff --git a/src/regression/mult1.c b/src/regression/mult1.c new file mode 100644 index 0000000..bcf9c71 --- /dev/null +++ b/src/regression/mult1.c @@ -0,0 +1,108 @@ +#include "gpsim_assert.h" + +#define TESTLIT 0x05 + +unsigned char failures=0; + + +signed char c1,c2,c3; +unsigned char uc1,uc2,uc3; + +unsigned int ui1,ui2,ui3; +signed int i1,i2; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void m1(void) +{ + + c1 = c1*5; // char = char * lit + + c2 = c1 *c3; // char = char * char + + uc1= uc1*5; // uchar = uchar * lit + uc2=uc1*uc3; // uchar = uchar * uchar + + if(c2 != 25) + failures++; +} + +void m2(unsigned char uc) +{ + + uc2 = uc1 * uc; + + if(uc2 != 0x20) + failures++; +} + +void m3(unsigned char uc) +{ + volatile unsigned char vuc; + + // uchar = uchar * lit + // testing literal multiply with same source and destination + vuc = uc; + uc2 = 0; + uc1 = vuc; uc1 = uc1*1; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*2; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*3; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*4; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*5; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*6; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*7; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*8; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*9; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*10; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*11; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*12; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*13; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*14; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*15; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*16; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*17; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*18; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*19; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*20; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*21; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*22; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*23; if( uc1 != (uc2+=TESTLIT) ) failures++; + uc1 = vuc; uc1 = uc1*24; if( uc1 != (uc2+=TESTLIT) ) failures++; + + uc1 = vuc; uc1 = uc1*31; if( uc1 != ((31*TESTLIT) & 0xff) ) failures++; + uc1 = vuc; uc1 = uc1*32; if( uc1 != ((32*TESTLIT) & 0xff) ) failures++; + uc1 = vuc; uc1 = uc1*64; if( uc1 != ((64*TESTLIT) & 0xff) ) failures++; + uc1 = vuc; uc1 = uc1*128;if( uc1 != ((128*TESTLIT)& 0xff) ) failures++; + + // testing literal multiply with different source and destination + uc1 = vuc*1; if( uc1 != ((1*TESTLIT) & 0xff) ) failures++; + uc1 = vuc*2; if( uc1 != ((2*TESTLIT) & 0xff) ) failures++; + uc1 = vuc*4; if( uc1 != ((4*TESTLIT) & 0xff) ) failures++; +} + + +void main(void) +{ + c1 = 1; + c3 = 5; + + m1(); + + uc1 = 0x10; + m2(2); + + ui1 = uc1*uc2; // uint = uchar * uchar + + i1 = c1*c2; // int = char * char + + ui3 = ui1*ui2; // uint = uint * unit + + //m3(TESTLIT); + + done(); +} diff --git a/src/regression/nestfor.c b/src/regression/nestfor.c new file mode 100644 index 0000000..654efe8 --- /dev/null +++ b/src/regression/nestfor.c @@ -0,0 +1,139 @@ +#include "gpsim_assert.h" +#include "picregs.h" + +unsigned char failures=0; + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; + +void dput(unsigned char val) +{ + PORTB = val; + PORTA = 0x01; + PORTA = 0x00; +} + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +/* both loops use the loop variable inside the inner loop */ +void for1(void) +{ + unsigned char i, j; + + uchar0 = 0; + uchar1 = 0; + for(i = 0; i < 3; i++) { + uchar0++; + for(j = 0; j < 4; j++) { + uchar1++; + dput(i); + dput(j); + } + } + if(uchar0 != 3) + failures++; + if(uchar1 != 12) + failures++; +} + +/* only the outer loop's variable is used inside, inner can be optimized into a repeat-loop */ +void for2(void) +{ + unsigned char i, j; + + uchar0 = 0; + uchar1 = 0; + for(i = 0; i < 3; i++) { + uchar0++; + for(j = 0; j < 4; j++) { + uchar1++; + dput(i); + } + } + if(uchar0 != 3) + failures++; + if(uchar1 != 12) + failures++; +} + +/* only the inner loop's variable is used inside */ +void for3(void) +{ + unsigned char i, j; + + uchar0 = 0; + uchar1 = 0; + for(i = 0; i < 3; i++) { + uchar0++; + for(j = 0; j < 4; j++) { + uchar1++; + dput(j); + } + } + if(uchar0 != 3) + failures++; + if(uchar1 != 12) + failures++; + +} + +/* neither loop variable used inside the loops */ +void for4(void) +{ + unsigned char i, j; + + uchar0 = 0; + uchar1 = 0; + for(i = 0; i < 3; i++) { + uchar0++; + for(j = 0; j < 4; j++) { + uchar1++; + dput(uchar0); + dput(uchar1); + } + } + if(uchar0 != 3) + failures++; + if(uchar1 != 12) + failures++; + +} + +/* like for1 but different condition in inner loop */ +void for5(void) +{ + unsigned char i, j; + + uchar0 = 0; + uchar1 = 0; + for(i = 0; i < 3; i++) { + uchar0++; + for(j = 10; j >= 5; j--) { + uchar1++; + dput(i); + dput(j); + } + } + if(uchar0 != 3) + failures++; + if(uchar1 != 18) + failures++; +} + +void main(void) +{ + for1(); + for2(); + for3(); + for4(); + for5(); + + done(); +} diff --git a/src/regression/or1.c b/src/regression/or1.c new file mode 100644 index 0000000..b828eb0 --- /dev/null +++ b/src/regression/or1.c @@ -0,0 +1,198 @@ +#include "gpsim_assert.h" + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + +unsigned char failures=0; + +#if SUPPORT_BIT_TYPES +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +#endif + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +unsigned long ulong0 = 0; +unsigned long ulong1 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +// uchar0 = 0; +void or_lit2uchar(void) +{ + + if(uchar0) + failures++; + + uchar0 |= 1; + + if(uchar0 != 1) + failures++; + + uchar0 |= 2; + + if(uchar0 != 3) + failures++; + + uchar0 |= 0x0e; + + if(uchar0 != 0x0f) + failures++; + +} + + +void or_lit2uint(void) +{ + + if(uint0) + failures++; + + uint0 |= 1; + if(uint0 != 1) + failures++; + + uint0 |= 2; + if(uint0 != 3) + failures++; + + uint0 |= 0x100; + if(uint0 != 0x103) + failures++; + + uint0 |= 0x102; + if(uint0 != 0x103) + failures++; + + uint0 |= 0x303; + if(uint0 != 0x303) + failures++; + +} + +void or_lit2ulong(void) +{ + + if(ulong0) + failures++; + + ulong0 |= 1; + if(ulong0 != 1) + failures++; + + ulong0 |= 2; + if(ulong0 != 3) + failures++; + + ulong0 |= 0x100; + if(ulong0 != 0x103) + failures++; + + ulong0 |= 0x102; + if(ulong0 != 0x103) + failures++; + + ulong0 |= 0x303; + if(ulong0 != 0x303) + failures++; + + ulong0 |= 0x80000000; + if(ulong0 != 0x80000303) + failures++; + +} + +/*-----------*/ +void or_uchar2uchar(void) +{ + + uchar0 |= uchar1; + + if(uchar0 != 1) + failures++; + + uchar1 |= 0x0f; + + uchar0 = uchar1 | 0x10; + + if(uchar0 != 0x1f) + failures++; +} + +void or_uint2uint(void) +{ + uint0 |= uint1; + + if(uint0 != 1) + failures++; + + uint1 |= 0x0f; + + uint0 = uint1 | 0x10; + + if(uint0 != 0x1f) + failures++; + +} + +#if SUPPORT_BIT_TYPES +void or_bits1(void) +{ + + bit0 = bit0 | bit1 | bit2; + +} + +void or_bits2(void) +{ + + bit0 = bit1 | bit2; + +} +#endif + +void main(void) +{ + + or_lit2uchar(); + or_lit2uint(); + or_lit2ulong(); + + uchar0=0; + uchar1=1; + or_uchar2uchar(); + + uint0=0; + uint1=1; + or_uint2uint(); + +#if SUPPORT_BIT_TYPES + or_bits1(); + if(bit0) + failures++; + + or_bits2(); + if(bit0) + failures++; + + bit1=1; + or_bits1(); + if(!bit0) + failures++; + + or_bits2(); + if(!bit0) + failures++; +#endif + + done(); +} diff --git a/src/regression/pcodeopt.c b/src/regression/pcodeopt.c new file mode 100644 index 0000000..8be5a2a --- /dev/null +++ b/src/regression/pcodeopt.c @@ -0,0 +1,26 @@ +#include "gpsim_assert.h" + +/* + * Test for buggy pCode optimization on + * CLRF reg ; pc1 + * ... + * MOVF reg,W ; pc2 + * + * Originally, both instructions were removed and pc2 replaced with + * CLRF reg iff reg was used afterwards, but Z and W were not, or + * MOVLW 0 iff reg and Z were not used afterwards, but W was. + * Detection of W being used used to be buggy, though... + */ +signed int x=0; +unsigned char y=1; + +void main() { + x += y; + x += y; + if (x != 2) { FAILED(); } + if (y != 1) { FAILED(); } + //ASSERT(MANGLE(x) == 2); + //ASSERT(MANGLE(y) == 1); + PASSED(); +} + diff --git a/src/regression/picregs.h b/src/regression/picregs.h new file mode 100644 index 0000000..92c20a9 --- /dev/null +++ b/src/regression/picregs.h @@ -0,0 +1,10 @@ +#ifndef PICREGS_H + +#if defined(__pic16) + #include +#else + #include +#endif + +#endif + diff --git a/src/regression/pointer1.c b/src/regression/pointer1.c new file mode 100644 index 0000000..23075b4 --- /dev/null +++ b/src/regression/pointer1.c @@ -0,0 +1,134 @@ +#include "gpsim_assert.h" + +// Pointer tests + +unsigned char failures = 0; +unsigned char dummy; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char *acharP = 0; + +unsigned char buff[10]; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +f1 (unsigned char *ucP) +{ + + if (ucP == 0) + { + failures++; + return; + } + + if (*ucP) + failures++; +} + +void +f2 (unsigned int *uiP) +{ + + if (uiP == 0) + { + failures++; + return; + } + + if (*uiP) + failures++; + +} + + +unsigned char * +f3 (void) +{ + + return &achar0; +} + + +void f4(unsigned char *ucP, unsigned char uc) +{ + + if(!ucP) { + failures++; + return; + } + + if(*ucP != uc) + failures++; + +} + +void init_array(char start_value) +{ + unsigned char c; + + for(c=0; c>= 1; + + check(); +} + +void shift_right_2(void) +{ + + achar0 >>= 2; + + check(); +} + +void shift_right_3(void) +{ + + achar0 >>= 3; + + check(); +} + +void shift_right_4(void) +{ + + achar0 >>= 4; + + check(); +} + +void shift_right_5(void) +{ + + achar0 >>= 5; + + check(); +} + +void shift_right_6(void) +{ + + achar0 >>= 6; + + check(); +} + +void shift_right_7(void) +{ + + achar0 >>= 7; + + check(); +} + + +void main(void) +{ + + // call with both values zero + shift_left_1(); + + achar0 = 1; + achar1 = 2; + for(achar2=0; achar2<6; achar2++) { + shift_left_1(); + achar1 <<=1; + } + + achar0 = 1; + achar1 = 4; + shift_left_2(); + + achar0 = 1; + achar1 = 8; + shift_left_3(); + + achar0 = 1; + achar1 = 0x10; + shift_left_4(); + + achar0 = 1; + achar1 = 0x20; + shift_left_5(); + + achar0 = 1; + achar1 = 0x40; + shift_left_6(); + + achar0 = 1; + achar1 = 0x80; + shift_left_7(); + + + + + achar0 = 2; + achar1 = 1; + shift_right_1(); + + achar0 = 4; + shift_right_2(); + + achar0 = 8; + shift_right_3(); + + achar0 = 0x10; + shift_right_4(); + + achar0 = 0x20; + shift_right_5(); + + achar0 = 0x40; + shift_right_6(); + + achar0 = 0x80; + shift_right_7(); + + done(); +} diff --git a/src/regression/rotate2.c b/src/regression/rotate2.c new file mode 100644 index 0000000..1d25110 --- /dev/null +++ b/src/regression/rotate2.c @@ -0,0 +1,62 @@ +#include "gpsim_assert.h" +// Shift bytes left and right by a variable. + +unsigned char failures=0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; +unsigned char achar3 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void shift_right_var(void) +{ + + achar0 >>= achar1; +} + +void shift_left_var(void) +{ + + achar0 <<= achar1; +} + +void shift_int_left_1(void) +{ + + aint0 <<= 1; + +} + +void main(void) +{ + char i; + + achar0 = 1; + achar1 = 1; + shift_left_var(); + + if(achar0 !=2) + failures++; + + achar0 = 1; + achar1 = 1; + achar2 = 1; + for(i=0; i<7; i++) { + shift_left_var(); + achar2 <<= 1; + + if(achar2 != achar0) + failures++; + } + + done(); +} diff --git a/src/regression/rotate3.c b/src/regression/rotate3.c new file mode 100644 index 0000000..70aab09 --- /dev/null +++ b/src/regression/rotate3.c @@ -0,0 +1,388 @@ +#include "gpsim_assert.h" +// Shift ints left and right + +unsigned char failures=0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; +unsigned char achar3 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void shift_int_left_1(void) +{ + + aint0 <<= 1; + +} + +void shift_int_left_2(void) +{ + + aint0 <<= 2; + +} + +void shift_int_left_3(void) +{ + + aint0 <<= 3; + +} + +void shift_int_left_4(void) +{ + + aint0 <<= 4; + +} + +void shift_int_left_5(void) +{ + + aint0 <<= 5; + +} + +void shift_int_left_6(void) +{ + + aint0 <<= 6; + +} + +void shift_int_left_7(void) +{ + + aint0 <<= 7; + +} + +void shift_int_left_8(void) +{ + + aint0 <<= 8; + +} + +void shift_int_left_9(void) +{ + + aint0 <<= 9; + +} + +void shift_int_left_10(void) +{ + + aint0 <<= 10; + +} + +void shift_int_left_11(void) +{ + + aint0 <<= 11; + +} + +void shift_int_left_12(void) +{ + + aint0 <<= 12; + +} + +void shift_int_left_13(void) +{ + + aint0 <<= 13; + +} + +void shift_int_left_14(void) +{ + + aint0 <<= 14; + +} + +void shift_int_left_15(void) +{ + + aint0 <<= 15; + +} + +/*****************************************************/ +void shift_int_right_1(void) +{ + aint0 >>= 1; +} + +void shift_int_right_2(void) +{ + aint0 >>= 2; +} + +void shift_int_right_3(void) +{ + aint0 >>= 3; +} + +void shift_int_right_4(void) +{ + aint0 >>= 4; +} + +void shift_int_right_5(void) +{ + aint0 >>= 5; +} + +void shift_int_right_6(void) +{ + aint0 >>= 6; +} + +void shift_int_right_7(void) +{ + aint0 >>= 7; +} + +void shift_int_right_8(void) +{ + aint0 >>= 8; +} + +void shift_int_right_9(void) +{ + aint0 >>= 9; +} + +void shift_int_right_10(void) +{ + aint0 >>= 10; +} + +void shift_int_right_11(void) +{ + aint0 >>= 11; +} + +void shift_int_right_12(void) +{ + aint0 >>= 12; +} + +void shift_int_right_13(void) +{ + aint0 >>= 13; +} + +void shift_int_right_14(void) +{ + aint0 >>= 14; +} + +void shift_int_right_15(void) +{ + aint0 >>= 15; +} + +/*****************************************************/ +void main(void) +{ + //char i; + + aint0 = 0xabcd; + + shift_int_left_1(); + if(aint0 != 0x579a) + failures++; + + aint0 = 0xabcd; + + shift_int_left_2(); + if(aint0 != 0xaf34) + failures++; + + aint0 = 0xabcd; + + shift_int_left_3(); + if(aint0 != 0x5e68) + failures++; + + aint0 = 0xabcd; + + shift_int_left_4(); + if(aint0 != 0xbcd0) + failures++; + + aint0 = 0xabcd; + + shift_int_left_5(); + if(aint0 != 0x79a0) + failures++; + + aint0 = 0xabcd; + + shift_int_left_6(); + if(aint0 != 0xf340) + failures++; + + aint0 = 0xabcd; + + shift_int_left_7(); + if(aint0 != 0xe680) + failures++; + + aint0 = 0xabcd; + + shift_int_left_8(); + if(aint0 != 0xcd00) + failures++; + + aint0 = 0xabcd; + + shift_int_left_9(); + if(aint0 != 0x9a00) + failures++; + + aint0 = 0xabcd; + + shift_int_left_10(); + if(aint0 != 0x3400) + failures++; + + aint0 = 0xabcd; + + shift_int_left_11(); + if(aint0 != 0x6800) + failures++; + + aint0 = 0xabcd; + + shift_int_left_12(); + if(aint0 != 0xd000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_13(); + if(aint0 != 0xa000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_14(); + if(aint0 != 0x4000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_15(); + if(aint0 != 0x8000) + failures++; + + /***********************/ + aint0 = 0xabcd; + + shift_int_right_1(); + if(aint0 != 0x55e6) + failures++; + + aint0 = 0xabcd; + + shift_int_right_2(); + if(aint0 != 0x2af3) + failures++; + + aint0 = 0xabcd; + + shift_int_right_3(); + if(aint0 != 0x1579) + failures++; + + aint0 = 0xabcd; + + shift_int_right_4(); + if(aint0 != 0x0abc) + failures++; + + aint0 = 0xabcd; + + shift_int_right_5(); + if(aint0 != 0x055e) + failures++; + + aint0 = 0xabcd; + + shift_int_right_6(); + if(aint0 != 0x02af) + failures++; + + aint0 = 0xabcd; + + shift_int_right_7(); + if(aint0 != 0x0157) + failures++; + + aint0 = 0xabcd; + + shift_int_right_8(); + if(aint0 != 0x00ab) + failures++; + + aint0 = 0xabcd; + + shift_int_right_9(); + if(aint0 != 0x0055) + failures++; + + aint0 = 0xabcd; + + shift_int_right_10(); + if(aint0 != 0x002a) + failures++; + + aint0 = 0xabcd; + + shift_int_right_11(); + if(aint0 != 0x0015) + failures++; + + aint0 = 0xabcd; + + shift_int_right_12(); + if(aint0 != 0x000a) + failures++; + + aint0 = 0xabcd; + + shift_int_right_13(); + if(aint0 != 0x0005) + failures++; + + aint0 = 0xabcd; + + shift_int_right_14(); + if(aint0 != 0x0002) + failures++; + + aint0 = 0xabcd; + + shift_int_right_15(); + if(aint0 != 0x0001) + failures++; + + done(); +} diff --git a/src/regression/rotate4.c b/src/regression/rotate4.c new file mode 100644 index 0000000..8d8f6f5 --- /dev/null +++ b/src/regression/rotate4.c @@ -0,0 +1,223 @@ +#include "gpsim_assert.h" +// rotate bytes left and right by a constant. + +unsigned char failures=0; + +unsigned int uint0 = 0; +unsigned int uint1 = 0; +unsigned char uchar0 = 0; +unsigned char uchar1 = 0; +unsigned char uchar2 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void check(void) +{ + if(uchar0 != uchar1) + failures++; +} + +void rotate_left_1(void) +{ + + uchar0 = (uchar0<<1) | (uchar0>>7); + + check(); +} + +void rotate_left_2(void) +{ + + uchar0 = (uchar0<<2) | (uchar0>>6); + + check(); +} + + +void rotate_left_3(void) +{ + + uchar0 <<= 3; + + if(uchar0 != uchar1) + failures++; +} + +void rotate_left_4(void) +{ + + uchar0 <<= 4; + + if(uchar0 != uchar1) + failures++; +} + +void rotate_left_5(void) +{ + + uchar0 <<= 5; + + if(uchar0 != uchar1) + failures++; +} + +void rotate_left_6(void) +{ + + uchar0 <<= 6; + + if(uchar0 != uchar1) + failures++; +} + +void rotate_left_7(void) +{ + + uchar0 <<= 7; + + if(uchar0 != uchar1) + failures++; +} + +void rotate_right_1(void) +{ + + uchar0 = (uchar0>>1) | (uchar0<<7); + + check(); + +} + +void rotate_right_2(void) +{ + + uchar0 = (uchar0>>2) | (uchar0<<6); + + check(); +} + +void rotate_right_3(void) +{ + + uchar0 >>= 3; + + check(); +} + +void rotate_right_4(void) +{ + + uchar0 >>= 4; + + check(); +} + +void rotate_right_5(void) +{ + + uchar0 >>= 5; + + check(); +} + +void rotate_right_6(void) +{ + + uchar0 >>= 6; + + check(); +} + +void rotate_right_7(void) +{ + + uchar0 >>= 7; + + check(); +} + + +void main(void) +{ + + // call with both values zero + rotate_left_1(); + + uchar0 = 1; + uchar1 = 2; + + rotate_left_1(); + + uchar0 = 0x80; + uchar1 = 1; + + rotate_left_1(); + + uchar1 = 2; + for(uchar2=0; uchar2<6; uchar2++) { + rotate_left_1(); + uchar1 <<=1; + } + + + uchar0 = 1; + uchar1 = 4; + rotate_left_2(); + + uchar0 = 1; + uchar1 = 8; + rotate_left_3(); + + uchar0 = 1; + uchar1 = 0x10; + rotate_left_4(); + + uchar0 = 1; + uchar1 = 0x20; + rotate_left_5(); + + uchar0 = 1; + uchar1 = 0x40; + rotate_left_6(); + + uchar0 = 1; + uchar1 = 0x80; + rotate_left_7(); + + + + + uchar0 = 2; + uchar1 = 1; + rotate_right_1(); + + uchar0 = 1; + uchar1 = 0x80; + rotate_right_1(); + + uchar0 = 4; + uchar1 = 1; + rotate_right_2(); + + uchar0 = 8; + rotate_right_3(); + + uchar0 = 0x10; + rotate_right_4(); + + uchar0 = 0x20; + rotate_right_5(); + + uchar0 = 0x40; + rotate_right_6(); + + uchar0 = 0x80; + rotate_right_7(); + + done(); +} diff --git a/src/regression/rotate5.c b/src/regression/rotate5.c new file mode 100644 index 0000000..dc5d0b2 --- /dev/null +++ b/src/regression/rotate5.c @@ -0,0 +1,234 @@ +#include "gpsim_assert.h" +// Shift bytes left and right by a constant. + +unsigned char failures=0; + +signed int aint0 = 0; +signed int aint1 = 0; +signed char achar0 = 0; +signed char achar1 = 0; +signed char achar2 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void check(void) +{ + if(achar0 != achar1) + failures++; +} + +void shift_left_1(void) +{ + + achar0 <<= 1; + + check(); +} + +void shift_left_2(void) +{ + + achar0 <<= 2; + + if(achar0 != achar1) + failures++; +} + + +void shift_left_3(void) +{ + + achar0 <<= 3; + + if(achar0 != achar1) + failures++; +} + +void shift_left_4(void) +{ + + achar0 <<= 4; + + if(achar0 != achar1) + failures++; +} + +void shift_left_5(void) +{ + + achar0 <<= 5; + + if(achar0 != achar1) + failures++; +} + +void shift_left_6(void) +{ + + achar0 <<= 6; + + if(achar0 != achar1) + failures++; +} + +void shift_left_7(void) +{ + + achar0 <<= 7; + + if(achar0 != achar1) + failures++; +} + +void shift_right_1(void) +{ + + achar0 >>= 1; + + check(); +} + +void shift_right_2(void) +{ + + achar0 >>= 2; + + check(); +} + +void shift_right_3(void) +{ + + achar0 >>= 3; + + check(); +} + +void shift_right_4(void) +{ + + achar0 >>= 4; + + check(); +} + +void shift_right_5(void) +{ + + achar0 >>= 5; + + check(); +} + +void shift_right_6(void) +{ + + achar0 >>= 6; + + check(); +} + +void shift_right_7(void) +{ + + achar0 >>= 7; + + check(); +} + + +void main(void) +{ + + // call with both values zero + shift_left_1(); + + achar0 = 1; + achar1 = 2; + for(achar2=0; achar2<6; achar2++) { + shift_left_1(); + achar1 <<=1; + } + + achar0 = 1; + achar1 = 4; + shift_left_2(); + + achar0 = 1; + achar1 = 8; + shift_left_3(); + + achar0 = 1; + achar1 = 0x10; + shift_left_4(); + + achar0 = 1; + achar1 = 0x20; + shift_left_5(); + + achar0 = 1; + achar1 = 0x40; + shift_left_6(); + + achar0 = 1; + achar1 = 0x80; + shift_left_7(); + + + + + achar0 = 2; + achar1 = 1; + shift_right_1(); + + achar0 = 4; + shift_right_2(); + + achar0 = 8; + shift_right_3(); + + achar0 = 0x10; + shift_right_4(); + + achar0 = 0x20; + shift_right_5(); + + achar0 = 0x40; + shift_right_6(); + + + achar0 = 0xff; + achar1 = 0xff; + shift_right_1(); + + achar0 = 0xfe; + achar1 = 0xff; + shift_right_1(); + + achar0 = 0xfc; + shift_right_2(); + + achar0 = 0xf8; + shift_right_3(); + + achar0 = 0xf0; + shift_right_4(); + + achar0 = 0xe0; + shift_right_5(); + + achar0 = 0xc0; + shift_right_6(); + + achar0 = 0x80; + achar1 = 0xff; + shift_right_7(); + + + done(); +} diff --git a/src/regression/rotate6.c b/src/regression/rotate6.c new file mode 100644 index 0000000..0589ae5 --- /dev/null +++ b/src/regression/rotate6.c @@ -0,0 +1,135 @@ +#include "gpsim_assert.h" +// Shift bytes left and right by a variable. + +unsigned char failures=0; + +signed int aint0 = 0; +signed int aint1 = 0; +signed char achar0 = 0; +signed char achar1 = 0; +signed char achar2 = 0; +signed char achar3 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void shift_right_var(void) +{ + + achar0 >>= achar1; + +} + +void shift_left_var(void) +{ + + achar0 <<= achar1; +} + +void shift_int_left_var(void) +{ + + aint0 <<= achar1; + +} + +void shift_int_right_var(void) +{ + + aint0 >>= achar1; + +} + +void shift_int_right_var2(void) +{ + + aint0 = aint1 >> achar1; + +} + +void shift_int_left_var2(void) +{ + + aint0 = aint1 << achar1; + +} + +void main(void) +{ + char i; + + achar0 = 1; + achar1 = 1; + shift_left_var(); + + if(achar0 !=2) + failures++; + + achar0 = 1; + achar1 = 1; + achar2 = 1; + for(i=0; i<7; i++) { + shift_left_var(); + achar2 <<= 1; + + if(achar2 != achar0) + failures++; + } + + achar1 = 4; + achar0 = 0xf0; + shift_right_var(); + if(achar0 != (char)0xff) + failures++; + + aint0 = 1; + aint1 = 1; + achar1 = 1; + + for(i=0; i<15; i++) { + shift_int_left_var(); + aint1 <<= 1; + if(aint0 != aint1) + failures++; + } + + aint0 = 0x4000; + aint1 = 0x4000; + + for(i=0; i<15; i++) { + shift_int_right_var(); + aint1 >>= 1; + if(aint0 != aint1) + failures++; + } + + + aint0 = -0x4000; + aint1 = -0x4000; + + for(i=0; i<15; i++) { + shift_int_right_var(); + aint1 >>= 1; + if(aint0 != aint1) + failures++; + } + + aint1 = 0xf000; + achar1 = 10; + shift_int_right_var2(); + + if(aint0 != 0xfffc) + failures++; + + aint1 = aint0; + shift_int_left_var2(); + + if(aint0 != 0xf000) + failures++; + + done(); +} diff --git a/src/regression/rotate7.c b/src/regression/rotate7.c new file mode 100644 index 0000000..1546055 --- /dev/null +++ b/src/regression/rotate7.c @@ -0,0 +1,388 @@ +#include "gpsim_assert.h" +// Shift ints left and right + +unsigned char failures=0; + +signed int aint0 = 0; +signed int aint1 = 0; +signed char achar0 = 0; +signed char achar1 = 0; +signed char achar2 = 0; +signed char achar3 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void shift_int_left_1(void) +{ + + aint0 <<= 1; + +} + +void shift_int_left_2(void) +{ + + aint0 <<= 2; + +} + +void shift_int_left_3(void) +{ + + aint0 <<= 3; + +} + +void shift_int_left_4(void) +{ + + aint0 <<= 4; + +} + +void shift_int_left_5(void) +{ + + aint0 <<= 5; + +} + +void shift_int_left_6(void) +{ + + aint0 <<= 6; + +} + +void shift_int_left_7(void) +{ + + aint0 <<= 7; + +} + +void shift_int_left_8(void) +{ + + aint0 <<= 8; + +} + +void shift_int_left_9(void) +{ + + aint0 <<= 9; + +} + +void shift_int_left_10(void) +{ + + aint0 <<= 10; + +} + +void shift_int_left_11(void) +{ + + aint0 <<= 11; + +} + +void shift_int_left_12(void) +{ + + aint0 <<= 12; + +} + +void shift_int_left_13(void) +{ + + aint0 <<= 13; + +} + +void shift_int_left_14(void) +{ + + aint0 <<= 14; + +} + +void shift_int_left_15(void) +{ + + aint0 <<= 15; + +} + +/*****************************************************/ +void shift_int_right_1(void) +{ + aint0 >>= 1; +} + +void shift_int_right_2(void) +{ + aint0 >>= 2; +} + +void shift_int_right_3(void) +{ + aint0 >>= 3; +} + +void shift_int_right_4(void) +{ + aint0 >>= 4; +} + +void shift_int_right_5(void) +{ + aint0 >>= 5; +} + +void shift_int_right_6(void) +{ + aint0 >>= 6; +} + +void shift_int_right_7(void) +{ + aint0 >>= 7; +} + +void shift_int_right_8(void) +{ + aint0 >>= 8; +} + +void shift_int_right_9(void) +{ + aint0 >>= 9; +} + +void shift_int_right_10(void) +{ + aint0 >>= 10; +} + +void shift_int_right_11(void) +{ + aint0 >>= 11; +} + +void shift_int_right_12(void) +{ + aint0 >>= 12; +} + +void shift_int_right_13(void) +{ + aint0 >>= 13; +} + +void shift_int_right_14(void) +{ + aint0 >>= 14; +} + +void shift_int_right_15(void) +{ + aint0 >>= 15; +} + +/*****************************************************/ +void main(void) +{ + //char i; + + aint0 = 0xabcd; + + shift_int_left_1(); + if(aint0 != 0x579a) + failures++; + + aint0 = 0xabcd; + + shift_int_left_2(); + if(aint0 != 0xaf34) + failures++; + + aint0 = 0xabcd; + + shift_int_left_3(); + if(aint0 != 0x5e68) + failures++; + + aint0 = 0xabcd; + + shift_int_left_4(); + if(aint0 != 0xbcd0) + failures++; + + aint0 = 0xabcd; + + shift_int_left_5(); + if(aint0 != 0x79a0) + failures++; + + aint0 = 0xabcd; + + shift_int_left_6(); + if(aint0 != 0xf340) + failures++; + + aint0 = 0xabcd; + + shift_int_left_7(); + if(aint0 != 0xe680) + failures++; + + aint0 = 0xabcd; + + shift_int_left_8(); + if(aint0 != 0xcd00) + failures++; + + aint0 = 0xabcd; + + shift_int_left_9(); + if(aint0 != 0x9a00) + failures++; + + aint0 = 0xabcd; + + shift_int_left_10(); + if(aint0 != 0x3400) + failures++; + + aint0 = 0xabcd; + + shift_int_left_11(); + if(aint0 != 0x6800) + failures++; + + aint0 = 0xabcd; + + shift_int_left_12(); + if(aint0 != 0xd000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_13(); + if(aint0 != 0xa000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_14(); + if(aint0 != 0x4000) + failures++; + + aint0 = 0xabcd; + + shift_int_left_15(); + if(aint0 != 0x8000) + failures++; + + /***********************/ + aint0 = 0xabcd; + + shift_int_right_1(); + if(aint0 != 0xd5e6) + failures++; + + aint0 = 0xabcd; + + shift_int_right_2(); + if(aint0 != 0xeaf3) + failures++; + + aint0 = 0xabcd; + + shift_int_right_3(); + if(aint0 != 0xf579) + failures++; + + aint0 = 0xabcd; + + shift_int_right_4(); + if(aint0 != 0xfabc) + failures++; + + aint0 = 0xabcd; + + shift_int_right_5(); + if(aint0 != 0xfd5e) + failures++; + + aint0 = 0xabcd; + + shift_int_right_6(); + if(aint0 != 0xfeaf) + failures++; + + aint0 = 0xabcd; + + shift_int_right_7(); + if(aint0 != 0xff57) + failures++; + + aint0 = 0xabcd; + + shift_int_right_8(); + if(aint0 != 0xffab) + failures++; + + aint0 = 0xabcd; + + shift_int_right_9(); + if(aint0 != 0xffd5) + failures++; + + aint0 = 0xabcd; + + shift_int_right_10(); + if(aint0 != 0xffea) + failures++; + + aint0 = 0xabcd; + + shift_int_right_11(); + if(aint0 != 0xfff5) + failures++; + + aint0 = 0xabcd; + + shift_int_right_12(); + if(aint0 != 0xfffa) + failures++; + + aint0 = 0xabcd; + + shift_int_right_13(); + if(aint0 != 0xfffd) + failures++; + + aint0 = 0xabcd; + + shift_int_right_14(); + if(aint0 != 0xfffe) + failures++; + + aint0 = 0xabcd; + + shift_int_right_15(); + if(aint0 != -1) + failures++; + + done(); +} diff --git a/src/regression/rt.sh b/src/regression/rt.sh new file mode 100755 index 0000000..c256367 --- /dev/null +++ b/src/regression/rt.sh @@ -0,0 +1,20 @@ +# test Script + +USAGE="Usage: `basename $0` BASENAME" + +if [ $# -lt 1 ] ; then + echo "$USAGE" + exit 1 +fi + +# compile + +../../bin/sdcc -I ../../device/include/pic -c -mpic14 -pp16f877 $1.c + + +# create the executable + +gplink --map -c -o $1.o $1.o +./create_stc $1.cod $1.stc + +./simulate $1.stc garbage.log diff --git a/src/regression/simulate b/src/regression/simulate new file mode 100755 index 0000000..2ee28b9 --- /dev/null +++ b/src/regression/simulate @@ -0,0 +1,26 @@ +# SDCC test script for the PIC Port +# +USAGE="Usage: `basename $0` GPSIM_SCRIPT OUTPUT" + +if [ $# -lt 2 ] ; then + echo "$USAGE" + exit 1 +fi + +if [ "$GPSIM_PATH" != "" ] ; then + PATH=$GPSIM_PATH:$PATH +fi + +GPSIM_SCRIPT=$1 +LOGFILE=$2 +echo "Test $GPSIM_SCRIPT" | tee -a $LOGFILE +gpsim --cli -c $GPSIM_SCRIPT | tee -a $LOGFILE |\ +sed -n -e '/running\.\.\./,$ { +/^ *Message:/ { +s/^ *Message:\(.*\)$/\1/p +q +} +} +$ a\ +===> FAILED +' | tee -a $LOGFILE diff --git a/src/regression/string1.c b/src/regression/string1.c new file mode 100644 index 0000000..163893a --- /dev/null +++ b/src/regression/string1.c @@ -0,0 +1,53 @@ +#include "gpsim_assert.h" +#include "picregs.h" + +unsigned char failures=0; +//unsigned bit bit1; + +typedef unsigned char byte; + + +byte uchar0; +const byte arr[] = { 1,2,8,9,0 }; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void isr(void) __interrupt 1 __using 1 +{ + + if(arr[3]!=9) + failures++; + PORTB = 7; +} + +void lcd1(const unsigned char *str) +{ + const char *p = "hello world!\r\n"; + + if(!str) + failures++; + + if(str && str[2] != 'r') + failures++; + + if(arr[3]!=9) + failures++; + + while (1) { + if (0 == *p) break; + PORTB = *p; + p++; + } + +} + +void main(void) +{ + lcd1("str"); + done(); +} diff --git a/src/regression/struct1.c b/src/regression/struct1.c new file mode 100644 index 0000000..99b9f21 --- /dev/null +++ b/src/regression/struct1.c @@ -0,0 +1,104 @@ +#include "gpsim_assert.h" +// Addition tests + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + +/* Some compilers that support bit types do not support bit arithmetic + * (like bitx = bity + bitz;) */ +#define SUPPORT_BIT_ARITHMETIC 1 + +unsigned char failures = 0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char *acharP = 0; + +struct chars + { + unsigned char c0, c1; + unsigned int i0, i1; + }; + + +struct chars struct1; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +struct_test (void) +{ + + if (struct1.c0 || struct1.c1) + failures++; + + struct1.c0++; + + if (struct1.c0 != 1) + failures++; +} +/* +void +ptr_to_struct (struct chars *p) +{ + + if (p->c1) + failures++; + + + p->c1++; + + if (p->c1 != 1) + failures++; +} +*/ +void add_chars(void) +{ + + achar0 = struct1.c0 + struct1.c1; + + if(achar0 != 1) + failures++; +} + +void +main (void) +{ + + + struct1.c0 = 0; + struct1.c1 = 0; + struct_test (); + // ptr_to_struct (&struct1); + + struct1.c0 = 0; + struct1.c1 = 1; + add_chars(); + + done (); +} diff --git a/src/regression/sub.c b/src/regression/sub.c new file mode 100644 index 0000000..db28d20 --- /dev/null +++ b/src/regression/sub.c @@ -0,0 +1,252 @@ +#include "gpsim_assert.h" +// Addition tests + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + +/* Some compilers that support bit types do not support bit arithmetic + * (like bitx = bity + bitz;) */ +#define SUPPORT_BIT_ARITHMETIC 1 + +unsigned char failures=0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; +unsigned char achar3 = 0; +unsigned char *acharP = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void sub_lit_from_uchar(void) +{ + + achar0 = achar0 - 5; + + if(achar0 != 0xfb) + failures++; + + achar0 -= 10; + + if(achar0 != 0xf1) + failures++; + + achar0 = achar0 -1; // Should be a decrement + if(achar0 != 0xf0) + failures++; + + for(achar1 = 0; achar1 < 100; achar1++) + achar0 -= 2; + + if(achar0 != 40) + failures++; + +} + +// achar0 = 1 +// achar1 = 100 + +void sub_uchar2uchar(void) +{ + + achar1 = achar1 - achar0; + + if(achar1 != 99) + failures++; + + for(achar2 = 0; achar2<7; achar2++) + achar1 -= achar0; + + if(achar1 != 92) + failures++; + +} + +// assumes +// achar0 = 10 +// achar1 = 32 +// achar2, achar3 can be anything. + +void sub_uchar2uchar2(void) +{ + + + achar0--; + achar0 = achar0 - 1; + achar0 = achar0 - 2; + achar0 = achar0 - 3; + if(achar0 != 3) + failures++; + + + achar1 -= achar0; + if(achar1 != 29) + failures++; + + achar2 = achar1 - achar0; + if(achar2 != 26) + failures++; + + + achar3 = achar2 - achar1 - achar0; + if(achar3 != 0xfa) + failures++; + +} + +// sub_bits +// all bit variables are 0 upon entry. + +#if SUPPORT_BIT_TYPES +void sub_bits(void) +{ + + bit1 = bit0; + + bit0 = 1; + + if(bit1 != 0) + failures++; + + bit1 = bit0-bit1; // 1 - 0 => 1 + if(bit1 != 1) + failures++; + +#if SUPPORT_BIT_ARITHMETIC + bit2 = bit1-bit0; // 1 - 1 => 0 + if(bit2) + failures++; + + bit7 = bit4-bit5; + bit6 = bit4+bit5; + bit3 = bit4-bit5-bit6-bit7-bit0; // 0-0-0-0-1 => 1 + if(!bit3) + failures++; +#endif +} + +/* sub_bit2uchar(void) - assumes bit0 = 1, achar0 = 7 */ + +void sub_bit2uchar(void) +{ + + achar0 -= bit0; + + if(achar0 != 6) + failures++; + + if(achar0 == bit0) + failures++; + +} + +void sub_bit2uint(void) +{ + + if(aint0 != bit11) + failures++; + + aint0 -= bit0; + if(aint0!=0xffff) + failures++; + +} +#endif + +void sub_ucharFromLit(void) +{ + + achar0 = 2 - achar0; + + if(achar0 != 2) + failures++; + + + aint0 = 2 - aint0; + + if(aint0 != 2) + failures++; + + aint0--; + + if(aint0 != 1) + failures++; + + aint0 = 0x100 - aint0; + + if(aint0 != 0xff) + failures++; + + aint0 = 0xff00 - aint0; + + if(aint0 != 0xfe01) + failures++; + + aint0 = 0x0e01 - aint0; + + if(aint0 != 0x1000) + failures++; + + aint0 = 0x10ff - aint0; + + if(aint0 != 0xff) + failures++; + + + +} + +void main(void) +{ + + sub_lit_from_uchar(); + + achar0=1; + achar1=100; + sub_uchar2uchar(); + + + achar0 = 10; + achar1 = 32; + sub_uchar2uchar2(); + +#if SUPPORT_BIT_TYPES + sub_bits(); + + achar0 = 7; + bit0 = 1; + sub_bit2uchar(); + sub_bit2uint(); +#endif + + aint0 = 0; + achar0 = 0; + sub_ucharFromLit(); + + done(); +} diff --git a/src/regression/sub2.c b/src/regression/sub2.c new file mode 100644 index 0000000..44b54d5 --- /dev/null +++ b/src/regression/sub2.c @@ -0,0 +1,172 @@ +#include "gpsim_assert.h" +// Addition tests + +/* bit types are not ANSI - so provide a way of disabling bit types + * if this file is used to test other compilers besides SDCC */ +#define SUPPORT_BIT_TYPES 0 + +/* Some compilers that support bit types do not support bit arithmetic + * (like bitx = bity + bitz;) */ +#define SUPPORT_BIT_ARITHMETIC 1 + +unsigned char failures=0; + +#if SUPPORT_BIT_TYPES + +bit bit0 = 0; +bit bit1 = 0; +bit bit2 = 0; +bit bit3 = 0; +bit bit4 = 0; +bit bit5 = 0; +bit bit6 = 0; +bit bit7 = 0; +bit bit8 = 0; +bit bit9 = 0; +bit bit10 = 0; +bit bit11 = 0; + +#endif + +int int0 = 0; +int int1 = 0; +char char0 = 0; +char char1 = 0; +char char2 = 0; + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void sub_int1(void) +{ + if(int0 != 5) + failures++; + + if(int1 != 4) + failures++; + + int0 = int0 - int1; + + if(int0 != 1) + failures++; + + int0 = 4 - int0; + if(int0 != 3) + failures++; + + int0 = int0 - int1; + + if(int0 != -1) + failures++; + + int0 = int0 - 0xff; + + if(int0 != -0x100) + failures++; + + int0 = 0xff - int0; + + if(int0 != 0x1ff) + failures++; + + +} + +void sub_char_int(void) +{ + + int0 = int0 - char0; + + if(int0 != 3) + failures++; + + if(int0 < char0) + failures++; + + int0 = int0 - char0; + + if(int0 != 1) + failures++; + + if(int0 > char0) + failures++; + + + int0 = int0 - char0; + if(int0 != -1) + failures++; + + if(int0>0) + failures++; + +} + +void assign_char2int(void) +{ + + int0 = char0; + if(int0 != 0x7f) + failures++; + + int1 = char1; + if(int1 != -5) + failures++; + +} + + +void sub_compound_char(void) +{ + + char0 = char1 - 5; + if(char0 != 4) + failures++; + + if((char1 - char0 - 5) != 0) + failures++; + +} + +void sub_compound_int(void) +{ + + int0 = int1 - 5; + if(int0 != 4) + failures++; + + if((int1 - int0 - 5) != 0) + failures++; + +} + +void main(void) +{ + + int0 = 5; + int1 = 4; + + sub_int1(); + + int0 = 5; + int1 = 4; + char0 = 2; + + sub_char_int(); + + char0 = 0x7f; + char1 = -5; + assign_char2int(); + + char1 = 9; + sub_compound_char(); + + int1 = 9; + sub_compound_int(); + + done(); +} diff --git a/src/regression/switch1.c b/src/regression/switch1.c new file mode 100644 index 0000000..71730ea --- /dev/null +++ b/src/regression/switch1.c @@ -0,0 +1,84 @@ +#include "gpsim_assert.h" +unsigned char failures=0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void switch1(void) +{ + + + switch(achar0) { + case 0: + achar0 = 9; + break; + case 1: + achar0 = 18; + break; + + default: + achar0 = 0; + } + + +} + +void switch2(void) +{ + + switch(achar1) { + case 0: achar0 = 9; break; + case 1: achar0 = 8; break; + case 2: achar0 = 7; break; + case 3: achar0 = 6; break; + case 4: achar0 = 5; break; + case 5: achar0 = 4; break; + case 6: achar0 = 3; break; + case 7: achar0 = 2; break; + case 8: achar0 = 1; break; + case 9: achar0 = 0; break; + case 10: achar0 = 9; break; + case 11: achar0 = 8; break; + case 12: achar0 = 7; break; + default: achar0 = 0xff; break; + } + +} + +void main(void) +{ + + achar0 = 0; + switch1(); + if(achar0 != 9) + failures++; + + switch1(); + if(achar0 != 0) + failures++; + + achar0++; + + switch1(); + if(achar0 != 18) + failures++; + + for(achar1=0; achar1<10;achar1++){ + switch2(); + if(achar0 != (9-achar1)) + failures++; + + } + + done(); +} diff --git a/src/regression/while.c b/src/regression/while.c new file mode 100644 index 0000000..4429b88 --- /dev/null +++ b/src/regression/while.c @@ -0,0 +1,42 @@ +#include "gpsim_assert.h" + +unsigned char failures = 0; + +unsigned int aint0 = 0; +unsigned int aint1 = 0; +unsigned char achar0 = 0; +unsigned char achar1 = 0; + + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void +while1 (void) +{ + unsigned char i = 10; + + do + { + achar0++; + } + while (--i); + + if (achar0 != 10) + failures++; + +} + + +void +main (void) +{ + while1 (); + + + done (); +} diff --git a/src/regression/xor.c b/src/regression/xor.c new file mode 100644 index 0000000..cf5308c --- /dev/null +++ b/src/regression/xor.c @@ -0,0 +1,56 @@ +#include "gpsim_assert.h" +unsigned char failures=0; + +unsigned char achar0 = 0; +unsigned char achar1 = 0; +unsigned char achar2 = 0; + +void +done() +{ + ASSERT(MANGLE(failures) == 0); + PASSED(); +} + +void xor_chars_0_1(void) +{ + + achar2 = achar0 ^ achar1; + + achar0 = achar0 ^ 0x1; + + achar1 = achar0 ^ achar1 ^ 4; +} + +void xor_if(void) +{ + + if(achar0 ^ achar1) + failures++; + + achar0 ^= 0xff; + + if( !(achar0 ^ achar1) ) + failures++; + +} + +void main(void) +{ + + xor_chars_0_1(); + + if(achar2) + failures++; + + if(achar0 != 1) + failures++; + + if(achar1 != 5) + failures++; + + achar0 = achar1; + xor_if(); + + done(); +} diff --git a/src/reswords.gperf b/src/reswords.gperf new file mode 100644 index 0000000..651d581 --- /dev/null +++ b/src/reswords.gperf @@ -0,0 +1,67 @@ +%{ +/* Command-line: gperf -L KR-C -F ', 0, 0' -p -j1 -i 1 -g -o -t -N is_reserved_word -k1,3,$ c-parse.gperf */ +%} +struct reserved_words { const char *name; int token; bool is_special; }; +%% +auto, AUTO, 0 +break, BREAK, 0 +case, CASE, 0 +char, CHAR, 0 +const, CONST, 0 +continue, CONTINUE, 0 +default, DEFAULT, 0 +do, DO, 0 +double, FLOAT, 0 +else, ELSE, 0 +enum, ENUM, 0 +extern, EXTERN, 0 +float, FLOAT, 0 +for, FOR, 0 +goto, GOTO, 0 +if, IF, 0 +int, INT, 0 +interrupt, INTERRUPT, 0 +long, LONG, 0 +register, REGISTER, 0 +return, RETURN, 0 +short, SHORT, 0 +signed, SIGNED, 0 +sizeof, SIZEOF, 0 +static, STATIC, 0 +struct, STRUCT, 0 +switch, SWITCH, 0 +typedef, TYPEDEF, 0 +union, UNION, 0 +unsigned, UNSIGNED, 0 +void, VOID, 0 +volatile, VOLATILE, 0 +while, WHILE, 0 +_code, _CODE, 1 +_eeprom, _EEPROM, 1 +_flash, _CODE, 1 +_generic, _GENERIC, 1 +_near, _NEAR, 1 +_sram, _XDATA, 1 +_xdata, _XDATA, 1 +_pdata, _PDATA, 1 +_idata, _IDATA, 1 +at, AT, 1 +bit, BIT, 1 +critical, CRITICAL, 1 +data, DATA, 1 +far, XDATA, 1 +eeprom, EEPROM, 1 +flash, CODE, 1 +idata, IDATA, 1 +nonbanked, NONBANKED, 1 +banked, BANKED, 1 +near, DATA, 1 +pdata, PDATA, 1 +reentrant, REENTRANT, 1 +sfr, SFR, 1 +sbit, SBIT, 1 +sram, XDATA, 1 +using, USING, 1 +code, CODE, 1 +xdata, XDATA, 1 +_data, _NEAR, 1 diff --git a/src/sdcc.vcxproj b/src/sdcc.vcxproj new file mode 100644 index 0000000..d47a395 --- /dev/null +++ b/src/sdcc.vcxproj @@ -0,0 +1,524 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {F9485499-151B-4886-935C-7D687C4A0549} + sdcc + + + + Application + false + MultiByte + + + Application + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + ..\bin_vc\ + $(Configuration)\ + true + ..\bin_vc\ + $(Configuration)\ + true + sdcc + sdcc + + + + .\Debug/src.tlb + + + + + Disabled + .;..;..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_CONSOLE;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/src.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + EditAndContinue + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + ..\bin_vc\$(TargetFileName) + true + true + .\Debug/sdcc.pdb + Console + false + + + MachineX86 + 0x2000000 + + + true + .\Debug/src.bsc + + + + + .\Release/src.tlb + + + + + MaxSpeed + OnlyExplicitInline + .;..;..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_CONSOLE;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/src.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + ..\bin_vc\$(TargetFileName) + true + Console + false + + + MachineX86 + 0x2000000 + + + true + .\Release/src.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + %(PreprocessorDefinitions) + %(PreprocessorDefinitions) + ..;%(AdditionalIncludeDirectories) + ..;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1} + false + + + {14a8a991-bad6-49eb-84fb-6f6cf12b436d} + false + + + {6fa87476-0fab-4518-9845-12efebbce03f} + false + + + {9facdb81-be66-42d0-95f5-ea2fa3b09065} + false + + + {b96e942e-39f5-4c7c-97fd-a095de6847c6} + false + + + {7e09a25e-1c9d-438d-85db-8535f134890d} + false + + + {0ba12b9f-bcd6-4c08-9992-69b4fb32d335} + false + + + {7444a72f-c7c6-4f90-9a62-2d46240b52db} + false + + + + + + \ No newline at end of file diff --git a/src/sdcc.vcxproj.filters b/src/sdcc.vcxproj.filters new file mode 100644 index 0000000..01afd69 --- /dev/null +++ b/src/sdcc.vcxproj.filters @@ -0,0 +1,313 @@ + + + + + {d62eee26-dbd0-4367-9726-8be6e7a7de74} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {542d6692-b312-430e-b08c-25d50a679c72} + h;hpp;hxx;hm;inl + + + {294f3d1a-92d6-4c47-884a-62ffa390e878} + + + {ffdab0d0-444f-4892-8854-39c4adee434b} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Regression Test + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + + + + Custom Build + + + Custom Build + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/src/stm8/Makefile b/src/stm8/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/stm8/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/stm8/Makefile.in b/src/stm8/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/stm8/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/stm8/gen.c b/src/stm8/gen.c new file mode 100644 index 0000000..7bf8c22 --- /dev/null +++ b/src/stm8/gen.c @@ -0,0 +1,8223 @@ +/*------------------------------------------------------------------------- + gen.c - code generator for STM8. + + Copyright (C) 2012 - 2013, Philipp Klaus Krause pkk@spth.de, philipp@informatik.uni-frankfurt.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include "ralloc.h" +#include "gen.h" + +/* Use the D macro for basic (unobtrusive) debugging messages */ +#define D(x) do if (options.verboseAsm) { x; } while (0) + +static bool regalloc_dry_run; +static unsigned int regalloc_dry_run_cost_bytes; +static unsigned int regalloc_dry_run_cost_cycles; +static unsigned int regalloc_dry_run_cycle_scale = 1; + +static struct +{ + short debugLine; + struct + { + int pushed; + int size; + int param_offset; + } stack; + bool saved; +} +G; + +enum asminst +{ + A_ADC, + A_ADD, + A_AND, + A_BCP, + A_CLR, + A_CLRW, + A_CP, + A_CPL, + A_CPLW, + A_DEC, + A_DECW, + A_INC, + A_INCW, + A_LD, + A_MOV, + A_NEG, + A_NEGW, + A_OR, + A_RLC, + A_RLCW, + A_RLWA, + A_RRC, + A_RRCW, + A_RRWA, + A_SBC, + A_SLL, + A_SLLW, + A_SRA, + A_SRAW, + A_SRL, + A_SRLW, + A_SUB, + A_SWAP, + A_TNZ, + A_TNZW, + A_XOR +}; + +static const char *asminstnames[] = +{ + "adc", + "add", + "and", + "bcp", + "clr", + "clrw", + "cp", + "cpl", + "cplw", + "dec", + "decw", + "inc", + "incw", + "ld", + "mov", + "neg", + "negw", + "or", + "rlc", + "rlcw", + "rlwa", + "rrc", + "rrcw", + "rrwa", + "sbc", + "sll", + "sllw", + "sra", + "sraw", + "srl", + "srlw", + "sub", + "swap", + "tnz", + "tnzw", + "xor" +}; + +static struct asmop asmop_a, asmop_x, asmop_y, asmop_xy, asmop_xyl, asmop_zero, asmop_one; +static struct asmop *const ASMOP_A = &asmop_a; +static struct asmop *const ASMOP_X = &asmop_x; +static struct asmop *const ASMOP_Y = &asmop_y; +static struct asmop *const ASMOP_XY = &asmop_xy; +static struct asmop *const ASMOP_XYL = &asmop_xyl; +static struct asmop *const ASMOP_ZERO = &asmop_zero; +static struct asmop *const ASMOP_ONE = &asmop_one; + +void +stm8_init_asmops (void) +{ + asmop_a.type = AOP_REG; + asmop_a.size = 1; + asmop_a.aopu.bytes[0].in_reg = TRUE; + asmop_a.aopu.bytes[0].byteu.reg = stm8_regs + A_IDX; + asmop_a.regs[A_IDX] = 0; + asmop_a.regs[XL_IDX] = -1; + asmop_a.regs[XH_IDX] = -1; + asmop_a.regs[YL_IDX] = -1; + asmop_a.regs[YH_IDX] = -1; + asmop_a.regs[C_IDX] = -1; + + asmop_x.type = AOP_REG; + asmop_x.size = 2; + asmop_x.aopu.bytes[0].in_reg = TRUE; + asmop_x.aopu.bytes[0].byteu.reg = stm8_regs + XL_IDX; + asmop_x.aopu.bytes[1].in_reg = TRUE; + asmop_x.aopu.bytes[1].byteu.reg = stm8_regs + XH_IDX; + asmop_x.regs[A_IDX] = -1; + asmop_x.regs[XL_IDX] = 0; + asmop_x.regs[XH_IDX] = 1; + asmop_x.regs[YL_IDX] = -1; + asmop_x.regs[YH_IDX] = -1; + asmop_x.regs[C_IDX] = -1; + + asmop_y.type = AOP_REG; + asmop_y.size = 2; + asmop_y.aopu.bytes[0].in_reg = TRUE; + asmop_y.aopu.bytes[0].byteu.reg = stm8_regs + YL_IDX; + asmop_y.aopu.bytes[1].in_reg = TRUE; + asmop_y.aopu.bytes[1].byteu.reg = stm8_regs + YH_IDX; + asmop_y.regs[A_IDX] = -1; + asmop_y.regs[XL_IDX] = -1; + asmop_y.regs[XH_IDX] = -1; + asmop_y.regs[YL_IDX] = 0; + asmop_y.regs[YH_IDX] = 1; + asmop_y.regs[C_IDX] = -1; + + asmop_xy.type = AOP_REG; + asmop_xy.size = 4; + asmop_xy.aopu.bytes[0].in_reg = TRUE; + asmop_xy.aopu.bytes[0].byteu.reg = stm8_regs + XL_IDX; + asmop_xy.aopu.bytes[1].in_reg = TRUE; + asmop_xy.aopu.bytes[1].byteu.reg = stm8_regs + XH_IDX; + asmop_xy.aopu.bytes[2].in_reg = TRUE; + asmop_xy.aopu.bytes[2].byteu.reg = stm8_regs + YL_IDX; + asmop_xy.aopu.bytes[3].in_reg = TRUE; + asmop_xy.aopu.bytes[3].byteu.reg = stm8_regs + YH_IDX; + asmop_xy.regs[A_IDX] = -1; + asmop_xy.regs[XL_IDX] = 0; + asmop_xy.regs[XH_IDX] = 1; + asmop_xy.regs[YL_IDX] = 2; + asmop_xy.regs[YH_IDX] = 3; + asmop_xy.regs[C_IDX] = -1; + + asmop_xyl.type = AOP_REG; + asmop_xyl.size = 3; + asmop_xyl.aopu.bytes[0].in_reg = TRUE; + asmop_xyl.aopu.bytes[0].byteu.reg = stm8_regs + XL_IDX; + asmop_xyl.aopu.bytes[1].in_reg = TRUE; + asmop_xyl.aopu.bytes[1].byteu.reg = stm8_regs + XH_IDX; + asmop_xyl.aopu.bytes[2].in_reg = TRUE; + asmop_xyl.aopu.bytes[2].byteu.reg = stm8_regs + YL_IDX; + asmop_xy.regs[A_IDX] = -1; + asmop_xy.regs[XL_IDX] = 0; + asmop_xy.regs[XH_IDX] = 1; + asmop_xy.regs[YL_IDX] = 2; + asmop_xy.regs[YH_IDX] = -1; + asmop_xy.regs[C_IDX] = -1; + + asmop_zero.type = AOP_LIT; + asmop_zero.size = 1; + asmop_zero.aopu.aop_lit = constVal ("0"); + asmop_zero.regs[A_IDX] = -1; + asmop_zero.regs[XL_IDX] = -1; + asmop_zero.regs[XH_IDX] = -1; + asmop_zero.regs[YL_IDX] = -1; + asmop_zero.regs[YH_IDX] = -1; + asmop_zero.regs[C_IDX] = -1; + + asmop_one.type = AOP_LIT; + asmop_one.size = 1; + asmop_one.aopu.aop_lit = constVal ("1"); + asmop_one.regs[A_IDX] = -1; + asmop_one.regs[XL_IDX] = -1; + asmop_one.regs[XH_IDX] = -1; + asmop_one.regs[YL_IDX] = -1; + asmop_one.regs[YH_IDX] = -1; + asmop_one.regs[C_IDX] = -1; +} + +void emit2 (const char *inst, const char *fmt, ...) +{ + if (!regalloc_dry_run) + { + va_list ap; + + va_start (ap, fmt); + va_emitcode (inst, fmt, ap); + va_end (ap); + } +} + +/*--------------------------------------------------------------------------*/ +/* updateCFA - update the debugger information to reflect the current */ +/* connonical frame address relative to the stack pointer */ +/*--------------------------------------------------------------------------*/ +static void +updateCFA (void) +{ + /* there is no frame unless there is a function */ + if (!currFunc) + return; + + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, &stm8_regs[SP_IDX], 1 + G.stack.param_offset + G.stack.pushed); +} +// + +/*-----------------------------------------------------------------*/ +/* aopRS - asmop in register or on stack */ +/*-----------------------------------------------------------------*/ +static bool +aopRS (const asmop *aop) +{ + return (aop->type == AOP_REG || aop->type == AOP_REGSTK || aop->type == AOP_STK); +} + +/*-----------------------------------------------------------------*/ +/* aopInReg - asmop from offset in the register */ +/*-----------------------------------------------------------------*/ +static bool +aopInReg (const asmop *aop, int offset, short rIdx) +{ + if (!(aop->type == AOP_REG || aop->type == AOP_REGSTK)) + return (FALSE); + + if (offset >= aop->size || offset < 0) + return (FALSE); + + if (rIdx == X_IDX) + return (aopInReg (aop, offset, XL_IDX) && aopInReg (aop, offset + 1, XH_IDX)); + + if (rIdx == Y_IDX) + return (aopInReg (aop, offset, YL_IDX) && aopInReg (aop, offset + 1, YH_IDX)); + + return (aop->aopu.bytes[offset].in_reg && aop->aopu.bytes[offset].byteu.reg->rIdx == rIdx); +} + +/*-----------------------------------------------------------------*/ +/* aopOnStack - asmop from offset on stack in consecutive memory */ +/*-----------------------------------------------------------------*/ +static bool +aopOnStack (const asmop *aop, int offset, int size) +{ + int i; + long int stk_base; + + if (!(aop->type == AOP_STK || aop->type == AOP_REGSTK)) + return (FALSE); + + if (offset + size > aop->size) + return (FALSE); + + // Fully on stack? + for (i = offset; i < offset + size; i++) + if (aop->aopu.bytes[i].in_reg) + return (FALSE); + + // Consecutive? + stk_base = aop->aopu.bytes[offset].byteu.stk; + for (i = 1; i < size; i++) + if (!regalloc_dry_run && aop->aopu.bytes[offset + i].byteu.stk != stk_base - i) // Todo: Stack offsets might be unavailable during dry run (messes with addition costs, so we should have a mechanism to do it better). + return (FALSE); + + return (TRUE); +} + +/*-----------------------------------------------------------------*/ +/* aopOnStack - asmop from offset on stack (excl. extended stack) */ +/*-----------------------------------------------------------------*/ +static bool +aopOnStackNotExt (const asmop *aop, int offset, int size) +{ + return (aopOnStack (aop, offset, size) && (aop->aopu.bytes[offset].byteu.stk + G.stack.pushed <= 255 || regalloc_dry_run));// Todo: Stack offsets might be unavailable during dry run (messes with addition costs, so we should have a mechanism to do it better). +} + +/*-----------------------------------------------------------------*/ +/* aopSame - are two asmops in the same location? */ +/*-----------------------------------------------------------------*/ +static bool +aopSame (const asmop *aop1, int offset1, const asmop *aop2, int offset2, int size) +{ + for(; size; size--, offset1++, offset2++) + { + if (aopRS (aop1) && aopRS (aop2) && // Same register + aop1->aopu.bytes[offset1].in_reg && aop2->aopu.bytes[offset2].in_reg && + aop1->aopu.bytes[offset1].byteu.reg == aop2->aopu.bytes[offset2].byteu.reg) + continue; + + if (aopOnStack (aop1, offset1, 1) && aopOnStack (aop2, offset2, 1) && // Same stack location + aop1->aopu.bytes[offset1].byteu.stk == aop2->aopu.bytes[offset2].byteu.stk) + continue; + + if (aop1->type == AOP_LIT && aop2->type == AOP_LIT && + byteOfVal (aop1->aopu.aop_lit, offset1) == byteOfVal (aop2->aopu.aop_lit, offset2)) + continue; + + if (aop1->type == AOP_DIR && aop2->type == AOP_DIR && + offset1 == offset2 && !strcmp(aop1->aopu.aop_dir, aop2->aopu.aop_dir)) + return (TRUE); + + return (FALSE); + } + + return (TRUE); +} + +/*-----------------------------------------------------------------*/ +/* aopIsLitVal - asmop from offset is val */ +/*-----------------------------------------------------------------*/ +static bool +aopIsLitVal (const asmop *aop, int offset, int size, unsigned long long int val) +{ + wassert_bt (size <= sizeof (unsigned long long int)); // Make sure we are not testing outside of argument val. + + for(; size; size--, offset++) + { + unsigned char b = val & 0xff; + val >>= 8; + + // Leading zeroes + if (aop->size <= offset && !b) + continue; + + if (aop->type != AOP_LIT) + return (FALSE); + + if (byteOfVal (aop->aopu.aop_lit, offset) != b) + return (FALSE); + } + + return (TRUE); +} + +static void +cost(unsigned int bytes, unsigned int cycles) +{ + regalloc_dry_run_cost_bytes += bytes; + regalloc_dry_run_cost_cycles += cycles * regalloc_dry_run_cycle_scale; +} + +void emitJP(const symbol *target, float probability) +{ + if (!regalloc_dry_run) + emit2 (options.model == MODEL_LARGE ? "jpf" : "jp", "%05d$", labelKey2num (target->key)); + cost (3 + (options.model == MODEL_LARGE), (1 + (options.model == MODEL_LARGE)) * probability); +} + +static const char * +aopGet(const asmop *aop, int offset) +{ + static char buffer[256]; + + /* Don't really need the value during dry runs, so save some time. */ + if (regalloc_dry_run) + return (""); + + if (offset >= aop->size) + return ("#0x00"); + + if (aop->type == AOP_LIT) + { + SNPRINTF (buffer, sizeof(buffer), "#0x%02x", byteOfVal (aop->aopu.aop_lit, offset)); + return (buffer); + } + + if (aopRS (aop) && aop->aopu.bytes[offset].in_reg) + return (aop->aopu.bytes[offset].byteu.reg->name); + + if (aopRS (aop) && !aop->aopu.bytes[offset].in_reg) + { + int soffset = aop->aopu.bytes[offset].byteu.stk + G.stack.pushed; + + if (soffset > 255) + { + long int eoffset = (long int)(aop->aopu.bytes[offset].byteu.stk) + G.stack.size - 256l; + + wassertl_bt (regalloc_dry_run || stm8_extend_stack, "Extended stack access, but y not prepared for extended stack access."); + wassertl_bt (regalloc_dry_run || eoffset >= 0l && eoffset <= 0xffffl, "Stack access out of extended stack range."); // Stack > 64K. + + SNPRINTF (buffer, sizeof(buffer), "(0x%x, y)", (unsigned)eoffset); + } + else + SNPRINTF (buffer, sizeof(buffer), "(0x%02x, sp)", (unsigned)soffset); + return (buffer); + } + + if (aop->type == AOP_IMMD) + { + wassertl_bt (offset < (2 + (options.model == MODEL_LARGE)), "Immediate operand out of range"); + if (offset == 0) + SNPRINTF (buffer, sizeof(buffer), "#<(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + else + SNPRINTF (buffer, sizeof(buffer), "#((%s + %d) >> %d)", aop->aopu.immd, aop->aopu.immd_off, offset * 8); + return (buffer); + } + + if (aop->type == AOP_DIR) + { + SNPRINTF (buffer, sizeof(buffer), "%s+%d", aop->aopu.aop_dir, aop->size - 1 - offset); + return (buffer); + } + + wassert_bt (0); + return ("dummy"); +} + +static const char * +aopGet2(const asmop *aop, int offset) +{ + static char buffer[256]; + + /* Workaround for an assembler issue */ + if (regalloc_dry_run && aop->type == AOP_IMMD && offset) + cost (100, 100); + /* Don't really need the value during dry runs, so save some time. */ + if (regalloc_dry_run) + return (""); + + if (aopInReg (aop, offset, X_IDX)) + return("x"); + if (aopInReg (aop, offset, Y_IDX)) + return("y"); + + if (aop->type != AOP_LIT && !aopOnStack (aop, offset, 2) && aop->type != AOP_IMMD && aop->type != AOP_DIR) + fprintf (stderr, "Invalid aop for aopGet2. aop->type %d. aop->size %d.\n", aop->type, aop->size); + wassert_bt (aop->type == AOP_LIT || aopOnStack (aop, offset, 2) || aop->type == AOP_IMMD || aop->type == AOP_DIR); + + if (aop->type == AOP_LIT) + { + SNPRINTF (buffer, sizeof(buffer), "#0x%02x%02x", byteOfVal (aop->aopu.aop_lit, offset + 1), byteOfVal (aop->aopu.aop_lit, offset)); + return (buffer); + } + else if (aop->type == AOP_IMMD) + { + if (offset) + SNPRINTF (buffer, sizeof(buffer), "#((%s + %d) >> %d)", aop->aopu.immd, aop->aopu.immd_off, offset * 8); + else + SNPRINTF (buffer, sizeof(buffer), "#(%s + %d)", aop->aopu.immd, aop->aopu.immd_off); + return (buffer); + } + + return (aopGet (aop, offset + 1)); +} + +/* For operations that always have the accumulator as left operand. */ +static void +op8_cost (const asmop *op2, int offset2) +{ + AOP_TYPE op2type = op2->type; + int r2Idx = ((aopRS (op2) && op2->aopu.bytes[offset2].in_reg)) ? op2->aopu.bytes[offset2].byteu.reg->rIdx : -1; + + if (offset2 >= op2->size) + op2type = AOP_LIT; + + switch (op2type) + { + case AOP_LIT: + case AOP_IMMD: + cost (2, 1); + return; + case AOP_DIR: + cost (3, 1); + return; + case AOP_REG: + case AOP_REGSTK: + case AOP_STK: + if (r2Idx != -1) + goto error; + cost (2, 1); + return; + default: + goto error; + } +error: + fprintf(stderr, "op2 type: %d, offset %d, rIdx %d\n", op2type, offset2, r2Idx); + wassert_bt (0); + cost (8, 4 * 8); +} + +/* For 8-bit operations that have only one operand, i.e. tnz */ +static void +op_cost (const asmop *op1, int offset1) +{ + AOP_TYPE op1type; + int r1Idx; + + wassert_bt (op1); + + op1type = op1->type; + r1Idx = ((aopRS (op1) && op1->aopu.bytes[offset1].in_reg)) ? op1->aopu.bytes[offset1].byteu.reg->rIdx : -1; + + switch (op1type) + { + case AOP_DIR: + cost (4, 1); + return; + case AOP_REG: + case AOP_REGSTK: + case AOP_STK: + if (r1Idx == A_IDX) + { + cost (1, 1); + return; + } + if (r1Idx != -1) + goto error; + cost (2, 1); + return; + default: + goto error; + } +error: + fprintf(stderr, "op1 type: %d, offset %d, rIdx %d\n", op1type, offset1, r1Idx); + wassert_bt (0); + cost (8, 4 * 8); +} + +/* For cheap 16-bit operations that have only one operand, i.e. incw */ +static void +opw_cost (const asmop *op1, int offset1) +{ + wassert_bt (op1); + + if (aopInReg (op1, offset1, XL_IDX)) + { + cost (1, 1); + return; + } + else if (aopInReg (op1, offset1, YL_IDX)) + { + cost (2, 1); + return; + } + + wassert_bt (0); + cost (8, 4 * 8); +} + +/* For 16-bit operations that have only one operand, i.e. tnzw */ +static void +opw_cost2 (const asmop *op1, int offset1) +{ + wassert_bt (op1); + + if (aopInReg (op1, offset1, XL_IDX)) + { + cost (1, 2); + return; + } + else if (aopInReg (op1, offset1, YL_IDX)) + { + cost (2, 2); + return; + } + + wassert_bt (0); + cost (8, 4 * 8); +} + +static void +ld_cost (const asmop *op1, int offset1, const asmop *op2, int offset2) +{ + int r1Idx, r2Idx; + + AOP_TYPE op1type = op1->type; + AOP_TYPE op2type = op2->type; + + /* Costs are symmetric */ + if (aopRS (op2) || op2type == AOP_DUMMY) + { + const asmop *tmp = op1; + const int tmpo = offset1; + op1 = op2; + op2 = tmp; + offset1 = offset2; + offset2 = tmpo; + op1type = op1->type; + op2type = op2->type; + } + + r1Idx = ((aopRS (op1) && op1->aopu.bytes[offset1].in_reg)) ? op1->aopu.bytes[offset1].byteu.reg->rIdx : -1; + r2Idx = ((aopRS (op2) && op2->aopu.bytes[offset2].in_reg)) ? op2->aopu.bytes[offset2].byteu.reg->rIdx : -1; + + if (offset2 >= op2->size) + op2type = AOP_LIT; + + switch (op1type) + { + case AOP_REG: + case AOP_REGSTK: + case AOP_STK: + switch (op2type) + { + case AOP_LIT: + case AOP_IMMD: + if (r1Idx != A_IDX) + goto error; + cost (2, 1); + return; + case AOP_REG: + case AOP_REGSTK: + case AOP_STK: + switch (r1Idx) + { + case A_IDX: + switch (r2Idx) + { + case XL_IDX: + case XH_IDX: + cost (1, 1); + return; + case YL_IDX: + case YH_IDX: + case -1: + cost (2, 1); + return; + default: + goto error; + } + case XL_IDX: + case XH_IDX: + if (r2Idx != A_IDX) + goto error; + cost (1, 1); + return; + case YL_IDX: + case YH_IDX: + case -1: + if (r2Idx != A_IDX) + goto error; + cost (2, 1); + return; + } + case AOP_DIR: + if (r1Idx != A_IDX) + goto error; + cost (3, 2); + return; + default: + goto error; + } + case AOP_DIR: + if (r2Idx != A_IDX) + goto error; + cost (3, 2); + return; + default: + goto error; + } +error: + fprintf(stderr, "op1 type: %d, offset %d, rIdx %d\n", op1type, offset1, r1Idx); + fprintf(stderr, "op2 type: %d, offset %d, rIdx %d\n", op2type, offset2, r2Idx); + wassert_bt (0); + cost (8, 4 * 8); +} + +static void +mov_cost (const asmop *op1, const asmop *op2) +{ + if (op2->type == AOP_LIT || op2->type == AOP_IMMD) + cost (4, 1); + else + cost (5, 1); +} + +static void +emit3cost (enum asminst inst, const asmop *op1, int offset1, const asmop *op2, int offset2) +{ + switch (inst) + { + case A_ADC: + case A_ADD: + case A_AND: + case A_BCP: + op8_cost (op2, offset2); + break; + case A_CLR: + op_cost (op1, offset1); + break; + case A_CP: + op8_cost (op2, offset2); + break; + case A_CPL: + op_cost (op1, offset1); + break; + case A_INC: + case A_DEC: + op_cost (op1, offset1); + break; + case A_LD: + ld_cost (op1, offset1, op2, offset2); + break; + case A_MOV: + mov_cost (op1, op2); + break; + case A_NEG: + op_cost (op1, offset1); + break; + case A_OR: + op8_cost (op2, offset2); + break; + case A_RLC: + case A_RRC: + op_cost (op1, offset1); + break; + case A_SBC: + op8_cost (op2, offset2); + break; + case A_SLL: + case A_SRA: + case A_SRL: + op_cost (op1, offset1); + break; + case A_SUB: + op8_cost (op2, offset2); + break; + case A_SWAP: + case A_TNZ: + op_cost (op1, offset1); + break; + case A_XOR: + op8_cost (op2, offset2); + break; + default: + wassertl_bt (0, "Tried to get cost for unknown 8-bit instruction"); + } +} + +static void +emit3wcost (enum asminst inst, const asmop *op1, int offset1, const asmop *op2, int offset2) +{ + switch (inst) + { + case A_CLRW: + opw_cost (op1, offset1); + break; + case A_CPLW: + opw_cost2 (op1, offset1); + break; + case A_DECW: + case A_INCW: + opw_cost (op1, offset1); + break; + case A_NEGW: + case A_RLCW: + opw_cost2 (op1, offset1); + break; + case A_RLWA: + opw_cost (op1, offset1); + break; + case A_RRCW: + opw_cost2 (op1, offset1); + break; + case A_RRWA: + opw_cost (op1, offset1); + break; + case A_SLLW: + case A_SRAW: + case A_SRLW: + case A_TNZW: + opw_cost2 (op1, offset1); + break; + default: + wassertl_bt (0, "Tried to get cost for unknown 16-bit instruction"); + } +} + +static void +emit3_o (enum asminst inst, asmop *op1, int offset1, asmop *op2, int offset2) +{ + emit3cost (inst, op1, offset1, op2, offset2); + if (regalloc_dry_run) + return; + + if (op2) + { + char *l = Safe_strdup (aopGet (op1, offset1)); + emit2 (asminstnames[inst], "%s, %s", l, aopGet (op2, offset2)); + Safe_free (l); + } + else + emit2 (asminstnames[inst], "%s", aopGet (op1, offset1)); +} + +static void +emit3w_o (enum asminst inst, asmop *op1, int offset1, asmop *op2, int offset2) +{ + emit3wcost (inst, op1, offset1, op2, offset2); + if (regalloc_dry_run) + return; + + if (op2) + { + char *l = Safe_strdup (aopGet2 (op1, offset1)); + emit2 (asminstnames[inst], "%s, %s", l, aopGet2 (op2, offset2)); + Safe_free (l); + } + else + emit2 (asminstnames[inst], "%s", aopGet2 (op1, offset1)); +} + +static void +emit3 (enum asminst inst, asmop *op1, asmop *op2) +{ + emit3_o (inst, op1, 0, op2, 0); +} + +static void +emit3w (enum asminst inst, asmop *op1, asmop *op2) +{ + emit3w_o (inst, op1, 0, op2, 0); +} + +static bool +regFree (int idx, const iCode *ic) +{ + if (idx == X_IDX) + return (regFree (XL_IDX, ic) && regFree (XH_IDX, ic)); + if (idx == Y_IDX) + return (regFree (YL_IDX, ic) && regFree (YH_IDX, ic)); + + if ((idx == YL_IDX || idx == YH_IDX) && stm8_extend_stack) + return FALSE; + + return (!bitVectBitValue (ic->rMask, idx)); +} + +static bool +regDead (int idx, const iCode *ic) +{ + if (idx == X_IDX) + return (regDead (XL_IDX, ic) && regDead (XH_IDX, ic)); + if (idx == Y_IDX) + return (regDead (YL_IDX, ic) && regDead (YH_IDX, ic)); + + if ((idx == YL_IDX || idx == YH_IDX) && stm8_extend_stack) + return FALSE; + + return (!bitVectBitValue (ic->rSurv, idx)); +} + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = Safe_calloc (1, sizeof (asmop)); + aop->type = type; + + aop->regs[A_IDX] = -1; + aop->regs[XL_IDX] = -1; + aop->regs[XH_IDX] = -1; + aop->regs[YL_IDX] = -1; + aop->regs[YH_IDX] = -1; + aop->regs[C_IDX] = -1; + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand *op) +{ + asmop *aop; + + wassert_bt (op); + + aop = op->aop; + + if (!aop) + return; + + Safe_free (aop); + + op->aop = NULL; + if (IS_SYMOP (op) && SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (const iCode *ic, symbol *sym) +{ + asmop *aop; + + wassert_bt (ic); + wassert_bt (sym); + wassert_bt (sym->etype); + + // Unlike other backends we really free asmops; to avoid a double-free, we need to support multiple asmops for the same symbol. + + if (IS_FUNC (sym->type)) + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = sym->rname; + aop->aopu.immd_off = 0; + aop->size = getSize (sym->type); + } + /* Assign depending on the storage class */ + else if (sym->onStack || sym->iaccess) + { + int offset; + int base; + + aop = newAsmop (AOP_STK); + aop->size = getSize (sym->type); + + base = sym->stack + (sym->stack > 0 ? G.stack.param_offset : 0); + + for(offset = 0; offset < aop->size; offset++) + aop->aopu.bytes[offset].byteu.stk = base + aop->size - offset; + } + else + { + aop = newAsmop (AOP_DIR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + } + + return (aop); +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol *sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop; + int val = 0; + + wassert_bt (ic); + + for (;;) + { + if (ic->op == '+') + { + if (isOperandLiteral (IC_RIGHT (ic))) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else + { + val += (int) operandLitValue (IC_LEFT (ic)); + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else if (ic->op == ADDRESS_OF) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + break; + } + else + wassert_bt (0); + } + + if (OP_SYMBOL (IC_LEFT (ic))->onStack) + { + aop = newAsmop (AOP_STL); + aop->aopu.stk_off = (long)(OP_SYMBOL (IC_LEFT (ic))->stack) + 1 + val; + } + else + { + aop = newAsmop (AOP_IMMD); + aop->aopu.immd = OP_SYMBOL (IC_LEFT (ic))->rname; + aop->aopu.immd_off = val; + } + + aop->size = getSize (sym->type); + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand *op, const iCode *ic) +{ + symbol *sym; + unsigned int i; + + wassert_bt (op); + + /* if already has an asmop */ + if (op->aop) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) + { + asmop *aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + op->aop = aop; + return; + } + + sym = OP_SYMBOL (op); + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, sym); + return; + } + + /* Rematerialize symbols where all bytes are spilt. */ + if (sym->remat && (sym->isspilt || regalloc_dry_run)) + { + bool completely_spilt = TRUE; + for (i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + if (completely_spilt) + { + op->aop = aopForRemat (sym); + return; + } + } + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + asmop *aop = newAsmop (AOP_CND); + op->aop = aop; + sym->aop = sym->aop; + return; + } + + /* None of the above, which only leaves temporaries. */ + { + bool completely_in_regs = TRUE; + bool completely_on_stack = TRUE; + asmop *aop = newAsmop (AOP_REGSTK); + + aop->size = getSize (operandType (op)); + op->aop = aop; + + for (i = 0; i < aop->size; i++) + { + aop->aopu.bytes[i].in_reg = !!sym->regs[i]; + if (sym->regs[i]) + { + completely_on_stack = FALSE; + aop->aopu.bytes[i].byteu.reg = sym->regs[i]; + aop->regs[sym->regs[i]->rIdx] = i; + } + else if (sym->isspilt && sym->usl.spillLoc || sym->nRegs && regalloc_dry_run) + { + completely_in_regs = FALSE; + + if (!regalloc_dry_run) + { + aop->aopu.bytes[i].byteu.stk = (long int)(sym->usl.spillLoc->stack) + aop->size - i; + + if (sym->usl.spillLoc->stack + aop->size - (int)(i) <= -G.stack.pushed) + { + fprintf (stderr, "%s %d %d %d %d at ic %d\n", sym->name, (int)(sym->usl.spillLoc->stack), (int)(aop->size), (int)(i), (int)(G.stack.pushed), ic->key); + wassertl_bt (0, "Invalid stack offset."); + } + } + else + { + static long int old_base = -10; + static const symbol *old_sym = 0; + if (sym != old_sym) + { + old_base -= aop->size; + if (old_base < -100) + old_base = -10; + old_sym = sym; + } + + aop->aopu.bytes[i].byteu.stk = old_base + aop->size - i; + } + } + else // Dummy iTemp. + { + aop->type = AOP_DUMMY; + return; + } + + if (!completely_in_regs && (!currFunc || GcurMemmap == statsg)) + { + if (!regalloc_dry_run) + wassertl_bt (0, "Stack asmop outside of function."); + cost (180, 180); + } + } + + if (completely_in_regs) + aop->type = AOP_REG; + else if (completely_on_stack) + aop->type = AOP_STK; + + return; + } +} + +static void +push (const asmop *op, int offset, int size) +{ + if (size == 1) + { + emit2 ("push", "%s", aopGet (op, offset)); + if (op->type == AOP_LIT) + cost (2, 1); + else if (op->type == AOP_IMMD) + cost (2, 1); + else if (aopInReg (op, offset, A_IDX)) + cost (1, 1); + else if (op->type == AOP_DIR) + cost (3, 1); + else + wassertl_bt (0, "Invalid aop type for size 1 for push"); + } + else if (size == 2) + { + if (aopInReg (op, offset, X_IDX)) + { + emit2 ("pushw", "x"); + cost (1, 2); + } + else if (aopInReg (op, offset, Y_IDX)) + { + emit2 ("pushw", "y"); + cost (2, 2); + } + else + wassertl_bt (0, "Invalid aop type for size 2 for pushw"); + } + else + wassertl_bt (0, "Invalid size for push/pushw"); + + G.stack.pushed += size; + updateCFA (); +} + +static void +pop (const asmop *op, int offset, int size) +{ + if (size == 1) + { + emit2 ("pop", "%s", aopGet (op, offset)); + if (aopInReg (op, offset, A_IDX)) + cost (1, 1); + else if (op->type == AOP_DIR) + cost (3, 1); + else + wassertl_bt (0, "Invalid aop type for size 1 for pop"); + } + else if (size == 2) + { + if (aopInReg (op, offset, X_IDX)) + { + emit2 ("popw", "x"); + cost (1, 2); + } + else if (aopInReg (op, offset, Y_IDX)) + { + emit2 ("popw", "y"); + cost (2, 2); + } + else + wassertl_bt (0, "Invalid aop type for size 2 for popw"); + } + else + wassertl_bt (0, "Invalid size for pop/popw"); + + G.stack.pushed -= size; + updateCFA (); +} + +void swap_to_a(int idx) +{ + switch (idx) + { + case A_IDX: + break; + case XL_IDX: + emit2 ("exg", "a, xl"); + cost (1, 1); + break; + case XH_IDX: + emit3w (A_RLWA, ASMOP_X, 0); + break; + case YL_IDX: + emit2 ("exg", "a, yl"); + cost (1, 1); + break; + case YH_IDX: + emit3w (A_RLWA, ASMOP_Y, 0); + break; + default: + wassert_bt (0); + } +} + +void swap_from_a(int idx) +{ + switch (idx) + { + case A_IDX: + break; + case XL_IDX: + emit2 ("exg", "a, xl"); + cost (1, 1); + break; + case XH_IDX: + emit3w (A_RRWA, ASMOP_X, 0); + break; + case YL_IDX: + emit2 ("exg", "a, yl"); + cost (1, 1); + break; + case YH_IDX: + emit3w (A_RRWA, ASMOP_Y, 0); + break; + default: + wassert_bt (0); + } +} + +/*-----------------------------------------------------------------*/ +/* stackAop - put xl, xh, yl, yh aop on stack */ +/*-----------------------------------------------------------------*/ +static +const asmop *stack_aop (const asmop *aop, int i, int *offset) +{ + asmop *stacked = NULL; + + if (aopRS (aop) && !aopOnStack (aop, i, 1) && !aopInReg (aop, i, A_IDX)) + { + if (aop->aopu.bytes[i].byteu.reg->rIdx == XL_IDX) + { + stacked = ASMOP_X; + *offset = 2; + } + else if (aop->aopu.bytes[i].byteu.reg->rIdx == XH_IDX) + { + stacked = ASMOP_X; + *offset = 1; + } + else if (aop->aopu.bytes[i].byteu.reg->rIdx == YL_IDX) + { + stacked = ASMOP_Y; + *offset = 2; + } + else if (aop->aopu.bytes[i].byteu.reg->rIdx == YH_IDX) + { + stacked = ASMOP_Y; + *offset = 1; + } + else + wassert_bt (0); + push (stacked, 0, 2); + } + + return (stacked); +} + +/*--------------------------------------------------------------------------*/ +/* adjustStack - Adjust the stack pointer by n bytes. */ +/*--------------------------------------------------------------------------*/ +static void +adjustStack (int n, bool a_free, bool x_free, bool y_free) +{ + while (n) + { + // The manual is ambigious (not even documenting if the #byte is signed), but it from experimenting with the hardware it + // seems addw sp, byte has a signed operand, while sub sp, #byte has an unsigned operand, also, in contrast to what the + // manual states, addw sp, #byte only takes 1 cycle. + + // todo: For big n, use addition in X or Y when free. + if (abs (n) > 255 * 2 + (n > 0 || a_free) + (optimize.codeSize ? x_free : 255) && x_free) + { + emit2 ("ldw", "x, sp"); + emit2 (n > 0 ? "addw" : "subw", "x, #%d", abs (n)); + emit2 ("ldw", "sp, x"); + cost (5, 4); + G.stack.pushed -= n; + updateCFA (); + n -= n; + } + else if (abs(n) > 255 * 3 + (n > 0 || a_free) + (optimize.codeSize && x_free) && y_free) + { + emit2 ("ldw", "y, sp"); + emit2 (n > 0 ? "addw" : "subw", "y, #%d", abs (n)); + emit2 ("ldw", "sp, y"); + cost (5, 4); + G.stack.pushed -= n; + updateCFA (); + n -= n; + } + else if (n > 255) + { + emit2 ("addw", "sp, #255"); + cost (2, 1); + G.stack.pushed -= 255; + updateCFA (); + n -= 255; + } + else if (n < -255) + { + emit2 ("sub", "sp, #255"); + cost (2, 1); + G.stack.pushed += 255; + updateCFA (); + n += 255; + } + else if (n == 2 && x_free && optimize.codeSize) + { + pop (ASMOP_X, 0, 2); // 1 Byte, 2 cycles - cheaper than addw sp, #byte when optimizing for code size. + n -= 2; + } + else if (n == 1 && a_free) + { + pop (ASMOP_A, 0, 1); // 1 Byte, 1 cycle - cheaper than addw sp, #byte. + n--; + } + else if (n == -2 && optimize.codeSize) + { + push (ASMOP_X, 0, 2); // 1 Byte, 2 cycles - cheaper than addw sp, #byte when optimizing for code size. + n += 2; + } + else if (n == -1) + { + push (ASMOP_A, 0, 1); // 1 Byte, 1 cycle - cheaper than addw sp, #byte. + n++; + } + else + { + emit2 (n > 0 ? "addw" : "sub", "sp, #%d", abs (n)); + cost (2, 1); + G.stack.pushed -= n; + updateCFA (); + n -= n; + } + } +} + +/*-----------------------------------------------------------------*/ +/* cheapMove - Copy a byte from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +cheapMove (asmop *result, int roffset, asmop *source, int soffset, bool save_a) +{ + bool dummy = (result->type == AOP_DUMMY || source->type == AOP_DUMMY); + + if (aopSame (result, roffset, source, soffset, 1)) + return; + else if (!dummy && (!aopRS (result) || aopInReg (result, roffset, A_IDX) || aopOnStack (result, roffset, 1)) && aopIsLitVal (source, soffset, 1, 0)) + emit3_o (A_CLR, result, roffset, 0, 0); + else if (!dummy && (aopInReg (result, roffset, A_IDX) || aopInReg (source, soffset, A_IDX))) + emit3_o (A_LD, result, roffset, source, soffset); + else if (result->type == AOP_DIR && (source->type == AOP_DIR || source->type == AOP_LIT)) + emit3_o (A_MOV, result, roffset, source, soffset); + else if (aopRS (result) && !aopOnStack (result, roffset, 1) && save_a) + { + if (!aopInReg (result, roffset, A_IDX)) + swap_to_a (result->aopu.bytes[roffset].byteu.reg->rIdx); + + // Some special cases where swap_to_a() changed the location of the source operand. + if (aopInReg (result, roffset, XH_IDX) && aopInReg (source, soffset, XL_IDX)) + emit3_o (A_LD, ASMOP_A, 0, ASMOP_X, 1); + else if (aopInReg (result, roffset, YH_IDX) && aopInReg (source, soffset, YL_IDX)) + emit3_o (A_LD, ASMOP_A, 0, ASMOP_Y, 1); + else + cheapMove (ASMOP_A, 0, source, soffset, FALSE); + + if (!aopInReg (result, roffset, A_IDX)) + swap_from_a (result->aopu.bytes[roffset].byteu.reg->rIdx); + } + else + { + if (save_a) + push (ASMOP_A, 0, 1); + if (!aopInReg (source, soffset, A_IDX) && source->type != AOP_DUMMY) + cheapMove (ASMOP_A, 0, source, soffset, FALSE); + if (!aopInReg (result, roffset, A_IDX) && result->type != AOP_DUMMY) + emit3_o (A_LD, result, roffset, ASMOP_A, 0); + if (save_a) + pop (ASMOP_A, 0, 1); + } +} + +/*-----------------------------------------------------------------*/ +/* genCopyStack - Copy the value - stack to stack only */ +/*-----------------------------------------------------------------*/ +static void +genCopyStack (asmop *result, int roffset, asmop *source, int soffset, int n, bool *assigned, int *size, bool a_free, bool x_free, bool y_free, bool really_do_it_now) +{ + int i; + bool pushed_x = FALSE; + +#if 0 + D (emit2("; genCopyStack", "%d %d %d", a_free, x_free, y_free)); +#endif + + for (i = 0; i < n;) + { + if (!aopOnStack (result, roffset + i, 1) || !aopOnStack (source, soffset + i, 1)) + { + i++; + continue; + } + + // Same location. + if (!assigned[i] && + result->aopu.bytes[roffset + i].byteu.stk == source->aopu.bytes[soffset + i].byteu.stk) + { + wassert_bt (*size >= 1); + + assigned[i] = TRUE; + (*size)--; + i++; + continue; + } + + // Could transfer two bytes at a time now. + if (i + 1 < n && + !assigned[i] && !assigned[i + 1] && + aopOnStackNotExt (result, roffset + i, 2) && aopOnStackNotExt (source, soffset + i, 2)) + { + wassert_bt (*size >= 2); + + // Using ldw results in substancially shorter, but somewhat slower code. + if (!x_free && !y_free && really_do_it_now && (optimize.codeSize || !a_free && !optimize.codeSpeed)) + { + push (ASMOP_X, 0, 2); + pushed_x = TRUE; + x_free = TRUE; + } + + if (y_free) // Unlike with other operations, loading between y and stk is as efficient as for x, so we try y first here. + { + emit2 ("ldw", "y, %s", aopGet2 (source, soffset + i)); + emit2 ("ldw", "%s, y", aopGet2 (result, roffset + i)); + } + else if (x_free) + { + emit2 ("ldw", "x, %s", aopGet2 (source, soffset + i)); + emit2 ("ldw", "%s, x", aopGet2 (result, roffset + i)); + } + else + { + i++; + continue; + } + cost (4, 4); + assigned[i] = TRUE; + assigned[i + 1] = TRUE; + (*size) -= 2; + i += 2; + } + else + i++; + } + + for (i = 0; i < n; i++) + { + if (!aopOnStack (result, roffset + i, 1) || !aopOnStack (source, soffset + i, 1)) + continue; + + // Just one byte to transfer. + if ((a_free || really_do_it_now) && !assigned[i] && + (i + 1 >= n || assigned[i + 1] || really_do_it_now)) + { + wassert_bt (*size >= 1); + cheapMove (result, roffset + i, source, soffset + i, !a_free); + assigned[i] = TRUE; + (*size)--; + } + } + + if (pushed_x) + pop (ASMOP_X, 0, 2); + + wassertl_bt (*size >= 0, "genCopyStack() copied more than there is to be copied."); +} + +/*-----------------------------------------------------------------*/ +/* genCopy - Copy the value from one reg/stk asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genCopy (asmop *result, int roffset, asmop *source, int soffset, int sizex, bool a_dead, bool x_dead, bool y_dead) +{ + int i, regsize, size, n = (sizex < source->size - soffset) ? sizex : (source->size - soffset); + bool assigned[8] = {false, false, false, false, false, false, false, false}; + bool a_free, x_free, y_free, xl_dead, xh_dead , yl_dead, yh_dead; + +#if 0 + D (emit2("; genCopy", "%d %d %d", a_dead, x_dead, y_dead)); +#endif + + wassertl_bt (n <= 8, "Invalid size for genCopy()."); + wassertl_bt (aopRS (source), "Invalid source type."); + wassertl_bt (aopRS (result), "Invalid result type."); + + size = n; + for (i = 0, regsize = 0; i < n; i++) + regsize += source->aopu.bytes[soffset + i].in_reg; + + a_dead |= (result->regs[A_IDX] >= 0 && result->regs[A_IDX] < n); + xl_dead = x_dead || (result->regs[XL_IDX] >= roffset && result->regs[XL_IDX] < roffset + n); + xh_dead = x_dead || (result->regs[XH_IDX] >= roffset && result->regs[XH_IDX] < roffset + n); + yl_dead = y_dead || (result->regs[YL_IDX] >= roffset && result->regs[YL_IDX] < roffset + n); + yh_dead = y_dead || (result->regs[YH_IDX] >= roffset && result->regs[YH_IDX] < roffset + n); + x_dead |= (xl_dead && xh_dead); + y_dead |= (yl_dead && yh_dead); + + // Do nothing for coalesced bytes. + for (i = 0; i < n; i++) + if (result->aopu.bytes[roffset + i].in_reg && source->aopu.bytes[soffset + i].in_reg && result->aopu.bytes[roffset + i].byteu.reg == source->aopu.bytes[soffset + i].byteu.reg) + { + assigned[i] = true; + regsize--; + size--; + } + + // Clear registers now that would be more expensive to clear later. + if(n >= 1 && !assigned[n - 1] && sizex > n && !assigned[n] && (aopInReg (result, roffset + n - 1, X_IDX) || aopInReg (result, roffset + n - 1, Y_IDX)) && // We want to clear the high byte of x or y. + size - regsize <= 1) // We won't need x or y for stack-to-stack copies. + { + const bool in_y = aopInReg (result, roffset + n - 1, Y_IDX); + const bool yl_free = source->regs[YL_IDX] < soffset || assigned[source->regs[YL_IDX] - soffset]; + const bool yh_free = source->regs[YH_IDX] < soffset || assigned[source->regs[YH_IDX] - soffset]; + const bool xl_free = source->regs[XL_IDX] < soffset || assigned[source->regs[XL_IDX] - soffset]; + const bool xh_free = source->regs[XH_IDX] < soffset || assigned[source->regs[XH_IDX] - soffset]; + const bool y_free = yl_free && yh_free; + const bool x_free = xl_free && xh_free; + + if (in_y ? y_free : x_free) + { + emit3w_o (A_CLRW, result, roffset + n - 1, 0, 0); + assigned[n] = true; + } + } + + // Handle stack locations that would be overwritten by data from registers + if (result->type == AOP_STK || result->type == AOP_REGSTK) + for (i = 0; i < n; i++) + { + if (assigned[i] || !aopOnStack (source, soffset + i, 1)) + continue; + for (int j = i + 1; j < n; j++) + { + if (!source->aopu.bytes[soffset + j].in_reg) + continue; + if (!aopOnStack (result, roffset + j, 1)) + continue; + if (result->aopu.bytes[roffset + j].byteu.stk != source->aopu.bytes[soffset + i].byteu.stk) + continue; + + cheapMove (result, roffset + i, source, soffset + i, false); + assigned[i] = true; + size--; + } + } + + // Move everything from registers to the stack. + for (i = 0; i < n;) + { + if (i < n - 1 && (aopInReg (source, soffset + i, X_IDX) || aopInReg (source, soffset + i, Y_IDX)) && aopOnStack (result, roffset + i, 2)) + { + wassert_bt (size >= 2); + + emit2 ("ldw", aopInReg (source, soffset + i, X_IDX) ? "%s, x" : "%s, y", aopGet2 (result, roffset + i)); + cost (2, 2); + assigned[i] = TRUE; + assigned[i + 1] = TRUE; + regsize -= 2; + size -= 2; + i += 2; + } + else if (aopRS (source) && !aopOnStack (source, soffset + i, 1) && aopOnStack (result, roffset + i, 1)) + { + wassert_bt (size >= 1); + + if (!aopInReg (source, soffset + i, A_IDX)) + swap_to_a (source->aopu.bytes[soffset + i].byteu.reg->rIdx); + emit3_o (A_LD, result, roffset + i, ASMOP_A, 0); + if (!aopInReg (source, soffset + i, A_IDX)) + swap_from_a (source->aopu.bytes[soffset + i].byteu.reg->rIdx); + assigned[i] = TRUE; + regsize--; + size--; + i++; + } + else // This byte is not a register-to-stack copy. + i++; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have. + a_free = a_dead; + x_free = x_dead; + y_free = y_dead; + for (i = 0; i < n; i++) + { + asmop *operand; + int offset; + + if (!assigned[i]) + { + operand = source; + offset = soffset + i; + } + else + { + operand = result; + offset = roffset + i; + } + + if (aopInReg (operand, offset, A_IDX)) + a_free = FALSE; + else if (aopInReg (operand, offset, XL_IDX) || aopInReg (operand, offset, XH_IDX)) + x_free = FALSE; + else if (aopInReg (operand, offset, YL_IDX) || aopInReg (operand, offset, YH_IDX)) + y_free = FALSE; + } + genCopyStack (result, roffset, source, soffset, n, assigned, &size, a_free, x_free, y_free, false); + + // Now do the register shuffling. + + // Try to use exgw x, y. + if (regsize >= 3) + { + int ex[4] = {-2, -2, -2, -2}; + + // Find XL and check that it is exchanged with YL, find XH and check that it is exchanged with YH. + for (i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, XL_IDX) && aopInReg (source, soffset + i, YL_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, YL_IDX) && aopInReg (source, soffset + i, XL_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, XH_IDX) && aopInReg (source, soffset + i, YH_IDX)) + ex[2] = i; + if (!assigned[i] && aopInReg (result, roffset + i, YH_IDX) && aopInReg (source, soffset + i, XH_IDX)) + ex[3] = i; + } + + int exsum = (ex[0] >= 0) + (ex[1] >= 0) + (ex[2] >= 0) + (ex[3] >= 0); + + if (exsum == 4) + { + emit2 ("exgw", "x, y"); + cost (1, 1); + if(ex[0] >= 0) + assigned[ex[0]] = TRUE; + if(ex[1] >= 0) + assigned[ex[1]] = TRUE; + if(ex[2] >= 0) + assigned[ex[2]] = TRUE; + if(ex[3] >= 0) + assigned[ex[3]] = TRUE; + regsize -= exsum; + size -= exsum; + } + } + + // Try to use rlwa x. + if (regsize >= 3) + { + int ex[3] = {-1, -1, -1}; + + for (i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, XL_IDX) && aopInReg (source, soffset + i, A_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, XH_IDX) && aopInReg (source, soffset + i, XL_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, A_IDX) && aopInReg (source, soffset + i, XH_IDX)) + ex[2] = i; + } + if (ex[0] >= 0 && ex[1] >= 0 && ex[2] >= 0) + { + emit3w (A_RLWA, ASMOP_X, 0); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + assigned[ex[2]] = TRUE; + regsize -= 3; + size -= 3; + } + } + + // Try to use rrwa x. + if (regsize >= 3) + { + int ex[3] = {-1, -1, -1}; + + for (i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, XL_IDX) && aopInReg (source, soffset + i, XH_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, XH_IDX) && aopInReg (source, soffset + i, A_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, A_IDX) && aopInReg (source, soffset + i, XL_IDX)) + ex[2] = i; + } + if (ex[0] >= 0 && ex[1] >= 0 && ex[2] >= 0) + { + emit3w (A_RRWA, ASMOP_X, 0); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + assigned[ex[2]] = TRUE; + regsize -= 3; + size -= 3; + } + } + + // Try to use rlwa y. + if (regsize >= 3) + { + int ex[3] = {-1, -1, -1}; + + for (i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, YL_IDX) && aopInReg (source, soffset + i, A_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, YH_IDX) && aopInReg (source, soffset + i, YL_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, A_IDX) && aopInReg (source, soffset + i, YH_IDX)) + ex[2] = i; + } + if (ex[0] >= 0 && ex[1] >= 0 && ex[2] >= 0) + { + emit3w (A_RLWA, ASMOP_Y, 0); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + assigned[ex[2]] = TRUE; + regsize -= 3; + size -= 3; + } + } + + // Try to use rrwa y. + if (regsize >= 3) + { + int ex[3] = {-1, -1, -1}; + + for (i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, YL_IDX) && aopInReg (source, soffset + i, YH_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, YH_IDX) && aopInReg (source, soffset + i, A_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, A_IDX) && aopInReg (source, soffset + i, YL_IDX)) + ex[2] = i; + } + if (ex[0] >= 0 && ex[1] >= 0 && ex[2] >= 0) + { + emit3w (A_RRWA, ASMOP_Y, 0); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + assigned[ex[2]] = TRUE; + regsize -= 3; + size -= 3; + } + + } + + // Try to use exg a, xl. + if (regsize >= 2) + { + int ex[2] = {-1, -1}; + + i = result->regs[A_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, XL_IDX)) + ex[0] = i; + i = result->regs[XL_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, A_IDX)) + ex[1] = i; + + if (ex[0] >= 0 && ex[1] >= 0) + { + emit2 ("exg", "a, xl"); + cost (1, 1); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + regsize -= 2; + size -= 2; + } + } + + // Try to use exg a, yl. + if (regsize >= 2) + { + int ex[2] = {-1, -1}; + + i = result->regs[A_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, YL_IDX)) + ex[0] = i; + i = result->regs[YL_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, A_IDX)) + ex[1] = i; + + if (ex[0] >= 0 && ex[1] >= 0) + { + emit2 ("exg", "a, yl"); + cost (1, 1); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + regsize -= 2; + size -= 2; + } + } + + // Try to use swapw x. + if (regsize >= 2) + { + int ex[2] = {-1, -1}; + + i = result->regs[XL_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, XH_IDX)) + ex[0] = i; + i = result->regs[XH_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, XL_IDX)) + ex[1] = i; + + if (ex[0] >= 0 && ex[1] >= 0) + { + emit2 ("swapw", "x"); + cost (1, 1); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + regsize -= 2; + size -= 2; + } + } + + // Try to use swapw y. + if (regsize >= 2) + { + int ex[2] = {-1, -1}; + + i = result->regs[YL_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, YH_IDX)) + ex[0] = i; + i = result->regs[YH_IDX] - roffset; + if (i > 0 && i < n && !assigned[i] && aopInReg (source, soffset + i, YL_IDX)) + ex[1] = i; + + if (ex[0] >= 0 && ex[1] >= 0) + { + emit2 ("swapw", "y"); + cost (2, 1); + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + regsize -= 2; + size -= 2; + } + } + + // Try to use ldw x, y + { + const int il = result->regs[XL_IDX] - roffset; + const int ih = result->regs[XH_IDX] - roffset; + const bool assign_l = (il >= 0 && il < n && !assigned[il] && aopInReg (source, soffset + il, YL_IDX)); + const bool assign_h = (ih >= 0 && ih < n && !assigned[ih] && aopInReg (source, soffset + ih, YH_IDX)); + if (source->regs[XL_IDX] < 0 && source->regs[XH_IDX] < 0 && + (assign_l && assign_h || assign_l && xh_dead && ih < 0 || assign_h && xl_dead && il < 0)) + { + emit2 ("ldw", "x, y"); + cost (1, 1); + if (assign_l) + { + assigned[il] = TRUE; + regsize--; + size--; + } + if (assign_h) + { + assigned[ih] = TRUE; + regsize--; + size--; + } + } + } + + // Try to use ldw y, x + { + const int il = result->regs[YL_IDX] - roffset; + const int ih = result->regs[YH_IDX] - roffset; + const bool assign_l = (il >= 0 && il < n && !assigned[il] && aopInReg (source, soffset + il, XL_IDX)); + const bool assign_h = (ih >= 0 && ih < n && !assigned[ih] && aopInReg (source, soffset + ih, XH_IDX)); + if (source->regs[YL_IDX] < 0 && source->regs[YH_IDX] < 0 && + (assign_l && assign_h || assign_l && yh_dead && ih < 0 || assign_h && yl_dead && il < 0)) + { + if(x_dead && assign_l && assign_h) + { + emit2 ("exgw", "x, y"); + cost (1, 1); + } + else + { + emit2 ("ldw", "y, x"); + cost (2, 1); + } + if (assign_l) + { + assigned[il] = TRUE; + regsize--; + size--; + } + if (assign_h) + { + assigned[ih] = TRUE; + regsize--; + size--; + } + } + } + + // Clear registers now that would be more expensive to clear later. + if(n >= 1 && !assigned[n - 1] && sizex > n && !assigned[n] && (aopInReg (result, roffset + n - 1, X_IDX) || aopInReg (result, roffset + n - 1, Y_IDX)) && // We want to clear the high byte of x or y. + size - regsize <= 1) // We won't need x or y for stack-to-stack copies. + { + const bool in_y = aopInReg (result, roffset + n - 1, Y_IDX); + const bool yl_free = source->regs[YL_IDX] < soffset || assigned[source->regs[YL_IDX] - soffset]; + const bool yh_free = source->regs[YH_IDX] < soffset || assigned[source->regs[YH_IDX] - soffset]; + const bool xl_free = source->regs[XL_IDX] < soffset || assigned[source->regs[XL_IDX] - soffset]; + const bool xh_free = source->regs[XH_IDX] < soffset || assigned[source->regs[XH_IDX] - soffset]; + const bool y_free = yl_free && yh_free; + const bool x_free = xl_free && xh_free; + + if (in_y ? y_free : x_free) + { + emit3w_o (A_CLRW, result, roffset + n - 1, 0, 0); + assigned[n] = TRUE; + } + } + + while (regsize) + { + // Find lowest byte that can be assigned and needs to be assigned. + for (i = 0; i < n; i++) + { + int j; + + if (assigned[i] || !source->aopu.bytes[soffset + i].in_reg) + continue; + + for (j = 0; j < n; j++) + { + if (!source->aopu.bytes[soffset + j].in_reg || !result->aopu.bytes[roffset + i].in_reg) + continue; + if (!assigned[j] && i != j && result->aopu.bytes[roffset + i].byteu.reg == source->aopu.bytes[soffset + j].byteu.reg) + goto skip_byte; // We can't write this one without overwriting the source. + } + + break; // Found byte that can be written safely. + +skip_byte: + ; + } + + if (i < n) + { + cheapMove (result, roffset + i, source, soffset + i, TRUE); // We can safely assign a byte. + regsize--; + size--; + assigned[i] = TRUE; + continue; + } + + // No byte can be assigned safely (i.e. the assignment is a permutation). + if (!regalloc_dry_run) + wassertl_bt (0, "Unimplemented."); + cost (180, 180); + return; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have now. + a_free = a_dead; + x_free = x_dead; + y_free = y_dead; + for (i = 0; i < n; i++) + { + if (!assigned[i]) + continue; + if (aopInReg (result, roffset + i, A_IDX)) + a_free = FALSE; + else if (aopInReg (result, roffset + i, XL_IDX) || aopInReg (result, roffset + i, XH_IDX)) + x_free = FALSE; + else if (aopInReg (result, roffset + i, YL_IDX) || aopInReg (result, roffset + i, YH_IDX)) + y_free = FALSE; + } + genCopyStack (result, roffset, source, soffset, n, assigned, &size, a_free, x_free, y_free, FALSE); + + // Last, move everything from stack to registers. + for (i = 0; i < n;) + { + if (i < n - 1 && (aopInReg (result, roffset + i, X_IDX) || aopInReg (result, roffset + i, Y_IDX)) && aopOnStackNotExt (source, soffset + i, 2)) + { + wassert_bt (size >= 2); + emit2 ("ldw", aopInReg (result, roffset + i, X_IDX) ? "x, %s" : "y, %s", aopGet2 (source, soffset + i)); + cost (2, 2); + assigned[i] = TRUE; + assigned[i + 1] = TRUE; + if (aopInReg (result, roffset + i, X_IDX)) + x_free = FALSE; + size -= 2; + i += 2; + } + else if (i < n - 1 && aopInReg (result, roffset + i, X_IDX) && aopOnStack (source, soffset + i, 2)) + { + long int eoffset = (long int)(source->aopu.bytes[soffset + i + 1].byteu.stk) + G.stack.size - 256l; + wassertl_bt (regalloc_dry_run || stm8_extend_stack, "Extended stack access, but y not prepared for extended stack access."); + wassertl_bt (regalloc_dry_run || eoffset >= 0l && eoffset <= 0xffffl, "Stack access out of extended stack range."); // Stack > 64K. + + emit2 ("ldw", "x, y"); + cost (1, 1); + emit2 ("ldw", "x, (0x%x, x)", (unsigned)eoffset); + cost (2 + (eoffset > 255), 2); + x_free = FALSE; + size -= 2; + i += 2; + } + // todo: Try to use ldw to load xl, xh, yl, yh when the other half is not in use. + else if (aopRS (result) && !aopOnStack (result, roffset + i, 1) && aopOnStack (source, soffset + i, 1)) + { + wassert_bt (size >= 1); + cheapMove (result, roffset + i, source, soffset + i, !a_free); + assigned[i] = TRUE; + if (aopInReg (result, roffset + i, A_IDX)) + a_free = FALSE; + if (aopInReg (result, roffset + i, XL_IDX) || aopInReg (result, roffset + i, XH_IDX)) + x_free = FALSE; + size--; + i++; + } + else // This byte is not a stack-to-register copy. + i++; + } + + // Free a reg to copy (stack-to-stack) whatever is left. + if (size) + { + a_free = a_dead && (result->regs[A_IDX] < 0 || result->regs[A_IDX] >= roffset + source->size); + if (!a_free) + push (ASMOP_A, 0, 1); + genCopyStack (result, roffset, source, soffset, n, assigned, &size, TRUE, x_free, y_free, TRUE); + if (!a_free) + pop (ASMOP_A, 0, 1); + } + + wassertl_bt (size >= 0, "genCopy() copied more than there is to be copied."); + + a_free = a_dead && (result->regs[A_IDX] < 0 || result->regs[A_IDX] >= roffset + source->size); + + // Place leading zeroes. + for (i = source->size - soffset; i < sizex;) + { + if (assigned[i]) + { + i++; + continue; + } + else if (i + 1 < sizex && !assigned[i + 1] && (aopInReg (result, roffset + i, X_IDX) || aopInReg (result, roffset + i, Y_IDX))) + { + if (aopInReg (result, roffset + i, X_IDX)) + emit3w (A_CLRW, ASMOP_X, 0); + else if (aopInReg (result, roffset + i, Y_IDX)) + emit3w (A_CLRW, ASMOP_Y, 0); + i += 2; + } + else if (y_dead && aopIsLitVal (source, soffset + i + 1, 1, 0x00) && + (aopInReg (result, roffset + i, YL_IDX) && result->regs[YH_IDX] < 0 || aopInReg (result, roffset + i, YH_IDX) && result->regs[YL_IDX] < 0)) + { + emit3w (A_CLRW, ASMOP_Y, 0); + i++; + } + else + { + cheapMove (result, roffset + i, ASMOP_ZERO, 0, !a_free); + assigned[i] = TRUE; + if (aopInReg (result, roffset + i, A_IDX)) + a_free = FALSE; + i++; + } + } + + if (size) + { + if (!regalloc_dry_run) + { + wassertl_bt (0, "genCopy failed to completely copy operands."); + fprintf (stderr, "%d bytes left.\n", size); + fprintf (stderr, "left type %d source type %d\n", result->type, source->type); + for (i = 0; i < n ; i++) + fprintf (stderr, "Byte %d, result in reg %d, source in reg %d. %s assigned.\n", i, result->aopu.bytes[roffset + i].in_reg ? result->aopu.bytes[roffset + i].byteu.reg->rIdx : -1, source->aopu.bytes[soffset + i].in_reg ? source->aopu.bytes[soffset + i].byteu.reg->rIdx : -1, assigned[i] ? "" : "not"); + } + cost (180, 180); + } +} + +/*-----------------------------------------------------------------*/ +/* genMove_o - Copy part of one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove_o (asmop *result, int roffset, asmop *source, int soffset, int size, bool a_dead_global, bool x_dead_global, bool y_dead_global) +{ + int i; + + bool clr_x = FALSE, clr_y = FALSE; + + wassertl_bt (result->type != AOP_LIT, "Trying to write to literal."); + wassertl_bt (result->type != AOP_IMMD, "Trying to write to immediate."); + wassertl_bt (roffset + size <= result->size, "Trying to write beyond end of operand"); + +#if 0 + D (emit2("; genMove_o", "offset %d %d, size %d, deadness %d %d %d", roffset, soffset, size, a_dead_global, x_dead_global, y_dead_global)); +#endif + + if (aopRS (result) && aopRS (source)) + { + genCopy (result, roffset, source, soffset, size, a_dead_global, x_dead_global, y_dead_global); + return; + } + + if (result->type == AOP_DIR && source->type == AOP_DIR && roffset == soffset && !strcmp(result->aopu.aop_dir, source->aopu.aop_dir)) + return; + + for (i = 0; i < size;) + { + const bool x_dead = x_dead_global && + (!aopRS (result) || (result->regs[XL_IDX] >= (roffset + i) || result->regs[XL_IDX] < 0) && (result->regs[XH_IDX] >= (roffset + i) || result->regs[XH_IDX] < 0)) && + (!aopRS (source) || source->regs[XL_IDX] <= i + 1 && source->regs[XH_IDX] <= i + 1); + const bool y_dead = y_dead_global && + (!aopRS (result) || (result->regs[YL_IDX] >= (roffset + i) || result->regs[YL_IDX] < 0) && (result->regs[YH_IDX] >= (roffset + i) || result->regs[YH_IDX] < 0)) && + (!aopRS (source) || source->regs[YL_IDX] <= i + 1 && source->regs[YH_IDX] <= i + 1); + const bool a_dead = a_dead_global && + (!aopRS (result) || (result->regs[A_IDX] >= (roffset + i) || result->regs[A_IDX] < 0)) && + (!aopRS (source) || source->regs[A_IDX] <= i); + + if (i + 1 < size && (aopInReg (result, roffset + i, X_IDX) || aopInReg (result, roffset + i, Y_IDX)) && aopIsLitVal (source, soffset + i, 2, 0x0000)) + { + if (aopInReg (result, roffset + i, X_IDX) && !clr_x) + { + emit3w (A_CLRW, ASMOP_X, 0); + clr_x = TRUE; + } + else if (aopInReg (result, roffset + i, Y_IDX) && !clr_y) + { + emit3w (A_CLRW, ASMOP_Y, 0); + clr_y = TRUE; + } + i += 2; + } + else if (i + 1 < size && i >= 2 && source->type == AOP_LIT && aopIsLitVal (source, soffset + i, 2, byteOfVal (source->aopu.aop_lit, soffset + i - 2) + byteOfVal (source->aopu.aop_lit, soffset + i - 1) * 256) && + (aopInReg (result, roffset + i, X_IDX) && aopInReg (result, roffset + i - 2, Y_IDX) || aopInReg (result, roffset + i, Y_IDX) && aopInReg (result, roffset + i - 2, X_IDX))) + { + emit2 ("ldw", "%s, %s", aopGet2 (result, roffset + i), aopGet2 (result, roffset + i - 2)); + cost (1 + aopInReg (result, roffset + i, Y_IDX), 1); + i += 2; + } + else if (i + 1 < size && aopInReg (result, roffset + i, X_IDX) && (aopIsLitVal (source, soffset + i, 2, 0x0001) || aopIsLitVal (source, soffset + i, 2, 0xffff))) + { + bool dec = aopIsLitVal (source, soffset + i, 2, 0xffff); + emit3w (A_CLRW, ASMOP_X, 0); + emit3w (dec ? A_DECW : A_INCW, ASMOP_X, 0); + i += 2; + } + else if (i + 1 < size && aopInReg (result, roffset + i, X_IDX) && + (source->type == AOP_LIT || source->type == AOP_DIR && soffset + i + 1 < source->size || source->type == AOP_IMMD)) + { + emit2 ("ldw", "x, %s", aopGet2 (source, soffset + i)); + cost (3, 2); + clr_x = FALSE; + i += 2; + } + else if (i + 1 < size && aopInReg (result, roffset + i, Y_IDX) && + (source->type == AOP_LIT || source->type == AOP_DIR && soffset + i + 1 < source->size || source->type == AOP_IMMD)) + { + emit2 ("ldw", "y, %s", aopGet2 (source, soffset + i)); + cost (4, 2); + clr_y = FALSE; + i += 2; + } + else if (i + 1 < size && result->type == AOP_DIR && aopInReg (source, soffset + i, X_IDX)) + { + emit2 ("ldw", "%s, x", aopGet2 (result, roffset + i)); + cost (3, 2); + i += 2; + } + else if (i + 1 < size && result->type == AOP_DIR && aopInReg (source, soffset + i, Y_IDX)) + { + emit2 ("ldw", "%s, y", aopGet2 (result, roffset + i)); + cost (4, 2); + i += 2; + } + else if (x_dead && i + 1 < size && + (aopOnStack (result, roffset + i, 2) || result->type == AOP_DIR) && + (aopOnStackNotExt (source, soffset + i, 2) || source->type == AOP_LIT || source->type == AOP_DIR && soffset + i + 1 < source->size || source->type == AOP_IMMD)) + { + if (aopIsLitVal (source, soffset + i, 2, 0x0000)) + { + if (!clr_x) + emit3w (A_CLRW, ASMOP_X, 0); + clr_x = TRUE; + } + else + { + emit2 ("ldw", "x, %s", aopGet2 (source, soffset + i)); + cost (3, 2); + clr_x = FALSE; + } + emit2 ("ldw", "%s, x", aopGet2 (result, roffset + i)); + cost (2, 2); + i += 2; + } + else if (i + 1 < size && aopIsLitVal (source, soffset + i + 1, 1, 0x00) && (aopInReg (result, roffset + i, X_IDX) || aopInReg (result, roffset + i, Y_IDX))) + { + emit3w_o (A_CLRW, result, roffset + i, 0, 0); + cheapMove (result, roffset + i, source, soffset + i, !a_dead); + i += 2; + } + else if ((!aopRS (result) || aopOnStack(result, roffset + i, 1) || aopInReg (result, roffset + i, A_IDX)) && aopIsLitVal (source, soffset + i, 1, 0x00)) + { + emit3_o (A_CLR, result, roffset + i, 0, 0); + i++; + } + else if (y_dead && aopOnStack (result, roffset + i, 2) && + (source->type == AOP_LIT || source->type == AOP_DIR && soffset + i + 1 < source->size || source->type == AOP_IMMD)) + { + if (aopIsLitVal (source, soffset + i, 2, 0x0000)) + { + if (!clr_y) + emit3w (A_CLRW, ASMOP_Y, 0); + clr_y = TRUE; + } + else + { + emit2 ("ldw", "y, %s", aopGet2 (source, soffset + i)); + cost (4, 2); + clr_y = FALSE; + } + emit2 ("ldw", "%s, y", aopGet2 (result, roffset + i)); + cost (2, 2); + i += 2; + } + else if (y_dead && i + 1 < size && aopOnStack (source, soffset + i, 2) && source->type == AOP_DIR) + { + emit2 ("ldw", "y, %s", aopGet2 (source, soffset + i)); + emit2 ("ldw", "%s, y", aopGet2 (result, roffset + i)); + cost (6, 4); + clr_y = FALSE; + i += 2; + } + else + { + cheapMove (result, roffset + i, source, soffset + i, !a_dead); + i++; + } + } +} + +/*-----------------------------------------------------------------*/ +/* genMove - Copy the value from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove (asmop *result, asmop *source, bool a_dead, bool x_dead, bool y_dead) +{ + genMove_o (result, 0, source, 0, result->size, a_dead, x_dead, y_dead); +} + +/*---------------------------------------------------------------------*/ +/* stm8_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*---------------------------------------------------------------------*/ +void +stm8_emitDebuggerSymbol (const char *debugSym) +{ + G.debugLine = 1; + emit2 ("", "%s ==.", debugSym); + G.debugLine = 0; +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = + { + 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx; + return -1; +} + +/*-----------------------------------------------------------------*/ +/* genNot - generates code for ! */ +/*-----------------------------------------------------------------*/ +static void +genNot (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + int i; + int pushed_a = false; + + D (emit2 ("; genNot", "")); + + aopOp (left, ic); + aopOp (result, ic); + + for (i = 1; i < left->aop->size; i++) + if (aopInReg (left->aop, i, A_IDX)) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + break; + } + + if (!regDead (A_IDX, ic) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + for (i = 0; i < left->aop->size;) + { + if (i == 0 && !IS_FLOAT (operandType (left)) && + (aopInReg (left->aop, i, X_IDX) || aopInReg (left->aop, i, Y_IDX) && regDead (Y_IDX, ic))) + { + if (aopInReg (left->aop, i, Y_IDX)) + { + emit2 ("subw", "y, #0x0001"); + cost (4, 2); + } + else + { + emit2 ("cpw", "x, #0x0001"); + cost (3, 2); + } + i += 2; + } + else if (i == 0 && i + 1 < left->aop->size && !IS_FLOAT (operandType (left)) && regDead (X_IDX, ic) && + (aopOnStack (left->aop, i, 2) && left->aop->regs[XL_IDX] < 0 && left->aop->regs[XH_IDX] < 0 || left->aop->type == AOP_DIR)) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, true, true, false); + emit2 ("subw", "x, #0x0001"); + cost (3, 2); + i += 2; + } + else + { + if (i && aopInReg (left->aop, i, A_IDX)) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, left->aop, i, false); + if (IS_FLOAT (operandType (left)) && i == left->aop->size - 1) + { + emit2 ("and", "a, #0x7f"); + cost (2, 1); + } + if (!i) + emit3 (A_SUB, ASMOP_A, ASMOP_ONE); + else + emit3 (A_SBC, ASMOP_A, ASMOP_ZERO); + i++; + } + } + + if (result->aop->size == 2 && (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX))) + { + emit3 (A_CLRW, result->aop, 0); + emit3 (A_RLCW, result->aop, 0); + } + else + { + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_RLC, ASMOP_A, 0); + + cheapMove (result->aop, 0, ASMOP_A, 0, false); + + for (i = 1; i < result->aop->size; i++) + cheapMove (result->aop, 0, ASMOP_ZERO, 0, true); + } + + if (pushed_a) + if (!regDead (A_IDX, ic) || result->aop->regs[A_IDX] < 0) + pop (ASMOP_A, 0, 1); + else + adjustStack (1, false, false, false); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + int left_in_a = 0; + bool result_in_a = FALSE; + bool destroyed_a = FALSE; + bool pushed_a = FALSE; + bool result_pushed = FALSE; + int i, size; + + D (emit2 ("; genCpl", "")); + + aopOp (left, ic); + aopOp (result, ic); + + size = result->aop->size; + + for (i = 1; i < left->aop->size; i++) + if (aopInReg (left->aop, i, A_IDX)) + { + left_in_a = i; + break; + } + + for (i = 0; i < size;) + { + // todo: Complement in source where dead and more efficient. + if (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX)) + { + const bool x_free = regDead (X_IDX, ic) && + left->aop->regs[XL_IDX] < i && left->aop->regs[XH_IDX] < i && + (result->aop->regs[XL_IDX] < 0 || result->aop->regs[XL_IDX] >= i) && (result->aop->regs[XH_IDX] < 0 || result->aop->regs[XH_IDX] >= i); + const bool y_free = regDead (Y_IDX, ic) && + left->aop->regs[YL_IDX] < i && left->aop->regs[YH_IDX] < i && + (result->aop->regs[YL_IDX] < 0 || result->aop->regs[YL_IDX] >= i) && (result->aop->regs[YH_IDX] < 0 || result->aop->regs[YH_IDX] >= i); + genMove_o (result->aop, i, left->aop, i, 2, (regDead (A_IDX, ic) || pushed_a) && !result_in_a && !(left_in_a > i), x_free, y_free); + + emit3w_o (A_CPLW, result->aop, i, 0, 0); + + i += 2; + } + else if ((aopOnStack (result->aop, i, 1) || result->aop->type == AOP_DIR) && aopSame (result->aop, i, left->aop, i, 1)) + { + emit3_o (A_CPL, result->aop, i, 0, 0); + i++; + } + else + { + bool pushed_left = destroyed_a && aopInReg (left->aop, i, A_IDX); + + if ((left_in_a > i || !regDead (A_IDX, ic) || result_in_a) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + if (result_in_a) + { + result_in_a = FALSE; + result_pushed = TRUE; + } + } + + if (pushed_left && !regDead (A_IDX, ic)) + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + else if (pushed_left) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + + destroyed_a = TRUE; + + emit3 (A_CPL, ASMOP_A, 0); + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + + if (aopInReg (result->aop, i, A_IDX)) + result_in_a = TRUE; + + i++; + } + } + + if (pushed_a && !regDead (A_IDX, ic) || result_pushed) + pop (ASMOP_A, 0, 1); + else if (pushed_a) + adjustStack (1, FALSE, FALSE, FALSE); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genSub - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genSub (const iCode *ic, asmop *result_aop, asmop *left_aop, asmop *right_aop) +{ + int size, i, j; + bool started; + bool pushed_a = FALSE; + bool result_in_a = FALSE; + + size = result_aop->size; + + for (i = 0, started = FALSE; i < size;) + { + bool a_free = regDead (A_IDX, ic) && left_aop->regs[A_IDX] <= i && right_aop->regs[A_IDX] <= i && !result_in_a || pushed_a; + bool xl_free = regDead (XL_IDX, ic) && (result_aop->regs[XL_IDX] >= i || result_aop->regs[XL_IDX] < 0) && left_aop->regs[XL_IDX] <= i + 1 && right_aop->regs[XL_IDX] < i; + bool xh_free = regDead (XH_IDX, ic) && (result_aop->regs[XH_IDX] >= i || result_aop->regs[XH_IDX] < 0) && left_aop->regs[XH_IDX] <= i + 1 && right_aop->regs[XH_IDX] < i; + bool x_free = xl_free && xh_free; + bool yl_free = regDead (YL_IDX, ic) && (result_aop->regs[YL_IDX] >= i || result_aop->regs[YL_IDX] < 0) && left_aop->regs[YL_IDX] <= i + 1 && right_aop->regs[YL_IDX] < i; + bool yh_free = regDead (YH_IDX, ic) && (result_aop->regs[YH_IDX] >= i || result_aop->regs[YH_IDX] < 0) && left_aop->regs[YH_IDX] <= i + 1 && right_aop->regs[YH_IDX] < i; + bool y_free = yl_free && yh_free; + + if (!started && left_aop->type == AOP_LIT && !byteOfVal (left_aop->aopu.aop_lit, i) && + (!byteOfVal (left_aop->aopu.aop_lit, i + 1) && (aopInReg (result_aop, i, X_IDX) || aopInReg (result_aop, i, Y_IDX)) || + !started && i == size - 1 && (aopInReg (result_aop, i, XL_IDX) && regDead (XH_IDX, ic) && right_aop->regs[XH_IDX] < 0 && result_aop->regs[XH_IDX] < 0 || aopInReg (result_aop, i, YL_IDX) && regDead (YH_IDX, ic) && right_aop->regs[YH_IDX] < 0 && result_aop->regs[YH_IDX] < 0))) + { + bool half = (i == size - 1); + bool x = aopInReg (result_aop, i, half ? XL_IDX : X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, right_aop, i, 2 - half, a_free, x, !x); + emit3w (A_NEGW, x ? ASMOP_X : ASMOP_Y, 0); + started = TRUE; + i += 2; + } + else if (!started && + aopOnStack (result_aop, i, 2) && aopOnStack (right_aop, i, 2) && result_aop->aopu.bytes[i].byteu.stk == right_aop->aopu.bytes[i].byteu.stk && result_aop->aopu.bytes[i + 1].byteu.stk == right_aop->aopu.bytes[i + 1].byteu.stk && + aopIsLitVal (right_aop, i, 2, 0x0000)) + { + emit3w_o (A_NEGW, result_aop, i, 0, 0); + started = TRUE; + i += 2; + } + // We can use incw / decw only for the only, top non-zero word, since it neither takes into account an existing carry nor does it update the carry. + else if (!started && i == size - 2 && + (aopInReg (result_aop, i, X_IDX) || aopInReg (result_aop, i, Y_IDX)) && + right_aop->type == AOP_LIT && !byteOfVal (right_aop->aopu.aop_lit, i + 1) && + byteOfVal (right_aop->aopu.aop_lit, i) <= 1 + aopInReg (result_aop, i, X_IDX) || + !started && i == size - 1 && + !(aopInReg (left_aop, i, A_IDX) && regDead (A_IDX, ic)) && + (aopInReg (result_aop, i, XL_IDX) && regDead (XH_IDX, ic) && left_aop->regs[XH_IDX] < 0 && result_aop->regs[XH_IDX] < 0 || aopInReg (result_aop, i, YL_IDX) && regDead (YH_IDX, ic) && left_aop->regs[YH_IDX] < 0 && result_aop->regs[YH_IDX] < 0) && + right_aop->type == AOP_LIT && byteOfVal (right_aop->aopu.aop_lit, i) <= 1 + aopInReg (result_aop, i, XL_IDX)) + { + bool half = (i == size - 1); + bool x = aopInReg (result_aop, i, half ? XL_IDX : X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, left_aop, i, 2 - half, a_free, x, !x); + for (j = 0; j < byteOfVal (right_aop->aopu.aop_lit, i); j++) + emit3w (A_DECW, x ? ASMOP_X : ASMOP_Y, 0); + cost (x ? 1 : 2, 1); + started = TRUE; + i += 2; + } + // In some cases we gain so much by using decw that it is worth handling the carry explictly. + else if (started && i == size - 2 && (aopInReg (result_aop, i, X_IDX) || aopInReg (result_aop, i, Y_IDX)) && aopIsLitVal (left_aop, i, 2, 0x0000) && + (aopOnStack (right_aop, i, 2) || right_aop->type == AOP_DIR)) + { + bool x = aopInReg (result_aop, i, X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, right_aop, i, 2, a_free, x_free, y_free); + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + if (!regalloc_dry_run) + emit2 ("jrnc", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 1); + emit3w_o (A_INCW, result_aop, i, 0, 0); + emitLabel (tlbl); + emit3w_o (A_NEGW, result_aop, i, 0, 0); + i += 2; + } + else if (!started && + (aopInReg (result_aop, i, X_IDX) || aopInReg (result_aop, i, Y_IDX)) && + (right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD || aopOnStackNotExt (right_aop, i, 2) || right_aop->type == AOP_DIR && i + 1 < right_aop->size)) + { + bool x = aopInReg (result_aop, i, X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, left_aop, i, 2, a_free, x, !x); + if (!aopIsLitVal (right_aop, i, 2, 0x0000)) + { + emit2 ("subw", x ? "x, %s" : "y, %s", aopGet2 (right_aop, i)); + cost ((x || aopOnStack (right_aop, 0, 2)) ? 3 : 4, 2); + started = TRUE; + } + i += 2; + } + else if (!started && i == size - 2 && aopInReg (right_aop, i, X_IDX) && aopInReg (result_aop, i, X_IDX) && + (left_aop->type == AOP_DIR || aopOnStackNotExt (left_aop, i, 2))) + { + emit3w (A_NEGW, ASMOP_X, 0); + emit2 ("addw", "x, %s", aopGet2 (left_aop, i)); + cost (4, 2); + started = TRUE; + i += 2; + } + else if (!started && aopIsLitVal (left_aop, i, 1, 0x00) && + (aopOnStack (result_aop, i, 1) || result_aop->type == AOP_DIR) && aopSame (result_aop, i, right_aop, i, 1)) + { + emit3_o (A_NEG, result_aop, i, 0, 0); + started = TRUE; + i++; + } + else if (!started && i == size - 1 && + (aopOnStack (result_aop, i, 1) || result_aop->type == AOP_DIR) && aopSame (result_aop, i, left_aop, i, 1) && + right_aop->type == AOP_LIT && byteOfVal (right_aop->aopu.aop_lit, i) <= 2 + !a_free) + { + for (j = 0; j < byteOfVal (right_aop->aopu.aop_lit, i); j++) + emit3_o (A_DEC, result_aop, i, 0, 0); + i++; + } + else if (!started && i == size - 1 && + (aopOnStack (result_aop, i, 1) || result_aop->type == AOP_DIR) && aopSame (result_aop, i, left_aop, i, 1) && + right_aop->type == AOP_LIT && byteOfVal (right_aop->aopu.aop_lit, i) >= 254 - !a_free) + { + for (j = byteOfVal (right_aop->aopu.aop_lit, i); j < 256; j++) + emit3_o (A_INC, result_aop, i, 0, 0); + i++; + } + else if (!started && i + 1 < size && (x_free || aopInReg(left_aop, i, X_IDX) && regDead (X_IDX, ic)) && + (aopOnStackNotExt (right_aop, i, 2) || right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD || right_aop->type == AOP_DIR && i + 1 < right_aop->size) && + ((aopOnStack (result_aop, i, 2) || result_aop->type == AOP_DIR) && (aopRS (left_aop) && !aopInReg(left_aop, i, A_IDX) && !aopInReg(left_aop, i + 1, A_IDX) || left_aop->type == AOP_DIR) || + aopInReg(left_aop, i, X_IDX) && aopInReg(result_aop, i, Y_IDX) || + aopInReg(left_aop, i, X_IDX) && (result_aop->regs[XL_IDX] < 0 || result_aop->regs[XL_IDX] >= i) && (result_aop->regs[XH_IDX] < 0 || result_aop->regs[XH_IDX] >= i) && (aopInReg(left_aop, i, XL_IDX) || aopInReg(left_aop, i + 1, XH_IDX) || aopInReg(left_aop, i, XH_IDX) && aopInReg(left_aop, i + 1, XL_IDX)))) + { + genMove_o (ASMOP_X, 0, left_aop, i, 2, a_free, TRUE, FALSE); + if (i == size - 2 && right_aop->type == AOP_LIT && byteOfVal (right_aop->aopu.aop_lit, i) <= 2 && !byteOfVal (right_aop->aopu.aop_lit, i + 1)) + for (j = 0; j < byteOfVal (right_aop->aopu.aop_lit, i); j++) + emit3w (A_DECW, ASMOP_X, 0); + else + { + emit2 ("subw", "x, %s", aopGet2 (right_aop, i)); + cost (3 + (right_aop->type == AOP_DIR), 2); + } + genMove_o (result_aop, i, ASMOP_X, 0, 2, a_free, TRUE, FALSE); + if (aopInReg (result_aop, i, A_IDX) || aopInReg (result_aop, i + 1, A_IDX)) + result_in_a = TRUE; + started = TRUE; + i += 2; + } + else if (!started && aopIsLitVal (left_aop, i, 1, 0x00)) + { + if (!a_free) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + result_in_a = FALSE; + } + + cheapMove (ASMOP_A, 0, right_aop, i, FALSE); + emit3 (A_NEG, ASMOP_A, 0); + cheapMove (result_aop, i, ASMOP_A, 0, FALSE); + + started = TRUE; + + if (aopInReg (result_aop, i, A_IDX)) + result_in_a = TRUE; + + i++; + } + else if (!started && i + 1 < size && (y_free || aopInReg(left_aop, i, Y_IDX) && regDead (Y_IDX, ic)) && + (aopOnStack (result_aop, i, 2) || result_aop->type == AOP_DIR) && + (aopOnStack (left_aop, i, 2) || aopInReg(left_aop, i, Y_IDX) || left_aop->type == AOP_DIR) && + (aopOnStackNotExt (right_aop, i, 2) || right_aop->type == AOP_LIT || right_aop->type == AOP_IMMD || right_aop->type == AOP_DIR && i + 1 < right_aop->size)) + { + genMove_o (ASMOP_Y, 0, left_aop, i, 2, a_free, TRUE, FALSE); + if (i == size - 2 && right_aop->type == AOP_LIT && byteOfVal (right_aop->aopu.aop_lit, i) <= 2 && !byteOfVal (right_aop->aopu.aop_lit, i + 1)) + for (j = 0; j < byteOfVal (right_aop->aopu.aop_lit, i); j++) + emit3w (A_DECW, ASMOP_Y, 0); + else + { + emit2 ("subw", "y, %s", aopGet2 (right_aop, i)); + cost (4 - aopOnStack (right_aop, i, 2), 2); + } + genMove_o (result_aop, i, ASMOP_Y, 0, 2, a_free, TRUE, FALSE); + if (aopInReg (result_aop, i, A_IDX) || aopInReg (result_aop, i + 1, A_IDX)) + result_in_a = TRUE; + started = TRUE; + i += 2; + } + else if (!started && right_aop->type == AOP_LIT && + (aopInReg (left_aop, i, XH_IDX) && aopInReg (result_aop, i, XH_IDX) || aopInReg (left_aop, i, YH_IDX) && aopInReg (result_aop, i, YH_IDX))) + { + emit2 ("subw", "%s, #%d", aopInReg (left_aop, i, YH_IDX) ? "y" : "x", byteOfVal (right_aop->aopu.aop_lit, i) << 8); + cost (3 + aopInReg (left_aop, i, YH_IDX), 2); + started = TRUE; + i++; + } + else if (!started && i == size - 1 && right_aop->type == AOP_LIT && // For yl, we only save a cycle comapred to the normal way. + (aopInReg (left_aop, i, XL_IDX) && aopInReg (result_aop, i, XL_IDX) && xh_free || aopInReg (left_aop, i, YL_IDX) && aopInReg (result_aop, i, YL_IDX) && yh_free)) + { + emit2 ("subw", "%s, #%d", aopInReg (left_aop, i, YL_IDX) ? "y" : "x", byteOfVal (right_aop->aopu.aop_lit, i)); + cost (3 + aopInReg (left_aop, i, YL_IDX), 2); + started = TRUE; + i++; + } + else if (!started && i == size - 1 && aopOnStackNotExt (right_aop, i, 1) && + (aopInReg (left_aop, i, XL_IDX) && aopInReg (result_aop, i, XL_IDX) && xh_free || aopInReg (left_aop, i, YL_IDX) && aopInReg (result_aop, i, YL_IDX) && yh_free)) + { + emit2 ("subw", "%s, (%d, sp)", aopInReg (left_aop, i, YL_IDX) ? "y" : "x", right_aop->aopu.bytes[i].byteu.stk + G.stack.pushed - 1); + cost (3, 2); + started = TRUE; + i++; + } + else if (aopInReg (right_aop, i, A_IDX)) // Needs special handling as generic code below would overwrite a. + { + if (!pushed_a) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left_aop, i, false); + emit2 (started ? "sbc" : "sub", "a, (1, sp)"); + cost (2, 1); + if (aopInReg (result_aop, i, A_IDX)) + { + adjustStack (1, false, false, false); + pushed_a = false; + } + else + { + cheapMove (result_aop, i, ASMOP_A, 0, false); + pushed_a = true; + } + i++; + } + else + { + if (pushed_a && left_aop->regs[A_IDX] == i && regDead (A_IDX, ic)) + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + else if (!a_free) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + result_in_a = FALSE; + } + + if (left_aop->regs[A_IDX] == i && pushed_a) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, left_aop, i, FALSE); + + if (!started && aopIsLitVal (right_aop, i, 1, 0)) + ; // Skip over this byte. + else if (!started && i + 1 == size && aopIsLitVal (right_aop, i, 1, 1)) + emit3 (A_DEC, ASMOP_A, 0); + else + { + const asmop *right_stacked = NULL; + int right_offset; + + right_stacked = stack_aop (right_aop, i, &right_offset); + + if (!right_stacked) + emit3_o (started ? A_SBC : A_SUB, ASMOP_A, 0, right_aop, i); + else + { + emit2 (started ? "sbc" : "sub", "a, (#%d, sp)", right_offset); + cost (2, 1); + } + + if (right_stacked) + pop (right_stacked, 0, 2); + + started = TRUE; + } + + cheapMove (result_aop, i, ASMOP_A, 0, FALSE); + + if (aopInReg (result_aop, i, A_IDX)) + result_in_a = TRUE; + + i++; + } + } + + if (pushed_a && !result_in_a) + pop (ASMOP_A, 0, 1); + else if (pushed_a) + adjustStack (1, FALSE, FALSE, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + bool move_all; + + D (emit2 ("; genUminusFloat", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + move_all = aopRS(left->aop) && left->aop->regs[A_IDX] >= 0 && aopRS(result->aop) && result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < result->aop->size - 1 || + (aopInReg (result->aop, result->aop->size - 2, X_IDX) || aopInReg (result->aop, result->aop->size - 2, Y_IDX)) && aopOnStack (left->aop, result->aop->size - 2, 2); + + genMove_o (result->aop, 0, left->aop, 0, result->aop->size - 1 + move_all, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + if (aopInReg (result->aop, result->aop->size - 1, YH_IDX) && (move_all || aopInReg (left->aop, result->aop->size - 1, YH_IDX)) || + aopInReg (result->aop, result->aop->size - 1, XH_IDX) && (move_all || aopInReg (left->aop, result->aop->size - 1, XH_IDX))) + { + const bool use_y = aopInReg (result->aop, result->aop->size - 1, YH_IDX); + emit3w (A_SLLW, use_y ? ASMOP_Y : ASMOP_X, 0); + emit2 ("ccf", ""); + cost (1, 1); + emit3w (A_RRCW, use_y ? ASMOP_Y : ASMOP_X, 0); + } + // todo: Use bcpl. use swap_to_a for left in same reg as right. + else + { + if (!regDead(A_IDX, ic) || aopRS(result->aop) && result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < result->aop->size - 1) + push (ASMOP_A, 0, 1); + + cheapMove (ASMOP_A, 0, (move_all ? result: left)->aop, left->aop->size - 1, FALSE); + emit2 ("xor", "a, #0x80"); + cost (2, 1); + cheapMove (result->aop, result->aop->size - 1, ASMOP_A, 0, FALSE); + + if (!regDead(A_IDX, ic) || aopRS(result->aop) && result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < result->aop->size - 1) + pop (ASMOP_A, 0, 1); + } + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - generates code for unary minus */ +/*-----------------------------------------------------------------*/ +static void +genUminus (const iCode *ic) +{ + operand *result; + operand *left; + + if (IS_FLOAT (operandType (IC_LEFT (ic)))) + { + genUminusFloat (ic); + return; + } + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + + D (emit2 ("; genUminus", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + genSub (ic, result->aop, ASMOP_ZERO, left->aop); + + freeAsmop (left); + freeAsmop (result); +} + +static void +saveRegsForCall (const iCode * ic) +{ + if (G.saved && !regalloc_dry_run) + return; + + //if (!regDead (C_IDX, ic)) + // push (ASMOP_C, 0, 1); + + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + if (!regDead (X_IDX, ic)) + push (ASMOP_X, 0, 2); + + if (!regDead (Y_IDX, ic)) + push (ASMOP_Y, 0, 2); + + G.saved = TRUE; +} + +/*-----------------------------------------------------------------*/ +/* genIpush - generate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (const iCode * ic) +{ + int size, offset = 0; + iCode *walk; + + D (emit2 ("; genIPush", "")); + + if (!ic->parmPush) + { + wassertl (0, "Encountered an unsupported spill push."); + return; + } + + /* Caller saves, and this is the first iPush. */ + /* Scan ahead until we find the function that we are pushing parameters to. + Count the number of addSets on the way to figure out what registers + are used in the send set. + */ + for (walk = ic->next; walk->op != CALL && walk->op != PCALL; walk = walk->next); + if (!G.saved && !regalloc_dry_run /* Cost is counted at CALL or PCALL instead */ ) + saveRegsForCall (walk); + + /* then do the push */ + aopOp (IC_LEFT (ic), ic); + + for (size = IC_LEFT (ic)->aop->size, offset = 0; size;) + { + if (aopInReg (IC_LEFT (ic)->aop, offset, X_IDX) || aopInReg (IC_LEFT (ic)->aop, offset, Y_IDX)) + { + push (IC_LEFT (ic)->aop, offset, 2); + offset += 2; + size -= 2; + } + // Going through x is more efficient than two individual pushes for some cases. + else if (size >= 2 && regDead (X_IDX, ic) && IC_LEFT (ic)->aop->regs[XL_IDX] < offset && IC_LEFT (ic)->aop->regs[XH_IDX] < offset && + (aopIsLitVal (IC_LEFT (ic)->aop, offset, 2, 0x0000) || IC_LEFT (ic)->aop->type == AOP_DIR && optimize.codeSize || aopOnStack (IC_LEFT (ic)->aop, offset, 2))) + { + genMove_o (ASMOP_X, 0, IC_LEFT (ic)->aop, offset, 2, regDead (A_IDX, ic) && IC_LEFT (ic)->aop->regs[A_IDX] < offset, TRUE, FALSE); + push (ASMOP_X, 0, 2); + offset += 2; + size -= 2; + } + // Going through y is more efficient than two individual pushes for stack operands only. + else if (size >= 2 && regDead (Y_IDX, ic) && IC_LEFT (ic)->aop->regs[YL_IDX] < offset && IC_LEFT (ic)->aop->regs[YH_IDX] < offset && aopOnStack (IC_LEFT (ic)->aop, offset, 2)) + { + genMove_o (ASMOP_Y, 0, IC_LEFT (ic)->aop, offset, 2, regDead (A_IDX, ic) && IC_LEFT (ic)->aop->regs[A_IDX] < offset, FALSE, TRUE); + push (ASMOP_Y, 0, 2); + offset += 2; + size -= 2; + } + // Push directly. + else if (IC_LEFT (ic)->aop->type == AOP_LIT || aopInReg (IC_LEFT (ic)->aop, offset, A_IDX) || IC_LEFT (ic)->aop->type == AOP_DIR || IC_LEFT (ic)->aop->type == AOP_IMMD) + { + push (IC_LEFT (ic)->aop, offset, 1); + offset++; + size--; + } + // a is not free. Try to use xl instead. + else if ((!regDead (A_IDX, ic) || IC_LEFT (ic)->aop->regs[A_IDX] > offset) && (regDead (XL_IDX, ic) && IC_LEFT (ic)->aop->regs[XL_IDX] <= offset || aopInReg (IC_LEFT (ic)->aop, offset, XL_IDX))) + { + genMove_o (ASMOP_X, 0, IC_LEFT (ic)->aop, offset, 1, FALSE, FALSE, FALSE); + push (ASMOP_X, 0, 2); + adjustStack (1, FALSE, FALSE, FALSE); + offset++; + size--; + } + // Neither a nor xl is free. Allocator guarantees that yl is free then; use it. + else if (!regDead (A_IDX, ic) || IC_LEFT (ic)->aop->regs[A_IDX] > offset) + { + genMove_o (ASMOP_Y, 0, IC_LEFT (ic)->aop, offset, 1, FALSE, FALSE, FALSE); + push (ASMOP_Y, 0, 2); + adjustStack (1, FALSE, FALSE, FALSE); + offset++; + size--; + } + else + { + cheapMove (ASMOP_A, 0, IC_LEFT (ic)->aop, offset, FALSE); + push (ASMOP_A, 0, 1); + offset++; + size--; + } + } + + freeAsmop (IC_LEFT (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (const iCode *ic) +{ + bool SomethingReturned, bigreturn, half; + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *etype = getSpec (dtype); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + + D (emit2 ("; genCall", "")); + + saveRegsForCall (ic); + + /* Return value of big type or returning struct or union. */ + bigreturn = (getSize (ftype->next) > 4) || IS_STRUCT (ftype->next); + if (bigreturn) + { + wassertl (IC_RESULT (ic), "Unused return value in call to function returning large type."); + + aopOp (IC_RESULT (ic), ic); + + if (IC_RESULT (ic)->aop->type != AOP_STK) + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented return value size / type combination."); + cost (180, 180); + } + + emit2 ("ldw", "x, sp"); + emit2 ("addw", "x, #%d", IC_RESULT (ic)->aop->aopu.bytes[getSize (ftype->next) - 1].byteu.stk + G.stack.pushed); + cost (2 + 4, 1 + 2); + push (ASMOP_X, 0, 2); + + freeAsmop (IC_RESULT (ic)); + } + + if (ic->op == PCALL) + { + operand *left = IC_LEFT (ic); + + aopOp (left, ic); + + if (options.model == MODEL_LARGE && left->aop->type == AOP_DIR) + { + wassertl (left->aop->size == 3, "Functions pointers should be 24 bits in large memory model."); + + emit2 ("callf", "[%s]", left->aop->aopu.aop_dir); + cost (4, 8); + } + else if (options.model == MODEL_LARGE) + { + wassertl (left->aop->size == 3, "Functions pointers should be 24 bits in large memory model."); + + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + + if (!regalloc_dry_run) + { + emit2("push", "#(!tlabel)", labelKey2num (tlbl->key)); + emit2("push", "#(!tlabel >> 8)", labelKey2num (tlbl->key)); + emit2("push", "#(!tlabel >> 16)", labelKey2num (tlbl->key)); + cost (6, 3); + G.stack.pushed += 3; + } + + if (aopInReg (left->aop, 0, X_IDX) || aopInReg (left->aop, 0, Y_IDX)) + push (left->aop, 0, 2); + else if (aopOnStackNotExt (left->aop, 0, 2) && !(aopInReg (left->aop, 2, XL_IDX) || aopInReg (left->aop, 2, XH_IDX)) || + aopInReg (left->aop, 2, A_IDX)) + { + genMove (ASMOP_X, left->aop, !aopInReg (left->aop, 2, A_IDX), true, false); + push (ASMOP_X, 0, 2); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, 0, false); + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left->aop, 1, false); + push (ASMOP_A, 0, 1); + } + cheapMove (ASMOP_A, 0, left->aop, 2, false); + push (ASMOP_A, 0, 1); + emit2("retf", ""); + cost (1, 5); + + G.stack.pushed -= 6; + + emitLabel (tlbl); + } + else + { + wassertl (left->aop->size == 2, "Functions pointers should be 16 bits in medium memory model."); + + if (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) + { + emit2 ("call", "%s", aopGet2 (left->aop, 0)); + cost (3, 4); + } + else if (aopInReg (left->aop, 0, Y_IDX)) // Faster than going through x. + { + emit2 ("call", "(y)"); + cost (2, 4); + } + else + { + genMove (ASMOP_X, left->aop, TRUE, TRUE, TRUE); + + emit2 ("call", "(x)"); + cost (1, 4); + } + } + freeAsmop (left); + } + else + { + if (options.model == MODEL_LARGE) + { + if (IS_LITERAL (etype)) + { + emit2 ("callf", "0x%06X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + cost (4, 5); + } + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); + emit2 (jump ? "jpf" : "callf", "%s", + (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + cost (4, jump ? 2 : 5); + } + } + else + { + if (IS_LITERAL (etype)) + { + emit2 ("call", "0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + cost (3, 4); + } + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (OP_SYMBOL (IC_LEFT (ic))->type)); + emit2 (jump ? "jp" : "call", "%s", + (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + cost (3, jump ? 1 : 4); + } + } + } + + SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || OP_SYMBOL (IC_RESULT (ic))->spildir)) + || IS_TRUE_SYMOP (IC_RESULT (ic)); + + if (ic->parmBytes || bigreturn) + adjustStack (ic->parmBytes + bigreturn * 2, !(SomethingReturned && getSize (ftype->next) == 1), !(SomethingReturned && (getSize (ftype->next) == 2 || getSize (ftype->next) == 4)), !(SomethingReturned && getSize (ftype->next) == 4)); + + half = stm8_extend_stack && SomethingReturned && getSize (ftype->next) == 4; + + /* Todo: More efficient handling of long return value for function with extendeds stack when the result value does not use the extended stack. */ + + /* Special handling of assignment of long result value when using extended stack. */ + if (half) + { + asmop *result; + int save_a = 0; + + aopOp (IC_RESULT (ic), ic); + result = IC_RESULT (ic)->aop; + + push (ASMOP_Y, 0, 2); + emit2 ("ldw", "y, (3, sp)"); + cost (2, 2); + + emit2 ("ld", "a, (2, sp)"); + cost (2, 1); + if (IC_RESULT (ic)->aop->size > 2) + cheapMove (IC_RESULT (ic)->aop, 2, ASMOP_A, 0, TRUE); + if (result->size > 2) + if (aopRS (result) && aopRS (ASMOP_A) && + result->aopu.bytes[2].in_reg && ASMOP_A->aopu.bytes[0].in_reg && + result->aopu.bytes[2].byteu.reg == ASMOP_A->aopu.bytes[0].byteu.reg) + { + push (ASMOP_A, 0, 1); + save_a = 1; + } + + if (save_a) + emit2 ("ld", "a, (2, sp)"); + else + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + if (IC_RESULT (ic)->aop->size > 3) + cheapMove (IC_RESULT (ic)->aop, 3, ASMOP_A, 0, TRUE); + if (save_a) + { + pop (ASMOP_A, 0, 1); + save_a = 0; + } + + adjustStack (4, FALSE, FALSE, FALSE); + + if (IC_RESULT (ic)->aop->regs[XL_IDX] >= 2 || IC_RESULT (ic)->aop->regs[XH_IDX] >= 2) + { + wassert (regalloc_dry_run); + cost (180, 180); + } + + freeAsmop (IC_RESULT (ic)); + } + else if (stm8_extend_stack) + pop (ASMOP_Y, 0, 2); + + /* if we need assign a result value */ + if (SomethingReturned && !bigreturn) + { + int size; + + aopOp (IC_RESULT (ic), ic); + + size = !half ? IC_RESULT (ic)->aop->size : (IC_RESULT (ic)->aop->size > 2 ? 2 : IC_RESULT (ic)->aop->size); + + wassert (getSize (ftype->next) >= 1 && getSize (ftype->next) <= 4); + + genMove_o (IC_RESULT (ic)->aop, 0, getSize (ftype->next) == 1 ? ASMOP_A : ASMOP_XY, 0, size, TRUE, TRUE, !stm8_extend_stack); + + freeAsmop (IC_RESULT (ic)); + } + + // Restore regs. + if (!regDead (Y_IDX, ic) && !stm8_extend_stack) + if (regDead (YH_IDX, ic)) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (YL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(YL_IDX); + } + else if (regDead (YL_IDX, ic)) + { + swap_to_a (YH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(YH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (ASMOP_Y, 0, 2); + + if (!regDead (X_IDX, ic)) + { + if (regDead (XH_IDX, ic)) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (XL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(XL_IDX); + } + else if (regDead (XL_IDX, ic)) + { + swap_to_a (XH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(XH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (ASMOP_X, 0, 2); + } + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + + //if (!regDead (C_IDX, ic)) + // pop (ASMOP_C, 0, 1); + + G.saved = FALSE; +} + +/*---------------------------------------------------------------------*/ +/* genCritical - mask interrupts until important block completes */ +/*---------------------------------------------------------------------*/ + +static void +genCritical (iCode * ic) +{ + emit2("sim", ""); + cost (1, 1); +} + +static void +genEndCritical (iCode * ic) +{ + emit2("rim", ""); + cost (1, 1); +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (iCode *ic) +{ + const symbol *sym = OP_SYMBOL_CONST (IC_LEFT (ic)); + sym_link *ftype = operandType (IC_LEFT (ic)); + bool bigreturn; + + G.stack.pushed = 0; + G.stack.param_offset = 0; + + /* create the function header */ + emit2 (";", "-----------------------------------------"); + emit2 (";", " function %s", sym->name); + emit2 (";", "-----------------------------------------"); + + D (emit2 (";", stm8_assignment_optimal ? "Register assignment is optimal." : "Register assignment might be sub-optimal.")); + D (emit2 (";", "Stack space usage: %d bytes.", sym->stack)); + + emit2 ("", "%s:", sym->rname); + genLine.lineCurr->isLabel = 1; + + if (IFFUNC_ISNAKED(ftype)) + { + emit2(";", "naked function: no prologue."); + return; + } + + if (IFFUNC_ISCRITICAL (ftype)) + genCritical (NULL); + + // Workaround for hardware bug: Undocumented bit 6 of the condition code register needs to be cleared before div/divw. It is set during div/divw execution, and then reset. Without the workaround, the div and divw inside interrupt routines will give wrong results when the interrupt itself occured while another div or divw was executed. + // For more information see sections titled "Unexpected DIV/DIVW instruction result in ISR" in various STM8 errata notes (apparently all STM8 are affected). + // The workaround here is the one recommended by STM in the erratum. There might be better ways to do it. + if (IFFUNC_ISISR (sym->type) && !sym->funcDivFlagSafe) + { + D (emit2 (";", "Reset bit 6 of reg CC. Hardware bug workaround.")); +#if 0 + // The workaround recommended by STM. 6 bytes, 7 cycles (5 nominally, two more due to pipeline stalls) + emit2 ("push", "cc"); + emit2 ("pop", "a"); + emit2 ("and", "a, #0xbf"); + emit2 ("push", "a"); + emit2 ("pop", "cc"); + cost (6, 5); +#else + // The workaround obtained by further investigation of RFE #449. Experiments on STM8S208MB and STM8L152C6 show that div resets bit 6 of cc. + if (!optimize.codeSize) + emit3 (A_CLR, ASMOP_A, 0); // Zero accumulator to reduce cycle cost in following division. + emit2 ("div", "x, a"); // According to measurements on the STM8S208MB and STM8L152C6, div takes 2-3 cycles for divisions by zero and 2-17 cycles in general. + cost (1, 3); +#endif + } + + if (stm8_extend_stack) // Setup for extended stack access. + { + G.stack.size = stm8_call_stack_size + (sym->stack ? sym->stack : 0); + D (emit2 (";", "Setup y for extended stack access.")); + emit2 ("ldw", "y, sp"); + emit2 ("subw", "y, #%ld", G.stack.size - 256); + cost (6, 3); + } + + bigreturn = (getSize (ftype->next) > 4); + G.stack.param_offset += bigreturn * 2; + + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, &stm8_regs[SP_IDX], 1); + + /* adjust the stack for the function */ + if (sym->stack) + adjustStack (-sym->stack, TRUE, TRUE, !stm8_extend_stack); +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode *ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int retsize = getSize (sym->type->next); + + D (emit2 ("; genEndFunction", "")); + + wassert (!regalloc_dry_run); + + if (IFFUNC_ISNAKED(sym->type)) + { + D (emit2 (";", "naked function: no epilogue.")); + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 0); + return; + } + + /* adjust the stack for the function */ + if (sym->stack) + adjustStack (sym->stack, retsize != 1, retsize != 2 && retsize != 4, retsize != 4); + + wassertl (!G.stack.pushed, "Unbalanced stack."); + + if (IFFUNC_ISCRITICAL (sym->type)) + genEndCritical (NULL); + + if (IFFUNC_ISISR (sym->type)) + { + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 1); + + emit2 ("iret", ""); + cost (1, 11); + } + else + { + /* if debug then send end of function */ + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeEndFunction (currFunc, ic, 1); + + if (options.model == MODEL_LARGE) + { + emit2 ("retf", ""); + cost (1, 5); + } + else + { + emit2 ("ret", ""); + cost (1, 4); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genReturn - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genReturn (const iCode *ic) +{ + operand *left = IC_LEFT (ic); + int size, i; + bool stacked = FALSE; + + D (emit2 ("; genReturn", "")); + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (left, ic); + size = left->aop->size; + + switch (size) + { + case 0: + break; + case 1: + cheapMove (ASMOP_A, 0, left->aop, 0, FALSE); + break; + case 2: + genMove (ASMOP_X, left->aop, TRUE, TRUE, TRUE); + break; + case 3: + wassertl (regalloc_dry_run || !stm8_extend_stack, "Unimplemented 24-bit return in function with extended stack access."); + genMove (ASMOP_XYL, left->aop, TRUE, TRUE, TRUE); + break; + case 4: + wassertl (regalloc_dry_run || !stm8_extend_stack, "Unimplemented long return in function with extended stack access."); + genMove (ASMOP_XY, left->aop, TRUE, TRUE, TRUE); + break; + default: + wassertl (size > 4, "Return not implemented for return value of this size."); + + for(i = 0; i < size; i++) + if (aopInReg (left->aop, i, XL_IDX) || aopInReg (left->aop, i, XH_IDX)) + { + push (ASMOP_X, 0, 2); + stacked = TRUE; + break; + } + + unsigned int o = G.stack.pushed + 3 + (options.model == MODEL_LARGE); + + if (o <= 255) + { + emit2 ("ldw", "x, (0x%02x, sp)", o); + cost (2, 2); + } + else + { + emit2 ("ldw", "x, sp"); + cost (1, 1); + emit2 ("addw", "x, #0x%04x", o); + cost (3, 2); + emit2 ("ldw", "x, (x)"); + cost (1, 1); + } + + // Clear a first. + for(i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX)) + { + emit2 ("ld", "(#%d, x), a", size - 1 - i); + cost (2, 1); + break; + } + + for(i = 0; i < size;) + { + if (aopInReg (left->aop, i, Y_IDX) || size > 2 && left->aop->regs[YL_IDX] < i && left->aop->regs[YH_IDX] < i && (aopOnStackNotExt (left->aop, i, 2) || left->aop->type == AOP_LIT)) + { + genMove_o (ASMOP_Y, 0, left->aop, i, 2, TRUE, FALSE, TRUE); + if (size - 2 - i) + { + emit2 ("ldw", "(#%d, x), y", size - 2 - i); + cost (2, 2); + } + else + { + emit2 ("ldw", "(x), y"); + cost (1, 2); + } + i += 2; + } + else if (aopInReg (left->aop, i, XL_IDX) || aopInReg (left->aop, i, XH_IDX)) + { + emit2 ("ld", "a, (#%d, sp)", (int)(aopInReg (left->aop, i, XL_IDX)) + 1); + emit2 ("ld", "(#%d, x), a", size - 1 - i); + cost (4, 2); + i++; + } + else if (!aopInReg (left->aop, i, A_IDX)) + { + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + if (size - 1 - i) + { + emit2 ("ld", "(#%d, x), a", size - 1 - i); + cost (2, 1); + } + else + { + emit2 ("ld", "(x), a"); + cost (1, 1); + } + i++; + } + else + i++; + } + + if (stacked) + adjustStack (2, TRUE, TRUE, TRUE); + } + + freeAsmop (left); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + emitJP(returnLabel, 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (const iCode *ic) +{ + D (emit2 ("; genLabel", "")); + + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + if (options.debug && !regalloc_dry_run) + debugFile->writeLabel (IC_LABEL (ic), ic); + + emitLabel (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a jump */ +/*-----------------------------------------------------------------*/ +static void +genGoto (const iCode *ic) +{ + D (emit2 ("; genGoto", "")); + + emitJP(IC_LABEL (ic), 1.0f); +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + asmop *leftop; + asmop *rightop; + + struct asmop lop_impl; + struct asmop rop_impl; + + int size, i, j; + bool started; + bool pushed_a = FALSE; + bool result_in_a = FALSE; + symbol *endlbl = 0; + + D (emit2 ("; genPlus", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + size = result->aop->size; + + /* Swap if left is literal or right is in A. */ + if (left->aop->type == AOP_LIT || right->aop->type != AOP_LIT && left->aop->type == AOP_IMMD || aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 0, X_IDX) || right->aop->type != AOP_LIT && right->aop->size == 1 && aopOnStackNotExt (left->aop, 0, 2) || left->aop->type == AOP_STK && (right->aop->type == AOP_REG || right->aop->type == AOP_REGSTK)) // todo: Swap in more cases when right in reg, left not. Swap individually per-byte. + { + operand *t = right; + right = left; + left = t; + } + + if (left->aop->type == AOP_REGSTK && right->aop->type == AOP_REGSTK) + { + bool all_in_reg, all_on_stack; + + lop_impl.size = 0; + rop_impl.size = 0; + + lop_impl.regs[A_IDX] = -1; + lop_impl.regs[XL_IDX] = -1; + lop_impl.regs[XH_IDX] = -1; + lop_impl.regs[YL_IDX] = -1; + lop_impl.regs[YH_IDX] = -1; + rop_impl.regs[A_IDX] = -1; + rop_impl.regs[XL_IDX] = -1; + rop_impl.regs[XH_IDX] = -1; + rop_impl.regs[YL_IDX] = -1; + rop_impl.regs[YH_IDX] = -1; + + for (i = 0; i < size; i++) + { + asmop *lop = 0; + asmop *rop = 0; + + if (left->aop->size < i && right->aop->size < i) + continue; + else if (left->aop->size < i && aopOnStack (right->aop, i, 1)) + rop = right->aop; + else if (left->aop->size < i) + lop = right->aop; + else if (right->aop->size < i && aopOnStack (left->aop, i, 1)) + rop = left->aop; + else if (right->aop->size < i) + lop = left->aop; + else if (!left->aop->aopu.bytes[i].in_reg && right->aop->aopu.bytes[i].in_reg) + { + lop = right->aop; + rop = left->aop; + } + else + { + lop = left->aop; + rop = right->aop; + } + + if (lop) + { + lop_impl.aopu.bytes[i] = lop->aopu.bytes[i]; + if (lop->aopu.bytes[i].in_reg) + lop_impl.regs[lop->aopu.bytes[i].byteu.reg->rIdx] = i; + lop_impl.size++; + } + if (rop) + { + rop_impl.aopu.bytes[i] = rop->aopu.bytes[i]; + if (rop->aopu.bytes[i].in_reg) + rop_impl.regs[rop->aopu.bytes[i].byteu.reg->rIdx] = i; + rop_impl.size++; + } + } + + all_in_reg = all_on_stack = TRUE; + for (i = 0; i < lop_impl.size; i++) + if (lop_impl.aopu.bytes[i].in_reg) + all_on_stack = FALSE; + else + all_in_reg = FALSE; + lop_impl.type = all_on_stack ? AOP_STK : (all_in_reg ? AOP_REG : AOP_REGSTK); + all_in_reg = all_on_stack = TRUE; + for (i = 0; i < rop_impl.size; i++) + if (rop_impl.aopu.bytes[i].in_reg) + all_on_stack = FALSE; + else + all_in_reg = FALSE; + rop_impl.type = all_on_stack ? AOP_STK : (all_in_reg ? AOP_REG : AOP_REGSTK); + + leftop = &lop_impl; + rightop = &rop_impl; + } + else + { + leftop = left->aop; + rightop = right->aop; + } + + for (i = 0, started = FALSE; i < size;) // Todo: 16-bit operation in dead source might be cheaper than add. + { + bool a_free = regDead (A_IDX, ic) && leftop->regs[A_IDX] <= i && rightop->regs[A_IDX] <= i && !result_in_a || pushed_a; + bool xl_free = regDead (XL_IDX, ic) && (result->aop->regs[XL_IDX] >= i || result->aop->regs[XL_IDX] < 0) && leftop->regs[XL_IDX] <= i + 1 && rightop->regs[XL_IDX] < i; + bool xh_free = regDead (XH_IDX, ic) && (result->aop->regs[XH_IDX] >= i || result->aop->regs[XH_IDX] < 0) && leftop->regs[XH_IDX] <= i + 1 && rightop->regs[XH_IDX] < i; + bool x_free = xl_free && xh_free; + bool yl_free = regDead (YL_IDX, ic) && (result->aop->regs[YL_IDX] >= i || result->aop->regs[YL_IDX] < 0) && leftop->regs[YL_IDX] <= i + 1 && rightop->regs[YL_IDX] < i; + bool yh_free = regDead (YH_IDX, ic) && (result->aop->regs[YH_IDX] >= i || result->aop->regs[YH_IDX] < 0) && leftop->regs[YH_IDX] <= i + 1 && rightop->regs[YH_IDX] < i; + bool y_free = yl_free && yh_free; + + // Special case for rematerializing sums + if (!started && i == size - 2 && (leftop->type == AOP_IMMD && rightop->type == AOP_LIT) && + (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX) || x_free && aopOnStack (result->aop, i, 2))) + { + unsigned offset = byteOfVal (right->aop->aopu.aop_lit, 1) * 256 + byteOfVal (right->aop->aopu.aop_lit, 0); + bool y = aopInReg (result->aop, i, Y_IDX) ; + emit2 ("ldw", y ? "y, %s+%d" : "x, %s+%d", aopGet2 (leftop, i), offset); + cost (3 + y, 2); + genMove_o (result->aop, i, y ? ASMOP_Y : ASMOP_X, 0, 2, a_free, TRUE, y_free); + started = TRUE; + i += 2; + } + // We can use incw / decw easily only for the only, top non-zero word, since it neither takes into account an existing carry nor does it update the carry. + else if (!started && i == size - 2 && + (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX)) && + rightop->type == AOP_LIT && !byteOfVal (rightop->aopu.aop_lit, i + 1) && + byteOfVal (rightop->aopu.aop_lit, i) <= 1 + aopInReg (result->aop, i, X_IDX) || + !started && i == size - 1 && + !(aopInReg (leftop, i, A_IDX) && regDead (A_IDX, ic)) && + (aopInReg (result->aop, i, XL_IDX) && regDead (XH_IDX, ic) && leftop->regs[XH_IDX] < 0 && result->aop->regs[XH_IDX] < 0 || aopInReg (result->aop, i, YL_IDX) && regDead (YH_IDX, ic) && leftop->regs[YH_IDX] < 0 && result->aop->regs[YH_IDX] < 0) && + rightop->type == AOP_LIT && byteOfVal (rightop->aopu.aop_lit, i) <= 1 + aopInReg (result->aop, i, XL_IDX)) + { + bool half = (i == size - 1); + bool x = aopInReg (result->aop, i, half ? XL_IDX : X_IDX) ; + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, leftop, i, 2 - half, a_free, x, !x); + for (j = 0; j < byteOfVal (rightop->aopu.aop_lit, i); j++) + emit3w (A_INCW, x ? ASMOP_X : ASMOP_Y, 0); + cost (x ? 1 : 2, 1); + started = TRUE; + i += 2; + } + else if (!started && i == size - 2 && + (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX)) && + aopIsLitVal (rightop, i, 2, 0xffff)) + { + bool x = aopInReg (result->aop, i, X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, leftop, i, 2, a_free, x, !x); + emit3w (A_DECW, x ? ASMOP_X : ASMOP_Y, 0); + started = TRUE; + i += 2; + } + // Using incw with a chain or conditional jumps to emulate carry - allows somewhat more efficient 32-bit increment. + else if(!started && !pushed_a && rightop->type == AOP_LIT && regDead (X_IDX, ic) && !((size - i) % 2) && + aopIsLitVal (rightop, i, 2, 0x0001) && aopIsLitVal (rightop, i + 2, size - i, 0) && + ((aopOnStack (leftop, i, size - i) && aopOnStack (result->aop, i, size - i) || + aopOnStack (leftop, i, size - i - 2) && aopOnStack (leftop, i, size - i - 2) && aopInReg (result->aop, size - 2, Y_IDX) && aopInReg (result->aop, size - 2, Y_IDX)) && + result->aop->aopu.bytes[i].byteu.stk == leftop->aopu.bytes[i].byteu.stk || + aopOnStack (leftop, i + 2, size - i - 2) && aopOnStack (leftop, i + 2, size - i - 2) && aopInReg (result->aop, i, Y_IDX) && aopInReg (result->aop, i, Y_IDX) && result->aop->aopu.bytes[i + 2].byteu.stk == leftop->aopu.bytes[i + 2].byteu.stk || + size - i == 4 && + (aopInReg (leftop, i, Y_IDX) && aopInReg (result->aop, i, Y_IDX) && aopInReg (leftop, i + 2, X_IDX) && aopInReg (result->aop, i + 2, X_IDX) || aopInReg (leftop, i, X_IDX) && aopInReg (result->aop, i, X_IDX) && aopInReg (leftop, i + 2, Y_IDX) && aopInReg (result->aop, i + 2, Y_IDX)))) + { + if(!endlbl && !regalloc_dry_run) + endlbl = newiTempLabel (NULL); + for(;;) + { + if(aopInReg (leftop, i, Y_IDX) && aopInReg (result->aop, i, Y_IDX) || + aopInReg (leftop, i, X_IDX) && aopInReg (result->aop, i, X_IDX)) + emit3w_o (A_INCW, result->aop, i, 0, 0); + else + { + genMove_o (ASMOP_X, 0, leftop, i, 2, a_free, TRUE, y_free); + emit3w (A_INCW, ASMOP_X, 0); + genMove_o (result->aop, i, ASMOP_X, 0, 2, a_free, TRUE, y_free); + } + i += 2; + if(i >= size) + break; + if (endlbl) + emit2 ("jrne", "!tlabel", labelKey2num (endlbl->key)); + } + } + else if (!started && + (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX)) && + (rightop->type == AOP_LIT || rightop->type == AOP_IMMD || aopOnStackNotExt (rightop, i, 2) || rightop->type == AOP_DIR && i + 1 < rightop->size) && + !(aopInReg (result->aop, i, Y_IDX) && aopInReg (left->aop, i, X_IDX) && regDead (X_IDX, ic))) + { + bool x = aopInReg (result->aop, i, X_IDX); + genMove_o (x ? ASMOP_X : ASMOP_Y, 0, leftop, i, 2, a_free, x, !x); + if (!aopIsLitVal (rightop, i, 2, 0x0000)) + { + emit2 ("addw", x ? "x, %s" : "y, %s", aopGet2 (rightop, i)); + cost ((x || aopOnStack (rightop, 0, 2)) ? 3 : 4, 2); + started = TRUE; + } + i += 2; + } + else if (!started && i == size - 1 && + (aopOnStack (leftop, i, 1) || leftop->type == AOP_DIR) && aopSame (result->aop, i, leftop, i, 1) && + rightop->type == AOP_LIT && byteOfVal (rightop->aopu.aop_lit, i) <= 2 + !a_free) + { + for (j = 0; j < byteOfVal (rightop->aopu.aop_lit, i); j++) + emit3_o (A_INC, result->aop, i, 0, 0); + i++; + } + else if (!started && i == size - 1 && + (aopOnStack (leftop, i, 1) || leftop->type == AOP_DIR) && aopSame (result->aop, i, leftop, i, 1) && + rightop->type == AOP_LIT && byteOfVal (rightop->aopu.aop_lit, i) >= 254 - !a_free) + { + for (j = byteOfVal (rightop->aopu.aop_lit, i); j < 256; j++) + emit3_o (A_DEC, result->aop, i, 0, 0); + i++; + } + else if (!started && i + 1 < size && (x_free || aopInReg(leftop, i, X_IDX) && regDead (X_IDX, ic)) && + (aopOnStackNotExt (rightop, i, 2) || rightop->type == AOP_LIT || rightop->type == AOP_IMMD || rightop->type == AOP_DIR && i + 1 < rightop->size) && + ((aopOnStack (result->aop, i, 2) || result->aop->type == AOP_DIR) && (aopRS (leftop) && !aopInReg(leftop, i, A_IDX) && !aopInReg(leftop, i + 1, A_IDX) || leftop->type == AOP_DIR) || + aopInReg(leftop, i, X_IDX) && aopInReg(result->aop, i, Y_IDX) || + aopInReg(leftop, i, X_IDX) && (result->aop->regs[XL_IDX] < 0 || result->aop->regs[XL_IDX] >= i) && (result->aop->regs[XH_IDX] < 0 || result->aop->regs[XH_IDX] >= i) && (aopInReg(leftop, i, XL_IDX) || aopInReg(leftop, i + 1, XH_IDX) || aopInReg(leftop, i, XH_IDX) && aopInReg(leftop, i + 1, XL_IDX)))) + { + genMove_o (ASMOP_X, 0, leftop, i, 2, a_free, TRUE, FALSE); + if (i == size - 2 && rightop->type == AOP_LIT && byteOfVal (rightop->aopu.aop_lit, i) <= 2 && !byteOfVal (rightop->aopu.aop_lit, i + 1)) + for (j = 0; j < byteOfVal (rightop->aopu.aop_lit, i); j++) + emit3w (A_INCW, ASMOP_X, 0); + else + { + emit2 ("addw", "x, %s", aopGet2 (rightop, i)); + cost (3 + (rightop->type == AOP_DIR), 2); + } + + genMove_o (result->aop, i, ASMOP_X, 0, 2, a_free, TRUE, FALSE); + if (aopInReg (result->aop, i, A_IDX) || aopInReg (result->aop, i + 1, A_IDX)) + result_in_a = TRUE; + started = TRUE; + i += 2; + } + else if (!started && i + 1 < size && (y_free || aopInReg(leftop, i, Y_IDX) && regDead (Y_IDX, ic)) && + (aopOnStack (result->aop, i, 2) || result->aop->type == AOP_DIR) && + (aopOnStack (leftop, i, 2) || aopInReg(leftop, i, Y_IDX) || leftop->type == AOP_DIR) && + (aopOnStackNotExt (rightop, i, 2) || rightop->type == AOP_LIT || rightop->type == AOP_IMMD || rightop->type == AOP_DIR && i + 1 < rightop->size)) + { + genMove_o (ASMOP_Y, 0, leftop, i, 2, a_free, TRUE, FALSE); + if (i == size - 2 && rightop->type == AOP_LIT && byteOfVal (rightop->aopu.aop_lit, i) <= 2 && !byteOfVal (rightop->aopu.aop_lit, i + 1)) + for (j = 0; j < byteOfVal (rightop->aopu.aop_lit, i); j++) + emit3w (A_INCW, ASMOP_Y, 0); + else + { + emit2 ("addw", "y, %s", aopGet2 (rightop, i)); + cost (4 - aopOnStack (rightop, i, 2), 2); + } + genMove_o (result->aop, i, ASMOP_Y, 0, 2, a_free, TRUE, FALSE); + if (aopInReg (result->aop, i, A_IDX) || aopInReg (result->aop, i + 1, A_IDX)) + result_in_a = TRUE; + started = TRUE; + i += 2; + } + else if (!started && rightop->type == AOP_LIT && + (aopInReg (leftop, i, XH_IDX) && aopInReg (result->aop, i, XH_IDX) || aopInReg (leftop, i, YH_IDX) && aopInReg (result->aop, i, YH_IDX))) + { + emit2 ("addw", "%s, #%d", aopInReg (leftop, i, YH_IDX) ? "y" : "x", byteOfVal (rightop->aopu.aop_lit, i) << 8); + cost (3 + aopInReg (leftop, i, YH_IDX), 2); + started = TRUE; + i++; + } + else if (!started && i == size - 1 && rightop->type == AOP_LIT && // For yl, we only save a cycle compared to the normal way. + (aopInReg (leftop, i, XL_IDX) && aopInReg (result->aop, i, XL_IDX) && xh_free || aopInReg (leftop, i, YL_IDX) && aopInReg (result->aop, i, YL_IDX) && yh_free)) + { + emit2 ("addw", "%s, #%d", aopInReg (leftop, i, YL_IDX) ? "y" : "x", byteOfVal (rightop->aopu.aop_lit, i)); + cost (3 + aopInReg (leftop, i, YL_IDX), 2); + started = TRUE; + i++; + } + else if (!started && i == size - 1 && aopOnStackNotExt (rightop, i, 1) && + (aopInReg (leftop, i, XL_IDX) && aopInReg (result->aop, i, XL_IDX) && xh_free || aopInReg (leftop, i, YL_IDX) && aopInReg (result->aop, i, YL_IDX) && yh_free)) + { + emit2 ("addw", "%s, (%d, sp)", aopInReg (leftop, i, YL_IDX) ? "y" : "x", rightop->aopu.bytes[i].byteu.stk + G.stack.pushed - 1); + cost (3, 2); + started = TRUE; + i++; + } + else if (started && i == size - 2 && (aopInReg (result->aop, i, X_IDX) || aopInReg (result->aop, i, Y_IDX)) && + (aopOnStackNotExt (leftop, i, 2) || leftop->type == AOP_DIR) && + (aopOnStackNotExt (rightop, i, 2) || rightop->type == AOP_LIT || rightop->type == AOP_IMMD || rightop->type == AOP_DIR)) + { + bool x = aopInReg (result->aop, i, X_IDX); + symbol *skiplbl = 0; + if (!regalloc_dry_run) + skiplbl = newiTempLabel (NULL); + genMove_o (result->aop, i, leftop, i, 2, a_free, TRUE, FALSE); + if (skiplbl) + emit2 ("jrnc", "!tlabel", labelKey2num (skiplbl->key)); + cost (2, 1); // Cycle cost 1: jump, incw together take 2 cycles. + emit3w_o (A_INCW, result->aop, i, 0, 0); + emitLabel (skiplbl); + if (!aopIsLitVal (rightop, i, 2, 0)) + { + emit2 ("addw", x ? "x, %s" : "y, %s", aopGet2 (rightop, i)); + cost ((x || aopOnStack (rightop, i, 2)) ? 3 : 4, 2); + } + i += 2; + } + else if (aopInReg (rightop, i, A_IDX)) //todo: Implement handling of right operands that can't be directly added to a. + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented addition operand."); + cost (180, 180); + i++; + } + else + { + if (pushed_a && leftop->regs[A_IDX] == i && regDead (A_IDX, ic)) + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + else if (!a_free) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + result_in_a = FALSE; + } + + if (leftop->regs[A_IDX] == i && pushed_a) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, leftop, i, FALSE); + + if (!started && aopIsLitVal (rightop, i, 1, 0)) + ; // Skip over this byte. + // We can use inc / dec only for the only, top non-zero byte, since it neither takes into account an existing carry nor does it update the carry. + else if (!started && i == size - 1 && (aopIsLitVal (rightop, i, 1, 1) || aopIsLitVal (rightop, i, 1, 255))) + { + emit3 (aopIsLitVal (rightop, i, 1, 1) ? A_INC : A_DEC, ASMOP_A, 0); + started = true; + } + else if (aopInReg (rightop, i, XL_IDX) || aopInReg (rightop, i, XH_IDX) || aopInReg (rightop, i, YL_IDX) || aopInReg (rightop, i, YH_IDX)) + { + int right_offset; + const asmop *right_stacked; + wassert(right_stacked = stack_aop (rightop, i, &right_offset)); + emit2 (started ? "adc" : "add", "a, (%d, sp)", right_offset); + pop (right_stacked, 0, 2); + started = true; + } + else + { + emit3_o (started ? A_ADC : A_ADD, ASMOP_A, 0, i < rightop->size ? rightop : ASMOP_ZERO, i); + started = true; + } + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + if (aopInReg (result->aop, i, A_IDX)) + result_in_a = TRUE; + + i++; + } + } + + if (pushed_a && !result_in_a) + pop (ASMOP_A, 0, 1); + else if (pushed_a) + adjustStack (1, FALSE, FALSE, FALSE); + + emitLabel (endlbl); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultLit (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + asmop *add_aop; + + D (emit2 ("; genMultLit", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + if (left->aop->type == AOP_LIT) + { + operand *tmp = left; + left = right; + right = tmp; + } + + wassert (right->aop->type == AOP_LIT); + + add_aop = aopOnStackNotExt (left->aop, 0, 2) ? left->aop : 0; + if(!regDead (X_IDX, ic)) + push (ASMOP_X, 0, 2); + genMove (ASMOP_X, left->aop, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + if (!add_aop && isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, 0)) < 0) + push (ASMOP_X, 0, 2); + + /* Generate a sequence of shifts, additions and subtractions based on the canonical signed digit representation of the literal operand */ + { + unsigned long long add, sub; + int topbit, nonzero; + + wassert(!csdOfVal (&topbit, &nonzero, &add, &sub, right->aop->aopu.aop_lit)); + + // If the leading digits of the cse are 1 0 -1 we can use 0 1 1 instead to reduce the number of shifts. + if (topbit >= 2 && (add & (1ull << topbit)) && (sub & (1ull << (topbit - 2)))) + { + add = (add & ~(1u << topbit)) | (3u << (topbit - 2)); + sub &= ~(1u << (topbit - 1)); + topbit--; + } + + for (int bit = topbit - 1; bit >= 0; bit--) + { + emit3w (A_SLLW, ASMOP_X, 0); + if ((add | sub) & (1ull << bit)) + { + emit2 (add & (1ull << bit) ? "addw" : "subw" , "x, %s", add_aop ? aopGet (add_aop, 1) : "(1, sp)"); + cost (3, 2); + } + } + } + + if (!add_aop && isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, 0)) < 0) + adjustStack (2, regDead (A_IDX, ic), FALSE, regDead (Y_IDX, ic)); + genMove (result->aop, ASMOP_X, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + if (regDead (XL_IDX, ic) ^ regDead (XH_IDX, ic)) + { + if (!regalloc_dry_run) + wassert (0); + cost (100, 100); + } + if(!regDead (X_IDX, ic)) + pop (ASMOP_X, 0, 2); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + bool use_y; + + D (emit2 ("; genMult", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + if ((left->aop->size == 2 || right->aop->size == 2) && result->aop->size == 2 && (left->aop->type == AOP_LIT || right->aop->type == AOP_LIT) || + // Some multiplications by powers of two originating from pointer additions reach here and are more efficiently done by genMultLit(). + (aopInReg (result->aop, 0, X_IDX) || (optimize.codeSpeed || !regDead (A_IDX, ic)) && aopInReg (result->aop, 0, Y_IDX)) && + result->aop->size == 2 && left->aop->size == 1 && right->aop->type == AOP_LIT && + (aopIsLitVal (right->aop, 0, 1, 4) || (optimize.codeSpeed || !regDead (A_IDX, ic)) && aopIsLitVal (right->aop, 0, 1, 8))) + { + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); + genMultLit (ic); + return; + } + + if (left->aop->size > 1 || right->aop->size > 1 || result->aop->size > 2) + wassertl (0, "Large multiplication is handled through support function calls."); + + /* Swap if left is literal or right is in A. */ + if (aopInReg (left->aop, 0, A_IDX) || aopInReg (right->aop, 0, XL_IDX) || aopInReg (right->aop, 0, YL_IDX) && !aopInReg (result->aop, 0, X_IDX)) // todo: Swap in more cases when right in reg, left not. + { + operand *t = right; + right = left; + left = t; + } + + use_y = aopInReg (result->aop, 0, Y_IDX) || aopInReg (left->aop, 0, YL_IDX) && !aopInReg (result->aop, 0, X_IDX); + + if (!regDead (use_y ? Y_IDX : X_IDX, ic)) + push (use_y ? ASMOP_Y : ASMOP_X, 0, 2); + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + cheapMove (use_y ? ASMOP_Y : ASMOP_X, 0, left->aop, 0, aopInReg (right->aop, 0, A_IDX)); + cheapMove (ASMOP_A, 0, right->aop, 0, TRUE); + + emit2 ("mul", use_y ? "y, a" : "x, a"); + cost (1 + use_y, 4); + + genMove (result->aop, use_y ? ASMOP_Y : ASMOP_X, TRUE, !use_y || regDead (X_IDX, ic), use_y || regDead (Y_IDX, ic)); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + if (!regDead (use_y ? Y_IDX : X_IDX, ic)) + { + if (result->aop->regs[use_y ? YH_IDX : XH_IDX] >= 0) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (use_y ? YL_IDX : XL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(use_y ? YL_IDX : XL_IDX); + } + else if (result->aop->regs[use_y ? YL_IDX : XL_IDX] >= 0) + { + swap_to_a (use_y ? YH_IDX : XH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(use_y ? YH_IDX : XH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (use_y ? ASMOP_Y : ASMOP_X, 0, 2); + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genDivMod2 - generates code for unsigned division */ +/* any operands and results of up to 2 bytes */ +/*-----------------------------------------------------------------*/ +static void +genDivMod2 (const iCode *ic) +{ +#if 0 + D (emit2 ("; genDivMod2", "")); +#endif + + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + if (!regDead (X_IDX, ic)) + push (ASMOP_X, 0, 2); + if (!regDead (Y_IDX, ic)) + push (ASMOP_Y, 0, 2); + + if (stm8_extend_stack) + { + if (left->aop->regs[XL_IDX] >= 0 || left->aop->regs[XH_IDX] >= 0 || right->aop->regs[A_IDX] >= 1) + { + wassert (regalloc_dry_run); + cost (180, 180); + } + + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + cheapMove (ASMOP_A, 0, right->aop, 0, TRUE); + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, right->aop, 1, TRUE); + push (ASMOP_A, 0, 1); + if (left->aop->regs[XL_IDX] >= 0 || left->aop->regs[XH_IDX] >= 0) + { + wassert (regalloc_dry_run); + cost (180, 180); + } + genMove (ASMOP_X, left->aop, TRUE, TRUE, FALSE); + pop (ASMOP_Y, 0, 2); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + else if (aopRS (left->aop) && left->aop->size >= 2 && aopRS (right->aop) && right->aop->size >= 2) + { + int i; + struct asmop cop; + cop.type = AOP_REGSTK; + cop.size = 4; + for (i = A_IDX; i <= C_IDX; i++) + cop.regs[i] = (left->aop->regs[i] >= right->aop->regs[i] + 2 ? left->aop->regs[i] : right->aop->regs[i] + 2); + cop.aopu.bytes[0] = left->aop->aopu.bytes[0]; + cop.aopu.bytes[1] = left->aop->aopu.bytes[1]; + cop.aopu.bytes[2] = right->aop->aopu.bytes[0]; + cop.aopu.bytes[3] = right->aop->aopu.bytes[1]; + genMove (ASMOP_XY, &cop, regDead (A_IDX, ic), TRUE, TRUE); + } + else if (aopRS (right->aop)) + { + if (left->aop->regs[YL_IDX] >= 0 || left->aop->regs[YH_IDX] >= 0) + { + wassert (regalloc_dry_run); + cost (180, 180); + } + genMove (ASMOP_Y, right->aop, regDead (A_IDX, ic), TRUE, TRUE); + genMove (ASMOP_X, left->aop, regDead (A_IDX, ic), TRUE, FALSE); + } + else + { + if (right->aop->regs[XL_IDX] >= 0 || right->aop->regs[XH_IDX] >= 0) + { + wassert (regalloc_dry_run); + cost (180, 180); + } + genMove (ASMOP_X, left->aop, regDead (A_IDX, ic), TRUE, TRUE); + genMove (ASMOP_Y, right->aop, regDead (A_IDX, ic), FALSE, TRUE); + } + + emit2 ("divw", "x, y"); + cost (1, 17); + + if (!stm8_extend_stack) + genMove (result->aop, ic->op == '/' ? ASMOP_X : ASMOP_Y, regDead (A_IDX, ic), TRUE, TRUE); + + if (ic->op == '%' && stm8_extend_stack) + { + emit2 ("exgw", "x, y"); + cost (1, 1); + } + + if (!regDead (Y_IDX, ic)) + { + if (result->aop->regs[YH_IDX] >= 0) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (YL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(YL_IDX); + } + else if (result->aop->regs[YL_IDX] >= 0) + { + swap_to_a (YH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(YH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (ASMOP_Y, 0, 2); + } + + if (stm8_extend_stack) + genMove (result->aop, ASMOP_X, regDead (A_IDX, ic), TRUE, FALSE); + + if (!regDead (X_IDX, ic)) + { + if (result->aop->regs[XH_IDX] >= 0) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (XL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(XL_IDX); + } + else if (result->aop->regs[XL_IDX] >= 0) + { + swap_to_a (XH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(XH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (ASMOP_X, 0, 2); + } +} + +/*-----------------------------------------------------------------*/ +/* genDivMod1 - generates code for unsigned division */ +/* left operand up to 2 bytes */ +/* right operand 1 byte */ +/* result up to 2 bytes for division, 1 byte for modulo */ +/*-----------------------------------------------------------------*/ +static void +genDivMod1 (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + bool use_y; + + use_y = ic->op == '/' && aopInReg (result->aop, 0, Y_IDX) || aopInReg (left->aop, 0, YL_IDX) && !(ic->op == '/' && aopInReg (result->aop, 0, X_IDX)); + + if (!regDead (use_y ? Y_IDX : X_IDX, ic)) + push (use_y ? ASMOP_Y : ASMOP_X, 0, 2); + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + if (!use_y && aopInReg (right->aop, 0, XL_IDX) && aopOnStack (left->aop, 0, 1)) + { + cheapMove (ASMOP_X, 1, ASMOP_ZERO, 0, false); + cheapMove (ASMOP_A, 0, left->aop, 0, false); + emit2 ("exg", "a, xl"); + cost (1, 1); + } + else if (aopInReg (right->aop, 0, use_y ? YL_IDX : XL_IDX) || aopInReg (right->aop, 0, use_y ? YH_IDX : XH_IDX)) + { + cheapMove (ASMOP_A, 0, right->aop, 0, false); + genMove_o (use_y ? ASMOP_Y : ASMOP_X, 0, left->aop, 0, 2, false, false, false); + } + else + { + genMove_o (use_y ? ASMOP_Y : ASMOP_X, 0, left->aop, 0, 2, right->aop->regs[A_IDX] < 0, false, false); + cheapMove (ASMOP_A, 0, right->aop, 0, false); + } + + emit2 ("div", use_y ? "y, a" : "x, a"); + cost (1 + use_y, 17); + + genMove_o (result->aop, 0, ic->op == '/' ? (use_y ? ASMOP_Y : ASMOP_X) : ASMOP_A, 0, result->aop->size, TRUE, !use_y || regDead(X_IDX, ic), use_y || regDead(Y_IDX, ic)); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + if (!regDead (use_y ? Y_IDX : X_IDX, ic)) + { + if (result->aop->regs[use_y ? YH_IDX : XH_IDX] >= 0) + { + adjustStack (1, FALSE, FALSE, FALSE); + swap_to_a (use_y ? YL_IDX : XL_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(use_y ? YL_IDX : XL_IDX); + } + else if (result->aop->regs[use_y ? YL_IDX : XL_IDX] >= 0) + { + swap_to_a (use_y ? YH_IDX : XH_IDX); + pop (ASMOP_A, 0, 1); + swap_from_a(use_y ? YH_IDX : XH_IDX); + adjustStack (1, FALSE, FALSE, FALSE); + } + else + pop (use_y ? ASMOP_Y : ASMOP_X, 0, 2); + } +} + +/*-----------------------------------------------------------------*/ +/* genDivMod - generates code for unsigned division */ +/*-----------------------------------------------------------------*/ +static void +genDivMod (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genDivMod", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + if (result->aop->size <= (ic->op == '/' ? 2 : 1) && left->aop->size <= 2 && right->aop->size <= 1) + genDivMod1(ic); + else + genDivMod2(ic); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for minus */ +/*-----------------------------------------------------------------*/ +static void +genMinus (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + + D (emit2 ("; genMinus", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + genSub (ic, result->aop, left->aop, right->aop); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* exchangedCmp : returns the opcode need if the two operands are */ +/* exchanged in a comparison */ +/*-----------------------------------------------------------------*/ +static int +exchangedCmp (int opcode) +{ + switch (opcode) + { + case '<': + return '>'; + case '>': + return '<'; + case LE_OP: + return GE_OP; + case GE_OP: + return LE_OP; + case NE_OP: + return NE_OP; + case EQ_OP: + return EQ_OP; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + return EQ_OP; /* shouldn't happen, but need to return something */ +} + +/*------------------------------------------------------------------*/ +/* branchInstCmp : returns the conditional branch instruction that */ +/* will branch if the comparison is true */ +/*------------------------------------------------------------------*/ +static char * +branchInstCmp (int opcode, int sign, bool negated) +{ + if (negated) + switch (opcode) + { + case '<': + opcode = GE_OP; + break; + case '>': + opcode = LE_OP; + break; + case LE_OP: + opcode = '>'; + break; + case GE_OP: + opcode = '<'; + break; + case NE_OP: + opcode = EQ_OP; + break; + case EQ_OP: + opcode = NE_OP; + break; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + + switch (opcode) + { + case '<': + if (sign) + return "jrslt"; + else + return "jrc"; + case '>': + if (sign) + return "jrsgt"; + else + return "jrugt"; + case LE_OP: + if (sign) + return "jrsle"; + else + return "jrule"; + case GE_OP: + if (sign) + return "jrsge"; + else + return "jrnc"; + case NE_OP: + return "jrne"; + case EQ_OP: + return "jreq"; + default: + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "opcode not a comparison"); + } + return "brn"; +} + +/*------------------------------------------------------------------*/ +/* genCmp :- greater or less than (and maybe with equal) comparison */ +/* Handles cases where the decision can be made based on top bytes. */ +/*------------------------------------------------------------------*/ +static int +genCmpTop (operand *left, operand *right, operand *result, const iCode *ic) +{ + sym_link *letype, *retype; + int sign, opcode; + int size; + int ret = 0; + + D (emit2 ("; genCmpTop", "")); + + if (left->aop->type != AOP_LIT && right->aop->type != AOP_LIT) + return 0; + + opcode = ic->op; + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + size = max (left->aop->size, right->aop->size); + + if (left->aop->type == AOP_LIT) + { + operand *temp = left; + wassert (right->aop->type != AOP_LIT); + left = right; + right = temp; + opcode = exchangedCmp (opcode); + } + wassert (right->aop->type == AOP_LIT); + + if ((size >= 2 && !sign && aopIsLitVal (right->aop, 0, size - 1, ~0) && aopIsLitVal (right->aop, size - 1, 1, 0x00) && opcode == '>')) + { + if (aopInReg (left->aop, size - 1, A_IDX) || aopOnStack (left->aop, size - 1, 1) || left->aop->type == AOP_DIR) + { + emit3_o (A_TNZ, left->aop, size - 1, 0, 0); + ret = 20; + } + else if (size > 2 || + (!aopInReg (left->aop, 0, X_IDX) && !aopInReg (left->aop, 0, Y_IDX) && (regDead (A_IDX, ic) || !regDead (X_IDX, ic)))) // When we can use tnzw moving to A costs more than we save by skipping a byte. + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + cheapMove (ASMOP_A, 0, left->aop, size - 1, FALSE); + emit3 (A_TNZ, ASMOP_A, NULL); + ret = 20; + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + } + else if (size >= 3 && !sign && aopIsLitVal (right->aop, 0, size - 2, ~0) && aopIsLitVal (right->aop, size - 2, 2, 0x0000) && opcode == '>') + { + if (aopInReg (left->aop, 2, X_IDX) || aopInReg (left->aop, 2, XH_IDX) && aopInReg (left->aop, 3, XL_IDX)) + emit3w (A_TNZW, ASMOP_X, NULL); + else if (aopInReg (left->aop, 2, Y_IDX) || aopInReg (left->aop, 2, YH_IDX) && aopInReg (left->aop, 3, YL_IDX)) + emit3w (A_TNZW, ASMOP_Y, NULL); + else if (regDead (X_IDX, ic) && (aopOnStackNotExt (left->aop, size - 2, 2) || left->aop->type == AOP_DIR)) + { + emit2 ("ldw", "x, %s", aopGet2 (left->aop, size - 2)); + cost (2 + (left->aop->type == AOP_DIR), 2); + } + else if (regDead (X_IDX, ic)) + { + genMove_o (ASMOP_X, 0, left->aop, size - 2, 2, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + emit3w (A_TNZW, ASMOP_X, NULL); + } + else if (size >= 2 && regDead (Y_IDX, ic) && (aopOnStackNotExt (left->aop, size - 2, 2) || left->aop->type == AOP_DIR)) + { + emit2 ("ldw", "y, %s", aopGet2 (left->aop, size - 2)); + cost (2 + 2 * (left->aop->type == AOP_DIR), 2); + } + else if (regDead (Y_IDX, ic)) + { + genMove_o (ASMOP_Y, 0, left->aop, size - 2, 2, regDead (A_IDX, ic), regDead (X_IDX, ic), TRUE); + emit3w (A_TNZW, ASMOP_Y, NULL); + } + else + { + push (ASMOP_X, 0, 2); + genMove_o (ASMOP_X, 0, left->aop, size - 2, 2, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + emit3w (A_TNZW, ASMOP_X, NULL); + pop (ASMOP_X, 0, 2); + } + ret = 20; + } + else if (sign && aopIsLitVal (right->aop, 0, size, 0) && opcode == '<') + { + if (aopInReg (left->aop, size - 1, A_IDX) || aopOnStack (left->aop, size - 1, 1) || left->aop->type == AOP_DIR) + emit3_o (A_TNZ, left->aop, size - 1, 0, 0); + else if (size >= 2 && aopInReg (left->aop, size - 2, X_IDX)) + emit3w (A_TNZW, ASMOP_X, NULL); + else if (size >= 2 && aopInReg (left->aop, size - 2, Y_IDX)) + emit3w (A_TNZW, ASMOP_Y, NULL); + else if (size >= 2 && regDead (X_IDX, ic)) + { + genMove_o (ASMOP_X, 0, left->aop, size - 2, 2, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + emit3w (A_TNZW, ASMOP_X, NULL); + } + else if (size >= 2 && regDead (Y_IDX, ic) && (aopOnStackNotExt (left->aop, size - 2, 2) || left->aop->type == AOP_DIR)) + { + emit2 ("ldw", "y, %s", aopGet2 (left->aop, size - 2)); + cost (2 + 2 * (left->aop->type == AOP_DIR), 2); + } + else if (size >= 2 && regDead (Y_IDX, ic)) + { + genMove_o (ASMOP_Y, 0, left->aop, size - 2, 2, regDead (A_IDX, ic), regDead (X_IDX, ic), TRUE); + emit3w (A_TNZW, ASMOP_Y, NULL); + } + else + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + cheapMove (ASMOP_A, 0, left->aop, size - 1, FALSE); + emit3 (A_TNZ, ASMOP_A, NULL); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + ret = 10; + } + + return ret; +} + +/*------------------------------------------------------------------*/ +/* genCmp :- greater or less than (and maybe with equal) comparison */ +/*------------------------------------------------------------------*/ +static void +genCmp (const iCode *ic, iCode *ifx) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + sym_link *letype, *retype; + int sign, opcode; + int size, i; + bool exchange = FALSE; + int special = 0; + + D (emit2 ("; genCmp", "")); + + opcode = ic->op; + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + size = max (left->aop->size, right->aop->size); + + /* Prefer literal operand on right */ + if (left->aop->type == AOP_LIT || + right->aop->type != AOP_LIT && left->aop->type == AOP_DIR || + (aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 0, X_IDX) || aopInReg (right->aop, 0, Y_IDX)) && left->aop->type == AOP_STK) + exchange = TRUE; + + /* Right operand is a special literal */ + if ((special = genCmpTop(left, right, result, ic)) > 0) + goto _genCmp_1; + + /* Cannot do multibyte signed comparison, except for 2-byte using cpw */ + if (size > 1 && !(size == 2 && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || right->aop->type == AOP_STK))) + { + if (exchange && (opcode == '<' || opcode == GE_OP)) + exchange = FALSE; + if (!exchange && (opcode == '>' || opcode == LE_OP)) + exchange = TRUE; + } + + if (exchange) + { + operand *temp = left; + left = right; + right = temp; + opcode = exchangedCmp (opcode); + } + + if (size == 1 && + (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || right->aop->type == AOP_STK) && + aopInReg (left->aop, 0, A_IDX)) + emit3 (A_CP, ASMOP_A, right->aop); + else if (size == 2 && (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || right->aop->type == AOP_STK)) + { + if (aopInReg (left->aop, 0, Y_IDX) && right->aop->type == AOP_STK) + { + if (regDead (X_IDX, ic) && regDead (Y_IDX, ic)) + { + emit2 ("ldw", "x, y"); + emit2 ("cpw", "x, %s", aopGet2 (right->aop, 0)); + cost (3, 3); + } + else + { + emit2 ("exgw", "x, y"); + emit2 ("cpw", "x, %s", aopGet2 (right->aop, 0)); + emit2 ("exgw", "x, y"); + cost (4, 4); + } + } + else + { + bool save_x = !regDead (X_IDX, ic) && !aopInReg (left->aop, 0, X_IDX) && !aopInReg (left->aop, 0, Y_IDX); + if (save_x) + push (ASMOP_X, 0, 2); + + if (!aopInReg (left->aop, 0, Y_IDX)) + genMove (ASMOP_X, left->aop, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + + emit2 ("cpw", aopInReg (left->aop, 0, Y_IDX) ? "y, %s" : "x, %s", aopGet2 (right->aop, 0)); + cost (3 + aopInReg (left->aop, 0, Y_IDX), 2); + + if (save_x) + pop (ASMOP_X, 0, 2); + } + } + else + { + bool pushed_a = false; + bool started = false; + + for (i = 0; i < size; i++) + if (i && aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + break; + } + + for (i = 0, started = false; i < size; i++) + { + const asmop *right_stacked = NULL; + int right_offset; + + if (!started && aopIsLitVal (right->aop, i, 2, 0) && (i + 1 < size)) // Skip over trailing 0x0000. + { + i++; + continue; + } + + if (!started && (aopInReg (left->aop, i, X_IDX) || aopInReg (left->aop, i, Y_IDX) && !aopOnStack(right->aop, i, 2)) && + (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || aopOnStack(right->aop, i, 2))) + { + bool x = aopInReg (left->aop, i, X_IDX); + emit2 ("cpw", x ? "x, %s" : "y, %s", aopGet2 (right->aop, i)); + cost ((x ? 3 : 4) - aopOnStack(right->aop, i, 2), 2); + i++; + started = true; + continue; + } + else if (!started && i + 1 < size && regDead (X_IDX, ic) && left->aop->regs[XL_IDX] < i && left->aop->regs[XH_IDX] < i && right->aop->regs[XL_IDX] < i && right->aop->regs[XH_IDX] < i && + (left->aop->type == AOP_LIT || left->aop->type == AOP_DIR || aopOnStack(left->aop, i, 2)) && + (right->aop->type == AOP_LIT || right->aop->type == AOP_DIR || aopOnStack(right->aop, i, 2))) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, regDead (A_IDX, ic) && left->aop->regs[A_IDX] <= i && right->aop->regs[A_IDX] < i, TRUE, FALSE); + emit2 ("cpw", "x, %s", aopGet2 (right->aop, i)); + cost (3 - aopOnStack(right->aop, i, 2), 2); + i++; + started = true; + continue; + } + + if (!regDead (A_IDX, ic) && !pushed_a && !aopInReg (left->aop, i, A_IDX)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + if (i && aopInReg (left->aop, i, A_IDX) && regDead (A_IDX, ic) && pushed_a) + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + else if (i && aopInReg (left->aop, i, A_IDX) && pushed_a) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + + right_stacked = stack_aop (right->aop, i, &right_offset); + + if (right_stacked || aopInReg (right->aop, i, A_IDX)) + { + emit2 (started ? "sbc" : "cp", "a, (%d, sp)", right_stacked ? right_offset : 1); + cost (2, 1); + } + else + emit3_o (started ? A_SBC : A_CP, ASMOP_A, 0, right->aop, i); + started = true; + + if (right_stacked) + pop (right_stacked, 0, 2); + } + + if (!regDead (A_IDX, ic) && pushed_a) + pop (ASMOP_A, 0, 1); + else if (pushed_a) + adjustStack (1, FALSE, FALSE, FALSE); + } + +_genCmp_1: + if (!special && !strcmp(branchInstCmp (opcode, sign, FALSE), "jrc") && !ifx && (aopInReg (result->aop, 0, A_IDX) || regDead (A_IDX, ic))) + { + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_RLC, ASMOP_A, 0); + cheapMove (result->aop, 0, ASMOP_A, 0, FALSE); + } + else if (!ifx) + { + symbol *tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + if (tlbl1) + switch (special) + { + case 10: /* special cases by genCmpTop () */ + emit2 ("jrmi", "%05d$", labelKey2num (tlbl1->key)); + break; + case 20: /* special cases by genCmpTop () */ + emit2 ("jrne", "%05d$", labelKey2num (tlbl1->key)); + break; + default: /* normal cases */ + emit2 (branchInstCmp (opcode, sign, FALSE), "%05d$", labelKey2num (tlbl1->key)); + break; + } + cost (2, 0); + cheapMove (result->aop, 0, ASMOP_ZERO, 0, !regDead (A_IDX, ic)); + emitJP (tlbl2, 1.0f); + emitLabel (tlbl1); + cheapMove (result->aop, 0, ASMOP_ONE, 0, !regDead (A_IDX, ic)); + emitLabel (tlbl2); + } + else + { + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + if (tlbl) + switch (special) + { + case 10: /* special cases by genCmpTop () */ + emit2 (IC_TRUE (ifx) ? "jrpl" : "jrmi", "%05d$", labelKey2num (tlbl->key)); + break; + case 20: /* special cases by genCmpTop () */ + emit2 (IC_TRUE (ifx) ? "jreq" : "jrne", "%05d$", labelKey2num (tlbl->key)); + break; + default: /* normal cases */ + emit2 (branchInstCmp (opcode, sign, IC_TRUE (ifx) ? TRUE : FALSE), "%05d$", labelKey2num (tlbl->key)); + break; + } + cost (2, 0); + emitJP (IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx), 1.0f); + emitLabel (tlbl); + if (!regalloc_dry_run) + ifx->generated = 1; + } + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEQorNE - equal or not equal comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpEQorNE (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int opcode; + int size, i; + symbol *tlbl_NE_pop = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl_NE = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + bool pushed_a = FALSE, pop_a = FALSE; + int pushed; + + D (emit2 ("; genCmpEQorNE", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + opcode = ic->op; + + pushed = G.stack.pushed; + + /* assign the amsops */ + aopOp (left, ic); + aopOp (right, ic); + aopOp (result, ic); + + size = max (left->aop->size, right->aop->size); + + for (i = 0; i < size;) + { + /* Prefer literal operand on right */ + if (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD || + right->aop->type != AOP_LIT && right->aop->type != AOP_IMMD && left->aop->type == AOP_DIR || + aopInReg (right->aop, i, A_IDX) && aopOnStack (left->aop, i, 1) || + (aopInReg (right->aop, i, X_IDX) || aopInReg (right->aop, i, Y_IDX)) && aopOnStack (left->aop, i, 2)) + { + operand *temp = left; + left = right; + right = temp; + } + + if (i <= size - 2 && (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD || right->aop->type == AOP_DIR || aopOnStack (right->aop, i, 2)) && !((aopInReg(left->aop, i, A_IDX) || aopInReg(left->aop, i + 1, A_IDX))&& pushed_a)) + { + bool x_dead = regDead (X_IDX, ic) && left->aop->regs[XL_IDX] <= i + 1 && left->aop->regs[XH_IDX] <= i + 1 && right->aop->regs[XL_IDX] <= i + 1 && right->aop->regs[XH_IDX] <= i + 1; + bool y_dead = regDead (Y_IDX, ic) && left->aop->regs[YL_IDX] <= i + 1 && left->aop->regs[YH_IDX] <= i + 1 && right->aop->regs[YL_IDX] <= i + 1 && right->aop->regs[YH_IDX] <= i + 1; + + /* Try to use flag setting from ldw */ + if((aopOnStackNotExt (left->aop, i, 2) || left->aop->type == AOP_DIR) && + right->aop->type == AOP_LIT && aopIsLitVal (right->aop, i, 2, 0x0000) && + (x_dead || y_dead)) + { + emit2 ("ldw", x_dead ? "x, %s" : "y, %s", aopGet2 (left->aop, i)); + cost (2 + (left->aop->type == AOP_DIR) * (2 - x_dead), 2); + } + else if (aopInReg (left->aop, i, Y_IDX) && aopOnStack (right->aop, i, 2)) + { + if (x_dead) + { + emit2 ("ldw", "x, y"); + emit2 ("cpw", "x, %s", aopGet2 (right->aop, i)); + cost (3, 3); + } + else + { + emit2 ("exgw", "x, y"); + emit2 ("cpw", "x, %s", aopGet2 (right->aop, i)); + emit2 ("exgw", "x, y"); + cost (4, 4); + } + } + else + { + bool cmp_y = aopInReg (left->aop, i, Y_IDX); + if (!cmp_y && !x_dead && !aopInReg (left->aop, i, X_IDX)) + push (ASMOP_X, 0, 2); + genMove_o (aopInReg (left->aop, i, Y_IDX) ? ASMOP_Y : ASMOP_X, 0, left->aop, i, 2, regDead (A_IDX, ic) && left->aop->regs[A_IDX] <= i + 1 && right->aop->regs[A_IDX] <= i + 1, TRUE, FALSE); + if (right->aop->type == AOP_LIT && aopIsLitVal (right->aop, i, 2, 0x0000)) + emit3w (A_TNZW, cmp_y ? ASMOP_Y : ASMOP_X, 0); + else if (right->aop->type == AOP_LIT && + (!cmp_y && (x_dead || !aopInReg (left->aop, i, X_IDX)) || cmp_y && regDead (Y_IDX, ic)) && + (aopIsLitVal (right->aop, i, 2, 0x0001) || aopIsLitVal (right->aop, i, 2, 0xffff))) + emit3w (aopIsLitVal (right->aop, i, 2, 0x0001) ? A_DECW : A_INCW, cmp_y ? ASMOP_Y : ASMOP_X, 0); + else + { + emit2 ("cpw", cmp_y ? "y, %s" : "x, %s", aopGet2 (right->aop, i)); + cost (3 + cmp_y, 2); + } + + if (!cmp_y && !x_dead && !aopInReg (left->aop, i, X_IDX)) + pop (ASMOP_X, 0, 2); + } + + i += 2; + } + else if (right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD || right->aop->type == AOP_DIR || aopOnStack (right->aop, i, 1)) + { + if ((!regDead (A_IDX, ic) && !aopInReg (left->aop, i, A_IDX) || left->aop->regs[A_IDX] > i || right->aop->regs[A_IDX] > i) && !pushed_a) // Todo: Test A early instead! + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + else if (aopInReg (left->aop, i, A_IDX) && pushed_a) + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + + if (right->aop->type == AOP_LIT && + !(aopInReg (left->aop, i, A_IDX) && !regDead (A_IDX, ic)) && + (aopIsLitVal (right->aop, i, 1, 0x01) || aopIsLitVal (right->aop, i, 1, 0xff))) + emit3 (aopIsLitVal (right->aop, i, 1, 0x01) ? A_DEC : A_INC, ASMOP_A, 0); + else + emit3_o (A_CP, ASMOP_A, 0, right->aop, i); + + i++; + } + else + { + if (!regalloc_dry_run) + { + fprintf(stderr, "ltype %d, lsize %d, rtype %d, rsize %d\n", left->aop->type, left->aop->size, right->aop->type, right->aop->size); + wassertl (0, "Unimplemented comparison operands."); + } + cost (180, 180); + + i++; + } + + if (size == 1 && pushed_a) // Popping it here once now is cheaper than doing it in multiple places later. + { + pop (ASMOP_A, 0, 1); + pushed_a = FALSE; + } + + if (pushed_a) + { + if (tlbl_NE_pop) + emit2 ("jrne", "%05d$", labelKey2num (tlbl_NE_pop->key)); + pop_a = TRUE; + } + else if (tlbl_NE) + emit2 ("jrne", "%05d$", labelKey2num (tlbl_NE->key)); + cost (2, 2); // Cycle cost is an estimate. + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + + wassertl (result->aop->size == 1 || ifx, "Unimplemented result size."); + + if (!ifx) + { + cheapMove (result->aop, 0, opcode == EQ_OP ? ASMOP_ONE : ASMOP_ZERO, 0, !regDead (A_IDX, ic)); + emitJP(tlbl, 0.0f); + if (pop_a) + { + emitLabel (tlbl_NE_pop); + pop (ASMOP_A, 0, 1); + } + emitLabel (tlbl_NE); + cheapMove (result->aop, 0, opcode == NE_OP ? ASMOP_ONE : ASMOP_ZERO, 0, !regDead (A_IDX, ic)); + emitLabel (tlbl); + } + else if (IC_TRUE (ifx) && opcode == EQ_OP || IC_FALSE (ifx) && opcode == NE_OP) + { + emitJP(IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx), 0.0f); + if (pop_a) + { + emitLabel (tlbl_NE_pop); + pop (ASMOP_A, 0, 1); + } + emitLabel (tlbl_NE); + if (!regalloc_dry_run) + ifx->generated = 1; + } + else + { + emitJP(tlbl, 0.0f); + if (pop_a) + { + emitLabel (tlbl_NE_pop); + pop (ASMOP_A, 0, 1); + } + emitLabel (tlbl_NE); + emitJP(IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx), 0.0f); + emitLabel (tlbl); + if (!regalloc_dry_run) + ifx->generated = 1; + } + + G.stack.pushed = pushed; + updateCFA (); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for or */ +/*-----------------------------------------------------------------*/ +static void +genXor (const iCode *ic) +{ + operand *left, *right, *result; + int size, i, j, omitbyte = -1; + bool result_in_a = false; + bool pushed_a = false; + + D (emit2 ("; genXor", "")); + + aopOp ((left = IC_LEFT (ic)), ic); + aopOp ((right = IC_RIGHT (ic)), ic); + aopOp ((result = IC_RESULT (ic)), ic); + + size = getSize (operandType (result)); + + /* Prefer literal operand on right */ + if (left->aop->type == AOP_LIT || + right->aop->type != AOP_LIT && left->aop->type == AOP_DIR || + (aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 0, X_IDX) || aopInReg (right->aop, 0, Y_IDX)) && left->aop->type == AOP_STK) + { + operand *temp = left; + left = right; + right = temp; + } + + // todo: Use bit complement instructions where it is faster. + if (!regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + + // Byte in a needs to be handled first. + for (i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX)) + { + const asmop *other_stacked = 0; + int other_offset; + asmop *other = (aopInReg (left->aop, i, A_IDX) ? right : left)->aop; + + other_stacked = stack_aop (other, i, &other_offset); + + if (aopIsLitVal (right->aop, i, 1, 0)) + ; + else if (aopIsLitVal (right->aop, i, 1, 0xff)) + emit3 (A_CPL, ASMOP_A, 0); + else if (!other_stacked) + emit3_o (A_XOR, ASMOP_A, 0, other, i); + else + { + emit2 ("xor", "a, (%d, sp)", other_offset); + cost (2, 1); + } + omitbyte = i; + + if (other_stacked) + pop (other_stacked, 0, 2); + + if (aopInReg (result->aop, i, A_IDX) && size > 1) + result_in_a = true; + else + { + // Avoid overwriting operand. + if (aopRS (result->aop) && !aopOnStack (result->aop, i, 1)) + for (j = 0; j < size; j++) + { + if (i == j) + continue; + if (j < left->aop->size && aopRS (left->aop) && !aopOnStack (left->aop, j, 1) && + left->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx || + j < right->aop->size && aopRS (right->aop) && !aopOnStack (right->aop, j, 1) && + right->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx) + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented xor operand."); + cost (180, 180); + } + } + + cheapMove (result->aop, i, ASMOP_A, 0, false); + } + break; + } + + for (i = 0; i < size; i++) + { + const asmop *right_stacked = 0; + int right_offset; + + if (omitbyte == i) + continue; + + if (aopIsLitVal (right->aop, i, 1, 0)) + { + cheapMove (result->aop, i, left->aop, i, result_in_a); + if (aopInReg (result->aop, i, A_IDX)) + result_in_a = true; + continue; + } + + if (left->aop->type == AOP_DIR && aopSame (left->aop, i, result->aop, i, 1) && + right->aop->type == AOP_LIT && isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) >= 0) + { + emit2 ("bcpl", "%s, #%d", aopGet (left->aop, i), isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i))); + continue; + } + + right_stacked = stack_aop (right->aop, i, &right_offset); + + if (result_in_a) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + result_in_a = false; + } + + cheapMove (ASMOP_A, 0, left->aop, i, false); + + if (aopIsLitVal (right->aop, i, 1, 0xff)) + emit3 (A_CPL, ASMOP_A, 0); + else if (!right_stacked && !(i && aopInReg (right->aop, i, A_IDX))) + emit3_o (A_XOR, ASMOP_A, 0, right->aop, i); + else + { + emit2 ("xor", "a, (%d, sp)", right_offset); + cost (2, 1); + } + + if (right_stacked) + pop (right_stacked, 0, 2); + + if (!aopInReg (result->aop, i, A_IDX)) + cheapMove (result->aop, i, ASMOP_A, 0, false); + else + result_in_a = true; + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + + freeAsmop (left); + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (const iCode *ic) +{ + operand *left, *right, *result; + int size, i, j, omitbyte = -1; + bool result_in_a = FALSE; + bool pushed_a = FALSE; + + D (emit2 ("; genOr", "")); + + aopOp ((left = IC_LEFT (ic)), ic); + aopOp ((right = IC_RIGHT (ic)), ic); + aopOp ((result = IC_RESULT (ic)), ic); + + size = getSize (operandType (result)); + + /* Prefer literal operand on right */ + if (left->aop->type == AOP_LIT || + right->aop->type != AOP_LIT && left->aop->type == AOP_DIR || + (aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 0, X_IDX) || aopInReg (right->aop, 0, Y_IDX)) && left->aop->type == AOP_STK) + { + operand *temp = left; + left = right; + right = temp; + } + + // todo: Use bit set instructions where it is faster. + if (!regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + // Byte in a needs to be handled first. + for (i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX)) + { + const asmop *other_stacked = NULL; + int other_offset; + asmop *other = (aopInReg (left->aop, i, A_IDX) ? right : left)->aop; + + other_stacked = stack_aop (other, i, &other_offset); + + if (aopIsLitVal (right->aop, i, 1, 0) || aopInReg (other, i, A_IDX)) + ; + else if (!other_stacked) + emit3_o (A_OR, ASMOP_A, 0, other, i); + else + { + emit2 ("or", "a, (%d, sp)", other_offset); + cost (2, 1); + } + omitbyte = i; + + if (other_stacked) + pop (other_stacked, 0, 2); + + if (aopInReg (result->aop, i, A_IDX) && size > 1) + result_in_a = TRUE; + else + { + // Avoid overwriting operand. + if (aopRS (result->aop) && !aopOnStack (result->aop, i, 1)) + for (j = 0; j < size; j++) + { + if (i == j) + continue; + if (j < left->aop->size && aopRS (left->aop) && !aopOnStack (left->aop, j, 1) && + left->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx || + j < right->aop->size && aopRS (right->aop) && !aopOnStack (right->aop, j, 1) && + right->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx) + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented or operand."); + cost (180, 180); + } + } + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + } + + break; + } + + for (i = 0; i < size;) + { + const asmop *right_stacked = NULL; + int right_offset; + + if (omitbyte == i) + i++; + else if (aopIsLitVal (right->aop, i, 2, 0x8000) && aopInReg (result->aop, i, X_IDX) && (aopOnStack (left->aop, i, 2) || left->aop->type == AOP_IMMD)) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, TRUE, TRUE, regFree (Y_IDX, ic)); + emit3w (A_SLLW, ASMOP_X, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3w (A_RRCW, ASMOP_X, 0); + i += 2; + } + else if (aopIsLitVal (right->aop, i, 2, 0x0001) && aopInReg (result->aop, i, X_IDX) && (aopOnStack (left->aop, i, 2) || left->aop->type == AOP_IMMD)) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, TRUE, TRUE, regFree (Y_IDX, ic)); + emit3w (A_SRLW, ASMOP_X, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3w (A_RLCW, ASMOP_X, 0); + i += 2; + } + else if (aopIsLitVal (right->aop, i, 1, 0x80) && (aopInReg (result->aop, i, XH_IDX) && aopInReg (left->aop, i, XH_IDX) || aopInReg (result->aop, i, YH_IDX) && aopInReg (left->aop, i, YH_IDX))) + { + emit3w (A_SLLW, aopInReg (result->aop, i, XH_IDX) ? ASMOP_X : ASMOP_Y, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3w (A_RRCW, aopInReg (result->aop, i, XH_IDX) ? ASMOP_X : ASMOP_Y, 0); + i++; + } + else if (aopIsLitVal (right->aop, i, 1, 0x01) && (aopInReg (result->aop, i, XL_IDX) && aopInReg (left->aop, i, XL_IDX) || aopInReg (result->aop, i, YL_IDX) && aopInReg (left->aop, i, YL_IDX))) + { + emit3w (A_SRLW, aopInReg (result->aop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3w (A_RLCW, aopInReg (result->aop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + i++; + } + else if (aopIsLitVal (right->aop, i, 1, 0x00)) // If long sequences of 0x00 are common, we should use genMove_o instead. + { + cheapMove (result->aop, i, left->aop, i, result_in_a && !pushed_a); + if (aopInReg (result->aop, i, A_IDX) && i != size - 1) + result_in_a = TRUE; + i++; + } + else if (aopOnStack (left->aop, i, 1) && aopOnStack (result->aop, i, 1) && result->aop->aopu.bytes[i].byteu.stk == left->aop->aopu.bytes[i].byteu.stk && aopIsLitVal (right->aop, i, 1, 0x80)) + { + emit3_o (A_SLL, left->aop, i, 0, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3_o (A_RRC, left->aop, i, 0, 0); + i++; + } + else if (aopOnStack (left->aop, i, 1) && aopOnStack (result->aop, i, 1) && result->aop->aopu.bytes[i].byteu.stk == left->aop->aopu.bytes[i].byteu.stk && aopIsLitVal (right->aop, i, 1, 0x01)) + { + emit3_o (A_SRL, left->aop, i, 0, 0); + emit2 ("scf", ""); + cost (1, 1); + emit3_o (A_RLC, left->aop, i, 0, 0); + i++; + } + else if (left->aop->type == AOP_DIR && aopSame (left->aop, i, result->aop, i, 1) && + right->aop->type == AOP_LIT && isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i)) >= 0) + { + emit2 ("bset", "%s, #%d", aopGet (left->aop, i), isLiteralBit (byteOfVal (right->aop->aopu.aop_lit, i))); + i++; + } + else + { + if (result_in_a) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + result_in_a = false; + } + + right_stacked = stack_aop (right->aop, i, &right_offset); + + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + + if (!right_stacked && !(i && aopInReg (right->aop, i, A_IDX))) + emit3_o (A_OR, ASMOP_A, 0, right->aop, i); + else + { + emit2 ("or", "a, (%d, sp)", right_offset); + cost (2, 1); + } + + if (right_stacked) + pop (right_stacked, 0, 2); + + if (!aopInReg (result->aop, i, A_IDX) || i == size - 1) + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + else + result_in_a = TRUE; + i++; + } + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + + freeAsmop (left); + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int size, i, j, omitbyte = -1; + bool pushed_a = FALSE; + bool result_in_a = FALSE; + + D (emit2 ("; genAnd", "")); + + aopOp ((left = IC_LEFT (ic)), ic); + aopOp ((right = IC_RIGHT (ic)), ic); + aopOp ((result = IC_RESULT (ic)), ic); + + size = getSize (operandType (result)); + + /* Prefer literal operand on right */ + if (left->aop->type == AOP_LIT || + right->aop->type != AOP_LIT && left->aop->type == AOP_DIR || + (aopInReg (right->aop, 0, A_IDX) || aopInReg (right->aop, 0, X_IDX) || aopInReg (right->aop, 0, Y_IDX)) && left->aop->type == AOP_STK) + { + operand *temp = left; + left = right; + right = temp; + } + + if (ifx && result->aop->type == AOP_CND) // TODO: Use sll for 0x7f, srl for 0xfe, swap for 0x08, sll for 0x40. Allow non-literal (and enable in ralloc2.cc) + { + int nonzero; + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); + + wassertl (right->aop->type == AOP_LIT, "Code generation for bitwise and can only jump on literal operands"); + + // Find the non-zero byte. + for (j = 0, nonzero = 0, i = -1; j < size; j++) + if (byteOfVal (right->aop->aopu.aop_lit, j)) + { + i = j; + nonzero++; + } + + wassertl (nonzero <= 1, "Code generation for bitwise and can handle at most one nonzero byte"); + + if (!nonzero) + goto release; + + // Try to use btjt / btjf. + if (left->aop->type == AOP_DIR && isLiteralBit (ulFromVal (right->aop->aopu.aop_lit)) >= 0) + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); + if (tlbl) + { + emit2 (IC_TRUE (ifx) ? "btjf" : "btjt", "%s, #%d, !tlabel", aopGet (left->aop, i), isLiteralBit (ulFromVal (right->aop->aopu.aop_lit)) - i * 8, labelKey2num (tlbl->key)); + emit2 (options.model == MODEL_LARGE ? "jpf" : "jp", "!tlabel", labelKey2num ((IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx))->key)); + emitLabel (tlbl); + } + cost (8, 4); // Hmm. Cost 2 or 3 for btjf? + goto release; + } + + if (byteOfVal (right->aop->aopu.aop_lit, i) == 0x80) + { + if (aopInReg (left->aop, i, XH_IDX)) + emit3w (A_TNZW, ASMOP_X, 0); + else if (aopInReg (left->aop, i, YH_IDX)) + emit3w (A_TNZW, ASMOP_Y, 0); + else if (aopInReg (left->aop, i, A_IDX) || aopOnStack (left->aop, i, 1) || left->aop->type == AOP_DIR) + emit3_o (A_TNZ, left->aop, i, 0, 0); + else + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + emit3 (A_TNZ, ASMOP_A, 0); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jrpl" : "jrmi", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + } + else if (byteOfVal (right->aop->aopu.aop_lit, i) == 0x01 && + (aopInReg (left->aop, i, XL_IDX) && regDead (X_IDX, ic) || aopInReg (left->aop, i, YL_IDX) && regDead (Y_IDX, ic))) + { + emit3w (A_SRLW, aopInReg (left->aop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jrnc" : "jrc", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + } + else if (byteOfVal (right->aop->aopu.aop_lit, i) == 0x01 && + (regDead (A_IDX, ic) || !aopInReg (left->aop, i, A_IDX))) + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + emit3 (A_SRL , ASMOP_A, 0); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jrnc" : "jrc", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + } + else if (byteOfVal (right->aop->aopu.aop_lit, i) == 0xff) + { + if (aopInReg (left->aop, i, A_IDX) || aopOnStack (left->aop, i, 1) || left->aop->type == AOP_DIR) + emit3_o (A_TNZ, left->aop, i, 0, 0); + else + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + emit3 (A_TNZ, ASMOP_A, 0); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jreq" : "jrne", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + } + else + { + if (!regDead (A_IDX, ic) && !aopInReg (left->aop, i, A_IDX)) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + emit3_o (A_BCP, ASMOP_A, 0, right->aop, i); + if (!regDead (A_IDX, ic) && !aopInReg (left->aop, i, A_IDX)) + pop (ASMOP_A, 0, 1); + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jreq" : "jrne", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + } + emitJP(IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx), 1.0f); // Hmm. Cycle cost overestimate. + emitLabel (tlbl); + goto release; + } + + // Byte in a needs to be handled first. + for (i = 0; i < size; i++) + if (aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX)) + { + const asmop *other_stacked = NULL; + int other_offset; + asmop *other = (aopInReg (left->aop, i, A_IDX) ? right : left)->aop; + + if (!regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + if (aopInReg (left->aop, i, A_IDX) && aopIsLitVal (right->aop, i, 1, 0x00)) // A is dead, it just doesn't know it yet. + break; + + other_stacked = stack_aop (other, i, &other_offset); + + if (aopIsLitVal (right->aop, i, 1, 0xff)) + ; + else if (!other_stacked) + emit3_o (A_AND, ASMOP_A, 0, other, i); + else + { + emit2 ("and", "a, (%d, sp)", other_offset); + cost (2, 1); + } + omitbyte = i; + + if (other_stacked) + pop (other_stacked, 0, 2); + + if (aopInReg (result->aop, i, A_IDX) && size > 1) + if (pushed_a) + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented and operand."); + cost (180, 180); + } + else + { + push (ASMOP_A, 0, 1); // todo: Do not push, if other bytes do not affect a (e.g. due to using clr). + pushed_a = TRUE; + } + else + { + // Avoid overwriting operand. + if (aopRS (result->aop) && !aopOnStack (result->aop, i, 1)) + for (j = 0; j < size; j++) + { + if (i == j) + continue; + if (j < left->aop->size && aopRS (left->aop) && !aopOnStack (left->aop, j, 1) && + left->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx || + j < right->aop->size && aopRS (right->aop) && !aopOnStack (right->aop, j, 1) && + right->aop->aopu.bytes[j].byteu.reg->rIdx == result->aop->aopu.bytes[i].byteu.reg->rIdx) + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented and operand."); + cost (180, 180); + } + } + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + } + + break; + } + + for (i = 0; i < size;) + { + // Cases that don't need a free a. + if (omitbyte == i) + { + i++; + continue; + } + else if (aopIsLitVal (right->aop, i, 1, 0x7f) && (aopInReg (result->aop, i, XH_IDX) && aopInReg (left->aop, i, XH_IDX) || aopInReg (result->aop, i, YH_IDX) && aopInReg (left->aop, i, YH_IDX))) + { + emit3w (A_SLLW, aopInReg (result->aop, i, XH_IDX) ? ASMOP_X : ASMOP_Y, 0); + emit3w (A_SRLW, aopInReg (result->aop, i, XH_IDX) ? ASMOP_X : ASMOP_Y, 0); + i++; + continue; + } + else if (aopIsLitVal (right->aop, i, 1, 0xfe) && (aopInReg (result->aop, i, XL_IDX) && aopInReg (left->aop, i, XL_IDX) || aopInReg (result->aop, i, YL_IDX) && aopInReg (left->aop, i, YL_IDX))) + { + emit3w (A_SRLW, aopInReg (result->aop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + emit3w (A_SLLW, aopInReg (result->aop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + i++; + continue; + } + else if (aopOnStack (left->aop, i, 1) && aopOnStack (result->aop, i, 1) && result->aop->aopu.bytes[i].byteu.stk == left->aop->aopu.bytes[i].byteu.stk && aopIsLitVal (right->aop, i, 1, 0x7f)) + { + emit3_o (A_SLL, left->aop, i, 0, 0); + emit3_o (A_SRL, left->aop, i, 0, 0); + i++; + continue; + } + else if (aopOnStack (left->aop, i, 1) && aopOnStack (result->aop, i, 1) && result->aop->aopu.bytes[i].byteu.stk == left->aop->aopu.bytes[i].byteu.stk && aopIsLitVal (right->aop, i, 1, 0xfe)) + { + emit3_o (A_SRL, left->aop, i, 0, 0); + emit3_o (A_SLL, left->aop, i, 0, 0); + i++; + continue; + } + else if (aopIsLitVal (right->aop, i, 1, 0xff) && aopSame (left->aop, i, result->aop, i, 1)) + { + i++; + continue; + } + else if (aopIsLitVal (right->aop, i, 2, 0x7fff) && aopInReg (result->aop, i, X_IDX) && (aopInReg (left->aop, i, X_IDX) || aopOnStack (left->aop, i, 2) || left->aop->type == AOP_IMMD)) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, pushed_a || (regDead (A_IDX, ic) && !result_in_a), TRUE, regFree (Y_IDX, ic)); + emit3w (A_SLLW, ASMOP_X, 0); + emit3w (A_SRLW, ASMOP_X, 0); + i += 2; + continue; + } + else if (aopIsLitVal (right->aop, i, 2, 0xffffe) && aopInReg (result->aop, i, X_IDX) && (aopInReg (left->aop, i, X_IDX) || aopOnStack (left->aop, i, 2) || left->aop->type == AOP_IMMD)) + { + genMove_o (ASMOP_X, 0, left->aop, i, 2, pushed_a || (regDead (A_IDX, ic) && !result_in_a), TRUE, regFree (Y_IDX, ic)); + emit3w (A_SRLW, ASMOP_X, 0); + emit3w (A_SLLW, ASMOP_X, 0); + i += 2; + continue; + } + else if (left->aop->type == AOP_DIR && aopSame (left->aop, i, result->aop, i, 1) && + right->aop->type == AOP_LIT && isLiteralBit (~byteOfVal (right->aop->aopu.aop_lit, i) & 0xff) >= 0) + { + emit2 ("bres", "%s, #%d", aopGet (left->aop, i), isLiteralBit (~byteOfVal (right->aop->aopu.aop_lit, i) & 0xff)); + i++; + continue; + } + + // Cases that want a free a. + if (!pushed_a && !(regDead (A_IDX, ic) && !result_in_a)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + if (aopIsLitVal (right->aop, i, 1, 0x00)) + { + bool new_in_a = FALSE; + for(j = i; j < size && j != omitbyte && aopIsLitVal (right->aop, j, 1, 0x00); j++) + if (aopInReg (result->aop, j, A_IDX)) + new_in_a = TRUE; + genMove_o (result->aop, i, ASMOP_ZERO, 0, j - i, TRUE, regFree (X_IDX, ic), regFree (Y_IDX, ic)); + result_in_a |= new_in_a; + i = j; + } + else if (aopIsLitVal (right->aop, i, 1, 0xff)) + { + bool new_in_a = FALSE; + for(j = i; j < size && j != omitbyte && aopIsLitVal (right->aop, j, 1, 0xff); j++) + if (aopInReg (result->aop, j, A_IDX)) + new_in_a = TRUE; + genMove_o (result->aop, i, left->aop, i, j - i, TRUE, regFree (X_IDX, ic), regFree (Y_IDX, ic)); + result_in_a |= new_in_a; + i = j; + } + else + { + const asmop *right_stacked = NULL; + int right_offset; + + wassert (pushed_a || regDead (A_IDX, ic) && !result_in_a); + + right_stacked = stack_aop (right->aop, i, &right_offset); + + cheapMove (ASMOP_A, 0, left->aop, i, FALSE); + + if (!right_stacked && !(i && aopInReg (right->aop, i, A_IDX))) + emit3_o (A_AND, ASMOP_A, 0, right->aop, i); + else + { + emit2 ("and", "a, (%d, sp)", right_offset); + cost (2, 1); + } + + if (right_stacked) + pop (right_stacked, 0, 2); + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + + if (aopInReg (result->aop, i, A_IDX)) + result_in_a = TRUE; + + i++; + } + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + +release: + freeAsmop (left); + freeAsmop (right); + freeAsmop (result); +} + +/*------------------------------------------------------------------*/ +/* init_shiftop - find a good place to shift in */ +/*------------------------------------------------------------------*/ +static void init_shiftop(asmop *shiftop, const asmop *result, const asmop *left, const asmop *right, const iCode *ic, bool a_needed_for_count) +{ + int i; + const int size = result->size; + unsigned int shCount = right->type == AOP_LIT ? ulFromVal (right->aopu.aop_lit) : 0; + bool all_in_reg = TRUE; + + shiftop->size = size; + shiftop->regs[A_IDX] = -1; + shiftop->regs[XL_IDX] = -1; + shiftop->regs[XH_IDX] = -1; + shiftop->regs[YL_IDX] = -1; + shiftop->regs[YH_IDX] = -1; + + for (i = 0; i < size;) + { + bool same_2_stack = aopOnStack (left, 0, 2) && aopOnStack (result, 0, 2) && left->aopu.bytes[i].byteu.stk == result->aopu.bytes[i].byteu.stk; + bool same_1_stack = aopOnStack (left, 0, 1) && aopOnStack (result, 0, 1) && left->aopu.bytes[i].byteu.stk == result->aopu.bytes[i].byteu.stk; + + if (!a_needed_for_count && aopInReg (left, i, A_IDX) && regDead (A_IDX, ic) && result->regs[A_IDX] == -1 && (size <= 1 || shCount >= 2)) + { + shiftop->aopu.bytes[i] = left->aopu.bytes[i]; + shiftop->regs[A_IDX] = i; + i++; + } + else if (aopInReg (left, i, X_IDX) && regDead (X_IDX, ic) && result->regs[XL_IDX] == -1 && result->regs[XH_IDX] == -1 && right->regs[XL_IDX] == -1 && right->regs[XH_IDX] == -1) + { + shiftop->aopu.bytes[i] = left->aopu.bytes[i]; + shiftop->aopu.bytes[i + 1] = left->aopu.bytes[i + 1]; + shiftop->regs[XL_IDX] = i; + shiftop->regs[XH_IDX] = i + 1; + i += 2; + } + else if (aopInReg (left, i, Y_IDX) && regDead (Y_IDX, ic) && result->regs[YL_IDX] == -1 && result->regs[YH_IDX] == -1 && !aopInReg (result, i, X_IDX)) + { + shiftop->aopu.bytes[i] = left->aopu.bytes[i]; + shiftop->aopu.bytes[i + 1] = left->aopu.bytes[i + 1]; + shiftop->regs[YL_IDX] = i; + shiftop->regs[YH_IDX] = i + 1; + i += 2; + } + // Try to shift in x instead of on stack. + else if ((aopOnStack (left, i, 2) || left->type == AOP_LIT) && aopOnStack (result, i, 2) && !same_2_stack && regDead (X_IDX, ic) && + shiftop->regs[XL_IDX] == -1 && shiftop->regs[XH_IDX] == -1 && + left->regs[XL_IDX] == -1 && left->regs[XH_IDX] == -1 && result->regs[XL_IDX] == -1 && result->regs[XH_IDX] == -1 && right->regs[XL_IDX] == -1 && right->regs[XH_IDX] == -1) + { + shiftop->aopu.bytes[i] = ASMOP_X->aopu.bytes[0]; + shiftop->aopu.bytes[i + 1] = ASMOP_X->aopu.bytes[1]; + shiftop->regs[XL_IDX] = i; + shiftop->regs[XH_IDX] = i + 1; + i += 2; + } + // Try to shift in y instead of on stack. + else if (size == 2 && (aopOnStack (left, i, 2) || left->type == AOP_LIT) && aopOnStack (result, i, 2) && !same_2_stack && regDead (Y_IDX, ic) && + shiftop->regs[YL_IDX] == -1 && shiftop->regs[YH_IDX] == -1 && + left->regs[YL_IDX] == -1 && left->regs[YH_IDX] == -1 && result->regs[YL_IDX] == -1 && result->regs[YH_IDX] == -1) + { + shiftop->aopu.bytes[i] = ASMOP_Y->aopu.bytes[0]; + shiftop->aopu.bytes[i + 1] = ASMOP_Y->aopu.bytes[1]; + shiftop->regs[YL_IDX] = i; + shiftop->regs[YH_IDX] = i + 1; + i += 2; + } + else if (!a_needed_for_count && size == 1 && aopOnStack (left, i, 1) && aopOnStack (result, i, 1) && !same_1_stack && regDead (A_IDX, ic) && shiftop->regs[A_IDX] == -1 && result->regs[A_IDX] == -1 && left->regs[A_IDX] == -1) // TODO: More cases. + { + shiftop->aopu.bytes[i] = ASMOP_A->aopu.bytes[0]; + shiftop->regs[A_IDX] = i; + i++; + } + else + { + shiftop->aopu.bytes[i] = result->aopu.bytes[i]; + if (result->aopu.bytes[i].in_reg) + shiftop->regs[result->aopu.bytes[i].byteu.reg->rIdx] = i; + i++; + } + } + + for (i = 0; i < size; i++) + if (!shiftop->aopu.bytes[i].in_reg) + all_in_reg = FALSE; + shiftop->type = all_in_reg ? AOP_REG : AOP_REGSTK; +} + +/*------------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count for size <= 2 */ +/*------------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand *left, operand *right, operand *result, const iCode *ic) +{ + unsigned int shCount = ulFromVal (right->aop->aopu.aop_lit); + unsigned int size, i; + + struct asmop shiftop_impl; + struct asmop *shiftop; + + D (emit2 ("; genLeftShiftLiteral", "")); + + size = getSize (operandType (result)); + + aopOp (left, ic); + aopOp (result, ic); + + if (shCount > (size * 8)) + shCount = size * 8; + + if (shCount >= (size * 8)) + { + genMove(result->aop, ASMOP_ZERO, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + } + + wassertl (size <= 2 || shCount % 8 <= 1, "Shifting of longs and long longs by non-trivial values should be handled by generic function."); + + if (shCount < 8 && aopRS (left->aop) && aopRS (result->aop)) + { + shiftop = &shiftop_impl; + init_shiftop (shiftop, result->aop, left->aop, right->aop, ic, FALSE); + genMove (shiftop, left->aop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + } + else if (size == 2 && shCount >= 8 && regDead (A_IDX, ic) && (aopInReg (left->aop, 0, XL_IDX) && aopInReg (result->aop, 0, X_IDX) || aopInReg (left->aop, 0, YL_IDX) && aopInReg (result->aop, 0, Y_IDX))) + { + shiftop = result->aop; + emit3 (A_CLR, ASMOP_A, 0); + emit3w (A_RLWA, shiftop, 0); + shCount -= 8; + } + else + { + shiftop = result->aop; + genMove_o (shiftop, shCount / 8, left->aop, 0, size - shCount / 8, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + genMove_o (shiftop, 0, ASMOP_ZERO, 0, shCount / 8, regDead (A_IDX, ic) && shiftop->regs[A_IDX] < 0, regDead (X_IDX, ic) && shiftop->regs[XL_IDX] < 0 && shiftop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && shiftop->regs[YL_IDX] < 0 && shiftop->regs[YH_IDX] < 0); + shCount %= 8; + } + + if (size == 1 && aopRS (shiftop) && shCount) + { + int std_bytes, swap_bytes = 0, mul_bytes = 0; + int std_cycles, swap_cycles = 0, mul_cycles = 0; + bool swap_possible = FALSE; + bool mul_possible = FALSE; + + if (aopInReg (shiftop, 0, A_IDX) || aopInReg (shiftop, 0, XL_IDX) && regDead (XH_IDX, ic)) + { + std_bytes = shCount; + std_cycles = shCount; + } + else if (aopOnStack (shiftop, 0, 1)) + { + std_bytes = shCount * 2; + std_cycles = shCount; + } + else if (aopInReg (shiftop, 0, YH_IDX)) + { + std_bytes = shCount * 5; + std_cycles = shCount * 3; + } + else + { + std_bytes = shCount * 3; + std_cycles = shCount * 3; + } + + if (!aopOnStack (shiftop, 0, 1)) + { + swap_bytes = shCount + !aopInReg (shiftop, 0, A_IDX) * 2 + aopInReg (shiftop, 0, YH_IDX) * 2; + swap_cycles = shCount + !aopInReg (shiftop, 0, A_IDX) * 2; + if (shCount >= 4) + { + swap_bytes -= 1; + swap_cycles -= 2; + } + swap_possible = TRUE; + } + + if (aopInReg (shiftop, 0, XL_IDX) && regDead (XH_IDX, ic) || aopInReg (shiftop, 0, YL_IDX) && regDead (YH_IDX, ic)) + { + mul_bytes = 3 + aopInReg (shiftop, 0, YL_IDX) + !regDead (A_IDX, ic) * 2; + mul_cycles = 2 + !regDead (A_IDX, ic) * 2; + mul_possible = TRUE; + } + + if (swap_possible && (swap_bytes <= std_bytes && swap_cycles <= std_cycles || swap_bytes < std_bytes && optimize.codeSize || swap_cycles < std_cycles && optimize.codeSpeed)) // swap better than std + { + if (mul_possible && (mul_bytes <= swap_bytes && mul_cycles <= swap_cycles || mul_bytes < swap_bytes && optimize.codeSize || mul_cycles < swap_cycles && optimize.codeSpeed)) // mul better than swap + goto mul; + goto swap; + } + if (mul_possible && (mul_bytes <= std_bytes && mul_cycles <= std_cycles || mul_bytes < std_bytes && optimize.codeSize || mul_cycles < std_cycles && optimize.codeSpeed)) // mul better than std + goto mul; + goto std; + +swap: + swap_to_a (shiftop->aopu.bytes[0].byteu.reg->rIdx); + if (shCount >= 4) + { + emit3 (A_SWAP, ASMOP_A, 0); + emit2 ("and", "a, #0xf0"); + cost (2, 1); + shCount -= 4; + } + while (shCount--) + emit3 (A_SLL, ASMOP_A, 0); + swap_from_a (shiftop->aopu.bytes[0].byteu.reg->rIdx); + genMove (result->aop, shiftop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + +mul: + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + emit2 ("ld", "a, #0x%02x", 1 << shCount); + cost (2, 1); + emit2 ("mul", aopInReg (shiftop, 0, YL_IDX) ? "y, a" : "x, a"); + cost (4, 1 + aopInReg (shiftop, 0, YL_IDX)); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + genMove (result->aop, shiftop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + } +std: + + while (shCount--) + for (i = 0; i < size;) + { + if (aopInReg (shiftop, i, X_IDX) || aopInReg (shiftop, i, Y_IDX)) + { + emit3w_o (i ? A_RLCW : A_SLLW, shiftop, i, 0, 0); + i += 2; + } + else if (i == size - 1 && aopInReg (shiftop, i, XL_IDX) && regDead (XH_IDX, ic) && shiftop->regs[XH_IDX] < 0) + { + emit3w_o (i ? A_RLCW : A_SLLW, ASMOP_X, 0, 0, 0); + i++; + } + else + { + int swapidx = -1; + if (aopRS (shiftop) && !aopInReg (shiftop, i, A_IDX) && shiftop->aopu.bytes[i].in_reg) + swapidx = shiftop->aopu.bytes[i].byteu.reg->rIdx; + + if (swapidx == -1) + emit3_o (i ? A_RLC : A_SLL, shiftop, i, 0, 0); + else + { + swap_to_a (swapidx); + emit3 (i ? A_RLC : A_SLL, ASMOP_A, 0); + swap_from_a (swapidx); + } + + i++; + } + } + + + genMove (result->aop, shiftop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + +release: + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (const iCode *ic) +{ + operand *left, *right, *result; + int i, size; + bool pushed_a = FALSE; + symbol *tlbl1, *tlbl2; + unsigned int iterations; + int skip_bytes = 0; + + struct asmop shiftop_impl; + struct asmop *shiftop; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic); + + /* if the shift count is known then do it + as efficiently as possible */ + if (right->aop->type == AOP_LIT && + (getSize (operandType (result)) <= 2 || + ulFromVal (right->aop->aopu.aop_lit) % 8 <= 1 || + ulFromVal (right->aop->aopu.aop_lit) >= getSize (operandType (result)) * 8 )) + { + genLeftShiftLiteral (left, right, result, ic); + freeAsmop (right); + return; + } + + D (emit2 ("; genLeftShift", "")); + + aopOp (result, ic); + aopOp (left, ic); + + if (!regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + + if (aopRS (left->aop) && aopRS (result->aop)) + { + shiftop = &shiftop_impl; + init_shiftop (shiftop, result->aop, left->aop, right->aop, ic, false); + } + else + shiftop = result->aop; + + iterations = (right->aop->type == AOP_LIT ? byteOfVal (right->aop->aopu.aop_lit, 0) : 2); + + if (right->aop->type == AOP_LIT) + { + skip_bytes = iterations / 16 * 2; + genMove_o (shiftop, skip_bytes, left->aop, 0, shiftop->size - skip_bytes, right->aop->regs[A_IDX] < 0, regDead (X_IDX, ic) && right->aop->regs[XL_IDX] < 0 && right->aop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && right->aop->regs[YL_IDX] < 0 && right->aop->regs[YH_IDX] < 0); + genMove_o (shiftop, 0, ASMOP_ZERO, 0, skip_bytes, true, regDead (X_IDX, ic) && shiftop->regs[XL_IDX] < 0 && shiftop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && shiftop->regs[YL_IDX] < 0 && shiftop->regs[YH_IDX] < 0); + iterations %= 16; + } + else + genMove (shiftop, left->aop, right->aop->regs[A_IDX] < 0, regDead (X_IDX, ic) && right->aop->regs[XL_IDX] < 0 && right->aop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && right->aop->regs[YL_IDX] < 0 && right->aop->regs[YH_IDX] < 0); + + size = result->aop->size; + + for (i = 0; i < size; i++) + { + if (aopRS (shiftop) && (!aopInReg (shiftop, i, A_IDX) || aopInReg (right->aop, 0, A_IDX)) && shiftop->aopu.bytes[i].in_reg && + right->aop->regs[shiftop->aopu.bytes[i].byteu.reg->rIdx] == 0) + { + if (!regalloc_dry_run) + wassertl (0, "Overwriting shift count"); + cost (380, 380); + } + if (aopInReg (shiftop, i, A_IDX) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + } + + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (0)); + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (0)); + + // Get shift count into a. + if (right->aop->type == AOP_LIT) + { + if (!iterations) + goto postshift; + emit2 ("ld", "a, #0x%02x", iterations); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, right->aop, 0, false); + + if (right->aop->type != AOP_LIT || aopIsLitVal (right->aop, 0, 1, 0)) + { + if (!aopOnStack (right->aop, 0, 1) && right->aop->type != AOP_DIR) + emit3 (A_TNZ, ASMOP_A, 0); + if (tlbl2) + emit2 ("jreq", "!tlabel", labelKey2num (tlbl2->key)); + cost (2, 0); + } + + emitLabel (tlbl1); + + regalloc_dry_run_cycle_scale = iterations; + for (i = skip_bytes; i < size;) + { + int swapidx = -1; + + if (aopInReg (shiftop, i, X_IDX) || aopInReg (shiftop, i, Y_IDX)) + { + emit3w_o (i - skip_bytes ? A_RLCW : A_SLLW, shiftop, i, 0, 0); + i += 2; + continue; + } + + if (aopInReg (shiftop, i, A_IDX)) + { + emit2 (i - skip_bytes ? "rlc" : "sll", "(1, sp)"); + cost (2, 1); + i++; + continue; + } + + if (aopRS (shiftop) && !aopInReg (shiftop, i, A_IDX) && shiftop->aopu.bytes[i].in_reg) + swapidx = shiftop->aopu.bytes[i].byteu.reg->rIdx; + + if (swapidx == -1) + emit3_o (i - skip_bytes ? A_RLC : A_SLL, shiftop, i, 0, 0); + else + { + swap_to_a (swapidx); + emit3 (i - skip_bytes ? A_RLC : A_SLL, ASMOP_A, 0); + swap_from_a (swapidx); + } + i++; + } + emit3 (A_DEC, ASMOP_A, 0); + regalloc_dry_run_cycle_scale = 1; + + if (tlbl1) + emit2 ("jrne", "!tlabel", labelKey2num (tlbl1->key)); + cost (2, (iterations - 1) * 2 + 1); + emitLabel (tlbl2); + +postshift: + if(!regDead (A_IDX, ic)) + { + genMove (result->aop, shiftop, regDead (A_IDX, ic) || pushed_a, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + if (pushed_a) + pop (ASMOP_A, 0, 1); + } + else + { + if (pushed_a) + pop (ASMOP_A, 0, 1); + genMove (result->aop, shiftop, false, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + } + + freeAsmop (left); + freeAsmop (result); + freeAsmop (right); +} + +/*------------------------------------------------------------------*/ +/* genGetABit - get a bit */ +/*------------------------------------------------------------------*/ +static void +genGetABit (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int shCount, leftcost, rightcost; + + D (emit2 ("; genGetABit", "")); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic); + aopOp (left, ic); + aopOp (result, ic); + + shCount = (int) ulFromVal ((right->aop)->aopu.aop_lit); + + if (ifx && result->aop->type == AOP_CND) + { + wassert (shCount % 8 == 7); + + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); + + if (aopInReg (left->aop, shCount / 8, XH_IDX)) + emit3w (A_TNZW, ASMOP_X, 0); + else if (aopInReg (left->aop, shCount / 8, YH_IDX)) + emit3w (A_TNZW, ASMOP_Y, 0); + else if (aopInReg (left->aop, shCount / 8, XL_IDX) || aopInReg (left->aop, shCount / 8, YL_IDX)) + { + wassert (regalloc_dry_run); + cost (200, 200); + } + else + emit3_o (A_TNZ, left->aop, shCount / 8, 0, 0); + + if (!regalloc_dry_run) + emit2 (IC_TRUE (ifx) ? "jrpl" : "jrmi", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // Hmm. Cycle cost overestimate. + + emitJP (IC_TRUE (ifx) ? IC_TRUE (ifx) : IC_FALSE (ifx), 1.0f); // Hmm. Cycle cost overestimate. + emitLabel (tlbl); + + goto release; + } + + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + if ((shCount % 8) == 7 && + (aopInReg (left->aop, shCount / 8, XH_IDX) && regDead (X_IDX, ic) || aopInReg (left->aop, shCount / 8, YH_IDX) && regDead (Y_IDX, ic))) + { + bool x = aopInReg (left->aop, shCount / 8, XH_IDX); + emit3w (A_SLLW, x ? ASMOP_X : ASMOP_Y, 0); + goto write_to_a; + } + + genMove_o (ASMOP_A, 0, left->aop, shCount / 8, 1, TRUE, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + shCount %= 8; + + rightcost = 2 + (shCount > 4) + shCount % 4; + leftcost = 3 + (shCount <= 4) + (7 - shCount) % 4; + + if (rightcost < leftcost) + { + if (shCount > 4) + { + emit3 (A_SWAP, ASMOP_A, 0); + shCount -= 4; + } + while (shCount--) + emit3 (A_SRL, ASMOP_A, 0); + emit3 (A_AND, ASMOP_A, ASMOP_ONE); + cost (2, 1); + } + else + { + if (shCount <= 4) + { + emit3 (A_SWAP, ASMOP_A, 0); + shCount += 4; + } + while (shCount++ < 8) + emit3 (A_SLL, ASMOP_A, 0); +write_to_a: + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_RLC, ASMOP_A, 0); + } + + genMove (result->aop, ASMOP_A, TRUE, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + +release: + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*------------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*------------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand *left, operand *right, operand *result, const iCode *ic) +{ + int shCount = (int) ulFromVal (right->aop->aopu.aop_lit); + int size, i; + bool sign; + bool xh_zero, yh_zero, xl_free, yl_free; + + struct asmop shiftop_impl; + struct asmop *shiftop; + + D (emit2 ("; genRightShiftLiteral", "")); + + size = getSize (operandType (result)); + + sign = !SPEC_USIGN (getSpec (operandType (left))); + + /* I suppose that the left size >= result size */ + wassert ((int) getSize (operandType (left)) >= size); + + aopOp (left, ic); + aopOp (result, ic); + + if (shCount > (size * 8)) + shCount = size * 8; + + if (!sign && shCount >= (size * 8)) + { + genMove(result->aop, ASMOP_ZERO, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + shiftop = result->aop; + goto release; + } + + wassertl (size <= 2 || shCount % 8 <= 1 + (size <= 4) || size == 4 && (shCount <= 10 || shCount >= 16), + "Shifting of longs and long longs by non-trivial values should be handled by generic function."); + + if ((shCount < 8 || sign) && aopRS (left->aop) && aopRS (result->aop)) + { + shiftop = &shiftop_impl; + init_shiftop (shiftop, result->aop, left->aop, right->aop, ic, FALSE); + + genMove (shiftop, left->aop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + } + else if (sign || + shCount >= 12 && aopInReg (result->aop, 0, X_IDX) && aopInReg (left->aop, 0, X_IDX) && regDead (Y_IDX, ic)) // Use divw, see below. + { + genMove (result->aop, left->aop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + shiftop = result->aop; + } + else // Top bytes will be zero. + { + genMove_o (result->aop, 0, left->aop, shCount / 8, size, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + size -= shCount / 8; + shCount %= 8; + shiftop = result->aop; + } + + if (!shCount) + goto release; + + xh_zero = shiftop->regs[XH_IDX] >= size; + yh_zero = shiftop->regs[YH_IDX] >= size; + xl_free = regDead (XL_IDX, ic) && shiftop->regs[XL_IDX] < 0; + yl_free = regDead (YL_IDX, ic) && shiftop->regs[YL_IDX] < 0; + + // Use swap a where beneficial. + if (!sign && size == 1 && aopRS (shiftop) && !aopOnStack (shiftop, 0, 1) && + !(aopInReg (shiftop, 0, XL_IDX) && xh_zero) && + !(aopInReg (shiftop, 0, YL_IDX) && yh_zero && shCount <= 3)) + { + swap_to_a (shiftop->aopu.bytes[0].byteu.reg->rIdx); + if (shCount >= 4) + { + emit3 (A_SWAP, ASMOP_A, 0); + emit2 ("and", "a, #0x0f"); + cost (2, 1); + shCount -= 4; + } + while (shCount--) + emit3 (A_SRL, ASMOP_A, 0); + swap_from_a (shiftop->aopu.bytes[0].byteu.reg->rIdx); + goto release; + } + + // div can be cheaper than a sequence of shifts. + if (!sign && shCount < 8 && + (shCount > 3 + !regDead (A_IDX, ic) * 2 && (size == 2 && aopInReg (shiftop, 0, X_IDX) || size == 1 && aopInReg (shiftop, 0, XL_IDX) && xh_zero) || + shCount * 2 > 4 + !regDead (A_IDX, ic) * 2 && (size == 2 && aopInReg (shiftop, 0, Y_IDX) || size == 1 && aopInReg (shiftop, 0, YL_IDX) && yh_zero))) + { + const bool in_y = aopInReg (shiftop, 0, Y_IDX); + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + emit2 ("ld", "a, #0x%02x", 1 << shCount); + cost (2, 1); + emit2 ("div", in_y ? "y, a" : "x, a"); + cost (1 + in_y, 17); // TODO: Find out exact value, replace 17 by exact value, and accordingly choose this optimization depending on optimization goal. + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + goto release; + } + + // divw can be cheaper than a sequence of shifts. + if (!sign && size == 2 && shCount > 5 && regDead (Y_IDX, ic) && aopInReg (shiftop, 0, X_IDX)) + { + emit2 ("ldw", "y, #0x%04x", 1 << shCount); + cost (4, 2); + emit2 ("divw", "x, y"); + cost (1, 17); // TODO: Find out exact value, replace 17 by exact value, and accordingly choose this optimization depending on optimization goal. + goto release; + } + + // Testing and rlwa is cheaper than 8 times sraw + if (sign && shCount >= (7 - regDead (A_IDX, ic)) && size >= 2 && (aopInReg (shiftop, size - 2, X_IDX) || aopInReg (shiftop, size - 2, Y_IDX)) && + (size == 2 || size == 3 && shCount >= 8 && aopInReg (shiftop, 0, A_IDX) || size == 4 && (aopInReg (shiftop, 0, X_IDX) || aopInReg (shiftop, 0, Y_IDX)))) + { + bool pushed_sign = false; + + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + symbol *tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + emit3 (A_CLR, ASMOP_A, 0); + emit3w_o (A_TNZW, shiftop, size - 2, 0, 0); + if (tlbl) + emit2 ("jrpl", "!tlabel", labelKey2num (tlbl->key)); + emit3 (A_DEC, ASMOP_A, 0); + cost (2, 0); + emitLabel (tlbl); + + if (shCount >= 8 + 6) + { + push (ASMOP_A, 0, 1); + pushed_sign = true; + } + while (shCount >= 6) + { + emit3w_o (A_RRWA, shiftop, size - 2, 0, 0); + if (size >= 4) + emit3w_o (A_RRWA, shiftop, 0, 0, 0); + shCount -= 8; + if (shCount >= 6) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + } + for (; shCount < 0; shCount++) + { + wassert (aopInReg (shiftop, 0, X_IDX) || aopInReg (shiftop, 0, Y_IDX)); + emit3 (A_SLL, ASMOP_A, 0); + emit3w_o (A_RLCW, shiftop, 0, 0, 0); + if (size >= 4) + { + wassert (aopInReg (shiftop, 2, X_IDX) || aopInReg (shiftop, 2, Y_IDX)); + emit3w_o (A_RLCW, shiftop, 2, 0, 0); + } + } + if (pushed_sign) + pop (ASMOP_A, 0, 1); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + + // Shifting right by 8, then shifting left a bit can be cheaper than shifting right all the way. + if (size == 4 && !sign && (shCount == 7 || shCount == 6) && + (aopInReg (shiftop, 0, X_IDX) || aopInReg (shiftop, 0, Y_IDX)) && + (aopInReg (shiftop, 2, X_IDX) || aopInReg (shiftop, 2, Y_IDX))) + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + emit3 (A_CLR, ASMOP_A, 0); + emit3w_o (A_RRWA, shiftop, 2, 0, 0); + emit3w_o (A_RRWA, shiftop, 0, 0, 0); + shCount -= 8; + for (; shCount < 0; shCount++) + { + emit3 (A_SLL, ASMOP_A, 0); + emit3w_o (A_RLCW, shiftop, 0, 0, 0); + emit3w_o (A_RLCW, shiftop, 2, 0, 0); + } + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + + while (shCount--) + for (i = size - 1; i >= 0;) + { + if (i > 0 && (aopInReg (shiftop, i - 1, X_IDX) || aopInReg (shiftop, i - 1, Y_IDX))) + { + emit3w_o ((i != size - 1) ? A_RRCW : (sign ? A_SRAW : A_SRLW), shiftop, i - 1, 0, 0); + i -= 2; + } + else if (!sign && i == size - 1 && (aopInReg (shiftop, i, XL_IDX) && xh_zero || aopInReg (shiftop, i, YL_IDX) && yh_zero)) // Skipped top byte, but 16-bit shift is cheaper than going through a and doing an 8-bit shift there. + { + emit3w (A_SRLW, aopInReg (shiftop, i, XL_IDX) ? ASMOP_X : ASMOP_Y, 0); + i--; + } + else if (i == 0 && (aopInReg (shiftop, i, XH_IDX) && xl_free || aopInReg (shiftop, i, YH_IDX) && yl_free)) // 16-bit shift is cheaper than going through a and doing an 8-bit shift there. + { + const bool in_x = aopInReg (shiftop, i, XH_IDX); + emit3w ((i != size - 1) ? A_RRCW : (sign ? A_SRAW : A_SRLW), in_x ? ASMOP_X : ASMOP_Y, 0); + i--; + } + else + { + int swapidx = -1; + if (aopRS (shiftop) && !aopInReg (shiftop, i, A_IDX) && shiftop->aopu.bytes[i].in_reg) + swapidx = shiftop->aopu.bytes[i].byteu.reg->rIdx; + + if (swapidx == -1) + emit3_o ((i != size - 1) ? A_RRC : (sign ? A_SRA : A_SRL), shiftop, i, 0, 0); + else + { + swap_to_a (swapidx); + emit3 ((i != size - 1) ? A_RRC : (sign ? A_SRA : A_SRL), ASMOP_A, 0); + swap_from_a (swapidx); + } + + i--; + } + } + +release: + genMove (result->aop, shiftop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generates code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (const iCode *ic) +{ + operand *left, *right, *result; + int i, size; + bool pushed_a = false; + symbol *tlbl1, *tlbl2; + bool sign; + unsigned int iterations; + int skip_bytes = 0; + + struct asmop shiftop_impl; + struct asmop *shiftop; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic); + + sign = !SPEC_USIGN (getSpec (operandType (left))); + + /* if the shift count is known then do it + as efficiently as possible */ + if (right->aop->type == AOP_LIT && + ((getSize (operandType (result)) <= 2) || + (!sign && ulFromVal (right->aop->aopu.aop_lit) % 8 <= (getSize (operandType (result)) <= 4 ? 2ul : 1ul)) || + (getSize (operandType (result)) <= 4 && ulFromVal (right->aop->aopu.aop_lit) <= 10) || + (getSize (operandType (result)) <= 4 && ulFromVal (right->aop->aopu.aop_lit) >= 16) || + (!sign && ulFromVal (right->aop->aopu.aop_lit) >= getSize (operandType (result)) * 8) ) ) + { + genRightShiftLiteral (left, right, result, ic); + freeAsmop (right); + return; + } + + D (emit2 ("; genRightShift", "")); + + aopOp (result, ic); + aopOp (left, ic); + + if (!regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + + if ((aopRS (left->aop) || left->aop->type == AOP_LIT) && aopRS (result->aop)) + { + shiftop = &shiftop_impl; + init_shiftop (shiftop, result->aop, left->aop, right->aop, ic, false); + } + else + shiftop = result->aop; + + iterations = (right->aop->type == AOP_LIT ? byteOfVal (right->aop->aopu.aop_lit, 0) : 2); + + if (right->aop->type == AOP_LIT && !sign) + { + skip_bytes = iterations / 16 * 2; + genMove_o (shiftop, 0, left->aop, skip_bytes, shiftop->size, right->aop->regs[A_IDX] < 0, regDead (X_IDX, ic) && right->aop->regs[XL_IDX] < 0 && right->aop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && right->aop->regs[YL_IDX] < 0 && right->aop->regs[YH_IDX] < 0); + iterations %= 16; + } + else // TODO: What if shiftop and right operand overlap on stack? + genMove (shiftop, left->aop, right->aop->regs[A_IDX] < 0, regDead (X_IDX, ic) && right->aop->regs[XL_IDX] < 0 && right->aop->regs[XH_IDX] < 0, regDead (Y_IDX, ic) && right->aop->regs[YL_IDX] < 0 && right->aop->regs[YH_IDX] < 0); + + size = shiftop->size; + + for (i = 0; i < size; i++) + { + if (aopRS (shiftop) && (!aopInReg (shiftop, i, A_IDX) || aopInReg (right->aop, 0, A_IDX)) && shiftop->aopu.bytes[i].in_reg && + right->aop->regs[shiftop->aopu.bytes[i].byteu.reg->rIdx] == 0) + { + if (!regalloc_dry_run) + wassertl (0, "Overwriting shift count"); + cost (380, 380); + } + if (aopInReg (shiftop, i, A_IDX) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = true; + } + } + + tlbl1 = (regalloc_dry_run ? 0 : newiTempLabel (0)); + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (0)); + + // Get shift count into a. + if (right->aop->type == AOP_LIT) + { + if (!iterations) + goto postshift; + emit2 ("ld", "a, #0x%02x", iterations); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, right->aop, 0, false); + + if (right->aop->type != AOP_LIT || aopIsLitVal (right->aop, 0, 1, 0)) + { + if (!aopOnStack (right->aop, 0, 1) && right->aop->type != AOP_DIR) + emit3 (A_TNZ, ASMOP_A, 0); + if (tlbl2) + emit2 ("jreq", "!tlabel", labelKey2num (tlbl2->key)); + cost (2, 0); + } + + emitLabel (tlbl1); + + regalloc_dry_run_cycle_scale = iterations; + for (i = size - 1 - skip_bytes; i >= 0;) + { + int swapidx = -1; + + if (i > 0 && (aopInReg (shiftop, i - 1, X_IDX) || aopInReg (shiftop, i - 1, Y_IDX))) + { + emit3w_o ((i != size - 1 - skip_bytes) ? A_RRCW : (sign ? A_SRAW : A_SRLW), shiftop, i - 1, 0, 0); + i -= 2; + continue; + } + else if (aopInReg (shiftop, i, A_IDX)) + { + emit2 ((i != size - 1 - skip_bytes) ? "rrc" : (sign ? "sra" : "srl"), "(1, sp)"); + cost (2, 1); + i--; + continue; + } + + if (aopRS (shiftop) && !aopInReg (shiftop, i, A_IDX) && shiftop->aopu.bytes[i].in_reg) + swapidx = shiftop->aopu.bytes[i].byteu.reg->rIdx; + + if (swapidx == -1) + emit3_o ((i != size - 1 - skip_bytes) ? A_RRC : (sign ? A_SRA : A_SRL), shiftop, i, 0, 0); + else + { + swap_to_a (swapidx); + emit3 ((i != size - 1 - skip_bytes) ? A_RRC : (sign ? A_SRA : A_SRL), ASMOP_A, 0); + swap_from_a (swapidx); + } + i--; + } + emit3 (A_DEC, ASMOP_A, 0); + regalloc_dry_run_cycle_scale = 1; + + if (tlbl1) + emit2 ("jrne", "!tlabel", labelKey2num (tlbl1->key)); + cost (2, (iterations - 1) * 2 + 1); + emitLabel (tlbl2); + +postshift: + if(!regDead (A_IDX, ic)) + { + genMove (result->aop, shiftop, regDead (A_IDX, ic) || pushed_a, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + if (pushed_a) + pop (ASMOP_A, 0, 1); + } + else + { + if (pushed_a) + pop (ASMOP_A, 0, 1); + genMove (result->aop, shiftop, false, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + } + + freeAsmop (left); + freeAsmop (result); + freeAsmop (right); +} + +/*------------------------------------------------------------------*/ +/* init_stackop - initalize asmop for stack location */ +/*------------------------------------------------------------------*/ +static void init_stackop (asmop *stackop, int size, long int stk_off) +{ + stackop->size = size; + stackop->regs[A_IDX] = -1; + stackop->regs[XL_IDX] = -1; + stackop->regs[XH_IDX] = -1; + stackop->regs[YL_IDX] = -1; + stackop->regs[YH_IDX] = -1; + + for (int i = 0; i < size; i++) + { + stackop->aopu.bytes[i].in_reg = false; + stackop->aopu.bytes[i].byteu.stk = stk_off + stackop->size - i - 1; + } + + stackop->type = AOP_STK; +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + int size, i; + unsigned offset; + bool use_y; + bool pushed_x = false; + bool pushed_a = false; + int blen, bstr; + bool bit_field = IS_BITVAR (getSpec (operandType (result))); + symbol *const tlbl = ((regalloc_dry_run || !bit_field) ? 0 : newiTempLabel (NULL)); + + blen = bit_field ? SPEC_BLEN (getSpec (operandType (result))) : 0; + bstr = bit_field ? SPEC_BSTR (getSpec (operandType (result))) : 0; + + D (emit2 ("; genPointerGet", "")); + + aopOp (IC_LEFT (ic), ic); + aopOp (IC_RIGHT (ic), ic); + aopOp (IC_RESULT (ic), ic); + + if (result->aop->type == AOP_DUMMY) + D (emit2 ("; Dummy read", "")); + + wassertl (right, "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (ic)), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + + size = result->aop->size; + + // todo: What if right operand is negative? + offset = byteOfVal (right->aop->aopu.aop_lit, 1) * 256 + byteOfVal (right->aop->aopu.aop_lit, 0); + + // Long pointer indirect long addressing mode is useful only in one very specific case: + if (!bit_field && size == 1 && !offset && left->aop->type == AOP_DIR && !regDead (X_IDX, ic) && regDead (A_IDX, ic)) + { + emit2 ("ld", "a, [%s]", aopGet2(left->aop, 0)); + cost (4, 4); + cheapMove (result->aop, 0, ASMOP_A, 0, false); + goto release; + } + // Special case for remat pointer to on-stack object. + else if (!bit_field && left->aop->type == AOP_STL) + { + struct asmop stackop_impl; + init_stackop (&stackop_impl, result->aop->size, left->aop->aopu.stk_off + (long)offset); + genMove(result->aop, &stackop_impl, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + } + // Special case for efficient handling of 8-bit I/O and rematerialized pointers + else if (!bit_field && size == 1 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) + && regDead (A_IDX, ic)) + { + if (left->aop->type == AOP_LIT) + emit2("ld", offset ? "a, 0x%02x%02x+%d" : "a, 0x%02x%02x", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), offset); + else + emit2("ld", offset ? "a, %s+%d" : "a, %s+%d", left->aop->aopu.immd, left->aop->aopu.immd_off + offset); + cost (3, 1); + cheapMove (result->aop, 0, ASMOP_A, 0, FALSE); + goto release; + } + // Special case for efficient handling of 16-bit I/O and rematerialized pointers + else if (!bit_field && size == 2 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && + (aopInReg (result->aop, 0, X_IDX) || aopInReg (result->aop, 0, Y_IDX) || aopOnStack (result->aop, 0, 2) && regDead (X_IDX, ic))) + { + bool use_y = aopInReg (result->aop, 0, Y_IDX); + if (left->aop->type == AOP_LIT) + emit2("ldw", offset ? "%s, 0x%02x%02x+%d" : "%s, 0x%02x%02x", use_y ? "y" : "x", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), offset); + else + emit2("ldw", offset ? "%s, %s+%d" : "%s, %s+%d", use_y ? "y" : "x", left->aop->aopu.immd, left->aop->aopu.immd_off + offset); + cost (3 + use_y, 2); + genMove (result->aop, use_y ? ASMOP_Y : ASMOP_X, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + } + + // todo: Handle this more gracefully, save x instead of using y. + use_y = (aopInReg (left->aop, 0, Y_IDX) && size <= 1 + aopInReg (result->aop, 0, Y_IDX)) || + !(regDead (X_IDX, ic) || aopInReg (left->aop, 0, X_IDX)) || + !bit_field && size == 2 && aopInReg (result->aop, 0, Y_IDX) && aopInReg (left->aop, 0, X_IDX) && !regDead (X_IDX, ic); + + if (use_y ? !(regDead (Y_IDX, ic) || aopInReg (left->aop, 0, Y_IDX)) : !(regDead (X_IDX, ic) || aopInReg (left->aop, 0, X_IDX))) // Preferred index register is not free. + { + // Try to free an index register. + if (result->aop->regs[XL_IDX] < 0 && result->aop->regs[XH_IDX] < 0) + { + push (ASMOP_X, 0, 2); + pushed_x = true; + use_y = false; + } + else + { + if (!regalloc_dry_run) + wassertl (0, use_y ? "No free reg y for pointer." : "No free reg x for pointer."); + cost (180, 180); + goto release; + } + } + + if (left->aop->type == AOP_STL) + { + emit2 ("ldw", "x, sp"); + emit2 ("addw", "x, #%ld", (long)(left->aop->aopu.stk_off) + G.stack.pushed ); + cost (4, 3); + } + else + genMove (use_y ? ASMOP_Y : ASMOP_X, left->aop, FALSE, regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + if (floatFromVal (right->aop->aopu.aop_lit) < 0.0) + { + emit2 ("addw", use_y ? "y, #0x%x" : "x, #0x%x", offset); + offset = 0; + cost (use_y ? 4 : 3, 2); + } + + // Get all the bytes. todo: Get the byte in a last (if not a bit-field), so we do not need to save a. + for (i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + int o = (bit_field ? i : size - 1 - i) + offset; + + if (!bit_field && i + 2 == size && !aopInReg (result->aop, i, A_IDX) && !aopInReg (result->aop, i + 1, A_IDX) && + (result->aop->regs[use_y ? YL_IDX : XL_IDX] < 0 || result->aop->regs[use_y ? YL_IDX : XL_IDX] >= i) && (result->aop->regs[use_y ? YH_IDX : XH_IDX] < 0 || result->aop->regs[use_y ? YH_IDX : XH_IDX] >= i) && regDead (use_y ? Y_IDX : X_IDX, ic)) + { + o--; + if (!o) + emit2 ("ldw", use_y ? "y, (y)" : "x, (x)"); + else + emit2 ("ldw", use_y ? "y, (0x%x, y)" : "x, (0x%x, x)", o); + cost (1 + use_y + (o > 0) + (o > 256), 2); + + genMove_o (result->aop, i, use_y ? ASMOP_Y : ASMOP_X, 0, 2, regDead (A_IDX, ic) && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= i) || pushed_a, FALSE, FALSE); // todo: Allow more. + + i++, blen -= 8; + continue; + } + else if (!bit_field && !use_y && + (aopInReg (result->aop, i, Y_IDX) || aopOnStackNotExt (result->aop, i, 2) && regDead (Y_IDX, ic) && result->aop->regs[YL_IDX] < 0 && result->aop->regs[YH_IDX] < 0 && i + 3 < size && !optimize.codeSpeed)) + { + o--; + + emit2 ("ldw", "y, x"); + if (!o) + emit2 ("ldw", "y, (y)"); + else + emit2 ("ldw", "y, (0x%x, y)", o); + cost (4 + (o > 0) + (o > 256), 3); + genMove_o (result->aop, i, ASMOP_Y, 0, 2, pushed_a, false, true); + + i++, blen -= 8; + continue; + } + + if (!pushed_a && (!regDead (A_IDX, ic) || result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < i)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + if (!o) + { + emit2 ("ld", use_y ? "a, (y)" : "a, (x)"); + cost (1 + use_y, 1); + } + else + { + emit2 ("ld", use_y ? "a, (0x%x, y)" : "a, (0x%x, x)", o); + cost ((o < 256 ? 2 : 3) + use_y, 1); + } + + if (bit_field && blen < 8 && !i) // The only byte might need shifting. + { + if (bstr >= 4) + { + emit3 (A_SWAP, ASMOP_A, 0); + bstr -= 4; + } + while (bstr--) + emit3 (A_SRL, ASMOP_A, 0); + } + if (bit_field && blen < 8) // The partial byte. + { + emit2 ("and", "a, #0x%02x", 0xff >> (8 - blen)); + cost (2, 1); + } + + if (bit_field && blen <= 8 && !SPEC_USIGN (getSpec (operandType (result)))) // Sign extension for partial byte of signed bit-field + { + emit2 ("bcp", "a, #0x%02x", 0x80 >> (8 - blen)); + cost (2, 1); + if (tlbl) + emit2 ("jreq", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 0); + emit2 ("or", "a, #0x%02x", (0xff00 >> (8 - blen)) & 0xff); + cost (2, 1); + emitLabel (tlbl); + } + + if (result->aop->type == AOP_DUMMY) + continue; + + cheapMove (result->aop, i, ASMOP_A, 0, FALSE); + + if (i < size - 1 && (use_y ? aopInReg (result->aop, i, YL_IDX) || aopInReg (result->aop, i, YH_IDX) : aopInReg (result->aop, i, XL_IDX) || aopInReg (result->aop, i, XH_IDX))) + { + if (!regalloc_dry_run) + wassertl (0, "Overwriting pointer"); + cost (180, 180); + } + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + + if (bit_field && i < size) + { + if (SPEC_USIGN (getSpec (operandType (result)))) + genMove_o (result->aop, i, ASMOP_ZERO, 0, bit_field ? i : size - i - 1, FALSE, FALSE, FALSE); + else + wassertl (0, "Unimplemented multibyte sign extension for bit-field."); + } + +release: + if (pushed_x) + pop (ASMOP_X, 0, 2); + + freeAsmop (right); + freeAsmop (left); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (const iCode *ic) +{ + operand *result, *right; + + D (emit2 ("; genAssign", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + aopOp (right, ic); + aopOp (result, ic); + + wassert (result->aop->type != AOP_DUMMY || right->aop->type != AOP_DUMMY); + + if (right->aop->type == AOP_DUMMY) + { + int i; + D (emit2 ("; Dummy write", "")); + for (i = 0; i < result->aop->size; i++) + cheapMove (result->aop, i, ASMOP_A, 0, TRUE); + } + else if (result->aop->type == AOP_DUMMY) + { + int i; + D (emit2 ("; Dummy read", "")); + + if (!regDead(A_IDX, ic) && right->aop->type == AOP_DIR) + for (i = 0; i < right->aop->size; i++) + emit3_o (A_TNZ, right->aop, i, 0, 0); + else + { + if (!regDead(A_IDX, ic)) + push (ASMOP_A, 0, 1); + for (i = 0; i < right->aop->size; i++) + cheapMove (ASMOP_A, 0, right->aop, i, FALSE); + if (!regDead(A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + } + else + genMove(result->aop, right->aop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + wassert (result->aop != right->aop); + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode *ic) +{ + operand *left = IC_LEFT (ic); + operand *right = IC_RIGHT (ic); + int size, i, j; + bool use_y; + int pushed_a = 0; + int blen, bstr; + bool bit_field = IS_BITVAR (getSpec (operandType (right))) || IS_BITVAR (getSpec (operandType (left))); + int cache_l = -1, cache_h = -1/*, cache_a = -1*/; + + blen = bit_field ? (SPEC_BLEN (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + bstr = bit_field ? (SPEC_BSTR (getSpec (operandType (IS_BITVAR (getSpec (operandType (right))) ? right : left)))) : 0; + + D (emit2 ("; genPointerSet", "")); + + aopOp (left, ic); + aopOp (right, ic); + + size = right->aop->size; + + // In some cases a sequence of mov instructions is more efficient. + if (!bit_field && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && (right->aop->type == AOP_DIR || right->aop->type == AOP_LIT || right->aop->type == AOP_IMMD)) + { + // First, make an estimate to find out if it is worth it (estimate not exact, could be improved a bit, probably not worth it since left type is uncommon) + const int mov_size = size * (right->aop->type == AOP_DIR ? 3 : 4); + const int mov_cycles = size * 1; + int normal_size = 3; + int normal_cycles = 2; + bool needs_a = false; + + for (i = 0; i < size;) + { + if (aopIsLitVal (right->aop, i, 1, 0)) // clr (x) + { + normal_size += i ? 2 : 1; + normal_cycles += 1; + i++; + } + else if (i + 1 < size) // ld y, . followed by ldw (x), y + { + normal_size += (i ? 6 : 5) - (right->aop->type == AOP_DIR); + normal_cycles += 3; + i += 2; + } + else // ld a, . followed by ldw (x), a + { + needs_a = true; + normal_size += i ? 4 : 3; + normal_cycles += 2; + i++; + } + } + + if (!regDead (X_IDX, ic)) + { + normal_size += 2; + normal_cycles += 2; + } + if (needs_a && !regDead (A_IDX, ic)) + { + normal_size += 2; + normal_cycles += 2; + } + + if ((mov_size <= normal_size || optimize.codeSpeed) && (mov_cycles <= normal_cycles || optimize.codeSize)) + { + for (i = 0; i < size; i++) + { + if (left->aop->type == AOP_LIT) + emit2 ("mov", "0x%02x%02x+%d, %s", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), size - i - 1, aopGet (right->aop, i)); + else + emit2 ("mov", "%s+%d, %s", left->aop->aopu.immd, left->aop->aopu.immd_off + size - i - 1, aopGet (right->aop, i)); + cost (right->aop->type == AOP_DIR ? 3 : 4, 1); + } + goto release; + } + } + + // Use bset / bres. + if (bit_field && blen == 1 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && right->aop->type == AOP_LIT) + { + const char *inst = (byteOfVal (right->aop->aopu.aop_lit, 0) & 1) ? "bset" : "bres"; + if (left->aop->type == AOP_LIT) + emit2 (inst, "0x%02x%02x, #%u", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), bstr); + else + emit2 (inst, "%s+%d, #%u", left->aop->aopu.immd, left->aop->aopu.immd_off, bstr); + cost (4, 1); + goto release; + } + // Use bccm + if (bit_field && blen == 1 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD)) + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, right->aop, 0, false); + emit3(A_SRL, ASMOP_A, 0); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + emit2 ("bccm", "%s+%d, #%u", left->aop->aopu.immd, left->aop->aopu.immd_off, bstr); + cost (4, 1); + goto release; + } + + if (!bit_field && size == 1 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && aopInReg(right->aop, 0, A_IDX)) + { + if (left->aop->type == AOP_LIT) + emit2 ("ld", "0x%02x%02x, %s", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), aopGet (right->aop, 0)); + else + emit2 ("ld", "%s+%d, %s", left->aop->aopu.immd, left->aop->aopu.immd_off, aopGet (right->aop, 0)); + cost (3, 1); + goto release; + } + if (!bit_field && size == 2 && (left->aop->type == AOP_LIT || left->aop->type == AOP_IMMD) && (aopInReg(right->aop, 0, X_IDX) || aopInReg(right->aop, 0, Y_IDX))) + { + if (left->aop->type == AOP_LIT) + emit2 ("ldw", "0x%02x%02x, %s", byteOfVal (left->aop->aopu.aop_lit, 1), byteOfVal (left->aop->aopu.aop_lit, 0), aopGet2 (right->aop, 0)); + else + emit2 ("ldw", "%s+%d, %s", left->aop->aopu.immd, left->aop->aopu.immd_off, aopGet2 (right->aop, 0)); + cost (3 + aopInReg(right->aop, 0, Y_IDX), 2); + goto release; + } + + // Long pointer indirect long addressing mode is useful only in two very specific cases: + if (!bit_field && size == 1 && left->aop->type == AOP_DIR && !regDead (X_IDX, ic) && (aopInReg(right->aop, 0, A_IDX) || regDead (A_IDX, ic))) + { + emit2("ld", "[%s], a", aopGet2 (left->aop, 0)); + cost (4, 4); + goto release; + } + else if (!bit_field && size == 2 && left->aop->type == AOP_DIR && (!regDead (Y_IDX, ic) || !optimize.codeSpeed) && aopInReg(right->aop, 0, X_IDX)) + { + emit2("ldw", "[%s], x", aopGet2 (left->aop, 0)); + cost (4, 5); + goto release; + } + + // Rematerialized pointer to on-stack object. + if (!bit_field && left->aop->type == AOP_STL) + { + struct asmop stackop_impl; + init_stackop (&stackop_impl, size, left->aop->aopu.stk_off); + genMove(&stackop_impl, right->aop, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + goto release; + } + + // todo: Handle this more gracefully, save x instead of using y, when doing so is more efficient. + use_y = (aopInReg (left->aop, 0, Y_IDX) && size <= 1 + aopInReg (right->aop, 0, X_IDX)) || regDead (Y_IDX, ic) && (!(regDead (X_IDX, ic) || aopInReg (left->aop, 0, X_IDX)) || right->aop->regs[XL_IDX] >= 0 || right->aop->regs[XH_IDX] >= 0); + + if (!(regDead (use_y ? Y_IDX : X_IDX, ic) || aopInReg (left->aop, 0, use_y ? Y_IDX : X_IDX)) || right->aop->regs[use_y ? YL_IDX : XL_IDX] >= 0 || right->aop->regs[use_y ? YH_IDX : XH_IDX] >= 0) + { + if (!regalloc_dry_run) + wassertl (0, "No free reg for pointer."); + + cost (180, 180); + goto release; + } + + if (left->aop->type == AOP_STL) + { + emit2 ("ldw", "x, sp"); + emit2 ("addw", "x, #%ld", (long)(left->aop->aopu.stk_off) + G.stack.pushed ); + cost (4, 3); + } + else + genMove (use_y ? ASMOP_Y : ASMOP_X, left->aop, regDead (A_IDX, ic) && !aopInReg (right->aop, 0, A_IDX), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + for (i = 0; !bit_field ? i < size : blen > 0; i++, blen -= 8) + { + if (!bit_field && aopIsLitVal (right->aop, i, 1, 0) && + !(!use_y && i + 1 < size && optimize.codeSize && regDead (Y_IDX, ic) && right->aop->type == AOP_LIT && (size - 2 - i || !cache_l && !cache_h))) // clrw y, ldw (d, x), y is cheaper than this. ldw (x), y is cheaper than this if y is zero. + { + if (!(size - 1 - i)) + emit2 ("clr", use_y ? "(y)" : "(x)"); + else + emit2 ("clr", use_y ? "(0x%x, y)" : "(0x%x, x)", size - 1 - i); + cost (1 + use_y + ((size - 1 - i) > 0) + ((size - 1 - i) > 256) + (!use_y && ((size - 1 - i) > 256)), 2); + + continue; + } + + if (!bit_field && i + 1 < size && !aopInReg (right->aop, i, A_IDX) && !aopInReg (right->aop, i + 1, A_IDX) && + (aopInReg(right->aop, i, use_y ? X_IDX : Y_IDX) || regDead (use_y ? X_IDX : Y_IDX, ic) && right->aop->regs[use_y ? XL_IDX : YL_IDX] <= i + 1 && right->aop->regs[use_y ? XH_IDX : YH_IDX] <= i + 1)) + { + if (right->aop->type == AOP_LIT) + { + if (cache_l != byteOfVal (right->aop->aopu.aop_lit, i) || cache_h != byteOfVal (right->aop->aopu.aop_lit, i + 1)) + { + genMove_o (use_y ? ASMOP_X : ASMOP_Y, 0, right->aop, i, 2, FALSE, FALSE, FALSE); + cache_l = byteOfVal (right->aop->aopu.aop_lit, i); + cache_h = byteOfVal (right->aop->aopu.aop_lit, i + 1); + } + } + else + genMove_o (use_y ? ASMOP_X : ASMOP_Y, 0, right->aop, i, 2, FALSE, FALSE, FALSE); + + if (!(size - 2 - i)) + emit2 ("ldw", use_y ? "(y), x" : "(x), y"); + else + emit2 ("ldw", use_y ? "(0x%x, y), x" : "(0x%x, x), y", size - 2 - i); + cost (1 + use_y + ((size - 2 - i) > 0) + ((size - 2 - i) > 256), 2); + + i++, blen -= 8; + continue; + } + + // todo: handle byte in a first, if dead, so we do not need to save it. + if ((!regDead (A_IDX, ic) && !(aopInReg (right->aop, i, A_IDX) && !bit_field) || right->aop->regs[A_IDX] > i) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + if (use_y ? aopInReg (right->aop, i, YL_IDX) || aopInReg (right->aop, i, YH_IDX) : aopInReg (right->aop, i, XL_IDX) || aopInReg (right->aop, i, XH_IDX)) + { + if (!regalloc_dry_run) + wassertl (0, "Overwriting pointer"); + cost (180, 180); + } + + if (bit_field && blen < 8 && right->aop->type == AOP_LIT) // We can save a lot of shifting and masking using the known literal value + { + unsigned char bval = (byteOfVal (right->aop->aopu.aop_lit, i) << bstr) & ((0xff >> (8 - blen)) << bstr); + + if (((~((0xff >> (8 - blen)) << bstr) & 0xff) | bval) == 0xff) + { + if (!i) + { + emit2 ("ld", use_y ? "a, (y)" : "a, (x)"); + cost (1 + use_y, 1); + } + else + { + emit2 ("ld", use_y ? "a, (0x%x, y)" : "a, (0x%x, x)", i); + cost ((size - 1 - i < 256 ? 2 : 3) + use_y, 1); + } + } + else + { + emit2 ("ld", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + cost (2, 1); + if (!i) + { + emit2 ("and", use_y ? "a, (y)" : "a, (x)"); + cost (1 + use_y, 1); + } + else + { + emit2 ("and", use_y ? "a, (0x%x, y)" : "a, (0x%x, x)", i); + cost ((size - 1 - i < 256 ? 2 : 3) + use_y, 1); + } + } + if (bval) + { + emit2 ("or", "a, #0x%02x", bval); + cost (2, 1); + } + goto store; + } + + if (pushed_a && aopInReg (right->aop, i, A_IDX)) + { + emit2 ("ld", "a, (1, sp)"); + cost (2, 1); + } + else + cheapMove (ASMOP_A, 0, right->aop, i, FALSE); + + if (bit_field && blen < 8) + { + if (bstr >= 4) + emit3 (A_SWAP, ASMOP_A, 0); + for (j = (bstr >= 4 ? 4 : 0); j < bstr; j++) + emit3 (A_SLL, ASMOP_A, 0); + emit2 ("and", "a, #0x%02x", (0xff >> (8 - blen)) << bstr); + cost (2, 1); + push (ASMOP_A, 0, 1); + pushed_a++; + emit2 ("ld", "a, #0x%02x", ~((0xff >> (8 - blen)) << bstr) & 0xff); + cost (2, 1); + if (!i) + { + emit2 ("and", use_y ? "a, (y)" : "a, (x)", i); + cost (1 + use_y, 1); + } + else + { + emit2 ("and", use_y ? "a, (0x%x, y)" : "a, (0x%x, x)", i); + cost ((size - 1 - i < 256 ? 2 : 3) + use_y, 1); + } + emit2 ("or", "a, (1, sp)"); + cost (2, 1); + } + +store: + + if (!(bit_field ? i : size - 1 - i)) + { + emit2 ("ld", use_y ? "(y), a" : "(x), a"); + cost (1 + use_y, 1); + } + else + { + emit2 ("ld", use_y ? "(0x%x, y), a" : "(0x%x, x), a", bit_field ? i : size - 1 - i); + cost ((size - 1 - i < 256 ? 2 : 3) + use_y, 1); + } + } + + while (pushed_a--) + pop (ASMOP_A, 0, 1); + +release: + freeAsmop (right); + freeAsmop (left); +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (const iCode *ic) +{ + // todo: This function currently reports code size costs only, other costs will depend on profiler information. + bool inv = FALSE; + operand *const cond = IC_COND (ic); + sym_link *type = operandType (cond); + symbol *const tlbl = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + symbol *tlbl2 = NULL; + aopOp (cond, ic); + + D (emit2 ("; genIfx", "")); + + if (IS_BOOL (type) && cond->aop->type == AOP_DIR) + { + if (tlbl) + emit2 (IC_FALSE (ic) ? "btjt" : "btjf", "%s, #0, !tlabel", aopGet (cond->aop, 0), labelKey2num (tlbl->key)); + cost (5, 0); + } + else if (aopInReg (cond->aop, 0, C_IDX)) + { + wassertl (IS_BOOL (type), "Variable of type other than _Bool in carry bit."); + if (tlbl) + emit2 (IC_FALSE (ic) ? "jrc" : "jrnc", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 0); + } + else if (aopRS (cond->aop) || cond->aop->type == AOP_DIR) + { + int i; + + for (i = 0; i < cond->aop->size;) // todo: Use tnzw; test a first, if dead, to free a; use swapw followed by exg to test xh if xl is dead (same for yh), use tnzw independently of where in the operand xl and xh are. + { + bool floattopbyte = (i == cond->aop->size - 1) && IS_FLOAT(type); + bool floattopword = (i == cond->aop->size - 2) && IS_FLOAT(type); + + if (!floattopword && i + 1 < cond->aop->size && + (aopInReg (cond->aop, i, X_IDX) || aopInReg (cond->aop, i, Y_IDX) || + (cond->aop->type == AOP_REG && (cond->aop->aopu.bytes[i].byteu.reg->rIdx == XH_IDX && cond->aop->aopu.bytes[i + 1].byteu.reg->rIdx == XL_IDX || cond->aop->aopu.bytes[i].byteu.reg->rIdx == YH_IDX && cond->aop->aopu.bytes[i + 1].byteu.reg->rIdx == YL_IDX)))) + { + bool in_y = (aopInReg (cond->aop, i, Y_IDX) || aopInReg (cond->aop, i, YH_IDX) && aopInReg (cond->aop, i + 1, YL_IDX)); + emit3w (A_TNZW, in_y ? ASMOP_Y : ASMOP_X, 0); + i += 2; + } + else if (i + 1 < cond->aop->size && regDead (X_IDX, ic) && cond->aop->regs[XL_IDX] < i && cond->aop->regs[XH_IDX] < i && + (aopOnStack (cond->aop, i, 2) || cond->aop->type == AOP_DIR)) + { + genMove_o (ASMOP_X, 0, cond->aop, i, 2, regDead (A_IDX, ic) && cond->aop->regs[A_IDX] < i, TRUE, FALSE); + if (floattopword) + emit3w (A_SLLW, ASMOP_X, 0); + i += 2; + } + else if (i + 1 < cond->aop->size && regDead (Y_IDX, ic) && cond->aop->regs[YL_IDX] < i && cond->aop->regs[YH_IDX] < i && + (aopOnStack (cond->aop, i, 2) || cond->aop->type == AOP_DIR)) + { + genMove_o (ASMOP_Y, 0, cond->aop, i, 2, regDead (A_IDX, ic) && cond->aop->regs[A_IDX] < i, FALSE, TRUE); + if (floattopword) + emit3w (A_SLLW, ASMOP_Y, 0); + i += 2; + } + else if ((aopInReg (cond->aop, i, XL_IDX) || aopInReg (cond->aop, i, XH_IDX) || aopInReg (cond->aop, i, YL_IDX) || aopInReg (cond->aop, i, YH_IDX)) && regDead (A_IDX, ic) && cond->aop->regs[A_IDX] <= i) + { + cheapMove (ASMOP_A, 0, cond->aop, i, FALSE); + emit3 (floattopbyte ? A_SLL : A_TNZ, ASMOP_A, 0); + i++; + } + // We can't just use swap_to_a() to improve the following four cases because it might use rrwa and rlwa which destroy the Z flag. + else if (aopInReg (cond->aop, i, XL_IDX) && (!floattopbyte || regDead (XL_IDX, ic))) + { + emit2 ("exg", "a, xl"); + cost (1, 1); + emit3(floattopbyte ? A_SLL : A_TNZ, ASMOP_A, 0); + emit2 ("exg", "a, xl"); + cost (1, 1); + i++; + } + else if (aopInReg (cond->aop, i, YL_IDX) && (!floattopbyte || regDead (YL_IDX, ic))) + { + emit2 ("exg", "a, yl"); + cost (1, 1); + emit3(floattopbyte ? A_SLL : A_TNZ, ASMOP_A, 0); + emit2 ("exg", "a, yl"); + cost (1, 1); + i++; + } + else if (!floattopbyte && !aopInReg (cond->aop, i, XH_IDX) && !aopInReg (cond->aop, i, YH_IDX)) + { + emit3_o (A_TNZ, cond->aop, i, 0, 0); + i++; + } + else if (floattopbyte && aopInReg (cond->aop, i, A_IDX)) + { + emit2 ("and", "a, 0x7f"); + cost (2, 1); + i++; + } + else + { + push (ASMOP_A, 0, 1); + cheapMove (ASMOP_A, 0, cond->aop, i, FALSE); + emit3(floattopbyte ? A_SLL : A_TNZ, ASMOP_A, 0); + pop (ASMOP_A, 0, 1); + i++; + } + + if (!inv && i < cond->aop->size && !IC_FALSE (ic)) + { + tlbl2 = (regalloc_dry_run ? 0 : newiTempLabel (NULL)); + inv = TRUE; + } + + if (tlbl) + emit2 ((!!IC_FALSE (ic) ^ (inv && i != cond->aop->size)) ? "jrne" : "jreq", "!tlabel", labelKey2num ((inv && i == cond->aop->size) ? tlbl2->key : tlbl->key)); + cost (2, 0); + } + } + else if (cond->aop->type == AOP_IMMD) + { + // An AOP_IMMD points to something valid, so it is not a null pointer. Just fall through to the unconditional jump generated below. + } + else + { + if (!regalloc_dry_run) + { + printf ("cond aop type %d, size %d\n", cond->aop->type, cond->aop->size); + wassertl (0, "Unimplemented conditional jump."); + } + cost (180, 180); + } + + if (inv) + { + emitLabel (tlbl); + emitJP (IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic), 0.0f); + emitLabel (tlbl2); + } + else + { + emitJP (IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic), 0.0f); + emitLabel (tlbl); + } + + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (const iCode *ic) +{ + const symbol *sym; + operand *result, *left, *right; + + D (emit2 ("; genAddrOf", "")); + + result = IC_RESULT (ic); + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + + wassert (result); + wassert (left); + wassert (IS_TRUE_SYMOP (left)); + wassert (right && IS_OP_LITERAL (IC_RIGHT (ic))); + + sym = OP_SYMBOL_CONST (left); + wassert (sym); + + aopOp (result, ic); + + // todo: When optimizing for size, putting on-stack address into y when y is free is cheaper calculating in x, then using exgw. + if (aopInReg (result->aop, 0, Y_IDX) || regDead (Y_IDX, ic) && !regDead (X_IDX, ic)) + { + if (!sym->onStack) + { + wassert (sym->name); + emit2 ("ldw", "y, #%s+%ld", sym->rname, (long)(operandLitValue (right))); + cost (4, 2); + } + else + { + emit2 ("ldw", "y, sp"); + cost (2, 1); + if ((long)(sym->stack) + G.stack.pushed + 1 + (long)(operandLitValue (right)) != 1l) + { + emit2 ("addw", "y, #%ld", (long)(sym->stack) + G.stack.pushed + 1 + (long)(operandLitValue (right))); + cost (4, 2); + } + else + emit3w (A_INCW, ASMOP_Y, 0); + } + genMove (result->aop, ASMOP_Y, regDead (A_IDX, ic), FALSE, regDead (X_IDX, ic)); + } + else if (!(regDead (XH_IDX, ic) ^ regDead (XL_IDX, ic))) + { + if (!regDead (X_IDX, ic)) + push (ASMOP_X, 0, 2); + if (!sym->onStack) + { + wassert (sym->name); + emit2 ("ldw", "x, #%s+%ld", sym->rname, (long)(operandLitValue (right))); + cost (3, 2); + } + else + { + wassert (regalloc_dry_run || sym->stack + G.stack.pushed + 1 + (long)(operandLitValue (right)) > 0); + emit2 ("ldw", "x, sp"); + cost (1, 1); + if ((long)(sym->stack) + G.stack.pushed + 1 + (long)(operandLitValue (right)) > 2l) + { + emit2 ("addw", "x, #%ld", (long)(sym->stack) + G.stack.pushed + 1 + (long)(operandLitValue (right))); + cost (3, 2); + } + else + { + emit3w (A_INCW, ASMOP_X, 0); + if ((long)(sym->stack) + G.stack.pushed + 1 + (long)(operandLitValue (right)) > 1l) + emit3w (A_INCW, ASMOP_X, 0); + } + } + genMove (result->aop, ASMOP_X, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + if (!regDead (X_IDX, ic)) + pop (ASMOP_X, 0, 2); + } + else // todo: Handle case of y alive and x partially alive; todo: Use mov when destination is a global variable. + { + if (!regalloc_dry_run) + wassertl (0, "Unimplemented genAddrOf deadness."); + cost (180, 180); + } + + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generate code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (const iCode *ic) +{ + symbol *jtab = regalloc_dry_run ? 0 : newiTempLabel (0); + operand *cond; + + D (emit2 ("; genJumpTab", "")); + + wassertl (options.model != MODEL_LARGE, "Jump tables not implemented for large memory model."); + + cond = IC_JTCOND (ic); + + aopOp (cond, ic); + + if (!regDead (X_IDX, ic)) + { + wassertl (regalloc_dry_run, "Need free X for jump table."); + cost (180, 180); + } + + genMove (ASMOP_X, cond->aop, regDead (A_IDX, ic), TRUE, regDead (Y_IDX, ic)); + + emit3w (A_SLLW, ASMOP_X, 0); + + if (!regalloc_dry_run) + { + emit2 ("ldw", "x, (#!tlabel, x)", labelKey2num (jtab->key)); + emit2 ("jp", "(x)"); + } + cost (4, 3); + + emitLabel (jtab); + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + { + if (!regalloc_dry_run) + emit2 (".dw", "#!tlabel", labelKey2num (jtab->key)); + cost (2, 0); + } + + freeAsmop (cond); +} + +/*-----------------------------------------------------------------*/ +/* genCast - generate code for cast */ +/*-----------------------------------------------------------------*/ +static void +genCast (const iCode *ic) +{ + operand *result, *right; + int size, offset; + sym_link *resulttype, *righttype; + + D (emit2 ("; genCast", "")); + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + resulttype = operandType (result); + righttype = operandType (right); + + if ((getSize (resulttype) <= getSize (righttype) || !IS_SPEC (righttype) || (SPEC_USIGN (righttype) || IS_BOOL (righttype))) && + (!IS_BOOL (resulttype) || IS_BOOL (righttype))) + { + genAssign (ic); + return; + } + + aopOp (right, ic); + aopOp (result, ic); + + if (IS_BOOL (resulttype) && right->aop->size == 1 && + (aopInReg (right->aop, 0, A_IDX) || (right->aop->type != AOP_REG && right->aop->type != AOP_REGSTK) || !right->aop->aopu.bytes[0].in_reg)) + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + if (aopInReg(right->aop, 0, A_IDX)) + { + emit3 (A_NEG, ASMOP_A, 0); + emit3 (A_CLR, ASMOP_A, 0); + } + else + { + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_CP, ASMOP_A, right->aop); + } + emit3 (A_RLC, ASMOP_A, 0); + cheapMove (result->aop, 0, ASMOP_A, 0, FALSE); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + else if (IS_BOOL (resulttype) && right->aop->size == 2 && + (aopInReg (right->aop, 0, X_IDX) && regDead (X_IDX, ic) || aopInReg (right->aop, 0, Y_IDX) && regDead (Y_IDX, ic))) + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0, 1); + + emit3w (A_NEGW, right->aop, 0); + cost (aopInReg (right->aop, 0, X_IDX) ? 1 : 2, 2); + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_RLC, ASMOP_A, 0); + cheapMove (result->aop, 0, ASMOP_A, 0, FALSE); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + else if (IS_BOOL (resulttype)) + { + bool a_need_clear = FALSE; + bool pushed_a = FALSE; + size = right->aop->size; + + for(offset = 1; offset < size; offset++) + if (aopInReg (right->aop, offset, A_IDX)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + break; + } + + if (!regDead (A_IDX, ic) && !pushed_a) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + for(offset = 0; offset < size; offset++) + { + const asmop *right_stacked = NULL; + int right_offset; + + right_stacked = stack_aop (right->aop, offset, &right_offset); + + if (offset && aopInReg (right->aop, offset, A_IDX)) + { + right_stacked = ASMOP_A; + right_offset = 0; + } + + if (!offset && aopInReg (right->aop, offset, A_IDX)) + { + emit3 (A_NEG, ASMOP_A, 0); + emit3 (A_CLR, ASMOP_A, 0); + } + else if (!right_stacked) + { + emit3 (A_CLR, ASMOP_A, 0); + emit3_o(offset ? A_SBC : A_SUB, ASMOP_A, 0, right->aop, offset); + a_need_clear = TRUE; + } + else + { + emit3 (A_CLR, ASMOP_A, 0); + emit2 (offset ? "sbc" : "sub", "a, (%d, sp)", right_offset); + a_need_clear = TRUE; + if (!aopInReg (right->aop, offset, A_IDX)) + pop (right_stacked, 0, 2); + } + } + if (a_need_clear) + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_RLC, ASMOP_A, 0); + cheapMove (result->aop, 0, ASMOP_A, 0, FALSE); + + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + else if (pushed_a) + adjustStack (1, FALSE, FALSE, FALSE); + } + else // Cast to signed type + { + bool pushed_a = FALSE; + + genMove_o (result->aop, 0, right->aop, 0, right->aop->size, regDead (A_IDX, ic), regDead (X_IDX, ic), regDead (Y_IDX, ic)); + + size = result->aop->size - right->aop->size; + offset = right->aop->size; + + if (size == 2 && (aopInReg (result->aop, offset, X_IDX) || aopInReg (result->aop, offset, Y_IDX)) && + (aopInReg (result->aop, right->aop->size - 1, XH_IDX) || aopInReg (result->aop, right->aop->size - 1, YH_IDX) || aopInReg (result->aop, right->aop->size - 1, A_IDX) || aopOnStack (result->aop, right->aop->size - 1, 1) || result->aop->type == AOP_DIR)) + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + emit3w_o (A_CLRW, result->aop, offset, 0, 0); + + if (aopInReg (result->aop, right->aop->size - 1, XH_IDX)) + emit3w (A_TNZW, ASMOP_X, 0); + else if (aopInReg (result->aop, right->aop->size - 1, YH_IDX)) + emit3w (A_TNZW, ASMOP_Y, 0); + else + emit3_o (A_TNZ, result->aop, right->aop->size - 1, 0, 0); + + if (!regalloc_dry_run) + emit2 ("jrpl", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // 2 for cycle cost is just an estimate; it also ignores pipelining. + emit3w_o (A_DECW, result->aop, offset, 0, 0); + emitLabel (tlbl); + + goto release; + } + + if (result->aop->regs[A_IDX] >= 0 && result->aop->regs[A_IDX] < right->aop->size || !regDead (A_IDX, ic)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + + cheapMove (ASMOP_A, 0, result->aop, right->aop->size - 1, FALSE); + emit3 (A_RLC, ASMOP_A, 0); + + + if (size == 2 && (aopInReg (result->aop, offset, X_IDX) || aopInReg (result->aop, offset, Y_IDX))) // Faster when just setting 16-bit reg. + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + emit3w_o (A_CLRW, result->aop, offset, 0, 0); + if (!regalloc_dry_run) + emit2 ("jrnc", "!tlabel", labelKey2num (tlbl->key)); + cost (2, 2); // 2 for cycle cost is just an estimate; it also ignores pipelining. + emit3w_o (A_DECW, result->aop, offset, 0, 0); + emitLabel (tlbl); + + if (pushed_a) + pop (ASMOP_A, 0, 1); + + goto release; + } + + emit3 (A_CLR, ASMOP_A, 0); + emit3 (A_SBC, ASMOP_A, ASMOP_ZERO); + while (size--) + { + if (size && aopInReg (result->aop, offset, A_IDX)) + { + push (ASMOP_A, 0, 1); + pushed_a = TRUE; + } + else + cheapMove (result->aop, offset, ASMOP_A, 0, FALSE); + offset++; + } + + if (pushed_a) + pop (ASMOP_A, 0, 1); + } + +release: + freeAsmop (right); + freeAsmop (result); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (const iCode *ic) +{ + operand *op; + int i; + + if ((op = IC_RIGHT (ic)) && IS_SYMOP (op)) + { + aopOp (op, ic); + + D (emit2 ("; genDummyRead", "")); + + if (!regDead(A_IDX, ic) && op->aop->type == AOP_DIR) + for (i = 0; i < op->aop->size; i++) + emit3_o (A_TNZ, op->aop, i, 0, 0); + else + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0 ,1); + for (i = 0; i < op->aop->size; i++) + cheapMove (ASMOP_A, 0, op->aop, i, FALSE); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + + freeAsmop (op); + } + + if ((op = IC_LEFT (ic)) && IS_SYMOP (op)) + { + aopOp (op, ic); + + D (emit2 ("; genDummyRead", "")); + + if (!regDead(A_IDX, ic) && op->aop->type == AOP_DIR) + for (i = 0; i < op->aop->size; i++) + emit3_o (A_TNZ, op->aop, i, 0, 0); + else + { + if (!regDead (A_IDX, ic)) + push (ASMOP_A, 0 ,1); + for (i = 0; i < op->aop->size; i++) + cheapMove (ASMOP_A, 0, op->aop, i, FALSE); + if (!regDead (A_IDX, ic)) + pop (ASMOP_A, 0, 1); + } + + freeAsmop (op); + } +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is to be rematerialized */ +/*-----------------------------------------------------------------*/ +static bool +resultRemat (const iCode *ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + const symbol *sym = OP_SYMBOL_CONST (IC_RESULT (ic)); + + if (!sym->remat) + return(false); + + bool completely_spilt = TRUE; + for (unsigned int i = 0; i < getSize (sym->type); i++) + if (sym->regs[i]) + completely_spilt = FALSE; + + if (completely_spilt) + return(true); + } + + return (false); +} + +/*---------------------------------------------------------------------*/ +/* genSTM8Code - generate code for STM8 for a single iCode instruction */ +/*---------------------------------------------------------------------*/ +static void +genSTM8iCode (iCode *ic) +{ + genLine.lineElement.ic = ic; + +#if 0 + if (!regalloc_dry_run) + printf ("ic %d op %d stack pushed %d\n", ic->key, ic->op, G.stack.pushed); +#endif + + if (resultRemat (ic)) + { + if (!regalloc_dry_run) + D (emit2 ("; skipping iCode since result will be rematerialized", "")); + return; + } + + if (ic->generated) + { + if (!regalloc_dry_run) + D (emit2 ("; skipping generated iCode", "")); + return; + } + + switch (ic->op) + { + case '!': + genNot (ic); + break; + + case '~': + genCpl (ic); + break; + + case UNARYMINUS: + genUminus (ic); + break; + + case IPUSH: + genIpush (ic); + break; + + case IPOP: + wassertl (0, "Unimplemented iCode"); + break; + + case CALL: + case PCALL: + genCall (ic); + break; + + case FUNCTION: + genFunction (ic); + break; + + case ENDFUNCTION: + genEndFunction (ic); + break; + + case RETURN: + genReturn (ic); + break; + + case LABEL: + genLabel (ic); + break; + + case GOTO: + genGoto (ic); + break; + + case '+': + genPlus (ic); + break; + + case '-': + genMinus (ic); + break; + + case '*': + genMult (ic); + break; + + case '/': + case '%': + genDivMod (ic); + break; + + case '>': + case '<': + case LE_OP: + case GE_OP: + genCmp(ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case NE_OP: + case EQ_OP: + genCmpEQorNE (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + case OR_OP: + wassertl (0, "Unimplemented iCode"); + break; + + case '^': + genXor (ic); + break; + + case '|': + genOr (ic); + break; + + case BITWISEAND: + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + genInline (ic); + break; + + case RRC: + case RLC: + wassertl (0, "Unimplemented iCode"); + break; + + case GETABIT: + genGetABit (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LEFT_OP: + genLeftShift (ic); + break; + + case RIGHT_OP: + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + genPointerGet (ic); + break; + + case SET_VALUE_AT_ADDRESS: + genPointerSet (ic); + break; + + case '=': + wassert (!POINTER_SET (ic)); + genAssign (ic); + break; + + case IFX: + genIfx (ic); + break; + + case ADDRESS_OF: + genAddrOf (ic); + break; + + case JUMPTABLE: + genJumpTab (ic); + break; + + case CAST: + genCast (ic); + break; + + case RECEIVE: + case SEND: + wassertl (0, "Unimplemented iCode"); + break; + + case DUMMY_READ_VOLATILE: + genDummyRead (ic); + break; + + case CRITICAL: + genCritical (ic); + break; + + case ENDCRITICAL: + genEndCritical (ic); + break; + + default: + wassertl (0, "Unknown iCode"); + } +} + +float +drySTM8iCode (iCode *ic) +{ + regalloc_dry_run = TRUE; + regalloc_dry_run_cost_bytes = 0; + regalloc_dry_run_cost_cycles = 0; + + initGenLineElement (); + + genSTM8iCode (ic); + + destroy_line_list (); + + wassert (regalloc_dry_run); + + const unsigned int byte_cost_weight = 2 << (optimize.codeSize * 3 + !optimize.codeSpeed * 3); + + return ((float)regalloc_dry_run_cost_bytes * byte_cost_weight + (float)regalloc_dry_run_cost_cycles * ic->count); +} + +/*---------------------------------------------------------------------*/ +/* genSTM8Code - generate code for STM8 for a block of intructions */ +/*---------------------------------------------------------------------*/ +void +genSTM8Code (iCode *lic) +{ + iCode *ic; + int clevel = 0; + int cblock = 0; + int cln = 0; + regalloc_dry_run = FALSE; + + /* if debug information required */ + if (options.debug && currFunc && !regalloc_dry_run) + debugFile->writeFunction (currFunc, lic); + + if (options.debug && !regalloc_dry_run) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + for (ic = lic; ic; ic = ic->next) + { + initGenLineElement (); + + genLine.lineElement.ic = ic; + + if (ic->level != clevel || ic->block != cblock) + { + if (options.debug) + debugFile->writeScope (ic); + clevel = ic->level; + cblock = ic->block; + } + + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + debugFile->writeCLine (ic); + + if (!options.noCcodeInAsm) + emit2 (";", "%s: %d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + cln = ic->lineno; + } + + regalloc_dry_run_cost_bytes = 0; + regalloc_dry_run_cost_cycles = 0; + + if (options.iCodeInAsm) + { + const char *iLine = printILine (ic); + emit2 ("; ic:", "%d: %s", ic->key, iLine); + dbuf_free (iLine); + } +#if 0 + emit2 (";", "count: %f", ic->count); +#endif + genSTM8iCode(ic); + +#if 0 + D (emit2 (";", "Cost for generated ic %d : (%d, %d)", ic->key, regalloc_dry_run_cost_bytes, regalloc_dry_run_cost_cycles)); +#endif + } + + if (options.debug) + debugFile->writeFrameAddress (NULL, NULL, 0); /* have no idea where frame is now */ + + /* now we are ready to call the + peephole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* now do the actual printing */ + printLine (genLine.lineHead, codeOutBuf); + + /* destroy the line list */ + destroy_line_list (); +} + diff --git a/src/stm8/gen.h b/src/stm8/gen.h new file mode 100644 index 0000000..79f7d63 --- /dev/null +++ b/src/stm8/gen.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + gen.h - header file for code generation for 8051 + + Written By - Philipp Krause . pkk@spth.de (2012) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef STM8GEN_H +#define STM8GEN_H 1 + +typedef enum +{ + AOP_INVALID, + /* Is a literal */ + AOP_LIT = 1, + /* Is in a register */ + AOP_REG, + /* Is partially in registers, partially on the stack */ + AOP_REGSTK, + /* Is on the stack */ + AOP_STK, + /* Is a stack location */ + AOP_STL, + /* Is an immediate value */ + AOP_IMMD, + /* Is in direct space */ + AOP_DIR, + /* Read undefined, discard writes */ + AOP_DUMMY, + /* Has been optimized out by jumping directly (see ifxForOp) */ + AOP_CND +} +AOP_TYPE; + +/* asmop_byte: A type for the location a single byte + of an operand can be in */ +typedef struct asmop_byte +{ + bool in_reg; + union + { + reg_info *reg; /* Register this byte is in. */ + long int stk; /* Stack offset for this byte. */ + } byteu; +} asmop_byte; + +/* asmop: A homogenised type for all the different + spaces an operand can be in */ +typedef struct asmop +{ + AOP_TYPE type; + short size; + union + { + value *aop_lit; + struct + { + char *immd; + int immd_off; + }; + int stk_off; + char *aop_dir; + asmop_byte bytes[8]; + } aopu; + signed char regs[6]; // Byte of this aop that is in the register. -1 if no byte of this aop is in the reg. +} +asmop; + +void genSTM8Code (iCode *); +void stm8_emitDebuggerSymbol (const char *); + +extern bool stm8_assignment_optimal; +extern long int stm8_call_stack_size; +extern bool stm8_extend_stack; + +#endif + diff --git a/src/stm8/main.c b/src/stm8/main.c new file mode 100644 index 0000000..49731a0 --- /dev/null +++ b/src/stm8/main.c @@ -0,0 +1,556 @@ +/*------------------------------------------------------------------------- + main.c - STM8 specific definitions. + + Philipp Klaus Krause 2012-2013 + Valentin Dudouyt 2013 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" + +#include "ralloc.h" +#include "gen.h" +#include "dbuf_string.h" +#include "peep.h" +#include "SDCCgas.h" + +#define OPTION_MEDIUM_MODEL "--model-medium" +#define OPTION_LARGE_MODEL "--model-large" +#define OPTION_CODE_SEG "--codeseg" +#define OPTION_CONST_SEG "--constseg" +#define OPTION_ELF "--out-fmt-elf" + +extern DEBUGFILE dwarf2DebugFile; +extern int dwarf2FinalizeFile(FILE *); + +static OPTION stm8_options[] = { + {0, OPTION_MEDIUM_MODEL, NULL, "16-bit address space for both data and code (default)"}, + {0, OPTION_LARGE_MODEL, NULL, "16-bit address space for data, 24-bit for code"}, + {0, OPTION_CODE_SEG, &options.code_seg, " use this name for the code segment", CLAT_STRING}, + {0, OPTION_CONST_SEG, &options.const_seg, " use this name for the const segment", CLAT_STRING}, + {0, OPTION_ELF, NULL, "Output executable in ELF format (deprecated)"}, + {0} +}; + +enum +{ + P_CODESEG = 1, + P_CONSTSEG, +}; + +static int +stm8_do_pragma (int id, const char *name, const char *cp) +{ + struct pragma_token_s token; + int processed = 1, error = 0; + + init_pragma_token (&token); + + switch (id) + { + case P_CODESEG: + case P_CONSTSEG: + { + char *segname; + + cp = get_pragma_token (cp, &token); + if (token.type == TOKEN_EOL) + { + error = 1; + break; + } + else + segname = Safe_strdup (get_pragma_string (&token)); + + cp = get_pragma_token (cp, &token); + if (token.type != TOKEN_EOL) + { + Safe_free (segname); + error = 1; + break; + } + else + { + if (id == P_CODESEG) + { + if (options.code_seg) + Safe_free (options.code_seg); + options.code_seg = segname; + } + else + { + if (options.const_seg) + Safe_free (options.const_seg); + options.const_seg = segname; + } + } + } + break; + default: + processed = 0; + break; + } + + if (error) + werror (W_BAD_PRAGMA_ARGUMENTS, name); + + free_pragma_token (&token); + return processed; +} + +static struct pragma_s stm8_pragma_tbl[] = { + {"codeseg", P_CODESEG, 0, stm8_do_pragma}, + {"constseg", P_CONSTSEG, 0, stm8_do_pragma}, + {NULL, 0, 0, NULL}, +}; + +static int +stm8_process_pragma (const char *s) +{ + return process_pragma_tbl (stm8_pragma_tbl, s); +} + +static char stm8_defaultRules[] = { +#include "peeph.rul" + "" +}; + + +static char *stm8_keywords[] = { + "at", + "critical", + "interrupt", + "trap", + "naked", + NULL +}; + +static void +stm8_genAssemblerEnd (FILE *of) +{ + if (options.out_fmt == 'E' && options.debug) + { + dwarf2FinalizeFile (of); + } +} + +static void +stm8_init (void) +{ + asm_addTree (&asm_asxxxx_mapping); +} + + +static void +stm8_reset_regparm (struct sym_link *funcType) +{ +} + +static int +stm8_reg_parm (sym_link * l, bool reentrant) +{ + return FALSE; +} + +static bool +stm8_parseOptions (int *pargc, char **argv, int *i) +{ + const char *const arg = argv[*i]; + + if (!strcmp (arg, OPTION_ELF)) + { + options.out_fmt = 'E'; + debugFile = &dwarf2DebugFile; + return TRUE; + } + + return FALSE; +} + +static void +stm8_finaliseOptions (void) +{ + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + + if (options.model == MODEL_LARGE) + { + port->s.funcptr_size = 3; + port->stack.call_overhead = 3; + port->jumptableCost.maxCount = 0; + } +} + +static void +stm8_setDefaultOptions (void) +{ + options.nopeep = 0; + options.stackAuto = 1; + options.intlong_rent = 1; + options.float_rent = 1; + options.noRegParams = 0; + options.data_loc = 0x0001; /* We can't use the byte at address zero in C, since NULL pointers have special meaning */ + options.code_loc = 0x8000; + + options.stack_loc = -1; /* Do not set the stack pointer in software- just use the device-specific reset value. */ + + options.out_fmt = 'i'; /* Default output format is ihx */ +} + +static const char * +stm8_getRegName (const struct reg_info *reg) +{ + if (reg) + return reg->name; + return "err"; +} + +static void +stm8_genExtraArea (FILE *of, bool hasMain) +{ + fprintf (of, "\n; default segment ordering for linker\n"); + tfprintf (of, "\t!area\n", HOME_NAME); + tfprintf (of, "\t!area\n", STATIC_NAME); + tfprintf (of, "\t!area\n", port->mem.post_static_name); + tfprintf (of, "\t!area\n", CONST_NAME); + tfprintf (of, "\t!area\n", "INITIALIZER"); + tfprintf (of, "\t!area\n", CODE_NAME); + fprintf (of, "\n"); +} + +static void +stm8_genInitStartup (FILE *of) +{ + fprintf (of, "__sdcc_gs_init_startup:\n"); + + if (options.stack_loc >= 0) + { + fprintf (of, "\tldw\tx, #0x%04x\n", options.stack_loc); + fprintf (of, "\tldw\tsp, x\n"); + } + + /* Init static & global variables */ + fprintf (of, "__sdcc_init_data:\n"); + fprintf (of, "; stm8_genXINIT() start\n"); + + /* Zeroing memory (required by standard for static & global variables) */ + fprintf (of, "\tldw x, #l_DATA\n"); + fprintf (of, "\tjreq\t00002$\n"); + fprintf (of, "00001$:\n"); + fprintf (of, "\tclr (s_DATA - 1, x)\n"); + fprintf (of, "\tdecw x\n"); + fprintf (of, "\tjrne\t00001$\n"); + fprintf (of, "00002$:\n"); + + /* Copy l_INITIALIZER bytes from s_INITIALIZER to s_INITIALIZED */ + fprintf (of, "\tldw\tx, #l_INITIALIZER\n"); + fprintf (of, "\tjreq\t00004$\n"); + fprintf (of, "00003$:\n"); + fprintf (of, "\tld\ta, (s_INITIALIZER - 1, x)\n"); + fprintf (of, "\tld\t(s_INITIALIZED - 1, x), a\n"); + fprintf (of, "\tdecw\tx\n"); + fprintf (of, "\tjrne\t00003$\n"); + fprintf (of, "00004$:\n"); + fprintf (of, "; stm8_genXINIT() end\n"); +} + +#define STM8_INTERRUPTS_COUNT 30 + +int +stm8_genIVT(struct dbuf_s * oBuf, symbol ** intTable, int intCount) +{ + int i; + dbuf_tprintf (oBuf, "\tint s_GSINIT ; reset\n"); + + if(intCount > STM8_INTERRUPTS_COUNT) + { + werror(E_INT_BAD_INTNO, intCount - 1); + intCount = STM8_INTERRUPTS_COUNT; + } + + if (interrupts[INTNO_TRAP] || intCount) + dbuf_printf (oBuf, "\tint %s ; trap\n", interrupts[INTNO_TRAP] ? interrupts[INTNO_TRAP]->rname : "0x000000"); + + for (i = 0; i < intCount; i++) + dbuf_printf (oBuf, "\tint %s ; int%d\n", interrupts[i] ? interrupts[i]->rname : "0x000000", i); + + return TRUE; +} + +/*----------------------------------------------------------------------*/ +/* stm8_dwarfRegNum - return the DWARF register number for a register. */ +/*----------------------------------------------------------------------*/ +static int +stm8_dwarfRegNum (const struct reg_info *reg) +{ + return reg->rIdx; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + int result_size = IS_SYMOP (IC_RESULT (ic)) ? getSize (OP_SYM_TYPE (IC_RESULT(ic))) : 4; + sym_link *test = NULL; + + if (IS_LITERAL (left)) + test = left; + else if (IS_LITERAL (right)) + test = right; + + switch (ic->op) + { + case '/': + case '%': + return (getSize (left) <= 2 && IS_UNSIGNED (left) && getSize (right) <= 2 && IS_UNSIGNED (right)); + case '*': + { + if (result_size == 1 || getSize (left) <= 1 && getSize (right) <= 1 && result_size == 2 && IS_UNSIGNED (left) && IS_UNSIGNED (right)) + return TRUE; + + if ((getSize (left) != 2 || getSize (right) != 2) || result_size != 2 || !test) + return FALSE; + + unsigned long long add, sub; + int topbit, nonzero; + + + if (floatFromVal (valFromType (test)) < 0 || csdOfVal (&topbit, &nonzero, &add, &sub, valFromType (test))) + return FALSE; + + int shifts = topbit; + + // If the leading digits of the cse are 1 0 -1 we can use 0 1 1 instead to reduce the number of shifts. + if (topbit >= 2 && (add & (1ull << topbit)) && (sub & (1ull << (topbit - 2)))) + shifts--; + + wassert (nonzero); + + // Shifts are 1 byte, additions and subtractions are 3 bytes. + if (shifts + 3 * (nonzero - 1) <= 9 - optimize.codeSize + 3 * optimize.codeSpeed) + return TRUE; + + return FALSE; + } + default: + return FALSE; + } +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + return (op == GETABIT); +} + +static const char * +get_model (void) +{ + switch (options.model) + { + case MODEL_MEDIUM: + return ("stm8"); + break; + case MODEL_LARGE: + return ("stm8-large"); + break; + default: + werror (W_UNKNOWN_MODEL, __FILE__, __LINE__); + return "unknown"; + } +} + +/** $1 is always the basename. + $2 is always the output file. + $3 varies + $l is the list of extra options that should be there somewhere... + MUST be terminated with a NULL. +*/ +static const char *_linkCmd[] = +{ + "sdldstm8", "-nf", "\"$1\"", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *stm8AsmCmd[] = +{ + "sdasstm8", "$l", "$3", "\"$1.asm\"", NULL +}; + +static const char *const _libs_stm8[] = { "stm8", NULL, }; + +PORT stm8_port = +{ + TARGET_ID_STM8, + "stm8", + "STM8", /* Target name */ + NULL, /* Processor name */ + { + glue, + TRUE, /* We want stm8_genIVT to be triggered */ + MODEL_MEDIUM | MODEL_LARGE, + MODEL_MEDIUM, + &get_model, /* model string used as library destination */ + gas_glue + }, + { /* Assembler */ + stm8AsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _linkCmd, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + NULL, /* crt */ + _libs_stm8, /* libs */ + }, + { /* Peephole optimizer */ + stm8_defaultRules, + stm8instructionSize, + NULL, + NULL, + NULL, + stm8notUsed, + stm8canAssign, + stm8notUsedFrom, + NULL, + }, + /* Sizes: char, short, int, long, long long, ptr, fptr, gptr, bit, float, max */ + { + 1, /* char */ + 2, /* short */ + 2, /* int */ + 4, /* long */ + 8, /* long long */ + 2, /* near ptr */ + 2, /* far ptr */ + 2, /* generic ptr */ + 2, /* func ptr */ + 0, /* banked func ptr */ + 1, /* bit */ + 4, /* float */ + }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", /* code */ + "DATA", /* data */ + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", /* reg */ + "GSINIT", /* static initialization */ + NULL, /* overlay */ + "GSFINAL", /* gsfinal */ + "HOME", /* home */ + NULL, /* xidata */ + NULL, /* xinit */ + "CONST", /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { stm8_genExtraArea, NULL }, + { /* stack information */ + -1, /* direction */ + 0, + 7, /* isr overhead */ + 2, /* call overhead */ + 0, + 2, + 1, /* sp points to next free stack location */ + }, + { -1, TRUE }, + { stm8_emitDebuggerSymbol, + { + stm8_dwarfRegNum, + 0, /* cfiSame */ + 0, /* cfiUndef */ + 4, /* addressSize */ + 9, /* regNumRet */ + SP_IDX, /* regNumSP */ + 0, /* regNumBP */ + 2, /* offsetSP */ + }, + }, + { + 32767, /* maxCount */ + 2, /* sizeofElement */ + {4, 5, 5}, /* sizeofMatchJump[] - assuming operand in reg, inverse can be optimized away - would be much higher otherwise */ + {4, 5, 5}, /* sizeofRangeCompare[] - same as above */ + 3, /* sizeofSubtract - assuming 2 byte index, would be 2 otherwise */ + 5, /* sizeofDispatch - 1 byte for sllw followed by 3 bytes for ldw x, (..., X) and 2 byte for jp (x) */ + }, + "_", + stm8_init, + stm8_parseOptions, + stm8_options, + NULL, + stm8_finaliseOptions, + stm8_setDefaultOptions, + stm8_assignRegisters, + stm8_getRegName, + 0, + NULL, + stm8_keywords, + NULL, + stm8_genAssemblerEnd, + stm8_genIVT, + 0, /* no genXINIT code */ + stm8_genInitStartup, /* genInitStartup */ + stm8_reset_regparm, + stm8_reg_parm, + stm8_process_pragma, /* process_pragma */ + NULL, /* getMangledFunctionName */ + _hasNativeMulFor, /* hasNativeMulFor */ + hasExtBitOp, /* hasExtBitOp */ + NULL, /* oclsExpense */ + TRUE, + FALSE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + NULL, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 5, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; diff --git a/src/stm8/peep.c b/src/stm8/peep.c new file mode 100644 index 0000000..238b7c1 --- /dev/null +++ b/src/stm8/peep.c @@ -0,0 +1,1236 @@ +#include "common.h" +#include "SDCCicode.h" +#include "SDCCglobl.h" +#include "SDCCgen.h" + +#include "peep.h" + +#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0) + +// #define D(_s) { printf _s; fflush(stdout); } +#define D(_s) + +#define EQUALS(l, i) (!STRCASECMP((l), (i))) +#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) +#define STARTSINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1)) + +typedef enum +{ + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +/*----------------------------------------------------------------------------*/ +/* strNextCharBlock - Returns the next block of chars (after spaces, comma) */ +/* Leading spaces and Current block are skipped and search stops at next block*/ +/* Valid block separators are: ' ' and ',' */ +/* If no block is found (EOS or ';'), returns NULL */ +/*----------------------------------------------------------------------------*/ +static char * +strNextCharBlock(const char *str) +{ + if (!str || !str[0]) + return 0; + + while (isblank ((unsigned char)(str[0]))) + str++; // skip leading blanks + + while (str[0] && !isblank ((unsigned char)(str[0])) && str[0] != ';') + { + if (str[0] == ',') + { + str++; // current block is finished with ',' + break; + } + str++; // next char of current block + } + + while (isblank ((unsigned char)(str[0]))) + str++; // skip trailing blanks + + if (str[0] && str[0] != ';') + return (char *)str; + return 0; +} + +static bool +isInt(const char *str) +{ + int ret; + while(str[0] == '#' || str[0] == '(' || str[0] == '[' || isspace ((unsigned char)str[0])) + str++; + if(sscanf(str, "0x%x", &ret)) + return(ret); + if(!sscanf(str, "%d", &ret)) + return(false); + return(true); +} + +static int +readint(const char *str) +{ + int ret; + while(str[0] == '#' || str[0] == '(' || str[0] == '[' || isspace ((unsigned char)str[0])) + str++; + if(sscanf(str, "0x%x", &ret)) + return(ret); + if(!sscanf(str, "%d", &ret)) + { + wassertl (0, "readint() got non-integer argument:"); + fprintf (stderr, "%s\n", str); + ret = -1; + } + return(ret); +} + +static int +isReg(const char *what) +{ + if(what[0] == '(') + what++; + if(what[0] == 'a' || what[0] == 'x' || what[0] == 'y') + return(true); + if(!strcmp(what, "sp")) + return(true); + return(false); +} + +static char * +nextToken(char *p) +{ + /* strtok replacement */ + static char *str, *ret, *end; + // Use an internal buffer to prevent *p from being modified + static char buf[128]; + if(p) { + strncpy(buf, p, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + str = buf; + end = buf + strlen(p); + } + if(str >= end) + return(NULL); + ret = str; + // Strip separators + while(*str == ',' || isspace(*str)) + str++; + if(*str == '(') + { + // Take an expression in brackets + while(*str && *str != ')') + str++; + str++; + } else { + // Take until EOL or separator + while(*str && *str != ',' && !isspace(*str)) + str++; + } + *str = '\0'; + str++; + return(ret); +} + +static bool +isRelativeAddr(const char *what, const char *mode) +{ + char buf[4]; + strcpy(buf, mode); + strcat(buf, ")"); + return(what[0] == '(' && strstr(what, buf)); +} + +static bool +isLabel(const char *what) +{ + const char *end; + + end = strchr(what, '+'); + if(!end) + end = what + strlen(what); + if(what[0] == '(' && !strchr(what, ',')) + what++; + if(what[0] == '#') + return (what[1] == '_' || what[1] == '<' || what[1] == '>'); + return(what[0] == '_' || *(end-1) == '$'); +} + +static bool +isImmediate(const char *what) +{ + return(what[0] == '#'); +} + +static bool +isShortoff(const char *what, const char *mode) +{ + return(isRelativeAddr(what, mode) && isInt(what) && readint(what) <= 0xff); +} + +static bool +isLongoff(const char *what, const char *mode) +{ + return(isRelativeAddr(what, mode) && (!isInt(what) || readint(what) > 0xff)); +} + +static bool +isPtr(const char *what) +{ + return(what[0] == '[' || what[0] == '(' && (what[1] == '[' || what[1] == '(')); +} + +static bool +isSpIndexed(const char *what) +{ + return isRelativeAddr(what, "sp"); +} + +/*-----------------------------------------------------------------*/ +/* stm8InstIsRegToReg - Checks if 'line' is a reg to reg move */ +/* isword == FALSE : Look for registers a, xl, xh, yl & yh */ +/* isword == TRUE : Look for registers x, y & sp */ +/*-----------------------------------------------------------------*/ +static bool +stm8InstIsRegToReg(const char *line, bool isword) +{ + int regNumber = 0; + + if ((line = strNextCharBlock(line))) + { + while(line[0]) + { + bool regFound = false; + char chrLow = tolower ((unsigned char)line[0]); + + // Check for register names + if (isword) + { + if (chrLow == 'x' || chrLow == 'y') + regFound = true; + + if (chrLow == 's') + { + line++; + if (line[0] == 'p') + regFound = true; + else + return false; + } + } + else + { + if (chrLow == 'a') + regFound = true; + + if (chrLow == 'x' || chrLow == 'y') + { + line++; + chrLow = tolower ((unsigned char)line[0]); + if (chrLow == 'h' || chrLow == 'l') + regFound = true; + else + return false; + } + } + + // If register, process next character + if (regFound) + line++; + + // Continue only if valid separator or end + if (!line[0] || line[0] == ',' || isblank (line[0])) + { + if (regFound) + regNumber++; + } + else + { + return false; + } + + // Next char if not eos + if(line[0]) + line++; + } + } + return (regNumber == 2); +} + +int +stm8instructionSize(lineNode *pl) +{ // this function is quite rough, it makes all indirect addressing cases to the longest. + char *operand; + char *op1start; + char *op2start; + + operand = nextToken(pl->line); + op1start = nextToken(NULL); + op2start = nextToken(NULL); + + while(op1start && isspace((unsigned char)op1start[0])) op1start++; + while(op2start && isspace((unsigned char)op2start[0])) op2start++; + //printf("line=%s operand=%s op1start=%s op2start=%s\n", pl->line, operand, op1start, op2start); + + /* Operations that always costs 1 byte */ + if (ISINST(operand, "ccf") + || ISINST(operand, "divw") + || ISINST(operand, "exgw") + || ISINST(operand, "iret") + || ISINST(operand, "nop") + || ISINST(operand, "rcf") + || ISINST(operand, "ret") + || ISINST(operand, "retf") + || ISINST(operand, "rvf") + || ISINST(operand, "break") + || ISINST(operand, "halt") + || ISINST(operand, "rim") + || ISINST(operand, "trap") + || ISINST(operand, "wfi") + || ISINST(operand, "sim") + || ISINST(operand, "scf")) + return 1; + + /* Operations that always costs 3 byte */ + if(ISINST(operand, "jrh") + || ISINST(operand, "jrnh") + || ISINST(operand, "jril") + || ISINST(operand, "jrih") + || ISINST(operand, "jrm") + || ISINST(operand, "jrnm")) + return 3; + + /* Operations that always costs 2 byte */ + if(STARTSINST(operand, "jr") + || ISINST(operand, "callr") + || ISINST(operand, "wfe")) + return 2; + + /* Operations that always costs 4 byte */ + if(ISINST(operand, "bccm") + || ISINST(operand, "bcpl") + || ISINST(operand, "bres") + || ISINST(operand, "bset") + || ISINST(operand, "callf") + || ISINST(operand, "int") + || ISINST(operand, "jpf")) + return 4; + + /* Operations that always costs 5 byte */ + if(ISINST(operand, "btjf") + || ISINST(operand, "btjt")) + return 5; + + if (EQUALS(operand, "push") + || EQUALS(operand, "pop")) + { + wassert (op1start); + if (!strcmp(op1start, "a")) + return 1; + if (!strcmp(op1start, "cc")) + return 1; + if (isImmediate(op1start)) // immediate + return 2; + else // longmem + return 3; + } + + /* arity=1 */ + if(EQUALS(operand, "clr") + || EQUALS(operand, "dec") + || EQUALS(operand, "inc") + || EQUALS(operand, "swap") + || EQUALS(operand, "jp") + || EQUALS(operand, "call") + || EQUALS(operand, "cpl") + || EQUALS(operand, "neg") + || EQUALS(operand, "sll") + || EQUALS(operand, "sla") + || EQUALS(operand, "srl") + || EQUALS(operand, "sra") + || EQUALS(operand, "rlc") + || EQUALS(operand, "rrc") + || EQUALS(operand, "tnz")) + { + int i = 0; + + wassert (op1start); + if(!strcmp(op1start, "a") || !strcmp(op1start, "(x)")) + return(1); + if(!strcmp(op1start, "(y)")) + return(2); + if(op1start[0] == '('|| op1start[0] == '[') + op1start++; + if(strstr(op1start, ",y)")) + i++; // costs extra byte for operating with y + if ((ISINST(operand, "jp") || ISINST(operand, "call")) && *op1start != '(' && *op1start != '[') // jp and call are 3 bytes for direct long addressing mode. + return(3); + if(isLabel(op1start)) + return(4); + if(readint(op1start) <= 0xFF) + return(2+i); + /* op1 > 0xFF */ + if((ISINST(operand, "jp") || ISINST(operand, "call")) && !strchr(op1start, 'y')) + return(3); + return(4); + } + + if(EQUALS(operand, "exg")) + { + assert (!strcmp(op1start, "a") && op2start != NULL); + if(isReg(op2start)) + return(1); + else + return(3); + } + + if(EQUALS(operand, "addw") || EQUALS(operand, "subw")) + { + assert (op1start != NULL); + if(!strcmp(op1start, "sp")) + return(2); + if(isImmediate(op2start) && op1start[0] == 'y') + return(4); + if(isImmediate(op2start) && op1start[0] == 'x') + return(3); + if(isSpIndexed(op2start)) + return(3); + return(4); + } + + if(ISINST(operand, "cplw")) + { + assert (op1start != NULL); + if(op1start[0] == 'y') + return(2); + else + return(1); + } + + if(ISINST(operand, "ldf")) + { + assert (op1start != NULL); + if(isRelativeAddr(op1start, "y") || isRelativeAddr(op2start, "y")) + return(5); + else + return(4); + } + + /* Operations that costs 2 or 3 bytes for immediate */ + if(STARTSINST(operand, "ld") + || !strncmp(operand, "cp", 2) + || EQUALS(operand, "adc") + || EQUALS(operand, "add") + || EQUALS(operand, "and") + || EQUALS(operand, "bcp") + || EQUALS(operand, "or") + || EQUALS(operand, "sbc") + || EQUALS(operand, "sub") + || EQUALS(operand, "xor")) + { + int i = 0; + char suffix; + wassert (op1start && op2start); + suffix = operand[strlen(operand)-1]; + if(suffix == 'w' && isImmediate(op2start)) + { + i++; // costs extra byte + if(!strcmp(op1start, "y")) + i++; + } + if(isImmediate(op2start)) + return(2+i); // ld reg, #immd + if(isSpIndexed(op1start) || isSpIndexed(op2start)) + return(2); + if(!strcmp(op1start, "(x)") || !strcmp(op2start, "(x)")) + return(1); + if(!strcmp(op1start, "(y)") || !strcmp(op2start, "(y)")) + return(2); + if(isShortoff(op1start, "x") || isShortoff(op2start, "x")) + return(2); + if(isShortoff(op1start, "y") || isShortoff(op2start, "y")) + return(3); + if(isLongoff(op1start, "x") || isLongoff(op2start, "x")) + return(3); + if(isLongoff(op1start, "y") || isLongoff(op2start, "y")) + return(4); + if(isPtr(op1start) || isPtr(op2start)) + return(4); + if(strchr(op1start, 'y') || strchr(op2start, 'y')) + i++; // costs extra byte for operating with y + if(isLabel(op1start) || isLabel(op2start)) + return(3+i); + if(isReg(op1start) && isReg(op2start)) + { + if (!strncmp(op1start, "x", 1) && (!strncmp(op2start, "y", 1) || !strncmp(op2start, "sp", 2)) + || !strncmp(op1start, "sp", 2) && !strncmp(op2start, "x", 1)) + return(1); + return(1+i); + } + if(!strcmp(op2start, "a")) + return(3); + if(readint(op2start) <= 0xFF) + return(2+i); + else + return(3+i); + return 4; + } + + /* mov costs 3, 4 or 5 bytes depending on its addressing mode */ + if(ISINST(operand, "mov")) { + assert (op1start != NULL && op2start != NULL); + if(isImmediate(op2start)) + return(4); + if(isLabel(op2start)) + return(5); + if(readint(op2start) <= 0xFF) + return(3); + if(readint(op2start) > 0xFF) + return(5); + } + + /* Operations that costs 2 or 1 bytes depending on + is the Y or X register used */ + if(EQUALS(operand, "clrw") + || EQUALS(operand, "decw") + || EQUALS(operand, "div") + || EQUALS(operand, "incw") + || EQUALS(operand, "mul") + || EQUALS(operand, "negw") + || EQUALS(operand, "popw") + || EQUALS(operand, "pushw") + || EQUALS(operand, "rlcw") + || EQUALS(operand, "rlwa") + || EQUALS(operand, "rrcw") + || EQUALS(operand, "rrwa") + || EQUALS(operand, "sllw") + || EQUALS(operand, "slaw") + || EQUALS(operand, "sraw") + || EQUALS(operand, "srlw") + || EQUALS(operand, "swapw") + || EQUALS(operand, "tnzw")) + { + assert (op1start != NULL); + if((op1start && !strcmp(op1start, "y")) || (op2start && !strcmp(op2start, "y"))) + return(2); + else + return(1); + } + + if(ISINST(pl->line, ".db") || ISINST(pl->line, ".byte")) + { + int i, j; + for(i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++); + return(i); + } + + if(ISINST(pl->line, ".dw") || ISINST(pl->line, ".word")) + { + int i, j; + for(i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++); + return(i * 2); + } + + return(5); // Maximum instruction size, e.g. btjt. +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each jump the label is at the end */ + p = strlen (pl->line) - 1 + pl->line; + + /* Skip trailing whitespace */ + while(isspace(*p)) + p--; + + /* scan backward until space or ',' */ + for (; p > pl->line; p--) + if (isspace(*p) || *p == ',') + break; + + /* sanity check */ + if (p == pl->line) + { + NOTUSEDERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if ( cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/* Check if reading arg implies reading what. */ +static bool argCont(const char *arg, char what) +{ + if (arg == NULL || strlen (arg) == 0 || !(what == 'a' || what == 'x' || what == 'y')) + return FALSE; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + if (arg[0] == ',') + arg++; + + while (isblank ((unsigned char)(arg[0]))) + arg++; + + if (arg[0] == '#') + return FALSE; + + if (arg[0] == '(' && arg[1] == '0' && (tolower(arg[2])) == 'x') + arg += 3; // Skip hex prefix to avoid false x positive. + + if (strlen(arg) == 0) + return FALSE; + + if (arg[0] == '_' && what == 'a') // The STM8 has no a-relative addressing modes. + return FALSE; + + return (strchr(arg, what) != NULL); +} + +static bool +isReturned(const char *what) +{ + symbol *sym; + sym_link *sym_lnk; + int size; + lineNode *l; + + l = _G.head; + do + { + l = l->next; + } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION); + + sym = OP_SYMBOL(IC_LEFT(l->ic)); + + if(sym && IS_DECL(sym->type)) + { + // Find size of return value. + specifier *spec; + if(sym->type->select.d.dcl_type != FUNCTION) + NOTUSEDERROR(); + spec = &(sym->etype->select.s); + if(spec->noun == V_VOID) + size = 0; + else if(spec->noun == V_CHAR || spec->noun == V_BOOL) + size = 1; + else if(spec->noun == V_INT && !(spec->b_long)) + size = 2; + else + size = 4; + + // Check for returned pointer. + sym_lnk = sym->type; + while (sym_lnk && !IS_PTR (sym_lnk)) + sym_lnk = sym_lnk->next; + if(IS_PTR(sym_lnk)) + size = 2; + } + else + { + NOTUSEDERROR(); + return TRUE; + } + + switch(*what) + { + case 'a': + return(size == 1); + case 'x': + return(size > 1); + case 'y': + return(size > 2); + default: + return FALSE; + } +} + +static bool +stm8MightReadFlag(const lineNode *pl, const char *what) +{ + if (strcmp (what, "c") && strcmp (what, "n") && strcmp (what, "z")) + return true; + + if (ISINST (pl->line, "push")) + return (pl->line[5] == 'c'); + + if (!strcmp (what, "n")) + return (ISINST (pl->line, "jrmi") || ISINST (pl->line, "jrpl") || ISINST (pl->line, "jrsge") || ISINST (pl->line, "jrsgte") || ISINST (pl->line, "jrsle") || ISINST (pl->line, "jrslt")); + + if (!strcmp (what, "z")) + return (ISINST (pl->line, "jreq") || ISINST (pl->line, "jrne") || ISINST (pl->line, "jrsgte") || ISINST (pl->line, "jrsle")); + + if (!strcmp (what, "c")) + return (ISINST (pl->line, "jrc") || ISINST (pl->line, "jrnc") || ISINST (pl->line, "jruge") || ISINST (pl->line, "jrugt") || ISINST (pl->line, "jrule") || ISINST (pl->line, "jrult") || + ISINST (pl->line, "adc") || ISINST (pl->line, "sbc") || + ISINST (pl->line, "ccf") || ISINST (pl->line, "rlc") || ISINST (pl->line, "rlcw") || ISINST (pl->line, "rrc") || ISINST (pl->line, "rrcw")); + + return true; +} + +static bool +stm8MightRead(const lineNode *pl, const char *what) +{ + char extra = 0; + + if (!strcmp (what, "xl") || !strcmp (what, "xh")) + extra = 'x'; + else if (!strcmp (what, "yl") || !strcmp (what, "yh")) + extra = 'y'; + else if (strcmp (what, "a") != 0) + return stm8MightReadFlag(pl, what); + + if (!extra) + { + if (ISINST (pl->line, "adc") + || ISINST (pl->line, "and") + || ISINST (pl->line, "bcp") + || ISINST (pl->line, "cp") + || ISINST (pl->line, "div") + || ISINST (pl->line, "mul") + || ISINST (pl->line, "or") + || ISINST (pl->line, "rlwa") + || ISINST (pl->line, "rrwa") + || ISINST (pl->line, "sbc") + || ISINST (pl->line, "trap") + || ISINST (pl->line, "xor")) + return TRUE; + + if ((ISINST (pl->line, "add") + || ISINST (pl->line, "cpl") + || ISINST (pl->line, "dec") + || ISINST (pl->line, "exg") + || ISINST (pl->line, "inc") + || ISINST (pl->line, "neg") + || ISINST (pl->line, "rlc") + || ISINST (pl->line, "rrc") + || ISINST (pl->line, "sll") + || ISINST (pl->line, "sla") + || ISINST (pl->line, "sra") + || ISINST (pl->line, "srl") + || ISINST (pl->line, "sub") + || ISINST (pl->line, "tnz")) && + pl->line[4] == 'a') + return TRUE; + + if ((ISINST (pl->line, "push") + || ISINST (pl->line, "swap")) && + pl->line[5] == 'a') + return TRUE; + + if ((ISINST (pl->line, "ld") || ISINST (pl->line, "ldf")) && argCont (strchr (pl->line, ','), 'a')) + return TRUE; + } + else + { + if (ISINST (pl->line, "divw") || ISINST (pl->line, "exgw") || ISINST (pl->line, "trap")) + return TRUE; + + if (ISINST (pl->line, "exg") && strstr (strchr(pl->line, ','), what)) + return true; + + if ((ISINST (pl->line, "div") || ISINST (pl->line, "mul")) && pl->line[4] == extra) + return true; + + if ((ISINST (pl->line, "addw") + || ISINST (pl->line, "cplw") + || ISINST (pl->line, "decw") + || ISINST (pl->line, "incw") + || ISINST (pl->line, "negw") + || ISINST (pl->line, "rlcw") + || ISINST (pl->line, "rlwa") + || ISINST (pl->line, "rrcw") + || ISINST (pl->line, "rrwa") + || ISINST (pl->line, "sllw") + || ISINST (pl->line, "slaw") + || ISINST (pl->line, "sraw") + || ISINST (pl->line, "srlw") + || ISINST (pl->line, "subw") + || ISINST (pl->line, "tnzw")) && + pl->line[5] == extra) + return TRUE; + + if ((ISINST (pl->line, "pushw") + || ISINST (pl->line, "swapw")) && pl->line[6] == extra) + return TRUE; + + if (ISINST (pl->line, "cpw") && pl->line[4] == extra) + return TRUE; + + if ((strchr (pl->line, ',') ? argCont (strchr (pl->line, ','), extra) : argCont (strchr (pl->line, '('), extra)) && + (ISINST (pl->line, "adc") + || ISINST (pl->line, "add") + || ISINST (pl->line, "and") + || ISINST (pl->line, "bcp") + || ISINST (pl->line, "call") + || ISINST (pl->line, "clr") + || ISINST (pl->line, "cp") + || ISINST (pl->line, "cpl") + || ISINST (pl->line, "dec") + || ISINST (pl->line, "inc") + || ISINST (pl->line, "jp") + || ISINST (pl->line, "neg") + || ISINST (pl->line, "or") + || ISINST (pl->line, "rlc") + || ISINST (pl->line, "rrc") + || ISINST (pl->line, "sbc") + || ISINST (pl->line, "sll") + || ISINST (pl->line, "sla") + || ISINST (pl->line, "sra") + || ISINST (pl->line, "srl") + || ISINST (pl->line, "sub") + || ISINST (pl->line, "swap") + || ISINST (pl->line, "tnz") + || ISINST (pl->line, "cpw") + || ISINST (pl->line, "ldf") + || ISINST (pl->line, "ldw") + || ISINST (pl->line, "ld") + || ISINST (pl->line, "xor"))) + return TRUE; + + if (ISINST (pl->line, "ld") || ISINST (pl->line, "ldw")) + { + char buf[64], *p; + strcpy (buf, pl->line); + if (!!(p = strstr (buf, "0x")) || !!(p = strstr (buf, "0X"))) + p[0] = p[1] = ' '; + if (!!(p = strchr (buf, '(')) && !!strchr (p, extra)) + return TRUE; + } + } + + if(ISINST(pl->line, "ret") || ISINST(pl->line, "retf")) + return(isReturned(what)); + + return FALSE; +} + +static bool +stm8UncondJump(const lineNode *pl) +{ + return (ISINST(pl->line, "jp") || ISINST(pl->line, "jra") || ISINST(pl->line, "jrt") || ISINST(pl->line, "jpf")); +} + +static bool +stm8CondJump(const lineNode *pl) +{ + return (!stm8UncondJump(pl) && STARTSINST(pl->line, "jr") || + ISINST(pl->line, "btjt") || ISINST(pl->line, "btjf")); +} + +static bool +stm8SurelyWritesFlag(const lineNode *pl, const char *what) +{ + if (!strcmp (what, "n") || !strcmp (what, "z")) + { + if (ISINST (pl->line, "addw") && !strcmp (pl->line + 5, "sp")) + return false; + if (ISINST (pl->line, "sub") && !strcmp (pl->line + 4, "sp")) + return false; + if (ISINST (pl->line, "ld")) + return !stm8InstIsRegToReg(pl->line, false); + if (ISINST (pl->line, "ldw")) + return !stm8InstIsRegToReg(pl->line, true); + if (ISINST (pl->line, "pop")) + return (pl->line[5] == 'c'); + if (ISINST (pl->line, "bccm") || ISINST (pl->line, "bcpl") || + ISINST (pl->line, "break") || + ISINST (pl->line, "bres") || ISINST (pl->line, "bset") || + ISINST (pl->line, "btjf") || ISINST (pl->line, "btjt") || + ISINST (pl->line, "call") || ISINST (pl->line, "callf") || ISINST (pl->line, "callr") || + ISINST (pl->line, "ccf") || + ISINST (pl->line, "exg") || ISINST (pl->line, "exgw") || + ISINST (pl->line, "halt") || ISINST (pl->line, "int") || + STARTSINST (pl->line, "jp") || + STARTSINST (pl->line, "jr") || + ISINST (pl->line, "mov") || ISINST (pl->line, "mul") || + ISINST (pl->line, "nop") || + ISINST (pl->line, "popw") || ISINST (pl->line, "push") || ISINST (pl->line, "pushw") || + ISINST (pl->line, "rcf") || + ISINST (pl->line, "ret") || ISINST (pl->line, "retf") || + ISINST (pl->line, "rvf") || ISINST (pl->line, "scf") || + ISINST (pl->line, "sim") || ISINST (pl->line, "trap") || ISINST (pl->line, "wfe") || ISINST (pl->line, "wfi")) + return false; + return true; + } + else if (!strcmp (what, "c")) + { + if (ISINST (pl->line, "addw") && !strcmp (pl->line + 5, "sp")) + return false; + if (ISINST (pl->line, "sub") && !strcmp (pl->line + 4, "sp")) + return false; + + if (ISINST (pl->line, "adc") || + STARTSINST (pl->line, "add") || // add, addw + STARTSINST (pl->line, "btj") || // btjt, btjf + ISINST (pl->line, "ccf") || + STARTSINST (pl->line, "cp") || // cp, cpw, cpl, cplw + STARTSINST (pl->line, "div") || // div, divw + STARTSINST (pl->line, "neg") || // neg, negw + ISINST (pl->line, "rcf") || + STARTSINST (pl->line, "rlc") || // rlc, rlcw + STARTSINST (pl->line, "rrc") || // rrc, rrcw + ISINST (pl->line, "sbc") || + ISINST (pl->line, "scf") || + STARTSINST (pl->line, "sl") || // sll, sla, sllw, slaw + STARTSINST (pl->line, "sr") || // sra, sraw, srl, srlw + STARTSINST (pl->line, "sub")) // sub, subw + return true; + } + + return false; +} + +static bool +stm8SurelyWrites(const lineNode *pl, const char *what) +{ + char extra = 0; + if (!strcmp (what, "xl") || !strcmp (what, "xh")) + extra = 'x'; + else if (!strcmp (what, "yl") || !strcmp (what, "yh")) + extra = 'y'; + else if (strcmp (what, "a")) + return (stm8SurelyWritesFlag (pl, what)); + + if (!extra) + { + if (ISINST (pl->line, "adc") + || ISINST (pl->line, "and") + || ISINST (pl->line, "div") + || ISINST (pl->line, "iret") + || ISINST (pl->line, "or") + || ISINST (pl->line, "rlwa") + || ISINST (pl->line, "rrwa") + || ISINST (pl->line, "sbc") + || ISINST (pl->line, "xor")) + return TRUE; + + if ((ISINST (pl->line, "add") + || ISINST (pl->line, "clr") + || ISINST (pl->line, "cpl") + || ISINST (pl->line, "dec") + || ISINST (pl->line, "exg") + || ISINST (pl->line, "inc") + || ISINST (pl->line, "neg") + || ISINST (pl->line, "pop") + || ISINST (pl->line, "rlc") + || ISINST (pl->line, "rrc") + || ISINST (pl->line, "sll") + || ISINST (pl->line, "sla") + || ISINST (pl->line, "sra") + || ISINST (pl->line, "srl") + || ISINST (pl->line, "ldf") + || ISINST (pl->line, "sub")) && + pl->line[4] == 'a') + return TRUE; + + if (ISINST (pl->line, "swap") && pl->line[5] == 'a') + return TRUE; + + if (ISINST (pl->line, "ld") && pl->line[3] == 'a') + return TRUE; + } + else + { + if (ISINST (pl->line, "divw") + || ISINST (pl->line, "exgw") + || ISINST (pl->line, "iret")) + return TRUE; + + if ((ISINST (pl->line, "div") + || ISINST (pl->line, "ldw") + || ISINST (pl->line, "mul")) + && pl->line[4] == extra) + return TRUE; + + if ((ISINST (pl->line, "addw") + || ISINST (pl->line, "clrw") + || ISINST (pl->line, "cplw") + || ISINST (pl->line, "decw") + || ISINST (pl->line, "incw") + || ISINST (pl->line, "negw") + || ISINST (pl->line, "popw") + || ISINST (pl->line, "rlcw") + || ISINST (pl->line, "rlwa") + || ISINST (pl->line, "rrcw") + || ISINST (pl->line, "rrwa") + || ISINST (pl->line, "sllw") + || ISINST (pl->line, "slaw") + || ISINST (pl->line, "sraw") + || ISINST (pl->line, "srlw") + || ISINST (pl->line, "subw")) && + pl->line[5] == extra) + return TRUE; + + if (ISINST (pl->line, "swapw") && pl->line[6] == extra) + return TRUE; + + if (ISINST (pl->line, "ld") + && strncmp (pl->line + 3, what, strlen (what)) == 0) + return TRUE; + + if (ISINST (pl->line, "exg") && strstr (strstr (pl->line, ","), what)) + return true; + } + + return false; +} + +static bool +stm8SurelyReturns(const lineNode *pl) +{ + return(ISINST(pl->line, "ret") || ISINST(pl->line, "retf")); +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or a opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *what, const char *untilOp, + lineNode **plCond) +{ + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) + continue; + D(("Scanning %s for %s\n", (*pl)->line, what)); + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + { + D(("S4O_ABORT at inline asm\n")); + return S4O_ABORT; + } + + if ((*pl)->visited) + { + D(("S4O_VISITED\n")); + return S4O_VISITED; + } + + (*pl)->visited = TRUE; + + if(stm8MightRead(*pl, what)) + { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } + + // Check writes before conditional jumps, some jumps (btjf, btjt) write 'c' + if(stm8SurelyWrites(*pl, what)) + { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } + + if(stm8UncondJump(*pl)) + { + *pl = findLabel (*pl); + if (!*pl) + { + D(("S4O_ABORT at unconditional jump\n")); + return S4O_ABORT; + } + } + if(stm8CondJump(*pl)) + { + *plCond = findLabel (*pl); + if (!*plCond) + { + D(("S4O_ABORT at conditional jump\n")); + return S4O_ABORT; + } + D(("S4O_CONDJMP\n")); + return S4O_CONDJMP; + } + + /* Don't need to check for de, hl since stm8MightRead() does that */ + if(stm8SurelyReturns(*pl)) + { + D(("S4O_TERM\n")); + return S4O_TERM; + } + } + D(("S4O_ABORT\n")); + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *what) +{ + lineNode *plConditional; + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, what, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return true; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + D(("CONDJMP trying other branch first\n")); + if (!doTermScan (&pl2, what)) + return false; + D(("Other branch OK.\n")); + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return false; + } + } +} + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = false; +} + +bool +stm8notUsed (const char *what, lineNode *endPl, lineNode *head) +{ + lineNode *pl; + if(strcmp(what, "x") == 0) + return(stm8notUsed("xl", endPl, head) && stm8notUsed("xh", endPl, head)); + else if(strcmp(what, "y") == 0) + return(stm8notUsed("yl", endPl, head) && stm8notUsed("yh", endPl, head)); + + _G.head = head; + + unvisitLines (_G.head); + + pl = endPl->next; + return (doTermScan (&pl, what)); +} + +bool +stm8notUsedFrom (const char *what, const char *label, lineNode *head) +{ + lineNode *cpl; + + for (cpl = head; cpl; cpl = cpl->next) + if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label))) + return (stm8notUsed (what, cpl, head)); + + return FALSE; +} + +/* can be directly assigned with ld */ +bool +stm8canAssign (const char *op1, const char *op2, const char *exotic) +{ + //fprintf(stderr, "op1=%s op2=%s exotic=%s\n", op1, op2, exotic); + const char *reg, *payload; + reg = op1[0] == 'a' ? op1 : op2; + payload = reg == op1 ? op2 : op1; + if(isRelativeAddr(payload, "x") + || isRelativeAddr(payload, "y") + || isRelativeAddr(payload, "sp") + || !strcmp(payload, "(x)") + || !strcmp(payload, "(y)") + || !strcmp(payload, "xl") + || !strcmp(payload, "xh")) + return(reg[0] == 'a'); + return(FALSE); +} diff --git a/src/stm8/peep.h b/src/stm8/peep.h new file mode 100644 index 0000000..1360d14 --- /dev/null +++ b/src/stm8/peep.h @@ -0,0 +1,5 @@ +bool stm8notUsed(const char *what, lineNode *endPl, lineNode *head); +bool stm8notUsedFrom(const char *what, const char *label, lineNode *head); +bool stm8canAssign (const char *dst, const char *src, const char *exotic); +int stm8instructionSize(lineNode *node); + diff --git a/src/stm8/peeph.def b/src/stm8/peeph.def new file mode 100644 index 0000000..f204389 --- /dev/null +++ b/src/stm8/peeph.def @@ -0,0 +1,1592 @@ +// peeph.def - STM8 peephole rules + +replace restart { + ld %1, %2 +} by { + ; peephole 0 removed dead load into %1 from %2. +} if notVolatile(%1), notVolatile(%2), notUsed(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw a, (%2, sp) +} by { + ; peephole 0a removed dead load into a from %2. +} if notUsed('n'), notUsed('z') + +replace restart { + ldw %1, %2 +} by { + ; peephole 0w removed dead load into %1 from %2. +} if notVolatile(%1), notVolatile(%2), notUsed(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw %1, (%2, sp) +} by { + ; peephole 0wa removed dead load into %1 from %2. +} if notUsed(%1), notUsed('n'), notUsed('z') + +replace restart { + clr %1 +} by { + ; peephole 1 removed dead clear of %1. +} if notVolatile(%1), notUsed(%1), notUsed('n'), notUsed('z') + +replace restart { + or a, %1 +} by { + ; peephole 2 removed dead or. +} if notVolatile(%1), notUsed('a'), notUsed('n'), notUsed('z') + +replace restart { + clrw %1 +} by { + ; peephole 3 removed dead clrw of %1. +} if notVolatile(%1), notUsed(%1), notUsed('n'), notUsed('z') + +replace restart { + ld %1, %2 + ld %2, %1 +} by { + ld %1, %2 + ; peephole 4 removed redundant load from %1 into %2. +} if notVolatile(%1), notVolatile(%2) + +replace restart { + ldw %1, %2 + ldw %2, %1 +} by { + ldw %1, %2 + ; peephole 4w removed redundant load from %1 into %2. +} if notVolatile(%1), notVolatile(%2) + +replace restart { + ld (%1, sp), %2 + ld %2, (%1, sp) +} by { + ld (%1, sp), %2 + ; peephole 4a removed redundant load from (%1, sp) into %2. +} + +replace restart { + ld %1, %2 + ld %3, %1 + ld %1, %2 +} by { + ld %1, %2 + ld %3, %1 + ; peephole 4b removed redundant load from %2 into %1. +} if notVolatile(%1), notVolatile(%2) + +replace restart { + ldw (%1, sp), %2 + ld a, (%2) + %3 a + ldw %2, (%1, sp) +} by { + ldw (%1, sp), %2 + ld a, (%2) + %3 a + ; peephole 4c removed redundant load from (%1, sp) into %2. +} + +replace restart { + ld %1, a + exg a, %1 +} by { + ld %1, a + ; peephole 5 removed redundant exg. +} if notVolatile(%1) + +replace restart { + ld xl, a + srl a + srlw x +} by { + srl a + ; peephole 5a removed redundant srlw x. +} if notUsed('x') + +replace restart { + ld %1, %2 + ld %3, %1 + ld %1, %2 +} by { + ld %1, %2 + ld %3, %1 + ; peephole 5a removed redundant rght shift of . +} if notVolatile(%1), notVolatile(%2) + +replace restart { + pop %1 + push %1 +} by { + ; peephole 6 removed dead pop / push pair. +} if notUsed(%1) + +replace restart { + popw %1 + pushw %1 +} by { + ; peephole 7 removed dead popw / pushw pair. +} if notUsed(%1) + +replace restart { + addw %1, #%2 + ldw (%1), %3 +} by { + ; peephole 8 moved addition of offset into storage instruction + ldw (%2, %1), %3 +} if notUsed(%1) + +replace restart { + addw %1, #%2 + ld a, %4 + ld (%1), a +} by { + ; peephole 9 moved addition of offset into storage instruction + ld a, %4 + ld (%2, %1), a +} if notUsed(%1) + +replace restart { + addw %1, #%2 + clr (%1) +} by { + ; peephole 9a moved addition of offset into clear instruction + clr (%2, %1) +} if notUsed(%1) + +replace restart { + addw %1, #%2 + ldw %3, %4 + ldw (%1), %3 +} by { + ; peephole 10 moved addition of offset into storage instruction + ldw %3, %4 + ldw (%2, %1), %3 +} if notUsed(%1) + +replace restart { + incw %1 + incw %1 + ldw %3, %4 + ldw (%1), %3 +} by { + ; peephole 10a moved addition of offset into storage instruction + ldw %3, %4 + ldw (0x02, %1), %3 +} if notUsed(%1) + +replace restart { + ldw (%1, sp), x + ldw x, (%2, sp) + addw x, (%1, sp) +} by { + ldw (%1, sp), x + ; peephole 11 eliminated load using commutativity of addition + addw x, (%2, sp) +} + +replace restart { + ldw %1, (%2, sp) + ld a, (%1) + %3 a + ldw %1, (%2, sp) +} by { + ldw %1, (%2, sp) + ld a, (%1) + %3 a + ; peephole 12 removed redundant load from (%2, sp) into %1. +} if notSame(%3 'push' 'pop') + +replace restart { + ldw (%1, sp), %2 + ldw %2, (%1, sp) +} by { + ldw (%1, sp), %2 + ; peephole 13 removed redundant load from (%1, sp) into %2. +} + +replace restart { + ldw (%1, sp), x + ldw y, (%1, sp) +} by { + ldw (%1, sp), x + ldw y, x + ; peephole 14 replaced load from (%1, sp) into y by load from x into y. +} + +replace restart { + ldw y, x + ldw (%1, sp), y +} by { + ldw (%1, sp), x + ; peephole 14a loaded (%1, sp) directly from x instead of going through y. +} if notUsed('y') + +replace restart { + ldw y, x + ld a, %1 + ld (%2, y), a +} by { + ; peephole 14b used y directly instead of going through x. + ld a, %1 + ld (%2, x), a +} if notUsed('y') + +replace restart { + ldw x, y + ld a, %1 + ld (%2, x), a +} by { + ; peephole 14c used y directly instead of going through x. + ld a, %1 + ld (%2, y), a +} if notUsed('x') + +replace restart { + ldw (%1, sp), y + ldw x, (%1, sp) +} by { + ldw (%1, sp), y + ldw x, y + ; peephole 15 replaced load from (%1, sp) into x by load from y into x. +} + +replace restart { + ld a, %1 + %2 a + ld %1, a +} by { + %2 %1 + ; peephole 16 applied %2 on %1 instead of a. +} if notUsed('a'), notSame(%2 'push' 'pop'), notSame(%1 'xl' 'xh' 'yl' 'yh') + +replace restart { + ld a, %1 + or a, #0x80 + ld %1, a +} by { + rlc %1 + scf + rrc %1 + ; peephole 17 set msb in carry instead of a. +} if notUsed('a'), notSame(%1 'xl' 'xh' 'yl' 'yh'), notUsed('c'), notVolatile(%1) + +replace restart { + ld a, %1 + or a, #0x01 + ld %1, a +} by { + bset %2, #0 + ; peephole 18-0 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x02 + ld %1, a +} by { + bset %2, #1 + ; peephole 18-1 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x04 + ld %1, a +} by { + bset %2, #2 + ; peephole 18-2 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x08 + ld %1, a +} by { + bset %2, #3 + ; peephole 18-3 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x10 + ld %1, a +} by { + bset %2, #4 + ; peephole 18-4 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1, %2) + +replace restart { + ld a, %1 + or a, #0x20 + ld %1, a +} by { + bset %2, #5 + ; peephole 18-5 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x40 + ld %1, a +} by { + bset %2, #6 + ; peephole 18-6 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + or a, #0x80 + ld %1, a +} by { + bset %2, #7 + ; peephole 18-7 replaced or by bset. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xfe + ld %1, a +} by { + bres %2, #0 + ; peephole 19-0 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xfd + ld %1, a +} by { + bres %2, #1 + ; peephole 19-1 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xfb + ld %1, a +} by { + bres %2, #2 + ; peephole 19-2 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xf7 + ld %1, a +} by { + bres %2, #3 + ; peephole 19-3 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xef + ld %1, a +} by { + bres %2, #4 + ; peephole 19-4 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xdf + ld %1, a +} by { + bres %2, #5 + ; peephole 19-5 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0xbf + ld %1, a +} by { + bres %2, #6 + ; peephole 19-6 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + and a, #0x7f + ld %1, a +} by { + bres %2, #7 + ; peephole 19-7 replaced and by bres. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x01 + ld (%1), a +} by { + bcpl %2, #0 + ; peephole 20-0 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x02 + ld (%1), a +} by { + bcpl %2, #1 + ; peephole 20-1 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x04 + ld (%1), a +} by { + bcpl %2, #2 + ; peephole 20-2 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x08 + ld (%1), a +} by { + bcpl %2, #3 + ; peephole 20-3 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x10 + ld (%1), a +} by { + bcpl %2, #4 + ; peephole 20-4 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x20 + ld (%1), a +} by { + bcpl %2, #5 + ; peephole 20-5 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x40 + ld (%1), a +} by { + bcpl %2, #6 + ; peephole 20-6 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ldw %1, #%2 + ld a, (%1) + xor a, #0x80 + ld (%1), a +} by { + bcpl %2, #7 + ; peephole 20-7 replaced or by bcpl. +} if notUsed(%1), notUsed('a') + +replace restart { + ld a, %1 + xor a, #0x01 + ld %1, a +} by { + bcpl %2, #0 + ; peephole 21-0 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x02 + ld %1, a +} by { + bcpl %2, #1 + ; peephole 21-1 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x04 + ld %1, a +} by { + bcpl %2, #2 + ; peephole 21-2 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x08 + ld %1, a +} by { + bcpl %2, #3 + ; peephole 21-3 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x10 + ld %1, a +} by { + bcpl %2, #4 + ; peephole 21-4 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1, %2) + +replace restart { + ld a, %1 + xor a, #0x20 + ld %1, a +} by { + bcpl %2, #5 + ; peephole 21-5 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x40 + ld %1, a +} by { + bcpl %2, #6 + ; peephole 21-6 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + ld a, %1 + xor a, #0x80 + ld %1, a +} by { + bcpl %2, #7 + ; peephole 21-7 replaced or by bcpl. +} if notUsed('a'), operandsLiteral(%1), immdInRange(0 65535 '+' 0 %1 %2) + +replace restart { + clr a + and a, %1 +} by { + clr a + ; peephole 22 removed redundant and. +} if notVolatile(%1) + +replace restart { + clr a + ; peephole 22a removed redundant clr a. + or a, %1 +} by { + ld a, %1 +} + +replace restart { + clr a + or a, (%1, %2) +} by { + ; peephole 22b removed redundant clr a. + ld a, (%1, %2) +} + +replace restart { + tnzw %1 + jrne %2 + clrw %1 +} by { + tnzw %1 + jrne %2 + ; peephole 22c removed redundant clrw %1. +} + +replace restart { + ldw %1, (%3, %4) + jrne %2 + clrw %1 +} by { + ldw %1, (%3, %4) + jrne %2 + ; peephole 22d removed redundant clrw %1. +} + +replace restart { + and a, %1 + tnz a +} by { + and a, %1 + ; peephole 23 removed redundant tnz. +} + +replace restart { + or a, %1 + tnz a +} by { + or a, %1 + ; peephole 24 removed redundant tnz. +} + +replace restart { + xor a, %1 + tnz a +} by { + xor a, %1 + ; peephole 25 removed redundant tnz. +} + +replace restart { + add a, %1 + tnz a +} by { + add a, %1 + ; peephole 26 removed redundant tnz. +} + +replace restart { + adc a, %1 + tnz a +} by { + adc a, %1 + ; peephole 27 removed redundant tnz. +} + +replace restart { + sub a, %1 + tnz a +} by { + sub a, %1 + ; peephole 28 removed redundant tnz. +} + +replace restart { + sbc a, %1 + tnz a +} by { + sbc a, %1 + ; peephole 29 removed redundant tnz. +} + +replace restart { + ld a, %1 + tnz a +} by { + ld a, %1 + ; peephole 30 removed redundant tnz. +} if notSame(%1 'xl' 'xh' 'yl' 'yh') + +replace restart { + ldw %1, (%1) + tnzw %1 +} by { + ldw %1, (%1) + ; peephole 30bw removed redundant tnzw. +} + +replace restart { + ld a, (%1, %2) + tnz a +} by { + ld a, (%1, %2) + ; peephole 31 removed redundant tnz. +} + +replace restart { + ldw %1, (%2, %3) + tnzw %1 +} by { + ldw %1, (%2, %3) + ; peephole 31w removed redundant tnzw. +} + +replace restart { + ld (%1, %2), a + tnz (%1, %2) +} by { + ld (%1, %2), a + ; peephole 31a removed redundant tnz. +} + +replace restart { + ld (%1, %2), a + tnz a +} by { + ld (%1, %2), a + ; peephole 31b removed redundant tnz. +} + +replace restart { + rlc a + tnz a +} by { + rlc a + ; peephole 32 removed redundant tnz. +} + +replace restart { + addw sp, #%1 + addw sp, #%2 +} by { + addw sp, #%9 + ; peephole 33 combined additions to sp. +} if immdInRange(0 255 '+' %1 %2 %9) + +replace restart { + pop a + addw sp, #%2 +} by { + addw sp, #%9 + ; peephole 34 merged pop a into addw. +} if notUsed('a'), immdInRange(0 255 '+' 1 %2 %9) + +replace restart { + addw sp, #%2 + pop a +} by { + addw sp, #%9 + ; peephole 35 merged pop a into addw. +} if notUsed('a'), immdInRange(0 255 '+' 1 %2 %9) + +replace restart { + popw x + addw sp, #%2 +} by { + addw sp, #%9 + ; peephole 36 merged popw x into addw. +} if notUsed('x'), immdInRange(0 255 '+' 2 %2 %9) + +replace restart { + addw sp, #%2 + popw x +} by { + addw sp, #%9 + ; peephole 37 merged popw x into addw. +} if notUsed('x'), immdInRange(0 255 '+' 2 %2 %9) + +replace restart { + pop a + pop a +} by { + popw x + ; peephole 38 merged pop a into popw x +} if notUsed('a'), notUsed('x') + +replace restart { + pop a + popw x +} by { + addw sp, #3 + ; peephole 39 merged popw x into addw. +} if notUsed('a'), notUsed('x') + +replace restart { + popw x + pop a +} by { + addw sp, #3 + ; peephole 40 merged popw x into addw. +} if notUsed('a'), notUsed('x') + +replace restart { + popw x + popw x +} by { + addw sp, #4 + ; peephole 41 merged popw x into addw. +} if notUsed('x') + +replace restart { + ld a, %1 + cp a, %2 + jrc %3 + ld a, %1 +} by { + ld a, %1 + cp a, %2 + jrc %3 + ; peephole 42 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ld a, %1 + cp a, %2 + jrslt %3 + ld a, %1 +} by { + ld a, %1 + cp a, %2 + jrslt %3 + ; peephole 43 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ld a, %1 + cp a, %2 + jrsle %3 + ld a, %1 +} by { + ld a, %1 + cp a, %2 + jrsle %3 + ; peephole 44 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ld a, %1 + cp a, %2 + jrule %3 + ld a, %1 +} by { + ld a, %1 + cp a, %2 + jrule %3 + ; peephole 45 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + cpw x, %2 + jrc %3 + ldw x, %1 +} by { + ldw x, %1 + cpw x, %2 + jrc %3 + ; peephole 46 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + cpw x, %2 + jrslt %3 + ldw x, %1 +} by { + ldw x, %1 + cpw x, %2 + jrslt %3 + ; peephole 47 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + cpw x, %2 + jrsle %3 + ldw x, %1 +} by { + ldw x, %1 + cpw x, %2 + jrsle %3 + ; peephole 48 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + cpw x, %2 + jrule %3 + ldw x, %1 +} by { + ldw x, %1 + cpw x, %2 + jrule %3 + ; peephole 49 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + cpw x, %2 + jrnc %3 + ldw x, %1 +} by { + ldw x, %1 + cpw x, %2 + jrnc %3 + ; peephole 50 removed redundant load of a from %1. +} if notVolatile(%1), notUsed('n'), notUsed('z') + +replace restart { + ldw x, %1 + jreq %3 + ldw x, %1 +} by { + ldw x, %1 + jreq %3 + ; peephole 50eq removed redundant load of x from %1. +} if notVolatile(%1) + +replace restart { + ldw x, %1 + jrne %3 + ldw x, %1 +} by { + ldw x, %1 + jrne %3 + ; peephole 50ne removed redundant load of x from %1. +} if notVolatile(%1) + +replace restart { + cp a, %1 + jrne %2 + ld a, #0x01 +} by { + sub a, %1 + jrne %2 + inc a + ; peephole 51 used inc to get #1 into a. +} if notUsedFrom(%2 'a') + +replace { + jp %5 + ret +} by { + jp %5 + ; peephole 52 removed unused ret. +} + +replace { + jp %5 + addw sp, %1 + ret +} by { + jp %5 + ; peephole 53 removed unused ret. +} + +replace restart { + jp %5 +} by { + jp %6 + ; peephole j1 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jpf %5 +} by { + jpf %6 + ; peephole j1a jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1 +%1: +} by { +%1: + ; peephole j1b removed redundant jump. +} if labelRefCountChange(%1 -1) + +replace restart { + jpf %1 +%1: +} by { +%1: + ; peephole j1c removed redundant jump. +} if labelRefCountChange(%1 -1) + +replace restart { + jp %1 +%2: +%1: +} by { +%2: +%1: + ; peephole j1d removed redundant jump. +} if labelRefCountChange(%1 -1) + +replace restart { + jpf %1 +%2: +%1: +} by { +%2: +%1: + ; peephole j1e removed redundant jump. +} if labelRefCountChange(%1 -1) + +replace restart { + jra %1 +%1: +} by { +%1: + ; peephole j1f removed redundant jump. +} if labelRefCountChange(%1 -1) + +replace restart { + jp %1 + jp %2 +} by { + jp %1 + ; peephole j2a removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + jra %1 + jp %2 +} by { + jra %1 + ; peephole j2b removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + jp %1 + jra %2 +} by { + jp %1 + ; peephole jc2 removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + jra %1 + jra %2 +} by { + jra %1 + ; peephole j2d removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + jreq %1 + jreq %2 +} by { + jreq %1 + ; peephole j2d-eq removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + jrne %1 + jrne %2 +} by { + jrne %1 + ; peephole j2d-ne removed unreachable jump to %2. +} if labelRefCountChange(%2 -1) + +// Ensure jump-to-jump optimiation of absolute jumps above is done before other jump-related optimizations. +barrier + +replace restart { + jp %5 +} by { + ret + ; peephole j2e replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + jpf %5 +} by { + retf + ; peephole j2f replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + ld a, %1 + srl a + btjt %1, #0, %2 +} by { + ld a, %1 + srl a + ; peephole j3 jumped by carry bit instead of testing bit explicitly. + jrc %2 +} + +replace restart { + ld a, %1 + srl a + btjf %1, #0, %2 +} by { + ld a, %1 + srl a + ; peephole j4 jumped by carry bit instead of testing bit explicitly. + jrnc %2 +} + +replace restart { + jp %5 +} by { + jra %5 + ; peephole j5 changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace restart { + jpf %5 +} by { + jra %5 + ; peephole j5a changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace restart { + jrc %1 + jra %5 +%1: +} by { + jrnc %5 + ; peephole j6 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jreq %1 + jra %5 +%1: +} by { + jrne %5 + ; peephole j7 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jreq %1 + jrne %2 +%1: +} by { + jrne %2 + ; peephole j7-eq-ne replaced jreq-jrne by jrne +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrne %1 + jreq %2 +%1: +} by { + jreq %2 + ; peephole j7-ne-eq replaced jrne-jreq by jreq +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrmi %1 + jra %5 +%1: +} by { + jrpl %5 + ; peephole j8 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrnc %1 + jra %5 +%1: +} by { + jrc %5 + ; peephole j9 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrne %1 + jra %5 +%1: +} by { + jreq %5 + ; peephole j10 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrpl %1 + jra %5 +%1: +} by { + jrmi %5 + ; peephole j11 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrsge %1 + jra %5 +%1: +} by { + jrslt %5 + ; peephole j12 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrsgt %1 + jra %5 +%1: +} by { + jrsle %5 + ; peephole j13 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrsle %1 + jra %5 +%1: +} by { + jrsgt %5 + ; peephole j14 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrslt %1 + jra %5 +%1: +} by { + jrsge %5 + ; peephole j15 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrugt %1 + jra %5 +%1: +} by { + jrule %5 + ; peephole j16 removed jra by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrule %1 + jra %5 +%1: +} by { + jrugt %5 + ; peephole j17 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jrc %5 +} by { + jrc %6 + ; peephole j18 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jreq %5 +} by { + jreq %6 + ; peephole j19 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrmi %5 +} by { + jrmi %6 + ; peephole j20 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrnc %5 +} by { + jrnc %6 + ; peephole j21 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrne %5 +} by { + jrne %6 + ; peephole j22 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrpl %5 +} by { + jrpl %6 + ; peephole j23 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrge %5 +} by { + jrge %6 + ; peephole j24 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrgt %5 +} by { + jrgt %6 + ; peephole j25 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrle %5 +} by { + jrle %6 + ; peephole j26 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrlt %5 +} by { + jrlt %6 + ; peephole j27 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrugt %5 +} by { + jrugt %6 + ; peephole j28 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jrule %5 +} by { + jrule %6 + ; peephole j29 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelInRange(%6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + call %5 +} by { + callr %5 + ; peephole j30 changed absolute to relative call. +} if labelInRange(%5) + +// Should be one of the last ones. Opens the code to further peephole optimization. +replace restart { +%1: +} by { + ; peephole j30 removed unused label %1. +} if labelRefCount(%1 0) + + +replace restart { + ldw x, (%1, sp) + cpw x, #%2 + jrsge %3 + ldw x, #%2 +} by { + ldw x, #%2 + cpw x, (%1, sp) + jrslt %3 + ; peephole j31 removed load by inverting jump condition. +} if notUsedFrom(%3 'x') + +replace restart { + ldw x, (%1, sp) + cpw x, #%2 + jrsle %3 + ldw x, #%2 +} by { + ldw x, #%2 + cpw x, (%1, sp) + jrsgt %3 + ; peephole j32 removed load by inverting jump condition. +} if notUsedFrom(%3 'x') + +// Barrier, since notUsed() is better at dealing with ret than with jp to unknown location. +barrier + +replace restart { + call %1 + ret +} by { + jp %1 + ; peephole j31 replaced call at end of function by jump (tail call optimization). +} + +replace restart { + callf %1 + retf +} by { + jpf %1 + ; peephole j31a replaced call at end of function by jump (tail call optimization). +} + +replace restart { + ld xl, a + ld a, xh +} by { + rlwa x + ; peephole r1 used rlwa. +} if notUsed('xh') + +replace restart { + ld yl, a + ld a, yh +} by { + rlwa y + ; peephole r2 used rlwa. +} if notUsed('yh') + +replace restart { + ld xh, a + ld a, xl +} by { + rrwa x + ; peephole r3 used rrwa. +} if notUsed('xl') + +replace restart { + ld yh, a + ld a, yl +} by { + rrwa y + ; peephole r4 used rrwa. +} if notUsed('yl') + +// Barrier, so nothing else ever sees the jump-on-false optimization. +barrier + +// The STM8 has a relative jump-on-false instruction, which never jumps to its target. This can be used to optimize jumps over 1-byte instructions as we can use the instruction we jump over as the offset for the jump. +replace { + jra %5 +%2: + clr a +%5: +} by { + .byte 0x21 + ; peephole jrf1 used jump-on-false opcode to shorten jump over 1-byte instruction. + %2: + clr a + %5: +} if labelRefCountChange(%5 -1) + +replace { + jra %5 +%2: + clrw x +%5: +} by { + .byte 0x21 + ; peephole jrf2 used jump-on-false opcode to shorten jump over 1-byte instruction. + %2: + clrw x + %5: +} if labelRefCountChange(%5 -1) + +replace { + jra %5 +%2: + ld a, xl +%5: +} by { + .byte 0x21 + ; peephole jrf3 used jump-on-false opcode to shorten jump over 1-byte instruction. + %2: + ld a, xl + %5: +} if labelRefCountChange(%5 -1) + +replace { + jrugt %1 + clr a + jra %2 +%1: + ld a, #0x01 +%2: +} by { + jrule %1 + ld a, #0x01 + .byte 0x21 + ; peephole jrf4 used jump-on-false opcode to shorten jump over 1-byte instruction. +%1: + clr a +%2: +} if labelRefCount(%1 1), labelRefCountChange(%2 -1) + +replace { + jra %5 +%2: + ldw x, y +%5: +} by { + .byte 0x21 + ; peephole jrf5 used jump-on-false opcode to shorten jump over 1-byte instruction. + %2: + ldw x, y + %5: +} if labelRefCountChange(%5 -1) + +// We don't have an explicit instruction to jump over 2 bytes. But when flags are not used, we can use cp a, longmen instead. + +// Jumping over ld a, #0xmm bcp does a read from a memory location at 0xa6mm - which for all current STM8 is in Flash or unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + ld a, #%2 +%5: +} by { + .byte 0xc5 + ; peephole jrf6 used bcp opcode to jump over 2-byte instruction. +%1: + ld a, #%2 +%5: +} if notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + +// Jumping over clr (mm, sp) bcp does a read from a memory location at 0x0fmm - which for all current STM8 is in RAM or unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + clr (%2, sp) +%5: +} by { + .byte 0xc5 + ; peephole jrf7 used bcp opcode to jump over 2-byte instruction. +%1: + clr (%2, sp) +%5: +} if notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + +// Jumping over clrw x; incwx bcp does a read from a memory location at 0x5f5c - which for all current STM8 is unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + clrw x + incw x +%5: +} by { + .byte 0xc5 + ; peephole jrf8 used bcp opcode to jump over 2-byte instruction sequence. +%1: + clrw x + incw x +%5: +} if notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + +// Jumping over ldw x, (mm, sp) bcp does a read from a memory location at 0x1emm - which for all current STM8 is unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + ldw x, (%2, sp) +%5: +} by { + .byte 0xc5 + ; peephole jrf9 used bcp opcode to jump over 2-byte instruction. +%1: + ldw x, (%2, sp) +%5: +} if notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + +// We don't have an explicit instruction to jump over 3 bytes. But when flags and a are not used, we can use ldf a, extmem instead. + +// Jumping over ldw x, #mmmm ldf does a read from a memory location at 0xaemmmm - which for all current STM8 is unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + ldw x, #%2 +%5: +} by { + .byte 0xbc + ; peephole jrf10 used ldf opcode to jump over 3-byte instruction. +%1: + ldw x, #%2 +%5: +} if notUsed('a'), notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + +// Jumping over clrw x; ldw (mm, sp), x ldf does a read from a memory location at 0x5f1fmm - which for all current STM8 is unused. If a future STM8 places memory-mapped I/O there, we're in trouble. +replace { + jra %5 +%1: + clrw x + ldw (%2, sp), x +%5: +} by { + .byte 0xbc + ; peephole jrf11 used ldf opcode to jump over 3-byte instruction. +%1: + clrw x + ldw (%2, sp), x +%5: +} if notUsed('a'), notUsed('n'), notUsed('z'), labelRefCountChange(%5 -1) + diff --git a/src/stm8/ralloc.c b/src/stm8/ralloc.c new file mode 100644 index 0000000..21ec727 --- /dev/null +++ b/src/stm8/ralloc.c @@ -0,0 +1,843 @@ +#include "ralloc.h" +#include "gen.h" + +#include "dbuf_string.h" + +extern void genSTM08Code (iCode *); + +reg_info stm8_regs[] = +{ + {REG_GPR, A_IDX, "a"}, + {REG_GPR, XL_IDX, "xl"}, + {REG_GPR, XH_IDX, "xh"}, + {REG_GPR, YL_IDX, "yl"}, + {REG_GPR, YH_IDX, "yh"}, + {REG_CND, C_IDX, "c"}, + {REG_GPR, X_IDX, "x"}, + {REG_GPR, Y_IDX, "y"}, + {0, SP_IDX, "sp"}, +}; + +/* Flags to turn on debugging code. + */ +enum +{ + D_ALLOC = 0, +}; + +/** Local static variables */ +static struct +{ + set *stackSpil; + int slocNum; + int stackExtend; + int dataExtend; +} _G; + +#if 1 +#define D(_a, _s) if (_a) { printf _s; fflush(stdout); } +#else +#define D(_a, _s) +#endif + +/** noOverLap - will iterate through the list looking for over lap + */ +static int +noOverLap (set *itmpStack, symbol *fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; sym = setNextItem (itmpStack)) + { + if (bitVectBitValue (sym->clashes, fsym->key)) + return 0; +#if 0 + // if sym starts before (or on) our end point + // and ends after (or on) our start point, + // it is an overlap. + if (sym->liveFrom <= fsym->liveTo && sym->liveTo >= fsym->liveFrom) + { + return 0; + } +#endif + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (isFreeSTM8) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && noOverLap (sym->usl.itmpStack, fsym) && getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol * sym) +{ + symbol *sloc = NULL; + struct dbuf_s dbuf; + + D (D_ALLOC, ("createStackSpil: for sym %p %s (old currFunc->stack %ld)\n", sym, sym->name, (long)(currFunc->stack))); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_AUTO; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE (sloc->etype) = 0; + + allocLocal (sloc); + + sloc->isref = 1; /* to prevent compiler warning */ + + wassertl (currFunc, "Local variable used outside of function."); + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + if (currFunc) + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + { + _G.dataExtend += getSize (sloc->type); + } + + /* add it to the stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + + D (D_ALLOC, ("createStackSpil: created new %s\n", sloc->name)); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +void +stm8SpillThis (symbol *sym, bool force_spill) +{ + int i; + + D (D_ALLOC, ("stm8SpillThis: spilling %p (%s)\n", sym, sym->name)); + + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc) || (sym->usl.spillLoc && !sym->usl.spillLoc->onStack)) // stm8 port currently only supports on-stack spill locations in code generation. + createStackSpil (sym); + + /* mark it as spilt */ + sym->isspilt = sym->spillA = 1; + + if (force_spill) + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + sym->regs[i] = 0; + } + + if (sym->usl.spillLoc && !sym->remat) + { + sym->usl.spillLoc->allocreq++; + } + return; +} + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (void) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed. Exception: Variables larger than 4 bytes - these might need a spill location when they are return values */ + if ((sym->liveTo - sym->liveFrom) == 0 && getSize (sym->type) <= 4) + continue; + else if ((sym->liveTo - sym->liveFrom) == 0 && bitVectnBitsOn (sym->defs) <= 1) + { + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (sym->defs)); + if (!dic || dic->op != CALL && dic->op != PCALL) + continue; + } + + D (D_ALLOC, ("regTypeNum: loop on sym %p\n", sym)); + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + D (D_ALLOC, + ("regTypeNum: isagg %u nRegs %u type %p\n", IS_AGGREGATE (sym->type) || sym->isptr, sym->nRegs, sym->type)); + sym->nRegs = + ((IS_AGGREGATE (sym->type) + || sym->isptr) ? getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + D (D_ALLOC, ("regTypeNum: setting nRegs of %s (%p) to %u\n", sym->name, sym, sym->nRegs)); + + D (D_ALLOC, ("regTypeNum: setup to assign regs sym %p\n", sym)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + /* Always general purpose */ + sym->regType = REG_GPR; + } + else + { + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + D (D_ALLOC, ("regTypeNum: #2 setting num of %p to 0\n", sym)); + sym->nRegs = 0; + } + } +} + +/** Transform weird SDCC handling of writes via pointers + into something more sensible. */ +static void +transformPointerSet (eBBlock **ebbs, int count) +{ + /* for all blocks */ + for (int i = 0; i < count; i++) + { + iCode *ic; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + if (POINTER_SET (ic)) + { + IC_LEFT (ic) = IC_RESULT (ic); + IC_RESULT (ic) = 0; + ic->op = SET_VALUE_AT_ADDRESS; + } + } +} + + +/** Register reduction for assignment. + */ +static int +packRegsForAssign (iCode *ic, eBBlock *ebp) +{ + iCode *dic, *sic; + + if (!IS_ITEMP (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + return 0; + + /* Avoid having multiple named address spaces in one iCode. */ + if (IS_SYMOP (IC_RESULT (ic)) && SPEC_ADDRSPACE (OP_SYMBOL (IC_RESULT (ic))->etype)) + return 0; + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* PENDING: Don't pack across function calls. */ + if (dic->op == CALL || dic->op == PCALL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ic->op == '=') + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* For now eliminate 8-bit temporary variables only. + The STM8 instructions operating directly on memory + operands are 8-bit, so the most benefit is in 8-bit + operations. On the other hand, supporting wider + operations well in codegen is also more effort. */ + if (bitsForType (operandType (IC_RESULT (dic))) > 8 && + !((dic->op == LEFT_OP || dic->op == RIGHT_OP) && IS_OP_LITERAL (IC_RIGHT (dic)) && operandLitValue (IC_RIGHT (dic)) == 1 && // Can do wide shift by 1 in place. + IS_SYMOP (IC_LEFT (dic)) && IS_SYMOP (IC_RESULT (ic)) && OP_SYMBOL (IC_LEFT (dic)) == OP_SYMBOL (IC_RESULT (ic)))) + return 0; + + /* if the result is on stack or iaccess then it must be + the same as at least one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } +pack: + /* found the definition */ + + /* delete from liverange table also + delete from all the points in between and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + // PENDING: Check vs mcs51 + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/** Will reduce some registers for single use. + */ +static int +packRegsForOneuse (iCode *ic, operand **opp, eBBlock *ebp) +{ + iCode *dic; + + operand *op = *opp; +//printf("packRegsForOneuse() at ic %d\n", ic->key); + /* if returning a literal then do nothing */ + if (!IS_ITEMP (op)) + return 0; + + /* if rematerializable do nothing */ + if (OP_SYMBOL (op)->remat) + return 0; + + /* this routine will mark the symbol as used in one + instruction use only && if the definition is local + (ie. within the basic block) && has only one definition */ + if (bitVectnBitsOn (OP_USES (op)) != 1 || bitVectnBitsOn (OP_DEFS (op)) != 1) + return 0; + + /* get the definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return 0; +//printf("Found dic %d\n", dic->key); + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return 0; /* non-local */ + + /* for now handle results from assignments from globals only */ + if (dic->op != '=' || !isOperandGlobal (IC_RIGHT (dic))) + return 0; + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (iCode *nic = dic->next; nic && nic != ic; nic = nic->next) + { + /* if there is an intervening function call then no */ + if (nic->op == CALL || nic->op == PCALL) + return 0; + + if (nic->op == GET_VALUE_AT_ADDRESS || nic->op == SET_VALUE_AT_ADDRESS) + return 0; + + /* if address of & the result is remat, then okay */ + if (nic->op == ADDRESS_OF && OP_SYMBOL (IC_RESULT (nic))->remat) + continue; + + if (IS_OP_VOLATILE (IC_LEFT (nic)) || + IS_OP_VOLATILE (IC_RIGHT (nic)) || + isOperandGlobal (IC_RESULT (nic))) + return 0; + } + + /* Optimize out the assignment */ + *opp = operandFromOperand (IC_RIGHT(dic)); + (*opp)->isaddr = true; + + bitVectUnSetBit (OP_SYMBOL (op)->defs, dic->key); + bitVectUnSetBit (OP_SYMBOL (op)->uses, ic->key); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + + /* delete from liverange table also + delete from all the points in between and the new + one */ + for (iCode *nic = dic; nic != ic; nic = nic->next) + bitVectUnSetBit (nic->rlive, op->key); + + remiCodeFromeBBlock (ebp, dic); + + hTabDeleteItem (&iCodehTab, dic->key, ic, DELETE_ITEM, NULL); + + return 1; +} + +/** Does some transformations to reduce register pressure. + */ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + D (D_ALLOC, ("packRegisters: entered.\n")); + + for(;;) + { + change = 0; + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=') + change += packRegsForAssign (ic, ebp); + } + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) + { + D (D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); + + /* Safe: address of a true sym is always constant. */ + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) /* The receiving of the parameter is not accounted for in DEFS */ && + IS_TRUE_SYMOP (IC_LEFT (ic)) && !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + if (ic->op == ADDRESS_OF && IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) && /* Can remat stack locations, but they can currently only be used for pointer read / write */ + IS_TRUE_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->onStack) + { + bool ok = true; + bitVect *uses = bitVectCopy (OP_USES (IC_RESULT (ic))); + for (int bit = bitVectFirstBit (uses); bitVectnBitsOn (uses); bitVectUnSetBit (uses, bit), bit = bitVectFirstBit (uses)) + { + const iCode *uic = hTabItemWithKey (iCodehTab, bit); + wassert (uic); + if (uic->op != SET_VALUE_AT_ADDRESS && uic->op != GET_VALUE_AT_ADDRESS) + { + ok = false; + break; + } + } + + if (ok) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + freeBitVect (uses); + } + + /* Safe: just propagates the remat flag */ + /* if straight assignment then carry remat flag if this is the + only definition */ + if (ic->op == '=' && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter is not accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter is not accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + sym_link *to_type = operandType (IC_LEFT (ic)); + sym_link *from_type = operandType (IC_RIGHT (ic)); + if (IS_GENPTR (to_type) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* In some cases redundant moves can be eliminated */ + if (ic->op == GET_VALUE_AT_ADDRESS || ic->op == SET_VALUE_AT_ADDRESS || + ic->op == IFX && operandSize (IC_COND (ic)) == 1 || + ic->op == IPUSH && operandSize (IC_LEFT (ic)) == 1) + packRegsForOneuse (ic, &(IC_LEFT (ic)), ebp); + } + +} + +/** + Mark variables for assignment by the register allocator. + */ +static void +serialRegMark (eBBlock ** ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + stm8_call_stack_size = 2; // Saving of register to stack temporarily. + + D (D_ALLOC, ("serialRegMark for %s, currFunc->stack %d\n", currFunc->name, currFunc->stack)); + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if ((ic->op == CALL || ic->op == PCALL) && ic->parmBytes + 5 > stm8_call_stack_size) + { + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + + /* 5 for saving all registers at call site + 2 for big return value */ + stm8_call_stack_size = ic->parmBytes + 5 + 2 * (getSize (ftype->next) > 4); + } + + if (ic->op == IPOP) + wassert (0); + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + + /* some don't need registers, since there is no result. */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || ic->op == SET_VALUE_AT_ADDRESS) + continue; + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + D (D_ALLOC, ("serialRegAssign: in loop on result %p %s\n", sym, sym->name)); + + if (sym->isspilt && sym->usl.spillLoc) // todo: Remove once remat is supported! + { + sym->usl.spillLoc->allocreq--; + sym->isspilt = FALSE; + } + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + sym->usl.spillLoc->allocreq++; + + /* if it does not need or is spilt + or is already marked for the new allocator + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || sym->for_newralloc || sym->liveTo <= ic->seq && (sym->nRegs <= 4 || ic->op != CALL && ic->op != PCALL)) + { + D (D_ALLOC, ("serialRegMark: won't live long enough.\n")); + continue; + } + + if (sym->usl.spillLoc && !sym->usl.spillLoc->_isparm) // I have no idea where these spill locations come from. Sometime two symbols even have the same spill location, whic tends to mess up stack allocation. THose that come from previous iterations in this loop would be okay, but those from outside are a problem. + { + sym->usl.spillLoc = 0; + sym->isspilt = false; + } + + if (sym->nRegs > 4 && ic->op == CALL) // To be allocated to stack due to the way long long return values are handled via a hidden pointer. + { + sym->for_newralloc = 0; + stm8SpillThis (sym, TRUE); + } + else if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + stm8SpillThis (sym, TRUE); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} + +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have been spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand * op, iCode * ic) +{ + symbol *sym; + int i; + bool completely_in_regs; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + + if (sym->regType == REG_CND) + return; + + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + sym->usl.spillLoc->allocreq++; + + if (sym->isspilt) + return; + + for(i = 0, completely_in_regs = TRUE; i < sym->nRegs; i++) + if (!sym->regs[i]) + completely_in_regs = FALSE; + if (completely_in_regs) + return; + + stm8SpillThis (sym, FALSE); +} + +void +stm8RegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +void stm8_init_asmops (void); + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +stm8_assignRegisters (ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + + stm8_init_asmops(); + + transformPointerSet (ebbs, count); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (int i = 0; i < count; i++) + packRegisters (ebbs[i]); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* Mark variables for assignment by the new allocator */ + serialRegMark (ebbs, count); + + stm8_extend_stack = stm8_call_stack_size > 255; + + /* Invoke optimal register allocator */ + ic = stm8_ralloc2_cc (ebbi); + + /* redo the offsets for stacked automatic variables */ + if (currFunc && !stm8_extend_stack && currFunc->stack + stm8_call_stack_size > 255) + { + _G.slocNum = 0; + + /* Mark variables for assignment by the new allocator */ + serialRegMark (ebbs, count); + + stm8_extend_stack = TRUE; + + /* Invoke optimal register allocator */ + ic = stm8_ralloc2_cc (ebbi); + + //redoStackOffsets (); + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + genSTM8Code (ic); + + _G.slocNum = 0; +} + diff --git a/src/stm8/ralloc.h b/src/stm8/ralloc.h new file mode 100644 index 0000000..0b7d1ed --- /dev/null +++ b/src/stm8/ralloc.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Philipp Krause . pkk@spth.de (2012) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#include "common.h" + +enum +{ + A_IDX = 0, // The accumulator + XL_IDX, // Lower byte of X + XH_IDX, // Upper byte of X + YL_IDX, // Lower byte of Y + YH_IDX, // Upper byte of Y + C_IDX, // Carry bit - for bool only. + + X_IDX, // X - for use with code generation support functions only. + Y_IDX, // Y - for use with code generation support functions only. + + SP_IDX // SP - for use with debug info. +}; + +enum +{ + REG_GPR = 2, + REG_CND = 4, +}; + +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ +} reg_info; + +extern reg_info stm8_regs[]; + +void stm8_assignRegisters (ebbIndex *); + +void stm8SpillThis (symbol *sym, bool force_spill); +iCode *stm8_ralloc2_cc(ebbIndex *ebbi); + +void stm8RegFix (eBBlock ** ebbs, int count); +#endif + diff --git a/src/stm8/ralloc2.cc b/src/stm8/ralloc2.cc new file mode 100644 index 0000000..9f50d2c --- /dev/null +++ b/src/stm8/ralloc2.cc @@ -0,0 +1,608 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2013 +// +// (c) 2010 - 2013 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// An optimal, polynomial-time register allocator. + +// #define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +// #define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#include "SDCCralloc.hpp" +#include "SDCCsalloc.hpp" + +extern "C" +{ + #include "ralloc.h" + #include "gen.h" + float drySTM8iCode (iCode *ic); + bool stm8_assignment_optimal; + long int stm8_call_stack_size; + bool stm8_extend_stack; +} + +#define REG_A 0 +#define REG_XL 1 +#define REG_XH 2 +#define REG_YL 3 +#define REG_YH 4 +#define REG_C 5 + +template +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + // Todo: More fine-grained control for these. + if (!(ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS && !IS_FLOAT (operandType (left)) || ic->op == '~' || + ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND || + ic->op == GET_VALUE_AT_ADDRESS)) + return; + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + if(r >= port->num_regs) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +template +static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const operand *const left = IC_LEFT(ic); + + const i_assignment_t &ia = a.i_assignment; + + if(ia.registers[REG_A][1] < 0) + return(true); // Register a not in use. + + if(ic->op == IPUSH) + { + if (ia.registers[REG_XL][1] < 0 || ia.registers[REG_YL][1] < 0 && !stm8_extend_stack) + return(true); // Register xl or yl free; code generation can use them when a is not available. + + // push a does not disturb a. + if (getSize(operandType(IC_LEFT(ic))) <= 1 && operand_in_reg(left, REG_A, ia, i, G)) + return(true); + + // push #byte does not disturb a. + if (IS_OP_LITERAL(left)) + return(true); + + // push longmem does not disturb a. + if (IS_OP_GLOBAL(left)) + return(true); + + // Only look at itemp pushes below. + if (!IS_ITEMP(left)) + return(false); + + // Register pushes do not disturb a. + for (unsigned short i = 0; i < getSize(operandType(IC_LEFT(ic)));) + { + if(operand_in_reg(left, REG_A, ia, i, G)) + i++; + else if(operand_in_reg(left, REG_XL, ia, i, G) && operand_in_reg(left, REG_XH, ia, i + 1, G)) + i += 2; + else if(operand_in_reg(left, REG_YL, ia, i, G) && operand_in_reg(left, REG_YH, ia, i + 1, G)) + i += 2; + else if(operand_in_reg(left, REG_XL, ia, i, G) || operand_in_reg(left, REG_YL, ia, i, G)) + i++; + else + return(false); + } + + return(true); + } + + return(true); +} + +template +static bool Yinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + const operand *const left = IC_LEFT(ic); + + const i_assignment_t &ia = a.i_assignment; + + if(!stm8_extend_stack) + return(true); // Only an extended stack can make Y unavailable. + + if(ia.registers[REG_YL][1] < 0 && ia.registers[REG_YH][1] < 0) + return(true); // Register Y not in use. + + return(false); +} + +template +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + + if(!(IC_RESULT(ic) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + if(G[i].dying.find(*v) == G[i].dying.end()) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = stm8_regs + a.global[v]; + sym->nRegs = I[v].size; + } + else + { + sym->regs[I[v].byte] = 0; + sym->nRegs = I[v].size; + } + } +} + +template +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + assign_operands_for_cost(a, (unsigned short)*(adjacent_vertices(i, G).first), G, I); +} + +template +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ +#if 0 + int v, byteregs[8]; // Todo: Change this when sdcc supports variables larger than 8 bytes. + unsigned short int size; + + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // Ensure: Fully in registers or fully in mem. + if(a.local.find(oi->second) != a.local.end()) + { + while(++oi != oi_end) + if(a.local.find(oi->second) == a.local.end()) + return(false); + } + else + { + while(++oi != oi_end) + if(a.local.find(oi->second) != a.local.end()) + return(false); + } + + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + v = oi->second; + byteregs[I[v].byte] = a.global[v]; + size = 1; + while(++oi != oi_end) + { + v = oi->second; + byteregs[I[v].byte] = a.global[v]; + size++; + } + + if (byteregs[0] == -1) + return(true); + + // Ensure: 8 bit only in A, 16 bit only in X or Y. + if (size == 1) + return(byteregs[0] == A_IDX); + if (size == 2) + return(byteregs[0] == XL_IDX && byteregs[1] == XH_IDX || byteregs[0] == YL_IDX && byteregs[1] == YH_IDX); + if (size > 2) + return(false); +#endif + + return(true); +} + +template +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +// Cost function. +template +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert(TARGET_IS_STM8); + wassert(ic); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits::infinity()); + +#if 0 + std::cout << "Calculating at cost at ic " << ic->key << ", op " << ic->op << " for: "; + print_assignment(a); + std::cout << "\n"; + std::cout.flush(); +#endif + + if(ic->generated) + { +#if 0 + std::cout << "Skipping, already generated.\n"; +#endif + return(0.0f); + } + + if(!Ainst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(!Yinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: +#if 0 + std::cout << "Skipping, indepent from assignment.\n"; +#endif + return(0.0f); + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '/': + case '%': + case '>': + case '<': + case LE_OP: + case GE_OP: + case EQ_OP: + case NE_OP: + case AND_OP: + case OR_OP: + case GETABIT: + case GETBYTE: + case GETWORD: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case SET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + /*case RECEIVE: + case SEND:*/ + case DUMMY_READ_VOLATILE: + /*case CRITICAL: + case ENDCRITICAL:*/ + case SWAP: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = drySTM8iCode(ic); + ic->generated = false; +#if 0 + std::cout << "Got cost " << c << "\n"; +#endif + return(c); + default: + return(0.0f); + } +} + +// For early removal of assignments that cannot be extended to valid assignments. This is just a dummy for now. +template +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. +template +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T) +{ + a = *T[t].assignments.begin(); + + std::set::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +// Suggest to honor register keyword and to not reverse bytes and prefer use of a. Prefer x over y. +template +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const i_assignment_t &ia = a.i_assignment; + float c = 0.0f; + + if(ia.registers[REG_A][1] < 0) + c += 0.05f; + + varset_t::const_iterator v, v_end; + for(v = a.local.begin(), v_end = a.local.end(); v != v_end; ++v) + { + const symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + if(a.global[*v] < 0 && !sym->remat) // Try to put non-rematerializeable variables into registers. + c += 0.1f; + if(a.global[*v] < 0 && IS_REGISTER(sym->type)) // Try to honour register keyword. + c += 4.0f; + if((I[*v].byte % 2) ? // Try not to reverse bytes. + (a.global[*v] == REG_XL || a.global[*v] == REG_YL) : + (a.global[*v] == REG_XH || a.global[*v] == REG_YH)) + c += 0.1f; + } + + return(c); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + if(ic->op == '>' || ic->op == '<' || ic->op == LE_OP || ic->op == GE_OP || ic->op == EQ_OP || ic->op == NE_OP || + ic->op == BITWISEAND && (IS_OP_LITERAL (IC_LEFT (ic)) || IS_OP_LITERAL (IC_RIGHT (ic))) || ic->op == GETABIT) + { + iCode *ifx; + + // Bitwise and code generation can only do the jump if one operand is a literal with at most one nonzero byte. + if (ic->op == BITWISEAND && getSize(operandType(IC_RESULT(ic))) > 1) + { + int nonzero = 0; + operand *const litop = IS_OP_LITERAL (IC_LEFT (ic)) ? IC_LEFT (ic) : IC_RIGHT (ic); + + for(unsigned int i = 0; i < getSize(operandType(IC_LEFT (ic))) && i < getSize(operandType(IC_RIGHT (ic))) && i < getSize(operandType(IC_RESULT(ic))); i++) + if(byteOfVal (OP_VALUE (litop), i)) + nonzero++; + + if(nonzero > 1) + return; + } + if (ic->op == GETABIT) + { + unsigned bit = byteOfVal (OP_VALUE (IC_RIGHT (ic)), 0); + + if (bit % 8 != 7) + return; + } + + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } +} + +template +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I, SI_t &SI) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + const assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + bool spilt = false; + + if(winner.global[v] >= 0) + sym->regs[I[v].byte] = stm8_regs + winner.global[v]; + else + { + sym->regs[I[v].byte] = 0; + spilt = true; + } + + if(spilt) + stm8SpillThis(sym, true); + + sym->nRegs = I[v].size; + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + set_spilt(G, I, SI); + + return(!assignment_optimal); +} + +iCode *stm8_ralloc2_cc(ebbIndex *ebbi) +{ + eBBlock **const ebbs = ebbi->bbOrder; + const int count = ebbi->count; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + iCode *ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + scon_t stack_conflict_graph; + + stm8_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph, stack_conflict_graph); + + stm8RegFix (ebbs, count); + + chaitin_salloc(stack_conflict_graph); + + if(options.dump_graphs) + dump_scon(stack_conflict_graph); + + return(ic); +} + diff --git a/src/stm8/stm8.vcxproj b/src/stm8/stm8.vcxproj new file mode 100644 index 0000000..830d2c6 --- /dev/null +++ b/src/stm8/stm8.vcxproj @@ -0,0 +1,114 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + + + + + + + + + + + + + + + + + {0BA12B9F-BCD6-4C08-9992-69B4FB32D335} + Win32Proj + stm8 + + + + StaticLibrary + true + MultiByte + + + StaticLibrary + false + true + MultiByte + + + + + + + + + + + + + + + $(Configuration)\ + + + port + + + $(Configuration)\ + + + port + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + MultiThreaded + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/src/stm8/stm8.vcxproj.filters b/src/stm8/stm8.vcxproj.filters new file mode 100644 index 0000000..2b6aad8 --- /dev/null +++ b/src/stm8/stm8.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {06d2760c-63da-486c-b9b7-ee18894e32a7} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Custom Build + + + \ No newline at end of file diff --git a/src/version.awk b/src/version.awk new file mode 100644 index 0000000..a84bba0 --- /dev/null +++ b/src/version.awk @@ -0,0 +1,31 @@ +BEGIN { +print "/*" +print " * version.h" +print " * control long build version number" +print " *" +print " * Created automatically with version.awk script" +print " *" +print " */" +print "" +print "#ifndef __VERSION_H__" +print "#define __VERSION_H__" +print "" + +FS="[ \t.]" +} + +/Revision/ { +if ($2) { +printf "#define SDCC_BUILD_NUMBER \"%s\"\n", $2 +printf "#define SDCC_BUILD_NR %s\n", $2 +} +else { +print "#define SDCC_BUILD_NUMBER \"0\"" +print "#define SDCC_BUILD_NR 0" +} +} + +END { +print "" +print "#endif" +} diff --git a/src/z80/Makefile b/src/z80/Makefile new file mode 100644 index 0000000..cb704c7 --- /dev/null +++ b/src/z80/Makefile @@ -0,0 +1,7 @@ + +srcdir = . +top_builddir = ../.. +top_srcdir = ../.. + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/z80/Makefile.in b/src/z80/Makefile.in new file mode 100644 index 0000000..dfb8a52 --- /dev/null +++ b/src/z80/Makefile.in @@ -0,0 +1,7 @@ +VPATH = @srcdir@ +srcdir = @srcdir@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# Make all in this directory +include $(srcdir)/../port.mk diff --git a/src/z80/gen.c b/src/z80/gen.c new file mode 100644 index 0000000..d63f5b0 --- /dev/null +++ b/src/z80/gen.c @@ -0,0 +1,13211 @@ +/*------------------------------------------------------------------------- + gen.c - code generator for Z80 / Z180 / GBZ80. + + Copyright (C) 1998, Sandeep Dutta . sandeep.dutta@usa.net + Copyright (C) 1999, Jean-Louis VERN.jlvern@writeme.com + Copyright (C) 2000, Michael Hope + Copyright (C) 2011-2018, Philipp Klaus Krause pkk@spth.de, philipp@informatik.uni-frankfurt.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "z80.h" +#include "gen.h" +#include "dbuf_string.h" + +/* This is the down and dirty file with all kinds of kludgy & hacky + stuff. This is what it is all about CODE GENERATION for a specific MCU. + Some of the routines may be reusable, will have to see */ + +/* Z80 calling convention description. + Parameters are passed right to left. As the stack grows downwards, + the parameters are arranged in left to right in memory. + + Everything is caller saves. i.e. the caller must save any registers + that it wants to preserve over the call, except for ix, which is callee saves. + GB: The return value is returned in DEHL. DE is normally used as a + working register pair. Caller saves allows it to be used for a + return value. + va args functions do not use register parameters. All arguments + are passed on the stack. + IX is used as an index register to the top of the local variable + area. ix-0 is the top most local variable. +*/ + +enum +{ + /* Set to enable debugging trace statements in the output assembly code. */ + DISABLE_DEBUG = 0 +}; + +// #define DEBUG_DRY_COST + +extern struct dbuf_s *codeOutBuf; + +enum +{ + INT8MIN = -128, + INT8MAX = 127 +}; + +/** Enum covering all the possible register pairs. + */ +typedef enum +{ + PAIR_INVALID, + PAIR_AF, + PAIR_BC, + PAIR_DE, + PAIR_HL, + PAIR_IY, + PAIR_IX, + NUM_PAIRS +} PAIR_ID; + +static struct +{ + const char *name; + const char *l; + const char *h; + int l_idx; + int h_idx; +} _pairs[NUM_PAIRS] = +{ + { + "??1", "?2", "?3", -1, -1 + }, + { + "af", "f", "a", -1, A_IDX + }, + { + "bc", "c", "b", C_IDX, B_IDX + }, + { + "de", "e", "d", E_IDX, D_IDX + }, + { + "hl", "l", "h", L_IDX, H_IDX + }, + { + "iy", "iyl", "iyh", IYL_IDX, IYH_IDX + }, + { + "ix", "ixl", "ixh", -1, -1 + } +}; + +enum +{ + LSB, + MSB16, + MSB24, + MSB32 +}; + +enum asminst +{ + A_ADD, + A_ADC, + A_AND, + A_CP, + A_CPL, + A_DEC, + A_INC, + A_LD, + A_NEG, + A_OR, + A_RL, + A_RLA, + A_RLC, + A_RLCA, + A_RR, + A_RRA, + A_RRC, + A_RRCA, + A_SBC, + A_SLA, + A_SRA, + A_SRL, + A_SUB, + A_XOR, + A_SWAP +}; + +static const char *asminstnames[] = +{ + "add", + "adc", + "and", + "cp", + "cpl", + "dec", + "inc", + "ld", + "neg", + "or", + "rl", + "rla", + "rlc", + "rlca", + "rr", + "rra", + "rrc", + "rrca", + "sbc", + "sla", + "sra", + "srl", + "sub", + "xor", + "swap" +}; + +/** Code generator persistent data. + */ +static struct +{ + /** Used to optimise setting up of a pair by remembering what it + contains and adjusting instead of reloading where possible. + */ + struct + { + AOP_TYPE last_type; + const char *base; + int offset; + } pairs[NUM_PAIRS]; + struct + { +// int last; + int pushed; + int param_offset; + int offset; + int pushedHL; + int pushedBC; + int pushedDE; + int pushedIY; + } stack; + + struct + { + int pushedBC; + int pushedDE; + } calleeSaves; + + bool omitFramePtr; + int frameId; + int receiveOffset; + bool flushStatics; + bool in_home; + const char *lastFunctionName; + iCode *current_iCode; + bool preserveCarry; + + set *sendSet; + + struct + { + /** TRUE if the registers have already been saved. */ + bool saved; + } saves; + + struct + { + allocTrace trace; + } lines; + + struct + { + allocTrace aops; + } trace; +} _G; + +bool z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX + 1]; +bool z80_symmParm_in_calls_from_current_function; +bool z80_regs_preserved_in_calls_from_current_function[IYH_IDX + 1]; + +static const char *aopGet (asmop *aop, int offset, bool bit16); + +static struct asmop asmop_a, asmop_b, asmop_c, asmop_d, asmop_e, asmop_h, asmop_l, asmop_iyh, asmop_iyl, asmop_zero, asmop_one, asmop_return; +static struct asmop *const ASMOP_A = &asmop_a; +static struct asmop *const ASMOP_B = &asmop_b; +static struct asmop *const ASMOP_C = &asmop_c; +static struct asmop *const ASMOP_D = &asmop_d; +static struct asmop *const ASMOP_E = &asmop_e; +static struct asmop *const ASMOP_H = &asmop_h; +static struct asmop *const ASMOP_L = &asmop_l; +static struct asmop *const ASMOP_IYH = &asmop_iyh; +static struct asmop *const ASMOP_IYL = &asmop_iyl; +static struct asmop *const ASMOP_ZERO = &asmop_zero; +static struct asmop *const ASMOP_ONE = &asmop_one; +static struct asmop *const ASMOP_RETURN = &asmop_return; + +static asmop *asmopregs[] = { &asmop_a, &asmop_c, &asmop_b, &asmop_e, &asmop_d, &asmop_l, &asmop_h, &asmop_iyl, &asmop_iyh }; + +void +z80_init_asmops (void) +{ + asmop_a.type = AOP_REG; + asmop_a.size = 1; + asmop_a.aopu.aop_reg[0] = regsZ80 + A_IDX; + memset (asmop_a.regs, -1, 9); + asmop_a.regs[A_IDX] = 0; + asmop_b.type = AOP_REG; + asmop_b.size = 1; + asmop_b.aopu.aop_reg[0] = regsZ80 + B_IDX; + memset (asmop_b.regs, -1, 9); + asmop_b.regs[B_IDX] = 0; + asmop_c.type = AOP_REG; + asmop_c.size = 1; + asmop_c.aopu.aop_reg[0] = regsZ80 + C_IDX; + memset (asmop_c.regs, -1, 9); + asmop_c.regs[C_IDX] = 0; + asmop_d.type = AOP_REG; + asmop_d.size = 1; + asmop_d.aopu.aop_reg[0] = regsZ80 + D_IDX; + memset (asmop_d.regs, -1, 9); + asmop_d.regs[D_IDX] = 0; + asmop_e.type = AOP_REG; + asmop_e.size = 1; + asmop_e.aopu.aop_reg[0] = regsZ80 + E_IDX; + memset (asmop_e.regs, -1, 9); + asmop_e.regs[E_IDX] = 0; + asmop_h.type = AOP_REG; + asmop_h.size = 1; + asmop_h.aopu.aop_reg[0] = regsZ80 + H_IDX; + memset (asmop_h.regs, -1, 9); + asmop_h.regs[H_IDX] = 0; + asmop_l.type = AOP_REG; + asmop_l.size = 1; + asmop_l.aopu.aop_reg[0] = regsZ80 + L_IDX; + memset (asmop_l.regs, -1, 9); + asmop_l.regs[L_IDX] = 0; + asmop_iyh.type = AOP_REG; + asmop_iyh.size = 1; + asmop_iyh.aopu.aop_reg[0] = regsZ80 + IYH_IDX; + memset (asmop_iyh.regs, -1, 9); + asmop_iyh.regs[IYH_IDX] = 0; + asmop_iyl.type = AOP_REG; + asmop_iyl.size = 1; + asmop_iyl.aopu.aop_reg[0] = regsZ80 + IYL_IDX; + memset (asmop_iyl.regs, -1, 9); + asmop_iyl.regs[IYL_IDX] = 0; + + /*asmop_hl.type = AOP_REG; + asmop_hl.size = 2; + asmop_hl.aopu.aop_reg[0] = regsZ80 + L_IDX; + asmop_hl.aopu.aop_reg[1] = regsZ80 + H_IDX; + memset (asmop_hl.regs, -1, 9); + asmop_hl.regs[L_IDX] = 0; + asmop_hl.regs[H_IDX] = 1;*/ + + asmop_zero.type = AOP_LIT; + asmop_zero.aopu.aop_lit = constVal ("0"); + asmop_zero.size = 1; + memset (asmop_zero.regs, -1, 9); + + asmop_one.type = AOP_LIT; + asmop_one.aopu.aop_lit = constVal ("1"); + asmop_one.size = 1; + memset (asmop_one.regs, -1, 9); + + asmop_return.type = AOP_REG; + asmop_return.size = 4; + memset (asmop_return.regs, -1, 9); + if (IS_GB) + { + asmop_return.aopu.aop_reg[0] = regsZ80 + E_IDX; + asmop_return.regs[E_IDX] = 0; + asmop_return.aopu.aop_reg[1] = regsZ80 + D_IDX; + asmop_return.regs[D_IDX] = 1; + asmop_return.aopu.aop_reg[2] = regsZ80 + L_IDX; + asmop_return.regs[L_IDX] = 2; + asmop_return.aopu.aop_reg[3] = regsZ80 + H_IDX; + asmop_return.regs[H_IDX] = 2; + } + else + { + asmop_return.aopu.aop_reg[0] = regsZ80 + L_IDX; + asmop_return.regs[L_IDX] = 0; + asmop_return.aopu.aop_reg[1] = regsZ80 + H_IDX; + asmop_return.regs[H_IDX] = 1; + asmop_return.aopu.aop_reg[2] = regsZ80 + E_IDX; + asmop_return.regs[E_IDX] = 2; + asmop_return.aopu.aop_reg[3] = regsZ80 + D_IDX; + asmop_return.regs[D_IDX] = 3; + } +} + +static bool regalloc_dry_run; +static unsigned int regalloc_dry_run_cost; + +static void +cost(unsigned int bytes, unsigned int cycles) +{ + regalloc_dry_run_cost += bytes; +} + +static void +cost2(unsigned int bytes, unsigned int cycles_z80, unsigned int cycles_z180, unsigned int cycles_r2k, unsigned int cycles_gbz80, unsigned int cycles_tlcs90, unsigned int cycles_ez80_z80) +{ + regalloc_dry_run_cost += bytes; +} + +/*-----------------------------------------------------------------*/ +/* aopRS - asmop in register or on stack */ +/*-----------------------------------------------------------------*/ +static bool +aopRS (const asmop *aop) +{ + return (aop->type == AOP_REG || aop->type == AOP_STK || aop->type == AOP_EXSTK); +} + +/*-----------------------------------------------------------------*/ +/* aopIsLitVal - asmop from offset is val */ +/*-----------------------------------------------------------------*/ +static bool +aopIsLitVal (const asmop *aop, int offset, int size, unsigned long long int val) +{ + wassert (size <= sizeof (unsigned long long int)); // Make sure we are not testing outside of argument val. + + for(; size; size--, offset++) + { + unsigned char b = val & 0xff; + val >>= 8; + + // Leading zeroes + if (aop->size <= offset && !b) + continue; + + if (aop->type != AOP_LIT) + return (FALSE); + + if (byteOfVal (aop->aopu.aop_lit, offset) != b) + return (FALSE); + } + + return (TRUE); +} + +/*-----------------------------------------------------------------*/ +/* aopInReg - asmop from offset in the register */ +/*-----------------------------------------------------------------*/ +static inline bool +aopInReg (const asmop *aop, int offset, short rIdx) +{ + if (!(aop->type == AOP_REG)) + return (false); + + if (offset >= aop->size || offset < 0) + return (false); + + if (rIdx == IY_IDX) + return (aopInReg (aop, offset, IYL_IDX) && aopInReg (aop, offset + 1, IYH_IDX)); + if (rIdx == BC_IDX) + return (aopInReg (aop, offset, C_IDX) && aopInReg (aop, offset + 1, B_IDX)); + if (rIdx == DE_IDX) + return (aopInReg (aop, offset, E_IDX) && aopInReg (aop, offset + 1, D_IDX)); + if (rIdx == HL_IDX) + return (aopInReg (aop, offset, L_IDX) && aopInReg (aop, offset + 1, H_IDX)); + + return (aop->aopu.aop_reg[offset]->rIdx == rIdx); +} + +/*-----------------------------------------------------------------*/ +/* aopOnStack - asmop from offset on stack in consecutive memory */ +/*-----------------------------------------------------------------*/ +static bool +aopOnStack (const asmop *aop, int offset, int size) +{ + if (!(aop->type == AOP_STK || aop->type == AOP_EXSTK)) + return (false); + + if (offset + size > aop->size) + return (false); + + return (true); +} + +/* WARNING: This function is dangerous to use. It works literally: + It will return true if ic the the last use of op, even if ic might + be executed again, e.g. due to a loop. Most of the time you will want + to use isPairDead(), or ic->rSurv instead of this function. */ +static bool +isLastUse (const iCode * ic, operand * op) +{ + bitVect *uses = bitVectCopy (OP_USES (op)); + + while (!bitVectIsZero (uses)) + { + if (bitVectFirstBit (uses) == ic->key) + { + if (bitVectnBitsOn (uses) == 1) + { + return TRUE; + } + else + { + return FALSE; + } + } + bitVectUnSetBit (uses, bitVectFirstBit (uses)); + } + + return FALSE; +} + +static PAIR_ID +_getTempPairId (void) +{ + if (IS_GB) + { + return PAIR_DE; + } + else + { + return PAIR_HL; + } +} + +static const char * +_getTempPairName (void) +{ + return _pairs[_getTempPairId ()].name; +} + +static bool +isPairInUse (PAIR_ID id, const iCode * ic) +{ + if (id == PAIR_DE) + { + return bitVectBitValue (ic->rMask, D_IDX) || bitVectBitValue (ic->rMask, E_IDX); + } + else if (id == PAIR_BC) + { + return bitVectBitValue (ic->rMask, B_IDX) || bitVectBitValue (ic->rMask, C_IDX); + } + else + { + wassertl (0, "Only implemented for DE and BC"); + return TRUE; + } +} + +static bool +isPairDead (PAIR_ID id, const iCode * ic) +{ + const bitVect *r = (!options.oldralloc ? ic->rSurv : + (POINTER_SET (ic) ? ic->rMask : + (bitVectCplAnd (bitVectCopy (ic->rMask), z80_rUmaskForOp (IC_RESULT (ic)))))); + + if (id == PAIR_DE) + return !(bitVectBitValue (r, D_IDX) || bitVectBitValue (r, E_IDX)); + else if (id == PAIR_BC) + return !(bitVectBitValue (r, B_IDX) || bitVectBitValue (r, C_IDX)); + else if (id == PAIR_HL) + return !(bitVectBitValue (r, H_IDX) || bitVectBitValue (r, L_IDX)); + else if (id == PAIR_IY) + return !(bitVectBitValue (r, IYH_IDX) || bitVectBitValue (r, IYL_IDX)); + else + { + wassertl (0, "Only implemented for DE, BC, HL and IY"); + return TRUE; + } +} + +static PAIR_ID +getDeadPairId (const iCode *ic) +{ + if (isPairDead (PAIR_BC, ic)) + { + return PAIR_BC; + } + else if (!IS_GB && isPairDead (PAIR_DE, ic)) + { + return PAIR_DE; + } + else + { + return PAIR_INVALID; + } +} + +static PAIR_ID +getFreePairId (const iCode *ic) +{ + if (!isPairInUse (PAIR_BC, ic)) + { + return PAIR_BC; + } + else if (!IS_GB && !isPairInUse (PAIR_DE, ic)) + { + return PAIR_DE; + } + else + { + return PAIR_INVALID; + } +} + +static void +_tidyUp (char *buf) +{ + /* Clean up the line so that it is 'prettier' */ + /* If it is a label - can't do anything */ + if (!strchr (buf, ':')) + { + /* Change the first (and probably only) ' ' to a tab so + everything lines up. + */ + while (*buf) + { + if (*buf == ' ') + { + *buf = '\t'; + break; + } + buf++; + } + } +} + +static void +_vemit2 (const char *szFormat, va_list ap) +{ + struct dbuf_s dbuf; + char *buffer, *p, *nextp; + + dbuf_init (&dbuf, INITIAL_INLINEASM); + + dbuf_tvprintf (&dbuf, szFormat, ap); + + buffer = p = dbuf_detach_c_str (&dbuf); + + _tidyUp (p); + + /* Decompose multiline macros */ + while ((nextp = strchr (p, '\n'))) + { + *nextp = '\0'; + emit_raw (p); + p = nextp + 1; + } + + emit_raw (p); + + dbuf_free (buffer); +} + +static void +emitDebug (const char *szFormat, ...) +{ + if (!DISABLE_DEBUG && !regalloc_dry_run && options.verboseAsm) + { + va_list ap; + + va_start (ap, szFormat); + _vemit2 (szFormat, ap); + va_end (ap); + } +} + +static void +emit2 (const char *szFormat, ...) +{ + if (!regalloc_dry_run) + { + va_list ap; + + va_start (ap, szFormat); + _vemit2 (szFormat, ap); + va_end (ap); + } +} + +static PAIR_ID +getPartPairId (const asmop *aop, int offset) +{ + if (aop->size <= offset + 1 || offset < 0) + return PAIR_INVALID; + + if (aop->type != AOP_REG) + return PAIR_INVALID; + + wassert (aop->aopu.aop_reg[offset] && aop->aopu.aop_reg[offset + 1]); + + if ((aop->aopu.aop_reg[offset]->rIdx == C_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == B_IDX)) + return PAIR_BC; + if ((aop->aopu.aop_reg[offset]->rIdx == E_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == D_IDX)) + return PAIR_DE; + if ((aop->aopu.aop_reg[offset]->rIdx == L_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == H_IDX)) + return PAIR_HL; + if ((aop->aopu.aop_reg[offset]->rIdx == IYL_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == IYH_IDX)) + return PAIR_IY; + + return PAIR_INVALID; +} + +static PAIR_ID +getPairId_o (const asmop *aop, int offset) +{ + if (offset + 2 <= aop->size) + { + if (aop->type == AOP_REG) + { + wassert (aop->aopu.aop_reg[offset] && aop->aopu.aop_reg[offset + 1]); + + if ((aop->aopu.aop_reg[offset]->rIdx == C_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == B_IDX)) + { + return PAIR_BC; + } + if ((aop->aopu.aop_reg[offset]->rIdx == E_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == D_IDX)) + { + return PAIR_DE; + } + if ((aop->aopu.aop_reg[offset]->rIdx == L_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == H_IDX)) + { + return PAIR_HL; + } + if ((aop->aopu.aop_reg[offset]->rIdx == IYL_IDX) && (aop->aopu.aop_reg[offset + 1]->rIdx == IYH_IDX)) + { + return PAIR_IY; + } + } + else if (aop->type == AOP_STR) + { + int i; + for (i = 0; i < NUM_PAIRS; i++) + { + if (!strcmp (aop->aopu.aop_str[offset], _pairs[i].l) && !strcmp (aop->aopu.aop_str[offset + 1], _pairs[i].h)) + return i; + } + } + } + return PAIR_INVALID; +} + +static PAIR_ID +getPairId (const asmop *aop) +{ + if (aop->size != 2) + return PAIR_INVALID; + return (getPairId_o (aop, 0)); +} + + +/*-----------------------------------------------------------------*/ +/* z80_emitDebuggerSymbol - associate the current code location */ +/* with a debugger symbol */ +/*-----------------------------------------------------------------*/ +void +z80_emitDebuggerSymbol (const char *debugSym) +{ + genLine.lineElement.isDebug = 1; + emit2 ("%s !equ .", debugSym); + emit2 ("!global", debugSym); + genLine.lineElement.isDebug = 0; +} + +// Todo: Handle IY correctly. +static unsigned char +ld_cost (const asmop *op1, const asmop *op2) +{ + AOP_TYPE op1type = op1->type; + AOP_TYPE op2type = op2->type; + + /* Costs are symmetric */ + if (op2type == AOP_REG || op2type == AOP_DUMMY) + { + const asmop *tmp = op1; + op1 = op2; + op2 = tmp; + op1type = op1->type; + op2type = op2->type; + } + + switch (op1type) + { + case AOP_REG: + case AOP_DUMMY: + switch (op2type) + { + case AOP_REG: + case AOP_DUMMY: + return (1); + case AOP_IMMD: + case AOP_LIT: + return (2); + case AOP_SFR: /* 2 from in a, (...) */ + return ((aopInReg (op1, 0, A_IDX) || op1type == AOP_DUMMY) ? 2 : 3); + case AOP_STK: + return (3); + case AOP_HL: /* 3 from ld hl, #... */ + return (4); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: /* 4 from ld iy, #... */ + return (7); + case AOP_PAIRPTR: + if (op2->aopu.aop_pairId == PAIR_HL) + return (1); + if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) + return (3); + if (op2->aopu.aop_pairId == PAIR_BC || op2->aopu.aop_pairId == PAIR_DE) + return ((aopInReg (op1, 0, A_IDX) || op1type == AOP_DUMMY) ? 1 : 2); + default: + printf ("ld_cost op1: AOP_REG, op2: %d\n", (int) (op2type)); + wassert (0); + } + case AOP_SFR: /* 2 from out (...), a */ + switch (op2type) + { + case AOP_REG: + case AOP_DUMMY: + return (2); + case AOP_IMMD: + case AOP_LIT: + return (4); + case AOP_STK: + return (5); + case AOP_HL: /* 3 from ld hl, #... */ + return (6); + case AOP_SFR: + return (4); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: /* 4 from ld iy, #... */ + return (9); + default: + printf ("ld_cost op1: AOP_SFR, op2: %d\n", (int) (op2type)); + wassert (0); + } + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: /* 4 from ld iy, #... */ + switch (op2type) + { + case AOP_IMMD: + case AOP_LIT: + return (8); + case AOP_SFR: /* 2 from in a, (...) */ + return (9); + case AOP_STK: + case AOP_HL: /* 3 from ld hl, #... */ + return (10); + case AOP_IY: + case AOP_EXSTK: + return (16); + default: + printf ("ld_cost op1: AOP_IY, op2: %d\n", (int) (op2type)); + wassert (0); + } + case AOP_STK: + switch (op2type) + { + case AOP_IMMD: + case AOP_LIT: + return (4); + case AOP_SFR: /* 2 from in a, (...) */ + return (5); + case AOP_STK: + return (6); + case AOP_HL: + return (7); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: + return (10); + case AOP_PAIRPTR: + if (op2->aopu.aop_pairId == PAIR_HL || op2->aopu.aop_pairId == PAIR_BC || op2->aopu.aop_pairId == PAIR_DE) + return (4); + if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) + return (6); + default: + printf ("ld_cost op1: AOP_STK, op2: %d\n", (int) (op2type)); + wassert (0); + } + case AOP_HL: /* 3 from ld hl, #... */ + switch (op2type) + { + case AOP_REG: + case AOP_DUMMY: + return (4); + case AOP_IMMD: + case AOP_LIT: + return (5); + case AOP_STK: + return (7); + case AOP_SFR: + case AOP_HL: + return (6); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: + return (11); + default: + printf ("ld_cost op1: AOP_HL, op2: %d", (int) (op2type)); + wassert (0); + } + case AOP_LIT: + case AOP_IMMD: + wassertl (0, "Trying to assign a value to a literal"); + break; + default: + printf ("ld_cost op1: %d\n", (int) (op1type)); + wassert (0); + } + return (8); // Fallback +} + +static unsigned char +op8_cost (const asmop * op2) +{ + switch (op2->type) + { + case AOP_REG: + case AOP_DUMMY: + return (1); + case AOP_IMMD: + case AOP_LIT: + return (2); + case AOP_STK: + return (3); + case AOP_HL: + return (4); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: /* 4 from ld iy, #... */ + return (7); + case AOP_PAIRPTR: + if (op2->aopu.aop_pairId == PAIR_HL) + return (1); + if (op2->aopu.aop_pairId == PAIR_IY || op2->aopu.aop_pairId == PAIR_IX) + return (3); + default: + printf ("op8_cost op2: %d\n", (int) (op2->type)); + wassert (0); + } + return (8); // Fallback +} + +static unsigned char +bit8_cost (const asmop * op1) +{ + switch (op1->type) + { + case AOP_REG: + case AOP_DUMMY: + return (2); + case AOP_STK: + return (4); + case AOP_HL: + return (5); + case AOP_IY: /* 4 from ld iy, #... */ + case AOP_EXSTK: /* 4 from ld iy, #... */ + return (8); + default: + printf ("bit8_cost op1: %d\n", (int) (op1->type)); + wassert (0); + } + return (8); //Fallback +} + +static unsigned char +emit3Cost (enum asminst inst, const asmop *op1, int offset1, const asmop *op2, int offset2) +{ + if (op2 && offset2 >= op2->size) + op2 = ASMOP_ZERO; + + switch (inst) + { + case A_CPL: + case A_RLA: + case A_RLCA: + case A_RRA: + case A_RRCA: + return (1); + case A_NEG: + return(2); + case A_LD: + return (ld_cost (op1, op2)); + case A_ADD: + case A_ADC: + case A_AND: + case A_CP: + case A_OR: + case A_SBC: + case A_SUB: + case A_XOR: + return (op8_cost (op2)); + case A_DEC: + case A_INC: + return (op8_cost (op1)); + case A_RL: + case A_RLC: + case A_RR: + case A_RRC: + case A_SLA: + case A_SRA: + case A_SRL: + case A_SWAP: + return (bit8_cost (op1)); + default: + wassertl (0, "Tried get cost for unknown instruction"); + } + return (0); +} + +static void +emit3_o (enum asminst inst, asmop *op1, int offset1, asmop *op2, int offset2) +{ + unsigned char cost; + + regalloc_dry_run_cost += emit3Cost (inst, op1, offset1, op2, offset2); + if (regalloc_dry_run) + return; + + cost = regalloc_dry_run_cost; + if (!op1) + emit2 ("%s", asminstnames[inst]); + else if (!op2) + emit2 ("%s %s", asminstnames[inst], aopGet (op1, offset1, FALSE)); + else + { + char *l = Safe_strdup (aopGet (op1, offset1, FALSE)); + //emit2("%s %s, %s", asminstnames[inst], aopGet(op1, offset1, FALSE), aopGet(op2, offset2, FALSE)); + emit2 ("%s %s, %s", asminstnames[inst], l, aopGet (op2, offset2, FALSE)); + Safe_free (l); + } + + regalloc_dry_run_cost = cost; + //emitDebug(";emit3_o cost: %d total so far: %d", (int)emit3Cost(inst, op1, offset1, op2, offset2), (int)cost); +} + +static void +emit3 (enum asminst inst, asmop *op1, asmop *op2) +{ + emit3_o (inst, op1, 0, op2, 0); +} + +static void +_emitMove (const char *to, const char *from) +{ + if (STRCASECMP (to, from) != 0) + { + emit2 ("ld %s,%s", to, from); + } + else + { + // Optimise it out. + // Could leave this to the peephole, but sometimes the peephole is inhibited. + } +} + +static void +_emitMove3 (asmop *to, int to_offset, asmop *from, int from_offset) +{ + /* Todo: Longer list of moves that can be optimized out. */ + if (to_offset == from_offset) + { + if (to->type == AOP_REG && from->type == AOP_REG && to->aopu.aop_reg[to_offset] == from->aopu.aop_reg[from_offset]) + return; + } + + emit3_o (A_LD, to, to_offset, from, from_offset); +} + +#if 0 +static const char *aopNames[] = +{ + "AOP_INVALID", + "AOP_LIT", + "AOP_REG", + "AOP_DIR", + "AOP_SFR", + "AOP_STK", + "AOP_IMMD", + "AOP_STR", + "AOP_CRY", + "AOP_IY", + "AOP_HL", + "AOP_EXSTK", + "AOP_PAIRPT", + "AOP_DUMMY" +}; + +static void +aopDump (const char *plabel, asmop * aop) +{ + int i; + char regbuf[9]; + char *rbp = regbuf; + + emitDebug ("; Dump of %s: type %s size %u", plabel, aopNames[aop->type], aop->size); + switch (aop->type) + { + case AOP_EXSTK: + case AOP_STK: + emitDebug ("; aop_stk %d", aop->aopu.aop_stk); + break; + case AOP_REG: + for (i = aop->size - 1; i >= 0; i--) + *rbp++ = *(aop->aopu.aop_reg[i]->name); + *rbp = '\0'; + emitDebug ("; reg = %s", regbuf); + break; + case AOP_PAIRPTR: + emitDebug ("; pairptr = (%s)", _pairs[aop->aopu.aop_pairId].name); + + default: + /* No information. */ + break; + } +} +#endif + +static void +_moveA (const char *moveFrom) +{ + _emitMove ("a", moveFrom); +} + +/* Load aop into A */ +static void +_moveA3 (asmop * from, int offset) +{ + _emitMove3 (ASMOP_A, 0, from, offset); +} + +static const char * +getPairName (asmop *aop) +{ + if (aop->type == AOP_REG) + { + switch (aop->aopu.aop_reg[0]->rIdx) + { + case C_IDX: + return "bc"; + break; + case E_IDX: + return "de"; + break; + case L_IDX: + return "hl"; + break; + case IYL_IDX: + return "iy"; + break; + } + } + else if (aop->type == AOP_STR) + { + int i; + for (i = 0; i < NUM_PAIRS; i++) + { + if (strcmp (aop->aopu.aop_str[0], _pairs[i].l) == 0) + return _pairs[i].name; + } + } + wassertl (0, "Tried to get the pair name of something that isn't a pair"); + return NULL; +} + +/** Returns TRUE if the registers used in aop form a pair (BC, DE, HL) */ +static bool +isPair (const asmop *aop) +{ + return (getPairId (aop) != PAIR_INVALID); +} + +/** Returns TRUE if the registers used in aop cannot be split into high + and low halves */ +static bool +isUnsplitable (const asmop * aop) +{ + switch (getPairId (aop)) + { + case PAIR_IX: + case PAIR_IY: + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +static bool +isPtrPair (const asmop * aop) +{ + PAIR_ID pairId = getPairId (aop); + switch (pairId) + { + case PAIR_HL: + case PAIR_IY: + case PAIR_IX: + return TRUE; + default: + return FALSE; + } +} + +static void +spillPair (PAIR_ID pairId) +{ + _G.pairs[pairId].last_type = AOP_INVALID; + _G.pairs[pairId].base = NULL; +} + +/* Given a register name, spill the pair (if any) the register is part of */ +static void +spillPairReg (const char *regname) +{ + if (strlen (regname) == 1) + { + switch (*regname) + { + case 'h': + case 'l': + spillPair (PAIR_HL); + break; + case 'd': + case 'e': + spillPair (PAIR_DE); + break; + case 'b': + case 'c': + spillPair (PAIR_BC); + break; + } + } +} + +static void +_push (PAIR_ID pairId) +{ + emit2 ("push %s", _pairs[pairId].name); + regalloc_dry_run_cost += (pairId == PAIR_IX || pairId == PAIR_IY ? 2 : 1); + _G.stack.pushed += 2; +} + +static void +_pop (PAIR_ID pairId) +{ + if (pairId != PAIR_INVALID) + { + emit2 ("pop %s", _pairs[pairId].name); + regalloc_dry_run_cost += (pairId == PAIR_IX || pairId == PAIR_IY ? 2 : 1); + _G.stack.pushed -= 2; + spillPair (pairId); + } +} + +static void +genMovePairPair (PAIR_ID srcPair, PAIR_ID dstPair) +{ + switch (dstPair) + { + case PAIR_IX: + case PAIR_IY: + case PAIR_AF: + _push (srcPair); + _pop (dstPair); + break; + case PAIR_BC: + case PAIR_DE: + case PAIR_HL: + if (srcPair == PAIR_IX || srcPair == PAIR_IY) + { + _push (srcPair); + _pop (dstPair); + } + else + { + emit2 ("ld %s, %s", _pairs[dstPair].l, _pairs[srcPair].l); + emit2 ("ld %s, %s", _pairs[dstPair].h, _pairs[srcPair].h); + regalloc_dry_run_cost += 2; + } + break; + default: + wassertl (0, "Tried to move a nonphysical pair"); + } + _G.pairs[dstPair].last_type = _G.pairs[srcPair].last_type; + _G.pairs[dstPair].base = _G.pairs[srcPair].base; + _G.pairs[dstPair].offset = _G.pairs[srcPair].offset; +} + + +/*-----------------------------------------------------------------*/ +/* newAsmop - creates a new asmOp */ +/*-----------------------------------------------------------------*/ +static asmop * +newAsmop (short type) +{ + asmop *aop; + + aop = traceAlloc (&_G.trace.aops, Safe_alloc (sizeof (asmop))); + aop->type = type; + memset (aop->regs, -1, 9); + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForSym - for a true symbol */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForSym (const iCode * ic, symbol * sym, bool requires_a) +{ + asmop *aop; + memmap *space; + + wassert (ic); + wassert (sym); + wassert (sym->etype); + + space = SPEC_OCLS (sym->etype); + + /* if already has one */ + if (sym->aop) + { + return sym->aop; + } + + /* Assign depending on the storage class */ + if (sym->onStack || sym->iaccess) + { + /* The pointer that is used depends on how big the offset is. + Normally everything is AOP_STK, but for offsets of < -128 or + > 127 on the Z80 an extended stack pointer is used. + */ + if (!IS_GB && (_G.omitFramePtr || sym->stack < INT8MIN || sym->stack > (int) (INT8MAX - getSize (sym->type)))) + { + emitDebug ("; AOP_EXSTK for %s, _G.omitFramePtr %d, sym->stack %d, size %d", sym->rname, (int) (_G.omitFramePtr), + sym->stack, getSize (sym->type)); + sym->aop = aop = newAsmop (AOP_EXSTK); + } + else + { + emitDebug ("; AOP_STK for %s", sym->rname); + sym->aop = aop = newAsmop (AOP_STK); + } + + aop->size = getSize (sym->type); + aop->aopu.aop_stk = sym->stack; + return aop; + } + + /* special case for a function */ + if (IS_FUNC (sym->type)) + { + sym->aop = aop = newAsmop (AOP_IMMD); + aop->aopu.aop_immd = traceAlloc (&_G.trace.aops, Safe_strdup (sym->rname)); + aop->size = 2; + return aop; + } + + if (IN_REGSP (space)) + { + /*.p.t.20030716 minor restructure to add SFR support to the Z80 */ + if (IS_GB) + { + /* if it is in direct space */ + if (!requires_a) + { + sym->aop = aop = newAsmop (AOP_SFR); + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + /* emitDebug ("; AOP_SFR for %s", sym->rname); */ + return aop; + } + } + else + { + /*.p.t.20030716 adding SFR support to the Z80 port */ + aop = newAsmop (AOP_SFR); + sym->aop = aop; + aop->aopu.aop_dir = sym->rname; + aop->size = getSize (sym->type); + aop->paged = FUNC_REGBANK (sym->type); + aop->bcInUse = isPairInUse (PAIR_BC, ic); + /* emitDebug (";Z80 AOP_SFR for %s banked:%d bc:%d", sym->rname, FUNC_REGBANK (sym->type), aop->bcInUse); */ + + return (aop); + } + } + + /* only remaining is far space */ + /* in which case DPTR gets the address */ + if (IS_GB || IY_RESERVED) + { + /* emitDebug ("; AOP_HL for %s", sym->rname); */ + sym->aop = aop = newAsmop (AOP_HL); + } + else + sym->aop = aop = newAsmop (AOP_IY); + + aop->size = getSize (sym->type); + aop->aopu.aop_dir = sym->rname; + + /* if it is in code space */ + if (IN_CODESPACE (space)) + aop->code = 1; + + return aop; +} + +/*-----------------------------------------------------------------*/ +/* aopForRemat - rematerializes an object */ +/*-----------------------------------------------------------------*/ +static asmop * +aopForRemat (symbol *sym) +{ + iCode *ic = sym->rematiCode; + asmop *aop = newAsmop (AOP_IMMD); + int val = 0; + struct dbuf_s dbuf; + + wassert(ic); + + for (;;) + { + if (ic->op == '+') + { + if (isOperandLiteral (IC_RIGHT (ic))) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else + { + val += (int) operandLitValue (IC_LEFT (ic)); + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + } + else if (ic->op == '-') + { + val -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + } + else if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + else if (ic->op == ADDRESS_OF) + { + val += (int) operandLitValue (IC_RIGHT (ic)); + break; + } + else + break; + } + + dbuf_init (&dbuf, 128); + if (val) + { + dbuf_printf (&dbuf, "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, val >= 0 ? '+' : '-', abs (val) & 0xffff); + } + else + { + dbuf_append_str (&dbuf, OP_SYMBOL (IC_LEFT (ic))->rname); + } + + aop->aopu.aop_immd = traceAlloc (&_G.trace.aops, dbuf_detach_c_str (&dbuf)); + return aop; +} + +/*-----------------------------------------------------------------*/ +/* regsInCommon - two operands have some registers in common */ +/*-----------------------------------------------------------------*/ +static bool +regsInCommon (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + int i; + + /* if they have registers in common */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + if (sym1->nRegs == 0 || sym2->nRegs == 0) + return FALSE; + + for (i = 0; i < sym1->nRegs; i++) + { + int j; + if (!sym1->regs[i]) + continue; + + for (j = 0; j < sym2->nRegs; j++) + { + if (!sym2->regs[j]) + continue; + + if (sym2->regs[j] == sym1->regs[i]) + return TRUE; + } + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* operandsEqu - equivalent */ +/*-----------------------------------------------------------------*/ +static bool +operandsEqu (operand * op1, operand * op2) +{ + symbol *sym1, *sym2; + + /* if they not symbols */ + if (!IS_SYMOP (op1) || !IS_SYMOP (op2)) + return FALSE; + + sym1 = OP_SYMBOL (op1); + sym2 = OP_SYMBOL (op2); + + /* if both are itemps & one is spilt + and the other is not then false */ + if (IS_ITEMP (op1) && IS_ITEMP (op2) && sym1->isspilt != sym2->isspilt) + return FALSE; + + /* if they are the same */ + if (sym1 == sym2) + return 1; + + if (sym1->rname[0] && sym2->rname[0] && strcmp (sym1->rname, sym2->rname) == 0) + return 2; + + /* if left is a tmp & right is not */ + if (IS_ITEMP (op1) && !IS_ITEMP (op2) && sym1->isspilt && (sym1->usl.spillLoc == sym2)) + return 3; + + if (IS_ITEMP (op2) && !IS_ITEMP (op1) && sym2->isspilt && sym1->level > 0 && (sym2->usl.spillLoc == sym1)) + return 4; + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* sameRegs - two asmops have the same registers */ +/*-----------------------------------------------------------------*/ +static bool +sameRegs (asmop * aop1, asmop * aop2) +{ + int i; + + if (aop1->type == AOP_SFR || aop2->type == AOP_SFR) + return FALSE; + + if (aop1 == aop2) + return TRUE; + + if (! regalloc_dry_run && // Todo: Check if always enabling this even for dry runs tends to result in better code. + (aop1->type == AOP_STK && aop2->type == AOP_STK || + aop1->type == AOP_EXSTK && aop2->type == AOP_EXSTK)) + return (aop1->aopu.aop_stk == aop2->aopu.aop_stk); + + if (aop1->type != AOP_REG || aop2->type != AOP_REG) + return FALSE; + + if (aop1->size != aop2->size) + return FALSE; + + for (i = 0; i < aop1->size; i++) + if (aop1->aopu.aop_reg[i] != aop2->aopu.aop_reg[i]) + return FALSE; + + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* aopOp - allocates an asmop for an operand : */ +/*-----------------------------------------------------------------*/ +static void +aopOp (operand *op, const iCode *ic, bool result, bool requires_a) +{ + asmop *aop; + symbol *sym; + int i; + + if (!op) + return; + + /* if this a literal */ + if (IS_OP_LITERAL (op)) /* TODO: && !op->isaddr, handle address literals in a sane way */ + { + op->aop = aop = newAsmop (AOP_LIT); + aop->aopu.aop_lit = OP_VALUE (op); + aop->size = getSize (operandType (op)); + return; + } + + /* if already has a asmop then continue */ + if (op->aop) + { + if (op->aop->type == AOP_SFR) + { + op->aop->bcInUse = isPairInUse (PAIR_BC, ic); + } + return; + } + + /* if the underlying symbol has a aop */ + if (IS_SYMOP (op) && OP_SYMBOL (op)->aop) + { + op->aop = OP_SYMBOL (op)->aop; + if (op->aop->type == AOP_SFR) + { + op->aop->bcInUse = isPairInUse (PAIR_BC, ic); + } + return; + } + + /* if this is a true symbol */ + if (IS_TRUE_SYMOP (op)) + { + op->aop = aopForSym (ic, OP_SYMBOL (op), requires_a); + return; + } + + /* this is a temporary : this has + only four choices : + a) register + b) spillocation + c) rematerialize + d) conditional + e) can be a return use only */ + + sym = OP_SYMBOL (op); + + /* if the type is a conditional */ + if (sym->regType == REG_CND) + { + aop = op->aop = sym->aop = newAsmop (AOP_CRY); + aop->size = 0; + return; + } + + /* if it is spilt then two situations + a) is rematerialize + b) has a spill location */ + if (sym->isspilt || sym->nRegs == 0) + { + if (sym->ruonly) + { + int i; + aop = op->aop = sym->aop = newAsmop (AOP_STR); + aop->size = getSize (sym->type); + for (i = 0; i < 4; i++) + aop->aopu.aop_str[i] = ASMOP_RETURN->aopu.aop_reg[i]->name; + return; + } + + if (sym->accuse) + { + if (sym->accuse == ACCUSE_A) /* For compability with old register allocator only */ + { + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = getSize (sym->type); + wassertl (aop->size == 1, "Internal error: Caching in A, but too big to fit in A"); + aop->aopu.aop_reg[0] = regsZ80 + A_IDX; + } + else if (sym->accuse == ACCUSE_IY) /* For compability with old register allocator only */ + { + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = getSize (sym->type); + wassertl (aop->size <= 2, "Internal error: Caching in IY, but too big to fit in IY"); + aop->aopu.aop_reg[0] = regsZ80 + IYL_IDX; + aop->aopu.aop_reg[0] = regsZ80 + IYH_IDX; + } + else + { + wassertl (0, "Marked as being allocated into A or IY but is actually in neither"); + } + return; + } + + /* rematerialize it NOW */ + if (sym->remat) + { + sym->aop = op->aop = aop = aopForRemat (sym); + aop->size = getSize (sym->type); + return; + } + + /* On-stack for dry run. */ + if (sym->nRegs && regalloc_dry_run) + { + sym->aop = op->aop = aop = newAsmop (AOP_STK); + aop->size = getSize (sym->type); + return; + } + + /* On stack. */ + if (sym->isspilt && sym->usl.spillLoc) + { + asmop *oldAsmOp = NULL; + + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* force a new aop if sizes differ */ + oldAsmOp = sym->usl.spillLoc->aop; + sym->usl.spillLoc->aop = NULL; + } + sym->aop = op->aop = aop = aopForSym (ic, sym->usl.spillLoc, requires_a); + if (getSize (sym->type) != getSize (sym->usl.spillLoc->type)) + { + /* Don't reuse the new aop, go with the last one */ + sym->usl.spillLoc->aop = oldAsmOp; + } + aop->size = getSize (sym->type); + return; + } + + /* else must be a dummy iTemp */ + sym->aop = op->aop = aop = newAsmop (AOP_DUMMY); + aop->size = getSize (sym->type); + return; + } + + /* must be in a register */ + sym->aop = op->aop = aop = newAsmop (AOP_REG); + aop->size = sym->nRegs; + for (i = 0; i < sym->nRegs; i++) + { + wassertl (sym->regs[i], "Symbol in register, but no register assigned."); + if(!sym->regs[i]) + fprintf(stderr, "Symbol %s at ic %d.\n", sym->name, ic->key); + aop->aopu.aop_reg[i] = sym->regs[i]; + aop->regs[sym->regs[i]->rIdx] = i; + } +} + +/*-----------------------------------------------------------------*/ +/* freeAsmop - free up the asmop given to an operand */ +/*----------------------------------------------------------------*/ +static void +freeAsmop (operand * op, asmop *aaop) +{ + asmop *aop; + + if (!op) + aop = aaop; + else + aop = op->aop; + + if (!aop) + return; + + if (aop->freed) + goto dealloc; + + aop->freed = 1; + + if (aop->type == AOP_PAIRPTR && !IS_GB && aop->aopu.aop_pairId == PAIR_DE) + { + _pop (aop->aopu.aop_pairId); + } + + if (getPairId (aop) == PAIR_HL) + { + spillPair (PAIR_HL); + } + +dealloc: + /* all other cases just dealloc */ + if (op) + { + op->aop = NULL; + if (IS_SYMOP (op)) + { + OP_SYMBOL (op)->aop = NULL; + /* if the symbol has a spill */ + if (SPIL_LOC (op)) + SPIL_LOC (op)->aop = NULL; + } + } + +} + +static bool +isLitWord (const asmop *aop) +{ + /* if (aop->size != 2) + return FALSE; */ + switch (aop->type) + { + case AOP_IMMD: + case AOP_LIT: + return TRUE; + default: + return FALSE; + } +} + +static const char * +aopGetLitWordLong (const asmop *aop, int offset, bool with_hash) +{ + static struct dbuf_s dbuf = { 0 }; + + if (dbuf_is_initialized (&dbuf)) + { + dbuf_set_length (&dbuf, 0); + } + else + { + dbuf_init (&dbuf, 128); + } + + /* depending on type */ + switch (aop->type) + { + case AOP_HL: + case AOP_IY: + case AOP_IMMD: + /* PENDING: for re-target */ + if (with_hash) + { + dbuf_tprintf (&dbuf, "!hashedstr + %d", aop->aopu.aop_immd, offset); + } + else if (offset == 0) + { + dbuf_tprintf (&dbuf, "%s", aop->aopu.aop_immd); + } + else + { + dbuf_tprintf (&dbuf, "%s + %d", aop->aopu.aop_immd, offset); + } + break; + + case AOP_LIT: + { + value *val = aop->aopu.aop_lit; + /* if it is a float then it gets tricky */ + /* otherwise it is fairly simple */ + if (!IS_FLOAT (val->type)) + { + unsigned long long v = ullFromVal (val); + + v >>= (offset * 8); + + dbuf_tprintf (&dbuf, with_hash ? "!immedword" : "!constword", (unsigned long) (v & 0xffffull)); + } + else + { + union + { + float f; + unsigned char c[4]; + } + fl; + unsigned int i; + + /* it is type float */ + fl.f = (float) floatFromVal (val); + +#ifdef WORDS_BIGENDIAN + i = fl.c[3 - offset] | (fl.c[3 - offset - 1] << 8); +#else + i = fl.c[offset] | (fl.c[offset + 1] << 8); +#endif + dbuf_tprintf (&dbuf, with_hash ? "!immedword" : "!constword", i); + } + } + break; + + case AOP_REG: + case AOP_STK: + case AOP_DIR: + case AOP_SFR: + case AOP_STR: + case AOP_CRY: + case AOP_EXSTK: + case AOP_PAIRPTR: + case AOP_DUMMY: + break; + + default: + dbuf_destroy (&dbuf); + fprintf (stderr, "aop->type: %d\n", aop->type); + wassertl (0, "aopGetLitWordLong got unsupported aop->type"); + exit (0); + } + return dbuf_c_str (&dbuf); +} + +static bool +isPtr (const char *s) +{ + if (!strcmp (s, "hl")) + return TRUE; + if (!strcmp (s, "ix")) + return TRUE; + if (!strcmp (s, "iy")) + return TRUE; + return FALSE; +} + +static void +adjustPair (const char *pair, int *pold, int new_val) +{ + wassert (pair); + + while (*pold < new_val) + { + emit2 ("inc %s", pair); + (*pold)++; + } + while (*pold > new_val) + { + emit2 ("dec %s", pair); + (*pold)--; + } +} + +static void +spillCached (void) +{ + spillPair (PAIR_HL); + spillPair (PAIR_IY); +} + +static bool +requiresHL (const asmop * aop) +{ + switch (aop->type) + { + case AOP_IY: + return FALSE; + case AOP_HL: + case AOP_EXSTK: + return TRUE; + case AOP_STK: + return (IS_GB || _G.omitFramePtr); + case AOP_REG: + { + int i; + for (i = 0; i < aop->size; i++) + { + wassert (aop->aopu.aop_reg[i]); + if (aop->aopu.aop_reg[i]->rIdx == L_IDX || aop->aopu.aop_reg[i]->rIdx == H_IDX) + return TRUE; + } + } + case AOP_PAIRPTR: + return (aop->aopu.aop_pairId == PAIR_HL); + default: + return FALSE; + } +} + +/*----------------------------------------------------------*/ +/* strtoul_z80: a wrapper to strtoul, which can also handle */ +/* hex numbers with a $ prefix. */ +/*----------------------------------------------------------*/ +static unsigned long int +strtoul_z80asm (const char *nptr, char **endptr, int base) +{ + char *p = NULL; + int i, flag = 0, len; + unsigned long ret; + + if (nptr != NULL && (p = malloc ((len = strlen (nptr)) + 1 + 1)) != NULL) + { + memset (p, 0, len + 2); + for (i = 0; i < len; i++) + { + if (!flag) + if (isspace (nptr[i])) + p[i] = nptr[i]; + else if (nptr[i] == '$') + { + p[i] = '0'; + p[i + 1] = 'x'; + flag = 1; + } + else + break; + else + p[i + 1] = nptr[i]; + } + } + + if (flag) + ret = strtoul (p, endptr, base); + else + ret = strtoul (nptr, endptr, base); + + if (p) + free (p); + return ret; +} + +static void +fetchLitPair (PAIR_ID pairId, asmop *left, int offset) +{ + const char *pair = _pairs[pairId].name; + char *l = Safe_strdup (aopGetLitWordLong (left, offset, FALSE)); + char *base_str = Safe_strdup (aopGetLitWordLong (left, 0, FALSE)); + const char *base = base_str; + + wassert (pair); + + emitDebug (";fetchLitPair"); + + if (isPtr (pair)) + { + if (pairId == PAIR_HL || pairId == PAIR_IY) + { + if (pairId == PAIR_HL && base[0] == '0') // Ugly workaround + { + unsigned int tmpoffset; + const char *tmpbase; + if (sscanf (base, "%xd", &tmpoffset) && (tmpbase = strchr (base, '+'))) + { + offset = tmpoffset; + base = tmpbase++; + } + } + if ((_G.pairs[pairId].last_type == AOP_IMMD && left->type == AOP_IMMD) || + (_G.pairs[pairId].last_type == AOP_IY && left->type == AOP_IY) || + (_G.pairs[pairId].last_type == AOP_HL && left->type == AOP_HL)) + { + if (!regalloc_dry_run && _G.pairs[pairId].base && !strcmp (_G.pairs[pairId].base, base)) // Todo: Exact cost. + { + if (pairId == PAIR_HL && abs (_G.pairs[pairId].offset - offset) < 3) + { + adjustPair (pair, &_G.pairs[pairId].offset, offset); + goto adjusted; + } + if (pairId == PAIR_IY && offset == _G.pairs[pairId].offset) + goto adjusted; + } + } + } + + if (pairId == PAIR_HL && left->type == AOP_LIT && _G.pairs[pairId].last_type == AOP_LIT && + !IS_FLOAT (left->aopu.aop_lit->type) && offset == 0 && _G.pairs[pairId].offset == 0) + { + unsigned new_low, new_high, old_low, old_high; + unsigned long v_new = ulFromVal (left->aopu.aop_lit); + unsigned long v_old = strtoul_z80asm (_G.pairs[pairId].base, NULL, 0); + new_low = (v_new >> 0) & 0xff; + new_high = (v_new >> 8) & 0xff; + old_low = (v_old >> 0) & 0xff; + old_high = (v_old >> 8) & 0xff; + + /* Change lower byte only. */ + if (new_high == old_high) + { + emit3_o (A_LD, ASMOP_L, 0, left, 0); + goto adjusted; + } + /* Change upper byte only. */ + else if (new_low == old_low) + { + emit3_o (A_LD, ASMOP_H, 0, left, 1); + goto adjusted; + } + } + + + _G.pairs[pairId].last_type = left->type; + _G.pairs[pairId].base = traceAlloc (&_G.trace.aops, Safe_strdup (base)); + _G.pairs[pairId].offset = offset; + } + /* Both a lit on the right and a true symbol on the left */ + emit2 ("ld %s, !hashedstr", pair, l); + regalloc_dry_run_cost += (pairId == PAIR_IX || pairId == PAIR_IY) ? 4 : 3; + Safe_free (base_str); + Safe_free (l); + return; + +adjusted: + _G.pairs[pairId].last_type = left->type; + _G.pairs[pairId].base = traceAlloc (&_G.trace.aops, Safe_strdup (base)); + _G.pairs[pairId].offset = offset; + Safe_free (base_str); + Safe_free (l); +} + +static PAIR_ID +makeFreePairId (const iCode * ic, bool * pisUsed) +{ + *pisUsed = FALSE; + + if (ic != NULL) + { + if (!bitVectBitValue (ic->rMask, B_IDX) && !bitVectBitValue (ic->rMask, C_IDX)) + { + return PAIR_BC; + } + else if (!IS_GB && !bitVectBitValue (ic->rMask, D_IDX) && !bitVectBitValue (ic->rMask, E_IDX)) + { + return PAIR_DE; + } + else + { + *pisUsed = TRUE; + return PAIR_HL; + } + } + else + { + *pisUsed = TRUE; + return PAIR_HL; + } +} + +/* If ic != 0, we can safely use isPairDead(). */ +static void +fetchPairLong (PAIR_ID pairId, asmop *aop, const iCode *ic, int offset) +{ + emitDebug (";fetchPairLong"); + + /* if this is rematerializable */ + if (isLitWord (aop)) + fetchLitPair (pairId, aop, offset); + else + { + if (getPairId (aop) == pairId) + { + /* Do nothing */ + } + else if (IS_EZ80_Z80 && aop->size - offset >= 2 && aop->type == AOP_STK) + { + int fp_offset = aop->aopu.aop_stk + offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + emit2 ("ld %s, %d (ix)", _pairs[pairId].name, fp_offset); + regalloc_dry_run_cost += 3; + } + /* Getting the parameter by a pop / push sequence is cheaper when we have a free pair (except for the Rabbit, which has an even cheaper sp-relative load). + Stack allocation can change after register allocation, so assume this optimization is not possible for the allocator's cost function (unless the stack location is for a parameter). */ + else if (!IS_RAB && aop->size - offset >= 2 && + (aop->type == AOP_STK || aop->type == AOP_EXSTK) && (!regalloc_dry_run || aop->aopu.aop_stk > 0) + && (aop->aopu.aop_stk + offset + _G.stack.offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0) + + _G.stack.pushed) == 2 && ic && getFreePairId (ic) != PAIR_INVALID && getFreePairId (ic) != pairId) + { + PAIR_ID extrapair = getFreePairId (ic); + _pop (extrapair); + _pop (pairId); + _push (pairId); + _push (extrapair); + } + /* Todo: Use even cheaper ex hl, (sp) and ex iy, (sp) when possible. */ + else if ((!IS_RAB || pairId == PAIR_BC || pairId == PAIR_DE) && aop->size - offset >= 2 && + (aop->type == AOP_STK || aop->type == AOP_EXSTK) && (!regalloc_dry_run || aop->aopu.aop_stk > 0) + && (aop->aopu.aop_stk + offset + _G.stack.offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0) + + _G.stack.pushed) == 0) + { + _pop (pairId); + _push (pairId); + } + else if (!IS_GB && (aop->type == AOP_IY || aop->type == AOP_HL) && !(pairId == PAIR_IY && aop->size < 2)) + { + /* Instead of fetching relative to IY, just grab directly + from the address IY refers to */ + emit2 ("ld %s, (%s)", _pairs[pairId].name, aopGetLitWordLong (aop, offset, FALSE)); + regalloc_dry_run_cost += (pairId == PAIR_HL ? 3 : 4); + + if (aop->size < 2) + { + emit2 ("ld %s, !zero", _pairs[pairId].h); + regalloc_dry_run_cost += 2; + } + } + /* we need to get it byte by byte */ + else if (pairId == PAIR_HL && (IS_GB || (IY_RESERVED && (aop->type == AOP_HL || aop->type == AOP_EXSTK))) && requiresHL (aop)) + { + if (!regalloc_dry_run) // TODO: Fix this to get correct cost! + aopGet (aop, offset, FALSE); + switch (aop->size - offset) + { + case 1: + emit2 ("ld l, !*hl"); + emit2 ("ld h, !immedbyte", 0); + regalloc_dry_run_cost += 3; + break; + default: + wassertl (aop->size - offset > 1, "Attempted to fetch no data into HL"); + if (IS_RAB || IS_TLCS90) + { + emit2 ("ld hl, 0 (hl)"); + regalloc_dry_run_cost += 3; + } + else if (IS_EZ80_Z80) + { + emit2 ("ld hl, (hl)"); + regalloc_dry_run_cost += 2; + } + else + { + if (ic && bitVectBitValue (ic->rMask, A_IDX)) + _push (PAIR_AF); + + emit2 ("ld a, !*hl"); + emit2 ("inc hl"); + emit2 ("ld h, !*hl"); + emit2 ("ld l, a"); + regalloc_dry_run_cost += 4; + + if (ic && bitVectBitValue (ic->rMask, A_IDX)) + _pop (PAIR_AF); + } + break; + } + } + else if (pairId == PAIR_IY) + { + /* The Rabbit has the ld iy, n (sp) instruction. */ + int fp_offset = aop->aopu.aop_stk + offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + if ((IS_RAB || IS_TLCS90) && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && abs (sp_offset) <= 127) + { + emit2 ("ld iy, %d (sp)", sp_offset); + regalloc_dry_run_cost += 3; + } + else if (isPair (aop) && (IS_RAB || IS_TLCS90) && getPairId (aop) == PAIR_HL) + { + emit2 ("ld iy, hl"); + regalloc_dry_run_cost += (1 + IS_RAB); + } + else if (isPair (aop)) + { + emit2 ("push %s", _pairs[getPairId (aop)].name); + emit2 ("pop iy"); + regalloc_dry_run_cost += 3; + } + else + { + bool isUsed; + PAIR_ID id = makeFreePairId (ic, &isUsed); + if (isUsed) + _push (id); + /* Can't load into parts, so load into HL then exchange. */ + if (!regalloc_dry_run) + { + emit2 ("ld %s, %s", _pairs[id].l, aopGet (aop, offset, FALSE)); + emit2 ("ld %s, %s", _pairs[id].h, aopGet (aop, offset + 1, FALSE)); + } + regalloc_dry_run_cost += ld_cost (ASMOP_L, aop) + ld_cost (ASMOP_H, aop); + + if ((IS_RAB || IS_TLCS90) && id == PAIR_HL) + { + emit2 ("ld iy, hl"); + regalloc_dry_run_cost += (1 + IS_RAB); + } + else + { + emit2 ("push %s", _pairs[id].name); + emit2 ("pop iy"); + regalloc_dry_run_cost += 3; + } + if (isUsed) + _pop (id); + } + } + else if (isUnsplitable (aop)) + { + emit2 ("push %s", _pairs[getPairId (aop)].name); + emit2 ("pop %s", _pairs[pairId].name); + regalloc_dry_run_cost += (pairId == PAIR_IY ? 2 : 1) + (getPairId (aop) == PAIR_IY ? 2 : 1); + } + else + { + /* The Rabbit has the ld hl, n (sp) and ld hl, n (ix) instructions. */ + int fp_offset = aop->aopu.aop_stk + offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + if ((IS_RAB || IS_TLCS90) && aop->size - offset >= 2 && (aop->type == AOP_STK || aop->type == AOP_EXSTK) + && (pairId == PAIR_HL || pairId == PAIR_IY || pairId == PAIR_DE) && (abs (fp_offset) <= 127 && pairId == PAIR_HL + && aop->type == AOP_STK + || abs (sp_offset) <= 127)) + { + if (pairId == PAIR_DE) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost += 1; + } + if (abs (sp_offset) <= 127) + emit2 ("ld %s, %d (sp)", pairId == PAIR_IY ? "iy" : "hl", sp_offset); /* Fetch relative to stack pointer. */ + else + emit2 ("ld hl, %d (ix)", fp_offset); /* Fetch relative to frame pointer. */ + regalloc_dry_run_cost += (pairId == PAIR_IY ? 3 : 2); + if (pairId == PAIR_DE) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost += 1; + } + } + /* Operand resides (partially) in the pair */ + else if (!regalloc_dry_run && !strcmp (aopGet (aop, offset + 1, FALSE), _pairs[pairId].l)) // aopGet (aop, offset + 1, FALSE) is problematic: It prevents calculation of exact cost, and results in redundant code being generated. Todo: Exact cost + { + _moveA3 (aop, offset); + if (!regalloc_dry_run) + emit2 ("ld %s, %s", _pairs[pairId].h, aopGet (aop, offset + 1, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_A, aop); + emit2 ("ld %s, a", _pairs[pairId].l); + regalloc_dry_run_cost += 1; + } + /* The Rabbit's cast to bool is a cheap way of zeroing h (similar to xor a, a for a for the Z80). */ + else if (pairId == PAIR_HL && IS_RAB && aop->size - offset == 1 && !(aop->type == AOP_REG && (aop->aopu.aop_reg[offset]->rIdx == L_IDX || aop->aopu.aop_reg[offset]->rIdx == H_IDX))) + { + emit2 ("bool hl"); + regalloc_dry_run_cost++; + if (!regalloc_dry_run) + emit2 ("ld %s, %s", _pairs[pairId].l, aopGet (aop, offset, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_L, aop); + } + else + { + if (!aopInReg (aop, offset, _pairs[pairId].l_idx)) + { + if (!regalloc_dry_run) + emit2 ("ld %s, %s", _pairs[pairId].l, aopGet (aop, offset, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_L, aop); + } + if (!aopInReg (aop, offset + 1, _pairs[pairId].h_idx)) + { + if (!regalloc_dry_run) + emit2 ("ld %s, %s", _pairs[pairId].h, aopGet (aop, offset + 1, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_H, aop); + } + } + } + /* PENDING: check? */ + spillPair (pairId); + } +} + +static void +fetchPair (PAIR_ID pairId, asmop *aop) +{ + fetchPairLong (pairId, aop, NULL, 0); +} + +static void +setupPairFromSP (PAIR_ID id, int offset) +{ + wassertl (id == PAIR_HL || id == PAIR_DE || id == PAIR_IY, "Setup relative to SP only implemented for HL, DE, IY"); + + if (_G.preserveCarry) + { + _push (PAIR_AF); + regalloc_dry_run_cost++; + offset += 2; + } + + if (id == PAIR_DE && !IS_GB) // TODO: Could hl be in use for gbz80, so it needs to be saved and restored? + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost++; + } + + if (offset < INT8MIN || offset > INT8MAX || id == PAIR_IY) + { + struct dbuf_s dbuf; + PAIR_ID lid = (id == PAIR_DE) ? PAIR_HL : id; + dbuf_init (&dbuf, sizeof(int) * 3 + 1); + dbuf_printf (&dbuf, "%d", offset); + emit2 ("ld %s, !hashedstr", _pairs[lid].name, dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + emit2 ("add %s, sp", _pairs[lid].name); + regalloc_dry_run_cost += 4 + (id == PAIR_IY) * 2; + } + else + { + wassert (id == PAIR_DE || id == PAIR_HL); + emit2 ("!ldahlsp", offset); + regalloc_dry_run_cost += 4 - IS_GB * 2; + } + + if (id == PAIR_DE && !IS_GB) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost++; + } + else if (id == PAIR_DE) + { + genMovePairPair (PAIR_HL, PAIR_DE); + spillPair (PAIR_HL); + } + + if (_G.preserveCarry) + { + _pop (PAIR_AF); + regalloc_dry_run_cost++; + offset -= 2; + } +} + +static void +shiftIntoPair (PAIR_ID id, asmop *aop); + +/*-----------------------------------------------------------------*/ +/* pointPairToAop() make a register pair point to a byte of an aop */ +/*-----------------------------------------------------------------*/ +static void pointPairToAop (PAIR_ID pairId, const asmop *aop, int offset) +{ + switch (aop->type) + { + case AOP_EXSTK: + wassertl (!IS_GB, "The GBZ80 doesn't have an extended stack"); + + case AOP_STK: + ; int abso = aop->aopu.aop_stk + offset + _G.stack.offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + + if ((_G.pairs[pairId].last_type == AOP_STK || _G.pairs[pairId].last_type == AOP_EXSTK) && abs (_G.pairs[pairId].offset - abso) < 3) + adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, abso); + else + setupPairFromSP (pairId, abso + _G.stack.pushed); + + _G.pairs[pairId].offset = abso; + + break; + + case AOP_HL: // Legacy. + fetchLitPair (pairId, (asmop *) aop, offset); + _G.pairs[pairId].offset = offset; + break; + + case AOP_PAIRPTR: + wassert (!offset); + + shiftIntoPair (pairId, (asmop *) aop); // Legacy. Todo eliminate uses of shiftIntoPair() ? + + break; + + default: + wassertl (0, "Unsupported aop type for pointPairToAop()"); + } + + _G.pairs[pairId].last_type = aop->type; +} + +// Weird function. Sometimes offset is used, sometimes not. +// Callers rely on that behaviour. Uses of this should be replaced +// by pointPairToAop() above after the 3.7.0 release. +static void +setupPair (PAIR_ID pairId, asmop *aop, int offset) +{ + switch (aop->type) + { + case AOP_IY: + wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "AOP_IY must be in IY or HL"); + fetchLitPair (pairId, aop, 0); + break; + + case AOP_HL: + wassertl (pairId == PAIR_HL, "AOP_HL must be in HL"); + + fetchLitPair (pairId, aop, offset); + _G.pairs[pairId].offset = offset; + break; + + case AOP_EXSTK: + wassertl (!IS_GB, "The GBZ80 doesn't have an extended stack"); + wassertl (pairId == PAIR_IY || pairId == PAIR_HL, "The Z80 extended stack must be in IY or HL"); + + { + int offset = aop->aopu.aop_stk + _G.stack.offset; + + if (aop->aopu.aop_stk >= 0) + offset += _G.stack.param_offset; + + if (_G.pairs[pairId].last_type == aop->type && abs(_G.pairs[pairId].offset - offset) <= 3) + adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, offset); + else + { + struct dbuf_s dbuf; + + /* PENDING: Do this better. */ + if (_G.preserveCarry) + _push (PAIR_AF); + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%d", offset + _G.stack.pushed); + emit2 ("ld %s, !hashedstr", _pairs[pairId].name, dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + emit2 ("add %s, sp", _pairs[pairId].name); + _G.pairs[pairId].last_type = aop->type; + _G.pairs[pairId].offset = offset; + if (_G.preserveCarry) + _pop (PAIR_AF); + } + } + break; + + case AOP_STK: + { + /* Doesnt include _G.stack.pushed */ + int abso = aop->aopu.aop_stk + offset + _G.stack.offset + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + + assert (pairId == PAIR_HL); + /* In some cases we can still inc or dec hl */ + if (_G.pairs[pairId].last_type == AOP_STK && abs (_G.pairs[pairId].offset - abso) < 3) + { + adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, abso); + } + else + { + setupPairFromSP (PAIR_HL, abso + _G.stack.pushed); + } + _G.pairs[pairId].offset = abso; + break; + } + + case AOP_PAIRPTR: + if (pairId != aop->aopu.aop_pairId) + genMovePairPair (aop->aopu.aop_pairId, pairId); + adjustPair (_pairs[pairId].name, &_G.pairs[pairId].offset, offset); + break; + + default: + wassert (0); + } + _G.pairs[pairId].last_type = aop->type; +} + +static void +emitLabelSpill (symbol *tlbl) +{ + emitLabel (tlbl); + spillCached (); +} + +/*-----------------------------------------------------------------*/ +/* aopGet - for fetching value of the aop */ +/*-----------------------------------------------------------------*/ +static const char * +aopGet (asmop *aop, int offset, bool bit16) +{ + static struct dbuf_s dbuf = { 0 }; + + wassert_bt (!regalloc_dry_run); + + if (dbuf_is_initialized (&dbuf)) + { + /* reuse the dynamically allocated buffer */ + dbuf_set_length (&dbuf, 0); + } + else + { + /* first time: initialize the dynamically allocated buffer */ + dbuf_init (&dbuf, 128); + } + + /* offset is greater than size then zero */ + /* PENDING: this seems a bit screwed in some pointer cases. */ + if (offset > (aop->size - 1) && aop->type != AOP_LIT) + { + dbuf_tprintf (&dbuf, "!zero"); + } + else + { + /* depending on type */ + switch (aop->type) + { + case AOP_DUMMY: + dbuf_append_char (&dbuf, 'a'); + break; + + case AOP_IMMD: + /* PENDING: re-target */ + if (bit16) + dbuf_tprintf (&dbuf, "!immedword", aop->aopu.aop_immd); + else + { + switch (offset) + { + case 2: + // dbuf_tprintf (&dbuf, "!bankimmeds", aop->aopu.aop_immd); Bank support not fully implemented yet. + dbuf_tprintf (&dbuf, "#0x00"); + break; + + case 1: + dbuf_tprintf (&dbuf, "!msbimmeds", aop->aopu.aop_immd); + break; + + case 0: + dbuf_tprintf (&dbuf, "!lsbimmeds", aop->aopu.aop_immd); + break; + + default: + dbuf_tprintf (&dbuf, "#0x00"); + } + } + break; + + case AOP_DIR: + wassert (IS_GB); + emit2 ("ld a, (%s+%d)", aop->aopu.aop_dir, offset); + regalloc_dry_run_cost += 3; + dbuf_append_char (&dbuf, 'a'); + break; + + case AOP_SFR: + wassertl (!IS_TLCS90, "TLCS-90 does not have a separate I/O space"); + if (IS_GB) + { + emit2 ("ldh a, (%s+%d)", aop->aopu.aop_dir, offset); + regalloc_dry_run_cost += 2; + dbuf_append_char (&dbuf, 'a'); + } + else if (IS_RAB) + { + emit2 ("ioi"); + emit2 ("ld a, (%s)", aop->aopu.aop_dir); + emit2 ("nop"); /* Workaround for Rabbit 2000 hardware bug. see TN302 for details. */ + dbuf_append_char (&dbuf, 'a'); + } + else + { + /*.p.t.20030716 handling for i/o port read access for Z80 */ + if (aop->paged) + { + /* banked mode */ + /* reg A goes to address bits 15-8 during "in a,(x)" instruction */ + emit2 ("ld a, !msbimmeds", aop->aopu.aop_dir); + emit2 ("in a, (!lsbimmeds)", aop->aopu.aop_dir); + } + else if (z80_opts.port_mode == 180) + { + /* z180 in0/out0 mode */ + emit2 ("in0 a, (%s)", aop->aopu.aop_dir); + } + else + { + /* 8 bit mode */ + emit2 ("in a, (%s)", aop->aopu.aop_dir); + } + + dbuf_append_char (&dbuf, 'a'); + } + break; + + case AOP_REG: + dbuf_append_str (&dbuf, aop->aopu.aop_reg[offset]->name); + break; + + case AOP_HL: + setupPair (PAIR_HL, aop, offset); + dbuf_tprintf (&dbuf, "!*hl"); + break; + + case AOP_IY: + wassert (!IS_GB); + setupPair (PAIR_IY, aop, offset); + dbuf_tprintf (&dbuf, "!*iyx", offset); + break; + + case AOP_EXSTK: + if (!IY_RESERVED) + { + wassert (!IS_GB); + setupPair (PAIR_IY, aop, offset); + dbuf_tprintf (&dbuf, "!*iyx", offset); + break; + } + + case AOP_STK: + if (IS_GB || aop->type == AOP_EXSTK) + { + pointPairToAop (PAIR_HL, aop, offset); + dbuf_tprintf (&dbuf, "!*hl"); + } + else if (_G.omitFramePtr) + { + if (aop->aopu.aop_stk >= 0) + offset += _G.stack.param_offset; + setupPair (PAIR_IX, aop, offset); + dbuf_tprintf (&dbuf, "!*ixx", offset); + } + else + { + if (aop->aopu.aop_stk >= 0) + offset += _G.stack.param_offset; + dbuf_tprintf (&dbuf, "!*ixx", aop->aopu.aop_stk + offset); + } + break; + + case AOP_CRY: + wassertl (0, "Tried to fetch from a bit variable"); + break; + + case AOP_LIT: + dbuf_append_str (&dbuf, aopLiteral (aop->aopu.aop_lit, offset)); + break; + + case AOP_STR: + aop->coff = offset; + dbuf_append_str (&dbuf, aop->aopu.aop_str[offset]); + break; + + case AOP_PAIRPTR: + setupPair (aop->aopu.aop_pairId, aop, offset); + if (aop->aopu.aop_pairId == PAIR_IX) + dbuf_tprintf (&dbuf, "!*ixx", offset); + else if (aop->aopu.aop_pairId == PAIR_IY) + dbuf_tprintf (&dbuf, "!*iyx", offset); + else + dbuf_printf (&dbuf, "(%s)", _pairs[aop->aopu.aop_pairId].name); + break; + + default: + dbuf_destroy (&dbuf); + fprintf (stderr, "aop->type: %d\n", aop->type); + wassertl (0, "aopGet got unsupported aop->type"); + exit (0); + } + } + return dbuf_c_str (&dbuf); +} + +static bool +isRegString (const char *s) +{ + if (!strcmp (s, "b") || !strcmp (s, "c") || !strcmp (s, "d") || !strcmp (s, "e") || + !strcmp (s, "a") || !strcmp (s, "h") || !strcmp (s, "l")) + return TRUE; + return FALSE; +} + +static bool +isConstantString (const char *s) +{ + /* This is a bit of a hack... */ + return (*s == '#' || *s == '$'); +} + +#define AOP(op) op->aop +#define AOP_TYPE(op) AOP(op)->type +#define AOP_SIZE(op) AOP(op)->size +#define AOP_NEEDSACC(x) (AOP(x) && ((AOP_TYPE(x) == AOP_CRY) || (AOP_TYPE(x) == AOP_SFR))) +#define AOP_IS_PAIRPTR(x, p) (AOP_TYPE (x) == AOP_PAIRPTR && AOP (x)->aopu.aop_pairId == p) + +static bool +canAssignToPtr (const char *s) +{ + if (isRegString (s)) + return TRUE; + if (isConstantString (s)) + return TRUE; + return FALSE; +} + +static bool +canAssignToPtr3 (const asmop *aop) +{ + if (aop->type == AOP_REG) + return (TRUE); + if (aop->type == AOP_IMMD || aop->type == AOP_LIT) + return (TRUE); + return (FALSE); +} + +/*-----------------------------------------------------------------*/ +/* aopPut - puts a string for a aop */ +/*-----------------------------------------------------------------*/ +static void +aopPut (asmop *aop, const char *s, int offset) +{ + struct dbuf_s dbuf; + + wassert (!regalloc_dry_run); + + if (aop->size && offset > (aop->size - 1)) + { + werror_bt (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got offset > aop->size"); + exit (0); + } + + // PENDING + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, s); + s = dbuf_c_str (&dbuf); + + /* will assign value to value */ + /* depending on where it is of course */ + switch (aop->type) + { + case AOP_DUMMY: + _moveA (s); /* in case s is volatile */ + break; + + case AOP_DIR: + /* Direct. Hmmm. */ + wassert (IS_GB); + if (strcmp (s, "a")) + emit2 ("ld a, %s", s); + emit2 ("ld (%s+%d),a", aop->aopu.aop_dir, offset); + break; + + case AOP_SFR: + wassertl (!IS_TLCS90, "TLCS-90 does not have a separate I/O space"); + if (IS_GB) + { + // wassert (IS_GB); + if (strcmp (s, "a")) + emit2 ("ld a, %s", s); + emit2 ("ldh (%s+%d),a", aop->aopu.aop_dir, offset); + } + else if (IS_RAB) + { + if (strcmp (s, "a")) + emit2 ("ld a, %s", s); + + /* LM 20110928: Need to fix to emit either "ioi" or "ioe" + * (for internal vs. external I/O space + */ + emit2 ("ioi"); + emit2 ("ld (%s),a", aop->aopu.aop_dir); + emit2 ("nop"); /* Workaround for Rabbit 2000 hardware bug. see TN302 for details. */ + } + else + { + /*.p.t.20030716 handling for i/o port read access for Z80 */ + if (aop->paged) + { + /* banked mode */ + if (aop->bcInUse) + emit2 ("push bc"); + + if (strlen (s) != 1 || (s[0] != 'a' && s[0] != 'd' && s[0] != 'e' && s[0] != 'h' && s[0] != 'l')) + { + emit2 ("ld a, %s", s); + s = "a"; + } + + emit2 ("ld bc,#%s", aop->aopu.aop_dir); + emit2 ("out (c),%s", s); + + if (aop->bcInUse) + emit2 ("pop bc"); + else + spillPair (PAIR_BC); + } + else if (z80_opts.port_mode == 180) + { + /* z180 in0/out0 mode */ + emit2 ("ld a, %s", s); + emit2 ("out0 (%s), a", aop->aopu.aop_dir); + } + else + { + /* 8 bit mode */ + if (strcmp (s, "a")) + emit2 ("ld a, %s", s); + emit2 ("out (%s), a", aop->aopu.aop_dir); + } + } + break; + + case AOP_REG: + if (!strcmp (aop->aopu.aop_reg[offset]->name, s)) + ; + else if (!strcmp (s, "!*hl")) + emit2 ("ld %s,!*hl", aop->aopu.aop_reg[offset]->name); + else + emit2 ("ld %s, %s", aop->aopu.aop_reg[offset]->name, s); + spillPairReg (aop->aopu.aop_reg[offset]->name); + break; + + case AOP_IY: + wassert (!IS_GB); + if (!canAssignToPtr (s)) + { + emit2 ("ld a, %s", s); + setupPair (PAIR_IY, aop, offset); + emit2 ("ld !*iyx, a", offset); + } + else + { + setupPair (PAIR_IY, aop, offset); + emit2 ("ld !*iyx, %s", offset, s); + } + break; + + case AOP_HL: + //wassert (IS_GB); + /* PENDING: for re-target */ + if (!strcmp (s, "!*hl") || !strcmp (s, "(hl)") || !strcmp (s, "[hl]")) + { + emit2 ("ld a, !*hl"); + s = "a"; + } + else if (strstr (s, "(ix)") || strstr (s, "(iy)")) + { + emit2 ("ld a, %s", s); + s = "a"; + } + setupPair (PAIR_HL, aop, offset); + + emit2 ("ld !*hl, %s", s); + break; + + case AOP_EXSTK: + if(!IY_RESERVED) + { + wassert (!IS_GB); + if (!canAssignToPtr (s)) + { + emit2 ("ld a, %s", s); + setupPair (PAIR_IY, aop, offset); + emit2 ("ld !*iyx, a", offset); + } + else + { + setupPair (PAIR_IY, aop, offset); + emit2 ("ld !*iyx, %s", offset, s); + } + break; + } + + case AOP_STK: + if (IS_GB || aop->type == AOP_EXSTK) + { + /* PENDING: re-target */ + if (!strcmp (s, "!*hl") || !strcmp (s, "(hl)") || !strcmp (s, "[hl]")) + { + emit2 ("ld a, !*hl"); + s = "a"; + } + pointPairToAop (PAIR_HL, aop, offset); + if (!canAssignToPtr (s)) + { + emit2 ("ld a, %s", s); + emit2 ("ld !*hl, a"); + } + else + emit2 ("ld !*hl, %s", s); + } + else + { + if (aop->aopu.aop_stk >= 0) + offset += _G.stack.param_offset; + if (!canAssignToPtr (s)) + { + emit2 ("ld a, %s", s); + emit2 ("ld !*ixx, a", aop->aopu.aop_stk + offset); + } + else + { + emit2 ("ld !*ixx, %s", aop->aopu.aop_stk + offset, s); + } + } + break; + + case AOP_CRY: + /* if bit variable */ + if (!aop->aopu.aop_dir) + { + emit2 ("ld a, !zero"); + emit2 ("rla"); + } + else + { + /* In bit space but not in C - cant happen */ + wassertl (0, "Tried to write into a bit variable"); + } + break; + + case AOP_STR: + aop->coff = offset; + if (strcmp (aop->aopu.aop_str[offset], s)) + { + emit2 ("ld %s, %s", aop->aopu.aop_str[offset], s); + } + spillPairReg (aop->aopu.aop_str[offset]); + break; + +#if 0 + case AOP_ACC: + aop->coff = offset; + if (!offset && (strcmp (s, "acc") == 0)) + break; + if (offset > 0) + { + wassertl (0, "Tried to access past the end of A"); + } + else + { + wassert (aop->aopu.aop_str[offset]); + wassert (s); + if (strcmp (aop->aopu.aop_str[offset], s)) + { + emit2 ("ld %s, %s", aop->aopu.aop_str[offset], s); + spillPairReg (aop->aopu.aop_str[offset]); + } + } + break; +#endif + + case AOP_PAIRPTR: + setupPair (aop->aopu.aop_pairId, aop, offset); + if (aop->aopu.aop_pairId == PAIR_IX) + emit2 ("ld !*ixx, %s", 0, s); + else if (aop->aopu.aop_pairId == PAIR_IY) + emit2 ("ld !*iyx, %s", 0, s); + else + emit2 ("ld (%s), %s", _pairs[aop->aopu.aop_pairId].name, s); + break; + + default: + dbuf_destroy (&dbuf); fprintf (stderr, "AOP_DIR: %d\n",AOP_DIR); + fprintf (stderr, "aop->type: %d\n", aop->type); + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "aopPut got unsupported aop->type"); + exit (0); + } + dbuf_destroy (&dbuf); +} + +// Move, but try not to. Cannot use xor to zero, since xor resets the carry flag. +static void +cheapMove (asmop *to, int to_offset, asmop *from, int from_offset, bool a_dead) +{ + if (aopInReg (to, to_offset, A_IDX)) + a_dead = true; + + if (to->type == AOP_REG && from->type == AOP_REG) + { + if (to->aopu.aop_reg[to_offset] == from->aopu.aop_reg[from_offset]) + return; + bool from_index = aopInReg (from, from_offset, IYL_IDX) || aopInReg (from, from_offset, IYH_IDX); + bool to_index = aopInReg (to, to_offset, IYL_IDX) || aopInReg (to, to_offset, IYH_IDX); + bool index = to_index || from_index; + if (!index || IS_EZ80_Z80) + { + bool a = aopInReg (to, to_offset, A_IDX) || aopInReg (from, from_offset, A_IDX); + if (!regalloc_dry_run) + aopPut (to, aopGet (from, from_offset, false), to_offset); + regalloc_dry_run_cost += 1 + (IS_TLCS90 + !a) + index; + return; + } + if (aopInReg (from, from_offset, IYL_IDX) && !to_index && a_dead) + { + _push(PAIR_IY); + _pop (PAIR_AF); + cheapMove (to, to_offset, ASMOP_A, 0, true); + return; + } + if (from_index && !to_index && _G.stack.pushed + _G.stack.offset + 2 <= 127 && !_G.omitFramePtr) + { + _push(PAIR_IY); + if (!regalloc_dry_run) + emit2 ("ld %s, %d (ix)", aopGet (to, to_offset, false), _G.stack.pushed + _G.stack.offset); + regalloc_dry_run_cost += 3; + _pop(PAIR_IY); + return; + } + + // Can't do it (todo: implement something there - will be expensive though, probably at least 7B of code). + regalloc_dry_run_cost += 100; + wassert (regalloc_dry_run); + } + + // Try to push to avoid setting up temporary stack pointer in hl or iy. + if ((to->type == AOP_STK || to->type == AOP_EXSTK) && _G.omitFramePtr && + (aopInReg (to, to_offset, A_IDX) || aopInReg (to, to_offset, B_IDX) || aopInReg (to, to_offset, D_IDX) || aopInReg (to, to_offset, H_IDX) || aopInReg (to, to_offset, IYH_IDX))) + { + int fp_offset = to->aopu.aop_stk + to_offset + (to->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (!sp_offset) + { + emit2 ("inc sp"); + emit2 ("push %s", aopInReg (from, from_offset, A_IDX) ? "af" : (aopInReg (from, from_offset, B_IDX) ? "bc" : (aopInReg (from, from_offset, D_IDX) ? "de" : (aopInReg (from, from_offset, H_IDX) ? "hl" : "iy")))); + emit2 ("inc sp"); + regalloc_dry_run_cost += 3 + aopInReg (from, from_offset, IYH_IDX); + return; + } + } + + if (aopInReg (from, from_offset, A_IDX) && to->type == AOP_IY) + { + emit2 ("ld (%s+%d), a", to->aopu.aop_dir, to_offset); + regalloc_dry_run_cost += 3; + } + else if (!aopInReg (to, to_offset, A_IDX) && !aopInReg (from, from_offset, A_IDX) && (from->type == AOP_DIR || from->type == AOP_SFR || to->type == AOP_IY && from->type == AOP_EXSTK)) // Go through a. + { + if (!a_dead) + _push (PAIR_AF); + + cheapMove (ASMOP_A, 0, from, from_offset, true); + cheapMove (to, to_offset, ASMOP_A, 0, true); + + if (!a_dead) + _pop (PAIR_AF); + } + else + { + if (!regalloc_dry_run) + aopPut (to, aopGet (from, from_offset, false), to_offset); + + regalloc_dry_run_cost += ld_cost (to, from_offset < from->size ? from : ASMOP_ZERO); + } +} + +static void +commitPair (asmop *aop, PAIR_ID id, const iCode *ic, bool dont_destroy) +{ + int fp_offset = aop->aopu.aop_stk + (aop->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + + if (getPairId (aop) == id) + return; + + /* Stack positions will change, so do not assume this is possible in the cost function. */ + if (!regalloc_dry_run && !IS_GB && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && !sp_offset + && ((!IS_RAB && id == PAIR_HL) || id == PAIR_IY) && !dont_destroy) + { + emit2 ("ex (sp), %s", _pairs[id].name); + regalloc_dry_run_cost += ((id == PAIR_IY || IS_RAB) ? 2 : 1); + spillPair (id); + } + else if ((IS_RAB || IS_TLCS90) && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && (id == PAIR_HL || id == PAIR_IY) && + (id == PAIR_HL && abs (fp_offset) <= 127 && aop->type == AOP_STK || abs (sp_offset) <= 127)) + { + if (abs (sp_offset) <= 127) + emit2 ("ld %d (sp), %s", sp_offset, id == PAIR_IY ? "iy" : "hl"); /* Relative to stack pointer. */ + else + emit2 ("ld %d (ix), hl", fp_offset); /* Relative to frame pointer. */ + regalloc_dry_run_cost += (id == PAIR_HL ? 2 : 3); + } + else if (IS_EZ80_Z80 && aop->type == AOP_STK) + { + emit2 ("ld %d (ix), %s", fp_offset, _pairs[id].name); + regalloc_dry_run_cost += 3; + } + else if (!regalloc_dry_run && (aop->type == AOP_STK || aop->type == AOP_EXSTK) && !sp_offset) + { + emit2 ("inc sp"); + emit2 ("inc sp"); + emit2 ("push %s", _pairs[id].name); + regalloc_dry_run_cost += (id == PAIR_IY ? 5 : 4); + } + + /* PENDING: Verify this. */ + else if (id == PAIR_HL && requiresHL (aop) && (IS_GB || IY_RESERVED && aop->type != AOP_HL && aop->type != AOP_IY)) + { + if (bitVectBitValue (ic->rSurv, D_IDX)) + _push (PAIR_DE); + if (!regalloc_dry_run) + { + emit2 ("ld a, l"); + emit2 ("ld d, h"); + aopPut (aop, "a", 0); + aopPut (aop, "d", 1); + } + regalloc_dry_run_cost += (2 + ld_cost (aop, ASMOP_A) + ld_cost (aop, ASMOP_D)); + if (bitVectBitValue (ic->rSurv, D_IDX)) + _pop (PAIR_DE); + } + else + { + /* Special cases */ + if ((aop->type == AOP_IY || aop->type == AOP_HL) && !IS_GB && aop->size == 2) + { + if (!regalloc_dry_run) + { + emit2 ("ld (%s), %s", aopGetLitWordLong (aop, 0, FALSE), _pairs[id].name); + } + regalloc_dry_run_cost += (id == PAIR_HL ? 3 : 4); + } + else + { + switch (id) + { + case PAIR_BC: + cheapMove (aop, 0, ASMOP_C, 0, true); + cheapMove (aop, 1, ASMOP_B, 0, true); + break; + case PAIR_DE: + if (!IS_GB && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == L_IDX && aop->aopu.aop_reg[1]->rIdx == H_IDX && !dont_destroy) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost++; + } + else + { + cheapMove (aop, 0, ASMOP_E, 0, true); + cheapMove (aop, 1, ASMOP_D, 0, true); + } + break; + case PAIR_HL: + if (aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == H_IDX && aop->aopu.aop_reg[1]->rIdx == L_IDX) + { + cheapMove (ASMOP_A, 0, ASMOP_L, 0, true); + cheapMove (aop, 1, ASMOP_H, 0, true); + cheapMove (aop, 0, ASMOP_A, 0, true); + } + else if (aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == H_IDX) // Do not overwrite upper byte. + { + cheapMove (aop, 1, ASMOP_H, 0, true); + cheapMove (aop, 0, ASMOP_L, 0, true); + } + else if (!IS_GB && aop->type == AOP_REG && aop->aopu.aop_reg[0]->rIdx == E_IDX && aop->aopu.aop_reg[1]->rIdx == D_IDX && !dont_destroy) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost++; + } + else + { + cheapMove (aop, 0, ASMOP_L, 0, true); + cheapMove (aop, 1, ASMOP_H, 0, true); + } + break; + case PAIR_IY: + cheapMove (aop, 0, ASMOP_IYL, 0, true); + cheapMove (aop, 1, ASMOP_IYH, 0, true); + break; + default: + wassertl (0, "Unknown pair id in commitPair()"); + fprintf (stderr, "pair %s\n", _pairs[id].name); + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genCopyStack - Copy the value - stack to stack only */ +/*-----------------------------------------------------------------*/ +static void +genCopyStack (asmop *result, int roffset, asmop *source, int soffset, int n, bool *assigned, int *size, bool a_free, bool hl_free, bool really_do_it_now) +{ + for (int i = 0; i < n;) + { + if (assigned[i]) + { + i++; + continue; + } + + if (!aopOnStack (result, roffset + i, 1) || !aopOnStack (source, soffset + i, 1)) + { + i++; + continue; + } + + if (i + 1 < n && !assigned[i + 1] && hl_free && (IS_RAB || IS_EZ80_Z80 || IS_TLCS90)) + { + if (!regalloc_dry_run) + { + emit2 ("ld hl, %s", aopGet (source, soffset + i, false)); + emit2 ("ld %s, hl", aopGet (result, roffset + i, false)); + } + cost2 (6 - 2 * IS_RAB, 0, 0, 22, 0, 21, 10); + + spillPair (PAIR_HL); + + assigned[i] = true; + assigned[i + 1] = true; + (*size) -= 2; + i += 2; + continue; + } + + if (a_free || really_do_it_now) + { + cheapMove (result, roffset + i, source, soffset + i, a_free); + assigned[i] = true; + (*size)--; + i++; + continue; + } + + i++; + } + + wassertl_bt (*size >= 0, "genCopyStack() copied more than there is to be copied."); +} + +/*-----------------------------------------------------------------*/ +/* genCopy - Copy the value from one reg/stk asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genCopy (asmop *result, int roffset, asmop *source, int soffset, int sizex, bool a_dead, bool hl_dead) +{ + int regsize, size, n = (sizex < source->size - soffset) ? sizex : (source->size - soffset); + bool assigned[8] = {false, false, false, false, false, false, false, false}; + bool a_free, hl_free; + int cached_byte = -1; + bool pushed_a = false; + + wassertl_bt (n <= 8, "Invalid size for genCopy()."); + wassertl_bt (aopRS (source), "Invalid source type."); + wassertl_bt (aopRS (result), "Invalid result type."); + + size = n; + regsize = 0; + for (int i = 0; i < n; i++) + regsize += (source->type == AOP_REG); + + // Do nothing for coalesced bytes. + for (int i = 0; i < n; i++) + if (result->type == AOP_REG && source->type == AOP_REG && result->aopu.aop_reg[roffset + i] == source->aopu.aop_reg[soffset + i]) + { + assigned[i] = true; + regsize--; + size--; + } + + // Move everything from registers to the stack. + for (int i = 0; i < n;) + { + if (i + 1 < n && result->type == AOP_STK && + (aopInReg (source, soffset + i, HL_IDX) && IS_RAB || + (aopInReg (source, soffset + i, BC_IDX) || aopInReg (source, soffset + i, DE_IDX) || aopInReg (source, soffset + i, HL_IDX) || aopInReg (source, soffset + i, IY_IDX)) && (IS_EZ80_Z80 || IS_TLCS90))) + { + if (!regalloc_dry_run) + emit2 ("ld %s, %s", aopGet (result, roffset + i, false), _pairs[getPairId_o (source, soffset + i)].name); + cost2 (3 - IS_RAB, 0, 0, 11, 0, 12, 5); + assigned[i] = true; + assigned[i + 1] = true; + regsize -= 2; + size -= 2; + i += 2; + } + else if (aopRS (source) && !aopOnStack (source, soffset + i, 1) && aopOnStack (result, roffset + i, 1)) + { + cheapMove (result, roffset + i, source, soffset + i, true); + assigned[i] = true; + regsize--; + size--; + i++; + } + else // This byte is not a register-to-stack copy. + i++; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have. + a_free = a_dead; + hl_free = hl_dead; + for (int i = 0; i < n; i++) + { + asmop *operand; + int offset; + + if (!assigned[i]) + { + operand = source; + offset = soffset + i; + } + else + { + operand = result; + offset = roffset + i; + } + + if (aopInReg (operand, offset, A_IDX)) + a_free = false; + else if (aopInReg (operand, offset, L_IDX) || aopInReg (operand, offset, H_IDX)) + hl_free = FALSE; + } + genCopyStack (result, roffset, source, soffset, n, assigned, &size, a_free, hl_free, false); + + // Now do the register shuffling. + + // Try to use: + // TLCS-90 ld rr, rr + // eZ80 lea rr, iy. + // All: push rr / pop iy + // All: push iy / pop rr + for (int i = 0; i + 1 < n; i++) + { + if (assigned[i] || assigned[i + 1]) + continue; + + for (int j = 0; j + 1 < n; j++) + { + if (!assigned[j] && i != j && i + 1 != j && (result->aopu.aop_reg[roffset + i] == source->aopu.aop_reg[soffset + j] || result->aopu.aop_reg[roffset + i + 1] == source->aopu.aop_reg[soffset + j])) + goto skip_byte_push_iy; // We can't write this one without overwriting the source. + } + + if (IS_TLCS90 && getPairId_o (result, roffset + i) != PAIR_INVALID && getPairId_o (source, soffset + i) != PAIR_INVALID) + { + emit2 ("ld %s, %s", _pairs[getPairId_o (result, roffset + i)].name, _pairs[getPairId_o (source, soffset + i)].name); + regalloc_dry_run_cost += 1 + (!aopInReg (result, roffset + i, HL_IDX) && !aopInReg (source, soffset + i, HL_IDX)); + } + else if (IS_EZ80_Z80 && getPairId_o (result, roffset + i) != PAIR_INVALID && aopInReg (source, soffset + i, IY_IDX)) + { + emit2 ("lea %s, iy, #0", _pairs[getPairId_o (result, roffset + i)].name); + regalloc_dry_run_cost += 3; + } + else if (aopInReg (result, roffset + i, IY_IDX) && getPairId_o (source, soffset + i) != PAIR_INVALID || + getPairId_o (result, roffset + i) != PAIR_INVALID && aopInReg (source, soffset + i, IY_IDX)) + { + emit2 ("push %s", _pairs[getPairId_o (source, soffset + i)].name); + emit2 ("pop %s", _pairs[getPairId_o (result, roffset + i)].name); + regalloc_dry_run_cost += 3; + } + else + continue; + + regsize -= 2; + size -= 2; + assigned[i] = true; + assigned[i + 1] = true; + +skip_byte_push_iy: + ; + } + + // Try to use ex de, hl. TODO: Also do so when only some bytes are used, while others are dead (useful e.g. for emulating ld de, hl or ld hl, de). + if (regsize >= 4) + { + int ex[4] = {-2, -2, -2, -2}; + + // Find L and check that it is exchanged with E, find H and check that it is exchanged with D. + for (int i = 0; i < n; i++) + { + if (!assigned[i] && aopInReg (result, roffset + i, L_IDX) && aopInReg (source, soffset + i, E_IDX)) + ex[0] = i; + if (!assigned[i] && aopInReg (result, roffset + i, E_IDX) && aopInReg (source, soffset + i, L_IDX)) + ex[1] = i; + if (!assigned[i] && aopInReg (result, roffset + i, H_IDX) && aopInReg (source, soffset + i, D_IDX)) + ex[2] = i; + if (!assigned[i] && aopInReg (result, roffset + i, D_IDX) && aopInReg (source, soffset + i, H_IDX)) + ex[3] = i; + } + + int exsum = (ex[0] >= 0) + (ex[1] >= 0) + (ex[2] >= 0) + (ex[3] >= 0); + + if (exsum == 4) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost += 1; // TODO: Use cost() to enable better optimization for speed. + if(ex[0] >= 0) + assigned[ex[0]] = TRUE; + if(ex[1] >= 0) + assigned[ex[1]] = TRUE; + if(ex[2] >= 0) + assigned[ex[2]] = TRUE; + if(ex[3] >= 0) + assigned[ex[3]] = TRUE; + regsize -= exsum; + size -= exsum; + } + } + + while (regsize && result->type == AOP_REG && source->type == AOP_REG) + { + int i; + + // Find lowest byte that can be assigned and needs to be assigned. + for (i = 0; i < n; i++) + { + if (assigned[i]) + continue; + + for (int j = 0; j < n; j++) + { + if (!assigned[j] && i != j && result->aopu.aop_reg[roffset + i] == source->aopu.aop_reg[soffset + j]) + goto skip_byte; // We can't write this one without overwriting the source. + } + + break; // Found byte that can be written safely. + +skip_byte: + ; + } + + if (i < n) + { + cheapMove (result, roffset + i, source, soffset + i, false); // We can safely assign a byte. + regsize--; + size--; + assigned[i] = true; + continue; + } + + // No byte can be assigned safely (i.e. the assignment is a permutation). Cache one in the accumulator. + + if (cached_byte != -1) + { + // Already one cached. Can happen when the assignment is a permutation consisting of multiple cycles. + cheapMove (result, roffset + cached_byte, ASMOP_A, 0, true); + cached_byte = -1; + continue; + } + + for (i = 0; i < n; i++) + if (!assigned[i]) + break; + + wassertl_bt (i != n, "genCopy error: Trying to cache non-existant byte in accumulator."); + if (a_free && !pushed_a) + { + _push (PAIR_AF); + pushed_a = TRUE; + } + cheapMove (ASMOP_A, 0, source, soffset + i, true); + regsize--; + size--; + assigned[i] = TRUE; + cached_byte = i; + } + + // Copy (stack-to-stack) what we can with whatever free regs we have now. + a_free = a_dead; + hl_free = hl_dead; + for (int i = 0; i < n; i++) + { + if (!assigned[i]) + continue; + if (aopInReg (result, roffset + i, A_IDX)) + a_free = false; + else if (aopInReg (result, roffset + i, L_IDX) || aopInReg (result, roffset + i, H_IDX)) + hl_free = false; + } + genCopyStack (result, roffset, source, soffset, n, assigned, &size, a_free, hl_free, false); + + // Last, move everything from stack to registers. + for (int i = 0; i < n;) + { + if (i + 1 < n && source->type == AOP_STK && + (aopInReg (result, roffset + i, HL_IDX) && IS_RAB || + (aopInReg (result, roffset + i, BC_IDX) || aopInReg (result, roffset + i, DE_IDX) || aopInReg (result, roffset + i, HL_IDX) || aopInReg (result, roffset + i, IY_IDX)) && (IS_EZ80_Z80 || IS_TLCS90))) + { + if (!regalloc_dry_run) + emit2 ("ld %s, %s", _pairs[getPairId_o (result, roffset + i)].name, aopGet (source, soffset + i, false)); + cost2 (3 - IS_RAB, 0, 0, 11, 0, 9, 5); + assigned[i] = true; + assigned[i + 1] = true; + size -= 2; + i += 2; + } + else if (aopRS (result) && aopOnStack (source, soffset + i, 1) && !aopOnStack (result, roffset + i, 1)) + { + cheapMove (result, roffset + i, source, soffset + i, true); + assigned[i] = true; + size--; + i++; + } + else // This byte is not a register-to-stack copy. + i++; + } + + // Free a reg to copy (stack-to-stack) whatever is left. + if (size) + { + a_free = a_dead && (result->regs[A_IDX] < 0 || result->regs[A_IDX] >= roffset + source->size); + hl_free = a_dead && (result->regs[L_IDX] < 0 || result->regs[L_IDX] >= roffset + source->size) && (result->regs[H_IDX] < 0 || result->regs[H_IDX] >= roffset + source->size); + if (!a_free) + _push (PAIR_AF); + genCopyStack (result, roffset, source, soffset, n, assigned, &size, true, hl_free, true); + if (!a_free) + _pop (PAIR_AF); + } + + wassertl_bt (size >= 0, "genCopy() copied more than there is to be copied."); + + a_free = a_dead && (result->regs[A_IDX] < 0 || result->regs[A_IDX] >= roffset + source->size); + + // Place leading zeroes. + + // todo + + if (cached_byte != -1) + cheapMove (result, roffset + cached_byte, ASMOP_A, 0, true); + + if (pushed_a) + _pop (PAIR_AF); +} + +/*-----------------------------------------------------------------*/ +/* genMove_o - Copy part of one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove_o (asmop *result, int roffset, asmop *source, int soffset, int size, bool a_dead_global, bool hl_dead_global) +{ + emitDebug ("; genMove_o"); + + if ((result->type == AOP_REG || !IS_GB && result->type == AOP_STK) && (source->type == AOP_REG || !IS_GB && source->type == AOP_STK)) + { + int csize = size > source->size - soffset ? source->size - soffset : size; + genCopy (result, roffset, source, soffset, csize, a_dead_global, hl_dead_global); + roffset += csize; + size -= csize; + genMove_o (result, roffset, ASMOP_ZERO, 0, size, a_dead_global && result->regs[A_IDX] < roffset, hl_dead_global && result->regs[H_IDX] < roffset && result->regs[L_IDX] < roffset); + return; + } + + bool zeroed_a = false; + long value_hl = -1; + bool a_dead = a_dead_global; + bool hl_dead = hl_dead_global; + for (unsigned int i = 0; i < size;) + { + if ((IS_EZ80_Z80 || IS_RAB || IS_TLCS90) && i + 1 < size && result->type == AOP_STK && + source->type == AOP_LIT && (value_hl >= 0 && aopIsLitVal (source, soffset + i, 2, value_hl) || hl_dead)) + { + if (value_hl < 0 || !aopIsLitVal (source, soffset + i, 2, value_hl)) + fetchLitPair (PAIR_HL, source, soffset + i); + if (!regalloc_dry_run) + emit2 ("ld %s, hl", aopGet (result, roffset + i, false)); + cost2 (3 - IS_RAB, 0, 0, 11, 0, 12, 5); + regalloc_dry_run_cost += 3; + value_hl = ullFromVal (source->aopu.aop_lit) >> ((soffset + i) * 8) & 0xffff; + i += 2; + continue; + } + + // Cache a copy of zero in a. + if (result->type != AOP_REG && aopIsLitVal (source, soffset + i, 2, 0x0000) && !zeroed_a && a_dead) + { + emit3 (A_XOR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost += 1; + zeroed_a = true; + } + + if (result->type == AOP_HL && a_dead_global && (!hl_dead_global || source->regs[L_IDX] != -1 || source->regs[H_IDX] != -1)) + { + if (!aopIsLitVal (source, soffset + i, 1, 0x00) || !zeroed_a) + { + cheapMove (ASMOP_A, 0, source, soffset + i, true); + zeroed_a = aopIsLitVal (source, soffset + i, 1, 0x00); + } + emit2 ("ld (%s), a", aopGetLitWordLong (result, roffset + i, FALSE)); + regalloc_dry_run_cost += 3; + } + else if (aopIsLitVal (source, soffset + i, 1, 0x00) && zeroed_a) + cheapMove (result, roffset + i, ASMOP_A, 0, false); + else if (aopIsLitVal (source, soffset + i, 1, 0x00) && aopInReg (result, roffset + i, A_IDX)) + { + emit3 (A_XOR, ASMOP_A, ASMOP_A); + regalloc_dry_run_cost += 1; + zeroed_a = true; + } + else + { + cheapMove (result, roffset + i, source, soffset + i, a_dead_global); + zeroed_a = false; + } + + if (aopInReg (result, roffset + i, A_IDX)) + a_dead = false; + if (aopInReg (result, roffset + i, H_IDX) || aopInReg (result, roffset + i, L_IDX)) + hl_dead = false; + + i++; + } +} + +/*-----------------------------------------------------------------*/ +/* genMove - Copy the value from one asmop to another */ +/*-----------------------------------------------------------------*/ +static void +genMove (asmop *result, asmop *source, bool a_dead, bool hl_dead) +{ + genMove_o (result, 0, source, 0, result->size, a_dead, hl_dead); +} + +/*-----------------------------------------------------------------*/ +/* getDataSize - get the operand data size */ +/*-----------------------------------------------------------------*/ +static int +getDataSize (operand * op) +{ + int size; + size = AOP_SIZE (op); + if (size == 3) + { + /* pointer */ + wassertl (0, "Somehow got a three byte data pointer"); + } + return size; +} + +/*--------------------------------------------------------------------------*/ +/* adjustStack - Adjust the stack pointer by n bytes. */ +/*--------------------------------------------------------------------------*/ +static void +adjustStack (int n, bool af_free, bool bc_free, bool hl_free, bool iy_free) +{ + _G.stack.pushed -= n; + + if (IS_TLCS90 && abs(n) > (optimize.codeSize ? 2 + (af_free || bc_free || hl_free || iy_free || n < 0) * 2: 1)) + { + emit2 ("add sp, #%d", n); + cost (3, 6); + n -= n; + } + else if (abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : (optimize.codeSize ? 8 : 5)) && hl_free) + { + spillCached (); + emit2 ("ld hl, #%d", n); + emit2 ("add hl, sp"); + emit2 ("ld sp, hl"); + cost2 (5, 27, 20, 10, 28, 18, 4); + regalloc_dry_run_cost += 5; + n -= n; + } + else if (!IS_GB && abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : 8) && iy_free) + { + spillCached (); + emit2 ("ld iy, #%d", n); + emit2 ("add iy, sp"); + emit2 ("ld sp, iy"); + regalloc_dry_run_cost += 8; + n -= n; + } + else if (abs(n) > ((IS_RAB || IS_GB) ? 127 * 4 - 1 : 8) && bc_free) + { + emit2 ("ld c, l"); + emit2 ("ld b, h"); + emit2 ("ld hl, #%d", n); + emit2 ("add hl, sp"); + emit2 ("ld sp, hl"); + emit2 ("ld l, c"); + emit2 ("ld h, b"); + regalloc_dry_run_cost += 9; + n -= n; + } + + while (abs(n)) + { + if ((IS_RAB || IS_GB) && abs(n) > (optimize.codeSize ? 2 : 1)) + { + int d; + if (n > 127) + d = 127; + else if (n < -128) + d = -128; + else + d = n; + emit2 ("add sp, #%d", d); + cost (2, IS_GB ? 16 : 4); + n -= d; + } + else if (n >= 2 && af_free && (IS_Z80 || optimize.codeSize)) + { + emit2 ("pop af"); + cost2 (1, 10, 9, 7, 12, 10, 3); + n -= 2; + } + else if (n <= -2 && (IS_Z80 || optimize.codeSize)) + { + emit2 ("push af"); + cost2 (1, 10, 11, 7, 12, 10, 3); + n += 2; + } + else if (n >= 2 && bc_free && (IS_Z80 || optimize.codeSize)) + { + emit2 ("pop bc"); + cost2 (1, 10, 9, 7, 12, 10, 3); + n -= 2; + } + else if (n >= 2 && hl_free && (IS_Z80 || optimize.codeSize)) + { + emit2 ("pop hl"); + cost2 (1, 10, 9, 7, 12, 10, 3); + n -= 2; + } + else if (IS_TLCS90 && n >= 2 && iy_free && optimize.codeSize) + { + emit2 ("pop iy"); + cost (1, 10); + n -= 2; + } + else if (n >= 1) + { + emit2 ("inc sp"); + cost2 (1, 6, 4, 2, 8, 4, 1); + n--; + } + else if (n <= -1) + { + emit2 ("dec sp"); + cost2 (1, 6, 4, 2, 8, 4, 1); + n++; + } + } + + wassert(!n); +} + +/*-----------------------------------------------------------------*/ +/* movLeft2Result - move byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +movLeft2Result (operand *left, int offl, operand *result, int offr, int sign) +{ + if (!sameRegs (AOP (left), AOP (result)) || (offl != offr)) + { + if (!sign) + cheapMove (AOP (result), offr, AOP (left), offl, true); + else + { + if (getDataSize (left) == offl + 1) + { + cheapMove (ASMOP_A, 0, AOP (left), offl, true); + cheapMove (AOP (result), offr, ASMOP_A, 0, true); + } + } + } +} + +/** Put Acc into a register set + */ +static void +outAcc (operand * result) +{ + int size = getDataSize (result); + if (size) + { + cheapMove (AOP (result), 0, ASMOP_A, 0, true); + size--; + genMove_o (result->aop, 1, ASMOP_ZERO, 0, size, true, false); + } +} + +/** Take the value in carry and put it into a register + */ +static void +outBitC (operand * result) +{ + /* if the result is bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + if (!IS_OP_RUONLY (result) && !regalloc_dry_run) + aopPut (AOP (result), "c", 0); // Todo: Cost. + } + else + { + emit2 ("ld a, !zero"); + emit2 ("rla"); + regalloc_dry_run_cost += 3; + outAcc (result); + } +} + +/*-----------------------------------------------------------------*/ +/* toBoolean - emit code for or a,operator(sizeop) */ +/*-----------------------------------------------------------------*/ +static void +_toBoolean (const operand *oper, bool needflag) +{ + int size = AOP_SIZE (oper); + sym_link *type = operandType (oper); + int skipbyte; + + if (size == 1 && needflag) + { + cheapMove (ASMOP_A, 0, oper->aop, 0, true); + emit3 (A_OR, ASMOP_A, ASMOP_A); + return; + } + + // Special handling to not overwrite a. + if (oper->aop->regs[A_IDX] >= 0) + skipbyte = oper->aop->regs[A_IDX]; + else + { + cheapMove (ASMOP_A, 0, oper->aop, size - 1, true); + skipbyte = size - 1; + } + + if (IS_FLOAT (type)) + { + if (skipbyte != size - 1) + { + wassert (regalloc_dry_run); + regalloc_dry_run_cost += 120; + } + emit2 ("res 7, a"); //clear sign bit + regalloc_dry_run_cost += 2; + skipbyte = size - 1; + } + while (size--) + if (size != skipbyte) + { + if (aopInReg (oper->aop, size, IYL_IDX) || aopInReg (oper->aop, size, IYH_IDX)) + { + regalloc_dry_run_cost += 100; + wassert (regalloc_dry_run); + } + emit3_o (A_OR, ASMOP_A, 0, oper->aop, size); + } +} + +/*-----------------------------------------------------------------*/ +/* castBoolean - emit code for casting operand to boolean in a */ +/*-----------------------------------------------------------------*/ +static void +_castBoolean (const operand *right) +{ + emitDebug ("; Casting to bool"); + + /* Can do without OR-ing for small arguments */ + if (AOP_SIZE (right) == 1 && !aopInReg (right->aop, 0, A_IDX)) + { + emit3 (A_XOR, ASMOP_A, ASMOP_A); + emit3 (A_CP, ASMOP_A, AOP (right)); + } + else + { + _toBoolean (right, FALSE); + emit2 ("add a, !immedbyte", 0xff); + emit2 ("ld a, !zero"); + regalloc_dry_run_cost += 4; + } + emit2 ("rla"); + regalloc_dry_run_cost += 1; +} + +/* Shuffle src reg array into dst reg array. */ +static void +regMove (const short *dst, const short *src, size_t n, bool preserve_a) +{ + bool assigned[9] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }; + int cached_byte = -1; + size_t size = n; + int ex[4] = {-1, -1, -1, -1}; + size_t i; + bool pushed_a = FALSE; + + wassert (n < 6); + + // Try to use ex de, hl + if (size >= 4) + { + // Find E and check that it is exchanged with L. + for (i = 0; i < n; i++) + if (dst[i] == E_IDX && src[i] == L_IDX) + ex[0] = i; + for (i = 0; i < n; i++) + if (dst[i] == L_IDX && src[i] == E_IDX) + ex[1] = i; + // Find D and check that it is exchanged with H. + for (i = 0; i < n; i++) + if (dst[i] == D_IDX && src[i] == H_IDX) + ex[2] = i; + for (i = 0; i < n; i++) + if (dst[i] == H_IDX && src[i] == D_IDX) + ex[3] = i; + if (ex[0] >= 0 && ex[1] >= 0 && ex[2] >= 0 && ex[3] >= 0) + { + emit2 ("ex de, hl"); + regalloc_dry_run_cost++; + assigned[ex[0]] = TRUE; + assigned[ex[1]] = TRUE; + assigned[ex[2]] = TRUE; + assigned[ex[3]] = TRUE; + size -= 4; + } + } + + // We need to be able to handle any assignment here, ensuring not to overwrite any parts of the source that we still need. + while (size) + { + // Find lowest byte that can be assigned and needs to be assigned. + for (i = 0; i < n; i++) + { + size_t j; + + if (assigned[i]) + continue; + + for (j = 0; j < n; j++) + { + if (!assigned[j] && i != j && dst[i] == src[j]) + goto skip_byte; // We can't write this one without overwriting the source. + } + + break; // Found byte that can be written safely. + +skip_byte: + ; + } + + if (i < n) + { + cheapMove (asmopregs[dst[i]], 0, asmopregs[src[i]], 0, false); // We can safely assign a byte. + size--; + assigned[i] = TRUE; + continue; + } + + // No byte can be assigned safely (i.e. the assignment is a permutation). Cache one in the accumulator. + + if (cached_byte != -1) + { + // Already one cached. Can happen when the assignment is a permutation consisting of multiple cycles. + cheapMove (asmopregs[dst[cached_byte]], 0, ASMOP_A, 0, true); + cached_byte = -1; + continue; + } + + for (i = 0; i < n; i++) + if (!assigned[i]) + break; + + wassertl (i != n, "regMove error: Trying to cache non-existant byte in accumulator."); + if (preserve_a && !pushed_a) + { + _push (PAIR_AF); + pushed_a = TRUE; + } + cheapMove (ASMOP_A, 0, asmopregs[src[i]], 0, true); + size--; + assigned[i] = TRUE; + cached_byte = i; + } + + if (cached_byte != -1) + cheapMove (asmopregs[dst[cached_byte]], 0, ASMOP_A, 0, true); + + if (pushed_a) + _pop (PAIR_AF); +} + +/*-----------------------------------------------------------------*/ +/* genNot - generate code for ! operation */ +/*-----------------------------------------------------------------*/ +static void +genNot (const iCode * ic) +{ + operand *left = IC_LEFT (ic); + operand *result = IC_RESULT (ic); + + /* assign asmOps to operand & result */ + aopOp (left, ic, FALSE, TRUE); + aopOp (result, ic, TRUE, FALSE); + + /* if in bit space then a special case */ + if (AOP_TYPE (left) == AOP_CRY) + { + wassertl (0, "Tried to negate a bit"); + } + else if (IS_BOOL (operandType (left))) + { + cheapMove (ASMOP_A, 0, AOP (left), 0, true); + emit2 ("xor a, !immedbyte", 0x01); + regalloc_dry_run_cost += 2; + cheapMove (AOP (result), 0, ASMOP_A, 0, true); + goto release; + } + + _toBoolean (left, FALSE); + + /* Not of A: + If A == 0, !A = 1 + else A = 0 + So if A = 0, A-1 = 0xFF and C is set, rotate C into reg. */ + emit2 ("sub a,!one"); + regalloc_dry_run_cost += 2; + outBitC (result); + +release: + /* release the aops */ + freeAsmop (left, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genCpl - generate code for complement */ +/*-----------------------------------------------------------------*/ +static void +genCpl (const iCode *ic) +{ + int skip_byte = -1; + + bool a_dead = !bitVectBitValue (ic->rSurv, A_IDX); + bool pushed_a = false; + + /* assign asmOps to operand & result */ + aopOp (IC_LEFT (ic), ic, false, false); + aopOp (IC_RESULT (ic), ic, true, false); + + /* if both are in bit space then + a special case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + wassertl (0, "Left and the result are in bit space"); + + int size = IC_RESULT (ic)->aop->size; + + if (IC_LEFT (ic)->aop->regs[A_IDX] >= 0 && IC_LEFT (ic)->aop->regs[A_IDX] < size) + { + int i = IC_LEFT (ic)->aop->regs[A_IDX]; + emit3 (A_CPL, 0, 0); + cheapMove (IC_RESULT (ic)->aop, i, ASMOP_A, 0, true); + skip_byte = i; + + if (aopInReg (IC_RESULT (ic)->aop, i, A_IDX)) + a_dead = false; + + // Do not overwrite still-needed value + if (IC_RESULT (ic)->aop->type == AOP_REG && !aopInReg (IC_RESULT (ic)->aop, i, A_IDX)) + { + int j = IC_LEFT (ic)->aop->regs[IC_RESULT (ic)->aop->aopu.aop_reg[i]->rIdx]; + if (j >= 0 && j != skip_byte && j < size) + { + regalloc_dry_run_cost += 150; + wassert (regalloc_dry_run); + } + } + } + + for (int i = 0; i < size; i++) + { + if (i == skip_byte) + continue; + + if (!a_dead && !pushed_a) + { + _push (PAIR_AF); + pushed_a = true; + } + + cheapMove (ASMOP_A, 0, IC_LEFT (ic)->aop, i, true); + emit3 (A_CPL, 0, 0); + cheapMove (IC_RESULT (ic)->aop, i, ASMOP_A, 0, true); + + if (aopInReg (IC_RESULT (ic)->aop, i, A_IDX)) + a_dead = false; + + // Do not overwrite still-needed value + if (IC_RESULT (ic)->aop->type == AOP_REG && !aopInReg (IC_RESULT (ic)->aop, i, A_IDX)) + { + int j = IC_LEFT (ic)->aop->regs[IC_RESULT (ic)->aop->aopu.aop_reg[i]->rIdx]; + if (j > i && j < size && j != skip_byte) + { + regalloc_dry_run_cost += 150; + wassert (regalloc_dry_run); + } + } + } + + if (pushed_a) + _pop (PAIR_AF); + + /* release the aops */ + freeAsmop (IC_LEFT (ic), 0); + freeAsmop (IC_RESULT (ic), 0); +} + +static void +_gbz80_emitAddSubLongLong (const iCode * ic, asmop * left, asmop * right, bool isAdd) +{ + enum asminst first = isAdd ? A_ADD : A_SUB; + enum asminst later = isAdd ? A_ADC : A_SBC; + + + /* Logic: + ld de,right.lw + setup hl to left + de = hl - de + push flags + store de into result + pop flags + ld de,right.hw + setup hl + de = hl -de + store de into result + */ + + wassertl (IS_GB, "Code is only relevant to the gbz80"); + wassertl (AOP (IC_RESULT (ic))->size == 4, "Only works for four bytes"); + + fetchPair (PAIR_DE, left); + + emit2 ("ld a, e"); + regalloc_dry_run_cost += 1; + emit3_o (first, ASMOP_A, 0, right, LSB); + emit2 ("ld e, a"); + emit2 ("ld a, d"); + regalloc_dry_run_cost += 2; + emit3_o (later, ASMOP_A, 0, right, MSB16); + + _push (PAIR_AF); + + cheapMove (AOP (IC_RESULT (ic)), MSB16, ASMOP_A, 0, true); + cheapMove (AOP (IC_RESULT (ic)), LSB, ASMOP_E, 0, true); + + fetchPairLong (PAIR_DE, left, NULL, MSB24); + + if (!regalloc_dry_run) + aopGet (right, MSB24, FALSE); + + _pop (PAIR_AF); + emit2 ("ld a, e"); + emit3_o (later, ASMOP_A, 0, right, MSB24); + emit2 ("ld e, a"); + emit2 ("ld a, d"); + regalloc_dry_run_cost += 2; + emit3_o (later, ASMOP_A, 0, right, MSB32); + + cheapMove (AOP (IC_RESULT (ic)), MSB32, ASMOP_A, 0, true); + cheapMove (AOP (IC_RESULT (ic)), MSB24, ASMOP_E, 0, true); +} + +static void +_gbz80_emitAddSubLong (const iCode * ic, bool isAdd) +{ + _gbz80_emitAddSubLongLong (ic, AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic)), isAdd); +} + +/*-----------------------------------------------------------------*/ +/* assignResultValue - */ +/*-----------------------------------------------------------------*/ +static void +assignResultValue (operand * oper) +{ + int size = oper->aop->size; + + wassertl (size <= 4, "Got a result that is bigger than four bytes"); + + if (IS_GB && size == 4 && requiresHL (AOP (oper))) + { + /* We do it the hard way here. */ + _push (PAIR_HL); + cheapMove (oper->aop, 0, ASMOP_RETURN, 0, true); + cheapMove (oper->aop, 1, ASMOP_RETURN, 1, true); + _pop (PAIR_DE); + cheapMove (oper->aop, 2, ASMOP_E, 0, true); + cheapMove (oper->aop, 3, ASMOP_D, 0, true); + } + else + genMove (oper->aop, ASMOP_RETURN, true, true); +} + +/* Pop saved regs from stack, taking care not to destroy result */ +static void +restoreRegs (bool iy, bool de, bool bc, bool hl, const operand *result) +{ + bool bInRet, cInRet, dInRet, eInRet, hInRet, lInRet; + bool SomethingReturned; + + SomethingReturned = (result && IS_ITEMP (result) && + (OP_SYMBOL_CONST (result)->nRegs || + OP_SYMBOL_CONST (result)->spildir || + OP_SYMBOL_CONST (result)->accuse == ACCUSE_A)) || IS_TRUE_SYMOP (result); + + if (SomethingReturned) + { + bitVect *rv = z80_rUmaskForOp (result); + bInRet = bitVectBitValue (rv, B_IDX); + cInRet = bitVectBitValue (rv, C_IDX); + dInRet = bitVectBitValue (rv, D_IDX); + eInRet = bitVectBitValue (rv, E_IDX); + hInRet = bitVectBitValue (rv, H_IDX); + lInRet = bitVectBitValue (rv, L_IDX); + freeBitVect (rv); + } + else + { + bInRet = FALSE; + cInRet = FALSE; + dInRet = FALSE; + eInRet = FALSE; + hInRet = FALSE; + lInRet = FALSE; + } + + if (iy) + _pop (PAIR_IY); + + if (de) + { + if (dInRet && eInRet) + wassertl (0, "Shouldn't push DE if it's wiped out by the return"); + else if (dInRet) + { + /* Only restore E */ + emit2 ("ld a, d"); + regalloc_dry_run_cost += 1; + _pop (PAIR_DE); + emit2 ("ld d, a"); + regalloc_dry_run_cost += 1; + } + else if (eInRet) + { + /* Only restore D */ + _pop (PAIR_AF); + emit2 ("ld d, a"); + regalloc_dry_run_cost += 1; + } + else + _pop (PAIR_DE); + } + + if (bc) + { + if (bInRet && cInRet) + wassertl (0, "Shouldn't push BC if it's wiped out by the return"); + else if (bInRet) + { + /* Only restore C */ + emit2 ("ld a, b"); + regalloc_dry_run_cost += 1; + _pop (PAIR_BC); + emit2 ("ld b, a"); + regalloc_dry_run_cost += 1; + } + else if (cInRet) + { + /* Only restore B */ + _pop (PAIR_AF); + emit2 ("ld b, a"); + regalloc_dry_run_cost += 1; + } + else + _pop (PAIR_BC); + } + + if (hl) + { + if (hInRet && lInRet) + wassertl (0, "Shouldn't push HL if it's wiped out by the return"); + else if (hInRet) + { + /* Only restore E */ + emit2 ("ld a, h"); + regalloc_dry_run_cost += 1; + _pop (PAIR_HL); + emit2 ("ld h, a"); + regalloc_dry_run_cost += 1; + } + else if (lInRet) + { + /* Only restore D */ + _pop (PAIR_AF); + emit2 ("ld h, a"); + regalloc_dry_run_cost += 1; + } + else + _pop (PAIR_HL); + } +} + +static void +_saveRegsForCall (const iCode * ic, bool dontsaveIY) +{ + /* Rules: + o Stack parameters are pushed before this function enters + o DE and BC may be used in this function. + o HL and DE may be used to return the result. + o HL and DE may be used to send variables. + o DE and BC may be used to store the result value. + o HL may be used in computing the sent value of DE + o The iPushes for other parameters occur before any addSets + + Logic: (to be run inside the first iPush or if none, before sending) + o Compute if DE, BC, HL, IY are in use over the call + o Compute if DE is used in the send set + o Compute if DE and/or BC are used to hold the result value + o If (DE is used, or in the send set) and is not used in the result, push. + o If BC is used and is not in the result, push + o + o If DE is used in the send set, fetch + o If HL is used in the send set, fetch + o Call + o ... + */ + + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + + if (_G.saves.saved == FALSE) + { + bool push_bc, push_de, push_hl, push_iy; + + if (options.oldralloc) + { + bool deInUse, bcInUse; + bool bcInRet = FALSE, deInRet = FALSE; + bitVect *rInUse; + + rInUse = bitVectCplAnd (bitVectCopy (ic->rMask), z80_rUmaskForOp (IC_RESULT (ic))); + + deInUse = bitVectBitValue (rInUse, D_IDX) || bitVectBitValue (rInUse, E_IDX); + bcInUse = bitVectBitValue (rInUse, B_IDX) || bitVectBitValue (rInUse, C_IDX); + + emitDebug ("; _saveRegsForCall: deInUse: %u bcInUse: %u", deInUse, bcInUse); + + push_bc = bcInUse && !bcInRet; + push_de = deInUse && !deInRet; + push_hl = FALSE; + push_iy = FALSE; + } + else + { + push_bc = bitVectBitValue (ic->rSurv, B_IDX) && !ftype->funcAttrs.preserved_regs[B_IDX] || bitVectBitValue (ic->rSurv, C_IDX) && !ftype->funcAttrs.preserved_regs[C_IDX]; + push_de = bitVectBitValue (ic->rSurv, D_IDX) && !ftype->funcAttrs.preserved_regs[D_IDX] || bitVectBitValue (ic->rSurv, E_IDX) && !ftype->funcAttrs.preserved_regs[E_IDX]; + push_hl = bitVectBitValue (ic->rSurv, H_IDX) || bitVectBitValue (ic->rSurv, L_IDX); + push_iy = !dontsaveIY && (bitVectBitValue (ic->rSurv, IYH_IDX) || bitVectBitValue (ic->rSurv, IYL_IDX)); + } + + if (push_hl) + { + _push (PAIR_HL); + _G.stack.pushedHL = TRUE; + } + if (push_bc) + { + _push (PAIR_BC); + _G.stack.pushedBC = TRUE; + } + if (push_de) + { + _push (PAIR_DE); + _G.stack.pushedDE = TRUE; + } + if (push_iy) + { + _push (PAIR_IY); + _G.stack.pushedIY = TRUE; + } + + if (!regalloc_dry_run) + _G.saves.saved = TRUE; + } + else + { + /* Already saved. */ + } +} + +/*-----------------------------------------------------------------*/ +/* genIpush - genrate code for pushing this gets a little complex */ +/*-----------------------------------------------------------------*/ +static void +genIpush (const iCode *ic) +{ + int size, offset = 0; + + /* if this is not a parm push : ie. it is spill push + and spill push is always done on the local stack */ + if (!ic->parmPush) + { + wassertl (0, "Encountered an unsupported spill push."); + return; + } + + /* Scan ahead until we find the function that we are pushing parameters to. + Count the number of addSets on the way to figure out what registers + are used in the send set. + */ + int nAddSets = 0; + iCode *walk = ic->next; + + while (walk) + { + if (walk->op == SEND && !_G.saves.saved && !regalloc_dry_run) + nAddSets++; + else if (walk->op == CALL || walk->op == PCALL) + break; // Found it. + + walk = walk->next; // Keep looking. + } + if (!regalloc_dry_run && !_G.saves.saved && !regalloc_dry_run) /* Cost is counted at CALL or PCALL instead */ + _saveRegsForCall (walk, false); /* Caller saves, and this is the first iPush. */ + + const bool smallc = IFFUNC_ISSMALLC (operandType (IC_LEFT (walk))); + + /* then do the push */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + + size = AOP_SIZE (IC_LEFT (ic)); + + if (isPair (AOP (IC_LEFT (ic))) && size == 2) + { + if (!regalloc_dry_run) + { + _G.stack.pushed += 2; + emit2 ("push %s", getPairName (AOP (IC_LEFT (ic)))); + } + regalloc_dry_run_cost += (getPairId (AOP (IC_LEFT (ic))) == PAIR_IY ? 2 : 1); + } + else + { + if (size == 1 && smallc) /* The SmallC calling convention pushes 8-bit parameters as 16-bit values. */ + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[2]->rIdx == C_IDX) + { + emit2 ("push bc"); + regalloc_dry_run_cost += 1; + } + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[2]->rIdx == E_IDX) + { + emit2 ("push de"); + regalloc_dry_run_cost += 1; + } + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[2]->rIdx == L_IDX) + { + emit2 ("push hl"); + regalloc_dry_run_cost += 1; + } + else + { + emit2 ("dec sp"); + cheapMove (ASMOP_A, 0, AOP (IC_LEFT (ic)), 0, true); + emit2 ("push af"); + emit2 ("inc sp"); + regalloc_dry_run_cost += 3; + } + if (!regalloc_dry_run) + _G.stack.pushed += 2; + goto release; + } + else if (size == 2) + { + PAIR_ID pair = getDeadPairId (ic); + if (pair == PAIR_INVALID || isPairDead (PAIR_HL, ic)) + pair = PAIR_HL; /* hl sometimes is cheaper to load than other pairs. */ + + fetchPairLong (pair, AOP (IC_LEFT (ic)), ic, 0); + if (!regalloc_dry_run) + { + emit2 ("push %s", _pairs[pair].name); + _G.stack.pushed += 2; + } + regalloc_dry_run_cost += 1; + goto release; + } + if (size == 4) + { + if (getPairId_o (IC_LEFT (ic)->aop, 2) != PAIR_INVALID) + { + emit2 ("push %s", _pairs[getPairId_o (IC_LEFT (ic)->aop, 2)].name); + regalloc_dry_run_cost += 1 + (getPairId_o (IC_LEFT (ic)->aop, 2) == PAIR_IY); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, 2); + emit2 ("push hl"); + regalloc_dry_run_cost += 1; + } + if (!regalloc_dry_run) + _G.stack.pushed += 2; + + if (getPairId_o (IC_LEFT (ic)->aop, 0) != PAIR_INVALID) + { + emit2 ("push %s", _pairs[getPairId_o (IC_LEFT (ic)->aop, 0)].name); + regalloc_dry_run_cost += 1 + (getPairId_o (IC_LEFT (ic)->aop, 2) == PAIR_IY); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, 0); + emit2 ("push hl"); + regalloc_dry_run_cost += 1; + } + if (!regalloc_dry_run) + _G.stack.pushed += 2; + + goto release; + } + offset = size; + while (size--) + { + if (size && getFreePairId (ic) != PAIR_INVALID) + { + offset -= 2; + + PAIR_ID pair = getFreePairId (ic); + fetchPairLong (pair, AOP (IC_LEFT (ic)), 0, offset); + emit2 ("push %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + size--; + + if (!regalloc_dry_run) + _G.stack.pushed += 2; + + continue; + } + else if (AOP (IC_LEFT (ic))->type == AOP_IY) + { + wassertl (!bitVectBitValue (ic->rSurv, A_IDX), "Loading from address destroys A, which must survive."); + emit2 ("ld a, (%s)", aopGetLitWordLong (AOP (IC_LEFT (ic)), --offset, FALSE)); + emit2 ("push af"); + regalloc_dry_run_cost += 4; + } + else + { + offset--; + if (AOP (IC_LEFT (ic))->type == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[offset]->rIdx == B_IDX) + { + emit2 ("push bc"); + regalloc_dry_run_cost += 1; + } + else if (AOP (IC_LEFT (ic))->type == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[offset]->rIdx == D_IDX) + { + emit2 ("push de"); + regalloc_dry_run_cost += 1; + } + else if (AOP (IC_LEFT (ic))->type == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[offset]->rIdx == H_IDX) + { + emit2 ("push hl"); + regalloc_dry_run_cost += 1; + } + else + { + if (AOP_TYPE (IC_LEFT (ic)) != AOP_SFR && !regalloc_dry_run && !strcmp (aopGet (AOP (IC_LEFT (ic)), offset, FALSE), "h")) // todo: More exact cost! + emit2 ("push hl"); + else + { + wassertl (aopInReg (IC_LEFT (ic)->aop, 0, A_IDX) || !bitVectBitValue (ic->rSurv, A_IDX), "Push operand destroys A, which must survive."); + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT && byteOfVal (AOP (IC_LEFT (ic))->aopu.aop_lit, offset) == 0x00) + emit3 (A_XOR, ASMOP_A, ASMOP_A); + else + { + cheapMove (ASMOP_A, 0, AOP (IC_LEFT (ic)), offset, true); + if (!aopInReg (IC_LEFT (ic)->aop, 0, A_IDX)) + regalloc_dry_run_cost += ld_cost (ASMOP_A, AOP (IC_LEFT (ic))); + } + emit2 ("push af"); + regalloc_dry_run_cost += 1; + } + } + } + if (!regalloc_dry_run) + { + emit2 ("inc sp"); + _G.stack.pushed++; + } + regalloc_dry_run_cost += 1; + } + } +release: + freeAsmop (IC_LEFT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genIpop - recover the registers: can happen only for spilling */ +/*-----------------------------------------------------------------*/ +static void +genIpop (const iCode * ic) +{ + int size, offset; + + wassert (!regalloc_dry_run); + + /* if the temp was not pushed then */ + if (OP_SYMBOL (IC_LEFT (ic))->isspilt) + return; + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + offset = (size - 1); + if (isPair (AOP (IC_LEFT (ic)))) + { + emit2 ("pop %s", getPairName (AOP (IC_LEFT (ic)))); + } + else + { + while (size--) + { + emit2 ("dec sp"); + emit2 ("pop hl"); + spillPair (PAIR_HL); + aopPut (AOP (IC_LEFT (ic)), "l", offset--); + } + } + + freeAsmop (IC_LEFT (ic), NULL); +} + +/* This is quite unfortunate */ +static void +setArea (int inHome) +{ + /* + static int lastArea = 0; + + if (_G.in_home != inHome) { + if (inHome) { + const char *sz = port->mem.code_name; + port->mem.code_name = "HOME"; + emit2("!area", CODE_NAME); + port->mem.code_name = sz; + } + else + emit2("!area", CODE_NAME); */ + _G.in_home = inHome; + // } +} + +static bool +isInHome (void) +{ + return _G.in_home; +} + +/** Emit the code for a register parameter + */ +static void genSend (const iCode *ic) +{ + int size; + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + wassertl (ic->next->op == CALL || ic->next->op == PCALL, "Sending register parameter for missing call"); + wassertl (!IS_GB, "Register parameters are not supported in gbz80 port"); + + if (_G.saves.saved == FALSE && !regalloc_dry_run /* Cost is counted at CALL or PCALL instead */ ) + { + /* Caller saves, and this is the first iPush. */ + /* Scan ahead until we find the function that we are pushing parameters to. + Count the number of addSets on the way to figure out what registers + are used in the send set. + */ + int nAddSets = 0; + iCode *walk = ic->next; + + while (walk) + { + if (walk->op == SEND) + { + nAddSets++; + } + else if (walk->op == CALL || walk->op == PCALL) + { + /* Found it. */ + break; + } + else + { + /* Keep looking. */ + } + walk = walk->next; + } + _saveRegsForCall (walk, FALSE); + } + if (size == 2) + { + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 0); + z80_regs_used_as_parms_in_calls_from_current_function[L_IDX] = true; + z80_regs_used_as_parms_in_calls_from_current_function[H_IDX] = true; + } + else if (size <= 4) + { + if (size == 4 && ASMOP_RETURN->aopu.aop_reg[0]->rIdx == L_IDX && ASMOP_RETURN->aopu.aop_reg[1]->rIdx == H_IDX && + ASMOP_RETURN->aopu.aop_reg[2]->rIdx == E_IDX && ASMOP_RETURN->aopu.aop_reg[3]->rIdx == D_IDX) + { + if (!isPairDead (PAIR_DE, ic) && getPairId_o (AOP (IC_LEFT (ic)), 2) != PAIR_DE) + { + regalloc_dry_run_cost += 100; + wassertl (regalloc_dry_run, "Register parameter overwrites value that is still needed"); + } + fetchPairLong (PAIR_DE, AOP (IC_LEFT (ic)), ic, 2); + z80_regs_used_as_parms_in_calls_from_current_function[E_IDX] = true; + z80_regs_used_as_parms_in_calls_from_current_function[D_IDX] = true; + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 0); + z80_regs_used_as_parms_in_calls_from_current_function[L_IDX] = true; + z80_regs_used_as_parms_in_calls_from_current_function[H_IDX] = true; + } + else + { + for (int i = 0; i < AOP_SIZE (IC_LEFT (ic)); i++) + if (!regalloc_dry_run) + z80_regs_used_as_parms_in_calls_from_current_function[ASMOP_RETURN->aopu.aop_reg[i]->rIdx] = true; + + genMove_o (ASMOP_RETURN, 0, IC_LEFT (ic)->aop, 0, IC_LEFT (ic)->aop->size, true, true); + } + } + + freeAsmop (IC_LEFT (ic), NULL); +} + +/** Emit the code for a call statement + */ +static void +emitCall (const iCode *ic, bool ispcall) +{ + bool SomethingReturned, bigreturn, z88dk_callee; + sym_link *dtype = operandType (IC_LEFT (ic)); + sym_link *etype = getSpec (dtype); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + int i; + + z88dk_callee = IFFUNC_ISZ88DK_CALLEE (ftype); + + for (i = 0; i < IYH_IDX + 1; i++) + z80_regs_preserved_in_calls_from_current_function[i] |= ftype->funcAttrs.preserved_regs[i]; + + _saveRegsForCall (ic, FALSE); + + /* Return value of big type or returning struct or union. */ + bigreturn = (getSize (ftype->next) > 4); + if (bigreturn) + { + PAIR_ID pair; + int fp_offset, sp_offset; + + if (ispcall && IS_GB) + _push (PAIR_HL); + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + wassert (AOP_TYPE (IC_RESULT (ic)) == AOP_STK || AOP_TYPE (IC_RESULT (ic)) == AOP_EXSTK); + fp_offset = + AOP (IC_RESULT (ic))->aopu.aop_stk + (AOP (IC_RESULT (ic))->aopu.aop_stk > + 0 ? _G.stack.param_offset : 0); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + pair = (ispcall && !IS_GB && !IY_RESERVED) ? PAIR_IY : PAIR_HL; + emit2 ("ld %s, !immedword", _pairs[pair].name, sp_offset); + emit2 ("add %s, sp", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IY ? 6 : 4); + if (ispcall && IS_GB) + { + emit2 ("ld e, l"); + emit2 ("ld d, h"); + regalloc_dry_run_cost += 2; + _pop (PAIR_HL); + pair = PAIR_DE; + } + emit2 ("push %s", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IY ? 2 : 1); + if (!regalloc_dry_run) + _G.stack.pushed += 2; + freeAsmop (IC_RESULT (ic), NULL); + } + + if (ispcall) + { + if (IFFUNC_ISBANKEDCALL (dtype)) + { + werror (W_INDIR_BANKED); + } + else if (IFFUNC_ISZ88DK_SHORTCALL (ftype)) + { + wassertl(0, "__z88dk_short_call via function pointer not implemented"); + } + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + + if (isLitWord (AOP (IC_LEFT (ic)))) + { + emit2 ("call %s", aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE)); + regalloc_dry_run_cost += 3; + } + else if (getPairId (AOP (IC_LEFT (ic))) != PAIR_IY && !IFFUNC_ISZ88DK_FASTCALL (ftype)) + { + spillPair (PAIR_HL); + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), ic, 0); + emit2 ("call ___sdcc_call_hl"); + } + else if (!IS_GB && !IY_RESERVED) + { + spillPair (PAIR_IY); + fetchPairLong (PAIR_IY, IC_LEFT (ic)->aop, ic, 0); + emit2 ("call ___sdcc_call_iy"); + } + else // Use bc, since it is the only 16-bit register guarateed to be free even for __z88dk_fastcall with --reserve-regs-iy + { + wassert (IY_RESERVED); // The peephole optimizer handles ret for purposes other than returning only for --reserve-regs-iy + if (aopInReg (IC_LEFT (ic)->aop, 0, B_IDX) || aopInReg (IC_LEFT (ic)->aop, 0, C_IDX) || aopInReg (IC_LEFT (ic)->aop, 1, B_IDX) || aopInReg (IC_LEFT (ic)->aop, 1, C_IDX)) + { + regalloc_dry_run_cost += 100; + wassertl (regalloc_dry_run, "Unimplemented function pointer in bc"); + } + symbol *tlbl = 0; + if (!regalloc_dry_run) + { + tlbl = newiTempLabel (NULL); + emit2 ("ld bc, #!tlabel", labelKey2num (tlbl->key)); + emit2 ("push bc"); + regalloc_dry_run_cost += 4; + } + fetchPairLong (PAIR_BC, IC_LEFT (ic)->aop, 0, 0); + emit2 ("push bc"); + emit2 ("ret"); + regalloc_dry_run_cost += 2; + if (tlbl) + emit2 ("!tlabeldef", labelKey2num (tlbl->key)); + } + + freeAsmop (IC_LEFT (ic), NULL); + } + else + { + /* make the call */ + if (IFFUNC_ISBANKEDCALL (dtype)) + { + char *name = OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name; + emit2 ("call banked_call"); + emit2 ("!dws", name); + emit2 ("!dw !bankimmeds", name); + regalloc_dry_run_cost += 6; + } + else + { + if (IS_LITERAL (etype)) + { + emit2 ("call 0x%04X", ulFromVal (OP_VALUE (IC_LEFT (ic)))); + regalloc_dry_run_cost += 3; + } + else if ( IFFUNC_ISZ88DK_SHORTCALL(ftype) ) + { + int rst = ftype->funcAttrs.z88dk_shortcall_rst; + int value = ftype->funcAttrs.z88dk_shortcall_val; + emit2 ("rst 0x%02x", rst); + if ( value < 256 ) + emit2 ("defb 0x%02x\n",value); + else + emit2 ("defw 0x%04x\n",value); + regalloc_dry_run_cost += 3; + } + else + { + bool jump = (!ic->parmBytes && IFFUNC_ISNORETURN (ftype)); + emit2 ("%s %s", jump ? "jp" : "call", + (OP_SYMBOL (IC_LEFT (ic))->rname[0] ? OP_SYMBOL (IC_LEFT (ic))->rname : OP_SYMBOL (IC_LEFT (ic))->name)); + regalloc_dry_run_cost += 3; + } + } + } + spillCached (); + + /* Mark the registers as restored. */ + _G.saves.saved = FALSE; + + SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->spildir || + OP_SYMBOL (IC_RESULT (ic))->accuse == ACCUSE_A)) || IS_TRUE_SYMOP (IC_RESULT (ic)); + + /* adjust the stack for parameters if required */ + if ((ic->parmBytes || bigreturn) && (IFFUNC_ISNORETURN (ftype) || z88dk_callee)) + { + if (!regalloc_dry_run) + { + _G.stack.pushed -= (ic->parmBytes + bigreturn * 2); + z80_symmParm_in_calls_from_current_function = FALSE; + } + } + else if ((ic->parmBytes || bigreturn)) + { + adjustStack (ic->parmBytes + bigreturn * 2, !IS_TLCS90, TRUE, !SomethingReturned || bigreturn, !IY_RESERVED); + + if (regalloc_dry_run) + _G.stack.pushed += ic->parmBytes + bigreturn * 2; + } + + /* if we need assign a result value */ + if (SomethingReturned && !bigreturn) + { + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + assignResultValue (IC_RESULT (ic)); + + freeAsmop (IC_RESULT (ic), NULL); + } + + spillCached (); + + restoreRegs (_G.stack.pushedIY, _G.stack.pushedDE, _G.stack.pushedBC, _G.stack.pushedHL, IC_RESULT (ic)); + _G.stack.pushedIY = FALSE; + _G.stack.pushedDE = FALSE; + _G.stack.pushedBC = FALSE; + _G.stack.pushedHL = FALSE; +} + +/*-----------------------------------------------------------------*/ +/* genCall - generates a call statement */ +/*-----------------------------------------------------------------*/ +static void +genCall (const iCode * ic) +{ + emitCall (ic, FALSE); +} + +/*-----------------------------------------------------------------*/ +/* genPcall - generates a call by pointer statement */ +/*-----------------------------------------------------------------*/ +static void +genPcall (const iCode * ic) +{ + emitCall (ic, TRUE); +} + +/*-----------------------------------------------------------------*/ +/* resultRemat - result is rematerializable */ +/*-----------------------------------------------------------------*/ +static int +resultRemat (const iCode * ic) +{ + if (SKIP_IC (ic) || ic->op == IFX) + return 0; + + if (IC_RESULT (ic) && IS_ITEMP (IC_RESULT (ic))) + { + const symbol *sym = OP_SYMBOL_CONST (IC_RESULT (ic)); + if (sym->remat && !POINTER_SET (ic) && sym->isspilt) + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* genFunction - generated code for function entry */ +/*-----------------------------------------------------------------*/ +static void +genFunction (const iCode * ic) +{ + bool stackParm; + + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + sym_link *ftype; + + bool bcInUse = FALSE; + bool deInUse = FALSE; + bool bigreturn; + + setArea (IFFUNC_NONBANKED (sym->type)); + wassert (!_G.stack.pushed); + + /* PENDING: Reset the receive offset as it + doesn't seem to get reset anywhere else. + */ + _G.receiveOffset = 0; + _G.stack.param_offset = sym->type->funcAttrs.z88dk_params_offset; + + /* Record the last function name for debugging. */ + _G.lastFunctionName = sym->rname; + + /* Create the function header */ + emit2 ("!functionheader", sym->name); + + emitDebug (z80_assignment_optimal ? "; Register assignment is optimal." : "; Register assignment might be sub-optimal."); + emitDebug ("; Stack space usage: %d bytes.", sym->stack); + + if (IS_STATIC (sym->etype)) + emit2 ("!functionlabeldef", sym->rname); + else + emit2 ("!globalfunctionlabeldef", sym->rname); + + if (!regalloc_dry_run) + genLine.lineCurr->isLabel = 1; + + ftype = operandType (IC_LEFT (ic)); + + if (IFFUNC_ISNAKED (ftype)) + { + emitDebug ("; naked function: no prologue."); + return; + } + + /* if this is an interrupt service routine + then save all potentially used registers. */ + if (IFFUNC_ISISR (sym->type)) + { + if (!IFFUNC_ISCRITICAL (sym->type)) + { + emit2 ("!ei"); + } + + emit2 ("!pusha"); + } + else + { + /* This is a non-ISR function. + If critical function then turn interrupts off */ + if (IFFUNC_ISCRITICAL (sym->type)) + { + if (IS_GB || IS_RAB || IS_TLCS90) + { + emit2 ("!di"); + } + else + { + //get interrupt enable flag IFF2 into P/O + emit2 ("ld a,i"); + emit2 ("!di"); + //save P/O flag + emit2 ("push af"); + _G.stack.param_offset += 2; + } + } + } + + if (options.profile) + { + emit2 ("!profileenter"); + } + + if (z80_opts.calleeSavesBC) + { + bcInUse = TRUE; + } + + /* Detect which registers are used. */ + if (IFFUNC_CALLEESAVES (sym->type) && sym->regsUsed) + { + int i; + for (i = 0; i < sym->regsUsed->size; i++) + { + if (bitVectBitValue (sym->regsUsed, i)) + { + switch (i) + { + case C_IDX: + case B_IDX: + bcInUse = TRUE; + break; + case D_IDX: + case E_IDX: + if (!IS_GB) + { + deInUse = TRUE; + } + else + { + /* Other systems use DE as a temporary. */ + } + break; + } + } + } + } + + if (bcInUse) + { + emit2 ("push bc"); + _G.stack.param_offset += 2; + } + + _G.calleeSaves.pushedBC = bcInUse; + + if (deInUse) + { + emit2 ("push de"); + _G.stack.param_offset += 2; + } + + _G.calleeSaves.pushedDE = deInUse; + + /* adjust the stack for the function */ +// _G.stack.last = sym->stack; + + bigreturn = (getSize (ftype->next) > 4); + _G.stack.param_offset += bigreturn * 2; + + stackParm = FALSE; + for (sym = setFirstItem (istack->syms); sym; sym = setNextItem (istack->syms)) + { + if (sym->_isparm && !IS_REGPARM (sym->etype)) + { + stackParm = TRUE; + break; + } + } + sym = OP_SYMBOL (IC_LEFT (ic)); + + _G.omitFramePtr = options.oldralloc ? (!IS_GB && options.omitFramePtr) : should_omit_frame_ptr; + + if (!IS_GB && !z80_opts.noOmitFramePtr && !stackParm && !sym->stack) + { + if (!regalloc_dry_run) + _G.omitFramePtr = TRUE; + } + else if (sym->stack) + { + if (!_G.omitFramePtr) + emit2 ((optimize.codeSize && !IFFUNC_ISZ88DK_FASTCALL (ftype)) ? "!enters" : "!enter"); + if (IS_EZ80_Z80 && !_G.omitFramePtr && -sym->stack > -128 && -sym->stack <= -3 && !IFFUNC_ISZ88DK_FASTCALL (ftype)) + { + emit2 ("lea hl, ix, #%d", -sym->stack); + emit2 ("ld sp, hl"); + regalloc_dry_run_cost += 4; + } + else + adjustStack (-sym->stack, !IS_TLCS90, TRUE, !IFFUNC_ISZ88DK_FASTCALL (ftype), !IY_RESERVED); + _G.stack.pushed = 0; + } + else if (!_G.omitFramePtr) + { + emit2 ((optimize.codeSize && !IFFUNC_ISZ88DK_FASTCALL (ftype)) ? "!enters" : "!enter"); + } + + _G.stack.offset = sym->stack; +} + +/*-----------------------------------------------------------------*/ +/* genEndFunction - generates epilogue for functions */ +/*-----------------------------------------------------------------*/ +static void +genEndFunction (iCode * ic) +{ + symbol *sym = OP_SYMBOL (IC_LEFT (ic)); + int retsize = getSize (sym->type->next); + /* __critical __interrupt without an interrupt number is the non-maskable interrupt */ + bool is_nmi = (IS_Z80 || IS_Z180 || IS_EZ80_Z80) && IFFUNC_ISCRITICAL (sym->type) && FUNC_INTNO (sym->type) == INTNO_UNSPEC; + + wassert (!regalloc_dry_run); + wassertl (!_G.stack.pushed, "Unbalanced stack."); + + if (IFFUNC_ISNAKED (sym->type) || IFFUNC_ISNORETURN (sym->type)) + { + emitDebug (IFFUNC_ISNAKED (sym->type) ? "; naked function: No epilogue." : "; _Noreturn function: No epilogue."); + return; + } + + wassertl(regalloc_dry_run || !IFFUNC_ISZ88DK_CALLEE(sym->type), "Unimplemented __z88dk_callee support on callee side"); + + if (!IS_GB && !_G.omitFramePtr && sym->stack > (optimize.codeSize ? 2 : 1)) + { + emit2 ("ld sp, ix"); + cost2 (2, 10, 7, 4, 0, 6, 2); + } + else + adjustStack (_G.stack.offset, !IS_TLCS90, TRUE, retsize == 0 || retsize > 4, !IY_RESERVED); + + if(!IS_GB && !_G.omitFramePtr) + emit2 ("pop ix"); + + if (_G.calleeSaves.pushedDE) + { + emit2 ("pop de"); + _G.calleeSaves.pushedDE = FALSE; + } + + if (_G.calleeSaves.pushedBC) + { + emit2 ("pop bc"); + _G.calleeSaves.pushedBC = FALSE; + } + + if (options.profile) + { + emit2 ("!profileexit"); + } + + /* if this is an interrupt service routine + then save all potentially used registers. */ + if (IFFUNC_ISISR (sym->type)) + emit2 ("!popa"); + else + { + /* This is a non-ISR function. + If critical function then turn interrupts back on */ + if (IFFUNC_ISCRITICAL (sym->type)) + { + if (IS_GB || IS_TLCS90) + emit2 ("!ei"); + else if (IS_RAB) + emit2 ("ipres"); + else + { + symbol *tlbl = newiTempLabel (NULL); + //restore P/O flag + emit2 ("pop af"); + //parity odd <==> P/O=0 <==> interrupt enable flag IFF2 was 0 <==> + //don't enable interrupts as they were off before + emit2 ("jp PO,!tlabel", labelKey2num (tlbl->key)); + emit2 ("!ei"); + emit2 ("!tlabeldef", labelKey2num (tlbl->key)); + genLine.lineCurr->isLabel = 1; + } + } + } + + if (options.debug && currFunc) + { + debugFile->writeEndFunction (currFunc, ic, 1); + } + + if (IFFUNC_ISISR (sym->type)) + { + if (is_nmi) + emit2 ("retn"); + else if (IS_RAB && IFFUNC_ISCRITICAL (sym->type) && FUNC_INTNO (sym->type) == INTNO_UNSPEC) + { + emit2 ("ipres"); + emit2 ("ret"); + } + else if (IS_GB) + emit2 (IFFUNC_ISCRITICAL (sym->type) ? "reti" : "ret"); + else + { + if (IFFUNC_ISCRITICAL (sym->type) && !is_nmi) + emit2 ("!ei"); + emit2 ("reti"); + } + } + else + { + /* Both banked and non-banked just ret */ + emit2 ("ret"); + } + + _G.flushStatics = 1; + _G.stack.pushed = 0; + _G.stack.offset = 0; +} + +/*-----------------------------------------------------------------*/ +/* genRet - generate code for return statement */ +/*-----------------------------------------------------------------*/ +static void +genRet (const iCode *ic) +{ + /* Errk. This is a hack until I can figure out how + to cause dehl to spill on a call */ + int size, offset = 0; + + /* if we have no return value then + just generate the "ret" */ + if (!IC_LEFT (ic)) + goto jumpret; + + /* we have something to return then + move the return value into place */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + size = AOP_SIZE (IC_LEFT (ic)); + + if (size == 2) + { + fetchPairLong (IS_GB ? PAIR_DE : PAIR_HL, AOP (IC_LEFT (ic)), ic, 0); + } + else if (size <= 4) + { + if (IS_GB && size == 4 && requiresHL (AOP (IC_LEFT (ic)))) + { + fetchPairLong (PAIR_DE, AOP (IC_LEFT (ic)), 0, 0); + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, 2); + } + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + genMove_o (ASMOP_RETURN, 0, IC_LEFT (ic)->aop, 0, IC_LEFT (ic)->aop->size, true, true); + else + { + bool skipbytes[4] = {false, false, false, false}; // Take care to not overwrite hl. + for (offset = 0; offset < size; offset++) + { + if (requiresHL (AOP (IC_LEFT (ic))) && (ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == H_IDX || ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == L_IDX)) + { + skipbytes[offset] = true; + continue; + } + cheapMove (ASMOP_RETURN, offset, IC_LEFT (ic)->aop, offset, true); + } + for (offset = 0; offset < size; offset++) + if (skipbytes[offset] && offset + 1 < size && ASMOP_RETURN->aopu.aop_reg[offset]->rIdx == L_IDX && ASMOP_RETURN->aopu.aop_reg[offset + 1]->rIdx == H_IDX) + { + fetchPairLong (PAIR_HL, IC_LEFT (ic)->aop, 0, offset); + break; + } + else if (skipbytes[offset]) + { + cheapMove (ASMOP_RETURN, offset, IC_LEFT (ic)->aop, offset, true); + } + } + } + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) + { + unsigned long long lit = ullFromVal (AOP (IC_LEFT (ic))->aopu.aop_lit); + setupPairFromSP (PAIR_HL, _G.stack.offset + _G.stack.param_offset + _G.stack.pushed + (_G.omitFramePtr || IS_GB ? 0 : 2)); + emit2 ("ld a, (hl)"); + emit2 ("inc hl"); + emit2 ("ld h, (hl)"); + emit2 ("ld l, a"); + regalloc_dry_run_cost += 8; + do + { + emit2 ("ld (hl), !immedbyte", (unsigned long) (lit & 0xff)); + regalloc_dry_run_cost += 2; + lit >>= 8; + if (size > 1) + { + emit2 ("inc hl"); + regalloc_dry_run_cost++; + } + } + while (--size); + } + else if (!IS_GB && AOP_TYPE (IC_LEFT (ic)) == AOP_STK || AOP_TYPE (IC_LEFT (ic)) == AOP_EXSTK + || AOP_TYPE (IC_LEFT (ic)) == AOP_DIR || AOP_TYPE (IC_LEFT (ic)) == AOP_IY) + { + setupPairFromSP (PAIR_HL, _G.stack.offset + _G.stack.param_offset + _G.stack.pushed + (_G.omitFramePtr || IS_GB ? 0 : 2)); + emit2 ("ld e, (hl)"); + emit2 ("inc hl"); + emit2 ("ld d, (hl)"); + regalloc_dry_run_cost += 7; + if (AOP_TYPE (IC_LEFT (ic)) == AOP_STK || AOP_TYPE (IC_LEFT (ic)) == AOP_EXSTK) + { + int sp_offset, fp_offset; + fp_offset = + AOP (IC_LEFT (ic))->aopu.aop_stk + (AOP (IC_LEFT (ic))->aopu.aop_stk > + 0 ? _G.stack.param_offset : 0); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2 ("ld hl, #%d", sp_offset); + emit2 ("add hl, sp"); + regalloc_dry_run_cost += 4; + } + else + { + emit2 ("ld hl, #%s", AOP (IC_LEFT (ic))->aopu.aop_dir); + regalloc_dry_run_cost += 3; + } + emit2 ("ld bc, #%d", size); + emit2 ("ldir"); + regalloc_dry_run_cost += 5; + } + else + { + setupPairFromSP (PAIR_HL, _G.stack.offset + _G.stack.param_offset + _G.stack.pushed + (_G.omitFramePtr || IS_GB ? 0 : 2)); + emit2 ("ld c, (hl)"); + emit2 ("inc hl"); + emit2 ("ld b, (hl)"); + regalloc_dry_run_cost += 7; + spillPair (PAIR_HL); + do + { + cheapMove (ASMOP_A, 0, AOP (IC_LEFT (ic)), offset++, true); + emit2 ("ld (bc), a"); + regalloc_dry_run_cost++; + if (size > 1) + { + emit2 ("inc bc"); + regalloc_dry_run_cost++; + } + } + while (--size); + } + freeAsmop (IC_LEFT (ic), NULL); + +jumpret: + /* generate a jump to the return label + if the next is not the return statement */ + if (!(ic->next && ic->next->op == LABEL && IC_LABEL (ic->next) == returnLabel)) + { + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (returnLabel->key)); + regalloc_dry_run_cost += 3; + } +} + +/*-----------------------------------------------------------------*/ +/* genLabel - generates a label */ +/*-----------------------------------------------------------------*/ +static void +genLabel (const iCode * ic) +{ + /* special case never generate */ + if (IC_LABEL (ic) == entryLabel) + return; + + emitLabelSpill (IC_LABEL (ic)); +} + +/*-----------------------------------------------------------------*/ +/* genGoto - generates a ljmp */ +/*-----------------------------------------------------------------*/ +static void +genGoto (const iCode * ic) +{ + emit2 ("jp !tlabel", labelKey2num (IC_LABEL (ic)->key)); +} + +/*-----------------------------------------------------------------*/ +/* genPlusIncr :- does addition with increment if possible */ +/*-----------------------------------------------------------------*/ +static bool +genPlusIncr (const iCode * ic) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + PAIR_ID resultId = getPairId (AOP (IC_RESULT (ic))); + + /* will try to generate an increment */ + /* if the right side is not a literal + we cannot */ + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + return FALSE; + + icount = (unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + + /* If result is a pair */ + if (resultId != PAIR_INVALID) + { + bool delayed_move; + if (isLitWord (AOP (IC_LEFT (ic)))) + { + fetchLitPair (getPairId (AOP (IC_RESULT (ic))), AOP (IC_LEFT (ic)), icount); + return TRUE; + } + if (isPair (AOP (IC_LEFT (ic))) && resultId == PAIR_HL && icount > 3) + { + if (getPairId (AOP (IC_LEFT (ic))) == PAIR_HL) + { + PAIR_ID freep = getDeadPairId (ic); + if (freep != PAIR_INVALID) + { + fetchPair (freep, AOP (IC_RIGHT (ic))); + emit2 ("add hl, %s", _pairs[freep].name); + regalloc_dry_run_cost += 1; + return TRUE; + } + } + else + { + fetchPair (resultId, AOP (IC_RIGHT (ic))); + emit2 ("add hl, %s", getPairName (AOP (IC_LEFT (ic)))); + regalloc_dry_run_cost += 1; + return TRUE; + } + } + if (icount > 5) + return FALSE; + /* Inc a pair */ + delayed_move = (getPairId (AOP (IC_RESULT (ic))) == PAIR_IY && getPairId (AOP (IC_LEFT (ic))) != PAIR_INVALID + && isPairDead (getPairId (AOP (IC_LEFT (ic))), ic)); + if (!sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + if (icount > 3) + return FALSE; + if (!delayed_move) + fetchPair (getPairId (AOP (IC_RESULT (ic))), AOP (IC_LEFT (ic))); + } + while (icount--) + { + PAIR_ID pair = delayed_move ? getPairId (AOP (IC_LEFT (ic))) : getPairId (AOP (IC_RESULT (ic))); + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IY ? 2 : 1); + } + if (delayed_move) + fetchPair (getPairId (AOP (IC_RESULT (ic))), AOP (IC_LEFT (ic))); + return TRUE; + } + + if (!IS_GB && isLitWord (AOP (IC_LEFT (ic))) && size == 2 && isPairDead (PAIR_HL, ic)) + { + fetchLitPair (PAIR_HL, AOP (IC_LEFT (ic)), icount); + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + return TRUE; + } + + if (icount > 4) // Not worth it if the sequence of inc gets too long. + return false; + + if (icount > 1 && size == 1 && aopInReg (IC_LEFT (ic)->aop, 0, A_IDX)) // add a, #n is cheaper than sequence of inc a. + return false; + + if (size == 2 && getPairId (AOP (IC_LEFT (ic))) != PAIR_INVALID && icount <= 3 && isPairDead (getPairId (AOP (IC_LEFT (ic))), ic)) + { + PAIR_ID pair = getPairId (AOP (IC_LEFT (ic))); + while (icount--) + emit2 ("inc %s", _pairs[pair].name); + commitPair (AOP (IC_RESULT (ic)), pair, ic, FALSE); + return true; + } + + if (size == 2 && icount <= 2 && isPairDead (PAIR_HL, ic) && !IS_GB && + (IC_LEFT (ic)->aop->type == AOP_HL || IC_LEFT (ic)->aop->type == AOP_IY)) + { + fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); + while (icount--) + emit2 ("inc hl"); + regalloc_dry_run_cost++; + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + return true; + } + + /* if increment 16 bits in register */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic))) && size > 1 && icount == 1) + { + int offset = 0; + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + while (size--) + { + if (size == 1 && getPairId_o (AOP (IC_RESULT (ic)), offset) != PAIR_INVALID) + { + emit2 ("inc %s", _pairs[getPairId_o (AOP (IC_RESULT (ic)), offset)].name); + size--; + offset += 2; + break; + } + emit3_o (A_INC, AOP (IC_RESULT (ic)), offset++, 0, 0); + if (size) + { + if (!regalloc_dry_run) + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + } + } + if (!regalloc_dry_run) + (AOP_TYPE (IC_LEFT (ic)) == AOP_HL || IS_GB + && AOP_TYPE (IC_LEFT (ic)) == AOP_STK) ? emitLabelSpill (tlbl) : emitLabel (tlbl); + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_HL) + spillCached (); + return TRUE; + } + + /* if the sizes are greater than 1 then we cannot */ + if (AOP_SIZE (IC_RESULT (ic)) > 1 || AOP_SIZE (IC_LEFT (ic)) > 1) + return FALSE; + + /* If the result is in a register then we can load then increment. + */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_REG) + { + cheapMove (AOP (IC_RESULT (ic)), LSB, AOP (IC_LEFT (ic)), LSB, true); + while (icount--) + emit3_o (A_INC, AOP (IC_RESULT (ic)), LSB, 0, 0); + return TRUE; + } + + /* we can if the aops of the left & result match or + if they are in registers and the registers are the + same */ + if (sameRegs (AOP (IC_LEFT (ic)), AOP (IC_RESULT (ic)))) + { + while (icount--) + emit3 (A_INC, AOP (IC_LEFT (ic)), 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* outBitAcc - output a bit in acc */ +/*-----------------------------------------------------------------*/ +static void +outBitAcc (operand * result) +{ + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + wassertl (0, "Tried to write A into a bit"); + } + else + { + if (!regalloc_dry_run) + { + emit2 ("jp Z, !tlabel", labelKey2num (tlbl->key)); + emit2 ("ld a, !one"); + emitLabel (tlbl); + } + regalloc_dry_run_cost += 5; + outAcc (result); + } +} + +static bool +couldDestroyCarry (const asmop *aop) +{ + if (aop) + { + if (aop->type == AOP_EXSTK || aop->type == AOP_IY) + { + return TRUE; + } + } + return FALSE; +} + +static void +shiftIntoPair (PAIR_ID id, asmop *aop) +{ + wassertl (!IS_GB, "Not implemented for the GBZ80"); + + emitDebug ("; Shift into pair"); + + switch (id) + { + case PAIR_HL: + setupPair (PAIR_HL, aop, 0); + break; + case PAIR_DE: + _push (PAIR_DE); + setupPair (PAIR_IY, aop, 0); + emit2 ("push iy"); + emit2 ("pop %s", _pairs[id].name); + break; + case PAIR_IY: + setupPair (PAIR_IY, aop, 0); + break; + default: + wassertl (0, "Internal error - hit default case"); + } + + aop->type = AOP_PAIRPTR; + aop->aopu.aop_pairId = id; + _G.pairs[id].offset = 0; + _G.pairs[id].last_type = aop->type; +} + +static void +setupToPreserveCarry (asmop *result, asmop *left, asmop *right) +{ + wassert (left && right); + + if (!IS_GB) + { + if (couldDestroyCarry (right) && couldDestroyCarry (result)) + { + shiftIntoPair (PAIR_HL, right); + /* check result again, in case right == result */ + if (couldDestroyCarry (result)) + { + if (couldDestroyCarry (left)) + shiftIntoPair (PAIR_DE, result); + else + shiftIntoPair (PAIR_IY, result); + } + } + else if (couldDestroyCarry (right)) + { + if (getPairId (result) == PAIR_HL) + _G.preserveCarry = TRUE; + else + shiftIntoPair (PAIR_HL, right); + } + else if (couldDestroyCarry (result)) + { + shiftIntoPair (PAIR_HL, result); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genPlus - generates code for addition */ +/*-----------------------------------------------------------------*/ +static void +genPlus (iCode * ic) +{ + int size, i, offset = 0; + signed char cached[2]; + bool premoved, started; + asmop *leftop; + asmop *rightop; + symbol *tlbl = 0; + + /* special cases :- */ + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + + /* Swap the left and right operands if: + + if literal, literal on the right or + if left requires ACC or right is already + in ACC */ + if ((AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) || (AOP_NEEDSACC (IC_RIGHT (ic))) || aopInReg (IC_RIGHT (ic)->aop, 0, A_IDX)) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + + leftop = IC_LEFT (ic)->aop; + rightop = IC_RIGHT (ic)->aop; + + /* if both left & right are in bit + space */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_CRY) + { + /* Cant happen */ + wassertl (0, "Tried to add two bits"); + } + + /* if left in bit space & right literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_CRY && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT) + { + /* Can happen I guess */ + wassertl (0, "Tried to add a bit to a literal"); + } + + /* if I can do an increment instead + of add then GOOD for ME */ + if (genPlusIncr (ic) == TRUE) + goto release; + + size = getDataSize (IC_RESULT (ic)); + + /* Special case when left and right are constant */ + if (isPair (AOP (IC_RESULT (ic)))) + { + char *left = Safe_strdup (aopGetLitWordLong (AOP (IC_LEFT (ic)), 0, FALSE)); + const char *right = aopGetLitWordLong (AOP (IC_RIGHT (ic)), 0, FALSE); + + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT && AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT && left && right) + { + struct dbuf_s dbuf; + + /* It's a pair */ + /* PENDING: fix */ + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "#(%s + %s)", left, right); + Safe_free (left); + emit2 ("ld %s, %s", getPairName (AOP (IC_RESULT (ic))), dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + regalloc_dry_run_cost += (getPairId (AOP (IC_RESULT (ic))) == PAIR_IY ? 4 : 3); + goto release; + } + Safe_free (left); + } + + // eZ80 has lea. + if (IS_EZ80_Z80 && isPair (IC_RESULT (ic)->aop) && getPairId (IC_LEFT (ic)->aop) == PAIR_IY && IC_RIGHT (ic)->aop->type == AOP_LIT) + { + int lit = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + if (lit >= -128 && lit < 128) + { + emit2 ("lea %s, iy, #%d", _pairs[getPairId (IC_RESULT (ic)->aop)].name, lit); + regalloc_dry_run_cost += 3; + spillPair (getPairId (IC_RESULT (ic)->aop)); + goto release; + } + } + + if ((isPair (AOP (IC_RIGHT (ic))) || isPair (AOP (IC_LEFT (ic)))) && getPairId (AOP (IC_RESULT (ic))) == PAIR_HL) + { + /* Fetch into HL then do the add */ + PAIR_ID left = getPairId (AOP (IC_LEFT (ic))); + PAIR_ID right = getPairId (AOP (IC_RIGHT (ic))); + + spillPair (PAIR_HL); + + if (left == PAIR_HL && right != PAIR_INVALID) + { + emit2 ("add hl, %s", _pairs[right].name); + regalloc_dry_run_cost += 1; + goto release; + } + else if (right == PAIR_HL && left != PAIR_INVALID) + { + emit2 ("add hl, %s", _pairs[left].name); + regalloc_dry_run_cost += 1; + goto release; + } + else if (right != PAIR_INVALID && right != PAIR_HL) + { + fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); + emit2 ("add hl, %s", getPairName (AOP (IC_RIGHT (ic)))); + regalloc_dry_run_cost += 1; + goto release; + } + else if (left != PAIR_INVALID && left != PAIR_HL) + { + fetchPair (PAIR_HL, AOP (IC_RIGHT (ic))); + emit2 ("add hl, %s", getPairName (AOP (IC_LEFT (ic)))); + regalloc_dry_run_cost += 1; + goto release; + } + else if (left == PAIR_HL && (isPairDead (PAIR_DE, ic) || isPairDead (PAIR_BC, ic))) + { + PAIR_ID pair = (isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC); + fetchPair (pair, AOP (IC_RIGHT (ic))); + emit2 ("add hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + goto release; + } + else if (right == PAIR_HL && (isPairDead (PAIR_DE, ic) || isPairDead (PAIR_BC, ic))) + { + PAIR_ID pair = (isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC); + fetchPair (pair, AOP (IC_LEFT (ic))); + emit2 ("add hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + goto release; + } + else + { + /* Can't do it */ + } + } + + // Handle AOP_EXSTK conflict with hl here, since setupToPreserveCarry() would cause problems otherwise. + if (IC_RESULT (ic)->aop->type == AOP_EXSTK && (getPairId (IC_LEFT (ic)->aop) == PAIR_HL || getPairId (IC_RIGHT (ic)->aop) == PAIR_HL) && + (isPairDead (PAIR_DE, ic) || isPairDead (PAIR_BC, ic)) && isPairDead (PAIR_HL, ic)) + { + PAIR_ID extrapair = isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + fetchPair (extrapair, getPairId (IC_LEFT (ic)->aop) == PAIR_HL ? IC_RIGHT (ic)->aop : IC_LEFT (ic)->aop); + emit2 ("add hl, %s", _pairs[extrapair].name); + regalloc_dry_run_cost += 1; + spillPair (PAIR_HL); + commitPair (IC_RESULT (ic)->aop, PAIR_HL, ic, FALSE); + goto release; + } + else if (getPairId (AOP (IC_RESULT (ic))) == PAIR_IY && + (getPairId (AOP (IC_LEFT (ic))) == PAIR_HL && isPair (AOP (IC_RIGHT (ic))) && getPairId (AOP (IC_RIGHT (ic))) != PAIR_IY || getPairId (AOP (IC_RIGHT (ic))) == PAIR_HL && isPair (AOP (IC_LEFT (ic))) && getPairId (AOP (IC_LEFT (ic))) != PAIR_IY) && + isPairDead (PAIR_HL, ic)) + { + PAIR_ID pair = (getPairId (AOP (IC_LEFT (ic))) == PAIR_HL ? getPairId (AOP (IC_RIGHT (ic))) : getPairId (AOP (IC_LEFT (ic)))); + emit2 ("add hl, %s", _pairs[pair].name); + _push (PAIR_HL); + _pop (PAIR_IY); + goto release; + } + else if (getPairId (AOP (IC_RESULT (ic))) == PAIR_IY) + { + bool save_pair = FALSE; + PAIR_ID pair; + + if (getPairId (AOP (IC_RIGHT (ic))) == PAIR_IY || getPairId (AOP (IC_LEFT (ic))) == PAIR_BC + || getPairId (AOP (IC_LEFT (ic))) == PAIR_DE || getPairId (AOP (IC_LEFT (ic))) != PAIR_IY + && (AOP_TYPE (IC_RIGHT (ic)) == AOP_IMMD || AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT)) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + pair = getPairId (AOP (IC_RIGHT (ic))); + if (pair != PAIR_BC && pair != PAIR_DE) + { + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == C_IDX + && (!bitVectBitValue (ic->rSurv, B_IDX) || !isPairDead (PAIR_DE, ic))) + pair = PAIR_BC; + else if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == E_IDX + && (!bitVectBitValue (ic->rSurv, D_IDX) || !isPairDead (PAIR_BC, ic))) + pair = PAIR_DE; + else + pair = isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + if (!isPairDead (pair, ic)) + save_pair = TRUE; + } + fetchPair (PAIR_IY, AOP (IC_LEFT (ic))); + if (save_pair) + _push (pair); + fetchPair (pair, AOP (IC_RIGHT (ic))); + emit2 ("add iy, %s", _pairs[pair].name); + spillPair (PAIR_IY); + regalloc_dry_run_cost += 2; + if (save_pair) + _pop (pair); + goto release; + } + + /* gbz80 special case: + ld hl,sp+n trashes C so we can't afford to do it during an + add with stack based variables. Worst case is: + ld hl,sp+left + ld a,(hl) + ld hl,sp+right + add (hl) + ld hl,sp+result + ld (hl),a + ld hl,sp+left+1 + ld a,(hl) + ld hl,sp+right+1 + adc (hl) + ld hl,sp+result+1 + ld (hl),a + So you can't afford to load up hl if either left, right, or result + is on the stack (*sigh*) The alt is: + ld hl,sp+left + ld de,(hl) + ld hl,sp+right + ld hl,(hl) + add hl,de + ld hl,sp+result + ld (hl),hl + Combinations in here are: + * If left or right are in bc then the loss is small - trap later + * If the result is in bc then the loss is also small + */ + if (IS_GB) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_STK || AOP_TYPE (IC_RIGHT (ic)) == AOP_STK || AOP_TYPE (IC_RESULT (ic)) == AOP_STK) + { + if ((AOP_SIZE (IC_LEFT (ic)) == 2 || + AOP_SIZE (IC_RIGHT (ic)) == 2) && (AOP_SIZE (IC_LEFT (ic)) <= 2 && AOP_SIZE (IC_RIGHT (ic)) <= 2 || size == 2)) + { + if (getPairId (AOP (IC_RIGHT (ic))) == PAIR_BC || getPairId (AOP (IC_RIGHT (ic))) == PAIR_DE) + { + /* Swap left and right */ + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + if (getPairId (AOP (IC_LEFT (ic))) == PAIR_BC) + { + fetchPair (PAIR_HL, AOP (IC_RIGHT (ic))); + emit2 ("add hl, bc"); + regalloc_dry_run_cost += 1; + } + else + { + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP_SIZE (IC_RIGHT (ic)) == 2 && AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP_SIZE (IC_LEFT (ic)) == 2) + { + const short dst[4] = { E_IDX, L_IDX, D_IDX, H_IDX }; + short src[4]; + if (AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == E_IDX + || AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == L_IDX) + { + src[0] = AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx; + src[1] = AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx; + src[2] = AOP (IC_RIGHT (ic))->aopu.aop_reg[1]->rIdx; + src[3] = AOP (IC_LEFT (ic))->aopu.aop_reg[1]->rIdx; + } + else + { + src[1] = AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx; + src[0] = AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx; + src[3] = AOP (IC_RIGHT (ic))->aopu.aop_reg[1]->rIdx; + src[2] = AOP (IC_LEFT (ic))->aopu.aop_reg[1]->rIdx; + } + regMove (dst, src, size, FALSE); + } + else if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && + (AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == E_IDX + || AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == D_IDX || AOP_SIZE (IC_RIGHT (ic)) == 2 + && (AOP (IC_RIGHT (ic))->aopu.aop_reg[1]->rIdx == E_IDX + || AOP (IC_RIGHT (ic))->aopu.aop_reg[1]->rIdx == D_IDX))) + { + fetchPair (PAIR_DE, AOP (IC_RIGHT (ic))); + fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); + } + else + { + fetchPair (PAIR_DE, AOP (IC_LEFT (ic))); + fetchPair (PAIR_HL, AOP (IC_RIGHT (ic))); + } + emit2 ("add hl, de"); + regalloc_dry_run_cost += 1; + } + spillPair (PAIR_HL); + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + goto release; + } + } + if (size == 4) + { + /* Be paranoid on the GB with 4 byte variables due to how C + can be trashed by lda hl,n(sp). + */ + _gbz80_emitAddSubLong (ic, TRUE); + goto release; + } + } + + // Avoid overwriting operand in h or l when setupToPreserveCarry () loads hl - only necessary if carry is actually used during addition. + premoved = FALSE; + if (size > 1 && !(size == 2 && (isPair (leftop) && rightop->type == AOP_LIT))) + { + if (!couldDestroyCarry (leftop) && (couldDestroyCarry (rightop) || couldDestroyCarry (AOP (IC_RESULT (ic))))) + { + cheapMove (ASMOP_A, 0, leftop, offset, true); + premoved = TRUE; + } + + setupToPreserveCarry (AOP (IC_RESULT (ic)), leftop, rightop); + } + // But if we don't actually want to use hl for the addition, it can make sense to setup an op to use cheaper hl instead of iy. + if (size == 1 && !aopInReg(leftop, 0, H_IDX) && !aopInReg(leftop, 0, L_IDX) && isPairDead (PAIR_HL, ic)) + { + if (couldDestroyCarry (AOP (IC_RESULT (ic))) && + (AOP (IC_RESULT (ic)) == leftop || AOP (IC_RESULT (ic)) == rightop)) + shiftIntoPair (PAIR_HL, AOP (IC_RESULT (ic))); + else if (couldDestroyCarry (rightop)) + shiftIntoPair (PAIR_HL, rightop); + } + + cached[0] = -1; + cached[1] = -1; + + for (i = 0, started = FALSE; i < size;) + { + // Addition of interleaved pairs. + if ((!premoved || i) && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && leftop->size - i >= 2 && rightop->size - i >= 2) + { + PAIR_ID pair = PAIR_INVALID; + + if (aopInReg (leftop, i, L_IDX) && aopInReg (rightop, i + 1, H_IDX)) + { + if (aopInReg (leftop, i + 1, D_IDX) && aopInReg (rightop, i, E_IDX)) + pair = PAIR_DE; + else if (aopInReg (leftop, i + 1, B_IDX) && aopInReg (rightop, i, C_IDX)) + pair = PAIR_BC; + } + else if (aopInReg (leftop, i + 1, H_IDX) && aopInReg (rightop, i, L_IDX)) + { + if (aopInReg (leftop, i, E_IDX) && aopInReg (rightop, i + 1, D_IDX)) + pair = PAIR_DE; + else if (aopInReg (leftop, i, C_IDX) && aopInReg (rightop, i + 1, B_IDX)) + pair = PAIR_BC; + } + + if (pair != PAIR_INVALID) + { + if (started) + { + emit2 ("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + i += 2; + continue; + } + } + + if ((!premoved || i) && !started && i == size - 2 && !i && isPair (AOP (IC_RIGHT (ic))) && AOP_TYPE (IC_LEFT (ic)) == AOP_IMMD && getPairId (AOP (IC_RIGHT (ic))) != PAIR_HL + && isPairDead (PAIR_HL, ic)) + { + fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); + emit2 ("add hl, %s", getPairName (AOP (IC_RIGHT (ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + spillPair (PAIR_HL); + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + i += 2; + } + else if ((!premoved || i) && !started && i == size - 2 && !i && isPair (AOP (IC_LEFT (ic))) && (rightop->type == AOP_LIT || rightop->type == AOP_IMMD) && getPairId (AOP (IC_LEFT (ic))) != PAIR_HL + && isPairDead (PAIR_HL, ic)) + { + fetchPair (PAIR_HL, AOP (IC_RIGHT (ic))); + emit2 ("add hl, %s", getPairName (AOP (IC_LEFT (ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + spillPair (PAIR_HL); + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + i += 2; + } + else if ((!premoved || i) && !started && i == size - 2 && !i && aopInReg (leftop, i, HL_IDX) && isPair (AOP (IC_RIGHT (ic))) && isPairDead (PAIR_HL, ic)) + { + emit2 ("add hl, %s", getPairName (AOP (IC_RIGHT (ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + i += 2; + } + else if ((!premoved || i) && !started && i == size - 2 && !i && isPair (AOP (IC_LEFT (ic))) && aopInReg (rightop, i, HL_IDX) && isPairDead (PAIR_HL, ic)) + { + emit2 ("add hl, %s", getPairName (AOP (IC_LEFT (ic)))); + started = TRUE; + regalloc_dry_run_cost += 1; + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + i += 2; + } + else if ((!premoved || i) && !started && i == size - 2 && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && + aopInReg (rightop, i, C_IDX) && !bitVectBitValue (ic->rSurv, B_IDX)) + { + if (aopInReg (rightop, i + 1, H_IDX) || aopInReg (rightop, i + 1, L_IDX)) + { + cheapMove (ASMOP_B, 0, AOP (IC_RIGHT (ic)), i + 1, true); + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, i); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, i); + cheapMove (ASMOP_B, 0, AOP (IC_RIGHT (ic)), i + 1, true); + } + emit2 ("add hl, bc"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } + else if (!options.oldralloc && (!premoved || i) && !started && i == size - 2 && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && + aopInReg (leftop, i, C_IDX) && !bitVectBitValue (ic->rSurv, B_IDX)) + { + if (aopInReg (leftop, i + 1, H_IDX) || aopInReg (leftop, i + 1, L_IDX)) + { + cheapMove (ASMOP_B, 0, AOP (IC_LEFT (ic)), i + 1, true); + fetchPairLong (PAIR_HL, AOP (IC_RIGHT (ic)), 0, i); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_RIGHT (ic)), 0, i); + cheapMove (ASMOP_B, 0, AOP (IC_LEFT (ic)), i + 1, true); + } + emit2 ("add hl, bc"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } + else if (!options.oldralloc && (!premoved || i) && !started && i == size - 2 && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && + aopInReg (rightop, i, E_IDX) && !bitVectBitValue (ic->rSurv, D_IDX)) + { + if (aopInReg (rightop, i + 1, H_IDX) || aopInReg (rightop, i + 1, L_IDX)) + { + cheapMove (ASMOP_D, 0, AOP (IC_RIGHT (ic)), i + 1, true); + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, i); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_LEFT (ic)), 0, i); + cheapMove (ASMOP_D, 0, AOP (IC_RIGHT (ic)), i + 1, true); + } + emit2 ("add hl, de"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } + else if (!options.oldralloc && (!premoved || i) && !started && i == size - 2 && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && + aopInReg (leftop, i, E_IDX) && !bitVectBitValue (ic->rSurv, D_IDX)) + { + if (aopInReg (leftop, i + 1, H_IDX) || aopInReg (leftop, i + 1, L_IDX)) + { + cheapMove (ASMOP_D, 0, AOP (IC_LEFT (ic)), i + 1, true); + fetchPairLong (PAIR_HL, AOP (IC_RIGHT (ic)), 0, i); + } + else + { + fetchPairLong (PAIR_HL, AOP (IC_RIGHT (ic)), 0, i); + cheapMove (ASMOP_D, 0, AOP (IC_LEFT (ic)), i + 1, true); + } + emit2 ("add hl, de"); + started = TRUE; + regalloc_dry_run_cost += 1; + i += 2; + } + // When adding a literal, the 16 bit addition results in smaller, faster code than two 8-bit additions. + else if ((!premoved || i) && aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && aopInReg (leftop, i, HL_IDX) && (rightop->type == AOP_LIT && !aopIsLitVal (rightop, i, 1, 0) || rightop->type == AOP_IMMD)) + { + PAIR_ID pair = getFreePairId (ic); + bool pair_alive; + if (pair == PAIR_INVALID) + pair = PAIR_DE; + if (pair_alive = !isPairDead (pair, ic)) + _push (pair); + fetchPairLong (pair, AOP (IC_RIGHT (ic)), 0, i); + if (started) + { + emit2 ("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + regalloc_dry_run_cost += 1; + if (pair_alive) + _pop (pair); + i += 2; + } + // When adding registers the 16 bit addition results in smaller, faster code than an 8-bit addition. + else if ((!premoved || i) && i == size - 1 && isPairDead (PAIR_HL, ic) && aopInReg (AOP (IC_RESULT (ic)), i, L_IDX) + && (aopInReg (leftop, i, L_IDX) || aopInReg (rightop, i, L_IDX)) + && (aopInReg (leftop, i, C_IDX) || aopInReg (rightop, i, C_IDX) || aopInReg (leftop, i, E_IDX) || aopInReg (rightop, i, E_IDX))) + { + PAIR_ID pair = (leftop->aopu.aop_reg[i]->rIdx == C_IDX + || rightop->aopu.aop_reg[i]->rIdx == C_IDX) ? PAIR_BC : PAIR_DE; + if (started) + { + emit2 ("adc hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 1; + } + i++; + } + // When adding a literal, the 16 bit addition results in smaller, slower code than an 8-bit addition. + else if ((!premoved || i) && optimize.codeSize && !started && i == size - 1 && isPairDead (PAIR_HL, ic) + && rightop->type == AOP_LIT && aopInReg (AOP (IC_RESULT (ic)), i, L_IDX) && aopInReg (leftop, i, L_IDX) + && (!bitVectBitValue (ic->rSurv, C_IDX) || !bitVectBitValue (ic->rSurv, E_IDX))) + { + PAIR_ID pair = bitVectBitValue (ic->rSurv, C_IDX) ? PAIR_DE : PAIR_BC; + emit2 ("ld %s, !immedbyte", _pairs[pair].l, ((unsigned int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit)) & 0xff); + emit2 ("add hl, %s", _pairs[pair].name); + started = TRUE; + regalloc_dry_run_cost += 3; + i++; + } + // Skip over this byte. + else if (!premoved && !started && (leftop->type == AOP_REG || AOP (IC_RESULT (ic))->type == AOP_REG) && aopIsLitVal (rightop, i, 1, 0)) + { + cheapMove (AOP (IC_RESULT (ic)), i, leftop, i, true); + i++; + } + // Conditional 16-bit inc. + else if (i == size - 2 && started && aopIsLitVal (rightop, i, 2, 0) && ( + aopInReg (AOP (IC_RESULT (ic)), i, BC_IDX) && aopInReg (leftop, i, BC_IDX) || + aopInReg (AOP (IC_RESULT (ic)), i, DE_IDX) && aopInReg (leftop, i, DE_IDX) || + aopInReg (AOP (IC_RESULT (ic)), i, HL_IDX) && aopInReg (leftop, i, HL_IDX) || + aopInReg (AOP (IC_RESULT (ic)), i, IY_IDX) && aopInReg (leftop, i, IY_IDX))) + { + PAIR_ID pair = getPairId_o (leftop, i); + + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (0); + + if (!regalloc_dry_run) + emit2 ("jp NC, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 2; // Use cost of jr as the peephole optimizer can typically optimize this jp into jr. Do not emit jr directly to still allow jump-to-jump optimization. + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += (1 + (pair == PAIR_IY)); + i += 2; + } + // Conditional 8-bit inc. + else if (i == size - 1 && started && aopIsLitVal (rightop, i, 1, 0) && + !aopInReg (leftop, i, A_IDX) && // adc a, #0 is cheaper than conditional inc. + (i < leftop->size && + leftop->type == AOP_REG && AOP (IC_RESULT (ic))->type == AOP_REG && + leftop->aopu.aop_reg[i]->rIdx == AOP (IC_RESULT (ic))->aopu.aop_reg[i]->rIdx && + leftop->aopu.aop_reg[i]->rIdx != IYL_IDX && leftop->aopu.aop_reg[i]->rIdx != IYH_IDX || + leftop->type == AOP_STK && leftop == AOP (IC_RESULT (ic)) || + leftop->type == AOP_PAIRPTR && leftop->aopu.aop_pairId == PAIR_HL)) + { + if (!tlbl && !regalloc_dry_run) + tlbl = newiTempLabel (0); + + if (!regalloc_dry_run) + emit2 ("jp NC, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 2; // Use cost of jr as the peephole optimizer can typically optimize this jp into jr. Do not emit jr directly to still allow jump-to-jump optimization. + emit3_o (A_INC, leftop, i, 0, 0); + i++; + } + else + { + if (!premoved) + cheapMove (ASMOP_A, 0, leftop, i, true); + else + premoved = FALSE; + + // Can't handle overwritten operand in hl. + if (started && (IC_RESULT (ic)->aop->type == AOP_EXSTK || IC_RESULT (ic)->aop->type == AOP_PAIRPTR) && requiresHL (IC_RESULT (ic)->aop) && + (aopInReg (leftop, i, L_IDX) || aopInReg (leftop, i, H_IDX) || aopInReg (rightop, i, L_IDX) || aopInReg (rightop, i, H_IDX))) + { + wassert (regalloc_dry_run); + regalloc_dry_run_cost += 1000; + } + + if (!started && aopIsLitVal (rightop, i, 1, 0)) + ; // Skip over this byte. + // We can use inc / dec only for the only, top non-zero byte, since it neither takes into account an existing carry nor does it update the carry. + else if (!started && i == size - 1 && (aopIsLitVal (rightop, i, 1, 1) || aopIsLitVal (rightop, i, 1, 255))) + { + emit3 (aopIsLitVal (rightop, i, 1, 1) ? A_INC : A_DEC, ASMOP_A, 0); + started = TRUE; + } + else + { + emit3_o (started ? A_ADC : A_ADD, ASMOP_A, 0, rightop, i); + started = TRUE; + } + + _G.preserveCarry = (i != size - 1); + if (size && + (requiresHL (rightop) && rightop->type != AOP_REG || requiresHL (leftop) + && leftop->type != AOP_REG) && AOP_TYPE (IC_RESULT (ic)) == AOP_REG + && (AOP (IC_RESULT (ic))->aopu.aop_reg[i]->rIdx == L_IDX + || AOP (IC_RESULT (ic))->aopu.aop_reg[i]->rIdx == H_IDX)) + { + wassert (cached[0] == -1 || cached[1] == -1); + cached[cached[0] == -1 ? 0 : 1] = offset++; + _push (PAIR_AF); + } + else + cheapMove (AOP (IC_RESULT (ic)), i, ASMOP_A, 0, true); + i++; + } + } + + _G.preserveCarry = FALSE; + + if (tlbl) + emitLabel (tlbl); + + for (size = 1; size >= 0; size--) + if (cached[size] != -1) + { + _pop (PAIR_AF); + cheapMove (AOP (IC_RESULT (ic)), cached[size], ASMOP_A, 0, true); + } + +release: + _G.preserveCarry = FALSE; + freeAsmop (IC_LEFT (ic), NULL); + freeAsmop (IC_RIGHT (ic), NULL); + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genSubDec :- does subtraction with decrement if possible */ +/*-----------------------------------------------------------------*/ +static bool +genMinusDec (const iCode *ic, asmop *result, asmop *left, asmop *right) +{ + unsigned int icount; + unsigned int size = getDataSize (IC_RESULT (ic)); + + /* will try to generate a decrement */ + /* if the right side is not a literal we cannot */ + if (right->type != AOP_LIT) + return FALSE; + + /* if the literal value of the right hand side + is greater than 4 then it is not worth it */ + if ((icount = (unsigned int) ulFromVal (right->aopu.aop_lit)) > 2) + return FALSE; + + size = getDataSize (IC_RESULT (ic)); + + /* if decrement 16 bits in register */ + if (sameRegs (left, result) && (size > 1) && isPair (result)) + { + while (icount--) + emit2 ("dec %s", getPairName (result)); + return TRUE; + } + + /* If result is a pair */ + if (isPair (AOP (IC_RESULT (ic)))) + { + fetchPair (getPairId (result), left); + while (icount--) + if (!regalloc_dry_run) + emit2 ("dec %s", getPairName (result)); + regalloc_dry_run_cost += 1; + return TRUE; + } + + /* if decrement 16 bits in register */ + if (sameRegs (left, result) && size == 2 && isPairDead (_getTempPairId (), ic) && !(requiresHL (left) && _getTempPairId () == PAIR_HL)) + { + fetchPair (_getTempPairId (), left); + + while (icount--) + if (!regalloc_dry_run) + emit2 ("dec %s", _getTempPairName ()); + regalloc_dry_run_cost += 1; + + commitPair (result, _getTempPairId (), ic, FALSE); + + return TRUE; + } + + + /* if the sizes are greater than 1 then we cannot */ + if (result->size > 1 || left->size > 1) + return FALSE; + + /* we can if the aops of the left & result match or if they are in + registers and the registers are the same */ + if (sameRegs (left, result)) + { + while (icount--) + emit3 (A_DEC, result, 0); + return TRUE; + } + + if (result->type == AOP_REG) + { + cheapMove (result, 0, left, 0, true); + while (icount--) + emit3 (A_DEC, result, 0); + return TRUE; + } + + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* genSub - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genSub (const iCode *ic, asmop *result, asmop *left, asmop *right) +{ + int size, offset = 0; + unsigned long long lit = 0L; + + /* special cases :- */ + /* if both left & right are in bit space */ + if (left->type == AOP_CRY && right->type == AOP_CRY) + { + wassertl (0, "Tried to subtract two bits"); + return; + } + + /* if I can do an decrement instead of subtract then GOOD for ME */ + if (genMinusDec (ic, result, left, right) == TRUE) + return; + + size = getDataSize (IC_RESULT (ic)); + + if (right->type == AOP_LIT) + { + lit = ullFromVal (right->aopu.aop_lit); + lit = -(long long) lit; + } + + /* Same logic as genPlus */ + if (IS_GB) + { + if (left->type == AOP_STK || right->type == AOP_STK || AOP_TYPE (IC_RESULT (ic)) == AOP_STK) + { + if ((left->size == 2 || + right->size == 2) && (left->size <= 2 && right->size <= 2)) + { + PAIR_ID leftp = getPairId (left); + PAIR_ID rightp = getPairId (right); + + if (leftp == PAIR_INVALID && rightp == PAIR_INVALID) + { + leftp = PAIR_DE; + rightp = PAIR_HL; + } + else if (rightp == PAIR_INVALID) + rightp = PAIR_DE; + else if (leftp == PAIR_INVALID) + leftp = PAIR_DE; + + fetchPair (leftp, left); + /* Order is important. Right may be HL */ + fetchPair (rightp, right); + + if (!regalloc_dry_run) + { + emit2 ("ld a, %s", _pairs[leftp].l); + emit2 ("sub a, %s", _pairs[rightp].l); + emit2 ("ld e, a"); + emit2 ("ld a, %s", _pairs[leftp].h); + emit2 ("sbc a, %s", _pairs[rightp].h); + } + regalloc_dry_run_cost += 5; + + if (AOP_SIZE (IC_RESULT (ic)) > 1) + cheapMove (AOP (IC_RESULT (ic)), 1, ASMOP_A, 0, true); + cheapMove (AOP (IC_RESULT (ic)), 0, ASMOP_E, 0, true); + return; + } + } + if (size == 4) + { + /* Be paranoid on the GB with 4 byte variables due to how C + can be trashed by lda hl,n(sp). + */ + _gbz80_emitAddSubLongLong (ic, left, right, FALSE); + return; + } + } + + setupToPreserveCarry (result, left, right); + + /* if literal right, add a, #-lit, else normal subb */ + while (size) + { + if (!IS_GB && + aopInReg (result, offset, HL_IDX) && + (aopInReg (left, offset, HL_IDX) || left->type == AOP_LIT || left->type == AOP_IY) && + (aopInReg (right, offset, BC_IDX) || aopInReg (right, offset, DE_IDX) || ((right->type == AOP_IY || right->type == AOP_HL) && getFreePairId (ic) != PAIR_INVALID))) + { + PAIR_ID rightpair; + + if (left->type == AOP_LIT || left->type == AOP_IY) + fetchPairLong (PAIR_HL, left, ic, offset); + if (right->type == AOP_IY || right->type == AOP_HL) + { + rightpair = getFreePairId (ic); + fetchPairLong (rightpair, right, ic, offset); + } + else + rightpair = getPartPairId (right, offset); + + if (!offset) + emit3 (A_CP, ASMOP_A, ASMOP_A); + emit2 ("sbc hl, %s", _pairs[rightpair].name); + regalloc_dry_run_cost += 2; + offset += 2; + size -= 2; + _G.preserveCarry = !!size; + continue; + } + + if (right->type != AOP_LIT) + { + if (!offset) + { + if (left->type == AOP_LIT && byteOfVal (left->aopu.aop_lit, offset) == 0x00 && aopInReg (right, offset, A_IDX)) + emit3 (A_NEG, 0, 0); + else + { + if (left->type == AOP_LIT && byteOfVal (left->aopu.aop_lit, offset) == 0x00) + emit3 (A_XOR, ASMOP_A, ASMOP_A); + else + cheapMove (ASMOP_A, 0, left, offset, true); + emit3_o (A_SUB, ASMOP_A, 0, right, offset); + } + } + else + { + cheapMove (ASMOP_A, 0, left, offset, true); + emit3_o (A_SBC, ASMOP_A, 0, right, offset); + } + } + else + { + cheapMove (ASMOP_A, 0, left, offset, true); + + /* first add without previous c */ + if (!offset) + { + if (size == 0 && (unsigned int) (lit & 0x0FFL) == 0xFF) + emit3 (A_DEC, ASMOP_A, 0); + else + { + if (!regalloc_dry_run) + emit2 ("add a, !immedbyte", (unsigned int) (lit & 0x0FFL)); + regalloc_dry_run_cost += 2; + } + } + else + emit2 ("adc a, !immedbyte", (unsigned int) ((lit >> (offset * 8)) & 0x0FFL)); + } + size--; + _G.preserveCarry = !!size; + cheapMove (result, offset++, ASMOP_A, 0, true); + } + + if (AOP_SIZE (IC_RESULT (ic)) == 3 && left->size == 3 && !sameRegs (result, left)) + { + wassertl (0, "Tried to subtract on a long pointer"); + } +} + +/*-----------------------------------------------------------------*/ +/* genMinus - generates code for subtraction */ +/*-----------------------------------------------------------------*/ +static void +genMinus (const iCode *ic) +{ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + + genSub (ic, AOP (IC_RESULT (ic)), AOP (IC_LEFT (ic)), AOP (IC_RIGHT (ic))); + + _G.preserveCarry = FALSE; + freeAsmop (IC_LEFT (ic), NULL); + freeAsmop (IC_RIGHT (ic), NULL); + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genUminusFloat - unary minus for floating points */ +/*-----------------------------------------------------------------*/ +static void +genUminusFloat (operand *op, operand *result) +{ + emitDebug ("; genUminusFloat"); + + /* for this we just need to flip the + first bit then copy the rest in place */ + + cheapMove (ASMOP_A, 0, AOP (op), MSB32, true); + + emit2 ("xor a,!immedbyte", 0x80); + regalloc_dry_run_cost += 2; + cheapMove (AOP (result), MSB32, ASMOP_A, 0, true); + + if (operandsEqu (result, op)) + return; + + genMove_o (result->aop, 0, op->aop, 0, AOP_SIZE (op) - 1, !aopInReg(result->aop, MSB32, A_IDX), false); +} + +/*-----------------------------------------------------------------*/ +/* genUminus - unary minus code generation */ +/*-----------------------------------------------------------------*/ +static void +genUminus (const iCode *ic) +{ + /* assign asmops */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + + /* if both in bit space then special + case */ + if (AOP_TYPE (IC_RESULT (ic)) == AOP_CRY && AOP_TYPE (IC_LEFT (ic)) == AOP_CRY) + { + wassertl (0, "Left and right are in bit space"); + goto release; + } + + if (IS_FLOAT (operandType (IC_LEFT (ic)))) + genUminusFloat (IC_LEFT (ic), IC_RESULT (ic)); + else + genSub (ic, AOP (IC_RESULT (ic)), ASMOP_ZERO, AOP (IC_LEFT (ic))); + +release: + _G.preserveCarry = FALSE; + freeAsmop (IC_LEFT (ic), NULL); + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genMultOneChar - generates code for unsigned 8x8 multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMultOneChar (const iCode * ic) +{ + symbol *tlbl1, *tlbl2; + bool savedB = FALSE; + + asmop *result = AOP (IC_RESULT (ic)); + int resultsize = AOP_SIZE (IC_RESULT (ic)); + + if (IS_GB) + { + wassertl (0, "Multiplication is handled through support function calls on gbz80"); + return; + } + + if ((IS_Z180 || IS_EZ80_Z80) && AOP_TYPE (IC_RESULT (ic)) == AOP_REG) + { + if ((resultsize > 1 ? result->aopu.aop_reg[1]->rIdx == B_IDX : !bitVectBitValue (ic->rSurv, B_IDX)) + && result->aopu.aop_reg[0]->rIdx == C_IDX) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == C_IDX || + AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == B_IDX) + { + cheapMove (ASMOP_C, 0, AOP (IC_LEFT (ic)), LSB, true); + cheapMove (ASMOP_B, 0, AOP (IC_RIGHT (ic)), LSB, true); + } + else + { + cheapMove (ASMOP_B, 0, AOP (IC_LEFT (ic)), LSB, true); + cheapMove (ASMOP_C, 0, AOP (IC_RIGHT (ic)), LSB, true); + } + emit2 ("mlt bc"); + regalloc_dry_run_cost += 2; + return; + } + if ((resultsize > 1 ? result->aopu.aop_reg[1]->rIdx == D_IDX : !bitVectBitValue (ic->rSurv, D_IDX)) + && result->aopu.aop_reg[0]->rIdx == E_IDX) + { + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == E_IDX || + AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == D_IDX) + { + cheapMove (ASMOP_E, 0, AOP (IC_LEFT (ic)), LSB, true); + cheapMove (ASMOP_D, 0, AOP (IC_RIGHT (ic)), LSB, true); + } + else + { + cheapMove (ASMOP_D, 0, AOP (IC_LEFT (ic)), LSB, true); + cheapMove (ASMOP_E, 0, AOP (IC_RIGHT (ic)), LSB, true); + } + emit2 ("mlt de"); + regalloc_dry_run_cost += 2; + return; + } + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && + ((AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == H_IDX && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == L_IDX || + AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == L_IDX && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == H_IDX) && + (resultsize > 1 ? result->aopu.aop_reg[1]->rIdx == H_IDX : !bitVectBitValue (ic->rSurv, H_IDX)) + && result->aopu.aop_reg[0]->rIdx == L_IDX)) + { + emit2 ("mlt hl"); + regalloc_dry_run_cost += 2; + return; + } + } + + if (IS_RAB && isPairDead (PAIR_HL, ic) && isPairDead (PAIR_BC, ic)) + { + const bool save_de = (resultsize > 1 && bitVectBitValue (ic->rSurv, D_IDX) || + bitVectBitValue (ic->rSurv, E_IDX) && !(AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == E_IDX) && !(AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == E_IDX)); + if (save_de) + _push (PAIR_DE); + + if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == E_IDX) + cheapMove (ASMOP_C, 0, AOP (IC_LEFT (ic)), 0, true); + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == E_IDX) + cheapMove (ASMOP_C, 0, AOP (IC_RIGHT (ic)), 0, true); + else if (AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == C_IDX) + cheapMove (ASMOP_E, 0, AOP (IC_LEFT (ic)), 0, true); + else if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == C_IDX) + cheapMove (ASMOP_E, 0, AOP (IC_RIGHT (ic)), 0, true); + else + { + cheapMove (ASMOP_C, 0, AOP (IC_LEFT (ic)), 0, true); + cheapMove (ASMOP_E, 0, AOP (IC_RIGHT (ic)), 0, true); + } + + if (resultsize > 1) + { + cheapMove (ASMOP_D, 0, ASMOP_ZERO, 0, true); + cheapMove (ASMOP_B, 0, ASMOP_D, 0, true); + } + + emit2 ("mul"); + regalloc_dry_run_cost++; + + if (resultsize > 1) + commitPair (result, PAIR_BC, ic, FALSE); + else + cheapMove (result, 0, ASMOP_C, 0, true); + + if (save_de) + _pop (PAIR_DE); + return; + } + + if (!isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + _G.stack.pushedDE = TRUE; + } + if (IS_RAB && !isPairDead (PAIR_BC, ic) || + !(IS_Z180 || IS_EZ80_Z80) && (!options.oldralloc && bitVectBitValue (ic->rSurv, B_IDX) || + options.oldralloc && bitVectBitValue (ic->rMask, B_IDX) && !(getPairId (AOP (IC_RESULT (ic))) == PAIR_BC))) + { + _push (PAIR_BC); + savedB = TRUE; + } + + // genMult() already swapped operands if necessary. + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == E_IDX || + AOP_TYPE (IC_RIGHT (ic)) == AOP_REG && AOP (IC_RIGHT (ic))->aopu.aop_reg[0]->rIdx == H_IDX + && !requiresHL (AOP (IC_LEFT (ic)))) + { + cheapMove (ASMOP_E, 0, AOP (IC_LEFT (ic)), 0, true); + cheapMove (ASMOP_H, 0, AOP (IC_RIGHT (ic)), 0, true); + } + else + { + cheapMove (ASMOP_E, 0, AOP (IC_RIGHT (ic)), 0, true); + cheapMove (ASMOP_H, 0, AOP (IC_LEFT (ic)), 0, true); + } + + if (IS_Z180 || IS_EZ80_Z80) + { + emit2 ("ld l, e"); + emit2 ("mlt hl"); + regalloc_dry_run_cost += 3; + } + else if (IS_RAB) + { + emit2 ("ld c, h"); + emit2 ("ld d, !immedbyte", 0x00); + emit2 ("ld b, d"); + emit2 ("mul"); + emit2 ("ld l, c"); + emit2 ("ld h, b"); + regalloc_dry_run_cost += 7; + } + else if (!regalloc_dry_run) + { + tlbl1 = newiTempLabel (NULL); + tlbl2 = newiTempLabel (NULL); + emit2 ("ld l, !immedbyte", 0x00); + emit2 ("ld d, l"); + emit2 ("ld b, !immedbyte", 0x08); + emitLabel (tlbl1); + emit2 ("add hl, hl"); + emit2 ("jp NC, !tlabel", labelKey2num (tlbl2->key)); + emit2 ("add hl, de"); + emitLabel (tlbl2); + emit2 ("djnz !tlabel", labelKey2num (tlbl1->key)); + regalloc_dry_run_cost += 12; + } + else + regalloc_dry_run_cost += 12; + + + spillPair (PAIR_HL); + + if (savedB) + { + _pop (PAIR_BC); + } + if (_G.stack.pushedDE) + { + _pop (PAIR_DE); + _G.stack.pushedDE = FALSE; + } + + if (result->type != AOP_HL) + { + if (resultsize == 1) + cheapMove (result, 0, ASMOP_L, 0, true); + else + commitPair (result, PAIR_HL, ic, FALSE); + } + else + { + if (resultsize == 1) + { + emit2 ("ld a, l"); + regalloc_dry_run_cost += 1; + cheapMove (result, 0, ASMOP_A, 0, true); + } + else + { + if (!isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + _G.stack.pushedDE = TRUE; + } + emit2 ("ld e, l"); + emit2 ("ld d, h"); + regalloc_dry_run_cost += 2; + commitPair (result, PAIR_DE, ic, FALSE); + if (!isPairDead (PAIR_DE, ic)) + { + _pop (PAIR_DE); + _G.stack.pushedDE = FALSE; + } + } + } +} + +/*-----------------------------------------------------------------*/ +/* genMult - generates code for multiplication */ +/*-----------------------------------------------------------------*/ +static void +genMult (iCode * ic) +{ + int val; + int count, i; + /* If true then the final operation should be a subtract */ + bool active = FALSE; + bool byteResult; + bool add_in_hl = FALSE; + int a_cost = 0, l_cost = 0; + PAIR_ID pair; + + /* Shouldn't occur - all done through function calls */ + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + aopOp (IC_RIGHT (ic), ic, FALSE, FALSE); + aopOp (IC_RESULT (ic), ic, TRUE, FALSE); + + byteResult = (AOP_SIZE (IC_RESULT (ic)) == 1); + + if (AOP_SIZE (IC_LEFT (ic)) > 2 || AOP_SIZE (IC_RIGHT (ic)) > 2 || AOP_SIZE (IC_RESULT (ic)) > 2) + wassertl (0, "Large multiplication is handled through support function calls."); + + /* Swap left and right such that right is a literal */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + + if (AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT) + { + genMultOneChar (ic); + goto release; + } + + wassertl (AOP_TYPE (IC_RIGHT (ic)) == AOP_LIT, "Right must be a literal."); + + val = (int) ulFromVal (AOP (IC_RIGHT (ic))->aopu.aop_lit); + wassertl (val != 1, "Can't multiply by 1"); + + // Try to use mlt. + if ((IS_Z180 || IS_EZ80_Z80) && AOP_SIZE (IC_LEFT (ic)) == 1 && AOP_SIZE (IC_RIGHT (ic)) == 1 && + (byteResult || SPEC_USIGN (getSpec (operandType (IC_LEFT (ic)))) && SPEC_USIGN (getSpec (operandType (IC_RIGHT (ic)))))) + { + pair = getPairId (AOP (IC_RESULT (ic))); + if (pair == PAIR_INVALID && AOP_TYPE (IC_RESULT (ic)) == AOP_REG) + { + if (!bitVectBitValue (ic->rSurv, H_IDX) && AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == L_IDX) + pair = PAIR_HL; + else if (!bitVectBitValue (ic->rSurv, D_IDX) && AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == E_IDX) + pair = PAIR_HL; + else if (!bitVectBitValue (ic->rSurv, B_IDX) && AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == C_IDX) + pair = PAIR_HL; + } + else if (pair == PAIR_INVALID) + pair = getDeadPairId (ic); + + if (pair == PAIR_INVALID) + { + if (!(AOP_TYPE (IC_RESULT (ic)) == AOP_REG && + (AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == L_IDX || AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == H_IDX || + !byteResult && (AOP (IC_RESULT (ic))->aopu.aop_reg[1]->rIdx == L_IDX || AOP (IC_RESULT (ic))->aopu.aop_reg[1]->rIdx == H_IDX)))) + pair = PAIR_HL; + else if (!(AOP_TYPE (IC_RESULT (ic)) == AOP_REG && + (AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == E_IDX || AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == D_IDX || + !byteResult && (AOP (IC_RESULT (ic))->aopu.aop_reg[1]->rIdx == E_IDX || AOP (IC_RESULT (ic))->aopu.aop_reg[1]->rIdx == D_IDX)))) + pair = PAIR_DE; + else + pair = PAIR_BC; + } + + // For small operands under low register pressure, the standard approach is better than the mlt one. + if (byteResult && val <= 6 && isPairDead (PAIR_HL, ic) && (isPairDead (PAIR_DE, ic) || isPairDead (PAIR_BC, ic)) && + !(AOP_TYPE (IC_RESULT (ic)) == AOP_REG && (AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == E_IDX || AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx == C_IDX))) + goto no_mlt; + + if (!isPairDead (pair, ic)) + _push (pair); + + switch (pair) + { + case PAIR_HL: + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == H_IDX) + cheapMove (ASMOP_L, 0, AOP (IC_RIGHT (ic)), 0, true); + else + { + cheapMove (ASMOP_L, 0, AOP (IC_LEFT (ic)), 0, true); + cheapMove (ASMOP_H, 0, AOP (IC_RIGHT (ic)), 0, true); + } + break; + case PAIR_DE: + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == D_IDX) + cheapMove (ASMOP_E, 0, AOP (IC_RIGHT (ic)), 0, true); + else + { + cheapMove (ASMOP_E, 0, AOP (IC_LEFT (ic)), 0, true); + cheapMove (ASMOP_D, 0, AOP (IC_RIGHT (ic)), 0, true); + } + break; + default: + wassert (pair == PAIR_BC); + if (AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == B_IDX) + cheapMove (ASMOP_C, 0, AOP (IC_RIGHT (ic)), 0, true); + else + { + cheapMove (ASMOP_C, 0, AOP (IC_LEFT (ic)), 0, true); + cheapMove (ASMOP_B, 0, AOP (IC_RIGHT (ic)), 0, true); + } + break; + } + + emit2 ("mlt %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + + if (byteResult) + cheapMove (AOP (IC_RESULT (ic)), 0, pair == PAIR_HL ? ASMOP_L : (pair == PAIR_DE ? ASMOP_E : ASMOP_C), 0, true); + else + commitPair (AOP (IC_RESULT (ic)), pair, ic, FALSE); + + if (!isPairDead (pair, ic)) + _pop (pair); + + goto release; + } +no_mlt: + + pair = PAIR_DE; + if (getPairId (AOP (IC_LEFT (ic))) == PAIR_BC || + (byteResult || !bitVectBitValue (ic->rSurv, B_IDX)) && AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == C_IDX) + pair = PAIR_BC; + if (isPairDead (PAIR_BC, ic) && !(AOP_TYPE (IC_LEFT (ic)) == AOP_REG && AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx == E_IDX)) + pair = PAIR_BC; + + if (pair == PAIR_DE && (byteResult ? bitVectBitValue (ic->rSurv, E_IDX) : !isPairDead (PAIR_DE, ic))) + { + _push (PAIR_DE); + _G.stack.pushedDE = TRUE; + } + + /* Use 16-bit additions even for 8-bit result when the operands are in the right places. */ + if (byteResult) + { + if (!aopInReg (IC_LEFT (ic)->aop, 0, A_IDX)) + a_cost += ld_cost (ASMOP_A, AOP (IC_LEFT (ic))); + if (!aopInReg (IC_RESULT (ic)->aop, 0, A_IDX)) + a_cost += ld_cost (AOP (IC_RESULT (ic)), ASMOP_A); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG || AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx != L_IDX) + l_cost += ld_cost (ASMOP_L, AOP (IC_LEFT (ic))); + if (AOP_TYPE (IC_RESULT (ic)) != AOP_REG || AOP (IC_RESULT (ic))->aopu.aop_reg[0]->rIdx != L_IDX) + l_cost += ld_cost (AOP (IC_RESULT (ic)), ASMOP_L); + } + add_in_hl = (!byteResult || isPairDead (PAIR_HL, ic) && l_cost < a_cost); + + if (byteResult) + { + cheapMove (add_in_hl ? ASMOP_L : ASMOP_A, 0, AOP (IC_LEFT (ic)), 0, true); + if (AOP_TYPE (IC_LEFT (ic)) != AOP_REG || AOP (IC_LEFT (ic))->aopu.aop_reg[0]->rIdx != (pair == PAIR_BC ? C_IDX : E_IDX)) + cheapMove (pair == PAIR_BC ? ASMOP_C : ASMOP_E, 0, add_in_hl ? ASMOP_L : ASMOP_A, 0, true); + } + else if (AOP_SIZE (IC_LEFT (ic)) == 1 && !SPEC_USIGN (getSpec (operandType (IC_LEFT (ic))))) + { + cheapMove (pair == PAIR_BC ? ASMOP_C : ASMOP_E, 0, AOP (IC_LEFT (ic)), 0, true); + emit2 ("ld a, %s", _pairs[pair].l); + emit2 ("rlc a"); + emit2 ("sbc a, a"); + emit2 ("ld %s, a", _pairs[pair].h); + regalloc_dry_run_cost += 5; + emit2 ("ld l, %s", _pairs[pair].l); + emit2 ("ld h, %s", _pairs[pair].h); + regalloc_dry_run_cost += 2; + } + else + { + fetchPair (pair, AOP (IC_LEFT (ic))); + if (getPairId (AOP (IC_LEFT (ic))) != PAIR_HL) + { + emit2 ("ld l, %s", _pairs[pair].l); + emit2 ("ld h, %s", _pairs[pair].h); + regalloc_dry_run_cost += 2; + } + } + + i = val; + + for (count = 0; count < 16; count++) + { + if (count != 0 && active) + { + if (!add_in_hl) + emit2 ("add a, a"); + else + emit2 ("add hl, hl"); + regalloc_dry_run_cost += 1; + } + if (i & 0x8000U) + { + if (active) + { + if (!add_in_hl) + emit2 ("add a, %s", _pairs[pair].l); + else + emit2 ("add hl, %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + active = TRUE; + } + i <<= 1; + } + + spillPair (PAIR_HL); + + if (_G.stack.pushedDE) + { + _pop (PAIR_DE); + _G.stack.pushedDE = FALSE; + } + + if (byteResult) + cheapMove (AOP (IC_RESULT (ic)), 0, add_in_hl ? ASMOP_L : ASMOP_A, 0, true); + else + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, FALSE); + +release: + freeAsmop (IC_LEFT (ic), NULL); + freeAsmop (IC_RIGHT (ic), NULL); + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genDiv - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genDiv (const iCode * ic) +{ + /* Shouldn't occur - all done through function calls */ + wassertl (0, "Division is handled through support function calls"); +} + +/*-----------------------------------------------------------------*/ +/* genMod - generates code for division */ +/*-----------------------------------------------------------------*/ +static void +genMod (const iCode * ic) +{ + /* Shouldn't occur - all done through function calls */ + wassert (0); +} + +/*-----------------------------------------------------------------*/ +/* genIfxJump :- will create a jump depending on the ifx */ +/*-----------------------------------------------------------------*/ +static void +genIfxJump (iCode * ic, char *jval) +{ + symbol *jlbl; + const char *inst; + + /* if true label then we jump if condition + supplied is true */ + if (IC_TRUE (ic)) + { + jlbl = IC_TRUE (ic); + if (!strcmp (jval, "a")) + { + emit3 (A_OR, ASMOP_A, ASMOP_A); + inst = "NZ"; + } + else if (!strcmp (jval, "z")) + { + inst = "Z"; + } + else if (!strcmp (jval, "nz")) + { + inst = "NZ"; + } + else if (!strcmp (jval, "c")) + { + inst = "C"; + } + else if (!strcmp (jval, "nc")) + { + inst = "NC"; + } + else if (!strcmp (jval, "m")) + { + inst = "M"; + } + else if (!strcmp (jval, "p")) + { + inst = "P"; + } + else if (!strcmp (jval, "po")) + { + inst = "PO"; + } + else if (!strcmp (jval, "pe")) + { + inst = "PE"; + } + else + { + /* The buffer contains the bit on A that we should test */ + emit2 ("bit %s, a", jval); + regalloc_dry_run_cost += 2; + inst = "NZ"; + } + } + else + { + /* false label is present */ + jlbl = IC_FALSE (ic); + if (!strcmp (jval, "a")) + { + emit3 (A_OR, ASMOP_A, ASMOP_A); + inst = "Z"; + } + else if (!strcmp (jval, "z")) + { + inst = "NZ"; + } + else if (!strcmp (jval, "nz")) + { + inst = "Z"; + } + else if (!strcmp (jval, "c")) + { + inst = "NC"; + } + else if (!strcmp (jval, "nc")) + { + inst = "C"; + } + else if (!strcmp (jval, "m")) + { + inst = "P"; + } + else if (!strcmp (jval, "p")) + { + inst = "M"; + } + else if (!strcmp (jval, "po")) + { + inst = "PE"; + } + else if (!strcmp (jval, "pe")) + { + inst = "PO"; + } + else + { + /* The buffer contains the bit on A that we should test */ + emit2 ("bit %s, a", jval); + regalloc_dry_run_cost += 2; + inst = "Z"; + } + } + /* Z80 can do a conditional long jump */ + if (!regalloc_dry_run) + emit2 ("jp %s, !tlabel", inst, labelKey2num (jlbl->key)); + regalloc_dry_run_cost += 3; + + /* mark the icode as generated */ + if (!regalloc_dry_run) + ic->generated = 1; +} + +#if DISABLED +static const char * +_getPairIdName (PAIR_ID id) +{ + return _pairs[id].name; +} +#endif + +/** Generic compare for > or < + */ +static void +genCmp (operand * left, operand * right, operand * result, iCode * ifx, int sign, const iCode * ic) +{ + int size, offset = 0; + unsigned long long lit = 0ull; + bool result_in_carry = FALSE; + int a_always_byte = -1; + + /* if left & right are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + /* Can't happen on the Z80 */ + wassertl (0, "Tried to compare two bits"); + } + else + { + /* Do a long subtract of right from left. */ + size = max (AOP_SIZE (left), AOP_SIZE (right)); + + if (AOP_TYPE (right) == AOP_SFR) /* Avoid overwriting A */ + { + bool save_a, save_b, save_bc; + wassertl (size == 1, "Right side sfr in comparison with more than 8 bits."); + + save_b = bitVectBitValue (ic->rSurv, B_IDX); + save_bc = (save_b && bitVectBitValue (ic->rSurv, C_IDX)); + save_a = (aopInReg (left->aop, 0, A_IDX) || + aopInReg (left->aop, 0, B_IDX) && save_b || + aopInReg (left->aop, 0, C_IDX) && !save_b && save_bc); + + if (save_bc) + _push (PAIR_BC); + if (save_a) + { + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + _push (PAIR_AF); + } + else + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + cheapMove (save_b ? ASMOP_C : ASMOP_B, 0, ASMOP_A, 0, true); + if (save_a) + _pop (PAIR_AF); + else + cheapMove (ASMOP_A, 0, AOP (left), 0, true); + emit3_o (A_SUB, ASMOP_A, 0, save_b ? ASMOP_C : ASMOP_B, offset); + if (save_bc) + _pop (PAIR_BC); + result_in_carry = TRUE; + goto fix; + } + + // Preserve A if necessary + if (ifx && size == 1 && !sign && aopInReg (left->aop, 0, A_IDX) && bitVectBitValue (ic->rSurv, A_IDX) && + (AOP_TYPE (right) == AOP_LIT || AOP_TYPE (right) == AOP_REG && AOP (right)->aopu.aop_reg[offset]->rIdx != IYL_IDX && AOP (right)->aopu.aop_reg[offset]->rIdx != IYH_IDX || AOP_TYPE (right) == AOP_STK)) + { + emit3 (A_CP, ASMOP_A, AOP (right)); + result_in_carry = TRUE; + goto release; + } + + // On the Gameboy we can't afford to adjust HL as it may trash the carry. + if (size > 1 && (IS_GB || IY_RESERVED) && left->aop->type != AOP_REG && right->aop->type != AOP_REG && (requiresHL (AOP (right)) && requiresHL (AOP (left)))) + { + if (!isPairDead (PAIR_DE, ic)) + _push (PAIR_DE); + + pointPairToAop (PAIR_DE, left->aop, 0); + pointPairToAop (PAIR_HL, right->aop, 0); + + while (size--) + { + emit2 ("ld a, (de)"); + emit2 ("%s a, (hl)", offset == 0 ? "sub" : "sbc"); + regalloc_dry_run_cost += 2; + + if (size != 0) + { + emit2 ("inc hl"); + emit2 ("inc de"); + regalloc_dry_run_cost += 2; + } + offset++; + } + if (sign && IS_GB) + { + wassert(isPairDead (PAIR_DE, ic)); + emit2 ("ld a, (de)"); + emit2 ("ld d, a"); + emit2 ("ld e, (hl)"); + regalloc_dry_run_cost += 3; + } + + spillPair (PAIR_DE); + if (!isPairDead (PAIR_DE, ic)) + _pop (PAIR_DE); + + spillPair (PAIR_HL); + result_in_carry = TRUE; + goto fix; + } + else if (size > 1 && IS_GB && (requiresHL (AOP (right)) && !requiresHL (AOP (left)))) + { + if (!regalloc_dry_run) + aopGet (AOP (right), LSB, FALSE); + + while (size--) + { + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + emit2 ("%s a, (hl)", offset == 0 ? "sub" : "sbc"); + regalloc_dry_run_cost += 1; + + if (size != 0) + { + emit2 ("inc hl"); + regalloc_dry_run_cost += 1; + } + offset++; + } + if (sign) + { + cheapMove (ASMOP_A, 0, AOP (left), offset - 1, true); + emit2 ("ld d, a"); + emit2 ("ld e, (hl)"); + regalloc_dry_run_cost += 2; + } + spillPair (PAIR_HL); + result_in_carry = TRUE; + goto fix; + } + else if (size > 1 && IS_GB && (!requiresHL (AOP (right)) && requiresHL (AOP (left)))) + { + if (!regalloc_dry_run) + aopGet (AOP (left), LSB, FALSE); + + while (size--) + { + emit2 ("ld a, (hl)"); + regalloc_dry_run_cost += 1; + emit3_o (offset == 0 ? A_SUB : A_SBC, ASMOP_A, 0, AOP (right), offset); + + if (size != 0) + { + emit2 ("inc hl"); + regalloc_dry_run_cost += 1; + } + offset++; + } + if (sign) + { + emit2 ("ld d, (hl)"); + regalloc_dry_run_cost += 1; + cheapMove (ASMOP_A, 0, AOP (right), offset - 1, true); + emit2 ("ld e, a"); + regalloc_dry_run_cost += 1; + } + spillPair (PAIR_HL); + result_in_carry = TRUE; + goto fix; + } + + if (IS_GB && sign && AOP_TYPE (right) != AOP_LIT) + { + cheapMove (ASMOP_A, 0, AOP (right), size - 1, true); + cheapMove (ASMOP_E, 0, ASMOP_A, 0, true); + cheapMove (ASMOP_A, 0, AOP (left), size - 1, true); + cheapMove (ASMOP_D, 0, ASMOP_A, 0, true); + } + + if (AOP_TYPE (right) == AOP_LIT) + { + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + /* optimize if(x < 0) or if(x >= 0) */ + if (lit == 0ull) + { + if (!sign) + { + /* No sign so it's always false */ + emit3 (A_CP, ASMOP_A, ASMOP_A); + result_in_carry = TRUE; + } + else + { + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx && + (AOP_TYPE (left) == AOP_REG || AOP_TYPE (left) == AOP_STK && !IS_GB)) + { + if (!regalloc_dry_run) + emit2 ("bit 7, %s", aopGet (AOP (left), AOP_SIZE (left) - 1, FALSE)); + regalloc_dry_run_cost += ((AOP_TYPE (left) == AOP_REG) ? 2 : 4); + genIfxJump (ifx, "nz"); + return; + } + /* Just load in the top most bit */ + cheapMove (ASMOP_A, 0, AOP (left), AOP_SIZE (left) - 1, true); + if (!(AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) && ifx) + { + genIfxJump (ifx, "7"); + return; + } + else + { + if (ifx) + { + genIfxJump (ifx, "nc"); + return; + } + result_in_carry = FALSE; + } + } + goto release; + } + + while (!((lit >> (offset * 8)) & 0xffull)) + { + size--; + offset++; + } + + if (sign) /* Map signed operands to unsigned ones. This pre-subtraction workaround to lack of signed comparison is cheaper than the post-subtraction one at fix. */ + { + if (size == 2 && !(IS_GB || !ifx && requiresHL(AOP(result)) && AOP_TYPE (result) != AOP_REG) && isPairDead (PAIR_HL, ic) && (isPairDead (PAIR_DE, ic) || isPairDead (PAIR_BC, ic)) && (getPairId (AOP (left)) == PAIR_HL || IS_RAB && (AOP_TYPE (left) == AOP_STK || AOP_TYPE (left) == AOP_EXSTK))) + { + PAIR_ID litpair = (isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC); + fetchPair (PAIR_HL, AOP (left)); + emit2 ("ld %s, !immedbyte", _pairs[litpair].name, (unsigned long) ((lit ^ 0x8000u) & 0xffffu)); + regalloc_dry_run_cost += 3; + emit2 ("add hl, hl"); + emit2 ("ccf"); + regalloc_dry_run_cost += 2; + if (IS_RAB) + { + emit2 ("rr hl"); + regalloc_dry_run_cost += 1; + } + else + { + emit2 ("rr h"); + emit2 ("rr l"); + regalloc_dry_run_cost += 2; + } + emit2 ("sbc hl, %s", _pairs[litpair].name); + regalloc_dry_run_cost += 2; + result_in_carry = TRUE; + goto release; + } + + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + if (size == 1) + { + emit2 ("xor a, !immedbyte", 0x80); + regalloc_dry_run_cost += 2; + } + emit2 ("sub a, !immedbyte", (unsigned long) (((lit >> (offset * 8)) & 0xff) ^ (size == 1 ? 0x80 : 0x00))); + regalloc_dry_run_cost += 2; + size--; + offset++; + + while (size--) + { + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + if (!size) + { + emit2 ("rla"); + emit2 ("ccf"); + emit2 ("rra"); + regalloc_dry_run_cost += 3; + } + /* Subtract through, propagating the carry */ + emit2 ("sbc a, !immedbyte", (unsigned long) (((lit >> (offset++ * 8)) & 0xff) ^ (size ? 0x00 : 0x80))); + regalloc_dry_run_cost += 2; + } + result_in_carry = TRUE; + goto release; + } + } + + if (!IS_GB && (!sign || size > 2) && getPartPairId(AOP (left), offset) == PAIR_HL && isPairDead (PAIR_HL, ic) && + (getPartPairId (AOP (right), offset) == PAIR_DE || getPartPairId(AOP (right), offset) == PAIR_BC)) + { + emit3 (A_CP, ASMOP_A, ASMOP_A); // Clear carry. + emit2 ("sbc hl, %s", _pairs[getPartPairId (AOP (right), offset)].name); + regalloc_dry_run_cost += 2; + size -= 2; + offset += 2; + } + else + { + if (AOP_TYPE (left) == AOP_LIT && byteOfVal (AOP (left)->aopu.aop_lit, offset) == 0x00) + emit3 (A_XOR, ASMOP_A, ASMOP_A); + else + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + if (size > 1 && AOP_TYPE (left) == AOP_LIT) + { + emit3_o (A_CP, ASMOP_A, 0, AOP (right), offset); + a_always_byte = byteOfVal (AOP (left)->aopu.aop_lit, offset); + } + else + emit3_o (A_SUB, ASMOP_A, 0, AOP (right), offset); + size--; + offset++; + } + + /* Subtract through, propagating the carry */ + while (size) + { + if (!IS_GB && (!sign || size > 2) && + isPairDead (PAIR_HL, ic) && + (getPartPairId (left->aop, offset) == PAIR_HL || left->aop->type == AOP_LIT && right->aop->regs[L_IDX] < offset && right->aop->regs[H_IDX] < offset) && + (getPartPairId (right->aop, offset) == PAIR_DE || getPartPairId (right->aop, offset) == PAIR_BC)) + { + fetchPairLong (PAIR_HL, left->aop, 0, offset); + emit2 ("sbc hl, %s", _pairs[getPartPairId (AOP (right), offset)].name); + regalloc_dry_run_cost += 2; + size -= 2; + offset += 2; + } + else + { + if (!(left->aop->type == AOP_LIT && byteOfVal (left->aop->aopu.aop_lit, offset) == a_always_byte)) + cheapMove (ASMOP_A, 0, left->aop, offset, true); + a_always_byte = -1; + emit3_o (A_SBC, ASMOP_A, 0, right->aop, offset); + size--; + offset++; + } + } + +fix: + /* There is no good signed compare in the Z80, so we need workarounds */ + if (sign) + { + if (!IS_GB) /* Directly check for overflow, can't be done on GBZ80 */ + { + if (!regalloc_dry_run) + { + symbol *tlbl = newiTempLabel (NULL); + emit2 (IS_RAB ? "jp LZ, !tlabel": "jp PO, !tlabel", labelKey2num (tlbl->key)); + emit2 ("xor a, !immedbyte", 0x80); + emitLabelSpill (tlbl); + } + regalloc_dry_run_cost += 5; + result_in_carry = FALSE; + } + else /* Do it the hard way */ + { + /* Test if one operand is negative, while the other is not. If this is the + case we can easily decide which one is greater, and we set/reset the carry + flag. If not, then the unsigned compare gave the correct result and we + don't change the carry flag. */ + if (!regalloc_dry_run) + { + symbol *tlbl1 = newiTempLabel (NULL); + symbol *tlbl2 = newiTempLabel (NULL); + emit2 ("bit 7, e"); + emit2 ("jp Z, !tlabel", labelKey2num (tlbl1->key)); + emit2 ("bit 7, d"); + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl2->key)); + emit2 ("cp a, a"); + emit2 ("jp !tlabel", labelKey2num (tlbl2->key)); + emitLabelSpill (tlbl1); + emit2 ("bit 7, d"); + emit2 ("jp Z, !tlabel", labelKey2num (tlbl2->key)); + emit2 ("scf"); + emitLabelSpill (tlbl2); + } + regalloc_dry_run_cost += 20; + result_in_carry = TRUE; + } + } + else + result_in_carry = TRUE; + } + +release: + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + if (!result_in_carry) + { + /* Shift the sign bit up into carry */ + emit2 ("rlca"); + regalloc_dry_run_cost += 1; + } + outBitC (result); + } + else + { + /* if the result is used in the next + ifx conditional branch then generate + code a little differently */ + if (ifx) + { + if (!result_in_carry) + { + if (!IS_GB) + genIfxJump (ifx, "m"); + else + { + emit2 ("rlca"); + regalloc_dry_run_cost += 1; + genIfxJump (ifx, "c"); + } + } + else + genIfxJump (ifx, "c"); + } + else + { + if (!result_in_carry) + { + /* Shift the sign bit up into carry */ + emit2 ("rlca"); + regalloc_dry_run_cost += 1; + } + outBitC (result); + } + /* leave the result in acc */ + } +} + +/*-----------------------------------------------------------------*/ +/* genCmpGt :- greater than comparison */ +/*-----------------------------------------------------------------*/ +static void +genCmpGt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the asmops */ + aopOp (left, ic, FALSE, FALSE); + aopOp (right, ic, FALSE, FALSE); + aopOp (result, ic, TRUE, FALSE); + + setupToPreserveCarry (AOP (result), AOP (left), AOP (right)); + + genCmp (right, left, result, ifx, sign, ic); + + _G.preserveCarry = FALSE; + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genCmpLt - less than comparisons */ +/*-----------------------------------------------------------------*/ +static void +genCmpLt (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + sym_link *letype, *retype; + int sign; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + + sign = 0; + if (IS_SPEC (operandType (left)) && IS_SPEC (operandType (right))) + { + letype = getSpec (operandType (left)); + retype = getSpec (operandType (right)); + sign = !(SPEC_USIGN (letype) | SPEC_USIGN (retype)); + } + + /* assign the asmops */ + aopOp (left, ic, FALSE, FALSE); + aopOp (right, ic, FALSE, FALSE); + aopOp (result, ic, TRUE, FALSE); + + setupToPreserveCarry (AOP (result), AOP (left), AOP (right)); + + genCmp (left, right, result, ifx, sign, ic); + + _G.preserveCarry = FALSE; + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* gencjneshort - compare and jump if not equal */ +/* returns pair that still needs to be popped */ +/*-----------------------------------------------------------------*/ +static PAIR_ID +gencjneshort (operand *left, operand *right, symbol *lbl, const iCode *ic) +{ + int size = max (AOP_SIZE (left), AOP_SIZE (right)); + int offset = 0; + bool a_result = FALSE; + bool next_zero; + + /* Swap the left and right if it makes the computation easier */ + if (AOP_TYPE (left) == AOP_LIT || aopInReg (right->aop, 0, A_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + /* Non-destructive compare */ + if (aopInReg (left->aop, 0, A_IDX) && bitVectBitValue (ic->rSurv, A_IDX) && + (AOP_TYPE (right) == AOP_LIT || AOP_TYPE (right) == AOP_REG && AOP (right)->aopu.aop_reg[offset]->rIdx != IYL_IDX && AOP (right)->aopu.aop_reg[offset]->rIdx != IYH_IDX || AOP_TYPE (right) == AOP_STK)) + { + if (AOP_TYPE (right) == AOP_LIT && !byteOfVal (AOP (right)->aopu.aop_lit, 0)) + emit3 (A_OR, ASMOP_A, ASMOP_A); + else + emit3 (A_CP, ASMOP_A, AOP (right)); + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + } + /* if the right side is a literal then anything goes */ + else if (AOP_TYPE (right) == AOP_LIT) + { + while (size--) + { + next_zero = size && !byteOfVal (AOP (right)->aopu.aop_lit, offset + 1); + + // Test for 0 can be done more efficiently using or + if (!byteOfVal (AOP (right)->aopu.aop_lit, offset)) + { + if (!a_result) + { + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + emit3 (A_OR, ASMOP_A, ASMOP_A); + } + else + emit3_o (A_OR, ASMOP_A, 0, AOP (left), offset); + + a_result = TRUE; + } + else if ((aopInReg (left->aop, 0, A_IDX) && !bitVectBitValue (ic->rSurv, A_IDX) || + AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && AOP (left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && !bitVectBitValue (ic->rSurv, AOP (left)->aopu.aop_reg[offset]->rIdx)) && + byteOfVal (AOP (right)->aopu.aop_lit, offset) == 0x01 && !next_zero) + { + if(!regalloc_dry_run) + emit2 ("dec %s", aopGet (AOP (left), offset, FALSE)); + regalloc_dry_run_cost++; + a_result = aopInReg (left->aop, 0, A_IDX); + } + else if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] < offset && size && byteOfVal (right->aop->aopu.aop_lit, offset) == 0xff && + (left->aop->type == AOP_REG || left->aop->type == AOP_STK) && + byteOfVal (right->aop->aopu.aop_lit, offset) == byteOfVal (right->aop->aopu.aop_lit, offset + 1)) + { + cheapMove (ASMOP_A, 0, left->aop, offset, true); + while (byteOfVal (right->aop->aopu.aop_lit, offset + 1) == 0xff && size) + { + emit3_o (A_AND, ASMOP_A, 0, left->aop, ++offset); + size--; + } + emit3 (A_INC, ASMOP_A, 0); + next_zero = size && !byteOfVal (AOP (right)->aopu.aop_lit, offset + 1); + a_result = true; + } + else if ((aopInReg (left->aop, 0, A_IDX) && !bitVectBitValue (ic->rSurv, A_IDX) || + AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[offset]->rIdx != IYL_IDX && AOP (left)->aopu.aop_reg[offset]->rIdx != IYH_IDX && !bitVectBitValue (ic->rSurv, AOP (left)->aopu.aop_reg[offset]->rIdx)) && + byteOfVal (AOP (right)->aopu.aop_lit, offset) == 0xff && !next_zero) + { + if(!regalloc_dry_run) + emit2 ("inc %s", aopGet (AOP (left), offset, FALSE)); + regalloc_dry_run_cost++; + a_result = aopInReg (left->aop, 0, A_IDX); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, offset, true); + + if (byteOfVal (right->aop->aopu.aop_lit, offset) == 0x01) + emit3 (A_DEC, ASMOP_A, 0); + else if (byteOfVal (right->aop->aopu.aop_lit, offset) == 0xff) + emit3 (A_INC, ASMOP_A, 0); + else + emit3_o (A_SUB, ASMOP_A, 0, right->aop, offset); + + a_result = true; + } + + // Only emit jump now if there is no following test for 0 (which would just or to a current result in a) + if (!(next_zero && a_result)) + { + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + } + offset++; + } + } + /* if the right side is in a register or + pointed to by HL, IX or IY */ + else if (AOP_TYPE (right) == AOP_REG || + AOP_TYPE (right) == AOP_HL || + AOP_TYPE (right) == AOP_IY || + AOP_TYPE (right) == AOP_STK || + AOP_TYPE (right) == AOP_EXSTK || + AOP_TYPE (right) == AOP_IMMD || + AOP_IS_PAIRPTR (right, PAIR_HL) || AOP_IS_PAIRPTR (right, PAIR_IX) || AOP_IS_PAIRPTR (right, PAIR_IY)) + { + while (size--) + { + if (aopInReg (right->aop, offset, A_IDX) || aopInReg (right->aop, offset, HL_IDX) || aopInReg (left->aop, offset, BC_IDX) || aopInReg (left->aop, offset, DE_IDX)) + { + operand *t = right; + right = left; + left = t; + } + + if (!IS_GB && isPairDead (PAIR_HL, ic) && + (aopInReg (left->aop, offset, HL_IDX) && (aopInReg (right->aop, offset, BC_IDX) || aopInReg (right->aop, offset, DE_IDX) || getFreePairId (ic) != PAIR_INVALID) || + size == 1 && (aopInReg (right->aop, offset, BC_IDX) || aopInReg (right->aop, offset, DE_IDX)))) + { + PAIR_ID pair = getPairId_o (right->aop, offset); + if (pair == PAIR_INVALID) + pair = getFreePairId (ic); + + fetchPairLong (PAIR_HL, left->aop, ic, offset); + fetchPairLong (pair, right->aop, 0, offset); + emit3 (A_CP, ASMOP_A, ASMOP_A); + emit2 ("sbc hl, %s", _pairs[pair].name); + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 5; + + offset += 2; + size--; + continue; + } + + cheapMove (ASMOP_A, 0, AOP (left), offset, true); + if (AOP_TYPE (right) == AOP_LIT && byteOfVal (AOP (right)->aopu.aop_lit, offset) == 0) + { + emit3 (A_OR, ASMOP_A, ASMOP_A); + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + } + else + { + emit3_o (A_SUB, ASMOP_A, 0, AOP (right), offset); + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + } + offset++; + } + } + /* right is in direct space or a pointer reg, need both a & b */ + else + { + PAIR_ID pair; + for (pair = PAIR_BC; pair <= PAIR_HL; pair++) + { + if (((AOP_TYPE (left) != AOP_PAIRPTR) || (AOP (left)->aopu.aop_pairId != pair)) && + ((AOP_TYPE (right) != AOP_PAIRPTR) || (AOP (right)->aopu.aop_pairId != pair))) + { + break; + } + } + _push (pair); + while (size--) + { + if (!regalloc_dry_run) + _emitMove (_pairs[pair].l, aopGet (AOP (left), offset, FALSE)); + else + regalloc_dry_run_cost += ld_cost (ASMOP_E, AOP (left)); + cheapMove (ASMOP_A, 0, AOP (right), offset, true); + emit2 ("sub a,%s", _pairs[pair].l); + regalloc_dry_run_cost += 1; + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + offset++; + } + return pair; + } + return PAIR_INVALID; +} + +/*-----------------------------------------------------------------*/ +/* gencjne - compare and jump if not equal */ +/*-----------------------------------------------------------------*/ +static void +gencjne (operand * left, operand * right, symbol * lbl, const iCode *ic) +{ + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + PAIR_ID pop; + + pop = gencjneshort (left, right, lbl, ic); + + /* PENDING: ?? */ + if (!regalloc_dry_run) + { + emit2 ("ld a,!one"); + emit2 ("jp !tlabel", labelKey2num (tlbl->key)); + emitLabelSpill (lbl); + emit2 ("xor a,a"); + emitLabel (tlbl); + } + regalloc_dry_run_cost += 6; + _pop (pop); +} + +/*-----------------------------------------------------------------*/ +/* genCmpEq - generates code for equal to */ +/*-----------------------------------------------------------------*/ +static void +genCmpEq (iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + bool hl_touched; + + aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE); + + hl_touched = (AOP_TYPE (IC_LEFT (ic)) == AOP_HL || AOP_TYPE (IC_RIGHT (ic)) == AOP_HL || IS_GB + && AOP_TYPE (IC_LEFT (ic)) == AOP_STK || IS_GB && AOP_TYPE (IC_RIGHT (ic)) == AOP_STK); + + /* Swap operands if it makes the operation easier. ie if: + 1. Left is a literal. + */ + if (AOP_TYPE (IC_LEFT (ic)) == AOP_LIT || AOP_TYPE (IC_RIGHT (ic)) != AOP_LIT && AOP_TYPE (IC_RIGHT (ic)) != AOP_REG + && AOP_TYPE (IC_LEFT (ic)) == AOP_REG) + { + operand *t = IC_RIGHT (ic); + IC_RIGHT (ic) = IC_LEFT (ic); + IC_LEFT (ic) = t; + } + + if (ifx && !AOP_SIZE (result)) + { + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + wassertl (0, "Tried to compare two bits"); + } + else + { + PAIR_ID pop; + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + pop = gencjneshort (left, right, tlbl, ic); + if (IC_TRUE (ifx)) + { + if (pop != PAIR_INVALID) + { + emit2 ("pop %s", _pairs[pop].name); + regalloc_dry_run_cost += 1; + } + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (IC_TRUE (ifx)->key)); + regalloc_dry_run_cost += 3; + if (!regalloc_dry_run) + hl_touched ? emitLabelSpill (tlbl) : emitLabel (tlbl); + else if (hl_touched) + spillCached (); + _pop (pop); + } + else + { + /* PENDING: do this better */ + symbol *lbl = regalloc_dry_run ? 0 : newiTempLabel (0); + if (pop != PAIR_INVALID) + { + emit2 ("pop %s", _pairs[pop].name); + regalloc_dry_run_cost += 1; + } + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (lbl->key)); + regalloc_dry_run_cost += 3; + if (!regalloc_dry_run) + hl_touched ? emitLabelSpill (tlbl) : emitLabel (tlbl); + else if (hl_touched) + spillCached (); + _pop (pop); + if (!regalloc_dry_run) + { + emit2 ("jp !tlabel", labelKey2num (IC_FALSE (ifx)->key)); + emitLabel (lbl); + } + regalloc_dry_run_cost += 3; + } + } + /* mark the icode as generated */ + ifx->generated = 1; + goto release; + } + + /* if they are both bit variables */ + if (AOP_TYPE (left) == AOP_CRY && ((AOP_TYPE (right) == AOP_CRY) || (AOP_TYPE (right) == AOP_LIT))) + { + wassertl (0, "Tried to compare a bit to either a literal or another bit"); + } + else + { + gencjne (left, right, regalloc_dry_run ? 0 : newiTempLabel (NULL), ic); + if (AOP_TYPE (result) == AOP_CRY && AOP_SIZE (result)) + { + wassert (0); + } + if (ifx) + { + genIfxJump (ifx, "a"); + goto release; + } + /* if the result is used in an arithmetic operation + then put the result in place */ + if (AOP_TYPE (result) != AOP_CRY) + outAcc (result); + /* leave the result in acc */ + } + +release: + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genAndOp - for && operation */ +/*-----------------------------------------------------------------*/ +static void +genAndOp (const iCode * ic) +{ + operand *left, *right, *result; + + /* note here that && operations that are in an if statement are + taken away by backPatchLabels only those used in arthmetic + operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE, TRUE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, TRUE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + wassertl (0, "Tried to and two bits"); + } + else + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + _toBoolean (left, TRUE); + if (!regalloc_dry_run) + emit2 ("jp Z,!tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + _toBoolean (right, FALSE); + if (!regalloc_dry_run) + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genOrOp - for || operation */ +/*-----------------------------------------------------------------*/ +static void +genOrOp (const iCode * ic) +{ + operand *left, *right, *result; + + /* note here that || operations that are in an + if statement are taken away by backPatchLabels + only those used in arthmetic operations remain */ + aopOp ((left = IC_LEFT (ic)), ic, FALSE, TRUE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, TRUE); + aopOp ((result = IC_RESULT (ic)), ic, FALSE, FALSE); + + /* if both are bit variables */ + if (AOP_TYPE (left) == AOP_CRY && AOP_TYPE (right) == AOP_CRY) + { + wassertl (0, "Tried to OR two bits"); + } + else + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + _toBoolean (left, TRUE); + if (!regalloc_dry_run) + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + _toBoolean (right, FALSE); + if (!regalloc_dry_run) + emitLabel (tlbl); + outBitAcc (result); + } + + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* isLiteralBit - test if lit == 2^n */ +/*-----------------------------------------------------------------*/ +static int +isLiteralBit (unsigned long lit) +{ + unsigned long pw[32] = + { + 1L, 2L, 4L, 8L, 16L, 32L, 64L, 128L, + 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, + 0x10000L, 0x20000L, 0x40000L, 0x80000L, + 0x100000L, 0x200000L, 0x400000L, 0x800000L, + 0x1000000L, 0x2000000L, 0x4000000L, 0x8000000L, + 0x10000000L, 0x20000000L, 0x40000000L, 0x80000000L + }; + int idx; + + for (idx = 0; idx < 32; idx++) + if (lit == pw[idx]) + return idx; + return -1; +} + +/*-----------------------------------------------------------------*/ +/* jmpTrueOrFalse - */ +/*-----------------------------------------------------------------*/ +static void +jmpTrueOrFalse (iCode * ic, symbol * tlbl) +{ + // ugly but optimized by peephole + // Using emitLabelSpill instead of emitLabel (esp. on gbz80) + // We could jump there from locations with different values in hl. + // This should be changed to a more efficient solution that spills + // only what and when necessary. + if (IC_TRUE (ic)) + { + if (!regalloc_dry_run) + { + symbol *nlbl = newiTempLabel (NULL); + emit2 ("jp !tlabel", labelKey2num (nlbl->key)); + emitLabelSpill (tlbl); + emit2 ("jp !tlabel", labelKey2num (IC_TRUE (ic)->key)); + emitLabelSpill (nlbl); + } + regalloc_dry_run_cost += 6; + } + else + { + if (!regalloc_dry_run) + { + emit2 ("jp !tlabel", labelKey2num (IC_FALSE (ic)->key)); + emitLabelSpill (tlbl); + } + regalloc_dry_run_cost += 3; + } + if (!regalloc_dry_run) + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAnd - code for and */ +/*-----------------------------------------------------------------*/ +static void +genAnd (const iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0L; + unsigned int bytelit = 0; + + aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE); + + bool pushed_a = false; + bool a_free = !bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (right) && !AOP_NEEDSACC (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (sameRegs (AOP (result), AOP (right)) && !AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + if (AOP_TYPE (left) == AOP_CRY) + { + wassertl (0, "Tried to perform an AND with a bit as an operand"); + goto release; + } + + /* Make sure A is on the left to not overwrite it. */ + if (aopInReg (right->aop, 0, A_IDX) || + !aopInReg (left->aop, 0, A_IDX) && isPair (AOP (right)) && (getPairId (AOP (right)) == PAIR_HL || getPairId (AOP (right)) == PAIR_IY)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + int sizel; + + if (!a_free) // Hard to handle pop with ifx + { + regalloc_dry_run_cost += 100; + wassert (regalloc_dry_run); + } + + sizel = AOP_SIZE (left); + if (size) + { + /* PENDING: Test case for this. */ + emit2 ("scf"); + regalloc_dry_run_cost += 1; + } + while (sizel) + { + char *jumpcond = "NZ"; + + if ((bytelit = ((lit >> (offset * 8)) & 0x0ffull)) == 0x00ull) + { + sizel--; + offset++; + continue; + } + + /* Testing for the border bits of the accumulator destructively is cheap. */ + if ((isLiteralBit (bytelit) == 0 || isLiteralBit (bytelit) == 7) && aopInReg (left->aop, 0, A_IDX) && !bitVectBitValue (ic->rSurv, A_IDX)) + { + emit3 (isLiteralBit (bytelit) == 0 ? A_RRCA : A_RLCA, 0 , 0); + jumpcond = "C"; + sizel--; + offset++; + } + /* Testing for the inverse of the border bits of some 32-bit registers destructively is cheap. */ + /* More combinations would be possible, but this one is the one that is common in the floating-point library. */ + else if (AOP_TYPE (left) == AOP_REG && sizel >= 4 && ((lit >> (offset * 8)) & 0xffffffffull) == 0x7fffffffull && + !IS_GB && getPartPairId (AOP (left), offset) == PAIR_HL && isPairDead (PAIR_HL, ic) && + IS_RAB && getPartPairId (AOP (left), offset + 2) == PAIR_DE && isPairDead (PAIR_HL, ic)) + { + emit3 (A_CP, ASMOP_A, ASMOP_A); // Clear carry. + emit2 ("adc hl, hl"); // Cannot use "add hl, hl instead, since it does not affect zero flag. + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (tlbl->key)); + emit2 ("rl de"); + regalloc_dry_run_cost += 6; + sizel -= 4; + offset += 4; + } + /* Testing for the inverse of the border bits of some 16-bit registers destructively is cheap. */ + /* More combinations would be possible, but these are the common ones. */ + else if (AOP_TYPE (left) == AOP_REG && sizel >= 2 && ((lit >> (offset * 8)) & 0xffffull) == 0x7fffull && + (!IS_GB && getPartPairId (AOP (left), offset) == PAIR_HL && isPairDead (PAIR_HL, ic) || + IS_RAB && getPartPairId (AOP (left), offset) == PAIR_DE && isPairDead (PAIR_DE, ic))) + { + PAIR_ID pair; + switch (AOP (left)->aopu.aop_reg[offset]->rIdx) + { + case L_IDX: + case H_IDX: + pair = PAIR_HL; + break; + case E_IDX: + case D_IDX: + pair = PAIR_DE; + break; + default: + pair = PAIR_INVALID; + wassertl (0, "Invalid pair"); + } + emit3 (A_CP, ASMOP_A, ASMOP_A); // Clear carry. + if (pair == PAIR_HL) + emit2 ("adc %s, %s", _pairs[pair].name, _pairs[pair].name); // Cannot use "add hl, hl instead, since it does not affect zero flag. + else + emit2 ("rl %s", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_DE ? 1 : 2); + sizel -= 2; + offset += 2; + } + /* Testing for the border bits of some 16-bit registers destructively is cheap. */ + else if (AOP_TYPE (left) == AOP_REG && sizel == 1 && + (isLiteralBit (bytelit) == 7 && ( + AOP (left)->aopu.aop_reg[offset]->rIdx == H_IDX && isPairDead (PAIR_HL, ic) || + IS_RAB && AOP (left)->aopu.aop_reg[offset]->rIdx == D_IDX && isPairDead (PAIR_DE, ic) || + AOP (left)->aopu.aop_reg[offset]->rIdx == IYH_IDX && isPairDead (PAIR_IY, ic) + ) || + isLiteralBit (bytelit) == 0 && IS_RAB && ( + AOP (left)->aopu.aop_reg[offset]->rIdx == L_IDX && isPairDead (PAIR_HL, ic) || + AOP (left)->aopu.aop_reg[offset]->rIdx == E_IDX && isPairDead (PAIR_DE, ic) || + AOP (left)->aopu.aop_reg[offset]->rIdx == IYL_IDX && isPairDead (PAIR_IY, ic) + ))) + { + PAIR_ID pair; + switch (AOP (left)->aopu.aop_reg[offset]->rIdx) + { + case L_IDX: + case H_IDX: + pair = PAIR_HL; + break; + case E_IDX: + case D_IDX: + pair = PAIR_DE; + break; + case IYL_IDX: + case IYH_IDX: + pair = PAIR_IY; + break; + default: + pair = PAIR_INVALID; + wassertl (0, "Invalid pair"); + } + if ((pair == PAIR_HL || pair == PAIR_IY) && isLiteralBit (bytelit) == 7) + emit2 ("add %s, %s", _pairs[pair].name, _pairs[pair].name); + else if (isLiteralBit (bytelit) == 7) + emit2 ("rl %s", _pairs[pair].name); + else + emit2 ("rr %s", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IY ? 2 : 1); + jumpcond = "C"; + sizel--; + offset++; + } + /* Non-destructive and when exactly one bit per byte is set. */ + else if (isLiteralBit (bytelit) >= 0 && + (AOP_TYPE (left) == AOP_STK || aopInReg (left->aop, 0, A_IDX) || AOP_TYPE (left) == AOP_HL || AOP_TYPE (left) == AOP_IY || AOP_TYPE (left) == AOP_REG && AOP (left)->aopu.aop_reg[0]->rIdx != IYL_IDX)) + { + if (!regalloc_dry_run) + emit2 ("bit %d, %s", isLiteralBit (bytelit), aopGet (AOP (left), offset, FALSE)); + regalloc_dry_run_cost += (AOP_TYPE (left) == AOP_STK || AOP_TYPE (left) == AOP_IY) ? 4 : 2; + sizel--; + offset++; + } + /* Z180 has non-destructive and. */ + else if ((IS_Z180 || IS_EZ80_Z80) && aopInReg (left->aop, 0, A_IDX) && bitVectBitValue (ic->rSurv, A_IDX) && bytelit != 0x0ff) + { + if (!regalloc_dry_run) + emit2 ("tst a, %s", aopGet (AOP (right), 0, FALSE)); + regalloc_dry_run_cost += (AOP_TYPE (right) == AOP_LIT ? 3 : 2); + sizel--; + offset++; + } + /* Generic case, loading into accumulator and testing there. */ + else + { + cheapMove (ASMOP_A, 0, left->aop, offset, true); + if (isLiteralBit (bytelit) == 0 || isLiteralBit (bytelit) == 7) + { + emit3 (isLiteralBit (bytelit) == 0 ? A_RRCA : A_RLCA, 0 , 0); + jumpcond = "C"; + } + else if (bytelit != 0xffu) + emit3_o (A_AND, ASMOP_A, 0, AOP (right), offset); + else + emit3 (A_OR, ASMOP_A, ASMOP_A); /* For the flags */ + sizel--; + offset++; + } + if (size || ifx) /* emit jmp only, if it is actually used */ + { + if (!regalloc_dry_run) + emit2 ("jp %s,!tlabel", jumpcond, labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + } + } + // bit = left & literal + if (size) + { + emit2 ("clr c"); + if (!regalloc_dry_run) + emit2 ("!tlabeldef", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + genLine.lineCurr->isLabel = 1; + } + // if(left & literal) + else + { + if (ifx) + jmpTrueOrFalse (ifx, tlbl); + goto release; + } + outBitC (result); + goto release; + } + + if (IS_RAB && isPair (AOP (result)) && + (getPairId (AOP (result)) == PAIR_HL && isPair (AOP (right)) && getPairId (AOP (right)) == PAIR_DE || + getPairId (AOP (result)) == PAIR_HL && isPair (AOP (left)) && getPairId (AOP (left)) == PAIR_DE || + isPair (AOP (left)) && getPairId (AOP (left)) == PAIR_IY && getPairId (AOP (result)) == PAIR_IY && isPair (AOP (right)) + && getPairId (AOP (right)) == PAIR_DE)) + { + if (isPair (AOP (left)) && getPairId (AOP (left)) == PAIR_DE) + fetchPair (PAIR_HL, AOP (right)); + else /* right operand in DE */ + fetchPair (getPairId (AOP (result)), AOP (left)); + emit2 ("and hl, de"); + regalloc_dry_run_cost += (getPairId (AOP (result)) == PAIR_HL ? 1 : 2); + goto release; + } + + wassertl (AOP_TYPE (result) != AOP_CRY, "Result of and is in a bit"); + + for (int i = 0; i < size;) + { + if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= i && right->aop->regs[A_IDX] <= i && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= i)) + a_free = true; + + if (pushed_a && (aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX))) + { + _pop (PAIR_AF); + if (bitVectBitValue (ic->rSurv, A_IDX)) + _push (PAIR_AF); + else + pushed_a = false; + } + + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = byteOfVal (right->aop->aopu.aop_lit, i); + + if (bytelit == 0x00) + { + cheapMove (result->aop, i, ASMOP_ZERO, 0, a_free); + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } + else if (bytelit == 0xff) + { + cheapMove (result->aop, i, AOP (left), i, a_free); + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } + else if (isLiteralBit (~bytelit & 0xffu) >= 0 && + (AOP_TYPE (result) == AOP_REG || left == right && (AOP_TYPE (result) == AOP_STK || AOP_TYPE (result) == AOP_DIR))) + { + cheapMove (result->aop, i, left->aop, i, a_free); + if (!regalloc_dry_run) + emit2 ("res %d, %s", isLiteralBit (~bytelit & 0xffu), aopGet (result->aop, i, false)); + regalloc_dry_run_cost += 2; + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } + else if (IS_RAB && + (aopInReg (result->aop, i, HL_IDX) && (aopInReg (left->aop, i, HL_IDX) && !isPairInUse (PAIR_DE, ic) || aopInReg (left->aop, i, DE_IDX)) || + aopInReg (result->aop, i, H_IDX) && aopInReg (result->aop, i + 1, L_IDX) && (aopInReg (left->aop, i, H_IDX) && aopInReg (left->aop, i + 1, L_IDX) && !isPairInUse (PAIR_DE, ic) || aopInReg (left->aop, i, D_IDX) && aopInReg (left->aop, i + 1, E_IDX)))) + { + unsigned short mask = aopInReg (result->aop, i, L_IDX) ? (bytelit + (byteOfVal (right->aop->aopu.aop_lit, i + 1) << 8)) : (byteOfVal (right->aop->aopu.aop_lit, i + 1) + (bytelit << 8)); + bool mask_in_de = (aopInReg (left->aop, i, L_IDX) | aopInReg (left->aop, i, H_IDX)); + emit2 (mask_in_de ? "ld de, !immedword" : "ld hl, !immedword", mask); + emit2 ("and hl, de"); + regalloc_dry_run_cost += 4; + i += 2; + continue; + } + } + + if (IS_RAB) + { + const bool this_byte_l = aopInReg (result->aop, i, L_IDX) && + (aopInReg (left->aop, i, L_IDX) && aopInReg (left->aop, i, E_IDX) || aopInReg (left->aop, i, E_IDX) && aopInReg (left->aop, i, L_IDX)); + const bool this_byte_h = aopInReg (result->aop, i, H_IDX) && + (aopInReg (left->aop, i, H_IDX) && aopInReg (left->aop, i, D_IDX) || aopInReg (left->aop, i, D_IDX) && aopInReg (left->aop, i, H_IDX)); + const bool next_byte_l = aopInReg (result->aop, i + 1, L_IDX) && + (aopInReg (left->aop, i + 1, L_IDX) && aopInReg (left->aop, i + 1, E_IDX) || aopInReg (left->aop, i + 1, E_IDX) && aopInReg (left->aop, i + 1, L_IDX)); + const bool next_byte_h = aopInReg (result->aop, i + 1, H_IDX) && + (aopInReg (left->aop, i + 1, H_IDX) && aopInReg (left->aop, i + 1, D_IDX) || aopInReg (left->aop, i + 1, D_IDX) && aopInReg (left->aop, i + 1, H_IDX)); + + const bool this_byte = this_byte_l || this_byte_h; + const bool next_byte = next_byte_l || next_byte_h; + + const bool next_byte_unused = !bitVectBitValue (ic->rMask, this_byte_l ? H_IDX : L_IDX); + + if (this_byte && (next_byte || next_byte_unused)) + { + emit2 ("and hl, de"); + regalloc_dry_run_cost++; + i += (1 + next_byte); + continue; + } + } + + if (!a_free) + { + wassert (!pushed_a); + _push (PAIR_AF); + pushed_a = true; + a_free = true; + } + + // Use plain and in a. + if (aopInReg (right->aop, i, A_IDX)) + emit3_o (A_AND, ASMOP_A, 0, left->aop, i); + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true); + emit3_o (A_AND, ASMOP_A, 0, right->aop, i); + } + cheapMove (result->aop, i, ASMOP_A, 0, true); + + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + + i++; + } + if (pushed_a) + _pop (PAIR_AF); + +release: + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genOr - code for or */ +/*-----------------------------------------------------------------*/ +static void +genOr (const iCode * ic, iCode * ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0; + int bytelit = 0; + + aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE); + + bool pushed_a = false; + bool a_free = !bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (right) && !AOP_NEEDSACC (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (sameRegs (AOP (result), AOP (right)) && !AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + if (AOP_TYPE (left) == AOP_CRY) + { + wassertl (0, "Tried to OR where left is a bit"); + goto release; + } + + /* Make sure A is on the left to not overwrite it. */ + if (aopInReg (right->aop, 0, A_IDX)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + // if(val | 0xZZ) - size = 0, ifx != FALSE - + // bit = val | 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + int sizel; + + sizel = AOP_SIZE (left); + + if (size) + { + wassertl (0, "Result is assigned to a bit"); + } + /* PENDING: Modeled after the AND code which is inefficient. */ + while (sizel--) + { + if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= offset && right->aop->regs[A_IDX] <= offset && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (!a_free) // Hard to handle pop with ifx + { + regalloc_dry_run_cost += 100; + wassert (regalloc_dry_run); + } + + bytelit = (lit >> (offset * 8)) & 0x0FFull; + + cheapMove (ASMOP_A, 0, left->aop, offset, true); + + if (bytelit != 0) + emit3_o (A_OR, ASMOP_A, 0, AOP (right), offset); + else if (ifx) + { + /* For the flags */ + emit3 (A_OR, ASMOP_A, ASMOP_A); + } + + if (ifx) /* emit jmp only, if it is actually used */ + { + if (!regalloc_dry_run) + emit2 ("jp NZ,!tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + } + + offset++; + } + if (ifx) + { + jmpTrueOrFalse (ifx, tlbl); + } + goto release; + } + + wassertl (AOP_TYPE (result) != AOP_CRY, "Result of or is in a bit"); + + for (int i = 0; i < size;) + { + if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= i && right->aop->regs[A_IDX] <= i && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= i)) + a_free = true; + + if (pushed_a && (aopInReg (left->aop, i, A_IDX) || aopInReg (right->aop, i, A_IDX))) + { + _pop (PAIR_AF); + if (bitVectBitValue (ic->rSurv, A_IDX)) + _push (PAIR_AF); + else + pushed_a = false; + } + + if (AOP_TYPE (right) == AOP_LIT) + { + bytelit = byteOfVal (right->aop->aopu.aop_lit, i); + + if (bytelit == 0xff) + { + // TODO + } + else if (bytelit == 0x00) + { + cheapMove (result->aop, i, left->aop, i, a_free); + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } + else if (isLiteralBit (bytelit) >= 0 && + (AOP_TYPE (result) == AOP_REG || left == right && (AOP_TYPE (result) == AOP_STK || AOP_TYPE (result) == AOP_DIR))) + { + cheapMove (result->aop, i, left->aop, i, a_free); + if (!regalloc_dry_run) + emit2 ("set %d, %s", isLiteralBit (bytelit), aopGet (result->aop, i, false)); + regalloc_dry_run_cost += 2; + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + continue; + } + else if (IS_RAB && + (aopInReg (result->aop, i, HL_IDX) && (aopInReg (left->aop, i, HL_IDX) && !isPairInUse (PAIR_DE, ic) || aopInReg (left->aop, i, DE_IDX)) || + aopInReg (result->aop, i, H_IDX) && aopInReg (result->aop, i + 1, L_IDX) && (aopInReg (left->aop, i, H_IDX) && aopInReg (left->aop, i + 1, L_IDX) && !isPairInUse (PAIR_DE, ic) || aopInReg (left->aop, i, D_IDX) && aopInReg (left->aop, i + 1, E_IDX)))) + { + unsigned short mask = aopInReg (result->aop, i, L_IDX) ? (bytelit + (byteOfVal (right->aop->aopu.aop_lit, i + 1) << 8)) : (byteOfVal (right->aop->aopu.aop_lit, i + 1) + (bytelit << 8)); + bool mask_in_de = (aopInReg (left->aop, i, L_IDX) | aopInReg (left->aop, i, H_IDX)); + emit2 (mask_in_de ? "ld de, !immedword" : "ld hl, !immedword", mask); + emit2 ("or hl, de"); + regalloc_dry_run_cost += 4; + i += 2; + continue; + } + } + + if (IS_RAB) + { + const bool this_byte_l = aopInReg (result->aop, i, L_IDX) && + (aopInReg (left->aop, i, L_IDX) && aopInReg (left->aop, i, E_IDX) || aopInReg (left->aop, i, E_IDX) && aopInReg (left->aop, i, L_IDX)); + const bool this_byte_h = aopInReg (result->aop, i, H_IDX) && + (aopInReg (left->aop, i, H_IDX) && aopInReg (left->aop, i, D_IDX) || aopInReg (left->aop, i, D_IDX) && aopInReg (left->aop, i, H_IDX)); + const bool next_byte_l = aopInReg (result->aop, i + 1, L_IDX) && + (aopInReg (left->aop, i + 1, L_IDX) && aopInReg (left->aop, i + 1, E_IDX) || aopInReg (left->aop, i + 1, E_IDX) && aopInReg (left->aop, i + 1, L_IDX)); + const bool next_byte_h = aopInReg (result->aop, i + 1, H_IDX) && + (aopInReg (left->aop, i + 1, H_IDX) && aopInReg (left->aop, i + 1, D_IDX) || aopInReg (left->aop, i + 1, D_IDX) && aopInReg (left->aop, i + 1, H_IDX)); + + const bool this_byte = this_byte_l || this_byte_h; + const bool next_byte = next_byte_l || next_byte_h; + + const bool next_byte_unused = !bitVectBitValue (ic->rMask, this_byte_l ? H_IDX : L_IDX); + + if (this_byte && (next_byte || next_byte_unused)) + { + emit2 ("or hl, de"); + regalloc_dry_run_cost++; + i += (1 + next_byte); + continue; + } + } + + // Use plain or in a. + if (!a_free) + { + wassert (!pushed_a); + _push (PAIR_AF); + pushed_a = true; + a_free = true; + } + + if (aopInReg (right->aop, i, A_IDX)) + emit3_o (A_OR, ASMOP_A, 0, left->aop, i); + else + { + cheapMove (ASMOP_A, 0, left->aop, i, true); + emit3_o (A_OR, ASMOP_A, 0, right->aop, i); + } + cheapMove (result->aop, i, ASMOP_A, 0, true); + if (aopInReg (result->aop, i, A_IDX)) + a_free = false; + i++; + } + + if (pushed_a) + _pop (PAIR_AF); + +release: + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genXor - code for xclusive or */ +/*-----------------------------------------------------------------*/ +static void +genXor (const iCode *ic, iCode *ifx) +{ + operand *left, *right, *result; + int size, offset = 0; + unsigned long long lit = 0; + bool pushed_a = false; + + aopOp ((left = IC_LEFT (ic)), ic, FALSE, FALSE); + aopOp ((right = IC_RIGHT (ic)), ic, FALSE, FALSE); + aopOp ((result = IC_RESULT (ic)), ic, TRUE, FALSE); + + bool a_free = !bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= 0 && right->aop->regs[A_IDX] <= 0; + + /* if left is a literal & right is not then exchange them */ + if ((AOP_TYPE (left) == AOP_LIT && AOP_TYPE (right) != AOP_LIT) || (AOP_NEEDSACC (right) && !AOP_NEEDSACC (left))) + { + operand *tmp = right; + right = left; + left = tmp; + } + + /* if result = right then exchange them */ + if (sameRegs (result->aop, AOP (right)) && !AOP_NEEDSACC (left)) + { + operand *tmp = right; + right = left; + left = tmp; + } + + if (AOP_TYPE (right) == AOP_LIT) + lit = ullFromVal (AOP (right)->aopu.aop_lit); + + size = AOP_SIZE (result); + + if (AOP_TYPE (left) == AOP_CRY) + { + wassertl (0, "Tried to XOR a bit"); + goto release; + } + + /* Make sure A is on the left to not overwrite it. */ + if (aopInReg (right->aop, 0, A_IDX)) + { + wassert (!AOP_NEEDSACC (left)); + operand *tmp = right; + right = left; + left = tmp; + } + + // if(val & 0xZZ) - size = 0, ifx != FALSE - + // bit = val & 0xZZ - size = 1, ifx = FALSE - + if ((AOP_TYPE (right) == AOP_LIT) && (AOP_TYPE (result) == AOP_CRY) && (AOP_TYPE (left) != AOP_CRY)) + { + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + int sizel; + + sizel = AOP_SIZE (left); + + if (size) + { + /* PENDING: Test case for this. */ + wassertl (0, "Tried to XOR left against a literal with the result going into a bit"); + } + while (sizel--) + { + if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= offset && right->aop->regs[A_IDX] <= offset && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (!a_free) + { + wassert (!pushed_a); + _push (PAIR_AF); + a_free = true; + pushed_a = true; + if (ifx) // The pop at the end is hard to eal with in case of ifx. + { + regalloc_dry_run_cost += 100; + wassert (regalloc_dry_run); + } + } + else if (pushed_a && (aopInReg (left->aop, offset, A_IDX) || aopInReg (right->aop, offset, A_IDX))) + { + _pop (PAIR_AF); + if (bitVectBitValue (ic->rSurv, A_IDX)) + _push (PAIR_AF); + else + pushed_a = false; + } + + if (aopInReg (right->aop, offset, A_IDX)) + emit3_o (A_XOR, ASMOP_A, 0, left->aop, offset); + else + { + cheapMove (ASMOP_A, 0, left->aop, offset, true); + emit3_o (A_XOR, ASMOP_A, 0, right->aop, offset); + } + if (ifx) /* emit jmp only, if it is actually used * */ + if (!regalloc_dry_run) + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + offset++; + } + if (pushed_a) + _pop (PAIR_AF); + if (ifx) + { + jmpTrueOrFalse (ifx, tlbl); + } + else if (size) + { + wassertl (0, "Result of XOR was destined for a bit"); + } + goto release; + } + + // left & result in different registers + if (AOP_TYPE (result) == AOP_CRY) + { + wassertl (0, "Result of XOR is in a bit"); + } + else + for (; (size--); offset++) + { + if (!bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] <= offset && right->aop->regs[A_IDX] <= offset && (result->aop->regs[A_IDX] < 0 || result->aop->regs[A_IDX] >= offset)) + a_free = true; + + if (pushed_a && (aopInReg (left->aop, offset, A_IDX) || aopInReg (right->aop, offset, A_IDX))) + { + _pop (PAIR_AF); + if (bitVectBitValue (ic->rSurv, A_IDX)) + _push (PAIR_AF); + else + pushed_a = false; + } + + // normal case + // result = left & right + if (right->aop->type == AOP_LIT) + { + if (((lit >> (offset * 8)) & 0x0FFL) == 0x00L) + { + cheapMove (result->aop, offset, left->aop, offset, a_free); + if (aopInReg (result->aop, offset, A_IDX)) + a_free = false; + continue; + } + } + // faster than result <- left, anl result,right + // and better if result is SFR + if (!a_free) + { + wassert (!pushed_a); + _push (PAIR_AF); + a_free = true; + pushed_a = true; + } + + if (aopInReg (right->aop, offset, A_IDX)) + emit3_o (A_XOR, ASMOP_A, 0, left->aop, offset); + else + { + cheapMove (ASMOP_A, 0, left->aop, offset, true); + if (right->aop->type == AOP_LIT && ((lit >> (offset * 8)) & 0xff) == 0xff) + emit3 (A_CPL, 0, 0); + else + emit3_o (A_XOR, ASMOP_A, 0, right->aop, offset); + } + cheapMove (result->aop, offset, ASMOP_A, 0, true); + if (aopInReg (result->aop, offset, A_IDX)) + a_free = false; + } + if (pushed_a) + _pop (PAIR_AF); + +release: + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genRRC - rotate right with carry */ +/*-----------------------------------------------------------------*/ +static void +genRRC (const iCode * ic) +{ + wassert (0); +} + +/*-----------------------------------------------------------------*/ +/* genRLC - generate code for rotate left with carry */ +/*-----------------------------------------------------------------*/ +static void +genRLC (const iCode * ic) +{ + wassert (0); +} + +/*-----------------------------------------------------------------*/ +/* genGetHbit - generates code get highest order bit */ +/*-----------------------------------------------------------------*/ +static void +genGetHbit (const iCode * ic) +{ + operand *left, *result; + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, FALSE); + + /* get the highest order byte into a */ + cheapMove (ASMOP_A, 0, left->aop, AOP_SIZE (left) - 1, true); + + if (AOP_TYPE (result) == AOP_CRY) + { + emit3 (A_RL, ASMOP_A, 0); + outBitC (result); + } + else + { + emit3 (A_RLC, ASMOP_A, 0); + emit2 ("and a, !one"); + regalloc_dry_run_cost += 2; + outAcc (result); + } + + + freeAsmop (left, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genGetAbit - generates code get a single bit */ +/*-----------------------------------------------------------------*/ +static void +genGetAbit (const iCode * ic) +{ + wassert (0); +} + +static void +emitRsh2 (asmop * aop, int size, int is_signed) +{ + int offset = 0; + + while (size--) + { + if (offset == 0) + emit3_o (is_signed ? A_SRA : A_SRL, aop, size, 0, 0); + else + emit3_o (A_RR, aop, size, 0, 0); + offset++; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR2Left2Result - shift right two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR2Left2Result (const iCode *ic, operand *left, int offl, operand *result, int offr, int shCount, int is_signed) +{ + int size = 2; + symbol *tlbl; + + if (IS_RAB && !is_signed && shCount < 4 && + (getPairId (AOP (result)) == PAIR_HL || getPairId (AOP (result)) == PAIR_DE)) + { + bool op_de = (getPairId (AOP (result)) == PAIR_DE); + fetchPairLong (getPairId (AOP (result)), AOP(left), ic, offl); + while (shCount--) + { + emit3 (A_CP, ASMOP_A, ASMOP_A); + emit2 (op_de? "rr de" : "rr hl"); + regalloc_dry_run_cost++; + } + return; + } + else if (IS_RAB && !is_signed && shCount >= 2 && isPairDead (PAIR_HL, ic) && + ((isPair (AOP (left)) && getPairId (AOP (left)) == PAIR_HL || isPair (AOP (result)) + && getPairId (AOP (result)) == PAIR_HL) && isPairDead (PAIR_DE, ic) || isPair (AOP (left)) + && getPairId (AOP (left)) == PAIR_DE)) + { + bool op_de = (getPairId (AOP (left)) == PAIR_DE); + if (op_de) + emit2 ("ld hl, !immedword", 0xffff >> shCount); + else + { + fetchPair (PAIR_HL, AOP (left)); + emit2 ("ld de, !immedword", 0xffff >> shCount); + } + regalloc_dry_run_cost += 3; + while (shCount--) + { + emit2 (op_de ? "rr de" : "rr hl"); + regalloc_dry_run_cost++; + } + emit2 ("and hl, de"); + regalloc_dry_run_cost += 1; + commitPair (AOP (IC_RESULT (ic)), PAIR_HL, ic, TRUE); + return; + } + + if (isPair (AOP (result)) && !offr) + fetchPairLong (getPairId (AOP (result)), AOP(left), ic, offl); + else + genMove_o (result->aop, offr, left->aop, offl, 2, true, isPairDead (PAIR_HL, ic)); + + if (shCount == 0) + return; + + /* if (AOP(result)->type == AOP_REG) { */ + + /* Left is already in result - so now do the shift */ + /* Optimizing for speed by default. */ + if (!optimize.codeSize || shCount <= 2) + { + while (shCount--) + { + emitRsh2 (AOP (result), size, is_signed); + } + } + else + { + bool use_b = (!IS_GB && !bitVectBitValue (ic->rSurv, B_IDX) + && !(AOP_TYPE (result) == AOP_REG + && (AOP (result)->aopu.aop_reg[0]->rIdx == B_IDX || AOP (result)->aopu.aop_reg[1]->rIdx == B_IDX))); + + tlbl = regalloc_dry_run ? 0 : newiTempLabel (NULL); + + if (!regalloc_dry_run) + { + emit2 ("ld %s, !immedbyte", use_b ? "b" : "a", shCount); + emitLabel (tlbl); + } + regalloc_dry_run_cost += 2; + + emitRsh2 (AOP (result), size, is_signed); + + if (!regalloc_dry_run) + { + if (use_b) + emit2 ("djnz !tlabel", labelKey2num (tlbl->key)); + else + { + emit2 ("dec a"); + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl->key)); + } + } + regalloc_dry_run_cost += use_b ? 2 : 4; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL2Left2Result - shift left two bytes from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL2Left2Result (operand *left, int offl, operand *result, int offr, int shCount, const iCode *ic) +{ + operand *shiftoperand = result; + + if (sameRegs (AOP (result), AOP (left)) && ((offl + MSB16) == offr)) + { + wassert (0); + } + + /* For a shift of 7 we can use cheaper right shifts */ + else if (shCount == 7 && AOP_TYPE (left) == AOP_REG && !bitVectBitValue (ic->rSurv, AOP (left)->aopu.aop_reg[0]->rIdx) && AOP_TYPE (result) == AOP_REG && + AOP (left)->aopu.aop_reg[0]->rIdx != IYL_IDX && AOP (left)->aopu.aop_reg[1]->rIdx != IYL_IDX && AOP (left)->aopu.aop_reg[0]->rIdx != IYH_IDX && AOP (left)->aopu.aop_reg[1]->rIdx != IYH_IDX && + AOP (result)->aopu.aop_reg[0]->rIdx != IYL_IDX && AOP (result)->aopu.aop_reg[1]->rIdx != IYL_IDX && AOP (result)->aopu.aop_reg[0]->rIdx != IYH_IDX && AOP (result)->aopu.aop_reg[1]->rIdx != IYH_IDX && + (optimize.codeSpeed || getPairId (AOP (result)) != PAIR_HL || getPairId (AOP (left)) != PAIR_HL)) /* but a sequence of add hl, hl might still be cheaper code-size wise */ + { + // Handling the low byte in A with xor clearing is cheaper. + bool special_a = (!bitVectBitValue (ic->rSurv, A_IDX) && !aopInReg (AOP (left), 0, A_IDX) && !aopInReg (AOP (left), 0, A_IDX)); + asmop *lowbyte = special_a ? ASMOP_A : AOP (result); + + if (special_a) + emit3 (A_XOR, ASMOP_A, ASMOP_A); + emit3_o (A_RR, AOP (left), 1, 0, 0); + emit3_o (A_LD, AOP (result), 1, AOP (left), 0); + emit3_o (A_RR, AOP (result), 1, 0, 0); + if (!special_a) + emit3_o (A_LD, AOP (result), 0, ASMOP_ZERO, 0); + if (aopInReg (lowbyte, 0, A_IDX)) + emit3 (A_RRA, 0, 0); + else + emit3 (A_RR, lowbyte, 0); + if (special_a) + cheapMove (result->aop, 0, lowbyte, 0, true); + return; + } + + if (AOP_TYPE (result) != AOP_REG && AOP_TYPE (left) == AOP_REG && AOP_SIZE (left) >= 2 && !bitVectBitValue (ic->rSurv, AOP (left)->aopu.aop_reg[0]->rIdx) && !bitVectBitValue (ic->rSurv, AOP (left)->aopu.aop_reg[1]->rIdx) || + getPairId (AOP (left)) == PAIR_HL && isPairDead (PAIR_HL, ic)) + shiftoperand = left; + else if (isPair (AOP (result)) && !offr) + fetchPairLong (getPairId (AOP (result)), AOP(left), ic, offl); + else + genMove_o (result->aop, offr, left->aop, offl, 2, true, isPairDead (PAIR_HL, ic)); + + if (shCount == 0) + ; + else if (getPairId (AOP (shiftoperand)) == PAIR_HL) + { + while (shCount--) + { + emit2 ("add hl, hl"); + regalloc_dry_run_cost += 1; + } + } + else if (getPairId (AOP (shiftoperand)) == PAIR_IY) + { + while (shCount--) + { + emit2 ("add iy, iy"); + regalloc_dry_run_cost += 2; + } + } + else if (IS_RAB && getPairId (AOP (shiftoperand)) == PAIR_DE) + { + while (shCount--) + { + emit3 (A_CP, ASMOP_A, ASMOP_A); + emit2 ("rl de"); + regalloc_dry_run_cost++; + } + } + else + { + int size = 2; + int offset = 0; + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (0); + + if (AOP (shiftoperand)->type == AOP_REG) + { + while (shCount--) + { + for (offset = 0; offset < size; offset++) + emit3_o (offset ? A_RL : A_SLA, AOP (shiftoperand), offset, 0, 0); + } + } + else + { + /* Left is already in result - so now do the shift */ + if (shCount > 1) + { + if (!regalloc_dry_run) + { + emit2 ("ld a, !immedbyte+1", shCount); + emit2 ("jp !tlabel", labelKey2num (tlbl1->key)); + emitLabel (tlbl); + } + regalloc_dry_run_cost += 4; + } + + while (size--) + { + emit3_o (offset ? A_RL : A_SLA, AOP (shiftoperand), offset, 0, 0); + + offset++; + } + if (shCount > 1) + { + if (!regalloc_dry_run) + { + emitLabel (tlbl1); + emit2 ("dec a"); + emit2 ("jp NZ, !tlabel", labelKey2num (tlbl->key)); + } + regalloc_dry_run_cost += 4; + } + } + } + + if (shiftoperand != result) + { + if (isPair (AOP (result)) && !offr) + fetchPairLong (getPairId (AOP (result)), AOP(shiftoperand), ic, offl); + else if (isPair (AOP (shiftoperand))) + commitPair (AOP (result), getPairId (AOP (shiftoperand)), ic, FALSE); + else + { + /* Copy left into result */ + movLeft2Result (shiftoperand, offl, result, offr, 0); + movLeft2Result (shiftoperand, offl + 1, result, offr + 1, 0); + } + } +} + +/*-----------------------------------------------------------------*/ +/* AccRol - rotate left accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRol (int shCount) +{ + shCount &= 0x0007; // shCount : 0..7 + + switch (shCount) + { + case 4: + if (IS_GB) + { + emit3 (A_SWAP, ASMOP_A, 0); + break; + } + emit3 (A_RLCA, 0, 0); + case 3: + if (IS_GB) + { + emit3 (A_SWAP, ASMOP_A, 0); + emit3 (A_RRCA, 0, 0); + break; + } + emit3 (A_RLCA, 0, 0); + case 2: + emit3 (A_RLCA, 0, 0); + case 1: + emit3 (A_RLCA, 0, 0); + case 0: + break; + case 5: + if (IS_GB) + { + emit3 (A_SWAP, ASMOP_A, 0); + emit3 (A_RLCA, 0, 0); + break; + } + emit3 (A_RRCA, 0, 0); + case 6: + emit3 (A_RRCA, 0, 0); + case 7: + emit3 (A_RRCA, 0, 0); + break; + } +} + +/*-----------------------------------------------------------------*/ +/* AccLsh - left shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccLsh (unsigned int shCount) +{ + static const unsigned char SLMask[] = + { + 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00 + }; + + if (shCount <= 3 + !IS_GB) + while (shCount--) + emit3 (A_ADD, ASMOP_A, ASMOP_A); + else + { + /* rotate left accumulator */ + AccRol (shCount); + /* and kill the lower order bits */ + emit2 ("and a, !immedbyte", SLMask[shCount]); + regalloc_dry_run_cost += 2; + } +} + +/*-----------------------------------------------------------------*/ +/* shiftL1Left2Result - shift left one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftL1Left2Result (operand *left, int offl, operand *result, int offr, unsigned int shCount) +{ + /* If operand and result are the same we can shift in place. + However shifting in acc using add is cheaper than shifting + in place using sla; when shifting by more than 2 shifting in + acc it is worth the additional effort for loading from / to acc. */ + if (!aopInReg(result->aop, 0, A_IDX) && sameRegs (AOP (left), AOP (result)) && shCount <= 2 && offr == offl) + { + while (shCount--) + emit3 (A_SLA, AOP (result), 0); + } + else + { + cheapMove (ASMOP_A, 0, left->aop, offl, true); + /* shift left accumulator */ + AccLsh (shCount); + cheapMove (AOP (result), offr, ASMOP_A, 0, true); + } +} + +/*-----------------------------------------------------------------*/ +/* genlshTwo - left shift two bytes by known amount */ +/*-----------------------------------------------------------------*/ +static void +genlshTwo (operand *result, operand *left, unsigned int shCount, const iCode *ic) +{ + int size = AOP_SIZE (result); + + wassert (size == 2); + + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (size > 1) + { + if (shCount) + shiftL1Left2Result (left, LSB, result, MSB16, shCount); + else + movLeft2Result (left, LSB, result, MSB16, 0); + } + cheapMove (result->aop, 0, ASMOP_ZERO, 0, true); + } + /* 0 <= shCount <= 7 */ + else + { + if (size == 1) + { + wassert (0); + } + else + { + shiftL2Left2Result (left, LSB, result, LSB, shCount, ic); + } + } +} + +/*-----------------------------------------------------------------*/ +/* genlshOne - left shift a one byte quantity by known count */ +/*-----------------------------------------------------------------*/ +static void +genlshOne (operand * result, operand * left, unsigned int shCount) +{ + shiftL1Left2Result (left, LSB, result, LSB, shCount); +} + +/*------------------------------------------------------------------*/ +/* genLeftShiftLiteral - left shifting by known count for size <= 2 */ +/*------------------------------------------------------------------*/ +static void +genLeftShiftLiteral (operand * left, operand * right, operand * result, const iCode *ic) +{ + unsigned int shCount = ulFromVal (AOP (right)->aopu.aop_lit); + unsigned int size; + + freeAsmop (right, NULL); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, FALSE); + + size = getSize (operandType (result)); + + /* I suppose that the left size >= result size */ + wassert (getSize (operandType (left)) >= size); + + if (shCount >= (size * 8)) + genMove (result->aop, ASMOP_ZERO, !bitVectBitValue (ic->rSurv, A_IDX) && left->aop->regs[A_IDX] != 0, isPairDead (PAIR_HL, ic) && left->aop->regs[H_IDX] < 0 && left->aop->regs[L_IDX] < 0); + else + { + switch (size) + { + case 1: + genlshOne (result, left, shCount); + break; + case 2: + genlshTwo (result, left, shCount, ic); + break; + case 4: + wassertl (0, "Shifting of longs should be handled by generic function."); + break; + default: + wassert (0); + } + } + freeAsmop (left, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genLeftShift - generates code for left shifting */ +/*-----------------------------------------------------------------*/ +static void +genLeftShift (const iCode * ic) +{ + int size, offset; + symbol *tlbl = 0, *tlbl1 = 0; + operand *left, *right, *result; + int countreg; + bool shift_by_lit; + int shiftcount = 0; + int byteshift = 0; + bool started; + bool save_a; + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT && getSize (operandType (result)) <= 2) + { + genLeftShiftLiteral (left, right, result, ic); + freeAsmop (right, NULL); + return; + } + + /* Useful for the case of shifting a size > 2 value by a literal */ + shift_by_lit = AOP_TYPE (right) == AOP_LIT; + if (shift_by_lit) + shiftcount = ulFromVal (AOP (right)->aopu.aop_lit); + + aopOp (result, ic, FALSE, FALSE); + aopOp (left, ic, FALSE, FALSE); + + if (AOP_TYPE (right) == AOP_REG && !bitVectBitValue (ic->rSurv, AOP (right)->aopu.aop_reg[0]->rIdx) && AOP (right)->aopu.aop_reg[0]->rIdx != IYL_IDX && (sameRegs (AOP (left), AOP (result)) || AOP_TYPE (left) != AOP_REG) && + (AOP_TYPE (result) != AOP_REG || + AOP (result)->aopu.aop_reg[0]->rIdx != AOP (right)->aopu.aop_reg[0]->rIdx && + !aopInReg (AOP (result), 0, AOP (right)->aopu.aop_reg[0]->rIdx) && !aopInReg (AOP (result), 1, AOP (right)->aopu.aop_reg[0]->rIdx) && !aopInReg (AOP (result), 2, AOP (right)->aopu.aop_reg[0]->rIdx) && !aopInReg (AOP (result), 3, AOP (right)->aopu.aop_reg[0]->rIdx))) + countreg = AOP (right)->aopu.aop_reg[0]->rIdx; + else if (!IS_GB && !bitVectBitValue (ic->rSurv, B_IDX) && (sameRegs (AOP (left), AOP (result)) || AOP_TYPE (left) != AOP_REG || shift_by_lit) && + !aopInReg (AOP (result), 0, B_IDX) && !aopInReg (AOP (result), 1, B_IDX) && !aopInReg (AOP (result), 2, B_IDX) && !aopInReg (AOP (result), 3, B_IDX)) + countreg = B_IDX; + else + countreg = A_IDX; + + if (!shift_by_lit) + cheapMove (asmopregs[countreg], 0, right->aop, 0, true); + + save_a = (countreg == A_IDX && !shift_by_lit) && + !(AOP_TYPE (left) == AOP_REG && AOP_TYPE (result) != AOP_REG || + !IS_GB && (AOP_TYPE (left) == AOP_STK && canAssignToPtr3 (result->aop) || AOP_TYPE (result) == AOP_STK && canAssignToPtr3 (left->aop))); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result))) + { + if (save_a) + _push (PAIR_AF); + + if (shift_by_lit) + { + byteshift = shiftcount / 8; + shiftcount %= 8; + } + size = AOP_SIZE (result) - byteshift; + int lsize = AOP_SIZE (left) - byteshift; + + genMove_o (result->aop, byteshift, left->aop, 0, size <= lsize ? size : lsize, save_a || countreg != A_IDX, false); + + genMove_o (result->aop, 0, ASMOP_ZERO, 0, byteshift, save_a || countreg != A_IDX, false); + + if (save_a) + _pop (PAIR_AF); + } + + if (!regalloc_dry_run) + { + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + } + size = AOP_SIZE (result); + offset = 0; + + if (shift_by_lit && !shiftcount) + goto end; + if (shift_by_lit && shiftcount > 1) + { + emit2 ("ld %s, !immedbyte", countreg == A_IDX ? "a" : regsZ80[countreg].name, shiftcount); + regalloc_dry_run_cost += 2; + } + else if (!shift_by_lit) + { + emit2 ("inc %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); + regalloc_dry_run_cost += 1; + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (tlbl1->key)); + regalloc_dry_run_cost += 3; + } + if (!(shift_by_lit && shiftcount == 1) && !regalloc_dry_run) + emitLabel (tlbl); + + if (requiresHL (AOP (result))) + spillPair (PAIR_HL); + + started = false; + while (size) + { + if (size >= 2 && offset + 1 >= byteshift && + AOP_TYPE (result) == AOP_REG && + (getPartPairId (AOP (result), offset) == PAIR_HL || + IS_RAB && getPartPairId (AOP (result), offset) == PAIR_DE)) + { + if (AOP (result)->aopu.aop_reg[offset]->rIdx == L_IDX) + { + emit2 (started ? "adc hl, hl" : "add hl, hl"); + regalloc_dry_run_cost += 1 + started; + } + else + { + if (!started) + emit3 (A_CP, ASMOP_A, ASMOP_A); + emit2 ("rl de"); + regalloc_dry_run_cost++; + } + + started = true; + size -= 2, offset += 2; + } + else + { + if (offset >= byteshift) + { + if (aopInReg (AOP (result) , offset, A_IDX)) + emit3 (started ? A_ADC : A_ADD, ASMOP_A, ASMOP_A); + else + emit3_o (started ? A_RL : A_SLA, AOP (result), offset, 0, 0); + started = true; + } + size--, offset++; + } + } + + if (!(shift_by_lit && shiftcount == 1)) + { + if (!regalloc_dry_run) + emitLabel (tlbl1); + if (!IS_GB && countreg == B_IDX) + { + if (!regalloc_dry_run) + emit2 ("djnz !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("dec %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); + if (!regalloc_dry_run) + emit2 ("jr NZ,!tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + } + } + +end: + if (!shift_by_lit && requiresHL (AOP (result))) // Shift by 0 skips over hl adjustments. + spillPair (PAIR_HL); + + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* AccRsh - right shift accumulator by known count */ +/*-----------------------------------------------------------------*/ +static void +AccRsh (int shCount) +{ + if (shCount >= 2) + { + /* rotate right accumulator */ + AccRol (8 - shCount); + /* and kill the higher order bits */ + if (!regalloc_dry_run) + emit2 ("and a, !immedbyte", 0xff >> shCount); + regalloc_dry_run_cost += 2; + } + else if(shCount) + emit3 (A_SRL, ASMOP_A, 0); +} + +/*-----------------------------------------------------------------*/ +/* genrshOne - right shift one byte by known amount */ +/*-----------------------------------------------------------------*/ +static void +genrshOne (operand *result, operand *left, int shCount, int is_signed, const iCode *ic) +{ + /* Errk */ + int size = AOP_SIZE (result); + + wassert (size == 1); + + bool a_dead = !bitVectBitValue (ic->rSurv, A_IDX); + + if ((IS_Z180 || IS_EZ80_Z80) && !is_signed && shCount >= 3 && shCount <= 6 + a_dead && // Try to use mlt. + (aopInReg (result->aop, 0, B_IDX) && isPairDead(PAIR_BC, ic) || aopInReg (result->aop, 0, D_IDX) && isPairDead(PAIR_DE, ic) || aopInReg (result->aop, 0, H_IDX) && isPairDead(PAIR_HL, ic))) + { + PAIR_ID pair = aopInReg (result->aop, 0, B_IDX) ? PAIR_BC : (aopInReg (result->aop, 0, D_IDX) ? PAIR_DE : PAIR_HL); + bool top = aopInReg (left->aop, 0, _pairs[pair].h_idx); + if (!top) + cheapMove (pair == PAIR_BC ? ASMOP_C : (pair == PAIR_DE ? ASMOP_E : ASMOP_L), 0, left->aop, 0, a_dead); + + emit2 ("ld %s, #%d", top ? _pairs[pair].l : _pairs[pair].h, 1 << (8 - shCount)); + emit2 ("mlt %s", _pairs[pair].name); + regalloc_dry_run_cost += 4; + } + else if (!is_signed && // Shifting in the accumulator is cheap for unsigned operands. + (aopInReg (result->aop, 0, A_IDX) || + result->aop->type != AOP_REG || + (shCount >= 4 + 2 * a_dead || shCount >= 2 * a_dead && aopInReg (left->aop, 0, A_IDX)))) + { + if (!a_dead) + _push (PAIR_AF); + cheapMove (ASMOP_A, 0, left->aop, 0, true); + AccRsh (shCount); + cheapMove (result->aop, 0, ASMOP_A, 0, true); + if (!a_dead) + _pop (PAIR_AF); + } + else if (AOP (result)->type == AOP_REG) // Can shift in destination for register result. + { + cheapMove (AOP (result), 0, AOP (left), 0, a_dead); + + while (shCount--) + emit3 (is_signed ? A_SRA : A_SRL, result->aop, 0); + } + else + { + if (!a_dead) + _push (PAIR_AF); + cheapMove (ASMOP_A, 0, left->aop, 0, true); + while (shCount--) + emit3 (is_signed ? A_SRA : A_SRL, ASMOP_A, 0); + cheapMove (result->aop, 0, ASMOP_A, 0, true); + if (!a_dead) + _pop (PAIR_AF); + } +} + +/*-----------------------------------------------------------------*/ +/* shiftR1Left2Result - shift right one byte from left to result */ +/*-----------------------------------------------------------------*/ +static void +shiftR1Left2Result (operand *left, int offl, operand *result, int offr, int shCount, int sign) +{ + cheapMove (ASMOP_A, 0, left->aop, offl, true); + if (sign) + { + while (shCount--) + emit3 (sign ? A_SRA : A_SRL, ASMOP_A, 0); + } + else + AccRsh (shCount); + cheapMove (result->aop, offr, ASMOP_A, 0, true); +} + +/*-----------------------------------------------------------------*/ +/* genrshTwo - right shift two bytes by known amount */ +/*-----------------------------------------------------------------*/ +static void +genrshTwo (const iCode * ic, operand * result, operand * left, int shCount, int sign) +{ + /* if shCount >= 8 */ + if (shCount >= 8) + { + shCount -= 8; + if (shCount) + { + shiftR1Left2Result (left, MSB16, result, LSB, shCount, sign); + } + else + { + movLeft2Result (left, MSB16, result, LSB, sign); + } + if (sign) + { + /* Sign extend the result */ + cheapMove (ASMOP_A, 0, result->aop, 0, true); + emit3 (A_RLC, ASMOP_A, 0); + emit3 (A_SBC, ASMOP_A, ASMOP_A); + cheapMove (result->aop, 1, ASMOP_A, 0, true); + } + else + cheapMove (result->aop, 1, ASMOP_ZERO, 0, true); + } + /* 0 <= shCount <= 7 */ + else + shiftR2Left2Result (ic, left, LSB, result, LSB, shCount, sign); +} + +/*-----------------------------------------------------------------*/ +/* genRightShiftLiteral - right shifting by known count */ +/*-----------------------------------------------------------------*/ +static void +genRightShiftLiteral (operand * left, operand * right, operand * result, const iCode *ic, int sign) +{ + unsigned int shCount = (unsigned int) ulFromVal (AOP (right)->aopu.aop_lit); + unsigned int size; + + freeAsmop (right, NULL); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, FALSE); + + size = getSize (operandType (result)); + + /* I suppose that the left size >= result size */ + wassert (getSize (operandType (left)) >= size); + + if (shCount >= (size * 8)) + { + if (!SPEC_USIGN (getSpec (operandType (left)))) + { + cheapMove (ASMOP_A, 0, left->aop, 0, true); + emit3 (A_RLC, ASMOP_A, 0); + emit3 (A_SBC, ASMOP_A, ASMOP_A); + while (size--) + cheapMove (result->aop, size, ASMOP_A, 0, true); + } + else + genMove (result->aop, ASMOP_ZERO, !bitVectBitValue (ic->rSurv, A_IDX), isPairDead (PAIR_HL, ic)); + } + else + { + switch (size) + { + case 1: + genrshOne (result, left, shCount, sign, ic); + break; + case 2: + genrshTwo (ic, result, left, shCount, sign); + break; + case 4: + wassertl (0, "Asked to shift right a long which should be handled in generic right shift function."); + break; + default: + wassertl (0, "Entered default case in right shift delegate"); + } + } + freeAsmop (left, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genRightShift - generate code for right shifting */ +/*-----------------------------------------------------------------*/ +static void +genRightShift (const iCode * ic) +{ + operand *right, *left, *result; + sym_link *retype; + int size, offset, first = 1; + bool is_signed; + int countreg; + bool shift_by_lit, shift_by_one, shift_by_zero; + int shiftcount = 0; + int byteoffset = 0; + bool save_a; + + symbol *tlbl = 0, *tlbl1 = 0; + + /* if signed then we do it the hard way preserve the + sign bit moving it inwards */ + retype = getSpec (operandType (IC_RESULT (ic))); + + is_signed = !SPEC_USIGN (retype); + + right = IC_RIGHT (ic); + left = IC_LEFT (ic); + result = IC_RESULT (ic); + + aopOp (right, ic, FALSE, FALSE); + + /* if the shift count is known then do it + as efficiently as possible */ + if (AOP_TYPE (right) == AOP_LIT && getSize (operandType (result)) <= 2) + { + genRightShiftLiteral (left, right, result, ic, is_signed); + freeAsmop (right, NULL); + return; + } + + /* Useful for the case of shifting a size > 2 value by a literal */ + shift_by_lit = AOP_TYPE (right) == AOP_LIT; + if (shift_by_lit) + shiftcount = ulFromVal (AOP (right)->aopu.aop_lit); + + aopOp (result, ic, FALSE, FALSE); + aopOp (left, ic, FALSE, FALSE); + + if (AOP_TYPE (right) == AOP_REG && !bitVectBitValue (ic->rSurv, AOP (right)->aopu.aop_reg[0]->rIdx) && AOP (right)->aopu.aop_reg[0]->rIdx != IYL_IDX && (sameRegs (AOP (left), AOP (result)) || AOP_TYPE (left) != AOP_REG) && + (AOP_TYPE (result) != AOP_REG || + AOP (result)->aopu.aop_reg[0]->rIdx != AOP (right)->aopu.aop_reg[0]->rIdx && + (AOP_SIZE (result) < 2 || AOP (result)->aopu.aop_reg[1]->rIdx != AOP (right)->aopu.aop_reg[0]->rIdx && + (AOP_SIZE (result) < 3 || AOP (result)->aopu.aop_reg[2]->rIdx != AOP (right)->aopu.aop_reg[0]->rIdx && + (AOP_SIZE (result) < 4 || AOP (result)->aopu.aop_reg[3]->rIdx != AOP (right)->aopu.aop_reg[0]->rIdx))))) + countreg = AOP (right)->aopu.aop_reg[0]->rIdx; + else if (!IS_GB && !bitVectBitValue (ic->rSurv, B_IDX) && (sameRegs (AOP (left), AOP (result)) || AOP_TYPE (left) != AOP_REG || shift_by_lit) && + (AOP_TYPE (result) != AOP_REG || + AOP (result)->aopu.aop_reg[0]->rIdx != B_IDX && + (AOP_SIZE (result) < 2 || AOP (result)->aopu.aop_reg[1]->rIdx != B_IDX && + (AOP_SIZE (result) < 3 || AOP (result)->aopu.aop_reg[2]->rIdx != B_IDX && + (AOP_SIZE (result) < 4 || AOP (result)->aopu.aop_reg[3]->rIdx != B_IDX))))) + countreg = B_IDX; + else + countreg = A_IDX; + + if (!shift_by_lit) + cheapMove (countreg == A_IDX ? ASMOP_A : asmopregs[countreg], 0, right->aop, 0, true); + + save_a = (countreg == A_IDX && !shift_by_lit) && + !(AOP_TYPE (left) == AOP_REG && AOP_TYPE (result) != AOP_REG || + !IS_GB && (AOP_TYPE (left) == AOP_STK && canAssignToPtr3 (result->aop) || AOP_TYPE (result) == AOP_STK && canAssignToPtr3 (left->aop))); + + /* now move the left to the result if they are not the + same */ + if (!sameRegs (AOP (left), AOP (result))) + { + int soffset = 0; + size = AOP_SIZE (result); + + if (!is_signed && shift_by_lit) + { + byteoffset = shiftcount / 8; + shiftcount %= 8; + soffset = byteoffset; + size -= byteoffset; + } + + if (save_a) + _push (PAIR_AF); + + genMove_o (result->aop, 0, left->aop, soffset, size, true, isPairDead (PAIR_HL, ic)); + + genMove_o (result->aop, result->aop->size - byteoffset, ASMOP_ZERO, 0, byteoffset, true, false); + + if (save_a) + _pop (PAIR_AF); + } + + shift_by_one = (shift_by_lit && shiftcount == 1); + shift_by_zero = (shift_by_lit && shiftcount == 0); + + if (!regalloc_dry_run) + { + tlbl = newiTempLabel (NULL); + tlbl1 = newiTempLabel (NULL); + } + size = AOP_SIZE (result); + offset = size - 1; + + if (shift_by_zero) + goto end; + else if (shift_by_lit && shiftcount > 1) + { + emit2 ("ld %s, !immedbyte", countreg == A_IDX ? "a" : regsZ80[countreg].name, shiftcount); + regalloc_dry_run_cost += 2; + } + else if (!shift_by_lit) + { + emit2 ("inc %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); + regalloc_dry_run_cost += 1; + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (tlbl1->key)); + regalloc_dry_run_cost += 3; + } + if (!shift_by_one && !regalloc_dry_run) + IS_GB ? emitLabelSpill (tlbl) : emitLabel (tlbl); + + if (requiresHL (AOP (result))) + spillPair (PAIR_HL); + + while (size) + { + if (IS_RAB && !(is_signed && first) && size >= 2 && byteoffset < 2 && AOP_TYPE (result) == AOP_REG && + (getPartPairId (AOP (result), offset - 1) == PAIR_HL || getPartPairId (AOP (result), offset - 1) == PAIR_DE)) + { + if (first) + { + emit3 (A_CP, ASMOP_A, ASMOP_A); + first = 0; + } + emit2 (AOP (result)->aopu.aop_reg[offset - 1]->rIdx == L_IDX ? "rr hl" : "rr de"); + regalloc_dry_run_cost++; + size -= 2, offset -= 2; + } + else if (!is_signed && first && byteoffset--) // Skip known 0 bytes + size--, offset--; + else if (first) + { + emit3_o (is_signed ? A_SRA : A_SRL, AOP (result), offset, 0, 0); + first = 0; + size--, offset--; + } + else + { + emit3_o (A_RR, AOP (result), offset, 0, 0); + size--, offset--; + } + } + + if (!shift_by_one) + { + if (!regalloc_dry_run) + emitLabel (tlbl1); + if (!IS_GB && countreg == B_IDX) + { + if (!regalloc_dry_run) + emit2 ("djnz !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("dec %s", countreg == A_IDX ? "a" : regsZ80[countreg].name); + if (!regalloc_dry_run) + emit2 ("jr NZ, !tlabel", labelKey2num (tlbl->key)); + regalloc_dry_run_cost += 3; + } + } + +end: + if (!shift_by_lit && requiresHL (AOP (result))) // Shift by 0 skips over hl adjustments. + spillPair (PAIR_HL); + + freeAsmop (left, NULL); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* unpackMaskA - generate masking code for unpacking last byte */ +/* of bitfiled. And mask for unsigned, sign extension for signed. */ +/*-----------------------------------------------------------------*/ +static void +unpackMaskA(sym_link *type, int len) +{ + if (SPEC_USIGN (type) || len != 1) + { + emit2 ("and a, !immedbyte", ((unsigned char) - 1) >> (8 - len)); + regalloc_dry_run_cost += 2; + } + if (!SPEC_USIGN (type)) + { + if (len == 1) + { + emit3(A_RRA, 0, 0); + emit3(A_SBC, ASMOP_A, ASMOP_A); + } + else + { + if (!regalloc_dry_run) + { + symbol *tlbl = newiTempLabel (NULL); + emit2 ("bit %d, a", len - 1); + emit2 ("jp Z, !tlabel", labelKey2num (tlbl->key)); + emit2 ("or a, !immedbyte", (unsigned char) (0xff << len)); + emitLabel (tlbl); + } + regalloc_dry_run_cost += 7; + } + } +} + +/*-----------------------------------------------------------------*/ +/* genUnpackBits - generates code for unpacking bits */ +/*-----------------------------------------------------------------*/ +static void +genUnpackBits (operand * result, int pair, const iCode *ic) +{ + int offset = 0; /* result byte offset */ + int rsize; /* result size */ + int rlen = 0; /* remaining bit-field length */ + sym_link *etype; /* bit-field type information */ + unsigned blen; /* bit-field length */ + unsigned bstr; /* bit-field starting bit within byte */ + unsigned int pairincrement = 0; + + emitDebug ("; genUnpackBits"); + + etype = getSpec (operandType (result)); + rsize = getSize (operandType (result)); + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bit-field length is less than a byte */ + if (blen < 8) + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; + AccRol (8 - bstr); + unpackMaskA (etype, blen); + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + goto finish; + } + + /* TODO: what if pair == PAIR_DE ? */ + if (getPairId (AOP (result)) == PAIR_HL || + AOP_TYPE (result) == AOP_REG && rsize >= 2 && (AOP (result)->aopu.aop_reg[0]->rIdx == L_IDX + || AOP (result)->aopu.aop_reg[0]->rIdx == H_IDX)) + { + wassertl (rsize == 2, "HL must be of size 2"); + emit2 ("ld a, !*hl"); + emit2 ("inc hl"); + if (AOP_TYPE (result) != AOP_REG || AOP (result)->aopu.aop_reg[0]->rIdx != H_IDX) + { + emit2 ("ld h, !*hl"); + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + emit2 ("ld a, h"); + } + else + { + emit2 ("ld l, !*hl"); + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + emit2 ("ld a, l"); + } + regalloc_dry_run_cost += 5; + unpackMaskA (etype, blen - 8); + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + regalloc_dry_run_cost += 1; + spillPair (PAIR_HL); + return; + } + + /* Bit field did not fit in a byte. Copy all + but the partial byte at the end. */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += 1; + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + if (rlen > 8) + { + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset++; + pairincrement++; + } + } + + /* Handle the partial byte at the end */ + if (rlen) + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost++; + unpackMaskA (etype, rlen); + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + } + +finish: + if (!isPairDead(pair, ic)) + while (pairincrement) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + pairincrement--; + _G.pairs[pair].offset--; + } + + if (offset < rsize) + { + asmop *source; + + if (SPEC_USIGN (etype)) + source = ASMOP_ZERO; + else + { + /* signed bit-field: sign extension with 0x00 or 0xff */ + emit3 (A_RLA, 0, 0); + emit3 (A_SBC, ASMOP_A, ASMOP_A); + source = ASMOP_A; + } + rsize -= offset; + while (rsize--) + cheapMove (AOP (result), offset++, source, 0, true); + } +} + +static void +_moveFrom_tpair_ (asmop * aop, int offset, PAIR_ID pair) +{ + emitDebug ("; _moveFrom_tpair_()"); + if (pair == PAIR_HL && aop->type == AOP_REG) + { + if (!regalloc_dry_run) + aopPut (aop, "!*hl", offset); + regalloc_dry_run_cost += ld_cost (aop, ASMOP_A); + } + else + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += 1; + cheapMove (aop, offset, ASMOP_A, 0, true); + } +} + +static void offsetPair (PAIR_ID pair, PAIR_ID extrapair, bool save_extrapair, int val) +{ + if (abs (val) < (save_extrapair ? 6 : 4) || pair != PAIR_HL && pair != PAIR_IY && pair != PAIR_IX) + { + while (val) + { + emit2 (val > 0 ? "inc %s" : "dec %s", _pairs[pair].name); + if (val > 0) + val--; + else + val++; + regalloc_dry_run_cost++; + } + } + else + { + if (save_extrapair) + _push (extrapair); + emit2 ("ld %s, !immedword", _pairs[extrapair].name, val); + emit2 ("add %s, %s", _pairs[pair].name, _pairs[extrapair].name); + regalloc_dry_run_cost += (pair == PAIR_HL ? 4 : 5); + if (save_extrapair) + _pop (extrapair); + } +} + +/*-----------------------------------------------------------------*/ +/* genPointerGet - generate code for pointer get */ +/*-----------------------------------------------------------------*/ +static void +genPointerGet (const iCode *ic) +{ + operand *left, *right, *result; + int size, offset, rightval; + int pair = PAIR_HL, extrapair; + sym_link *retype; + bool pushed_pair = FALSE; + bool pushed_a = FALSE; + bool surviving_a = !options.oldralloc && bitVectBitValue (ic->rSurv, A_IDX); + bool rightval_in_range; + + left = IC_LEFT (ic); + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + retype = getSpec (operandType (result)); + + aopOp (left, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, FALSE); + size = AOP_SIZE (result); + + /* Historically GET_VALUE_AT_ADDRESS didn't have a right operand */ + wassertl (right, "GET_VALUE_AT_ADDRESS without right operand"); + wassertl (IS_OP_LITERAL (IC_RIGHT (ic)), "GET_VALUE_AT_ADDRESS with non-literal right operand"); + rightval = (int)operandLitValue (right); + rightval_in_range = (rightval >= -128 && rightval + size - 1 < 127); + if (IS_GB) + wassert (!rightval); + + if (IS_GB && left->aop->type == AOP_STK) // Try to avoid (hl) to hl copy, which requires 3 instructions and free a. + pair = PAIR_DE; + if ((IS_GB || IY_RESERVED) && requiresHL (AOP (result)) && size > 1 && AOP_TYPE (result) != AOP_REG) + pair = PAIR_DE; + + if (AOP_TYPE (left) == AOP_IMMD && size == 1 && aopInReg (result->aop, 0, A_IDX) && !IS_BITVAR (retype)) + { + emit2 ("ld a, (%s)", aopGetLitWordLong (AOP (left), rightval, TRUE)); + regalloc_dry_run_cost += 3; + goto release; + } + else if (!IS_GB && AOP_TYPE (left) == AOP_IMMD && isPair (AOP (result)) && !IS_BITVAR (retype)) + { + PAIR_ID pair = getPairId (AOP (result)); + emit2 ("ld %s, (%s)", _pairs[pair].name, aopGetLitWordLong (AOP (left), rightval, TRUE)); + regalloc_dry_run_cost += (pair == PAIR_HL ? 3 : 4); + goto release; + } + else if (!IS_GB && AOP_TYPE (left) == AOP_IMMD && getPartPairId (AOP (result), 0) != PAIR_INVALID && getPartPairId (AOP (result), 2) != PAIR_INVALID) + { + PAIR_ID pair; + pair = getPartPairId (AOP (result), 0); + emit2 ("ld %s, (%s)", _pairs[pair].name, aopGetLitWordLong (AOP (left), rightval, TRUE)); + regalloc_dry_run_cost += (pair == PAIR_HL ? 3 : 4); + pair = getPartPairId (AOP (result), 2); + emit2 ("ld %s, (%s)", _pairs[pair].name, aopGetLitWordLong (AOP (left), rightval + 2, TRUE)); + regalloc_dry_run_cost += (pair == PAIR_HL ? 3 : 4); + goto release; + } + + if (isPair (AOP (left)) && size == 1 && !IS_BITVAR (retype) && !rightval) + { + /* Just do it */ + if (isPtrPair (AOP (left))) + { + if (!regalloc_dry_run) // Todo: More exact cost. + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "!*pair", getPairName (AOP (left))); + aopPut (AOP (result), dbuf_c_str (&dbuf), 0); + dbuf_destroy (&dbuf); + } + regalloc_dry_run_cost += ld_cost (AOP (result), ASMOP_A); + } + else + { + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + emit2 ("ld a, !*pair", getPairName (AOP (left))); + regalloc_dry_run_cost += (getPairId (AOP (left)) == PAIR_IY ? 3 : 1); + cheapMove (AOP (result), 0, ASMOP_A, 0, true); + } + + goto release; + } + + if (getPairId (AOP (left)) == PAIR_IY && !IS_BITVAR (retype) && rightval_in_range) + { + offset = 0; + + if ((IS_RAB || IS_TLCS90) && getPartPairId (AOP (result), 0) == PAIR_HL) + { + emit2 ("ld hl, %d (iy)", rightval); + regalloc_dry_run_cost += 3; + offset = 2; + size -= 2; + } + else if (IS_EZ80_Z80 && getPartPairId (AOP (result), 0) != PAIR_INVALID) + { + emit2 ("ld %s, %d (iy)", _pairs[getPartPairId (AOP (result), 0)].name, rightval); + regalloc_dry_run_cost += 3; + offset = 2; + size -= 2; + } + + if (!size) + goto release; + + /* Just do it */ + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + + while (size--) + { + if (!regalloc_dry_run) + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_tprintf (&dbuf, "!*iyx", rightval + offset); + aopPut (AOP (result), dbuf_c_str (&dbuf), offset); + dbuf_destroy (&dbuf); + } + regalloc_dry_run_cost += ld_cost (AOP (result), ASMOP_A) + 2; // Todo: More exact cost. + offset++; + } + + goto release; + } + + /* Using ldir is cheapest for large memory-to-memory transfers. */ + if (!IS_GB && (AOP_TYPE (result) == AOP_STK || AOP_TYPE (result) == AOP_EXSTK) && size > 2 && (!rightval || AOP_TYPE (left) == AOP_IMMD)) + { + int fp_offset, sp_offset; + + if(!isPairDead (PAIR_HL, ic)) + _push (PAIR_HL); + if(!isPairDead (PAIR_DE, ic)) + _push (PAIR_DE); + if(!isPairDead (PAIR_BC, ic)) + _push (PAIR_BC); + + if (!rightval) + fetchPair (PAIR_DE, AOP (left)); + else + { + emit2 ("ld de, %s", aopGetLitWordLong (AOP (left), rightval, TRUE)); + regalloc_dry_run_cost += 3; + } + + fp_offset = AOP (result)->aopu.aop_stk + (AOP (result)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2 ("ld hl, !immedword", sp_offset); + emit2 ("add hl, sp"); + emit2 ("ex de, hl"); + emit2 ("ld bc, !immedword", size); + emit2 ("ldir"); + regalloc_dry_run_cost += 10; + spillPair (PAIR_HL); + spillPair (PAIR_DE); + spillPair (PAIR_BC); + + if(!isPairDead (PAIR_BC, ic)) + _pop (PAIR_BC); + if(!isPairDead (PAIR_DE, ic)) + _pop (PAIR_DE); + if(!isPairDead (PAIR_HL, ic)) + _pop (PAIR_HL); + + goto release; + } + + extrapair = isPairDead (PAIR_DE, ic) ? PAIR_DE : PAIR_BC; + + /* For now we always load into temp pair */ + /* if this is rematerializable */ + if (!IS_GB && (getPairId (AOP (left)) == PAIR_BC || getPairId (AOP (left)) == PAIR_DE) && AOP_TYPE (result) == AOP_STK && !rightval + || getPairId (AOP (left)) == PAIR_IY && SPEC_BLEN (getSpec (operandType (result))) < 8 && rightval_in_range) + pair = getPairId (AOP (left)); + else + { + if (!isPairDead (pair, ic) && size > 1 && (getPairId (AOP (left)) != pair || rightval || IS_BITVAR (retype) || size > 2)) // For simple cases, restoring via dec is cheaper than push / pop. + _push (pair), pushed_pair = TRUE; + if (AOP_TYPE(left) == AOP_IMMD) + { + emit2 ("ld %s, %s", _pairs[pair].name, aopGetLitWordLong (AOP (left), rightval, TRUE)); + spillPair (pair); + regalloc_dry_run_cost += 3; + rightval = 0; + } + else + fetchPair (pair, AOP (left)); + } + + /* if bit then unpack */ + if (IS_BITVAR (retype)) + { + offsetPair (pair, extrapair, !isPairDead (extrapair, ic), rightval); + genUnpackBits (result, pair, ic); + if (rightval) + spillPair (pair); + + goto release; + } + + if (isPair (AOP (result)) && IS_EZ80_Z80 && getPairId (AOP (left)) == PAIR_HL && !IS_BITVAR (retype)) + { + emit2 ("ld %s, (hl)", _pairs[getPairId (AOP (result))].name); + regalloc_dry_run_cost += 2; + goto release; + } + else if (getPairId (AOP (result)) == PAIR_HL || size == 2 && (aopInReg (result->aop, 0, L_IDX) || aopInReg (result->aop, 0, H_IDX))) + { + wassertl (size == 2, "HL must be of size 2"); + if (IS_RAB && getPairId (AOP (result)) == PAIR_HL && rightval_in_range) + { + emit2 ("ld hl, %d (hl)", rightval); + regalloc_dry_run_cost += 3; + } + else if (IS_EZ80_Z80 && getPairId (AOP (result)) == PAIR_HL && !rightval) + { + emit2 ("ld hl, (hl)"); + regalloc_dry_run_cost += 2; + } + else if (aopInReg (result->aop, 1, A_IDX)) + { + offsetPair (pair, extrapair, !isPairDead (extrapair, ic), rightval + 1); + emit2 ("ld a, !*hl"); + emit2 ("dec hl"); + if (!regalloc_dry_run) + aopPut (AOP (result), "!*hl", 0); + regalloc_dry_run_cost += 3; + } + else + { + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + offsetPair (pair, extrapair, !isPairDead (extrapair, ic), rightval); + emit2 ("ld a, !*hl"); + emit2 ("inc hl"); + if (!regalloc_dry_run) + aopPut (AOP (result), "!*hl", 1); + regalloc_dry_run_cost += 3; + cheapMove (result->aop, 0, ASMOP_A, 0, true); + } + spillPair (PAIR_HL); + goto release; + } + + offsetPair (pair, extrapair, !isPairDead (extrapair, ic), rightval); + + if (pair == PAIR_HL + || (!IS_GB && (getPairId (AOP (left)) == PAIR_BC || getPairId (AOP (left)) == PAIR_DE) + && AOP_TYPE (result) == AOP_STK)) + { + size = AOP_SIZE (result); + offset = 0; + int last_offset = 0; + + /* might use ld a,(hl) followed by ld d (iy),a */ + if ((AOP_TYPE (result) == AOP_EXSTK || AOP_TYPE (result) == AOP_STK) && surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + + if (size >= 2 && pair == PAIR_HL && AOP_TYPE (result) == AOP_REG) + { + int i, l = -10, h = -10, r; + for (i = 0; i < size; i++) + { + if (AOP (result)->aopu.aop_reg[i]->rIdx == L_IDX) + l = i; + else if (AOP (result)->aopu.aop_reg[i]->rIdx == H_IDX) + h = i; + } + + if (l == -10 && h >= 0 && h < size - 1 || h == -10 && l >= 0 && l < size - 1) // One byte of result somewehere in hl. Just assign it last. + { + r = (l == -10 ? h : l); + + while (offset < size) + { + if (offset != r) + _moveFrom_tpair_ (AOP (result), offset, pair); + + if (offset < size) + { + offset++; + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset++; + } + } + + for (size = offset; size != r; size--) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + + _moveFrom_tpair_ (AOP (result), r, pair); + + // No fixup since result uses HL. + spillPair (pair); + goto release; + } + else if (l >= 0 && h >= 0) // Two bytes of result somewehere in hl. Assign it last and use a for caching. + { + while (offset < size) + { + last_offset = offset; + + if (IS_EZ80_Z80 && offset != l && offset != h && getPairId_o (result->aop, offset) != PAIR_INVALID) + { + emit2 ("ld %s, !*hl", _pairs[getPairId_o (result->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + if (offset < size) + { + emit2 ("inc %s", _pairs[pair].name); + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + _G.pairs[pair].offset += 2; + } + continue; + } + + if (offset != l && offset != h) + _moveFrom_tpair_ (AOP (result), offset, pair); + offset++; + + if (offset < size) + { + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset++; + } + } + + r = (l > h ? l : h); + for (size = last_offset; size != r; size--) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + _moveFrom_tpair_ (ASMOP_A, 0, pair); + + r = (l < h ? l : h); + for (; size != r; size--) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + _moveFrom_tpair_ (AOP (result), r, pair); + + r = (l > h ? l : h); + cheapMove (result->aop, r, ASMOP_A, 0, true); + + // No fixup since result uses HL. + spillPair (pair); + goto release; + } + } + + while (offset < size) + { + last_offset = offset; + + if (IS_EZ80_Z80 && getPairId_o (result->aop, offset) != PAIR_INVALID) + { + emit2 ("ld %s, !*hl", _pairs[getPairId_o (result->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + if (offset < size) + { + emit2 ("inc %s", _pairs[pair].name); + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 2; + _G.pairs[pair].offset += 2; + } + continue; + } + + _moveFrom_tpair_ (AOP (result), offset++, pair); + + if (offset < size) + { + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset++; + } + } + /* Fixup HL back down */ + if (getPairId (AOP (left)) == pair && !isPairDead (pair, ic) && !pushed_pair) + while (last_offset --> 0) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset--; + } + else if (rightval || AOP_SIZE (result)) + spillPair (pair); + } + else + { + size = AOP_SIZE (result); + offset = 0; + + for (offset = 0; offset < size;) + { + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + + /* PENDING: make this better */ + if ((pair == PAIR_HL) && AOP_TYPE (result) == AOP_REG) + { + if (!regalloc_dry_run) + aopPut (AOP (result), "!*hl", offset++); + regalloc_dry_run_cost += ld_cost (AOP (result), ASMOP_A); + } + else + { + emit2 ("ld a,!*pair", _pairs[pair].name); + regalloc_dry_run_cost += 1; + cheapMove (result->aop, offset++, ASMOP_A, 0, true); + } + if (offset < size) + { + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + _G.pairs[pair].offset++; + } + } + if (rightval || AOP_SIZE (result)) + spillPair (pair); + } + +release: + if (pushed_a) + _pop (PAIR_AF); + if (pushed_pair) + _pop (pair); + + freeAsmop (left, NULL); + freeAsmop (result, NULL); +} + +static bool +isRegOrLit (asmop * aop) +{ + if (aop->type == AOP_REG || aop->type == AOP_LIT || aop->type == AOP_IMMD) + return true; + return false; +} + + +/*-----------------------------------------------------------------*/ +/* genPackBits - generates code for packed bit storage */ +/*-----------------------------------------------------------------*/ +static void +genPackBits (sym_link * etype, operand * right, int pair, const iCode * ic) +{ + int offset = 0; /* source byte offset */ + int pair_offset = 0; + int rlen = 0; /* remaining bit-field length */ + unsigned blen; /* bit-field length */ + unsigned bstr; /* bit-field starting bit within byte */ + int litval; /* source literal value (if AOP_LIT) */ + unsigned char mask; /* bitmask within current byte */ + int extraPair; /* a tempory register */ + bool needPopExtra = 0; /* need to restore original value of temp reg */ + unsigned int pairincrement = 0; + + emitDebug ("; genPackBits", ""); + + blen = SPEC_BLEN (etype); + bstr = SPEC_BSTR (etype); + + /* If the bit-field length is less than a byte */ + if (blen < 8) + { + mask = ((unsigned char) (0xFF << (blen + bstr)) | (unsigned char) (0xFF >> (8 - bstr))); + + if (AOP_TYPE (right) == AOP_LIT && blen == 1 && (pair == PAIR_HL || pair == PAIR_IX || pair == PAIR_IY)) + { + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + emit2 (litval & 1 ? "set %d, !*pair" : "res %d, !*pair", bstr, _pairs[pair].name); + regalloc_dry_run_cost = (pair == PAIR_IX || pair == PAIR_IY) ? 4 : 2; + return; + } + else if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with a bit-field length <8 and literal source */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval <<= bstr; + litval &= (~mask) & 0xff; + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; + if ((mask | litval) != 0xff) + { + emit2 ("and a, !immedbyte", mask); + regalloc_dry_run_cost += 2; + } + if (litval) + { + emit2 ("or a, !immedbyte", litval); + regalloc_dry_run_cost += 1; + } + emit2 ("ld !*pair,a", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; + return; + } + else if (blen == 4 && bstr % 4 == 0 && pair == PAIR_HL && !aopInReg (right->aop, 0, A_IDX) && !requiresHL (right->aop) && (IS_Z80 || IS_Z180 || IS_EZ80_Z80)) + { + emit2 (bstr ? "rld" : "rrd"); + regalloc_dry_run_cost += 2; + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + emit2 (bstr ? "rrd" : "rld"); + regalloc_dry_run_cost += 2; + return; + } + else + { + /* Case with a bit-field length <8 and arbitrary source */ + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + /* shift and mask source value */ + if (blen + bstr == 8) + AccLsh (bstr); + else + { + AccRol (bstr); + emit2 ("and a, !immedbyte", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + } + + extraPair = getFreePairId (ic); + if (extraPair == PAIR_INVALID) + { + if (pair != PAIR_HL) + extraPair = PAIR_HL; + else + { + extraPair = PAIR_BC; + if (getPairId (AOP (right)) != PAIR_BC || !isLastUse (ic, right)) + { + _push (extraPair); + needPopExtra = 1; + } + } + } + emit2 ("ld %s, a", _pairs[extraPair].l); + spillPair (extraPair); + regalloc_dry_run_cost += 1; + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; + + emit2 ("and a, !immedbyte", mask); + regalloc_dry_run_cost += 2; + emit2 ("or a, %s", _pairs[extraPair].l); + regalloc_dry_run_cost += 1; + emit2 ("ld !*pair, a", _pairs[pair].name); + regalloc_dry_run_cost += (pair == PAIR_IX || pair == PAIR_IY) ? 3 : 1; + if (needPopExtra) + _pop (extraPair); + return; + } + } + + /* Bit length is greater than 7 bits. In this case, copy */ + /* all except the partial byte at the end */ + for (rlen = blen; rlen >= 8; rlen -= 8) + { + cheapMove (ASMOP_A, 0, AOP (right), offset++, true); + if (pair == PAIR_IX || pair == PAIR_IY) + { + emit2 ("ld %d !*pair,a", pair_offset, _pairs[pair].name); + regalloc_dry_run_cost += 3; + } + else + { + emit2 ("ld !*pair,a", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + if (rlen > 8 && pair != PAIR_IX && pair != PAIR_IY) + { + emit2 ("inc %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + pairincrement++; + _G.pairs[pair].offset++; + } + else + pair_offset++; + } + + /* If there was a partial byte at the end */ + if (rlen) + { + mask = (((unsigned char) - 1 << rlen) & 0xff); + + if (AOP_TYPE (right) == AOP_LIT) + { + /* Case with partial byte and literal source */ + litval = (int) ulFromVal (AOP (right)->aopu.aop_lit); + litval >>= (blen - rlen); + litval &= (~mask) & 0xff; + + if (pair == PAIR_IX || pair == PAIR_IY) + { + emit2 ("ld a, %d !*pair", pair_offset, _pairs[pair].name); + regalloc_dry_run_cost += 3; + } + else + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + + if ((mask | litval) != 0xff) + emit2 ("and a, !immedbyte", mask); + if (litval) + emit2 ("or a, !immedbyte", litval); + } + else + { + /* Case with partial byte and arbitrary source */ + cheapMove (ASMOP_A, 0, AOP (right), offset++, true); + emit2 ("and a, !immedbyte", (~mask) & 0xff); + regalloc_dry_run_cost += 2; + + extraPair = getFreePairId (ic); + if (extraPair == PAIR_INVALID) + { + if (pair != PAIR_HL) + extraPair = PAIR_HL; + else + { + extraPair = PAIR_BC; + if (getPairId (AOP (right)) != PAIR_BC || !isLastUse (ic, right)) + { + _push (extraPair); + needPopExtra = 1; + } + } + } + + emit2 ("ld %s,a", _pairs[extraPair].l); + spillPair (extraPair); + regalloc_dry_run_cost += 1; + + if (pair == PAIR_IX || pair == PAIR_IY) + { + emit2 ("ld a, %d !*pair", pair_offset, _pairs[pair].name); + regalloc_dry_run_cost += 3; + } + else + { + emit2 ("ld a, !*pair", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + + emit2 ("and a, !immedbyte", mask); + regalloc_dry_run_cost += 2; + emit2 ("or a, %s", _pairs[extraPair].l); + regalloc_dry_run_cost += 1; + if (needPopExtra) + _pop (extraPair); + + } + if (pair == PAIR_IX || pair == PAIR_IY) + { + emit2 ("ld %d !*pair, a", pair_offset, _pairs[pair].name); + regalloc_dry_run_cost += 3; + } + else + { + emit2 ("ld !*pair, a", _pairs[pair].name); + regalloc_dry_run_cost += 1; + } + } + if (!isPairDead(pair, ic)) + while (pairincrement) + { + emit2 ("dec %s", _pairs[pair].name); + regalloc_dry_run_cost += 1; + pairincrement--; + _G.pairs[pair].offset--; + } +} + +/*-----------------------------------------------------------------*/ +/* genPointerSet - stores the value into a pointer location */ +/*-----------------------------------------------------------------*/ +static void +genPointerSet (iCode *ic) +{ + int size, offset = 0; + int last_offset = 0; + operand *right, *result; + PAIR_ID pairId = PAIR_HL; + bool isBitvar; + sym_link *retype; + sym_link *letype; + bool pushed_a = FALSE; + bool surviving_a = !options.oldralloc && bitVectBitValue (ic->rSurv, A_IDX); + + right = IC_RIGHT (ic); + result = IC_RESULT (ic); + retype = getSpec (operandType (right)); + letype = getSpec (operandType (result)); + + aopOp (result, ic, FALSE, FALSE); + aopOp (right, ic, FALSE, FALSE); + + if (IS_GB) + pairId = isRegOrLit (AOP (right)) ? PAIR_HL : PAIR_DE; + else if (IY_RESERVED) + pairId = (isRegOrLit (AOP (right)) || AOP_TYPE (right) == AOP_STK) ? PAIR_HL : PAIR_DE; + if (isPair (AOP (result)) && isPairDead (getPairId (AOP (result)), ic)) + pairId = getPairId (AOP (result)); + + size = AOP_SIZE (right); + + isBitvar = IS_BITVAR (retype) || IS_BITVAR (letype); + emitDebug ("; isBitvar = %d", isBitvar); + + /* Handle the exceptions first */ + if (isPair (AOP (result)) && size == 1 && !isBitvar) + { + /* Just do it */ + const char *pair = getPairName (AOP (result)); + if (canAssignToPtr3 (AOP (right)) && isPtr (pair)) // Todo: correct cost for pair iy. + { + if (!regalloc_dry_run) + emit2 ("ld !*pair, %s", pair, aopGet (AOP (right), 0, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_A, AOP (right)) + (getPairId (AOP (result)) != PAIR_IY ? 0 : 2); + } + else + { + if (surviving_a && !pushed_a && !aopInReg (right->aop, 0, A_IDX)) + _push (PAIR_AF), pushed_a = TRUE; + if (AOP_TYPE (right) == AOP_LIT && byteOfVal (AOP (right)->aopu.aop_lit, offset) == 0x00) + emit3 (A_XOR, ASMOP_A, ASMOP_A); + else + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + emit2 ("ld !*pair, a", pair); + regalloc_dry_run_cost += (getPairId (AOP (result)) != PAIR_IY ? 1 : 3); + } + goto release; + } + + /* Using ldir is cheapest for large memory-to-memory transfers. */ + if (!IS_GB && (AOP_TYPE (right) == AOP_STK || AOP_TYPE (right) == AOP_EXSTK) && size > 2) + { + int fp_offset, sp_offset; + + if(!isPairDead (PAIR_DE, ic)) + _push (PAIR_DE); + if(!isPairDead (PAIR_BC, ic)) + _push (PAIR_BC); + if(!isPairDead (PAIR_HL, ic)) + _push (PAIR_HL); + + fetchPair (PAIR_DE, AOP (result)); + + fp_offset = AOP (right)->aopu.aop_stk + (AOP (right)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0); + sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2 ("ld hl, !immedword", sp_offset); + emit2 ("add hl, sp"); + emit2 ("ld bc, !immedword", size); + emit2 ("ldir"); + regalloc_dry_run_cost += 9; + + if(!isPairDead (PAIR_HL, ic)) + _pop (PAIR_HL); + if(!isPairDead (PAIR_BC, ic)) + _pop (PAIR_BC); + if(!isPairDead (PAIR_DE, ic)) + _pop (PAIR_DE); + goto release; + } + + if (getPairId (AOP (result)) == PAIR_IY && !isBitvar) + { + /* Just do it */ + while (size--) + { + if (canAssignToPtr3 (AOP (right))) + { + if (!regalloc_dry_run) + emit2 ("ld !*iyx, %s", offset, aopGet (AOP (right), offset, FALSE)); + regalloc_dry_run_cost += 3; // Todo: More exact cost here! + } + else + { + cheapMove (ASMOP_A, 0, AOP (right), offset, true); + emit2 ("ld !*iyx, a", offset); + regalloc_dry_run_cost += 3; + } + offset++; + } + goto release; + } + else if (getPairId (result->aop) == PAIR_HL && !isPairDead (PAIR_HL, ic) && !isBitvar) + { + while (offset < size) + { + last_offset = offset; + + if (IS_EZ80_Z80 && offset + 1 < size && getPairId_o (right->aop, offset) != PAIR_INVALID) + { + emit2 ("ld !*pair, %s", _pairs[PAIR_HL].name, _pairs[getPairId_o (right->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + + if (offset < size) + { + emit2 ("inc %s", _pairs[PAIR_HL].name); + emit2 ("inc %s", _pairs[PAIR_HL].name); + regalloc_dry_run_cost += 2; + _G.pairs[PAIR_HL].offset++; + } + + continue; + } + else if (isRegOrLit (AOP (right)) && !IS_GB) + { + if (!regalloc_dry_run) + emit2 ("ld !*pair, %s", _pairs[PAIR_HL].name, aopGet (right->aop, offset, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_A, right->aop); + offset++; + } + else + { + if (surviving_a && !pushed_a && (!aopInReg (right->aop, 0, A_IDX) || offset)) + _push (PAIR_AF), pushed_a = TRUE; + cheapMove (ASMOP_A, 0, right->aop, offset, true); + emit2 ("ld !*pair, a", _pairs[PAIR_HL].name); + regalloc_dry_run_cost += 1; + offset++; + } + + if (offset < size) + { + emit2 ("inc %s", _pairs[PAIR_HL].name); + regalloc_dry_run_cost += 1; + _G.pairs[PAIR_HL].offset++; + } + } + + /* Fixup HL back down */ + while (last_offset --> 0) + { + emit2 ("dec %s", _pairs[PAIR_HL].name); + regalloc_dry_run_cost += 1; + } + goto release; + } + + if (!IS_GB && !isBitvar && isLitWord (AOP (result)) && size == 2 && offset == 0 && + (AOP_TYPE (right) == AOP_REG && getPairId (AOP (right)) != PAIR_INVALID || isLitWord (AOP (right)))) + { + if (isLitWord (AOP (right))) + { + pairId = PAIR_HL; + fetchPairLong (pairId, AOP (right), ic, 0); + } + else + pairId = getPairId (AOP (right)); + emit2 ("ld (%s), %s", aopGetLitWordLong (AOP (result), offset, FALSE), _pairs[pairId].name); + regalloc_dry_run_cost += (pairId == PAIR_HL) ? 3 : 4; + goto release; + } + if (!IS_GB && !isBitvar && isLitWord (AOP (result)) && size == 4 && offset == 0 && + (getPartPairId (AOP (right), 0) != PAIR_INVALID && getPartPairId (AOP (right), 2) != PAIR_INVALID || isLitWord (AOP (right)))) + { + if (isLitWord (AOP (right))) + { + pairId = PAIR_HL; + fetchPairLong (pairId, AOP (right), ic, 0); + } + else + pairId = getPartPairId (AOP (right), 0); + emit2 ("ld (%s), %s", aopGetLitWordLong (AOP (result), offset, FALSE), _pairs[pairId].name); + regalloc_dry_run_cost += (pairId == PAIR_HL) ? 3 : 4; + if (isLitWord (AOP (right))) + { + pairId = PAIR_HL; + fetchPairLong (pairId, AOP (right), ic, 2); + } + else + pairId = getPartPairId (AOP (right), 2); + emit2 ("ld (%s+%d), %s", aopGetLitWordLong (AOP (result), offset, FALSE),2, _pairs[pairId].name); // Handling of literal addresses is somewhat broken, use explicit offset as workaround. + regalloc_dry_run_cost += (pairId == PAIR_HL) ? 3 : 4; + goto release; + } + + /* if the operand is already in dptr + then we do nothing else we move the value to dptr */ + if (AOP_TYPE (result) != AOP_STR) + { + if (isBitvar && getPairId (AOP (result)) != PAIR_INVALID && (getPairId (AOP (result)) != PAIR_IY || SPEC_BLEN (IS_BITVAR (retype) ? retype : letype) < 8 || isPairDead (getPairId (AOP (result)), ic))) /* Avoid destroying result by increments */ + pairId = getPairId (AOP (result)); + else + fetchPairLong (pairId, AOP (result), ic, 0); + } + /* so hl now contains the address */ + /*freeAsmop (result, NULL, ic);*/ + + /* if bit then unpack */ + if (isBitvar) + { + genPackBits ((IS_BITVAR (retype) ? retype : letype), right, pairId, ic); + goto release; + } + else + { + bool zero_a = false; + + for (offset = 0; offset < size;) + { + last_offset = offset; + + if (IS_EZ80_Z80 && offset + 1 < size && pairId == PAIR_HL && getPairId_o (right->aop, offset) != PAIR_INVALID) + { + emit2 ("ld !*pair, %s", _pairs[pairId].name, _pairs[getPairId_o (right->aop, offset)].name); + regalloc_dry_run_cost += 2; + offset += 2; + + if (offset < size) + { + emit2 ("inc %s", _pairs[pairId].name); + emit2 ("inc %s", _pairs[pairId].name); + regalloc_dry_run_cost += 2; + _G.pairs[pairId].offset++; + } + + continue; + } + + if (!zero_a && offset + 1 < size && aopIsLitVal (right->aop, offset, 2, 0x0000) && !surviving_a) + { + emit2 ("xor a, a"); + regalloc_dry_run_cost++; + zero_a = true; + } + + if (aopIsLitVal (right->aop, offset, 1, 0x00) && zero_a) + { + emit2 ("ld !*pair, a", _pairs[pairId].name); + regalloc_dry_run_cost++; + } + else if (isRegOrLit (right->aop) && pairId == PAIR_HL) + { + if (!regalloc_dry_run) + emit2 ("ld !*pair, %s", _pairs[pairId].name, aopGet (AOP (right), offset, FALSE)); + regalloc_dry_run_cost += ld_cost (ASMOP_A, AOP (right)); + } + else + { + if (surviving_a && !pushed_a && (!aopInReg (right->aop, 0, A_IDX) || offset)) + _push (PAIR_AF), pushed_a = TRUE; + cheapMove (ASMOP_A, 0, AOP (right), offset, true); + zero_a = false; + emit2 ("ld !*pair, a", _pairs[pairId].name); + regalloc_dry_run_cost += 1; + } + offset++; + + if (offset < size) + { + emit2 ("inc %s", _pairs[pairId].name); + regalloc_dry_run_cost += 1; + _G.pairs[pairId].offset++; + } + } + /* Restore operand partially in HL. */ + if (!isPairDead (pairId, ic) && AOP (result)->type == AOP_REG) + { + while(last_offset --> 0) + { + emit2 ("dec %s", _pairs[pairId].name); + regalloc_dry_run_cost += 1; + _G.pairs[pairId].offset--; + } + commitPair (AOP (result), pairId, ic, FALSE); + } + } +release: + if (pushed_a) + _pop (PAIR_AF); + + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genIfx - generate code for Ifx statement */ +/*-----------------------------------------------------------------*/ +static void +genIfx (iCode *ic, iCode *popIc) +{ + operand *cond = IC_COND (ic); + int isbit = 0; + + aopOp (cond, ic, FALSE, TRUE); + + /* get the value into acc */ + if (AOP_TYPE (cond) != AOP_CRY && !IS_BOOL (operandType (cond))) + _toBoolean (cond, !popIc); + /* Special case: Condition is bool */ + else if (IS_BOOL (operandType (cond))) + { + if (!regalloc_dry_run) + { + emit2 ("bit 0, %s", aopGet (AOP (cond), 0, FALSE)); + emit2 ("jp %s, !tlabel", IC_TRUE (ic) ? "NZ" : "Z", labelKey2num ((IC_TRUE (ic) ? IC_TRUE (ic) : IC_FALSE (ic))->key)); + } + regalloc_dry_run_cost += (bit8_cost (AOP (cond)) + 3); + + freeAsmop (cond, NULL); + if (!regalloc_dry_run) + ic->generated = 1; + return; + } + else + isbit = 1; + /* the result is now in the accumulator */ + freeAsmop (cond, NULL); + + /* if there was something to be popped then do it */ + if (popIc) + genIpop (popIc); + + /* if the condition is a bit variable */ + if (isbit && IS_ITEMP (cond) && SPIL_LOC (cond)) + genIfxJump (ic, SPIL_LOC (cond)->rname); + else if (isbit && !IS_ITEMP (cond)) + genIfxJump (ic, OP_SYMBOL (cond)->rname); + else + genIfxJump (ic, popIc ? "a" : "nz"); + + if (!regalloc_dry_run) + ic->generated = 1; +} + +/*-----------------------------------------------------------------*/ +/* genAddrOf - generates code for address of */ +/*-----------------------------------------------------------------*/ +static void +genAddrOf (const iCode * ic) +{ + symbol *sym; + PAIR_ID pair; + operand *right = IC_RIGHT (ic); + wassert (IS_TRUE_SYMOP (IC_LEFT (ic))); + wassert (right && IS_OP_LITERAL (IC_RIGHT (ic))); + sym = OP_SYMBOL (IC_LEFT (ic)); + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + if (sym->onStack) + { + int fp_offset = sym->stack + (sym->stack > 0 ? _G.stack.param_offset : 0) + (int)operandLitValue (right); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + bool in_fp_range = !_G.omitFramePtr && (fp_offset >= -128 && fp_offset < 128); + + if (IS_EZ80_Z80 && in_fp_range && getPairId (IC_RESULT (ic)->aop) != PAIR_INVALID) + pair = getPairId (IC_RESULT (ic)->aop); + else + pair = (getPairId (IC_RESULT (ic)->aop) == PAIR_IY) ? PAIR_IY : PAIR_HL; + spillPair (pair); + if (IS_EZ80_Z80 && in_fp_range) + { + emit2 ("lea %s, ix, #%d", _pairs[pair].name, fp_offset); + regalloc_dry_run_cost += 3; + } + else + setupPairFromSP (pair, sp_offset); + } + else + { + pair = getPairId (IC_RESULT (ic)->aop); + if (pair == PAIR_INVALID) + { + pair = IS_GB ? PAIR_DE : PAIR_HL; + spillPair (pair); + } + emit2 ("ld %s, !hashedstr+%ld", _pairs[pair].name, sym->rname, (long)(operandLitValue (right))); + regalloc_dry_run_cost += (pair == PAIR_IY ? 4 : 3); + } + + commitPair (IC_RESULT (ic)->aop, pair, ic, FALSE); + + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genAssign - generate code for assignment */ +/*-----------------------------------------------------------------*/ +static void +genAssign (const iCode * ic) +{ + operand *result, *right; + int size, offset; + + result = IC_RESULT (ic); + right = IC_RIGHT (ic); + + /* Dont bother assigning if they are the same */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE, FALSE); + aopOp (result, ic, TRUE, FALSE); + + /* if they are the same registers */ + if (sameRegs (AOP (right), AOP (result))) + { + emitDebug ("; (locations are the same)"); + goto release; + } + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + wassertl (0, "Tried to assign to a bit"); + } + + /* general case */ + size = AOP_SIZE (result); + offset = 0; + + if (isPair (AOP (result))) + fetchPairLong (getPairId (AOP (result)), AOP (right), ic, LSB); + else if (isPair (AOP (right)) && AOP_TYPE (result) == AOP_IY && size == 2) + commitPair (AOP (result), getPairId (AOP (right)), ic, FALSE); + else if (size == 2 && isPairDead (PAIR_HL, ic) && + (!IS_GB && (AOP_TYPE (right) == AOP_STK && !_G.omitFramePtr || AOP_TYPE (right) == AOP_IY || AOP_TYPE (right) == AOP_LIT) && AOP_TYPE (result) == AOP_IY || // Use ld (nn), hl + !IS_GB && AOP_TYPE (right) == AOP_IY && (AOP_TYPE (result) == AOP_STK && !_G.omitFramePtr || AOP_TYPE (result) == AOP_IY) || // Use ld hl, (nn) + !IS_GB && AOP_TYPE (right) == AOP_LIT && (AOP_TYPE(result) == AOP_STK || AOP_TYPE(result) == AOP_EXSTK) && (AOP(result)->aopu.aop_stk + offset + _G.stack.offset + (AOP(result)->aopu.aop_stk > 0 ? _G.stack.param_offset : 0) + _G.stack.pushed) == 0 || // Use ex (sp), hl + (IS_RAB || IS_TLCS90) && (AOP_TYPE(result) == AOP_STK || AOP_TYPE(result) == AOP_EXSTK) && (AOP_TYPE(right) == AOP_LIT || AOP_TYPE (right) == AOP_IMMD))) // Use ld d(sp), hl + { + fetchPair (PAIR_HL, AOP (right)); + commitPair (AOP (result), PAIR_HL, ic, FALSE); + } + else if (size == 2 && getPairId (AOP (right)) != PAIR_INVALID && getPairId (AOP (right)) != PAIR_IY && AOP_TYPE (result) != AOP_REG) + { + commitPair (AOP (result), getPairId (AOP (right)), ic, TRUE); + } + else if (getPairId (AOP (right)) == PAIR_IY) + { + while (size--) + { + if (size == 0) + { + if (IS_TLCS90) + { + emit2 ("push iy"); + emit2 ("ld a, 0(sp)"); + emit2 ("inc sp"); + emit2 ("inc sp"); + cost (5, 26); + } + else + { + emit2 ("push iy"); + emit2 ("dec sp"); + emit2 ("pop af"); + emit2 ("inc sp"); + regalloc_dry_run_cost += 5; + } + if (AOP_TYPE (result) == AOP_IY) /* Take care not to overwrite iy */ + { + emit2 ("ld (%s+%d), a", AOP (result)->aopu.aop_dir, size); + regalloc_dry_run_cost += 3; + } + else + cheapMove (AOP (result), size, ASMOP_A, 0, true); + } + else if (size == 1) + { + if (AOP_TYPE (result) == AOP_IY) /* Take care not to overwrite iy */ + { + emit2 ("ld (%s), iy", AOP (result)->aopu.aop_dir); + regalloc_dry_run_cost += 4; + size--; + } + else if (AOP_TYPE (result) == AOP_EXSTK || IS_TLCS90) /* Take care not to overwrite iy */ + { + bool pushed_pair = FALSE; + PAIR_ID pair = getDeadPairId (ic); + if (pair == PAIR_INVALID) + { + pair = PAIR_HL; + _push(pair); + pushed_pair= TRUE; + } + fetchPair (pair, AOP (right)); + commitPair (AOP (result), pair, ic, FALSE); + if (pushed_pair) + _pop (pair); + size--; + } + else + { + emit2 ("push iy"); + emit2 ("pop af"); + regalloc_dry_run_cost += 3; + cheapMove (AOP (result), size, ASMOP_A, 0, true); + } + } + else + { + if (AOP_TYPE (result) == AOP_IY) /* Take care not to overwrite iy */ + { + cheapMove (ASMOP_A, 0, ASMOP_ZERO, 0, true); + emit2 ("ld (%s+%d), a", AOP (result)->aopu.aop_dir, size); + regalloc_dry_run_cost += 3; + } + else + cheapMove (AOP (result), size, ASMOP_ZERO, 0, true); + } + } + } + else if (size == 2 && requiresHL (AOP (right)) && requiresHL (AOP (result)) && isPairDead (PAIR_DE, ic) && (IS_GB /*|| IY_RESERVED */ )) + { + /* Special case. Load into a and d, then load out. */ + cheapMove (ASMOP_A, 0, AOP (right), 0, true); + emit3_o (A_LD, ASMOP_E, 0, AOP (right), 1); + cheapMove (AOP (result), 0, ASMOP_A, 0, true); + cheapMove (AOP (result), 1, ASMOP_E, 0, true); + } + else if (size == 4 && requiresHL (AOP (right)) && requiresHL (AOP (result)) && isPairDead (PAIR_DE, ic) && (IS_GB /*|| IY_RESERVED */ )) + { + /* Special case - simple memcpy */ + if (!regalloc_dry_run) + { + aopGet (AOP (right), LSB, FALSE); + emit2 ("ld d, h"); + emit2 ("ld e, l"); + aopGet (AOP (result), LSB, FALSE); + } + regalloc_dry_run_cost += 8; // Todo: More exact cost here! + + while (size--) + { + emit2 ("ld a, (de)"); + /* Peephole will optimise this. */ + emit2 ("ld (hl), a"); + regalloc_dry_run_cost += 2; + if (size != 0) + { + emit2 ("inc hl"); + emit2 ("inc de"); + regalloc_dry_run_cost += 2; + } + } + spillPair (PAIR_HL); + } + else + { + if (!IS_GB && /* gbz80 doesn't have ldir */ + (AOP_TYPE (result) == AOP_STK || AOP_TYPE (result) == AOP_EXSTK || AOP_TYPE (result) == AOP_DIR + || AOP_TYPE (result) == AOP_IY) && (AOP_TYPE (right) == AOP_STK || AOP_TYPE (right) == AOP_EXSTK + || AOP_TYPE (right) == AOP_DIR || AOP_TYPE (right) == AOP_IY) && size >= 2) + { + /* This estimation is only accurate, if neither operand is AOP_EXSTK, and we are optimizing for code size or targeting the z80 or z180. */ + int sizecost_n, sizecost_l, cyclecost_n, cyclecost_l; + const bool hl_alive = !isPairDead (PAIR_HL, ic); + const bool de_alive = !isPairDead (PAIR_DE, ic); + const bool bc_alive = !isPairDead (PAIR_BC, ic); + bool l_better; + sizecost_n = 6 * size; + sizecost_l = 13 + hl_alive * 2 + de_alive * 2 + bc_alive * 2 - (AOP_TYPE (right) == AOP_DIR + || AOP_TYPE (right) == AOP_IY) - (AOP_TYPE (result) == + AOP_DIR + || AOP_TYPE (result) + == AOP_IY) * 2; + if (IS_Z180 || IS_EZ80_Z80) + cyclecost_n = 30 * size; + else /* Z80 */ + cyclecost_n = 38 * size; + if (IS_Z180 || IS_EZ80_Z80) + cyclecost_l = 14 * size + 42 + hl_alive * 22 + de_alive * 22 + bc_alive * 22 - (AOP_TYPE (right) == AOP_DIR + || AOP_TYPE (right) == + AOP_IY) * 7 - (AOP_TYPE (result) == + AOP_DIR + || AOP_TYPE (result) + == AOP_IY) * 10; + else /* Z80 */ + cyclecost_l = 21 * size + 51 + hl_alive * 20 + de_alive * 20 + bc_alive * 20 - (AOP_TYPE (right) == AOP_DIR + || AOP_TYPE (right) == + AOP_IY) * 11 - (AOP_TYPE (result) == + AOP_DIR + || AOP_TYPE (result) + == AOP_IY) * 15; + + if (optimize.codeSize) + l_better = (sizecost_l < sizecost_n || sizecost_l == sizecost_n && cyclecost_l < cyclecost_n); + else + l_better = (cyclecost_l < cyclecost_n || cyclecost_l == cyclecost_n && sizecost_l < sizecost_n); + if (l_better) + { + if (hl_alive) + _push (PAIR_HL); + if (de_alive) + _push (PAIR_DE); + if (bc_alive) + _push (PAIR_BC); + + if (AOP_TYPE (result) == AOP_STK || AOP_TYPE (result) == AOP_EXSTK) + { + int fp_offset = + AOP (result)->aopu.aop_stk + offset + (AOP (result)->aopu.aop_stk > + 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2 ("ld hl, #%d", sp_offset); + emit2 ("add hl, sp"); + emit2 ("ex de, hl"); + regalloc_dry_run_cost += 5; + } + else + { + emit2 ("ld de, #%s", AOP (IC_RESULT (ic))->aopu.aop_dir); + regalloc_dry_run_cost += 3; + } + + if (AOP_TYPE (right) == AOP_STK || AOP_TYPE (right) == AOP_EXSTK) + { + int fp_offset = + AOP (right)->aopu.aop_stk + offset + (AOP (right)->aopu.aop_stk > + 0 ? _G.stack.param_offset : 0); + int sp_offset = fp_offset + _G.stack.pushed + _G.stack.offset; + emit2 ("ld hl, #%d", sp_offset); + emit2 ("add hl, sp"); + regalloc_dry_run_cost += 4; + } + else + { + emit2 ("ld hl, #%s", AOP (IC_RIGHT (ic))->aopu.aop_dir); + regalloc_dry_run_cost += 3; + } + spillPair (PAIR_HL); + + if (size <= 2 + optimize.codeSpeed) + for(int i = 0; i < size; i++) + { + emit2 ("ldi"); + regalloc_dry_run_cost += 2; + } + else + { + emit2 ("ld bc, #%d", size); + emit2 ("ldir"); + regalloc_dry_run_cost += 5; + } + + if (bc_alive) + _pop (PAIR_BC); + if (de_alive) + _pop (PAIR_DE); + if (hl_alive) + _pop (PAIR_HL); + + goto release; + } + } + if ((result->aop->type == AOP_REG || result->aop->type == AOP_STK || result->aop->type == AOP_EXSTK) && (right->aop->type == AOP_REG || right->aop->type == AOP_STK || right->aop->type == AOP_LIT)) + genMove (result->aop, right->aop, !bitVectBitValue (ic->rSurv, A_IDX), isPairDead (PAIR_HL, ic)); + else + while (size--) + { + /* PENDING: do this check better */ + if ((IS_GB || IY_RESERVED) && requiresHL (AOP (right)) && requiresHL (AOP (result))) + { + _push (PAIR_HL); + cheapMove (ASMOP_A, 0, AOP (right), offset, true); + cheapMove (AOP (result), offset, ASMOP_A, 0, true); + _pop (PAIR_HL); + spillPair (PAIR_HL); + } + else + cheapMove (AOP (result), offset, AOP (right), offset, true); + offset++; + } + } + +release: + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genJumpTab - generate code for jump table */ +/*-----------------------------------------------------------------*/ +static void +genJumpTab (const iCode *ic) +{ + symbol *jtab = NULL; + operand *jtcond = IC_JTCOND (ic); + bool pushed_pair = FALSE; + PAIR_ID pair; + + aopOp (jtcond, ic, FALSE, FALSE); + + // Choose extra pair DE or BC for addition + if (AOP_TYPE (jtcond) == AOP_REG && AOP (jtcond)->aopu.aop_reg[0]->rIdx == E_IDX && isPairDead (PAIR_DE, ic)) + pair = PAIR_DE; + else if (AOP_TYPE (jtcond) == AOP_REG && AOP (jtcond)->aopu.aop_reg[0]->rIdx == C_IDX && isPairDead (PAIR_BC, ic)) + pair = PAIR_BC; + else if ((pair = getDeadPairId (ic)) == PAIR_INVALID) + pair = PAIR_DE; + + if (!isPairDead (pair, ic)) + { + _push (pair); + pushed_pair = TRUE; + } + + cheapMove (pair == PAIR_DE ? ASMOP_E : ASMOP_C, 0, AOP (jtcond), 0, true); + if (!regalloc_dry_run) + { + emit2 ("ld %s, !zero", _pairs[pair].h); + jtab = newiTempLabel (NULL); + } + regalloc_dry_run_cost += 2; + spillPair (PAIR_HL); + if (!regalloc_dry_run) + { + emit2 ("ld hl, !immed!tlabel", labelKey2num (jtab->key)); + emit2 ("add hl, %s", _pairs[pair].name); + emit2 ("add hl, %s", _pairs[pair].name); + emit2 ("add hl, %s", _pairs[pair].name); + } + regalloc_dry_run_cost += 5; + freeAsmop (IC_JTCOND (ic), NULL); + + if (pushed_pair) + _pop (pair); + + if (!regalloc_dry_run) + { + emit2 ("jp !*hl"); + emitLabelSpill (jtab); + } + regalloc_dry_run_cost += 1; + /* now generate the jump labels */ + for (jtab = setFirstItem (IC_JTLABELS (ic)); jtab; jtab = setNextItem (IC_JTLABELS (ic))) + if (!regalloc_dry_run) + emit2 ("jp !tlabel", labelKey2num (jtab->key)); + /*regalloc_dry_run_cost += 3 doesn't matter and might overflow cost */ +} + +/*-----------------------------------------------------------------*/ +/* genCast - gen code for casting */ +/*-----------------------------------------------------------------*/ +static void +genCast (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + sym_link *rtype = operandType (IC_RIGHT (ic)); + operand *right = IC_RIGHT (ic); + int size, offset; + bool surviving_a = !options.oldralloc && bitVectBitValue (ic->rSurv, A_IDX); + bool pushed_a = FALSE; + + /* if they are equivalent then do nothing */ + if (operandsEqu (IC_RESULT (ic), IC_RIGHT (ic))) + return; + + aopOp (right, ic, FALSE, FALSE); + aopOp (result, ic, FALSE, FALSE); + + /* if the result is a bit */ + if (AOP_TYPE (result) == AOP_CRY) + { + wassertl (0, "Tried to cast to a bit"); + } + + /* casting to bool */ + if (IS_BOOL (operandType (result)) && IS_RAB && right->aop->size == 2 && + (aopInReg (right->aop, 0, HL_IDX) && isPairDead (PAIR_HL, ic)|| aopInReg (right->aop, 0, IY_IDX) && isPairDead (PAIR_IY, ic))) + { + bool iy = aopInReg (right->aop, 0, IY_IDX); + emit2 ("bool %s", _pairs[getPairId (right->aop)].name); + cheapMove (result->aop, 0, iy ? ASMOP_IYL : ASMOP_L, 0, !bitVectBitValue (ic->rSurv, A_IDX)); + goto release; + } + if (IS_BOOL (operandType (result))) + { + _castBoolean (right); + outAcc (result); + goto release; + } + + /* if they are the same size or less */ + if (AOP_SIZE (result) <= AOP_SIZE (right)) + { + genAssign (ic); + goto release; + } + + /* So we now know that the size of destination is greater + than the size of the source */ + genMove_o (result->aop, 0, right->aop, 0, right->aop->size - 1, true, isPairDead (PAIR_HL, ic)); + + /* now depending on the sign of the destination */ + size = result->aop->size - right->aop->size; + offset = right->aop->size - 1; + /* Unsigned or not an integral type - fill with zeros */ + if (IS_BOOL (rtype) || !IS_SPEC (rtype) || SPEC_USIGN (rtype) || AOP_TYPE (right) == AOP_CRY) + { + cheapMove (result->aop, offset, right->aop, offset, true); + offset++; + genMove_o (result->aop, offset, ASMOP_ZERO, 0, size, true, false); + } + else + { + if (surviving_a && !pushed_a) + _push (PAIR_AF), pushed_a = TRUE; + + cheapMove (ASMOP_A, 0, AOP (right), offset, true); + if (AOP (right)->type != AOP_REG || AOP (result)->type != AOP_REG || AOP (right)->aopu.aop_reg[offset] != AOP (result)->aopu.aop_reg[offset]) + cheapMove (AOP (result), offset, ASMOP_A, 0, true); + offset++; + + /* we need to extend the sign */ + emit3 (A_RLA, 0, 0); + emit3 (A_SBC, ASMOP_A, ASMOP_A); + while (size--) + cheapMove (AOP (result), offset++, ASMOP_A, 0, true); + } + +release: + if (pushed_a) + _pop (PAIR_AF); + freeAsmop (right, NULL); + freeAsmop (result, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genReceive - generate code for a receive iCode */ +/*-----------------------------------------------------------------*/ +static void +genReceive (const iCode *ic) +{ + operand *result = IC_RESULT (ic); + aopOp (result, ic, FALSE, FALSE); + + genMove (result->aop, ASMOP_RETURN, true, isPairDead (PAIR_HL, ic)); + + freeAsmop (IC_RESULT (ic), NULL); +} + +/*-----------------------------------------------------------------*/ +/* genDummyRead - generate code for dummy read of volatiles */ +/*-----------------------------------------------------------------*/ +static void +genDummyRead (const iCode * ic) +{ + operand *op; + int size, offset; + + op = IC_RIGHT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE, FALSE); + + /* general case */ + size = AOP_SIZE (op); + offset = 0; + + while (size--) + { + _moveA3 (AOP (op), offset); + offset++; + } + + freeAsmop (op, NULL); + } + + op = IC_LEFT (ic); + if (op && IS_SYMOP (op)) + { + aopOp (op, ic, FALSE, FALSE); + + /* general case */ + size = AOP_SIZE (op); + offset = 0; + + while (size--) + { + _moveA3 (AOP (op), offset); + offset++; + } + + freeAsmop (op, NULL); + } +} + +/*-----------------------------------------------------------------*/ +/* genCritical - generate code for start of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genCritical (const iCode * ic) +{ + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + + if (IS_GB || IS_RAB || IS_TLCS90) + { + emit2 ("!di"); + regalloc_dry_run_cost += 1; + } + else if (IC_RESULT (ic)) + { + aopOp (IC_RESULT (ic), ic, true, false); + cheapMove (IC_RESULT (ic)->aop, 0, ASMOP_ZERO, 0, true); + if (!regalloc_dry_run) + { + //get interrupt enable flag IFF2 into P/O + emit2 ("ld a,i"); + + //disable interrupt + emit2 ("!di"); + //parity odd <==> P/O=0 <==> interrupt enable flag IFF2=0 + emit2 ("jp PO, !tlabel", labelKey2num (tlbl->key)); + } + regalloc_dry_run_cost += 5; + cheapMove (IC_RESULT (ic)->aop, 0, ASMOP_ONE, 0, true); + if (!regalloc_dry_run) + { + emit2 ("!tlabeldef", labelKey2num ((tlbl->key))); + genLine.lineCurr->isLabel = 1; + } + freeAsmop (IC_RESULT (ic), NULL); + } + else + { + //get interrupt enable flag IFF2 into P/O + emit2 ("ld a,i"); + + //disable interrupt + emit2 ("!di"); + regalloc_dry_run_cost += 2; + //save P/O flag + if (!regalloc_dry_run) // _push unbalances _G.stack.pushed. + _push (PAIR_AF); + else + regalloc_dry_run_cost++; + } +} + +/*-----------------------------------------------------------------*/ +/* genEndCritical - generate code for end of a critical sequence */ +/*-----------------------------------------------------------------*/ +static void +genEndCritical (const iCode * ic) +{ + symbol *tlbl = regalloc_dry_run ? 0 : newiTempLabel (0); + + if (IS_GB || IS_TLCS90) + { + emit2 ("!ei"); + regalloc_dry_run_cost += 1; + } + else if (IS_RAB) + { + emit2 ("ipres"); + regalloc_dry_run_cost += 1; + } + else if (IC_RIGHT (ic)) + { + aopOp (IC_RIGHT (ic), ic, FALSE, TRUE); + _toBoolean (IC_RIGHT (ic), TRUE); + + if (!regalloc_dry_run) + { + //don't enable interrupts if they were off before + emit2 ("jp Z, !tlabel", labelKey2num (tlbl->key)); + emit2 ("!ei"); + emitLabelSpill (tlbl); + } + regalloc_dry_run_cost += 4; + freeAsmop (IC_RIGHT (ic), NULL); + } + else + { + //restore P/O flag + if (!regalloc_dry_run) // _pop unbalances _G.stack.pushed. + _pop (PAIR_AF); + else + regalloc_dry_run_cost++; + //parity odd <==> P/O=0 <==> interrupt enable flag IFF2 was 0 <==> + //don't enable interrupts as they were off before + if (!regalloc_dry_run) + { + emit2 ("jp PO, !tlabel", labelKey2num (tlbl->key)); + emit2 ("!ei"); + emit2 ("!tlabeldef", labelKey2num ((tlbl->key))); + genLine.lineCurr->isLabel = 1; + } + regalloc_dry_run_cost += 4; + } +} + +#if 0 //Disabled since it doesn't work for arrays of float. +enum +{ + /** Maximum number of bytes to emit per line. */ + DBEMIT_MAX_RUN = 8 +}; + +/** Context for the byte output chunker. */ +typedef struct +{ + unsigned char buffer[DBEMIT_MAX_RUN]; + int pos; +} DBEMITCTX; + + +/** Flushes a byte chunker by writing out all in the buffer and + reseting. +*/ +static void +_dbFlush (DBEMITCTX * self) +{ + char line[256]; + + if (self->pos > 0) + { + int i; + sprintf (line, ".db 0x%02X", self->buffer[0]); + + for (i = 1; i < self->pos; i++) + { + sprintf (line + strlen (line), ", 0x%02X", self->buffer[i]); + } + emit2 (line); + } + self->pos = 0; +} + +/** Write out another byte, buffering until a decent line is + generated. +*/ +static void +_dbEmit (DBEMITCTX * self, int c) +{ + if (self->pos == DBEMIT_MAX_RUN) + { + _dbFlush (self); + } + self->buffer[self->pos++] = c; +} + +/** Context for a simple run length encoder. */ +typedef struct +{ + unsigned last; + unsigned char buffer[128]; + int pos; + /** runLen may be equivalent to pos. */ + int runLen; +} RLECTX; + +enum +{ + RLE_CHANGE_COST = 4, + RLE_MAX_BLOCK = 127 +}; + +/** Flush the buffer of a run length encoder by writing out the run or + data that it currently contains. +*/ +static void +_rleCommit (RLECTX * self) +{ + int i; + if (self->pos != 0) + { + DBEMITCTX db; + memset (&db, 0, sizeof (db)); + + emit2 (".db %u", self->pos); + + for (i = 0; i < self->pos; i++) + { + _dbEmit (&db, self->buffer[i]); + } + _dbFlush (&db); + } + /* Reset */ + self->pos = 0; +} + +/* Encoder design: + Can get either a run or a block of random stuff. + Only want to change state if a good run comes in or a run ends. + Detecting run end is easy. + Initial state? + + Say initial state is in run, len zero, last zero. Then if you get a + few zeros then something else then a short run will be output. + Seems OK. While in run mode, keep counting. While in random mode, + keep a count of the run. If run hits margin, output all up to run, + restart, enter run mode. +*/ + +/** Add another byte into the run length encoder, flushing as + required. The run length encoder uses the Amiga IFF style, where + a block is prefixed by its run length. A positive length means + the next n bytes pass straight through. A negative length means + that the next byte is repeated -n times. A zero terminates the + chunks. +*/ +static void +_rleAppend (RLECTX * self, unsigned c) +{ + int i; + + if (c != self->last) + { + /* The run has stopped. See if it is worthwhile writing it out + as a run. Note that the random data comes in as runs of + length one. + */ + if (self->runLen > RLE_CHANGE_COST) + { + /* Yes, worthwhile. */ + /* Commit whatever was in the buffer. */ + _rleCommit (self); + emit2 ("!db !immed-%u,!immedbyte", self->runLen, self->last); + } + else + { + /* Not worthwhile. Append to the end of the random list. */ + for (i = 0; i < self->runLen; i++) + { + if (self->pos >= RLE_MAX_BLOCK) + { + /* Commit. */ + _rleCommit (self); + } + self->buffer[self->pos++] = self->last; + } + } + self->runLen = 1; + self->last = c; + } + else + { + if (self->runLen >= RLE_MAX_BLOCK) + { + /* Commit whatever was in the buffer. */ + _rleCommit (self); + + emit2 ("!db !immed-%u,!immedbyte", self->runLen, self->last); + self->runLen = 0; + } + self->runLen++; + } +} + +static void +_rleFlush (RLECTX * self) +{ + _rleAppend (self, -1); + _rleCommit (self); + self->pos = 0; + self->last = 0; + self->runLen = 0; +} + +/** genArrayInit - Special code for initialising an array with constant + data. +*/ + +static void +genArrayInit (iCode * ic) +{ + literalList *iLoop; + int ix; + int elementSize = 0, eIndex, i; + unsigned val, lastVal; + sym_link *type; + RLECTX rle; + + memset (&rle, 0, sizeof (rle)); + + aopOp (IC_LEFT (ic), ic, FALSE, FALSE); + + _saveRegsForCall (ic, 0); + + fetchPair (PAIR_HL, AOP (IC_LEFT (ic))); + emit2 ("call __initrleblock"); + + type = operandType (IC_LEFT (ic)); + + if (type && type->next) + { + if (IS_SPEC (type->next) || IS_PTR (type->next)) + { + elementSize = getSize (type->next); + } + else if (IS_ARRAY (type->next) && type->next->next) + { + elementSize = getSize (type->next->next); + } + else + { + printTypeChainRaw (type, NULL); + wassertl (0, "Can't determine element size in genArrayInit."); + } + } + else + { + wassertl (0, "Can't determine element size in genArrayInit."); + } + + wassertl ((elementSize > 0) && (elementSize <= 4), "Illegal element size in genArrayInit."); + + iLoop = IC_ARRAYILIST (ic); + lastVal = (unsigned) - 1; + + /* Feed all the bytes into the run length encoder which will handle + the actual output. + This works well for mixed char data, and for random int and long + data. + */ + while (iLoop) + { + ix = iLoop->count; + + for (i = 0; i < ix; i++) + { + for (eIndex = 0; eIndex < elementSize; eIndex++) + { + val = (((int) iLoop->literalValue) >> (eIndex * 8)) & 0xff; + _rleAppend (&rle, val); + } + } + + iLoop = iLoop->next; + } + + _rleFlush (&rle); + /* Mark the end of the run. */ + emit2 (".db 0"); + + _restoreRegsAfterCall (); + + spillCached (); + + freeAsmop (IC_LEFT (ic), NULL, ic); +} +#endif + +static void +setupForMemcpy (const iCode *ic, const operand *to, const operand *from) +{ + /* Both are in regs. Let regMove() do the shuffling. */ + if (AOP_TYPE (to) == AOP_REG && AOP_TYPE (from) == AOP_REG) + { + const short larray[4] = {E_IDX, D_IDX, L_IDX, H_IDX}; + short oparray[4]; + oparray[0] = AOP (to)->aopu.aop_reg[0]->rIdx; + oparray[1] = AOP (to)->aopu.aop_reg[1]->rIdx; + oparray[2] = AOP (from)->aopu.aop_reg[0]->rIdx; + oparray[3] = AOP (from)->aopu.aop_reg[1]->rIdx; + + regMove (larray, oparray, 4, FALSE); + } + else + { + /* DE is free. Write it first. */ + if (AOP_TYPE (from) != AOP_REG || AOP (from)->aopu.aop_reg[0]->rIdx != E_IDX && AOP (from)->aopu.aop_reg[0]->rIdx != D_IDX && AOP (from)->aopu.aop_reg[1]->rIdx != E_IDX && AOP (from)->aopu.aop_reg[1]->rIdx != D_IDX) + { + fetchPair (PAIR_DE, AOP (to)); + fetchPair (PAIR_HL, AOP (from)); + } + /* HL is free. Write it first. */ + else if (AOP_TYPE (to) != AOP_REG || AOP (to)->aopu.aop_reg[0]->rIdx != L_IDX && AOP (to)->aopu.aop_reg[0]->rIdx != H_IDX && AOP (to)->aopu.aop_reg[1]->rIdx != L_IDX && AOP (to)->aopu.aop_reg[1]->rIdx != H_IDX) + { + fetchPair (PAIR_HL, AOP (from)); + fetchPair (PAIR_DE, AOP (to)); + } + /* L is free, but H is not. */ + else if ((AOP_TYPE (to) != AOP_REG || AOP (to)->aopu.aop_reg[0]->rIdx != L_IDX && AOP (to)->aopu.aop_reg[1]->rIdx != L_IDX) && + (AOP_TYPE (from) != AOP_REG || AOP (from)->aopu.aop_reg[0]->rIdx != L_IDX && AOP (from)->aopu.aop_reg[1]->rIdx != L_IDX)) + { + cheapMove (ASMOP_L, 0, AOP (from), 0, true); + fetchPair (PAIR_DE, AOP (to)); + cheapMove (ASMOP_H, 0, AOP (from), 1, true); + } + /* H is free, but L is not. */ + else + { + cheapMove (ASMOP_H, 0, AOP (from), 1, true); + fetchPair (PAIR_DE, AOP (to)); + cheapMove (ASMOP_L, 0, AOP (from), 0, true); + } + } +} + +static void +genBuiltInMemcpy (const iCode *ic, int nparams, operand **pparams) +{ + int i; + operand *from, *to, *count; + bool saved_BC = FALSE, saved_DE = FALSE, saved_HL = FALSE; + unsigned int n; + + for (i = 0; i < nparams; i++) + aopOp (pparams[i], ic, FALSE, FALSE); + + wassertl (!IS_GB, "Built-in memcpy() not available on gbz80."); + wassertl (nparams == 3, "Built-in memcpy() must have three parameters."); + /* Check for zero length copy. */ + wassertl (AOP_TYPE (pparams[2]) == AOP_LIT, "Last parameter to builtin memcpy() must be literal."); + + count = pparams[2]; + from = pparams[1]; + to = pparams[0]; + + if (!(n = (unsigned int) ulFromVal (AOP (pparams[2])->aopu.aop_lit))) + goto done; + + if (!isPairDead (PAIR_HL, ic)) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (!isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + saved_DE = TRUE; + } + if (!isPairDead (PAIR_BC, ic) && n > 2) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + + setupForMemcpy (ic, to, from); + + if (n == 1) + { + emit2 ("ld a, (hl)"); + emit2 ("ld (de), a"); + regalloc_dry_run_cost += 2; + } + else if (n == 2) + { + emit2 ("ldi"); + emit2 ("ld a, (hl)"); + emit2 ("ld (de), a"); + regalloc_dry_run_cost += 4; + if (!isPairDead (PAIR_BC, ic)) /* Restore bc. */ + { + emit2 ("inc bc"); + regalloc_dry_run_cost++; + } + } + else + { + fetchPair (PAIR_BC, AOP (count)); + emit2 ("ldir"); + regalloc_dry_run_cost += 2; + } + + spillPair (PAIR_HL); + + if (saved_BC) + _pop (PAIR_BC); + if (saved_DE) + _pop (PAIR_DE); + if (saved_HL) + _pop (PAIR_HL); + +done: + freeAsmop (count, NULL); + freeAsmop (to, NULL); + freeAsmop (from, NULL); + + /* No need to assign result - would have used ordinary memcpy() call instead. */ +} + +static void +setupForMemset (const iCode *ic, const operand *dst, const operand *c, bool direct_c) +{ + /* Both are in regs. Let regMove() do the shuffling. */ + if (AOP_TYPE (dst) == AOP_REG && !direct_c && AOP_TYPE (c) == AOP_REG) + { + const short larray[2] = {L_IDX, H_IDX}; + short oparray[2]; + bool early_a = AOP_TYPE (c) == AOP_REG && (AOP (c)->aopu.aop_reg[0]->rIdx == L_IDX || AOP (c)->aopu.aop_reg[0]->rIdx == H_IDX); + + if (early_a) + cheapMove (ASMOP_A, 0, AOP (c), 0, true); + + oparray[0] = AOP (dst)->aopu.aop_reg[0]->rIdx; + oparray[1] = AOP (dst)->aopu.aop_reg[1]->rIdx; + + regMove (larray, oparray, 2, early_a); + + if (!early_a) + cheapMove (ASMOP_A, 0, AOP (c), 0, true); + } + else if (AOP_TYPE (c) == AOP_REG && requiresHL (AOP (c))) + { + cheapMove (ASMOP_A, 0, AOP (c), 0, true); + if (AOP_TYPE (dst) == AOP_EXSTK) + _push (PAIR_AF); + fetchPair (PAIR_HL, AOP (dst)); + if (AOP_TYPE (dst) == AOP_EXSTK) + _pop (PAIR_AF); + } + else + { + fetchPair (PAIR_HL, AOP (dst)); + if (!direct_c) + { + if (requiresHL (AOP (c))) + _push (PAIR_HL); + cheapMove (ASMOP_A, 0, AOP (c), 0, true); + if (requiresHL (AOP (c))) + _pop (PAIR_HL); + } + } +} + +static void +genBuiltInMemset (const iCode *ic, int nParams, operand **pparams) +{ + operand *dst, *c, *n; + bool direct_c, direct_cl; + bool indirect_c; + bool preinc = FALSE; + unsigned long sizecost_ldir, sizecost_direct, sizecost_loop; + bool double_loop; + unsigned size; + bool live_BC = !isPairDead (PAIR_BC, ic), live_DE = !isPairDead (PAIR_DE, ic), live_HL = !isPairDead (PAIR_HL, ic), live_B = bitVectBitValue (ic->rSurv, B_IDX); + bool saved_BC = FALSE, saved_DE = FALSE, saved_HL = FALSE; + + wassertl (nParams == 3, "Built-in memset() must have three parameters"); + + dst = pparams[0]; + c = pparams[1]; + n = pparams[2]; + + aopOp (c, ic, FALSE, FALSE); + aopOp (dst, ic, FALSE, FALSE); + aopOp (n, ic, FALSE, FALSE); + + wassertl (AOP_TYPE (n) == AOP_LIT, "Last parameter to builtin memset() must be literal."); + if(!(size = ulFromVal (AOP (n)->aopu.aop_lit))) + goto done; + + direct_c = (AOP_TYPE (c) == AOP_LIT || AOP_TYPE (c) == AOP_REG && AOP (c)->aopu.aop_reg[0]->rIdx != H_IDX + && AOP (c)->aopu.aop_reg[0]->rIdx != L_IDX); + direct_cl = (AOP_TYPE (c) == AOP_LIT || AOP_TYPE (c) == AOP_REG && AOP (c)->aopu.aop_reg[0]->rIdx != H_IDX + && AOP (c)->aopu.aop_reg[0]->rIdx != L_IDX && AOP (c)->aopu.aop_reg[0]->rIdx != B_IDX); + indirect_c = IS_R3KA && ulFromVal (AOP (n)->aopu.aop_lit) > 1 && AOP_TYPE (c) == AOP_IY; + + double_loop = (size > 255 || optimize.codeSpeed); + + sizecost_direct = 3 + 2 * size - 1 + !direct_c * ld_cost (ASMOP_A, AOP (c)); + sizecost_direct += (live_HL) * 2; + sizecost_loop = 9 + double_loop * 2 + ((size % 2) && double_loop) * 2 + !direct_cl * ld_cost (ASMOP_A, AOP (c)); + sizecost_loop += (live_HL + live_B) * 2; + sizecost_ldir = indirect_c ? 11 : (12 + !direct_c * ld_cost (ASMOP_A, AOP (c)) - (IS_R3KA && !optimize.codeSpeed)); + sizecost_ldir += (live_HL + live_DE + live_BC) * 2; + + if (sizecost_direct <= sizecost_loop && sizecost_direct < sizecost_ldir) // straight-line code. + { + if (live_HL) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + + setupForMemset (ic, dst, c, direct_c); + + regalloc_dry_run_cost += (size * 2 - 1); + if (!regalloc_dry_run) + while (size--) + { + emit2 ("ld (hl), %s", aopGet (direct_c ? AOP (c) : ASMOP_A, 0, FALSE)); + if (size) + emit2 ("inc hl"); + } + } + else if (size <= 510 && sizecost_loop < sizecost_ldir) // Loop + { + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel (NULL); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel (NULL); + + if (live_HL) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (bitVectBitValue (ic->rSurv, B_IDX)) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + + setupForMemset (ic, dst, c, direct_cl); + + emit2 ("ld b, !immedbyte", double_loop ? (size / 2 + size % 2) : size); + regalloc_dry_run_cost += 2; + + if (double_loop && size % 2) + { + if (!regalloc_dry_run) + emit2 ("jr !tlabel", labelKey2num (tlbl2->key)); + regalloc_dry_run_cost += 2; + } + + if (!regalloc_dry_run) + { + emitLabel (tlbl1); + emit2 ("ld (hl), %s", aopGet (direct_cl ? AOP (c) : ASMOP_A, 0, FALSE)); + emit2 ("inc hl"); + if (double_loop) + { + if (size % 2) + emitLabel (tlbl2); + emit2 ("ld (hl), %s", aopGet (direct_cl ? AOP (c) : ASMOP_A, 0, FALSE)); + emit2 ("inc hl"); + } + emit2 ("djnz !tlabel", labelKey2num (tlbl1->key)); + } + regalloc_dry_run_cost += (double_loop ? 6 : 4); + } + else // Use ldir / lsidr + { + if (live_HL) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (live_DE) + { + _push (PAIR_DE); + saved_DE = TRUE; + } + if (live_BC) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + if (indirect_c) + { + fetchPair (PAIR_DE, AOP (dst)); + emit2 ("ld hl, #%s", AOP (c)->aopu.aop_dir); + regalloc_dry_run_cost += 3; + } + else + { + setupForMemset (ic, dst, c, direct_c); + + if (!regalloc_dry_run) + emit2 ("ld (hl), %s", aopGet (direct_c ? AOP (c) : ASMOP_A, 0, FALSE)); + regalloc_dry_run_cost += (direct_c && AOP_TYPE (c) == AOP_LIT) ? 2 : 1; + if (ulFromVal (AOP (n)->aopu.aop_lit) <= 1) + goto done; + + emit2 ("ld e, l"); + emit2 ("ld d, h"); + regalloc_dry_run_cost += 2; + if (!IS_R3KA || optimize.codeSpeed) + { + emit2 ("inc de"); + regalloc_dry_run_cost++; + preinc = TRUE; + } + } + emit2 ("ld bc, !immedword", size - preinc); + emit2 (IS_R3KA ? "lsidr" : "ldir"); + regalloc_dry_run_cost += 5; + } + +done: + spillPair (PAIR_HL); + + freeAsmop (n, NULL); + freeAsmop (c, NULL); + freeAsmop (dst, NULL); + + + if (saved_BC) + _pop (PAIR_BC); + if (saved_DE) + _pop (PAIR_DE); + if (saved_HL) + _pop (PAIR_HL); + + /* No need to assign result - would have used ordinary memset() call instead. */ +} + +static void +genBuiltInStrcpy (const iCode *ic, int nParams, operand **pparams) +{ + operand *dst, *src; + bool saved_BC = FALSE, saved_DE = FALSE, saved_HL = FALSE; + int i; + bool SomethingReturned; + + SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->spildir || + OP_SYMBOL (IC_RESULT (ic))->accuse == ACCUSE_A)) || IS_TRUE_SYMOP (IC_RESULT (ic)); + + wassertl (nParams == 2, "Built-in strcpy() must have two parameters."); + wassertl (!IS_GB, "Built-in strcpy() not available for gbz80."); + + dst = pparams[0]; + src = pparams[1]; + + for (i = 0; i < nParams; i++) + aopOp (pparams[i], ic, FALSE, FALSE); + + if (!isPairDead (PAIR_HL, ic)) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (!isPairDead (PAIR_BC, ic)) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + if (!isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + saved_DE = TRUE; + } + + setupForMemcpy (ic, dst, src); + + emit3 (A_XOR, ASMOP_A, ASMOP_A); + if (SomethingReturned) + _push (PAIR_DE); + if (!regalloc_dry_run) + { + symbol *tlbl = newiTempLabel (NULL); + emitLabel (tlbl); + emit2 ("cp a, (hl)"); + emit2 ("ldi"); + emit2 ("jr NZ, !tlabel", labelKey2num (tlbl->key)); + } + regalloc_dry_run_cost += 5; + + spillPair (PAIR_HL); + + if (SomethingReturned) + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + if (!SomethingReturned || SomethingReturned && getPairId (AOP (IC_RESULT (ic))) != PAIR_INVALID) + { + if (SomethingReturned) + _pop (getPairId (AOP (IC_RESULT (ic)))); + if (saved_DE) + _pop (PAIR_DE); + if (saved_BC) + _pop (PAIR_BC); + if (saved_HL) + _pop (PAIR_HL); + } + else + { + _pop (PAIR_HL); + assignResultValue (IC_RESULT (ic)); + + restoreRegs (0, saved_DE, saved_BC, saved_HL, IC_RESULT (ic)); + } + + if (SomethingReturned) + freeAsmop (IC_RESULT (ic), NULL); + freeAsmop (src, NULL); + freeAsmop (dst, NULL); +} + +static void +genBuiltInStrncpy (const iCode *ic, int nparams, operand **pparams) +{ + int i; + operand *s1, *s2, *n; + bool saved_BC = FALSE, saved_DE = FALSE, saved_HL = FALSE; + + for (i = 0; i < nparams; i++) + aopOp (pparams[i], ic, FALSE, FALSE); + + wassertl (!IS_GB, "Built-in strncpy() not available on gbz80."); + wassertl (nparams == 3, "Built-in strncpy() must have three parameters."); + wassertl (AOP_TYPE (pparams[2]) == AOP_LIT, "Last parameter to builtin strncpy() must be literal."); + + s1 = pparams[0]; + s2 = pparams[1]; + n = pparams[2]; + + if (!ulFromVal (AOP (n)->aopu.aop_lit)) + goto done; + + if (!isPairDead (PAIR_HL, ic)) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (!isPairDead (PAIR_BC, ic)) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + if (!isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + saved_DE = TRUE; + } + + setupForMemcpy (ic, s1, s2); + + fetchPair (PAIR_BC, AOP (n)); + + emit3 (A_XOR, ASMOP_A, ASMOP_A); + if (!regalloc_dry_run) + { + symbol *tlbl1 = newiTempLabel (0); + symbol *tlbl2 = newiTempLabel (0); + symbol *tlbl3 = newiTempLabel (0); + emitLabel (tlbl2); + emit2 ("cp a, (hl)"); + emit2 ("ldi"); + emit2 (IS_RAB ? "jp LZ, !tlabel" : "jp PO, !tlabel", labelKey2num (tlbl1->key)); + emit2 ("jr NZ, !tlabel", labelKey2num (tlbl2->key)); + emitLabel (tlbl3); + emit2 ("dec hl"); + emit2 ("ldi"); + emit2 (IS_RAB ? "jp LO, !tlabel" : "jp PE, !tlabel", labelKey2num (tlbl3->key)); + emitLabel (tlbl1); + } + regalloc_dry_run_cost += 14; + + spillPair (PAIR_HL); + + restoreRegs (0, saved_DE, saved_BC, saved_HL, 0); + +done: + freeAsmop (n, NULL); + freeAsmop (s2, NULL); + freeAsmop (s1, NULL); +} + +static void +genBuiltInStrchr (const iCode *ic, int nParams, operand **pparams) +{ + operand *s, *c; + bool saved_BC = FALSE, saved_DE = FALSE, saved_HL = FALSE; + int i; + bool SomethingReturned; + PAIR_ID pair; + bool direct_c; + asmop *aop_c; + symbol *tlbl1 = regalloc_dry_run ? 0 : newiTempLabel(0); + symbol *tlbl2 = regalloc_dry_run ? 0 : newiTempLabel(0); + + SomethingReturned = (IS_ITEMP (IC_RESULT (ic)) && + (OP_SYMBOL (IC_RESULT (ic))->nRegs || + OP_SYMBOL (IC_RESULT (ic))->spildir || + OP_SYMBOL (IC_RESULT (ic))->accuse == ACCUSE_A)) || IS_TRUE_SYMOP (IC_RESULT (ic)); + + wassertl (nParams == 2, "Built-in strchr() must have two parameters."); + + s = pparams[0]; + c = pparams[1]; + + for (i = 0; i < nParams; i++) + aopOp (pparams[i], ic, FALSE, FALSE); + + if (SomethingReturned) + aopOp (IC_RESULT (ic), ic, FALSE, FALSE); + + if (getPairId (AOP (s)) != PAIR_INVALID && getPairId (AOP (s)) != PAIR_IY) + pair = getPairId (AOP (s)); + else if (SomethingReturned && getPairId (AOP (IC_RESULT (ic))) != PAIR_INVALID && getPairId (AOP (IC_RESULT (ic))) != PAIR_IY) + pair = getPairId (AOP (IC_RESULT (ic))); + else + pair = PAIR_HL; + + if (AOP_TYPE (c) == AOP_REG && AOP (c)->aopu.aop_reg[0]->rIdx != IYL_IDX && AOP (c)->aopu.aop_reg[0]->rIdx != IYH_IDX && + !(pair == PAIR_HL && (AOP (c)->aopu.aop_reg[0]->rIdx == L_IDX || AOP (c)->aopu.aop_reg[0]->rIdx == H_IDX)) && + !(pair == PAIR_DE && (AOP (c)->aopu.aop_reg[0]->rIdx == E_IDX || AOP (c)->aopu.aop_reg[0]->rIdx == D_IDX)) && + !(pair == PAIR_BC && (AOP (c)->aopu.aop_reg[0]->rIdx == B_IDX || AOP (c)->aopu.aop_reg[0]->rIdx == C_IDX))) + direct_c = TRUE; + else if (AOP_TYPE (c) == AOP_LIT && optimize.codeSize) + direct_c = TRUE; + else + direct_c = FALSE; + + aop_c = direct_c ? AOP (c) : (pair == PAIR_DE ? ASMOP_H : ASMOP_D); + + if ((pair == PAIR_HL || pair == PAIR_DE && !direct_c) && !isPairDead (PAIR_HL, ic)) + { + _push (PAIR_HL); + saved_HL = TRUE; + } + if (pair == PAIR_BC && !isPairDead (PAIR_BC, ic)) + { + _push (PAIR_BC); + saved_BC = TRUE; + } + if ((pair == PAIR_DE || !direct_c) && !isPairDead (PAIR_DE, ic)) + { + _push (PAIR_DE); + saved_DE = TRUE; + } + + if (!direct_c) + cheapMove (aop_c, 0, AOP (c), 0, true); + fetchPair (pair, AOP (s)); + + if (!regalloc_dry_run) + emitLabel (tlbl2); + emit2 ("ld a, (%s)", _pairs[pair].name); + regalloc_dry_run_cost++; + emit3 (A_CP, ASMOP_A, aop_c); + if (!regalloc_dry_run) + emit2 ("jp Z, !tlabel", labelKey2num (tlbl1->key)); + emit2 ("or a, a"); + emit2 ("inc %s", _pairs[pair].name); + if (!regalloc_dry_run) + emit2 ("jr NZ, !tlabel", labelKey2num (tlbl2->key)); + emit2 ("ld %s, a", _pairs[pair].l); + emit2 ("ld %s, a", _pairs[pair].h); + regalloc_dry_run_cost += 8; // jp will most likely be optimized into jr. + if (!regalloc_dry_run) + emitLabel (tlbl1); + if (SomethingReturned) + commitPair (AOP (IC_RESULT (ic)), pair, ic, FALSE); + + restoreRegs (0, saved_DE, saved_BC, saved_HL, SomethingReturned ? IC_RESULT (ic) : 0); + + if (SomethingReturned) + freeAsmop (IC_RESULT (ic), NULL); + freeAsmop (c, NULL); + freeAsmop (s, NULL); +} + +/*-----------------------------------------------------------------*/ +/* genBuiltIn - calls the appropriate function to generate code */ +/* for a built in function */ +/*-----------------------------------------------------------------*/ +static void +genBuiltIn (iCode *ic) +{ + operand *bi_parms[MAX_BUILTIN_ARGS]; + int nbi_parms; + iCode *bi_iCode; + symbol *bif; + + /* get all the arguments for a built in function */ + bi_iCode = getBuiltinParms (ic, &nbi_parms, bi_parms); + + /* which function is it */ + bif = OP_SYMBOL (IC_LEFT (bi_iCode)); + + wassertl (!ic->prev || ic->prev->op != SEND || !ic->prev->builtinSEND, "genBuiltIn() must be called on first SEND icode only."); + + if (!strcmp (bif->name, "__builtin_memcpy")) + { + genBuiltInMemcpy (bi_iCode, nbi_parms, bi_parms); + } + else if (!strcmp (bif->name, "__builtin_strcpy")) + { + genBuiltInStrcpy (bi_iCode, nbi_parms, bi_parms); + } + else if (!strcmp (bif->name, "__builtin_strncpy")) + { + genBuiltInStrncpy (bi_iCode, nbi_parms, bi_parms); + } + else if (!strcmp (bif->name, "__builtin_strchr")) + { + genBuiltInStrchr (bi_iCode, nbi_parms, bi_parms); + } + else if (!strcmp (bif->name, "__builtin_memset")) + { + genBuiltInMemset (bi_iCode, nbi_parms, bi_parms); + } + else + { + wassertl (0, "Unknown builtin function encountered"); + } +} + +/*-------------------------------------------------------------------------------------*/ +/* genZ80iCode - generate code for Z80 based controllers for a single iCode instruction*/ +/*-------------------------------------------------------------------------------------*/ +static void +genZ80iCode (iCode * ic) +{ + genLine.lineElement.ic = ic; + + /* if the result is marked as + spilt and rematerializable or code for + this has already been generated then + do nothing */ + if (resultRemat (ic) || ic->generated) + return; + + /* depending on the operation */ + switch (ic->op) + { + case '!': + emitDebug ("; genNot"); + genNot (ic); + break; + + case '~': + emitDebug ("; genCpl"); + genCpl (ic); + break; + + case UNARYMINUS: + emitDebug ("; genUminus"); + genUminus (ic); + break; + + case IPUSH: + emitDebug ("; genIpush"); + genIpush (ic); + break; + + case IPOP: + /* IPOP happens only when trying to restore a + spilt live range, if there is an ifx statement + following this pop then the if statement might + be using some of the registers being popped which + would destroy the contents of the register so + we need to check for this condition and handle it */ + if (ic->next && ic->next->op == IFX && regsInCommon (IC_LEFT (ic), IC_COND (ic->next))) + { + emitDebug ("; genIfx"); + genIfx (ic->next, ic); + } + else + { + emitDebug ("; genIpop"); + genIpop (ic); + } + break; + + case CALL: + emitDebug ("; genCall"); + genCall (ic); + break; + + case PCALL: + emitDebug ("; genPcall"); + genPcall (ic); + break; + + case FUNCTION: + emitDebug ("; genFunction"); + genFunction (ic); + break; + + case ENDFUNCTION: + emitDebug ("; genEndFunction"); + genEndFunction (ic); + break; + + case RETURN: + emitDebug ("; genRet"); + genRet (ic); + break; + + case LABEL: + emitDebug ("; genLabel"); + genLabel (ic); + break; + + case GOTO: + emitDebug ("; genGoto"); + genGoto (ic); + break; + + case '+': + emitDebug ("; genPlus"); + genPlus (ic); + break; + + case '-': + emitDebug ("; genMinus"); + genMinus (ic); + break; + + case '*': + emitDebug ("; genMult"); + genMult (ic); + break; + + case '/': + emitDebug ("; genDiv"); + genDiv (ic); + break; + + case '%': + emitDebug ("; genMod"); + genMod (ic); + break; + + case '>': + emitDebug ("; genCmpGt"); + genCmpGt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '<': + emitDebug ("; genCmpLt"); + genCmpLt (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case LE_OP: + case GE_OP: + case NE_OP: + + /* note these two are xlated by algebraic equivalence + during parsing SDCC.y */ + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "got '>=' or '<=' shouldn't have come here"); + break; + + case EQ_OP: + emitDebug ("; genCmpEq"); + genCmpEq (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case AND_OP: + emitDebug ("; genAndOp"); + genAndOp (ic); + break; + + case OR_OP: + emitDebug ("; genOrOp"); + genOrOp (ic); + break; + + case '^': + emitDebug ("; genXor"); + genXor (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case '|': + emitDebug ("; genOr"); + genOr (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case BITWISEAND: + emitDebug ("; genAnd"); + genAnd (ic, ifxForOp (IC_RESULT (ic), ic)); + break; + + case INLINEASM: + emitDebug ("; genInline"); + genInline (ic); + break; + + case RRC: + emitDebug ("; genRRC"); + genRRC (ic); + break; + + case RLC: + emitDebug ("; genRLC"); + genRLC (ic); + break; + + case GETHBIT: + emitDebug ("; genGetHbit"); + genGetHbit (ic); + break; + + case GETABIT: + emitDebug ("; genGetAbit"); + genGetAbit (ic); + break; + + case LEFT_OP: + emitDebug ("; genLeftShift"); + genLeftShift (ic); + break; + + case RIGHT_OP: + emitDebug ("; genRightShift"); + genRightShift (ic); + break; + + case GET_VALUE_AT_ADDRESS: + emitDebug ("; genPointerGet"); + genPointerGet (ic); + break; + + case '=': + + if (POINTER_SET (ic)) + { + emitDebug ("; genAssign (pointer)"); + genPointerSet (ic); + } + else + { + emitDebug ("; genAssign"); + genAssign (ic); + } + break; + + case IFX: + emitDebug ("; genIfx"); + genIfx (ic, NULL); + break; + + case ADDRESS_OF: + emitDebug ("; genAddrOf"); + genAddrOf (ic); + break; + + case JUMPTABLE: + emitDebug ("; genJumpTab"); + genJumpTab (ic); + break; + + case CAST: + emitDebug ("; genCast"); + genCast (ic); + break; + + case RECEIVE: + emitDebug ("; genReceive"); + genReceive (ic); + break; + + case SEND: + if (ic->builtinSEND) + { + emitDebug ("; genBuiltIn"); + genBuiltIn (ic); + } + else + { + emitDebug ("; genSend"); + genSend (ic); + } + break; + +#if 0 + case ARRAYINIT: + emitDebug ("; genArrayInit"); + genArrayInit (ic); + break; +#endif + + case DUMMY_READ_VOLATILE: + emitDebug ("; genDummyRead"); + genDummyRead (ic); + break; + + case CRITICAL: + emitDebug ("; genCritical"); + genCritical (ic); + break; + + case ENDCRITICAL: + emitDebug ("; genEndCritical"); + genEndCritical (ic); + break; + + default: + ; + } +} + +unsigned char +dryZ80iCode (iCode * ic) +{ + regalloc_dry_run = TRUE; + regalloc_dry_run_cost = 0; + + initGenLineElement (); + _G.omitFramePtr = should_omit_frame_ptr; + + genZ80iCode (ic); + + destroy_line_list (); + freeTrace (&_G.trace.aops); + + { + int pairId; + for (pairId = 0; pairId < NUM_PAIRS; pairId++) + spillPair (pairId); + } + + return (regalloc_dry_run_cost); +} + +#ifdef DEBUG_DRY_COST +static void +dryZ80Code (iCode * lic) +{ + iCode *ic; + + for (ic = lic; ic; ic = ic->next) + if (ic->op != FUNCTION && ic->op != ENDFUNCTION && ic->op != LABEL && ic->op != GOTO && ic->op != INLINEASM) + printf ("; iCode %d total cost: %d\n", ic->key, (int) (dryZ80iCode (ic))); +} +#endif + +/*-------------------------------------------------------------------------------------*/ +/* genZ80Code - generate code for Z80 based controllers for a block of intructions */ +/*-------------------------------------------------------------------------------------*/ +void +genZ80Code (iCode * lic) +{ +#ifdef DEBUG_DRY_COST + dryZ80Code (lic); +#endif + + iCode *ic; + int cln = 0; + regalloc_dry_run = FALSE; + + initGenLineElement (); + + memset(z80_regs_used_as_parms_in_calls_from_current_function, 0, sizeof(bool) * (IYH_IDX + 1)); + z80_symmParm_in_calls_from_current_function = TRUE; + memset(z80_regs_preserved_in_calls_from_current_function, 0, sizeof(bool) * (IYH_IDX + 1)); + + /* if debug information required */ + if (options.debug && currFunc) + { + debugFile->writeFunction (currFunc, lic); + } + + for (ic = lic; ic; ic = ic->next) + ic->generated = FALSE; + + /* Generate Code for all instructions */ + for (ic = lic; ic; ic = ic->next) + { + if (ic->lineno && cln != ic->lineno) + { + if (options.debug) + debugFile->writeCLine (ic); + if (!options.noCcodeInAsm) + emit2 (";%s:%d: %s", ic->filename, ic->lineno, printCLine (ic->filename, ic->lineno)); + cln = ic->lineno; + } + if (options.iCodeInAsm) + { + const char *iLine = printILine (ic); + emit2 (";ic:%d: %s", ic->key, iLine); + dbuf_free (iLine); + } + regalloc_dry_run_cost = 0; + genZ80iCode (ic); + +#ifdef DEBUG_DRY_COST + emit2 ("; iCode %d total cost: %d\n", ic->key, regalloc_dry_run_cost); +#endif + } + + /* now we are ready to call the + peep hole optimizer */ + if (!options.nopeep) + peepHole (&genLine.lineHead); + + /* This is unfortunate */ + /* now do the actual printing */ + { + struct dbuf_s *buf = codeOutBuf; + if (isInHome () && codeOutBuf == &code->oBuf) + codeOutBuf = &home->oBuf; + printLine (genLine.lineHead, codeOutBuf); + if (_G.flushStatics) + { + flushStatics (); + _G.flushStatics = 0; + } + codeOutBuf = buf; + } + + { + int pairId; + for (pairId = 0; pairId < NUM_PAIRS; pairId++) + spillPair (pairId); + } + + destroy_line_list (); + freeTrace (&_G.trace.aops); +} + diff --git a/src/z80/gen.h b/src/z80/gen.h new file mode 100644 index 0000000..cbe7ae4 --- /dev/null +++ b/src/z80/gen.h @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + SDCCgen51.h - header file for code generation for 8051 + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +-------------------------------------------------------------------------*/ + +#ifndef Z80GEN_H +#define Z80GEN_H + +typedef enum +{ + AOP_INVALID, + /* Is a literal */ + AOP_LIT = 1, + /* Is in a register */ + AOP_REG, + /* Is in direct space */ + AOP_DIR, + /* SFR space ($FF00 and above) */ + AOP_SFR, + /* Is on the stack */ + AOP_STK, + /* Is an immediate value */ + AOP_IMMD, + /* Is a string (?) */ + AOP_STR, + /* Is in the carry register */ + AOP_CRY, + /* Is pointed to by IY */ + AOP_IY, + /* Is pointed to by HL */ + AOP_HL, + /* Is in the extended stack pointer (IY on the Z80) */ + AOP_EXSTK, + /* Is referenced by a pointer in a register pair. */ + AOP_PAIRPTR, + /* Read undefined, discard writes */ + AOP_DUMMY +} +AOP_TYPE; + +/* type asmop : a homogenised type for + all the different spaces an operand can be + in */ +typedef struct asmop +{ + AOP_TYPE type; + short coff; /* current offset */ + short size; /* total size */ + unsigned code:1; /* is in Code space */ + unsigned paged:1; /* in paged memory */ + unsigned freed:1; /* already freed */ + unsigned bcInUse:1; /* for banked I/O, which uses bc for the I/O address */ + union + { + value *aop_lit; /* if literal */ + reg_info *aop_reg[4]; /* array of registers */ + char *aop_dir; /* if direct */ + char *aop_immd; /* if immediate others are implied */ + int aop_stk; /* stack offset when AOP_STK */ + const char *aop_str[4]; /* just a string array containing the location */ + int aop_pairId; /* The pair ID */ + } + aopu; + signed char regs[9]; // Byte of this aop that is in the register. -1 if no byte of this aop is in the reg. +} +asmop; + +void genZ80Code (iCode *); +void z80_emitDebuggerSymbol (const char *); + +extern bool z80_assignment_optimal; +extern bool should_omit_frame_ptr; + +#endif + diff --git a/src/z80/main.c b/src/z80/main.c new file mode 100644 index 0000000..a96493e --- /dev/null +++ b/src/z80/main.c @@ -0,0 +1,1758 @@ +/*------------------------------------------------------------------------- + main.c - Z80 specific definitions. + + Michael Hope 2001 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include "z80.h" +#include "SDCCsystem.h" +#include "SDCCutil.h" +#include "SDCCargs.h" +#include "dbuf_string.h" + +#define OPTION_BO "-bo" +#define OPTION_BA "-ba" +#define OPTION_CODE_SEG "--codeseg" +#define OPTION_CONST_SEG "--constseg" +#define OPTION_DATA_SEG "--dataseg" +#define OPTION_CALLEE_SAVES_BC "--callee-saves-bc" +#define OPTION_PORTMODE "--portmode=" +#define OPTION_ASM "--asm=" +#define OPTION_NO_STD_CRT0 "--no-std-crt0" +#define OPTION_RESERVE_IY "--reserve-regs-iy" +#define OPTION_OLDRALLOC "--oldralloc" +#define OPTION_FRAMEPOINTER "--fno-omit-frame-pointer" +#define OPTION_EMIT_EXTERNS "--emit-externs" + +static char _z80_defaultRules[] = { +#include "peeph.rul" +#include "peeph-z80.rul" +}; + +static char _r2k_defaultRules[] = { +#include "peeph.rul" +#include "peeph-r2k.rul" +}; + +static char _tlcs90_defaultRules[] = { +#include "peeph.rul" +#include "peeph-tlcs90.rul" +}; + +static char _gbz80_defaultRules[] = { +#include "peeph.rul" +#include "peeph-gbz80.rul" +}; + + + +Z80_OPTS z80_opts; + +static OPTION _z80_options[] = { + {0, OPTION_CALLEE_SAVES_BC, &z80_opts.calleeSavesBC, "Force a called function to always save BC"}, + {0, OPTION_PORTMODE, NULL, "Determine PORT I/O mode (z80/z180)"}, + {0, OPTION_ASM, NULL, "Define assembler name (rgbds/asxxxx/isas/z80asm)"}, + {0, OPTION_CODE_SEG, &options.code_seg, " use this name for the code segment", CLAT_STRING}, + {0, OPTION_CONST_SEG, &options.const_seg, " use this name for the const segment", CLAT_STRING}, + {0, OPTION_DATA_SEG, &options.data_seg, " use this name for the data segment", CLAT_STRING}, + {0, OPTION_NO_STD_CRT0, &options.no_std_crt0, "For the z80/gbz80 do not link default crt0.rel"}, + {0, OPTION_RESERVE_IY, &z80_opts.reserveIY, "Do not use IY (incompatible with --fomit-frame-pointer)"}, + {0, OPTION_OLDRALLOC, &options.oldralloc, "Use old register allocator"}, + {0, OPTION_FRAMEPOINTER, &z80_opts.noOmitFramePtr, "Do not omit frame pointer"}, + {0, OPTION_EMIT_EXTERNS, NULL, "Emit externs list in generated asm"}, + {0, NULL} +}; + +static OPTION _gbz80_options[] = { + {0, OPTION_BO, NULL, " use code bank "}, + {0, OPTION_BA, NULL, " use data bank "}, + {0, OPTION_CALLEE_SAVES_BC, &z80_opts.calleeSavesBC, "Force a called function to always save BC"}, + {0, OPTION_CODE_SEG, &options.code_seg, " use this name for the code segment", CLAT_STRING}, + {0, OPTION_CONST_SEG, &options.const_seg, " use this name for the const segment", CLAT_STRING}, + {0, OPTION_DATA_SEG, &options.data_seg, " use this name for the data segment", CLAT_STRING}, + {0, OPTION_NO_STD_CRT0, &options.no_std_crt0, "For the z80/gbz80 do not link default crt0.rel"}, + {0, NULL} +}; + +typedef enum +{ + /* Must be first */ + ASM_TYPE_ASXXXX, + ASM_TYPE_RGBDS, + ASM_TYPE_ISAS, + ASM_TYPE_Z80ASM +} +ASM_TYPE; + +static struct +{ + ASM_TYPE asmType; + /* determine if we can register a parameter */ + int regParams; + bool z88dk_fastcall; +} +_G; + +static char *_keywords[] = { + "sfr", + "nonbanked", + "banked", + "at", + "_naked", + "critical", + "interrupt", + "z88dk_fastcall", + "z88dk_callee", + "smallc", + "z88dk_shortcall", + "z88dk_params_offset", + NULL +}; + +static char *_keywordsgb[] = { + "sfr", + "nonbanked", + "banked", + "at", + "_naked", + "critical", + "interrupt", + "z88dk_callee", + "smallc", + NULL +}; + +static char *_keywordstlcs90[] = { + "nonbanked", + "banked", + "at", + "_naked", + "critical", + "interrupt", + "z88dk_fastcall", + "z88dk_callee", + "smallc", + NULL +}; + +extern PORT z80_port; +extern PORT r2k_port; +extern PORT gbz80_port; + +#include "mappings.i" + +static builtins _z80_builtins[] = { + {"__builtin_memcpy", "vg*", 3, {"vg*", "Cvg*", "Ui"}}, + {"__builtin_strcpy", "cg*", 2, {"cg*", "Ccg*"}}, + {"__builtin_strncpy", "cg*", 3, {"cg*", "Ccg*", "Ui"}}, + {"__builtin_strchr", "cg*", 2, {"Ccg*", "i"}}, + {"__builtin_memset", "vg*", 3, {"vg*", "i", "Ui"}}, + {NULL, NULL, 0, {NULL}} +}; + +static void +_z80_init (void) +{ + z80_opts.sub = SUB_Z80; + asm_addTree (&_asxxxx_z80); +} + +static void +_z180_init (void) +{ + z80_opts.sub = SUB_Z180; + asm_addTree (&_asxxxx_z80); +} + +static void +_r2k_init (void) +{ + z80_opts.sub = SUB_R2K; + asm_addTree (&_asxxxx_r2k); +} + +static void +_r3ka_init (void) +{ + z80_opts.sub = SUB_R3KA; + asm_addTree (&_asxxxx_r2k); +} + +static void +_gbz80_init (void) +{ + z80_opts.sub = SUB_GBZ80; +} + +static void +_tlcs90_init (void) +{ + z80_opts.sub = SUB_TLCS90; + asm_addTree (&_asxxxx_z80); +} + +static void +_ez80_z80_init (void) +{ + z80_opts.sub = SUB_EZ80_Z80; + asm_addTree (&_asxxxx_z80); +} + +static void +_reset_regparm (struct sym_link *funcType) +{ + _G.regParams = 0; + _G.z88dk_fastcall = IFFUNC_ISZ88DK_FASTCALL (funcType); + if (_G.z88dk_fastcall && IFFUNC_HASVARARGS (funcType)) + werror (E_Z88DK_FASTCALL_PARAMETERS); +} + +static int +_reg_parm (sym_link *l, bool reentrant) +{ + if (_G.z88dk_fastcall) + { + if (_G.regParams) + werror (E_Z88DK_FASTCALL_PARAMETERS); + if (getSize (l) > 4) + werror (E_Z88DK_FASTCALL_PARAMETER); + _G.regParams++; + return TRUE; + } + return FALSE; +} + +enum +{ + P_BANK = 1, + P_PORTMODE, + P_CODESEG, + P_CONSTSEG, +}; + +static int +do_pragma (int id, const char *name, const char *cp) +{ + struct pragma_token_s token; + int err = 0; + int processed = 1; + + init_pragma_token (&token); + + switch (id) + { + case P_BANK: + { + struct dbuf_s buffer; + + dbuf_init (&buffer, 128); + + cp = get_pragma_token (cp, &token); + + switch (token.type) + { + case TOKEN_EOL: + err = 1; + break; + + case TOKEN_INT: + switch (_G.asmType) + { + case ASM_TYPE_ASXXXX: + dbuf_printf (&buffer, "CODE_%d", token.val.int_val); + break; + + case ASM_TYPE_RGBDS: + dbuf_printf (&buffer, "CODE,BANK[%d]", token.val.int_val); + break; + + case ASM_TYPE_ISAS: + /* PENDING: what to use for ISAS? */ + dbuf_printf (&buffer, "CODE,BANK(%d)", token.val.int_val); + break; + + default: + wassert (0); + } + break; + + default: + { + const char *str = get_pragma_string (&token); + + dbuf_append_str (&buffer, (0 == strcmp ("BASE", str)) ? "HOME" : str); + } + break; + } + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + dbuf_c_str (&buffer); + /* ugly, see comment in src/port.h (borutr) */ + gbz80_port.mem.code_name = dbuf_detach (&buffer); + code->sname = gbz80_port.mem.code_name; + options.code_seg = (char *) gbz80_port.mem.code_name; + } + break; + + case P_PORTMODE: + { /*.p.t.20030716 - adding pragma to manipulate z80 i/o port addressing modes */ + const char *str; + + cp = get_pragma_token (cp, &token); + + if (TOKEN_EOL == token.type) + { + err = 1; + break; + } + + str = get_pragma_string (&token); + + cp = get_pragma_token (cp, &token); + if (TOKEN_EOL != token.type) + { + err = 1; + break; + } + + if (!strcmp (str, "z80")) + { + z80_opts.port_mode = 80; + } + else if (!strcmp (str, "z180")) + { + z80_opts.port_mode = 180; + } + else if (!strcmp (str, "save")) + { + z80_opts.port_back = z80_opts.port_mode; + } + else if (!strcmp (str, "restore")) + { + z80_opts.port_mode = z80_opts.port_back; + } + else + err = 1; + } + break; + + case P_CODESEG: + case P_CONSTSEG: + { + char *segname; + + cp = get_pragma_token (cp, &token); + if (token.type == TOKEN_EOL) + { + err = 1; + break; + } + + segname = Safe_strdup (get_pragma_string (&token)); + + cp = get_pragma_token (cp, &token); + if (token.type != TOKEN_EOL) + { + Safe_free (segname); + err = 1; + break; + } + + if (id == P_CODESEG) + { + if (options.code_seg) + Safe_free (options.code_seg); + options.code_seg = segname; + } + else + { + if (options.const_seg) + Safe_free (options.const_seg); + options.const_seg = segname; + } + } + break; + + default: + processed = 0; + break; + } + + get_pragma_token (cp, &token); + + if (1 == err) + werror (W_BAD_PRAGMA_ARGUMENTS, name); + + free_pragma_token (&token); + return processed; +} + +static struct pragma_s pragma_tbl[] = { + {"bank", P_BANK, 0, do_pragma}, + {"portmode", P_PORTMODE, 0, do_pragma}, + {"codeseg", P_CODESEG, 0, do_pragma}, + {"constseg", P_CONSTSEG, 0, do_pragma}, + {NULL, 0, 0, NULL}, +}; + +static int +_process_pragma (const char *s) +{ + return process_pragma_tbl (pragma_tbl, s); +} + +static const char *_gbz80_rgbasmCmd[] = { + "rgbasm", "-o$1.rel", "$1.asm", NULL +}; + +static const char *_gbz80_rgblinkCmd[] = { + "xlink", "-tg", "-n$1.sym", "-m$1.map", "-zFF", "$1.lnk", NULL +}; + +static void +_gbz80_rgblink (void) +{ + FILE *lnkfile; + struct dbuf_s lnkFileName; + char *buffer; + + dbuf_init (&lnkFileName, PATH_MAX); + + /* first we need to create the .lnk file */ + dbuf_append_str (&lnkFileName, dstFileName); + dbuf_append_str (&lnkFileName, ".lk"); + if (!(lnkfile = fopen (dbuf_c_str (&lnkFileName), "w"))) + { + werror (E_FILE_OPEN_ERR, dbuf_c_str (&lnkFileName)); + dbuf_destroy (&lnkFileName); + exit (1); + } + dbuf_destroy (&lnkFileName); + + fprintf (lnkfile, "[Objects]\n"); + + fprintf (lnkfile, "%s.rel\n", dstFileName); + + fputStrSet (lnkfile, relFilesSet); + + fprintf (lnkfile, "\n[Libraries]\n"); + /* additional libraries if any */ + fputStrSet (lnkfile, libFilesSet); + + fprintf (lnkfile, "\n[Output]\n" "%s.gb", dstFileName); + + fclose (lnkfile); + + buffer = buildCmdLine (port->linker.cmd, dstFileName, NULL, NULL, NULL); + /* call the linker */ + if (sdcc_system (buffer)) + { + Safe_free (buffer); + perror ("Cannot exec linker"); + exit (1); + } + Safe_free (buffer); +} + +static bool +_parseOptions (int *pargc, char **argv, int *i) +{ + if (argv[*i][0] == '-') + { + if (IS_GB) + { + if (!strncmp (argv[*i], OPTION_BO, sizeof (OPTION_BO) - 1)) + { + /* ROM bank */ + int bank = getIntArg (OPTION_BO, argv, i, *pargc); + struct dbuf_s buffer; + + dbuf_init (&buffer, 16); + dbuf_printf (&buffer, "CODE_%u", bank); + dbuf_c_str (&buffer); + /* ugly, see comment in src/port.h (borutr) */ + gbz80_port.mem.code_name = dbuf_detach (&buffer); + options.code_seg = (char *) gbz80_port.mem.code_name; + return TRUE; + } + else if (!strncmp (argv[*i], OPTION_BA, sizeof (OPTION_BA) - 1)) + { + /* RAM bank */ + int bank = getIntArg (OPTION_BA, argv, i, *pargc); + struct dbuf_s buffer; + + dbuf_init (&buffer, 16); + dbuf_printf (&buffer, "DATA_%u", bank); + dbuf_c_str (&buffer); + /* ugly, see comment in src/port.h (borutr) */ + gbz80_port.mem.data_name = dbuf_detach (&buffer); + return TRUE; + } + } + + if (!strncmp (argv[*i], OPTION_ASM, sizeof (OPTION_ASM) - 1)) + { + char *asmblr = getStringArg (OPTION_ASM, argv, i, *pargc); + + if (!strcmp (asmblr, "rgbds")) + { + asm_addTree (&_rgbds_gb); + gbz80_port.assembler.cmd = _gbz80_rgbasmCmd; + gbz80_port.linker.cmd = _gbz80_rgblinkCmd; + gbz80_port.linker.do_link = _gbz80_rgblink; + _G.asmType = ASM_TYPE_RGBDS; + return TRUE; + } + else if (!strcmp (asmblr, "asxxxx")) + { + _G.asmType = ASM_TYPE_ASXXXX; + return TRUE; + } + else if (!strcmp (asmblr, "isas")) + { + asm_addTree (&_isas_gb); + /* Munge the function prefix */ + gbz80_port.fun_prefix = ""; + _G.asmType = ASM_TYPE_ISAS; + return TRUE; + } + else if (!strcmp (asmblr, "z80asm")) + { + port->assembler.externGlobal = TRUE; + asm_addTree (&_z80asm_z80); + _G.asmType = ASM_TYPE_ISAS; + return TRUE; + } + } + else if (!strncmp (argv[*i], OPTION_PORTMODE, sizeof (OPTION_PORTMODE) - 1)) + { + char *portmode = getStringArg (OPTION_ASM, argv, i, *pargc); + + if (!strcmp (portmode, "z80")) + { + z80_opts.port_mode = 80; + return TRUE; + } + else if (!strcmp (portmode, "z180")) + { + z80_opts.port_mode = 180; + return TRUE; + } + } + else if (!strncmp (argv[*i], OPTION_EMIT_EXTERNS, sizeof (OPTION_EMIT_EXTERNS) - 1)) + { + port->assembler.externGlobal = 1; + return TRUE; + } + } + return FALSE; +} + +static void +_setValues (void) +{ + const char *s; + struct dbuf_s dbuf; + + if (options.nostdlib == FALSE) + { + const char *s; + char *path; + struct dbuf_s dbuf; + + dbuf_init (&dbuf, PATH_MAX); + + for (s = setFirstItem (libDirsSet); s != NULL; s = setNextItem (libDirsSet)) + { + path = buildCmdLine2 ("-k\"%s" DIR_SEPARATOR_STRING "{port}\" ", s); + dbuf_append_str (&dbuf, path); + Safe_free (path); + } + path = buildCmdLine2 ("-l\"{port}.lib\"", s); + dbuf_append_str (&dbuf, path); + Safe_free (path); + + setMainValue ("z80libspec", dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + for (s = setFirstItem (libDirsSet); s != NULL; s = setNextItem (libDirsSet)) + { + struct stat stat_buf; + + path = buildCmdLine2 ("%s" DIR_SEPARATOR_STRING "{port}" DIR_SEPARATOR_STRING "crt0{objext}", s); + if (stat (path, &stat_buf) == 0) + { + Safe_free (path); + break; + } + else + Safe_free (path); + } + + if (s == NULL) + setMainValue ("z80crt0", "\"crt0{objext}\""); + else + { + struct dbuf_s dbuf; + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "\"%s\"", path); + setMainValue ("z80crt0", dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + } + } + else + { + setMainValue ("z80libspec", ""); + setMainValue ("z80crt0", ""); + } + + setMainValue ("z80extralibfiles", (s = joinStrSet (libFilesSet))); + Safe_free ((void *) s); + setMainValue ("z80extralibpaths", (s = joinStrSet (libPathsSet))); + Safe_free ((void *) s); + + if (IS_GB) + { + setMainValue ("z80outputtypeflag", "-Z"); + setMainValue ("z80outext", ".gb"); + } + else + { + setMainValue ("z80outputtypeflag", "-i"); + setMainValue ("z80outext", ".ihx"); + } + + setMainValue ("stdobjdstfilename", "{dstfilename}{objext}"); + setMainValue ("stdlinkdstfilename", "{dstfilename}{z80outext}"); + + setMainValue ("z80extraobj", (s = joinStrSet (relFilesSet))); + Safe_free ((void *) s); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "-b_CODE=0x%04X -b_DATA=0x%04X", options.code_loc, options.data_loc); + setMainValue ("z80bases", dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* For the old register allocator (with the new one we decide to omit the frame pointer for each function individually) */ + if (!IS_GB && options.omitFramePtr) + port->stack.call_overhead = 2; +} + +static void +_finaliseOptions (void) +{ + port->mem.default_local_map = data; + port->mem.default_globl_map = data; + if (_G.asmType == ASM_TYPE_ASXXXX && IS_GB) + asm_addTree (&_asxxxx_gb); + + if (IY_RESERVED) + port->num_regs -= 2; + + _setValues (); +} + +static void +_setDefaultOptions (void) +{ + options.nopeep = 0; + options.stackAuto = 1; + /* first the options part */ + options.intlong_rent = 1; + options.float_rent = 1; + options.noRegParams = 0; + /* Default code and data locations. */ + options.code_loc = 0x200; + + options.data_loc = IS_GB ? 0xC000 : 0x8000; + options.out_fmt = 'i'; /* Default output format is ihx */ +} + +#if 0 +/* Mangling format: + _fun_policy_params + where: + policy is the function policy + params is the parameter format + + policy format: + rsp + where: + r is 'r' for reentrant, 's' for static functions + s is 'c' for callee saves, 'r' for caller saves + f is 'f' for profiling on, 'x' for profiling off + examples: + rr - reentrant, caller saves + params format: + A combination of register short names and s to signify stack variables. + examples: + bds - first two args appear in BC and DE, the rest on the stack + s - all arguments are on the stack. +*/ +static const char * +_mangleSupportFunctionName (const char *original) +{ + struct dbuf_s dbuf; + + if (strstr (original, "longlong")) + return (original); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%s_rr%s_%s", original, options.profile ? "f" : "x", options.noRegParams ? "s" : "bds" /* MB: but the library only has hds variants ??? */ + ); + + return dbuf_detach_c_str (&dbuf); +} +#endif + +static const char * +_getRegName (const struct reg_info *reg) +{ + if (reg) + { + return reg->name; + } + /* assert (0); */ + return "err"; +} + +static int +_getRegByName (const char *name) +{ + if (!strcmp (name, "a")) + return 0; + if (!strcmp (name, "c")) + return 1; + if (!strcmp (name, "b")) + return 2; + if (!strcmp (name, "e")) + return 3; + if (!strcmp (name, "d")) + return 4; + if (!strcmp (name, "l")) + return 5; + if (!strcmp (name, "h")) + return 6; + if (!strcmp (name, "iyl")) + return 7; + if (!strcmp (name, "iyh")) + return 8; + return -1; +} + +static bool +_hasNativeMulFor (iCode *ic, sym_link *left, sym_link *right) +{ + sym_link *test = NULL; + int result_size = IS_SYMOP (IC_RESULT(ic)) ? getSize (OP_SYM_TYPE (IC_RESULT(ic))) : 4; + + if (ic->op != '*') + { + return FALSE; + } + + if (IS_LITERAL (left)) + test = left; + else if (IS_LITERAL (right)) + test = right; + /* 8x8 unsigned multiplication code is shorter than + call overhead for the multiplication routine. */ + else if (IS_CHAR (right) && IS_UNSIGNED (right) && IS_CHAR (left) && IS_UNSIGNED (left) && !IS_GB) + { + return TRUE; + } + /* Same for any multiplication with 8 bit result. */ + else if (result_size == 1 && !IS_GB) + { + return TRUE; + } + else + { + return FALSE; + } + + if (getSize (test) <= 2) + { + return TRUE; + } + + return FALSE; +} + +/* Indicate which extended bit operations this port supports */ +static bool +hasExtBitOp (int op, int size) +{ + if (op == GETHBIT) + return TRUE; + else + return FALSE; +} + +/* Indicate the expense of an access to an output storage class */ +static int +oclsExpense (struct memmap *oclass) +{ + if (IN_FARSPACE (oclass)) + return 1; + + return 0; +} + + +//#define LINKCMD "sdld{port} -nf {dstfilename}" +/* +#define LINKCMD \ + "sdld{port} -n -c -- {z80bases} -m -j" \ + " {z80libspec}" \ + " {z80extralibfiles} {z80extralibpaths}" \ + " {z80outputtypeflag} \"{linkdstfilename}\"" \ + " {z80crt0}" \ + " \"{dstfilename}{objext}\"" \ + " {z80extraobj}" +*/ + +static const char *_z80LinkCmd[] = { + "sdldz80", "-nf", "$1", NULL +}; + +static const char *_gbLinkCmd[] = { + "sdldgb", "-nf", "$1", NULL +}; + +/* $3 is replaced by assembler.debug_opts resp. port->assembler.plain_opts */ +static const char *_z80AsmCmd[] = { + "sdasz80", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char *_r2kAsmCmd[] = { + "sdasrab", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char *_gbAsmCmd[] = { + "sdasgb", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char *_tlcs90AsmCmd[] = { + "sdastlcs90", "$l", "$3", "$2", "$1.asm", NULL +}; + +static const char *const _crt[] = { "crt0.rel", NULL, }; +static const char *const _libs_z80[] = { "z80", NULL, }; +static const char *const _libs_z180[] = { "z180", NULL, }; +static const char *const _libs_r2k[] = { "r2k", NULL, }; +static const char *const _libs_r3ka[] = { "r3ka", NULL, }; +static const char *const _libs_tlcs90[] = { "tlcs90", NULL, }; +static const char *const _libs_gb[] = { "gbz80", NULL, }; +static const char *const _libs_ez80_z80[] = { "ez80_z80", NULL, }; + +/* Globals */ +PORT z80_port = +{ + TARGET_ID_Z80, + "z80", + "Zilog Z80", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _z80AsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, /* needLinkerScript */ + _crt, /* crt */ + _libs_z80, /* libs */ + }, + { /* Peephole optimizer */ + _z80_defaultRules, + z80instructionSize, + 0, + 0, + 0, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", /* static initialization */ + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _z80_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywords, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT z180_port = +{ + TARGET_ID_Z180, + "z180", + "Zilog Z180", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _z80AsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_z180, /* libs */ + }, + { /* Peephole optimizer */ + _z80_defaultRules, + z80instructionSize, + NULL, + NULL, + NULL, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _z180_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywords, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT r2k_port = +{ + TARGET_ID_R2K, + "r2k", + "Rabbit 2000", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _r2kAsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm"}, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_r2k, /* libs */ + }, + { /* Peephole optimizer */ + _r2k_defaultRules, + z80instructionSize, + NULL, + NULL, + NULL, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _r2k_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywords, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT r3ka_port = +{ + TARGET_ID_R3KA, + "r3ka", + "Rabbit 3000A", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _r2kAsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_r3ka, /* libs */ + }, + { /* Peephole optimizer */ + _r2k_defaultRules, + z80instructionSize, + 0, + 0, + 0, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _r3ka_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywords, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +/* Globals */ +PORT gbz80_port = +{ + TARGET_ID_GBZ80, + "gbz80", + "Gameboy Z80-like", /* Target name */ + NULL, + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _gbAsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm", + NULL /* no do_assemble function */ + }, + { /* Linker */ + _gbLinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_gb, /* libs */ + }, + { /* Peephole optimizer */ + _gbz80_defaultRules, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG", + "GSINIT", + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + NULL, /* name of segment for initialized variables */ + NULL, /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 2, 0, 4, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _gbz80_init, + _parseOptions, + _gbz80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywordsgb, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + NULL, /* no builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 5, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT tlcs90_port = +{ + TARGET_ID_TLCS90, + "tlcs90", + "Toshiba TLCS-90", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _tlcs90AsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_tlcs90, /* libs */ + }, + { /* Peephole optimizer */ + _tlcs90_defaultRules, + z80instructionSize, + NULL, + NULL, + NULL, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", /* static initialization */ + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _tlcs90_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywordstlcs90, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + +PORT ez80_z80_port = +{ + TARGET_ID_EZ80_Z80, + "ez80_z80", + "eZ80-Z80", /* Target name */ + NULL, /* Processor name */ + { + glue, + FALSE, + NO_MODEL, + NO_MODEL, + NULL, /* model == target */ + }, + { /* Assembler */ + _z80AsmCmd, + NULL, + "-plosgffwy", /* Options with debug */ + "-plosgffw", /* Options without debug */ + 0, + ".asm" + }, + { /* Linker */ + _z80LinkCmd, //NULL, + NULL, //LINKCMD, + NULL, + ".rel", + 1, + _crt, /* crt */ + _libs_ez80_z80, /* libs */ + }, + { /* Peephole optimizer */ + _z80_defaultRules, + z80instructionSize, + NULL, + NULL, + NULL, + z80notUsed, + z80canAssign, + z80notUsedFrom, + z80symmParmStack, + }, + /* Sizes: char, short, int, long, long long, near ptr, far ptr, gptr, func ptr, banked func ptr, bit, float */ + { 1, 2, 2, 4, 8, 2, 2, 2, 2, 2, 1, 4 }, + /* tags for generic pointers */ + { 0x00, 0x40, 0x60, 0x80 }, /* far, near, xstack, code */ + { + "XSEG", + "STACK", + "CODE", + "DATA", + NULL, /* idata */ + NULL, /* pdata */ + NULL, /* xdata */ + NULL, /* bit */ + "RSEG (ABS)", + "GSINIT", + NULL, /* overlay */ + "GSFINAL", + "HOME", + NULL, /* xidata */ + NULL, /* xinit */ + NULL, /* const_name */ + "CABS (ABS)", /* cabs_name */ + "DABS (ABS)", /* xabs_name */ + NULL, /* iabs_name */ + "INITIALIZED", /* name of segment for initialized variables */ + "INITIALIZER", /* name of segment for copies of initialized variables in code space */ + NULL, + NULL, + 1, /* CODE is read-only */ + 1 /* No fancy alignments supported. */ + }, + { NULL, NULL }, + { -1, 0, 0, 4, 0, 2, 0 }, + { -1, FALSE }, + { z80_emitDebuggerSymbol }, + { + 256, /* maxCount */ + 3, /* sizeofElement */ + {6, 7, 8}, /* sizeofMatchJump[] - Assumes operand allocated to registers */ + {6, 9, 15}, /* sizeofRangeCompare[] - Assumes operand allocated to registers*/ + 1, /* sizeofSubtract - Assumes use of a singel inc or dec */ + 9, /* sizeofDispatch - Assumes operand allocated to register e or c*/ + }, + "_", + _ez80_z80_init, + _parseOptions, + _z80_options, + NULL, + _finaliseOptions, + _setDefaultOptions, + z80_assignRegisters, + _getRegName, + _getRegByName, + NULL, + _keywords, + 0, /* no assembler preamble */ + NULL, /* no genAssemblerEnd */ + 0, /* no local IVT generation code */ + 0, /* no genXINIT code */ + NULL, /* genInitStartup */ + _reset_regparm, + _reg_parm, + _process_pragma, + NULL, + _hasNativeMulFor, + hasExtBitOp, /* hasExtBitOp */ + oclsExpense, /* oclsExpense */ + TRUE, + TRUE, /* little endian */ + 0, /* leave lt */ + 0, /* leave gt */ + 1, /* transform <= to ! > */ + 1, /* transform >= to ! < */ + 1, /* transform != to !(a == b) */ + 0, /* leave == */ + FALSE, /* Array initializer support. */ + 0, /* no CSE cost estimation yet */ + _z80_builtins, /* builtin functions */ + GPOINTER, /* treat unqualified pointers as "generic" pointers */ + 1, /* reset labelKey to 1 */ + 1, /* globals & local statics allowed */ + 9, /* Number of registers handled in the tree-decomposition-based register allocator in SDCCralloc.hpp */ + PORT_MAGIC +}; + diff --git a/src/z80/mappings.i b/src/z80/mappings.i new file mode 100644 index 0000000..6cae8d3 --- /dev/null +++ b/src/z80/mappings.i @@ -0,0 +1,475 @@ +static const ASM_MAPPING _asxxxx_gb_mapping[] = { + /* We want to prepend the _ */ + { "area", ".area _%s" }, + { "areacode", ".area _%s" }, + { "areadata", ".area _%s" }, + { "areahome", ".area _%s" }, + { "functionlabeldef", "%s:" }, + { "globalfunctionlabeldef", "%s::" }, + { "*hl", "(hl)" }, + { "di", "di" }, + { "ei", "ei" }, + /*{ "ldahli", "ldi\ta, (hl)" }, use when assembler update is complete*/ + {"ldahli", "ld\ta, (hl)\ninc\thl"}, + { "ldahlsp", "ldhl\tsp, #%d" }, + { "ldaspsp", "add sp, #%d" }, + { "*pair", "(%s)" }, + { "enter", "" }, + { "enters", "" }, + { "enterx", + "add sp, #-%d" }, + { "pusha", + "push af\n" + "push bc\n" + "push de\n" + "push hl" + }, + { "popa", + "pop hl\n" + "pop de\n" + "pop bc\n" + "pop af" + }, + { "adjustsp", "lda sp, -%d(sp)" }, + { "fileprelude", "" }, + { "profileenter", + "ld a, #3\n" + "rst\t0x08" + }, + { "profileexit", + "ld a, #4\n" + "rst\t0x08" + }, + { NULL, NULL } +}; + +static const ASM_MAPPING _asxxxx_z80_mapping[] = { + /* We want to prepend the _ */ + { "area", ".area _%s" }, + { "areacode", ".area _%s" }, + { "areadata", ".area _%s" }, + { "areahome", ".area _%s" }, + { "*ixx", "%d (ix)" }, + { "*iyx", "%d (iy)" }, + { "*hl", "(hl)" }, + { "di", "di" }, + { "ei", "ei" }, + { "ldahli", + "ld a, (hl)\n" + "inc\thl" }, + { "ldahlsp", + "ld hl, #%d\n" + "add\thl, sp" }, + { "ldaspsp", + "ld iy,#%d\n" + "add\tiy,sp\n" + "ld\tsp,iy" }, + { "*pair", "(%s)" }, + { "enter", + "push\tix\n" + "ld\tix,#0\n" + "add\tix,sp" }, + { "enters", + "call\t___sdcc_enter_ix\n" }, + { "pusha", + "push af\n" + "push\tbc\n" + "push\tde\n" + "push\thl\n" + "push\tiy" + }, + { "popa", + "pop iy\n" + "pop\thl\n" + "pop\tde\n" + "pop\tbc\n" + "pop\taf" + }, + { "adjustsp", "lda sp,-%d(sp)" }, + { "profileenter", + "ld a,#3\n" + "rst\t0x08" + }, + { "profileexit", + "ld a,#4\n" + "rst\t0x08" + }, + { NULL, NULL } +}; + +static const ASM_MAPPING _asxxxx_r2k_mapping[] = { + /* We want to prepend the _ */ + { "area", ".area _%s" }, + { "areacode", ".area _%s" }, + { "areadata", ".area _%s" }, + { "areahome", ".area _%s" }, + { "*ixx", "%d (ix)" }, + { "*iyx", "%d (iy)" }, + { "*hl", "(hl)" }, + { "di", "ipset3" }, + { "ei", "ipres" }, + { "ldahli", + "ld a, (hl)\n" + "inc\thl" }, + { "ldahlsp", + "ld hl, #%d\n" + "add\thl, sp" }, + { "ldaspsp", + "ld iy,#%d\n" + "add\tiy,sp\n" + "ld\tsp,iy" }, + { "*pair", "(%s)" }, + { "enter", + "push\tix\n" + "ld\tix,#0\n" + "add\tix,sp" }, + { "enters", + "call\t___sdcc_enter_ix\n" }, + { "pusha", + "push af\n" + "push\tbc\n" + "push\tde\n" + "push\thl\n" + "push\tiy" + }, + { "popa", + "pop iy\n" + "pop\thl\n" + "pop\tde\n" + "pop\tbc\n" + "pop\taf" + }, + { "adjustsp", "lda sp,-%d(sp)" }, + { "profileenter", + "ld a,#3\n" + "rst\t0x28" + }, + { "profileexit", + "ld a,#4\n" + "rst\t0x28" + }, + { NULL, NULL } +}; + +static const ASM_MAPPING _rgbds_mapping[] = { + { "global", "GLOBAL %s" }, + { "extern", "GLOBAL %s" }, + { "slabeldef", "%s:" }, + { "labeldef", "%s:" }, + { "tlabeldef", ".l%05d:" }, + { "tlabel", ".l%05d" }, + { "fileprelude", + "; Generated using the rgbds tokens.\n" + "\t; We have to define these here as sdcc doesn't make them global by default\n" + "\tGLOBAL __mulschar\n" + "\tGLOBAL __muluchar\n" + "\tGLOBAL __mulint\n" + "\tGLOBAL __divschar\n" + "\tGLOBAL __divuchar\n" + "\tGLOBAL __divsint\n" + "\tGLOBAL __divuint\n" + "\tGLOBAL __modschar\n" + "\tGLOBAL __moduchar\n" + "\tGLOBAL __modsint\n" + "\tGLOBAL __moduint\n" + "\tGLOBAL __mullong\n" + "\tGLOBAL __modslong\n" + "\tGLOBAL __divslong\n" + "\tGLOBAL banked_call\n" + "\tGLOBAL banked_ret\n" + }, + { "functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------" + }, + { "functionlabeldef", "%s:" }, + { "globalfunctionlabeldef", "%s::" }, + { "zero", "$00" }, + { "one", "$01" }, + { "area", "SECTION \"%s\",CODE" }, + { "areadata", "SECTION \"%F_%s\",BSS" }, + { "areacode", "SECTION \"%F_CODE\",%s" }, + { "areahome", "SECTION \"%F_HOME\",HOME" }, + { "ascii", "DB \"%s\"" }, + { "ds", "DS %d" }, + { "db", "DB" }, + { "dbs", "DB %s" }, + { "dw", "DW" }, + { "dws", "DW %s" }, + { "immed", "" }, + { "constbyte", "$%02X" }, + { "constword", "$%04X" }, + { "immedword", "$%04X" }, + { "immedbyte", "$%02X" }, + { "hashedstr", "%s" }, + { "lsbimmeds", "%s & $FF" }, + { "msbimmeds", "%s >> 8" }, + { "bankimmeds", "BANK(%s)" }, + { "module", "; MODULE %s" }, + { NULL, NULL } +}; + +static const ASM_MAPPING _rgbds_gb_mapping[] = { + { "pusha", + "push af\n" + "\tpush bc\n" + "\tpush de\n" + "\tpush hl" + }, + { "popa", + "pop hl\n" + "\tpop de\n" + "\tpop bc\n" + "\tpop af" + }, + { "di", "di" }, + { "ei", "ei" }, + { "adjustsp", "add sp, -%d" }, + { "enter", "" }, + { "enters", "" }, + { "ldahli", "ld a, [hl+]" }, + { "*hl", "[hl]" }, + { "ldahlsp", "ld hl, [sp+%d]" }, + { "ldaspsp", "add sp, %d" }, + { "*pair", "[%s]" }, + { NULL, NULL } +}; + +static const ASM_MAPPING _isas_mapping[] = { + { "global", "GLOBAL %s" }, + { "extern", "GLOBAL %s" }, + { "slabeldef", "%s:" }, + { "labeldef", "%s:" }, + { "tlabeldef", "?l%05d:" }, + { "tlabel", "?l%05d" }, + { "fileprelude", + ";Generated using the isas tokens.\n" + "\tLPREFIX '?' ; Treat labels starting with ? as local.\n" + "\tONCNUM ; Numbers are hex\n" + "\tCAPSOFF ; Case sensitive\n" + "\tISDMG ; Gameboy mode\n" + "_CODE\tGROUP\n" + "\t; We have to define these here as sdcc doesn't make them global by default\n" + "\tGLOBAL __mulschar\n" + "\tGLOBAL __muluchar\n" + "\tGLOBAL __mulint\n" + "\tGLOBAL __divschar\n" + "\tGLOBAL __divuchar\n" + "\tGLOBAL __divsint\n" + "\tGLOBAL __divuint\n" + "\tGLOBAL __modschar\n" + "\tGLOBAL __moduchar\n" + "\tGLOBAL __modsint\n" + "\tGLOBAL __moduint\n" + "\tGLOBAL banked_call\n" + "\tGLOBAL banked_ret\n" + }, + { "functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------" + }, + { "functionlabeldef", "%s:" }, + { "globalfunctionlabeldef", "%s::" }, + { "zero", "$00" }, + { "one", "$01" }, + { "area", "%s\tGROUP" }, + { "areacode", "_CODE\tGROUP" }, + { "areadata", "_DATA\tGROUP" }, + { "areahome", "_CODE\tGROUP" }, + { "ascii", "DB \"%s\"" }, + { "ds", "DS %d" }, + { "db", "DB" }, + { "dbs", "DB %s" }, + { "dw", "DW" }, + { "dws", "DW %s" }, + { "immed", "" }, + { "constbyte", "0x%02X" }, + { "constword", "0x%04X" }, + { "immedword", "0x%04X" }, + { "immedbyte", "0x%02X" }, + { "hashedstr", "%s" }, + { "lsbimmeds", "%s & 0xFF" }, + { "msbimmeds", "%s >> 8" }, + { "bankimmeds", "!%s" }, + { "module", "; MODULE %s" }, + { NULL, NULL } +}; + +static const ASM_MAPPING _isas_gb_mapping[] = { + { "pusha", + "push af\n" + "push bc\n" + "push de\n" + "push hl" + }, + { "popa", + "pop hl\n" + "pop de\n" + "pop bc\n" + "pop af" + }, + { "di", "di" }, + { "ei", "ei" }, + { "adjustsp", "add sp, -%d" }, + { "enter", "" }, + { "enters", "" }, + { "ldahli", "ld a, (hli)" }, + { "*hl", "(hl)" }, + { "ldahlsp", "ldhl sp, %d" }, + { "ldaspsp", "add sp, %d" }, + { "*pair", "(%s)" }, + { NULL, NULL } +}; + +static const ASM_MAPPING _z80asm_mapping[] = { + { "global", "XDEF %s" }, + { "extern", "XREF %s" }, + { "slabeldef", "\n.%s" }, + { "labeldef", "\n.%s" }, + { "tlabeldef", "\n.l%N%05d" }, + { "tlabel", "l%N%05d" }, + { "fileprelude", + "; Generated using the z80asm/z88 tokens.\n" + "\tXREF __muluchar_rrx_s\n" + "\tXREF __mulschar_rrx_s\n" + "\tXREF __mulint_rrx_s\n" + "\tXREF __mullong_rrx_s\n" + "\tXREF __divuchar_rrx_s\n" + "\tXREF __divschar_rrx_s\n" + "\tXREF __divsint_rrx_s\n" + "\tXREF __divuint_rrx_s\n" + "\tXREF __divulong_rrx_s\n" + "\tXREF __divslong_rrx_s\n" + "\tXREF __rrulong_rrx_s\n" + "\tXREF __rrslong_rrx_s\n" + "\tXREF __rlulong_rrx_s\n" + "\tXREF __rlslong_rrx_s\n" + }, + { "functionheader", + "; ---------------------------------\n" + "; Function %s\n" + "; ---------------------------------" + }, + { "functionlabeldef", ".%s" }, + { "globalfunctionlabeldef", ".%s" }, + { "zero", "$00" }, + { "one", "$01" }, + { "ascii", "DEFM \"%s\"" }, + { "ds", "DEFS %d" }, + { "db", "DEFB" }, + { "dbs", "DEFB %s" }, + { "dw", "DEFW" }, + { "dws", "DEFB %s" }, + { "immed", "" }, + { "constbyte", "$%02X" }, + { "constword", "$%04X" }, + { "immedword", "$%04X" }, + { "immedbyte", "$%02X" }, + { "hashedstr", "%s" }, + { "lsbimmeds", "%s ~ $FF" }, + { "msbimmeds", "%s / 256" }, + + { "bankimmeds", "BANK(%s)" }, + { "module", "MODULE %s" }, + { "area", "; Area %s" }, + { "areadata", "; Aread BSS" }, + { "areacode", "; Area CODE" }, + { "areahome", "; Area HOME" }, + { NULL, NULL } +}; + +static const ASM_MAPPING _z80asm_z80_mapping[] = { + { "*ixx", "(ix%+d)" }, + { "*iyx", "(iy%+d)" }, + { "*hl", "(hl)" }, + { "di", "di" }, + { "ei", "ei" }, + { "ldahli", + "ld a, (hl)\n" + "inc\thl" }, + { "ldahlsp", + "ld hl, %d\n" + "add\thl, sp" }, + { "ldaspsp", + "ld iy, %d\n" + "add\tiy, sp\n" + "ld\tsp, iy" }, + { "*pair", "(%s)" }, + { "enter", + "push\tix\n" + "ld\tix,0\n" + "add\tix,sp" }, + { "enters", + "call\t___sdcc_enter_ix\n" }, + { "pusha", + "push af\n" + "push\tbc\n" + "push\tde\n" + "push\thl\n" + "push\tiy" + }, + { "popa", + "pop\tiy\n" + "pop\thl\n" + "pop\tde\n" + "pop\tbc\n" + "pop\taf" + }, + { "adjustsp", "lda sp, (sp%+d)" }, + { "profileenter", + "ld a,3\n" + "rst\t$08" + }, + { "profileexit", + "ld a,4\n" + "rst\t$08" + }, + { NULL, NULL } +}; + +static const ASM_MAPPINGS _isas = { + NULL, + _isas_mapping +}; + +const ASM_MAPPINGS _isas_gb = { + &_isas, + _isas_gb_mapping +}; + +static const ASM_MAPPINGS _rgbds = { + NULL, + _rgbds_mapping +}; + +const ASM_MAPPINGS _rgbds_gb = { + &_rgbds, + _rgbds_gb_mapping +}; + +const ASM_MAPPINGS _asxxxx_gb = { + &asm_asxxxx_mapping, + _asxxxx_gb_mapping +}; + +const ASM_MAPPINGS _asxxxx_z80 = { + &asm_asxxxx_mapping, + _asxxxx_z80_mapping +}; + +static const ASM_MAPPINGS _z80asm = { + NULL, + _z80asm_mapping +}; + +const ASM_MAPPINGS _z80asm_z80 = { + &_z80asm, + _z80asm_z80_mapping +}; + +const ASM_MAPPINGS _asxxxx_r2k = { + &asm_asxxxx_mapping, + _asxxxx_r2k_mapping +}; diff --git a/src/z80/peep.c b/src/z80/peep.c new file mode 100644 index 0000000..8b2c416 --- /dev/null +++ b/src/z80/peep.c @@ -0,0 +1,1169 @@ +/*------------------------------------------------------------------------- + peep.c - source file for peephole optimizer helper functions + + Written By - Philipp Klaus Krause + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include "common.h" +#include "SDCCicode.h" +#include "z80.h" +#include "SDCCglobl.h" +#include "SDCCpeeph.h" +#include "gen.h" + +#define NOTUSEDERROR() do {werror(E_INTERNAL_ERROR, __FILE__, __LINE__, "error in notUsed()");} while(0) + +#if 0 +#define D(_s) { printf _s; fflush(stdout); } +#else +#define D(_s) +#endif + +#define ISINST(l, i) (!STRNCASECMP((l), (i), sizeof(i) - 1) && (!(l)[sizeof(i) - 1] || isspace((unsigned char)((l)[sizeof(i) - 1])))) + +typedef enum +{ + S4O_CONDJMP, + S4O_WR_OP, + S4O_RD_OP, + S4O_TERM, + S4O_VISITED, + S4O_ABORT, + S4O_CONTINUE +} S4O_RET; + +static struct +{ + lineNode *head; +} _G; + +extern bool z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX + 1]; +extern bool z80_symmParm_in_calls_from_current_function; +extern bool z80_regs_preserved_in_calls_from_current_function[IYH_IDX + 1]; + +/*-----------------------------------------------------------------*/ +/* univisitLines - clear "visited" flag in all lines */ +/*-----------------------------------------------------------------*/ +static void +unvisitLines (lineNode *pl) +{ + for (; pl; pl = pl->next) + pl->visited = FALSE; +} + +#define AOP(op) op->aop +#define AOP_SIZE(op) AOP(op)->size + +static bool +isReturned(const char *what) +{ + symbol *sym; + sym_link *sym_lnk; + int size; + lineNode *l; + + if(strncmp(what, "iy", 2) == 0) + return FALSE; + if(strlen(what) != 1) + return TRUE; + + l = _G.head; + do + { + l = l->next; + } while(l->isComment || l->ic == NULL || l->ic->op != FUNCTION); + + sym = OP_SYMBOL(IC_LEFT(l->ic)); + + if(sym && IS_DECL(sym->type)) + { + // Find size of return value. + specifier *spec; + if(sym->type->select.d.dcl_type != FUNCTION) + NOTUSEDERROR(); + spec = &(sym->etype->select.s); + if(spec->noun == V_VOID || spec->noun == V_INT && spec->b_longlong) // long long is not returned via registers for the Z80-related ports + size = 0; + else if(spec->noun == V_CHAR || spec->noun == V_BOOL) + size = 1; + else if(spec->noun == V_INT && !(spec->b_long)) + size = 2; + else + size = 4; + + // Check for returned pointer. + sym_lnk = sym->type; + while (sym_lnk && !IS_PTR (sym_lnk)) + sym_lnk = sym_lnk->next; + if(IS_PTR(sym_lnk)) + size = 2; + } + else + { + NOTUSEDERROR(); + size = 4; + } + + switch(*what) + { + case 'd': + return(size >= 4); + case 'e': + return(size >= 3); + case 'h': + return(size >= 2); + case 'l': + return(size >= 1); + default: + return FALSE; + } +} + +/*-----------------------------------------------------------------*/ +/* incLabelJmpToCount - increment counter "jmpToCount" in entry */ +/* of the list labelHash */ +/*-----------------------------------------------------------------*/ +static bool +incLabelJmpToCount (const char *label) +{ + labelHashEntry *entry; + + entry = getLabelRef (label, _G.head); + if (!entry) + return FALSE; + entry->jmpToCount++; + return TRUE; +} + +/*-----------------------------------------------------------------*/ +/* findLabel - */ +/* 1. extracts label in the opcode pl */ +/* 2. increment "label jump-to count" in labelHash */ +/* 3. search lineNode with label definition and return it */ +/*-----------------------------------------------------------------*/ +static lineNode * +findLabel (const lineNode *pl) +{ + char *p; + lineNode *cpl; + + /* 1. extract label in opcode */ + + /* In each z80 jumping opcode the label is at the end of the opcode */ + p = strlen (pl->line) - 1 + pl->line; + + /* scan backward until ',' or '\t' */ + for (; p > pl->line; p--) + if (*p == ',' || isspace(*p)) + break; + + /* sanity check */ + if (p == pl->line) + { + NOTUSEDERROR(); + return NULL; + } + + /* skip ',' resp. '\t' */ + ++p; + + /* 2. increment "label jump-to count" */ + if (!incLabelJmpToCount (p)) + return NULL; + + /* 3. search lineNode with label definition and return it */ + for (cpl = _G.head; cpl; cpl = cpl->next) + { + if (cpl->isLabel + && strncmp (p, cpl->line, strlen(p)) == 0) + { + return cpl; + } + } + return NULL; +} + +/* Check if reading arg implies reading what. */ +static bool argCont(const char *arg, const char *what) +{ + wassert (arg); + + while(isspace (*arg) || *arg == ',') + arg++; + + if (arg[0] == '#') + return false; + + if(arg[0] == '(' && arg[1] && arg[2] && (arg[2] != ')' && arg[3] != ')')) + return FALSE; + + if(*arg == '(') + arg++; + + // Get suitable end to avoid reading into further arguments. + const char *end = strchr(arg, ','); + if (!end) + end = arg + strlen(arg); + + const char *found = StrStr(arg, what); + + return(found && found < end); +} + +static bool +z80MightBeParmInCallFromCurrentFunction(const char *what) +{ + if (strchr(what, 'l') && z80_regs_used_as_parms_in_calls_from_current_function[L_IDX]) + return TRUE; + if (strchr(what, 'h') && z80_regs_used_as_parms_in_calls_from_current_function[H_IDX]) + return TRUE; + if (strchr(what, 'e') && z80_regs_used_as_parms_in_calls_from_current_function[E_IDX]) + return TRUE; + if (strchr(what, 'd') && z80_regs_used_as_parms_in_calls_from_current_function[D_IDX]) + return TRUE; + if (strchr(what, 'c') && z80_regs_used_as_parms_in_calls_from_current_function[C_IDX]) + return TRUE; + if (strchr(what, 'b') && z80_regs_used_as_parms_in_calls_from_current_function[B_IDX]) + return TRUE; + if (strstr(what, "iy") && (z80_regs_used_as_parms_in_calls_from_current_function[IYL_IDX] || z80_regs_used_as_parms_in_calls_from_current_function[IYH_IDX])) + return TRUE; + + return FALSE; +} + +static bool +z80MightRead(const lineNode *pl, const char *what) +{ + if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0) + what = "iy"; + if(strcmp(what, "ixl") == 0 || strcmp(what, "ixh") == 0) + what = "ix"; + + if(strcmp(pl->line, "call\t__initrleblock") == 0) + return TRUE; + + if(strcmp(pl->line, "call\t___sdcc_call_hl") == 0 && (strchr(what, 'h') != 0 || strchr(what, 'l') != 0)) + return TRUE; + + if(strcmp(pl->line, "call\t___sdcc_call_iy") == 0 && strstr(what, "iy") != 0) + return TRUE; + + if(strncmp(pl->line, "call\t", 5) == 0 && strchr(pl->line, ',') == 0) + { + const symbol *f = findSym (SymbolTab, 0, pl->line + 6); + if (f) + { + const value *args = FUNC_ARGS (f->type); + + if (IFFUNC_ISZ88DK_FASTCALL (f->type) && args) // Has one register argument of size up to 32 bit. + { + const unsigned int size = getSize (args->type); + wassert (!args->next); // Only one argment allowed in __z88dk_fastcall functions. + if (strchr(what, 'l') && size >= 1) + return TRUE; + if (strchr(what, 'h') && size >= 2) + return TRUE; + if (strchr(what, 'e') && size >= 3) + return TRUE; + if (strchr(what, 'd') && size >= 4) + return TRUE; + } + return FALSE; + } + else // Fallback needed for calls through function pointers and for calls to literal addresses. + return z80MightBeParmInCallFromCurrentFunction(what); + } + + if(ISINST(pl->line, "reti") || ISINST(pl->line, "retn")) + return(false); + + if(ISINST(pl->line, "ret")) // --reserve-regs-iy uses ret in code gen for calls through function pointers + return(IY_RESERVED ? isReturned(what) || z80MightBeParmInCallFromCurrentFunction(what) : isReturned(what)); + + if(!strcmp(pl->line, "ex\t(sp), hl") || !strcmp(pl->line, "ex\t(sp),hl")) + return(!strcmp(what, "h") || !strcmp(what, "l")); + if(!strcmp(pl->line, "ex\t(sp), ix") || !strcmp(pl->line, "ex\t(sp),ix")) + return(!!strstr(what, "ix")); + if(!strcmp(pl->line, "ex\t(sp), iy") || !strcmp(pl->line, "ex\t(sp),iy")) + return(!!strstr(what, "iy")); + if(!strcmp(pl->line, "ex\tde, hl") || !strcmp(pl->line, "ex\tde,hl")) + return(!strcmp(what, "h") || !strcmp(what, "l") || !strcmp(what, "d") || !strcmp(what, "e")); + if(ISINST(pl->line, "ld")) + { + if(argCont(strchr(pl->line, ','), what)) + return(true); + if(*(strchr(pl->line, ',') - 1) == ')' && strstr(pl->line + 3, what) && (strchr(pl->line, '#') == 0 || strchr(pl->line, '#') > strchr(pl->line, ','))) + return(true); + return(false); + } + + if(!strcmp(pl->line, "xor\ta, a") || !strcmp(pl->line, "xor\ta,a")) + return(false); + + if(ISINST(pl->line, "adc") || + ISINST(pl->line, "add") || + ISINST(pl->line, "and") || + ISINST(pl->line, "sbc") || + ISINST(pl->line, "sub") || + ISINST(pl->line, "xor")) + { + const char *arg = pl->line + 4; + while(isspace(*arg)) + arg++; + if(*arg == 'a' && *(arg + 1) == ',') + { + if(!strcmp(what, "a")) + return(true); + arg += 2; + } + else if(!strncmp(arg, "hl", 2) && *(arg + 2) == ',') + { + if(!strcmp(what, "h") || !strcmp(what, "l")) + return(true); + arg += 3; + } + return(argCont(arg, what)); + } + + if(ISINST(pl->line, "or") || ISINST(pl->line, "cp") ) + { + const char *arg = pl->line + 3; + while(isspace(*arg)) + arg++; + if(*arg == 'a' && *(arg + 1) == ',') + { + if(!strcmp(what, "a")) + return(true); + arg += 2; + } + else if(!strncmp(arg, "hl", 2) && *(arg + 2) == ',') + { + if(!strcmp(what, "h") || !strcmp(what, "l")) + return(true); + arg += 3; + } + return(argCont(arg, what)); + } + + if(ISINST(pl->line, "neg")) + return(strcmp(what, "a") == 0); + + if(ISINST(pl->line, "pop")) + return(false); + + if(ISINST(pl->line, "push")) + return(strstr(pl->line + 5, what) != 0); + + if(ISINST(pl->line, "dec") || + ISINST(pl->line, "inc")) + { + return(argCont(pl->line + 4, what)); + } + + if(ISINST(pl->line, "cpl")) + return(!strcmp(what, "a")); + + if(ISINST(pl->line, "di") || ISINST(pl->line, "ei")) + return(false); + + // Rotate and shift group + if(ISINST(pl->line, "rlca") || + ISINST(pl->line, "rla") || + ISINST(pl->line, "rrca") || + ISINST(pl->line, "rra")) + { + return(strcmp(what, "a") == 0); + } + if(ISINST(pl->line, "rl") || + ISINST(pl->line, "rr")) + { + return(argCont(pl->line + 3, what)); + } + if(ISINST(pl->line, "rlc") || + ISINST(pl->line, "sla") || + ISINST(pl->line, "sra") || + ISINST(pl->line, "srl")) + { + return(argCont(pl->line + 4, what)); + } + if(!IS_GB && !IS_RAB && + (ISINST(pl->line, "rld") || + ISINST(pl->line, "rrd"))) + return(!!strstr("ahl", what)); + + // Bit set, reset and test group + if(ISINST(pl->line, "bit") || + ISINST(pl->line, "set") || + ISINST(pl->line, "res")) + { + return(argCont(strchr(pl->line + 4, ','), what)); + } + + if(ISINST(pl->line, "ccf") || + ISINST(pl->line, "nop")) + return(false); + + if(ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) + return(false); + + if(ISINST(pl->line, "djnz")) + return(strchr(what, 'b') != 0); + + if(!IS_GB && (ISINST(pl->line, "ldd") || ISINST(pl->line, "lddr") || ISINST(pl->line, "ldi") || ISINST(pl->line, "ldir"))) + return(strchr("bcdehl", *what)); + + if(!IS_GB && !IS_RAB && (ISINST(pl->line, "cpd") || ISINST(pl->line, "cpdr") || ISINST(pl->line, "cpi") || ISINST(pl->line, "cpir"))) + return(strchr("abchl", *what)); + + if(!IS_GB && !IS_RAB && ISINST(pl->line, "out")) + return(strstr(strchr(pl->line + 4, ','), what) != 0 || strstr(pl->line + 4, "(c)") && (!strcmp(what, "b") || !strcmp(what, "c"))); + if(!IS_GB && !IS_RAB && ISINST(pl->line, "in")) + return(!strstr(strchr(pl->line + 4, ','), "(c)") && !strcmp(what, "a") || strstr(strchr(pl->line + 4, ','), "(c)") && (!strcmp(what, "b") || !strcmp(what, "c"))); + + if(!IS_GB && !IS_RAB && + (ISINST(pl->line, "ini") || ISINST(pl->line, "ind") || ISINST(pl->line, "inir") || ISINST(pl->line, "indr") || + ISINST(pl->line, "outi") || ISINST(pl->line, "outd") || ISINST(pl->line, "otir") || ISINST(pl->line, "otdr"))) + return(strchr("bchl", *what)); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "in0")) + return(false); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "mlt")) + return(argCont(pl->line + 4, what)); + + if((IS_Z180 || IS_EZ80_Z80) && + (ISINST(pl->line, "otim") || ISINST(pl->line, "otimr") || ISINST(pl->line, "otir") || ISINST(pl->line, "otirx"))) + return(strchr("bchl", *what)); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "slp")) + return(false); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "tst")) + return(argCont(pl->line + 4, what)); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "tstio")) + return(!strcmp(what, "c")); + + if(IS_RAB && ISINST(pl->line, "mul")) + return(!strcmp(what, "b") || !strcmp(what, "c") || !strcmp(what, "d") || !strcmp(what, "e")); + + if(IS_RAB && ISINST(pl->line, "bool")) + return(argCont(pl->line + 5, what)); + + /* TODO: Can we know anything about rst? */ + if(ISINST(pl->line, "rst")) + return(true); + + return(true); +} + +static bool +z80UncondJump(const lineNode *pl) +{ + if((ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) && + strchr(pl->line, ',') == 0) + return TRUE; + return FALSE; +} + +static bool +z80CondJump(const lineNode *pl) +{ + if(((ISINST(pl->line, "jp") || ISINST(pl->line, "jr")) && + strchr(pl->line, ',') != 0) || + ISINST(pl->line, "djnz")) + return TRUE; + return FALSE; +} + +static bool +z80SurelyWrites(const lineNode *pl, const char *what) +{ + if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0) + what = "iy"; + if(strcmp(what, "ixl") == 0 || strcmp(what, "ixh") == 0) + what = "ix"; + + if(ISINST(pl->line, "xor") && strcmp(what, "a") == 0) + return(true); + if(ISINST(pl->line, "ld") && strncmp(pl->line + 3, "hl", 2) == 0 && (what[0] == 'h' || what[0] == 'l')) + return(true); + if(ISINST(pl->line, "ld") && strncmp(pl->line + 3, "de", 2) == 0 && (what[0] == 'd' || what[0] == 'e')) + return(true); + if(ISINST(pl->line, "ld") && strncmp(pl->line + 3, "bc", 2) == 0 && (what[0] == 'b' || what[0] == 'c')) + return(true); + if((ISINST(pl->line, "ld") || ISINST(pl->line, "in")) + && strncmp(pl->line + 3, what, strlen(what)) == 0 && pl->line[3 + strlen(what)] == ',') + return(true); + if(ISINST(pl->line, "pop") && strstr(pl->line + 4, what)) + return(true); + if(ISINST(pl->line, "call") && strchr(pl->line, ',') == 0) + { + const symbol *f = findSym (SymbolTab, 0, pl->line + 6); + const bool *preserved_regs; + + if(!strcmp(what, "ix")) + return(false); + + if(f) + preserved_regs = f->type->funcAttrs.preserved_regs; + else // Err on the safe side. + preserved_regs = z80_regs_preserved_in_calls_from_current_function; + + if(!strcmp(what, "c")) + return !preserved_regs[C_IDX]; + if(!strcmp(what, "b")) + return !preserved_regs[B_IDX]; + if(!strcmp(what, "e")) + return !preserved_regs[E_IDX]; + if(!strcmp(what, "d")) + return !preserved_regs[D_IDX]; + if(!strcmp(what, "l")) + return !preserved_regs[L_IDX]; + if(!strcmp(what, "h")) + return !preserved_regs[H_IDX]; + if(!strcmp(what, "iy")) + return !preserved_regs[IYL_IDX] && !preserved_regs[IYH_IDX]; + } + if(strcmp(pl->line, "ret") == 0) + return true; + if(strcmp(pl->line, "ld\tiy") == 0 && strncmp(what, "iy", 2) == 0) + return true; + + if (IS_Z180 || IS_EZ80_Z80) + { + if (ISINST(pl->line, "mlt")) + return(strchr(pl->line + 4, *what) != 0); + + if (ISINST(pl->line, "otim") || + ISINST(pl->line, "otimr") || + ISINST(pl->line, "otdm") || + ISINST(pl->line, "otdmr")) + return(strchr("bchl", *what) != NULL); + + if (ISINST(pl->line, "in0")) + return(!strncmp(pl->line + 4, what, strlen(what))); + } + + return(false); +} + +static bool +z80SurelyReturns(const lineNode *pl) +{ + if(strcmp(pl->line, "\tret") == 0) + return TRUE; + return FALSE; +} + +/*-----------------------------------------------------------------*/ +/* scan4op - "executes" and examines the assembler opcodes, */ +/* follows conditional and un-conditional jumps. */ +/* Moreover it registers all passed labels. */ +/* */ +/* Parameter: */ +/* lineNode **pl */ +/* scanning starts from pl; */ +/* pl also returns the last scanned line */ +/* const char *pReg */ +/* points to a register (e.g. "ar0"). scan4op() tests for */ +/* read or write operations with this register */ +/* const char *untilOp */ +/* points to NULL or a opcode (e.g. "push"). */ +/* scan4op() returns if it hits this opcode. */ +/* lineNode **plCond */ +/* If a conditional branch is met plCond points to the */ +/* lineNode of the conditional branch */ +/* */ +/* Returns: */ +/* S4O_ABORT */ +/* on error */ +/* S4O_VISITED */ +/* hit lineNode with "visited" flag set: scan4op() already */ +/* scanned this opcode. */ +/* S4O_FOUNDOPCODE */ +/* found opcode and operand, to which untilOp and pReg are */ +/* pointing to. */ +/* S4O_RD_OP, S4O_WR_OP */ +/* hit an opcode reading or writing from pReg */ +/* S4O_CONDJMP */ +/* hit a conditional jump opcode. pl and plCond return the */ +/* two possible branches. */ +/* S4O_TERM */ +/* acall, lcall, ret and reti "terminate" a scan. */ +/*-----------------------------------------------------------------*/ +static S4O_RET +scan4op (lineNode **pl, const char *what, const char *untilOp, + lineNode **plCond) +{ + for (; *pl; *pl = (*pl)->next) + { + if (!(*pl)->line || (*pl)->isDebug || (*pl)->isComment || (*pl)->isLabel) + continue; + D(("Scanning %s for %s\n", (*pl)->line, what)); + /* don't optimize across inline assembler, + e.g. isLabel doesn't work there */ + if ((*pl)->isInline) + { + D(("S4O_RD_OP: Inline asm\n")); + return S4O_ABORT; + } + + if ((*pl)->visited) + { + D(("S4O_VISITED\n")); + return S4O_VISITED; + } + + (*pl)->visited = TRUE; + + if(z80MightRead(*pl, what)) + { + D(("S4O_RD_OP\n")); + return S4O_RD_OP; + } + + if(z80UncondJump(*pl)) + { + *pl = findLabel (*pl); + if (!*pl) + { + D(("S4O_ABORT\n")); + return S4O_ABORT; + } + } + if(z80CondJump(*pl)) + { + *plCond = findLabel (*pl); + if (!*plCond) + { + D(("S4O_ABORT\n")); + return S4O_ABORT; + } + D(("S4O_CONDJMP\n")); + return S4O_CONDJMP; + } + + if(z80SurelyWrites(*pl, what)) + { + D(("S4O_WR_OP\n")); + return S4O_WR_OP; + } + + /* Don't need to check for de, hl since z80MightRead() does that */ + if(z80SurelyReturns(*pl)) + { + D(("S4O_TERM\n")); + return S4O_TERM; + } + } + D(("S4O_ABORT\n")); + return S4O_ABORT; +} + +/*-----------------------------------------------------------------*/ +/* doTermScan - scan through area 2. This small wrapper handles: */ +/* - action required on different return values */ +/* - recursion in case of conditional branches */ +/*-----------------------------------------------------------------*/ +static bool +doTermScan (lineNode **pl, const char *what) +{ + lineNode *plConditional; + + for (;; *pl = (*pl)->next) + { + switch (scan4op (pl, what, NULL, &plConditional)) + { + case S4O_TERM: + case S4O_VISITED: + case S4O_WR_OP: + /* all these are terminating conditions */ + return TRUE; + case S4O_CONDJMP: + /* two possible destinations: recurse */ + { + lineNode *pl2 = plConditional; + D(("CONDJMP trying other branch first\n")); + if (!doTermScan (&pl2, what)) + return FALSE; + D(("Other branch OK.\n")); + } + continue; + case S4O_RD_OP: + default: + /* no go */ + return FALSE; + } + } +} + +/* Regular 8 bit reg */ +static bool +isReg(const char *what) +{ + if(strlen(what) != 1) + return FALSE; + switch(*what) + { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'h': + case 'l': + return TRUE; + } + return FALSE; +} + +/* 8-Bit reg only accessible by 16-bit and undocumented instructions */ +static bool +isUReg(const char *what) +{ + if(strcmp(what, "iyl") == 0 || strcmp(what, "iyh") == 0) + return TRUE; + if(strcmp(what, "ixl") == 0 || strcmp(what, "ixh") == 0) + return TRUE; + return FALSE; +} + +static bool +isRegPair(const char *what) +{ + if(strlen(what) != 2) + return FALSE; + if(strcmp(what, "bc") == 0) + return TRUE; + if(strcmp(what, "de") == 0) + return TRUE; + if(strcmp(what, "hl") == 0) + return TRUE; + if(strcmp(what, "sp") == 0) + return TRUE; + if(strcmp(what, "ix") == 0) + return TRUE; + if(strcmp(what, "iy") == 0) + return TRUE; + return FALSE; +} + +/* Check that what is never read after endPl. */ + +bool +z80notUsed (const char *what, lineNode *endPl, lineNode *head) +{ + lineNode *pl; + D(("Checking for %s\n", what)); + if(isRegPair(what)) + { + char low[2], high[2]; + low[0] = what[1]; + high[0] = what[0]; + low[1] = 0; + high[1] = 0; + if(strcmp(what, "iy") == 0) + { + if(IY_RESERVED) + return FALSE; + return(z80notUsed("iyl", endPl, head) && z80notUsed("iyh", endPl, head)); + } + if(strcmp(what, "ix") == 0) + { + if(IY_RESERVED) + return FALSE; + return(z80notUsed("ixl", endPl, head) && z80notUsed("ixh", endPl, head)); + } + return(z80notUsed(low, endPl, head) && z80notUsed(high, endPl, head)); + } + + if(!isReg(what) && !isUReg(what)) + return FALSE; + + _G.head = head; + + unvisitLines (_G.head); + + pl = endPl->next; + if (!doTermScan (&pl, what)) + return FALSE; + + return TRUE; +} + +bool +z80notUsedFrom (const char *what, const char *label, lineNode *head) +{ + lineNode *cpl; + + for (cpl = head; cpl; cpl = cpl->next) + { + if (cpl->isLabel && !strncmp (label, cpl->line, strlen(label))) + { + return z80notUsed (what, cpl, head); + } + } + + return false; +} + +bool +z80canAssign (const char *op1, const char *op2, const char *exotic) +{ + const char *dst, *src; + + // Indexed accesses: One is the indexed one, the other one needs to be a reg or immediate. + if(exotic) + { + if(!strcmp(exotic, "ix") || !strcmp(exotic, "iy")) + { + if(isReg(op1)) + return TRUE; + } + else if(!strcmp(op2, "ix") || !strcmp(op2, "iy")) + { + if(isReg(exotic) || exotic[0] == '#') + return TRUE; + } + + return FALSE; + } + + // Everything else. + dst = op1; + src = op2; + + // 8-bit regs can be assigned to each other directly. + if(isReg(dst) && isReg(src)) + return TRUE; + + // Same if at most one of them is (hl). + if(isReg(dst) && !strcmp(src, "(hl)")) + return TRUE; + if(!strcmp(dst, "(hl)") && isReg(src)) + return TRUE; + + // Can assign between a and (bc), (de) + if(!strcmp(dst, "a") && (!strcmp(src, "(bc)") || ! strcmp(src, "(de)"))) + return TRUE; + if((!strcmp(dst, "(bc)") || ! strcmp(dst, "(de)")) && !strcmp(src, "a")) + return TRUE; + + // Can load immediate values directly into registers and register pairs. + if((isReg(dst) || isRegPair(dst)) && src[0] == '#') + return TRUE; + + if((!strcmp(dst, "a") || isRegPair(dst)) && !strncmp(src, "(#", 2)) + return TRUE; + if(!strncmp(dst, "(#", 2) && (!strcmp(src, "a") || isRegPair(src))) + return TRUE; + + // Can load immediate values directly into (hl). + if(!strcmp(dst, "(hl)") && src[0] == '#') + return TRUE; + + return FALSE; +} + +int z80instructionSize(lineNode *pl) +{ + const char *op1start, *op2start; + + /* move to the first operand: + * leading spaces are already removed, skip the mnenonic */ + for (op1start = pl->line; *op1start && !isspace (*op1start); ++op1start); + + /* skip the spaces between mnemonic and the operand */ + while (isspace (*op1start)) + ++op1start; + if (!(*op1start)) + op1start = NULL; + + if (op1start) + { + /* move to the second operand: + * find the comma and skip the following spaces */ + op2start = strchr(op1start, ','); + if (op2start) + { + do + ++op2start; + while (isspace (*op2start)); + + if ('\0' == *op2start) + op2start = NULL; + } + } + else + op2start = NULL; + + if(TARGET_IS_TLCS90) // Todo: More accurate estimate. + return(6); + + /* All ld instructions */ + if(ISINST(pl->line, "ld")) + { + /* These 4 are the only cases of 4 byte long ld instructions. */ + if(!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2)) + return(4); + if((argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) && op2start[0] == '#') + return(4); + + if(op1start[0] == '(' && STRNCASECMP(op1start, "(bc)", 4) && + STRNCASECMP(op1start, "(de)", 4) && STRNCASECMP(op1start, "(hl)", 4) && + STRNCASECMP(op2start, "hl", 2) && STRNCASECMP(op2start, "a", 1) || + op2start[0] == '(' && STRNCASECMP(op2start, "(bc)", 4) && + STRNCASECMP(op1start, "(de)", 4) && STRNCASECMP(op2start, "(hl)", 4) && + STRNCASECMP(op1start, "hl", 2) && STRNCASECMP(op1start, "a", 1)) + return(4); + + /* Rabbit 16-bit pointer load */ + if(IS_RAB && !STRNCASECMP(op1start, "hl", 2) && (argCont(op2start, "(hl)") || argCont(op2start, "(iy)"))) + return(4); + if(IS_RAB && !STRNCASECMP(op1start, "hl", 2) && (argCont(op2start, "(sp)") || argCont(op2start, "(ix)"))) + return(3); + + if(IS_EZ80_Z80 && /* eZ80 16-bit pointer load */ + (!STRNCASECMP(op1start, "bc", 2) || !STRNCASECMP(op1start, "de", 2) || !STRNCASECMP(op1start, "hl", 2) || !STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2))) + { + if (!STRNCASECMP(op2start, "(hl)", 4)) + return(2); + if (argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) + return(3); + } + + /* These 4 are the only remaining cases of 3 byte long ld instructions. */ + if(argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) + return(3); + if(argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) + return(3); + if((op1start[0] == '(' && STRNCASECMP(op1start, "(bc)", 4) && STRNCASECMP(op1start, "(de)", 4) && STRNCASECMP(op1start, "(hl)", 4)) || + (op2start[0] == '(' && STRNCASECMP(op2start, "(bc)", 4) && STRNCASECMP(op2start, "(de)", 4) && STRNCASECMP(op2start, "(hl)", 4))) + return(3); + if(op2start[0] == '#' && + (!STRNCASECMP(op1start, "bc", 2) || !STRNCASECMP(op1start, "de", 2) || !STRNCASECMP(op1start, "hl", 2) || !STRNCASECMP(op1start, "sp", 2))) + return(3); + + /* These 3 are the only remaining cases of 2 byte long ld instructions. */ + if(op2start[0] == '#') + return(2); + if(!STRNCASECMP(op1start, "i", 1) || !STRNCASECMP(op1start, "r", 1) || + !STRNCASECMP(op2start, "i", 1) || !STRNCASECMP(op2start, "r", 1)) + return(2); + if(!STRNCASECMP(op2start, "ix", 2) || !STRNCASECMP(op2start, "iy", 2)) + return(2); + + /* All other ld instructions */ + return(1); + } + + /* Exchange */ + if(ISINST(pl->line, "exx")) + return(1); + if(ISINST(pl->line, "ex")) + { + if(!op2start) + { + werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, __FUNCTION__, 4, pl->line); + return(4); + } + if(argCont(op1start, "(sp)") && (IS_RAB || !STRNCASECMP(op2start, "ix", 2) || !STRNCASECMP(op2start, "iy", 2))) + return(2); + return(1); + } + + /* Push / pop */ + if(ISINST(pl->line, "push") || ISINST(pl->line, "pop")) + { + if(!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2)) + return(2); + return(1); + } + + /* 16 bit add / subtract / and */ + if((ISINST(pl->line, "add") || ISINST(pl->line, "adc") || ISINST(pl->line, "sbc") || IS_RAB && ISINST(pl->line, "and")) && + !STRNCASECMP(op1start, "hl", 2)) + { + if(ISINST(pl->line, "add") || ISINST(pl->line, "and")) + return(1); + return(2); + } + if(ISINST(pl->line, "add") && (!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2))) + return(2); + + /* signed 8 bit adjustment to stack pointer */ + if((IS_RAB || IS_GB) && ISINST(pl->line, "add") && !STRNCASECMP(op1start, "sp", 2)) + return(2); + + /* 16 bit adjustment to stack pointer */ + if(IS_TLCS90 && ISINST(pl->line, "add") && !STRNCASECMP(op1start, "sp", 2)) + return(3); + + /* 8 bit arithmetic, two operands */ + if(op2start && op1start[0] == 'a' && + (ISINST(pl->line, "add") || ISINST(pl->line, "adc") || ISINST(pl->line, "sub") || ISINST(pl->line, "sbc") || + ISINST(pl->line, "cp") || ISINST(pl->line, "and") || ISINST(pl->line, "or") || ISINST(pl->line, "xor"))) + { + if(argCont(op2start, "(ix)") || argCont(op2start, "(iy)")) + return(3); + if(op2start[0] == '#') + return(2); + return(1); + } + + if(ISINST(pl->line, "rlca") || ISINST(pl->line, "rla") || ISINST(pl->line, "rrca") || ISINST(pl->line, "rra")) + return(1); + + /* Increment / decrement */ + if(ISINST(pl->line, "inc") || ISINST(pl->line, "dec")) + { + if(!STRNCASECMP(op1start, "ix", 2) || !STRNCASECMP(op1start, "iy", 2)) + return(2); + if(argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) + return(3); + return(1); + } + + if(ISINST(pl->line, "rlc") || ISINST(pl->line, "rl") || ISINST(pl->line, "rrc") || ISINST(pl->line, "rr") || + ISINST(pl->line, "sla") || ISINST(pl->line, "sra") || ISINST(pl->line, "srl")) + { + if(argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) + return(4); + return(2); + } + + if(ISINST(pl->line, "rld") || ISINST(pl->line, "rrd")) + return(2); + + /* Bit */ + if(ISINST(pl->line, "bit") || ISINST(pl->line, "set") || ISINST(pl->line, "res")) + { + if(argCont(op1start, "(ix)") || argCont(op1start, "(iy)")) + return(4); + return(2); + } + + if(ISINST(pl->line, "jr") || ISINST(pl->line, "djnz")) + return(2); + + if(ISINST(pl->line, "jp")) + { + if(!STRNCASECMP(op1start, "(hl)", 4)) + return(1); + if(!STRNCASECMP(op1start, "(ix)", 4) || !STRNCASECMP(op1start, "(iy)", 4)) + return(2); + return(3); + } + + if (IS_RAB && + (ISINST(pl->line, "ipset3") || ISINST(pl->line, "ipset2") || + ISINST(pl->line, "ipset1") || ISINST(pl->line, "ipset0") || + ISINST(pl->line, "ipres"))) + return(2); + + if(ISINST(pl->line, "reti") || ISINST(pl->line, "retn")) + return(2); + + if(ISINST(pl->line, "ret") || ISINST(pl->line, "rst")) + return(1); + + if(ISINST(pl->line, "call")) + return(3); + + if(ISINST(pl->line, "ldi") || ISINST(pl->line, "ldd") || ISINST(pl->line, "cpi") || ISINST(pl->line, "cpd")) + return(2); + + if(ISINST(pl->line, "neg")) + return(2); + + if(ISINST(pl->line, "daa") || ISINST(pl->line, "cpl") || ISINST(pl->line, "ccf") || ISINST(pl->line, "scf") || + ISINST(pl->line, "nop") || ISINST(pl->line, "halt") || ISINST(pl->line, "ei") || ISINST(pl->line, "di")) + return(1); + + if(ISINST(pl->line, "im")) + return(2); + + if(ISINST(pl->line, "in") || ISINST(pl->line, "out") || ISINST(pl->line, "ot")) + return(2); + + if((IS_Z180 || IS_EZ80_Z80) && (ISINST(pl->line, "in0") || ISINST(pl->line, "out0"))) + return(3); + + if(ISINST(pl->line, "di") || ISINST(pl->line, "ei")) + return(1); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "mlt")) + return(2); + + if((IS_Z180 || IS_EZ80_Z80) && ISINST(pl->line, "tst")) + return((op1start[0] == '#' || op2start && op1start[0] == '#') ? 3 : 2); + + if(IS_RAB && ISINST(pl->line, "mul")) + return(1); + + if(ISINST(pl->line, "lddr") || ISINST(pl->line, "ldir")) + return(2); + + if(IS_R3KA && + (ISINST(pl->line, "lddsr") || ISINST(pl->line, "ldisr") || + ISINST(pl->line, "lsdr") || ISINST(pl->line, "lsir") || + ISINST(pl->line, "lsddr") || ISINST(pl->line, "lsidr"))) + return(2); + + if(IS_R3KA && (ISINST(pl->line, "uma") || ISINST(pl->line, "ums"))) + return(2); + + if(IS_RAB && ISINST(pl->line, "bool")) + return(!STRNCASECMP(op1start, "hl", 2) ? 1 : 2); + + if(IS_EZ80_Z80 && (ISINST(pl->line, "lea") || ISINST(pl->line, "pea"))) + return(3); + + if(ISINST(pl->line, ".db") || ISINST(pl->line, ".byte")) + { + int i, j; + for(i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++); + return(i); + } + + if(ISINST(pl->line, ".dw") || ISINST(pl->line, ".word")) + { + int i, j; + for(i = 1, j = 0; pl->line[j]; i += pl->line[j] == ',', j++); + return(i * 2); + } + + /* If the instruction is unrecognized, we shouldn't try to optimize. */ + /* For all we know it might be some .ds or similar possibly long line */ + /* Return a large value to discourage optimization. */ + if (pl->ic) + werrorfl(pl->ic->filename, pl->ic->lineno, W_UNRECOGNIZED_ASM, __func__, 999, pl->line); + else + werrorfl("unknown", 0, W_UNRECOGNIZED_ASM, __func__, 999, pl->line); + return(999); +} + +bool z80symmParmStack (void) +{ + return z80_symmParm_in_calls_from_current_function; +} + diff --git a/src/z80/peep.h b/src/z80/peep.h new file mode 100644 index 0000000..be14860 --- /dev/null +++ b/src/z80/peep.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + peep.h - header file for peephole optimizer helper functions + + Written By - Philipp Klaus Krause + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +bool z80notUsed(const char *what, lineNode *endPl, lineNode *head); +bool z80notUsedFrom(const char *what, const char *label, lineNode *head); +bool z80canAssign (const char *dst, const char *src, const char *exotic); +bool z80symmParmStack (void); +int z80instructionSize(lineNode *node); + diff --git a/src/z80/peeph-gbz80.def b/src/z80/peeph-gbz80.def new file mode 100644 index 0000000..6116cc6 --- /dev/null +++ b/src/z80/peeph-gbz80.def @@ -0,0 +1,823 @@ +// Due to major changes in the Z80 peepholes the old peepholes from peeph.def have been moved here. +// All new peepholes went into peeph-z80.def. A GBZ80 expert should look into this files and peeph-z80.def +// And move peepholes that are useful and correct for both Z80 and GBZ80 into peeph.def. + +// peeph.def - Common Z80 and gbz80 peephole rules +// +// +// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2007 +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +replace restart { + ld %1, %1 +} by { + ; peephole 1 removed redundant load. +} if notVolatile(%1) + +replace restart { + ldhl sp,#%1 + dec hl +} by { + ; peephole 2 combined ld and dec. + ldhl sp,#(%1 - 1) +} + +replace restart { + jp NC,%1 + jp %2 +%1: +} by { + jp C,%2 + ; peephole 3 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp C,%1 + jp %2 +%1: +} by { + jp NC,%2 + ; peephole 4 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp NZ,%1 + jp %2 +%1: +} by { + jp Z,%2 + ; peephole 5 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp Z,%1 + jp %2 +%1: +} by { + jp NZ,%2 + ; peephole 6 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp %5 +} by { + jp %6 + ; peephole 7 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1,%5 +} by { + jp %1,%6 + ; peephole 8 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1 +%2: +%1: +} by { + ; peephole 9 eliminated jump. +%2: +%1: +} if labelRefCountChange(%1 -1) + +replace { + ld e,#0x%1 + ld d,#0x%2 +} by { + ld de,#0x%2%1 + ; peephole 10 combined constant loads into register pair. +} + +replace { + ld l,#0x%1 + ld h,#0x%2 +} by { + ld hl,#0x%2%1 + ; peephole 11 combined constant loads into register pair. +} + +replace { + ld c,#0x%1 + ld b,#0x%2 +} by { + ld bc,#0x%2%1 + ; peephole 12 combined constant loads into register pair. +} + +replace restart { + ld %1,a + ld a,%1 +} by { + ld %1,a + ; peephole 13 removed redundant load from %1 into a. +} if notVolatile(%1), notSame(%1 '(hl+)'), notSame(%1 '(hl-)') + +replace restart { + ld a,%1 + ld %1,a +} by { + ld a,%1 + ; peephole 14 removed redundant load from a into %1. +} if notVolatile(%1), notSame(%1 '(hl+)'), notSame(%1 '(hl-)') + +replace restart { + and a,%1 + or a,a +} by { + and a,%1 + ; peephole 15 removed redundant or after and. +} + +replace restart { + xor a,%1 + or a,a +} by { + xor a,%1 + ; peephole 16 removed redundant or after xor. +} + +replace restart { + ld %1,a + and a,%2 + ld %1,a +} by { + ; peephole 17 removed redundant load into %1. + and a,%2 + ld %1,a +} if notVolatile(%1) + +replace { + ld %1,%2 + ld a,%2 +} by { + ld a,%2 + ld %1,a + ; peephole 18 load value in a first and use it next +} if notVolatile(%1 %2) + +replace restart { + ld %1,%2 + ld %3,%4 + ld %2,%1 + ld %4,%3 +} by { + ld %1,%2 + ld %3,%4 + ; peephole 19 removed redundant load from %3%1 into %4%2 +} if notVolatile(%1 %2 %3 %4) + +replace restart { + ld a,%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + ld a,%1 + cp a,%2 + jp %3,%4 + ; peephole 20 removed load by replacing sub with cp + assert a=%1 +} if notVolatile(%1) + +replace restart { + assert a=%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + cp a,#%2 + jp %3,%4 + ; peephole 21 removed load by replacing sub with cp + assert a=%1 +} + +replace restart { + assert a=%1 +} by { +} + +replace restart { + rlca + ld a,#0x00 + rla +} by { + rlca + and a,#0x01 + ; peephole 22 replaced zero load, rla by and since rlca writes the same value to carry bit and least significant bit. +} + +replace restart { + add a,#0x00 + ld %2,a + ld a,%3 + adc a,%4 +} by { + ; peephole 23 removed lower part of multibyte addition. + ld %2,a + ld a,%3 + add a,%4 +} + +replace restart { + or a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + or a,%1 + jp NZ,%2 + ld %3,a + ; peephole 24 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + and a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + and a,%1 + jp NZ,%2 + ld %3,a + ; peephole 25 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + ld bc,#%1 + %2 + inc bc + inc bc + inc bc +} by { + ld bc,#%1 + %2 + 3 + ; peephole 26 moved triple increment of bc to constant. +} + +replace restart { + ld bc,#%1 + %2 + inc bc + inc bc +} by { + ld bc,#%1 + %2 + 2 + ; peephole 27 moved double increment of bc to constant. +} + +replace restart { + ld bc,#%1 + %2 + inc bc +} by { + ld bc,#%1 + %2 + 1 + ; peephole 28 moved increment of bc to constant. +} + +replace restart { + ld bc,#%1 + ld a,c + add a,#0x%2 + ld c,a + ld a,b + adc a,#0x%3 + ld b,a +} by { + ld bc,#%1 + 0x%3%2 + ; peephole 29 moved addition of constant 0x%3%2 to bc to constant. +} + +replace restart { + ld bc,#%1 + %4 + ld a,c + add a,#0x%2 + ld c,a + ld a,b + adc a,#0x%3 + ld b,a +} by { + ld bc,#%1 + %4 + 0x%3%2 + ; peephole 30 moved addition of constant 0x%3%2 to bc to constant. +} + +replace restart { + call %1 + ret +} by { + jp %1 + ; peephole 31 replaced call at end of function by jump. +} + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp NZ, %1 +%2: + ld a, #%4 + ; peephole 168z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp Z, %1 +%2: + ld a, #%4 + ; peephole 168nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp NZ, %1 + ; peephole 169xz used double assignment in case of Z condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp Z, %1 + ; peephole 169xnz used double assignment in case of NZ condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp NZ, %1 +%2: + ld c, #%4 + ; peephole 170z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp Z, %1 +%2: + ld c, #%4 + ; peephole 170nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp NZ, %1 +%2: + ld e, #%4 + ; peephole 171z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp Z, %1 +%2: + ld e, #%4 + ; peephole 171nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp NZ, %1 +%2: + ld l, #%4 + ; peephole 172z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp Z, %1 +%2: + ld l, #%4 + ; peephole 172nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +barrier + +// Should be one of the last ones. Opens the code to further peephole optimization. +replace restart { +%1: +} by { + ; peephole 32 removed unused label %1. +} if labelRefCount(%1 0) + +// Ensure that all rules above see only jp, not jr. +barrier + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 150-3 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 150-3' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 150-4 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 150-4' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 150-5 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 150-5' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 150-6 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 150-6' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 150-7 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 150-7' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +barrier + +// Do all jump optimizations before replacing by ret. + +replace restart { + jp %5 +} by { + ret + ; peephole 151 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + jp %1,%5 +} by { + ret %1 + ; peephole 152 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace { + jp %5 +} by { + jr %5 + ; peephole 153 changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace { + jp %1,%5 +} by { + jr %1,%5 + ; peephole 154 changed absolute to relative conditional jump. +} if labelInRange(%5) + +barrier + +// Do not try further peephole optimization after these, as other peepholes e.g. might try to generate add a, (hl-), etc otherwise. + +replace { + ld a, (hl) + inc hl +} by { + ld a, (hl+) + ; peephole GB1 used ldi to increment hl after load +} + +replace { + ld a, (hl) + dec hl +} by { + ld a, (hl-) + ; peephole GB2 used ldd to decrement hl after load +} + +replace { + ld (hl), a + inc hl +} by { + ld (hl+), a + ; peephole GB3 used ldi to increment hl after load +} + +replace { + ld (hl), a + dec hl +} by { + ld (hl-), a + ; peephole GB4 used ldd to decrement hl after load +} + +replace { + inc hl + dec hl +} by { + ; peephole GB5 removed inc hl / dec hl pair +} + +replace { + ld (hl), a + inc de + ld a, (de) + inc hl +} by { + ld (hl+), a + ; peephole GB6 used ldi to increment hl + inc de + ld a, (de) +} + +replace { + ld a, (hl) + ld (bc), a + inc bc + inc hl +} by { + ld a, (hl+) + ; peephole GB7 used ldi to increment hl + ld (bc), a + inc bc +} + diff --git a/src/z80/peeph-r2k.def b/src/z80/peeph-r2k.def new file mode 100644 index 0000000..f043548 --- /dev/null +++ b/src/z80/peeph-r2k.def @@ -0,0 +1,2355 @@ +// peeph-r2k.def - Rabbit 2000 (derived from Z80 peephole rules) +// +// Some of these peepholes could be potentially moved to peeph.def, but a +// GBZ80 expert should have a look at them before. +// +// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2015 +// *** modified by Leland Morrison for rabbit 2000 processor 2011 +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +replace restart { + ld %1, %1 +} by { + ; peephole 0 removed redundant load from %1 into %1. +} if notVolatile(%1) + +replace restart { + ld %1, %2 +} by { + ; peephole 1 removed dead load from %2 into %1. +} if notVolatile(%1), notUsed(%1), notVolatile(%2) + +replace restart { + add ix,sp +} by { + ; peephole 2 removed dead frame pointer setup. +} if notUsed('ix') + +replace restart { + ld %1, %2 + %3 +} by { + ; peephole 3 removed dead load from %2 + %3 into %1. +} if notVolatile(%1), notUsed(%1) +// Should probably check for notVolatile(%2), too, but gives many false positives and no regression tests fail. + +replace restart { + ld %1, (iy) +} by { + ld %1, 0 (iy) + ; peephole 4 made 0 offset explicit. +} + +replace restart { + ld (iy), %1 +} by { + ld 0 (iy), %1 + ; peephole 5 made 0 offset explicit. +} + +replace restart { + inc hl +} by { + ; peephole 5a removed dead increment of hl. +} if notUsed('hl') + +replace restart { + dec hl +} by { + ; peephole 6 removed dead decrement of hl. +} if notUsed('hl') + +replace restart { + ld %1, %2 (iy) +} by { + ; peephole 7 removed dead load from %2 (iy) into %1. +} if notUsed(%1) +// Should probably check for notVolatile(), but gives many false positives and no regression tests fail. + +replace restart { + ld %1, %2 (ix) +} by { + ; peephole 8 removed dead load from %2 (ix) into %1. +} if notUsed(%1) + +replace restart { + ld %1, %2 + ld %3, %1 +} by { + ; peephole 9 loaded %3 from %2 directly instead of going through %1. + ld %3, %2 +} if canAssign(%3 %2), notVolatile(%1), notUsed(%1) + +replace restart { + ld %1, hl + ld hl, %1 +} by { + ; peephole 9a reused value still in hl. + ld %1, hl +} if notVolatile(%1) + +replace restart { + ld %1, %2 + ld %3, %4 + ld %5, %1 +} by { + ld %5, %2 + ; peephole 10 loaded %5 from %2 directly instead of going through %1. + ld %3, %4 +} if canAssign(%5 %2), notVolatile(%1), operandsNotRelated(%1 %4), operandsNotRelated(%1 %3), operandsNotRelated(%3 %5), operandsNotRelated(%4 %5), notUsed(%1), notSame(%3 %4 '(hl)' '(de)' '(bc)'), notVolatile(%5) +// Rule OK unless both %5 and %4 are volatile, but we can't express that directly. + +replace restart { + ld %1, %2 (%3) + ld %4, %1 +} by { + ; peephole 11 loaded %2 (%3) into %4 directly instead of going through %1. + ld %4, %2 (%3) +} if canAssign(%4 %2 %3), notVolatile(%1), notUsed(%1) + +replace restart { + ld %1, %2 + ld %3 (%4), %1 +} by { + ; peephole 12 loaded %2 into %3 (%4) directly instead of going through %1. + ld %3 (%4), %2 +} if canAssign(%3 %4 %2), notVolatile(%1), notUsed(%1), notSame(%1 'hl') + +replace restart { + ld %1, %2 (%3) + ld %4, %5 (%6) + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 13 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 (%6) +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), notSame(%1 %4), notSame(%7 %4) + +replace restart { + ld %1, %7 + ld %5 (%6), %4 + ld %2 (%3), %1 +} by { + ld %5 (%6), %4 + ; peephole 14 loaded %7 into %2 (%3) directly instead of going through %1. + ld %2 (%3), %7 +} if canAssign(%2 %3 %7), notVolatile(%1), notUsed(%1), notSame(%1 %4 %1 'hl') + +replace restart { + ld %1, %2 (%3) + ld %4, %5 + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 15 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), operandsNotRelated(%1 %5), operandsNotRelated(%7 %4), operandsNotRelated(%7 %5), notSame(%4 '(hl)' '(de)' '(bc)'), notSame(%5 '(hl)' '(de)' '(bc)' '(iy)') + +replace restart { + ld %1,#%2 + ld a,%3 (%1) +} by { + ; peephole 16 loaded %2 into a directly instead of going through %1. + ld a,(#%2 + %3) +} if notUsed(%1) + +replace restart { + ld hl,#%1 + ld a,(hl) +} by { + ld a,(#%1) + ; peephole 17 loaded a from (#%1) directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld a,(hl) +} by { + ; peephole 18 loaded %2 into a directly instead of using hl. + ld a,(#%1 + %2) +} if notUsed('hl') + +replace restart { + ld hl,#%1 + ld (hl),a +} by { + ld (#%1),a + ; peephole 19 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld (hl),a +} by { + ld (#%1 + %2),a + ; peephole 20 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + srl %1 + ld a,%1 +} by { + ld a,%1 + ; peephole 21 shifted in a instead of %1. + srl a +} if notVolatile(%1), notUsed(%1) + +replace restart { + ld e, l + ld d, h + ld a, (de) + srl a + ld (de), a +} by { + ld e, l + ld d, h + srl (hl) + ; peephole 21a shifted in (hl) instead of a. +} if notUsed('a') + +replace restart { + ld iy, #%1 + ld %2, %3 (iy) + srl %2 + bit %4, %3 (iy) +} by { + ld hl, #%1 + %3 + ; peephole 21c used hl instead of iy. + ld %2, (hl) + srl %2 + bit %4, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld %1,(hl) + ld a,%2 (%3) + sub a,%1 +} by { + ld a,%2 (%3) + ; peephole 22 used (hl) in sub directly instead of going through %1. + sub a,(hl) +} if notVolatile(%1), notUsed(%1) + +replace restart { + inc bc + ld l,c + ld h,b +} by { + ld l,c + ld h,b + ; peephole 23 incremented in hl instead of bc. + inc hl +} if notUsed('bc') + +replace restart { + inc de + ld l,e + ld h,d +} by { + ld l,e + ld h,d + ; peephole 24 incremented in hl instead of de. + inc hl +} if notUsed('de') + +replace restart { + ld c,l + ld b,h + ld a,#%1 + ld (bc),a +} by { + ld c,l + ld b,h + ld (hl),#%1 + ; peephole 25 loaded #%1 into (hl) instead of (bc). +} if notUsed('a') + +replace restart { + ex de, hl + push de +} by { + ; peephole 26 pushed hl directly instead of going through de. + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ld l,%1 + ld h,d + push hl +} by { + ; peephole 27 pushed de instead of hl removing a load. + ld e,%1 + push de +} if notUsed('hl'), notUsed('e') + +replace restart { + ex de, hl + push bc + push de +} by { + ; peephole 28 pushed hl directly instead of going through de. + push bc + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ld l,c + ld h,b + push hl +} by { + ; peephole 29 pushed bc directly instead of going through hl. + push bc +} if notUsed('hl') + +replace restart { + ld l,%1 + ld h,b + push hl +} by { + ; peephole 30 pushed bc instead of hl removing a load. + ld c,%1 + push bc +} if notUsed('hl'), notUsed('c') + +replace restart { + ld c,l + ld b,h + push %1 + push bc +} by { + ; peephole 31 pushed hl directly instead of going through bc. + push %1 + push hl +} if notUsed('bc'), notSame(%1 'bc') + +replace restart { + pop de + ld l, e + ld h, d +} by { + ; peephole 32 popped hl directly instead of going through de. + pop hl +} if notUsed('de') + +replace restart { + pop bc + ld l, c + ld h, b +} by { + ; peephole 33 popped hl directly instead of going through bc. + pop hl +} if notUsed('bc') + +replace restart { + ld %1 (ix), %2 + ld %3, %1 (ix) +} by { + ; peephole 34 loaded %3 from %2 instead of going through %1 (ix). + ld %1 (ix), %2 + ld %3, %2 +} if canAssign(%3 %2) +// Don't need to check for volatile, since ix is used for the stack. + +replace restart { + ld %1 (ix), a + push de + ld %2, %1 (ix) +} by { + ld %1 (ix), a + push de + ; peephole 34a loaded %2 from a instead of %1 (ix) + ld %2, a +} if canAssign(%2 'a') + +replace restart { + push af + inc sp + ld a,e + push af + inc sp +} by { + ; peephole 35 pushed de instead of pushing a twice. + ld d,a + push de +} if notUsed('d'), notUsed('a') + +replace restart { + push af + inc sp + ld a,#%1 + push af + inc sp +} by { + ; peephole 36 pushed de instead of pushing a twice. + ld d,a + ld e,#%1 + push de +} if notUsed('de') + +replace restart { + push af + inc sp + ld a,#%1 + push af + inc sp +} by { + ; peephole 37 pushed bc instead of pushing a twice. + ld b,a + ld c,#%1 + push bc +} if notUsed('bc') + +replace restart { + push bc + inc sp + push de + inc sp +} by { + ld c, d + ; peephole 37a combined pushing of b and d. + push bc +} if notUsed('c') + +replace restart { + push bc + inc sp + ld a, c + push af + inc sp +} by { + push bc + ld a, c + ; peephole 38 simplified pushing bc. +} + +replace restart { + push de + inc sp + ld a, #%1 + push af + inc sp +} by { + ld e, #%1 + push de + ; peephole 39 simplified pushing de. +} if notUsed('e') + +replace restart { + ld a,#%1 + ld d,a +} by { + ; peephole 40 loaded #%1 into d directly instead of going through a. + ld d,#%1 +} if notUsed('a') + +replace restart { + ld %1,a + ld %2,%1 +} by { + ; peephole 41 loaded %2 from a directly instead of going through %1. + ld %2,a +} if notUsed(%1) + +replace restart { + ld a,%1 (ix) + push af + inc sp + ld a,%2 (ix) + push af + inc sp +} by { + ; peephole 42 pushed %1 (ix), %2(ix) through hl instead of af. + ld h,%1 (ix) + ld l,%2 (ix) + push hl +} if notUsed('a'), notUsed('hl') + +replace restart { + ld c, l + ld b, h + push bc +} by { + ; peephole 43 pushed hl instead of bc. + push hl +} if notUsed('bc') + +replace restart { + pop %1 + push %1 +} by { + ; peephole 44 eleminated dead pop/push pair. +} if notUsed(%1) + +replace restart { + ld iy,#%1 + or a,%2 (iy) +} by { + ; peephole 45 used hl instead of iy. + ld hl,#%1 + %2 + or a,(hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld %2,%3 (iy) +} by { + ; peephole 46 used hl instead of iy. + ld hl,#%1 + %3 + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld h,%3 (iy) +} by { + ; peephole 46a used hl instead of iy. + ld hl,#%1 + %3 + ld h, (hl) +} if notUsed('iy'), notUsed('l') + +replace restart { + ld iy,#%1 + ld %2 (iy), %3 +} by { + ; peephole 46b used hl instead of iy. + ld hl,#%1 + %2 + ld (hl), %3 +} if notUsed('iy'), notUsed('hl'), notSame(%3 'h' 'l') + +replace restart { + ld iy,#%1 + ld %2,0 (iy) + ld %3,1 (iy) +} by { + ; peephole 47 used hl instead of iy. + ld hl,#%1 + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%2 'hl') + +replace restart { + ld iy,#%1 + ld %2 (iy),%3 + ld l,%2 (iy) +} by { + ; peephole 48 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl),%3 + ld l,(hl) +} if notUsed('iy'), notUsed('h') + +replace restart { + ld iy,#%1 + ld %2 (%3), %4 +} by { + ; peephole 49 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl), %4 +} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%4 'hl') + +replace restart { + ld iy,#%1 + bit %2,%3 (iy) +} by { + ; peephole 49a used hl instead of iy. + ld hl,#%1+%3 + bit %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, %3 (iy) +} by { + ; peephole 49b used hl instead of iy. + ld hl, #%1+%3 + add hl, sp + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, 0 (iy) + ld %3, 1 (iy) +} by { + ; peephole 49c used hl instead of iy. + ld hl, #%1 + add hl, sp + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld l, 0 (iy) + ld h, 1 (iy) +} by { + ; peephole 49d used hl instead of iy. + ld hl, #%1 + add hl, sp + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +} if notUsed('iy'), notUsed('a') + +replace restart { + ld iy, #%1 + add iy, sp + ld 0 (iy), #%2 + ld 1 (iy), #%3 +} by { + ; peephole 49e used hl instead of iy. + ld hl, #%1 + add hl, sp + ld (hl), #%2 + inc hl + ld (hl), #%3 +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + ld a, 1 (iy) + or a, 0 (iy) +} by { + ld hl, #%1 + ld a, (hl) + inc hl + or a, (hl) + ; peephole 49f used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld a, 1 (iy) + or a, 0 (iy) +} by { + ld hl, #%1 + add hl, sp + ld a, (hl) + inc hl + or a, (hl) + ; peephole 49f' used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + bit %2, %3 (iy) +} by { + ld hl, #%1+%3 + add hl, sp + bit %2, (hl) + ; peephole 49g used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + or a, %2 (iy) +} by { + ld hl, #%1+%2 + add hl, sp + or a, (hl) + ; peephole 49h used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld %1,(hl) + or a,%1 +} by { + or a,(hl) + ; peephole 50 used (hl) directly instead of going through %1. +} if notUsed(%1), notSame(%1 'a') + +replace restart { + ld c,l + ld b,h + inc bc +} by { + ; peephole 51 incremented in hl instead of bc. + inc hl + ld c,l + ld b,h +} if notUsed('hl') + +replace restart { + ld a,%1 (%2) + bit %3,a +} by { + ; peephole 52 tested bit of %1 (%2) directly instead of going through a. + bit %3,%1 (%2) +} if notUsed('a') + +replace restart { + ld a,%1 + bit %2,a +} by { + ; peephole 53 tested bit %2 of %1 directly instead of going through a. + bit %2,%1 +} if notUsed('a'), canAssign(%1 'b') + +replace restart { + ld a, %1 + set %2, a + ld %1, a +} by { + ; peephole 54 set bit %2 of %1 directly instead of going through a. + set %2, %1 + ld a, %1 +} if canAssign(%1 'b') +// canAssign(%1 'b') is true, iff set b, %1 is possible. + +replace restart { + ld a, %1 (%2) + set %3, a + ld %1 (%2), a +} by { + ; peephole 55 set bit %3 of %1 (%2) directly instead of going through a. + set %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + ld a, %1 + res %2, a + ld %1, a +} by { + ; peephole 56 reset bit %2 of %1 directly instead of going through a. + res %2, %1 + ld a, %1 +} if canAssign(%1 'b') +// canAssign(%1 'b') is true, iff set b, %1 is possible. + +replace restart { + ld a, %1 (%2) + res %3, a + ld %1 (%2), a +} by { + ; peephole 57 reset bit %3 of %1 (%2) directly instead of going through a. + res %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + ld c, %1 (%2) + ld b, %3 (%4) + ld l,c + ld h,b +} by { + ; peephole 58 stored %1 (%2) %3 (%4) into hl directly instead of going through bc. + ld l, %1 (%2) + ld h, %3 (%4) +} if notUsed('bc') + +replace restart { + ld c, %1 + ld b, %2 + ld l,c + ld h,b +} by { + ; peephole 59 stored %2%1 into hl directly instead of going through bc. + ld l, %1 + ld h, %2 +} if notUsed('bc'), operandsNotRelated(%2 'l') + +replace restart { + jp NC,%1 + jp %2 +%1: +} by { + jp C,%2 + ; peephole 60 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp C,%1 + jp %2 +%1: +} by { + jp NC,%2 + ; peephole 61 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp NZ,%1 + jp %2 +%1: +} by { + jp Z,%2 + ; peephole 62 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp Z,%1 + jp %2 +%1: +} by { + jp NZ,%2 + ; peephole 63 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp %5 +} by { + jp %6 + ; peephole 64 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1,%5 +} by { + jp %1,%6 + ; peephole 65 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1 +%2: +%1: +} by { + ; peephole 65a eliminated jump. +%2: +%1: +} if labelRefCountChange(%1 -1) + +// A peephole that makes the code longer. Let's hope it's worth it in speed gain and further optimization potential. +replace restart { + ld a,#0x00 +%1: + bit %2,a + jp Z,%3 +} by { + ld a,#0x00 + jp %3 + ; peephole 65a jumped directly to %3 instead of testing a first. +%1: + bit %2,a + jp Z,%3 +} if labelRefCountChange(%3 +1) + +replace restart { + ld %1, %2 + jp %3 + jp %4 +} by { + ld %1, %2 + jp %3 + ; peephole 65b removed unreachable jump to %3. +} + +replace restart { + ld %1, %2 + jp %3 +%3: +} by { + ld %1, %2 +%3: + ; peephole 65c removed redundant jump to %3. +} if labelRefCountChange(%3 -1) + +replace restart { + ld %1, #0x01 + bit 0, %1 + jp Z, %2 +} by { + ld %1, #0x01 + ; peephole 65d removed impossible jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + xor a,a + ld a,#0x00 +} by { + xor a,a + ; peephole 66 removed redundant load of 0 into a. +} + +replace { + ld e,#0x%1 + ld d,#0x%2 +} by { + ld de,#0x%2%1 + ; peephole 67 combined constant loads into register pair. +} + +replace { + ld l,#0x%1 + ld h,#0x%2 +} by { + ld hl,#0x%2%1 + ; peephole 68 combined constant loads into register pair. +} + +replace { + ld c,#0x%1 + ld b,#0x%2 +} by { + ld bc,#0x%2%1 + ; peephole 69 combined constant loads into register pair. +} + +replace restart { + ld %1,a + ld a,%1 +} by { + ld %1,a + ; peephole 70 removed redundant load from %1 into a. +} if notVolatile(%1) +// This gives many false negatives and without the test no problems are encountered in the regression tests +// Maybe we can try this after 2.7.0 release + +replace restart { + ld a,%1 + ld %1,a +} by { + ld a,%1 + ; peephole 71 removed redundant load from a into %1. +} if notVolatile(%1) +// This gives many false negatives and without the test no problems are encountered in the regression tests +// Maybe we can try this after 2.7.0 release + +replace restart { + ld %2 (ix), %1 + ld %1, %2 (ix) +} by { + ld %2 (ix), %1 + ; peephole 71a removed redundant load of %1 from %2 (ix) +} + +replace restart { + ld %1,a + ld a,%2 + or a,%1 +} by { + ld %1,a + or a,%2 + ; peephole 72 removed load by reordering or arguments. +} if notVolatile(%1), canAssign('b' %2) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + or a,%1 + or a,a +} by { + or a,%1 + ; peephole 73 removed redundant or after or. +} + +replace restart { + or a,%1 (%2) + or a,a +} by { + or a,%1 (%2) + ; peephole 74 removed redundant or after or. +} + +replace restart { + and a,%1 + or a,a +} by { + and a,%1 + ; peephole 75 removed redundant or after and. +} + +replace restart { + xor a,%1 + or a,a +} by { + xor a,%1 + ; peephole 76 removed redundant or after xor. +} + +replace restart { + xor a,%1 (%2) + or a,a +} by { + xor a,%1 (%2) + ; peephole 77 removed redundant or after xor. +} + +replace { + ld %1,%2 + ld a,%2 +} by { + ld a,%2 + ld %1,a + ; peephole 78 load value in a first and use it next +} if notVolatile(%1 %2) + +replace restart { + ld %1,%2 + ld %3,%4 + ld %2,%1 + ld %4,%3 +} by { + ld %1,%2 + ld %3,%4 + ; peephole 79 removed redundant load from %3%1 into %4%2 +} if notVolatile(%1 %2 %3 %4) + +replace restart { + push de + inc sp + ld a,e + push af + inc sp +} by { + push de + ; peephole 80 pushed de +} if notUsed('a') + +replace restart { + ld iy,%1 + add iy,sp + ld sp,iy +} by { + ld hl,%1 + add hl,sp + ld sp,hl + ; peephole 81 fixed stack using hl instead of iy. +} if notUsed('hl'), notUsed('iy') + +replace restart { + ld a,%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + ld a,%1 + cp a,%2 + jp %3,%4 + ; peephole 82 removed load by replacing sub with cp + assert a=%1 +} if notVolatile(%1), notUsedFrom(%4 'a') + +replace restart { + assert a=%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + cp a,%2 + jp %3,%4 + ; peephole 83 removed load by replacing sub with cp + assert a=%1 +} if notUsedFrom(%4 'a') + +replace restart { + assert a=%1 +} by { +} + +replace restart { + sub a,#0xFF + jp Z,%1 +} by { + inc a + ; peephole 84 replaced sub a,#0xFF by inc a. + jp Z,%1 +} + +replace restart { + sub a,#0xFF + jp NZ,%1 +} by { + inc a + ; peephole 85 replaced sub a,#0xFF by inc a. + jp NZ,%1 +} + +replace restart { + rlca + ld a,#0x00 + rla +} by { + rlca + and a,#0x01 + ; peephole 86 replaced zero load, rla by and since rlca writes the same value to carry bit and least significant bit. +} + +replace restart { + ld %1,%2 + push %1 + pop %4 + ld %1,%3 +} by { + ld %4,%2 + ; peephole 87 moved %2 directly into de instead of going through %1. + ld %1,%3 +} + +replace restart { + add a,#0x00 + ld %2,a + ld a,%3 + adc a,%4 +} by { + ; peephole 88 removed lower part of multibyte addition. + ld %2,a + ld a,%3 + add a,%4 +} + +replace restart { + ld a, l + add a, #0x%1 + ld e, a + ld a, h + adc a, #0x%2 + ld d, a +} by { + ld de, #0x%2%1 + add hl, de + ; peephole 89 used 16-bit addition. + ld e, l + ld d, h + ld a, h +} if notUsed('hl') + +replace restart { + ld a, l + add a, #0x%1 + ld c, a + ld a, h + adc a, #0x%2 + ld b, a +} by { + ld bc, #0x%2%1 + add hl,bc + ; peephole 90 used 16-bit addition. + ld c, l + ld b, h + ld a, h +} if notUsed('hl') + +replace restart { + ld %1,a + ld a,%2 + add a,%1 +} by { + ; peephole 91 removed loads by exploiting commutativity of addition. + add a,%2 +} if notVolatile(%1), notUsed(%1), canAssign('b' %2) +// canAssign('b' %2) is true, iff add a,%2 is possible. + +replace restart { + ld %1 (ix),a + ld a,#%2 + add a,%1 (ix) +} by { + ld %1 (ix),a + ; peephole 92 removed loads by exploiting commutativity of addition. + add a,#%2 +} +// Don't need to check for volatile, since ix is used to access the stack. + +replace restart { + ld l,%1 (ix) + ld h,%2 (ix) + ld a,(hl) + inc a + ld l,%1 (ix) + ld h,%2 (ix) + ld (hl),a +} by { + ld l,%1 (ix) + ld h,%2 (ix) + inc (hl) + ; peephole 93 incremented in (hl) instead of going through a. +} if notUsed('a') + +replace restart { + ld %1, %2 (%3) + inc %1 + ld %2 (%3), %1 +} by { + inc %2 (%3) + ld %1, %2 (%3) + ; peephole 93a incremented in %2 (%3) instead of going through %1. +} if notSame(%3 'sp') + +replace restart { + ld %1,a + ld a,%2 + add a,%1 +} by { + ld %1, a + ; peephole 94 removed load by exploiting commutativity of addition. + add a,%2 +} if notSame(%2 '(bc)' '(de)'), canAssign('b' %2) +// canAssign('b' %2) is true, iff add a,%2 is possible. + +replace restart { + ld c,l + ld b,h + ld hl,#%1 + add hl,bc +} by { + ; peephole 95 removed loads by exploiting commutativity of addition. + ld bc,#%1 + add hl,bc +} if notUsed('bc') + +replace restart { + ld hl,#%1 + add hl,%2 + ld bc,#%4 + add hl,bc +} by { + ; peephole 96 removed loads by exploiting commutativity of addition. + ld hl,#%1 + %4 + add hl,%2 +} if notUsed('bc') + +replace restart { + ld c,e + ld b,d + ld hl,#%1 + add hl,bc +} by { + ; peephole 97 removed loads by exploiting commutativity of addition. + ld hl,#%1 + add hl,de +} if notUsed('bc') + +replace restart { + or a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + or a,%1 + jp NZ,%2 + ld %3,a + ; peephole 98 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + and a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + and a,%1 + jp NZ,%2 + ld %3,a + ; peephole 99 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + sub a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + sub a,%1 + jp NZ,%2 + ld %3,a + ; peephole 100 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + inc a + jp NZ,%1 + ld %2,#0x00 +} by { + inc a + jp NZ,%1 + ld %2,a + ; peephole 101 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + dec a + jp NZ,%1 + ld %2,#0x00 +} by { + dec a + jp NZ,%1 + ld %2,a + ; peephole 102 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + or a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + or a,%1 + jp NZ,%2 + or a,%3 + ; peephole 103 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + sub a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + sub a,%1 + jp NZ,%2 + or a,%3 + ; peephole 104 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + or a,%1 + jp NZ,%2 + push %3 + ld %4,#0x00 +} by { + or a,%1 + jp NZ,%2 + push %3 + ld %4,a + ; peephole 105 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + ld hl,#%1 + add hl,%2 + inc hl +} by { + ld hl,#%1+1 + add hl,%2 + ; peephole 107 moved increment of hl to constant. +} + +replace restart { + inc hl + ld %1,#%2 + add hl,%1 +} by { + ld %1,#%2+1 + add hl,%1 + ; peephole 108 moved increment of hl to constant. +} if notUsed(%1) + +replace restart { + dec hl + ld %1,#%2 + add hl,%1 +} by { + ld %1,#%2-1 + add hl,%1 + ; peephole 109 moved decrement of hl to constant. +} if notUsed(%1) + +replace restart { + inc hl + inc hl + inc hl + ld hl, (hl) +} by { + ; peephole 109a moved increment of hl to offset. + ld hl, 3 (hl) +} + +replace restart { + inc hl + inc hl + ld hl, (hl) +} by { + ; peephole 109b moved increment of hl to offset. + ld hl, 2 (hl) +} + +replace restart { + inc hl + ld hl, (hl) +} by { + ; peephole 109c moved increment of hl to offset. + ld hl, 1 (hl) +} + +replace restart { + inc iy + ld %1, %2 (iy) +} by { + ld %1, %2+1 (iy) + ; peephole 110 moved increment of iy to offset. +} if notUsed('iy') + +replace restart { + push hl + pop iy + pop hl + inc iy +} by { + inc hl + push hl + pop iy + pop hl + ; peephole 111 incremented in hl instead of iy. +} + +replace restart { + push hl + pop iy + inc iy +} by { + inc hl + push hl + pop iy + ; peephole 111a incremented in hl instead of iy. +} if notUsed('hl') + +replace restart { + push bc + pop iy + inc iy +} by { + inc bc + push bc + pop iy + ; peephole 111b incremented in bc instead of iy. +} if notUsed('bc') + +replace restart { + push de + pop iy + inc iy +} by { + inc de + push de + pop iy + ; peephole 111c incremented in de instead of iy. +} if notUsed('de') + +replace restart { + ld hl,%1 + add hl,%2 + push hl + pop iy +} by { + ld iy,%1 + add iy,%2 + ; peephole 111b added in iy instead of hl. +} if notUsed('hl'), notSame(%2 'hl') + +replace restart { + pop af + ld sp,%1 +} by { + ; peephole 112 removed redundant pop af. + ld sp,%1 +} if notUsed('a') + +replace restart { + inc sp + ld sp,%1 +} by { + ; peephole 113 removed redundant inc sp. + ld sp,%1 +} if notUsed('a') + +replace restart { + call %1 + ret +} by { + jp %1 + ; peephole 114 replaced call at end of function by jump (tail call optimization). +} if symmParmStack(%1) + +// Callee saves ix. +replace restart { + call %1 + pop ix + ret +} by { + pop ix + jp %1 + ; peephole115 replaced call at end of function by jump moving call beyond pop ix (tail call optimization). +} if symmParmStack(%1) + +replace restart { + ld %1,#%2 + ld %3,%4 + ld %1,#%2 +} by { + ld %1,#%2 + ld %3,%4 + ; peephole 116 removed load of #%2 into %1 since it's still there. +} if notVolatile(%1), operandsNotRelated(%3 %1) + +replace restart { + ld hl,#%1 + ld de,#%1 +} by { + ; peephole 117 used #%1 from hl for load into de. + ld hl,#%1 + ld e,l + ld d,h +} + +replace restart { + ld %1 (ix),l + ld %2 (ix),h + ld %3,%1 (ix) + ld %4,%2 (ix) +} by { + ld %1 (ix),l + ld %2 (ix),h + ; peephole 118 used hl instead of %2 (ix), %1 (ix) to load %4%3. + ld %3,l + ld %4,h +} if operandsNotRelated('h' %3) +// Don't check for volatile since ix points to the stack. + +replace restart { + ld %1, a + ld a, %2 (%3) + adc a, #%4 + ld %6, %1 +} by { + ld %6, a + ld a, %2 (%3) + adc a, #%4 + ; peephole 119 loaded %6 from a directly instead of going through %1. +} if notUsed(%1) + +replace restart { + ld %1, a + ld a, %2 (%3) + adc a, #%4 + ld %5, a + ld %6, %1 +} by { + ld %6, a + ld a, %2 (%3) + adc a, #%4 + ld %5, a + ; peephole 120 loaded %6 from a directly instead of going through %1. +} if notUsed(%1), notSame(%5 %1), notSame(%5 '(hl)' '(de)' '(bc)'), notSame(%5 %6), notSame(%6 '(hl)' '(de)' '(bc)'), notSame(%5 'a'), notSame(%6 'a') + +replace restart { + ld %1, a + ld a, #%2 + adc a, #%3 + ld %5, a + ld %6, %1 +} by { + ld %6, a + ld a, #%2 + adc a, #%3 + ld %5, a + ; peephole 121 loaded %6 from a directly instead of going through %1. +} if notUsed(%1), notSame(%5 %1), notSame(%5 %6 '(hl)' '(de)' '(bc)'), notSame(%6 'a') + +replace restart { + ld hl, #%1 + add hl, %2 + ex de, hl + ld hl, #%3 + add hl, de +} by { + ld hl, #%1+%3 + add hl, %2 + ; peephole 122 removed addition and loads exploiting commutativity of addition. +} if notUsed('de') + +replace restart { + ld %1,l + ld %2,h + ex de,hl + ld (hl),%1 + inc hl + ld (hl),%2 +} by { + ld %1,l + ex de,hl + ; peephole 122a used de instead of going through %1%2. + ld (hl),e + inc hl + ld (hl),d +} if notUsed(%2), notSame(%1 'l' 'h' 'e' 'd'), notSame(%2 'l' 'h' 'e' 'd') + +replace restart { + ld e, l + ld d, h + ld hl, #0x0001 + add hl, de +} by { + ld e, l + ld d, h + inc hl + ; peephole 123 replaced addition by increment. +} + +replace restart { + ld sp,hl + ld hl,#0x0002 + add hl,sp +} by { + ld sp, hl + inc hl + inc hl + ; peephole 124 replaced addition by increment. +} + +replace restart { + ex de, hl + ld hl, #%1 + add hl, de +} by { + ; peephole 125 removed ex exploiting commutativity of addition. + ld de, #%1 + add hl, de +} if notUsed('de') + +replace restart { + ex de, hl + push bc + ex de, hl +} by { + push bc + ; peephole 126 canceled subsequent ex de, hl. +} + +replace restart { + ld hl, #%1 + add hl, %2 + ex de, hl + inc de +} by { + ld hl, #%1+1 + ; peephole 127 moved increment to constant. + add hl, %2 + ex de, hl +} if notUsed('hl') + +replace restart { + ld a,#0x01 + jp %1 +%2: + xor a,a +%1: + sub a,#0x01 + ld a,#0x00 + rla +} by { + xor a,a + jp %1 +%2: + ld a,#0x01 +%1: + ; peephole 128 removed negation. +} if labelRefCount(%1 1) + +replace restart { + and a,#0x01 + sub a,#0x01 + ld a,#0x00 + rla +} by { + and a,#0x01 + xor a,#0x01 + ; peephole 129 used xor for negation. +} + +replace restart { + or a,a + sub a,#%1 +} by { + ; peephole 130 removed redundant or. + sub a,#%1 +} + +replace restart { + ld a,#0x00 + rla + sub a,#0x01 + ld a,#0x00 + rla +} by { + ld a,#0x00 + ccf + ; peephole 131 moved negation from bit 0 to carry flag. + rla +} + +replace restart { + ld a, #<(%1) + add a, l + ld l, a + ld a, #>(%1) + adc a, h + ld h, a + push bc +} by { + push bc + ld bc, #%1 + add hl, bc + ; peephole 132 used 16 bit addition by moving push bc + ld a, h +} if notUsed('bc') + +replace restart { + pop af + push hl +} by { + ; peephole 133 used ex to move hl onto the stack. + ld 0(sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + ld hl, #%1 + push hl +} by { + ld hl, #%1 + ; peephole 134 used 0(sp) to move hl onto the stack. + ld 0(sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + inc sp + ld hl,#%1 + push hl +} by { + inc sp + ld hl,#%1 + ; peephole 135 used 0(sp) to move #%1 onto the stack. + ld 0(sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + ld a,#%1 + push af + inc sp +} by { + ld h,#%1 + ld 0(sp),hl + ; peephole 136 used 0(sp) to move #%1 onto the stack. + inc sp +} if notUsed('a'), notUsed('hl') + +replace restart { + ld %1,#%2 + ld %3 (%1),a +%4: + ld %1,%5 +} by { + ld (#%2 + %3),a + ; peephole 137 directly used #%2 instead of going through %1 using indirect addressing. +%4: + ld %1,%5 +} + +replace restart { + pop af + ld %1,#%2 + ld %3 (%1),%4 + ld %1,#%5 +} by { + ld a,%4 + ld (#%2 + %3),a + ; peephole 138 used #%2 directly instead of going through %1 using indirect addressing. + pop af + ld %1,#%5 +} if notSame(%3 'a') + +replace restart { + ld %1,a + bit %2,%1 +} by { + bit %2,a + ; peephole 139 tested bit %2 of a directly instead of going through %1. +} if notUsed(%1) + +replace restart { + sbc a,%1 + bit 7,a + jp Z,%2 +} by { + sbc a,%1 + jp P,%2 + ; peephole 140 used sign flag instead of testing bit 7. +} + +replace restart { + sbc a,%1 + bit 7,a + jp NZ,%2 +} by { + sbc a,%1 + jp M,%2 + ; peephole 141 used sign flag instead of testing bit 7. +} + +replace restart { + dec %1 + ld a, %1 + or a, a +} by { + dec %1 + ; peephole 141a removed redundant transfer and flag setting in a +} if notUsed('a') + +replace restart { + ld %1,a + or a,a + jp %3,%4 + ld a,%1 +} by { + ld %1,a + or a,a + jp %3,%4 + ; peephole 142 used value still in a instead of reloading from %1. +} + +replace { + jp %5 + ret +} by { + jp %5 + ; peephole 143 removed unused ret. +} + +replace { + jp %5 + ld sp,ix + pop ix + ret +} by { + jp %5 + ; peephole 144 removed unused ret. +} + +replace restart { + or a,%1 + jp NZ,%2 + xor a,a + jp %3 +} by { + or a,%1 + jp NZ,%2 + ; peephole 145 removed redundant zeroing of a (which has just been tested to be #0x00). + jp %3 +} + +replace restart { + dec hl + inc hl +} by { + ; peephole 145a removed inc hl / dec hl pair. +} + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp NZ, %1 +%2: + ld a, #%4 + ; peephole 168z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp Z, %1 +%2: + ld a, #%4 + ; peephole 168nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp NZ, %1 + ; peephole 169xz used double assignment in case of Z condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp Z, %1 + ; peephole 169xnz used double assignment in case of NZ condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp NZ, %1 +%2: + ld c, #%4 + ; peephole 170z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp Z, %1 +%2: + ld c, #%4 + ; peephole 170nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp NZ, %1 +%2: + ld e, #%4 + ; peephole 171z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp Z, %1 +%2: + ld e, #%4 + ; peephole 171nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp NZ, %1 +%2: + ld l, #%4 + ; peephole 172z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp Z, %1 +%2: + ld l, #%4 + ; peephole 172nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +// These ex-generating rules should be among the last ones since ex counts as a read from both hl and de for notUsed(). +barrier + +replace restart { + ld d,h + ld e,l +} by { + ; peephole 146 used ex to load hl into de. + ex de,hl +} if notUsed('hl') + +replace restart { + ld e,l + ld d,h +} by { + ; peephole 147 used ex to load hl into de. + ex de,hl +} if notUsed('hl') + +replace restart { + ld l,e + ld h,d +} by { + ; peephole 148 used ex to load de into hl. + ex de,hl +} if notUsed('de') + +barrier + +// Should be one of the last ones. Opens the code to further peephole optimization. +replace restart { +%1: +} by { + ; peephole 149 removed unused label %1. +} if labelRefCount(%1 0) + +// Ensure that all rules above see only jp, not jr. +barrier + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 150-3 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 150-3' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 150-4 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 150-4' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 150-5 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 150-5' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 150-6 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 150-6' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 150-7 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 150-7' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +barrier + +// Do all jump optimizations before replacing by ret. + +replace restart { + jp %5 +} by { + ret + ; peephole 151 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + jp %1,%5 +} by { + ret %1 + ; peephole 152 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// Replace jp by ret before replacing jp by jr outside of jump tables, since using ret can reduce the number of references to jump labels. +barrier + +replace { + jp %5 +} by { + jr %5 + ; peephole 153 changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace { + jp Z,%5 +} by { + jr Z,%5 + ; peephole 154 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NZ,%5 +} by { + jr NZ,%5 + ; peephole 155 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp C,%5 +} by { + jr C,%5 + ; peephole 156 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NC,%5 +} by { + jr NC,%5 + ; peephole 157 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + dec b + jr NZ, %5 +} by { + djnz %5 + ; peephole 158 used djnz +} + diff --git a/src/z80/peeph-tlcs90.def b/src/z80/peeph-tlcs90.def new file mode 100644 index 0000000..ce168f1 --- /dev/null +++ b/src/z80/peeph-tlcs90.def @@ -0,0 +1,1518 @@ +// peeph-z80.def - Z80 peephole rules +// +// Some of these peepholes could be potentially moved to peeph.def, but a +// GBZ80 expert should have a look at them before. +// +// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2015 +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +replace restart { + ld %1, %1 +} by { + ; peephole 0 removed redundant load from %1 into %1. +} if notVolatile(%1) + +replace restart { + add ix,sp +} by { + ; peephole 1 removed dead frame pointer setup. +} if notUsed('ix') + +replace restart { + ld %1, %2 + %3 +} by { + ; peephole 2 removed dead load from %2 + %3 into %1. +} if notVolatile(%1), notUsed(%1) +// Should probably check for notVolatile(%2), too, but gives many false positives and no regression tests fail. + +replace restart { + ld %1, (iy) +} by { + ld %1, 0 (iy) + ; peephole 3 made 0 offset explicit. +} + +replace restart { + ld (iy), %1 +} by { + ld 0 (iy), %1 + ; peephole 4 made 0 offset explicit. +} + +replace restart { + dec hl +} by { + ; peephole 5 removed dead decrement of hl. +} if notUsed('hl') + +replace restart { + ld %1, %2 (iy) +} by { + ; peephole 6 removed dead load from %2 (iy) into %1. +} if notUsed(%1) +// Should probably check for notVolatile(), but gives many false positives and no regression tests fail. + +replace restart { + ld %1, %2 (ix) +} by { + ; peephole 7 removed dead load from %2 (ix) into %1. +} if notUsed(%1) + +replace restart { + ld %1, %2 + ld %3, %1 +} by { + ; peephole 8 loaded %3 from %2 directly instead of going through %1. + ld %3, %2 +} if canAssign(%3 %2), notVolatile(%1), notUsed(%1) + +replace restart { + ld %1, %2 + ld %3, %4 + ld %5, %1 +} by { + ld %5, %2 + ; peephole 9 loaded %5 from %2 directly instead of going through %1. + ld %3, %4 +} if canAssign(%5 %2), notVolatile(%1), operandsNotRelated(%1 %4), operandsNotRelated(%1 %3), operandsNotRelated(%3 %5), operandsNotRelated(%4 %5), notUsed(%1), notSame(%3 %4 '(hl)' '(de)' '(bc)'), notVolatile(%5) +// Rule OK unless both %5 and %4 are volatile, but we can't express that directly. + +replace restart { + ld %1, %2 (%3) + ld %4, %1 +} by { + ; peephole 10 loaded %2 (%3) into %4 directly instead of going through %1. + ld %4, %2 (%3) +} if canAssign(%4 %2 %3), notVolatile(%1), notUsed(%1) + +replace restart { + ld %1, %2 + ld %3 (%4), %1 +} by { + ; peephole 11 loaded %2 into %3 (%4) directly instead of going through %1. + ld %3 (%4), %2 +} if canAssign(%3 %4 %2), notVolatile(%1), notUsed(%1), notSame(%1 'hl') + +replace restart { + ld %1, %2 (%3) + ld %4, %5 (%6) + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 12 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 (%6) +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), notSame(%1 %4), notSame(%7 %4) + +replace restart { + ld %1, %7 + ld %5 (%6), %4 + ld %2 (%3), %1 +} by { + ld %5 (%6), %4 + ; peephole 13 loaded %7 into %2 (%3) directly instead of going through %1. + ld %2 (%3), %7 +} if canAssign(%2 %3 %7), notVolatile(%1), notUsed(%1), notSame(%1 %4 %1 'hl') + +replace restart { + ld %1, %2 (%3) + ld %4, %5 + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 14 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), operandsNotRelated(%1 %5), operandsNotRelated(%7 %4), operandsNotRelated(%7 %5), notSame(%4 '(hl)' '(de)' '(bc)'), notSame(%5 '(hl)' '(de)' '(bc)' '(iy)') + +replace restart { + ld %1,#%2 + ld a,%3 (%1) +} by { + ; peephole 15 loaded %2 into a directly instead of going through %1. + ld a,(#%2 + %3) +} if notUsed(%1) + +replace restart { + ld hl,#%1 + ld a,(hl) +} by { + ld a,(#%1) + ; peephole 16 loaded a from (#%1) directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld a,(hl) +} by { + ; peephole 17 loaded %2 into a directly instead of using hl. + ld a,(#%1 + %2) +} if notUsed('hl') + +replace restart { + ld hl,#%1 + ld (hl),a +} by { + ld (#%1),a + ; peephole 18 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld (hl),a +} by { + ld (#%1 + %2),a + ; peephole 19 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld a, %1 (%2) + srl a + ld %1 (%2), a +} by { + srl %1 (%2) + ld a, %1 (%2) + ; peephole 20 shifted in (%2) instead of a. +} + +replace restart { + ld %1,(hl) + ld a,%2 (%3) + sub a,%1 +} by { + ld a,%2 (%3) + ; peephole 21 used (hl) in sub directly instead of going through %1. + sub a,(hl) +} if notVolatile(%1), notUsed(%1) + +replace restart { + inc bc + ld l,c + ld h,b +} by { + ld l,c + ld h,b + ; peephole 22 incremented in hl instead of bc. + inc hl +} if notUsed('bc') + +replace restart { + inc de + ld l,e + ld h,d +} by { + ld l,e + ld h,d + ; peephole 23 incremented in hl instead of de. + inc hl +} if notUsed('de') + +replace restart { + ld c,l + ld b,h + ld a,#%1 + ld (bc),a +} by { + ld c,l + ld b,h + ld (hl),#%1 + ; peephole 24 loaded #%1 into (hl) instead of (bc). +} if notUsed('a') + +replace restart { + ex de, hl + push de +} by { + ; peephole 25 pushed hl directly instead of going through de. + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ex de, hl + push bc + push de +} by { + ; peephole 26 pushed hl directly instead of going through de. + push bc + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ld c,l + ld b,h + push %1 + push bc +} by { + ; peephole 27 pushed hl directly instead of going through bc. + push %1 + push hl +} if notUsed('bc'), notSame(%1 'bc') + +replace restart { + pop de + ld l, e + ld h, d +} by { + ; peephole 28 popped hl directly instead of going through de. + pop hl +} if notUsed('de') + +replace restart { + pop bc + ld l, c + ld h, b +} by { + ; peephole 29 popped hl directly instead of going through bc. + pop hl +} if notUsed('bc') + +replace restart { + ld %1 (ix), %2 + ld %3, %1 (ix) +} by { + ; peephole 30 loaded %3 from %2 instead of going through %1 (ix). + ld %1 (ix), %2 + ld %3, %2 +} if canAssign(%3 %2) +// Don't need to check for volatile, since ix is used for the stack. + +replace restart { + ld %1 (ix), a + push de + ld %2, %1 (ix) +} by { + ld %1 (ix), a + push de + ; peephole 31 loaded %2 from a instead of %1 (ix) + ld %2, a +} if canAssign(%2 'a') + +replace restart { + push af + inc sp + ld a,e + push af + inc sp +} by { + ; peephole 32 pushed de instead of pushing a twice. + ld d,a + push de +} if notUsed('d'), notUsed('a') + +replace restart { + push af + inc sp + ld a,#%1 + push af + inc sp +} by { + ; peephole 33 pushed de instead of pushing a twice. + ld d,a + ld e,#%1 + push de +} if notUsed('de') + +replace restart { + push bc + inc sp + push de + inc sp +} by { + ld c, d + ; peephole 34 combined pushing of b and d. + push bc +} if notUsed('c') + +replace restart { + ld a,#%1 + ld d,a +} by { + ; peephole 35 loaded #%1 into d directly instead of going through a. + ld d,#%1 +} if notUsed('a') + +replace restart { + ld %1,a + ld %2,%1 +} by { + ; peephole 36 loaded %2 from a directly instead of going through %1. + ld %2,a +} if notUsed(%1) + +replace restart { + ld a,%1 (%3) + push af + inc sp + ld a,%2 (%3) + push af + inc sp +} by { + ; peephole 37 pushed %1 (%3), %2(%3) through hl instead of af. + ld h,%1 (%3) + ld l,%2 (%3) + push hl +} if notUsed('a'), notUsed('hl') + +replace restart { + ld c, l + ld b, h + push bc +} by { + ; peephole 38 pushed hl instead of bc. + push hl +} if notUsed('bc') + +replace restart { + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + push hl +} by { + ; peephole 39 pushed bc instead of hl. + ld c, (hl) + inc hl + ld b, (hl) + push bc +} if notUsed('bc'), notUsed('hl') + +replace restart { + pop %1 + push %1 +} by { + ; peephole 40 eleminated dead pop/push pair. +} if notUsed(%1) + +replace restart { + push hl + pop bc +} by { + ld c, l + ld b, h + ; peephole 41 replaced push/pop pair by loads. +} if notUsed('hl') + +replace restart { + push hl + pop de +} by { + ld e, l + ld d, h + ; peephole 42 replaced push/pop pair by loads. +} if notUsed('hl') + +replace restart { + ld iy,#%1 + or a,%2 (iy) +} by { + ; peephole 43 used hl instead of iy. + ld hl,#%1 + %2 + or a,(hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld %2,%3 (iy) +} by { + ; peephole 44 used hl instead of iy. + ld hl,#%1 + %3 + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld h,%3 (iy) +} by { + ; peephole 45 used hl instead of iy. + ld hl,#%1 + %3 + ld h, (hl) +} if notUsed('iy'), notUsed('l') + +replace restart { + ld iy,#%1 + ld %2 (iy), %3 +} by { + ; peephole 46 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl), %3 +} if notUsed('iy'), notUsed('hl'), notSame(%3 'h' 'l') + +replace restart { + ld iy,#%1 + ld %2,0 (iy) + ld %3,1 (iy) +} by { + ; peephole 47 used hl instead of iy. + ld hl,#%1 + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%2 'hl') + +replace restart { + ld iy,#%1 + bit %2,%3 (iy) +} by { + ; peephole 48 used hl instead of iy. + ld hl,#%1+%3 + bit %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, %3 (iy) +} by { + ; peephole 49 used hl instead of iy. + ld hl, #%1+%3 + add hl, sp + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, 0 (iy) + ld %3, 1 (iy) +} by { + ; peephole 50 used hl instead of iy. + ld hl, #%1 + add hl, sp + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld l, 0 (iy) + ld h, 1 (iy) +} by { + ; peephole 51 used hl instead of iy. + ld hl, #%1 + add hl, sp + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +} if notUsed('iy'), notUsed('a') + +replace restart { + ld iy, #%1+1 + ld a, 1 (iy) + or a, 0 (iy) +} by { + ld hl, #%1 + ld a, (hl) + dec hl + or a, (hl) + ; peephole 52 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld a, 1 (iy) + or a, 0 (iy) +} by { + ld hl, #%1+1 + add hl, sp + ld a, (hl) + dec hl + or a, (hl) + ; peephole 53 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + bit %2, %3 (iy) +} by { + ld hl, #%1+%3 + add hl, sp + bit %2, (hl) + ; peephole 54 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + or a, %2 (iy) +} by { + ld hl, #%1+%2 + add hl, sp + or a, (hl) + ; peephole 55 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld c,l + ld b,h + inc bc +} by { + ; peephole 56 incremented in hl instead of bc. + inc hl + ld c,l + ld b,h +} if notUsed('hl') + +replace restart { + ld iy, #%1 + inc %2 (iy) +} by { + ; peephole 57 incremented in (hl) instead of %2 (iy). + ld hl, #%1+%2 + inc (hl) +} if notUsed('hl'), notUsed('iy') + +replace restart { + ld iy, #%1 + dec %2 (iy) +} by { + ; peephole 58 decremented in (hl) instead of %2 (iy). + ld hl, #%1+%2 + dec (hl) +} if notUsed('hl'), notUsed('iy') + +replace restart { + add hl, hl + ld e, l + ld d, h + inc de + inc de +} by { + inc hl + ; peephole 59 incremented once in hl instead of incrementing in de twice. + add hl, hl + ld e, l + ld d, h +} if notUsed('hl') + +replace restart { + add hl, hl + inc hl + inc hl +} by { + inc hl + ; peephole 60 incremented once in hl instead of incrementing in hl twice. + add hl, hl +} + +replace restart { + add a, a + add a, a + add a, #0x04 +} by { + inc a + ; peephole 61 incremented a once instead of adding #0x04 to a. + add a, a + add a, a +} + +replace restart { + add hl, hl + pop de + inc hl + inc hl +} by { + inc hl + ; peephole 62 incremented once in hl instead of incrementing in hl twice. + add hl, hl + pop de +} + +replace restart { + ld a,%1 (%2) + bit %3,a +} by { + ; peephole 63 tested bit of %1 (%2) directly instead of going through a. + bit %3,%1 (%2) +} if notUsed('a') + +replace restart { + ld a,%1 + bit %2,a +} by { + ; peephole 64 tested bit %2 of %1 directly instead of going through a. + bit %2,%1 +} if notUsed('a'), canAssign(%1 'b') + +replace restart { + ld a, %1 + set %2, a + ld %1, a +} by { + ; peephole 65 set bit %2 of %1 directly instead of going through a. + set %2, %1 + ld a, %1 +} if canAssign(%1 'b') +// canAssign(%1 'b') is true, iff set b, %1 is possible. + +replace restart { + ld a, %1 (%2) + set %3, a + ld %1 (%2), a +} by { + ; peephole 66 set bit %3 of %1 (%2) directly instead of going through a. + set %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + ld a, %1 (%2) + res %3, a + ld %1 (%2), a +} by { + ; peephole 67 reset bit %3 of %1 (%2) directly instead of going through a. + res %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + jp NC,%1 + jp %2 +%1: +} by { + jp C,%2 + ; peephole 68 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp C,%1 + jp %2 +%1: +} by { + jp NC,%2 + ; peephole 69 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp NZ,%1 + jp %2 +%1: +} by { + jp Z,%2 + ; peephole 70 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp Z,%1 + jp %2 +%1: +} by { + jp NZ,%2 + ; peephole 71 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp %5 +} by { + jp %6 + ; peephole 72 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1,%5 +} by { + jp %1,%6 + ; peephole 73 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1 +%2: +%1: +} by { + ; peephole 74 eliminated jump. +%2: +%1: +} if labelRefCountChange(%1 -1) + +// A peephole that makes the code longer. Let's hope it's worth it in speed gain and further optimization potential. +replace restart { + ld a,#0x00 +%1: + bit %2,a + jp Z,%3 +} by { + ld a,#0x00 + jp %3 + ; peephole 77 jumped directly to %3 instead of testing a first. +%1: + bit %2,a + jp Z,%3 +} if labelRefCountChange(%3 +1) + +replace { + ld e,#0x%1 + ld d,#0x%2 +} by { + ld de,#0x%2%1 + ; peephole 78 combined constant loads into register pair. +} + +replace { + ld d,#0x%1 + ld e,#0x%2 +} by { + ld de,#0x%1%2 + ; peephole 79 combined constant loads into register pair. +} + +replace { + ld l,#0x%1 + ld h,#0x%2 +} by { + ld hl,#0x%2%1 + ; peephole 80 combined constant loads into register pair. +} + +replace { + ld h,#0x%1 + ld l,#0x%2 +} by { + ld hl,#0x%1%2 + ; peephole 81 combined constant loads into register pair. +} + +replace { + ld c,#0x%1 + ld b,#0x%2 +} by { + ld bc,#0x%2%1 + ; peephole 82 combined constant loads into register pair. +} + +replace { + ld b,#0x%1 + ld c,#0x%2 +} by { + ld bc,#0x%1%2 + ; peephole 83 combined constant loads into register pair. +} + +replace restart { + ld %1,a + ld a,%1 +} by { + ld %1,a + ; peephole 84 removed redundant load from %1 into a. +} if notVolatile(%1) +// This gives many false negatives and without the test no problems are encountered in the regression tests +// Maybe we can try this after 2.7.0 release + +replace restart { + ld %2 (ix), %1 + ld %1, %2 (ix) +} by { + ld %2 (ix), %1 + ; peephole 84a removed redundant load of %1 from %2 (ix) +} + +replace restart { + ld %1,a + ld a,%2 + or a,%1 +} by { + ld %1,a + or a,%2 + ; peephole 85 removed load by reordering or arguments. +} if notVolatile(%1), canAssign('b' %2) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + and a,%1 + or a,a +} by { + and a,%1 + ; peephole 86 removed redundant or after and. +} + +replace restart { + xor a,%1 (%2) + or a,a +} by { + xor a,%1 (%2) + ; peephole 87 removed redundant or after xor. +} + +replace { + ld %1,%2 + ld a,%2 +} by { + ld a,%2 + ld %1,a + ; peephole 88 load value in a first and use it next +} if notVolatile(%1 %2) + +replace restart { + ld %1,%2 + ld %3,%4 + ld %2,%1 + ld %4,%3 +} by { + ld %1,%2 + ld %3,%4 + ; peephole 89 removed redundant load from %3%1 into %4%2 +} if notVolatile(%1 %2 %3 %4) + +replace restart { + push de + inc sp + ld a,e + push af + inc sp +} by { + push de + ; peephole 90 pushed de +} if notUsed('a') + +replace restart { + ld a,%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + ld a,%1 + cp a,%2 + jp %3,%4 + ; peephole 91 removed load from %1 into a by replacing sub with cp + assert a=%1 +} if notVolatile(%1), notUsedFrom(%4 'a') + +replace restart { + assert a=%1 +} by { +} + +replace restart { + rlca + ld a,#0x00 + rla +} by { + rlca + and a,#0x01 + ; peephole 92 replaced zero load, rla by and since rlca writes the same value to carry bit and least significant bit. +} + +replace restart { + ld %1,%2 + push %1 + pop %4 + ld %1,%3 +} by { + ld %4,%2 + ; peephole 93 moved %2 directly into de instead of going through %1. + ld %1,%3 +} + +replace restart { + add a,#0x00 + ld %2,a + ld a,%3 + adc a,%4 +} by { + ; peephole 94 removed lower part of multibyte addition. + ld %2,a + ld a,%3 + add a,%4 +} + +replace restart { + ld a, l + add a, #0x%1 + ld e, a + ld a, h + adc a, #0x%2 + ld d, a +} by { + ld de, #0x%2%1 + add hl, de + ; peephole 95 used 16-bit addition. + ld e, l + ld d, h + ld a, h +} if notUsed('hl') + +replace restart { + ld a, l + add a, #0x%1 + ld c, a + ld a, h + adc a, #0x%2 + ld b, a +} by { + ld bc, #0x%2%1 + add hl,bc + ; peephole 96 used 16-bit addition. + ld c, l + ld b, h + ld a, h +} if notUsed('hl') + + +replace restart { + ld a,(hl) + inc a + ld (hl),a +} by { + inc (hl) + ; peephole 97 incremented in (hl) instead of going through a. +} if notUsed('a') + +replace restart { + ld c,l + ld b,h + ld hl,#%1 + add hl,bc +} by { + ; peephole 98 removed loads by exploiting commutativity of addition. + ld bc,#%1 + add hl,bc +} if notUsed('bc') + +replace restart { + ld hl,#%1 + add hl,%2 + ld bc,#%4 + add hl,bc +} by { + ; peephole 99 removed loads by exploiting commutativity of addition. + ld hl,#%1 + %4 + add hl,%2 +} if notUsed('bc') + +replace restart { + or a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + or a,%1 + jp NZ,%2 + ld %3,a + ; peephole 100 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + and a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + and a,%1 + jp NZ,%2 + ld %3,a + ; peephole 101 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + inc a + jp NZ,%1 + ld %2,#0x00 +} by { + inc a + jp NZ,%1 + ld %2,a + ; peephole 102 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + or a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + or a,%1 + jp NZ,%2 + or a,%3 + ; peephole 103 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + sub a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + sub a,%1 + jp NZ,%2 + or a,%3 + ; peephole 104 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + ld hl,#%1 + add hl,%2 + inc hl +} by { + ld hl,#%1+1 + add hl,%2 + ; peephole 106 moved increment of hl to constant. +} + +replace restart { + call %1 + ret +} by { + jp %1 + ; peephole 107 replaced call at end of function by jump (tail call optimization). +} if symmParmStack(%1) + +replace restart { + ld %1,#%2 + ld %3,%4 + ld %1,#%2 +} by { + ld %1,#%2 + ld %3,%4 + ; peephole 108 removed load of #%2 into %1 since it's still there. +} if notVolatile(%1), operandsNotRelated(%3 %1) + +replace restart { + ld hl,#%1 + ld de,#%1 +} by { + ; peephole 109 used #%1 from hl for load into de. + ld hl,#%1 + ld e,l + ld d,h +} + +replace restart { + ld %1, a + ld a, %2 (%3) + adc a, #%4 + ld %5, a + ld %6, %1 +} by { + ld %6, a + ld a, %2 (%3) + adc a, #%4 + ld %5, a + ; peephole 110 loaded %6 from a directly instead of going through %1. +} if notUsed(%1), notSame(%5 %1), notSame(%5 '(hl)' '(de)' '(bc)'), notSame(%5 %6), notSame(%6 '(hl)' '(de)' '(bc)'), notSame(%5 'a'), notSame(%6 'a') + +replace restart { + ex de, hl + ld hl, #%1 + add hl, de +} by { + ; peephole 111 removed ex exploiting commutativity of addition. + ld de, #%1 + add hl, de +} if notUsed('de') + +replace restart { + ld hl, #%1 + add hl, %2 + ex de, hl + inc de +} by { + ld hl, #%1+1 + ; peephole 112 moved increment to constant. + add hl, %2 + ex de, hl +} if notUsed('hl') + +replace restart { + ld %1,a + bit %2,%1 +} by { + bit %2,a + ; peephole 113 tested bit %2 of a directly instead of going through %1. +} if notUsed(%1) + +replace restart { + sbc a,%1 + bit 7,a + jp NZ,%2 +} by { + sbc a,%1 + jp M,%2 + ; peephole 114 used sign flag instead of testing bit 7. +} + +replace restart { + ld %1,a + or a,a + jp %3,%4 + ld a,%1 +} by { + ld %1,a + or a,a + jp %3,%4 + ; peephole 115 used value still in a instead of reloading from %1. +} + +replace { + jp %5 + ret +} by { + jp %5 + ; peephole 116 removed unused ret. +} + +replace { + jp %5 + ld sp,ix + pop ix + ret +} by { + jp %5 + ; peephole 117 removed unused ret. +} + +replace restart { + or a,%1 + jp NZ,%2 + xor a,a + jp %3 +} by { + or a,%1 + jp NZ,%2 + ; peephole 118 removed redundant zeroing of a (which has just been tested to be #0x00). + jp %3 +} + +// These ex-generating rules should be among the last ones since ex counts as a read from both hl and de for notUsed(). +barrier + +replace restart { + ld e,l + ld d,h +} by { + ; peephole 119 used ex to load hl into de. + ex de,hl +} if notUsed('hl') + +replace restart { + ld l,e + ld h,d +} by { + ; peephole 120 used ex to load de into hl. + ex de,hl +} if notUsed('de') + +barrier + +// Should be one of the last ones. Opens the code to further peephole optimization. +replace restart { +%1: +} by { + ; peephole 121 removed unused label %1. +} if labelRefCount(%1 0) + +// Ensure that all rules above see only jp, not jr. +barrier + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 123 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 124 removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 125 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 126 removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 127 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 128 removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 129 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 130 removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 131 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 132 removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +barrier + +// Do all jump optimizations before replacing by ret. + +replace restart { + jp %5 +} by { + ret + ; peephole 122 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + jp %1,%5 +} by { + ret %1 + ; peephole 134 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// Replace jp by ret before replacing jp by jr outside of jump tables, since using ret can reduce the number of references to jump labels. +barrier + +replace { + jp %5 +} by { + jr %5 + ; peephole 135 changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace { + jp Z,%5 +} by { + jr Z,%5 + ; peephole 136 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NZ,%5 +} by { + jr NZ,%5 + ; peephole 137 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp C,%5 +} by { + jr C,%5 + ; peephole 138 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NC,%5 +} by { + jr NC,%5 + ; peephole 139 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + dec b + jr NZ, %5 +} by { + djnz %5 + ; peephole 140 used djnz +} + diff --git a/src/z80/peeph-z80.def b/src/z80/peeph-z80.def new file mode 100644 index 0000000..f44e2a6 --- /dev/null +++ b/src/z80/peeph-z80.def @@ -0,0 +1,2161 @@ +// peeph-z80.def - Z80 peephole rules +// +// Some of these peepholes could be potentially moved to peeph.def, but a +// GBZ80 expert should have a look at them before. +// +// (c) Philipp Klaus Krause (pkk@spth.de, philipp@colecovision.eu) 2006 - 2015 +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +replace restart { + ld %1, %1 +} by { + ; peephole 0 removed redundant load from %1 into %1. +} if notVolatile(%1) + +replace restart { + ld %1, %2 +} by { + ; peephole 1 removed dead load from %2 into %1. +} if notVolatile(%1), notUsed(%1), notVolatile(%2) + +replace restart { + add ix,sp +} by { + ; peephole 2 removed dead frame pointer setup. +} if notUsed('ix') + +replace restart { + ld %1, %2 + %3 +} by { + ; peephole 3 removed dead load from %2 + %3 into %1. +} if notVolatile(%1), notUsed(%1) +// Should probably check for notVolatile(%2), too, but gives many false positives and no regression tests fail. + +replace restart { + ld %1, (iy) +} by { + ld %1, 0 (iy) + ; peephole 4 made 0 offset explicit. +} + +replace restart { + ld (iy), %1 +} by { + ld 0 (iy), %1 + ; peephole 5 made 0 offset explicit. +} + +replace restart { + inc hl +} by { + ; peephole 5a removed dead increment of hl. +} if notUsed('hl') + +replace restart { + dec hl +} by { + ; peephole 6 removed dead decrement of hl. +} if notUsed('hl') + +replace restart { + ld %1, %2 (iy) +} by { + ; peephole 7 removed dead load from %2 (iy) into %1. +} if notUsed(%1) +// Should probably check for notVolatile(), but gives many false positives and no regression tests fail. + +replace restart { + ld %1, %2 (ix) +} by { + ; peephole 8 removed dead load from %2 (ix) into %1. +} if notUsed(%1) + +replace restart { + ld %1, %2 + ld %3, %1 +} by { + ; peephole 9 loaded %3 from %2 directly instead of going through %1. + ld %3, %2 +} if canAssign(%3 %2), notVolatile(%1), notUsed(%1), notSame(%3 '(hl)' '(de)' '(bc)') + +replace restart { + ld %1, %2 + ld %3, %1 +} by { + ; peephole 9a loaded %3 from %2 directly instead of going through %1. + ld %3, %2 +} if canAssign(%3 %2), notVolatile(%1), notUsed(%1), canAssign('a' %1) + +replace restart { + ld %1, hl + ld hl, %1 +} by { + ; peephole 9b reused value still in hl. + ld %1, hl +} if notVolatile(%1) + +replace restart { + ld %1, %2 + ld %3, %4 + ld %5, %1 +} by { + ld %5, %2 + ; peephole 10 loaded %5 from %2 directly instead of going through %1. + ld %3, %4 +} if canAssign(%5 %2), notVolatile(%1), operandsNotRelated(%1 %4), operandsNotRelated(%1 %3), operandsNotRelated(%3 %5), operandsNotRelated(%4 %5), notUsed(%1), notSame(%3 %4 '(hl)' '(de)' '(bc)'), notVolatile(%5) +// Rule OK unless both %5 and %4 are volatile, but we can't express that directly. + +replace restart { + ld %3 (ix), %1 + ld %4 (ix), %2 + ld %5, %3 (ix) + ld %6, %4 (ix) +} by { + ld %3 (ix), %1 + ld %4 (ix), %2 + ; peephole 11 loaded %5%6 from %1%2 directly instead of going through %3 (ix) %4 (ix). + ld %5, %1 + ld %6, %2 +} if canAssign(%5 %1), canAssign(%6 %2), notSame(%5 %2) + +replace restart { + ld %1, %2 (%3) + ld %4, %1 +} by { + ; peephole 12 loaded %2 (%3) into %4 directly instead of going through %1. + ld %4, %2 (%3) +} if canAssign(%4 %2 %3), notVolatile(%1), notUsed(%1) + +replace restart { + ld %1, %2 + ld %3 (%4), %1 +} by { + ; peephole 13 loaded %2 into %3 (%4) directly instead of going through %1. + ld %3 (%4), %2 +} if canAssign(%3 %4 %2), notVolatile(%1), notUsed(%1), canAssign('a' %1) +// canAssign ('a' %1) checks that %1 is an 8-bit value + +replace restart { + ld %1, %2 (%3) + ld %4, %5 (%6) + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 14 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 (%6) +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), notSame(%1 %4), notSame(%7 %4) + +replace restart { + ld %1, %2 (%3) + ld %4, %5 + ld %7, %1 +} by { + ld %7, %2 (%3) + ; peephole 15 loaded %2 (%3) into %7 directly instead of going through %1. + ld %4, %5 +} if canAssign(%7 %2 %3), notVolatile(%1), notUsed(%1), operandsNotRelated(%1 %5), operandsNotRelated(%7 %4), operandsNotRelated(%7 %5), notSame(%4 '(hl)' '(de)' '(bc)'), notSame(%5 '(hl)' '(de)' '(bc)' '(iy)') + +replace restart { + ld %1,#%2 + ld a,%3 (%1) +} by { + ; peephole 16 loaded %2 into a directly instead of going through %1. + ld a,(#%2 + %3) +} if notUsed(%1) + +replace restart { + ld hl, #%1 + ld a, (hl) +} by { + ld a, (#%1) + ; peephole 17 loaded a from (#%1) directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld a,(hl) +} by { + ; peephole 18 loaded %2 into a directly instead of using hl. + ld a,(#%1 + %2) +} if notUsed('hl') + +replace restart { + ld hl,#%1 + ld (hl),a +} by { + ld (#%1),a + ; peephole 19 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld hl,#%1 + %2 + ld (hl),a +} by { + ld (#%1 + %2),a + ; peephole 20 loaded (#%1) from a directly instead of using hl. +} if notUsed('hl') + +replace restart { + ld a, %1 (%2) + srl a + ld %1 (%2), a +} by { + srl %1 (%2) + ld a, %1 (%2) + ; peephole 21 shifted in (%2) instead of a. +} + +replace restart { + ld %1,(hl) + ld a,%2 (%3) + sub a,%1 +} by { + ld a,%2 (%3) + ; peephole 22 used (hl) in sub directly instead of going through %1. + sub a,(hl) +} if notVolatile(%1), notUsed(%1) + +replace restart { + inc bc + ld l,c + ld h,b +} by { + ld l,c + ld h,b + ; peephole 23 incremented in hl instead of bc. + inc hl +} if notUsed('bc') + +replace restart { + inc de + ld l,e + ld h,d +} by { + ld l,e + ld h,d + ; peephole 24 incremented in hl instead of de. + inc hl +} if notUsed('de') + +replace restart { + ld c,l + ld b,h + ld a,#%1 + ld (bc),a +} by { + ld c,l + ld b,h + ld (hl),#%1 + ; peephole 25 loaded #%1 into (hl) instead of (bc). +} if notUsed('a') + +replace restart { + ex de, hl + push de +} by { + ; peephole 26 pushed hl directly instead of going through de. + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ld l,%1 + ld h,d + push hl +} by { + ; peephole 27 pushed de instead of hl removing a load. + ld e,%1 + push de +} if notUsed('hl'), notUsed('e') + +replace restart { + ex de, hl + push bc + push de +} by { + ; peephole 28 pushed hl directly instead of going through de. + push bc + push hl +} if notUsed('de'), notUsed('hl') + +replace restart { + ld l,c + ld h,b + push hl +} by { + ; peephole 29 pushed bc directly instead of going through hl. + push bc +} if notUsed('hl') + +replace restart { + ld c,l + ld b,h + push %1 + push bc +} by { + ; peephole 30 pushed hl directly instead of going through bc. + push %1 + push hl +} if notUsed('bc'), notSame(%1 'bc') + +replace restart { + pop de + ld l, e + ld h, d +} by { + ; peephole 31 popped hl directly instead of going through de. + pop hl +} if notUsed('de') + +replace restart { + pop bc + ld l, c + ld h, b +} by { + ; peephole 32 popped hl directly instead of going through bc. + pop hl +} if notUsed('bc') + +replace restart { + ld %1 (ix), %2 + ld %3, %1 (ix) +} by { + ; peephole 33 loaded %3 from %2 instead of going through %1 (ix). + ld %1 (ix), %2 + ld %3, %2 +} if canAssign(%3 %2) +// Don't need to check for volatile, since ix is used for the stack. + +replace restart { + ld %1 (ix), a + push de + ld %2, %1 (ix) +} by { + ld %1 (ix), a + push de + ; peephole 34 loaded %2 from a instead of %1 (ix) + ld %2, a +} if canAssign(%2 'a') + +replace restart { + push af + inc sp + ld a,e + push af + inc sp +} by { + ; peephole 35 pushed de instead of pushing a twice. + ld d,a + push de +} if notUsed('d'), notUsed('a') + +replace restart { + push de + inc sp + push af + inc sp +} by { + ; peephole 36 pushed de instead of pushing twice. + ld e, a + push de +} if notUsed('e'), notUsed('a') + +replace restart { + push af + inc sp + ld a,#%1 + push af + inc sp +} by { + ; peephole 37 pushed de instead of pushing a twice. + ld d,a + ld e,#%1 + push de +} if notUsed('de') + +replace restart { + push af + inc sp + ld a,#%1 + push af + inc sp +} by { + ; peephole 38 pushed bc instead of pushing a twice. + ld b,a + ld c,#%1 + push bc +} if notUsed('bc') + +replace restart { + push hl + inc sp + push de + inc sp +} by { + ld l, d + ; peephole 39 combined pushing of b and d. + push hl +} if notUsed('l') + +replace restart { + push bc + inc sp + push de + inc sp +} by { + ld c, d + ; peephole 40 combined pushing of b and d. + push bc +} if notUsed('c') + +replace restart { + push de + inc sp + push bc + inc sp +} by { + ld e, b + ; peephole 41 combined pushing of d and b. + push de +} if notUsed('e') + +replace restart { + push af + inc sp + ld a, c + push af + inc sp +} by { + ld b, a + ; peephole 42 combined pushing of a and c. + push bc +} if notUsed('b'), notUsed('a') + +replace restart { + push de + inc sp + ld a, c + push af + inc sp +} by { + ld e, c + ; peephole 43 combined pushing of d and c. + push de +} if notUsed('e'), notUsed('a') + +replace restart { + ld a, e + push af + inc sp + ld a, %2 (ix) + push af + inc sp +} by { + ld d, e + ld e, %2 (ix) + ; peephole 44 combined pushing of e and %2 (ix). + push de +} if notUsed('d'), notUsed('e'), notUsed('a') + +replace restart { + ld a,#%1 + ld d,a +} by { + ; peephole 45 loaded #%1 into d directly instead of going through a. + ld d,#%1 +} if notUsed('a') + +replace restart { + ld %1,a + ld %2,%1 +} by { + ; peephole 46 loaded %2 from a directly instead of going through %1. + ld %2,a +} if notUsed(%1) + +replace restart { + ld a, %1 (%3) + push af + inc sp + ld a, %2 (%3) + push af + inc sp +} by { + ; peephole 47 pushed %1 (%3), %2 (%3) through hl instead of af. + ld h, %1 (%3) + ld l, %2 (%3) + push hl +} if notUsed('a'), notUsed('hl') + +replace restart { + ld c, l + ld b, h + push bc +} by { + ; peephole 48 pushed hl instead of bc. + push hl +} if notUsed('bc') + +replace restart { + ld a, (hl) + inc hl + ld h, (hl) + ld l, a + push hl +} by { + ; peephole 49 pushed bc instead of hl. + ld c, (hl) + inc hl + ld b, (hl) + push bc +} if notUsed('bc'), notUsed('hl') + +replace restart { + pop %1 + push %1 +} by { + ; peephole 50 eleminated dead pop/push pair. +} if notUsed(%1) + +replace restart { + pop %1 + ld %2 (ix), %3 + push %1 +} by { + ; peephole 51 eleminated dead pop/push pair. + ld %2 (ix), %3 +} if notUsed(%1), operandsNotRelated(%1 %3) + +replace restart { + push hl + pop bc +} by { + ld c, l + ld b, h + ; peephole 52 replaced push/pop pair by loads. +} if notUsed('hl') + +replace restart { + push hl + pop de +} by { + ld e, l + ld d, h + ; peephole 53 replaced push/pop pair by loads. +} if notUsed('hl') + +replace restart { + ld iy,#%1 + or a,%2 (iy) +} by { + ; peephole 54 used hl instead of iy. + ld hl,#%1 + %2 + or a,(hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld %2,%3 (iy) +} by { + ; peephole 55 used hl instead of iy. + ld hl,#%1 + %3 + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy,#%1 + ld h,%3 (iy) +} by { + ; peephole 56 used hl instead of iy. + ld hl,#%1 + %3 + ld h, (hl) +} if notUsed('iy'), notUsed('l') + +replace restart { + ld iy,#%1 + ld %2 (iy), %3 +} by { + ; peephole 57 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl), %3 +} if notUsed('iy'), notUsed('hl'), notSame(%3 'h' 'l') + +replace restart { + ld iy,#%1 + ld %2,0 (iy) + ld %3,1 (iy) +} by { + ; peephole 58 used hl instead of iy. + ld hl,#%1 + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%2 'hl') + +replace restart { + ld iy,#%1 + ld %2 (iy),%3 + ld l,%2 (iy) +} by { + ; peephole 59 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl),%3 + ld l,(hl) +} if notUsed('iy'), notUsed('h') + +replace restart { + ld iy,#%1 + ld %2 (%3), %4 +} by { + ; peephole 60 used hl instead of iy. + ld hl,#%1 + %2 + ld (hl), %4 +} if notUsed('iy'), notUsed('hl'), operandsNotRelated(%4 'hl') + +replace restart { + ld iy,#%1 + bit %2,%3 (iy) +} by { + ; peephole 61 used hl instead of iy. + ld hl,#%1+%3 + bit %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, %3 (iy) +} by { + ; peephole 62 used hl instead of iy. + ld hl, #%1+%3 + add hl, sp + ld %2, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld %2, 0 (iy) + ld %3, 1 (iy) +} by { + ; peephole 63 used hl instead of iy. + ld hl, #%1 + add hl, sp + ld %2, (hl) + inc hl + ld %3, (hl) +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld l, 0 (iy) + ld h, 1 (iy) +} by { + ; peephole 64 used hl instead of iy. + ld hl, #%1 + add hl, sp + ld a, (hl) + inc hl + ld h, (hl) + ld l, a +} if notUsed('iy'), notUsed('a') + +replace restart { + ld iy, #%1 + add iy, sp + ld 0 (iy), #%2 + ld 1 (iy), #%3 +} by { + ; peephole 65 used hl instead of iy. + ld hl, #%1 + add hl, sp + ld (hl), #%2 + inc hl + ld (hl), #%3 +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + ld a, 1 (iy) + or a, 0 (iy) +} by { + ld hl, #%1+1 + add hl, sp + ld a, (hl) + dec hl + or a, (hl) + ; peephole 66 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + bit %2, %3 (iy) +} by { + ld hl, #%1+%3 + add hl, sp + bit %2, (hl) + ; peephole 67 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld iy, #%1 + add iy, sp + or a, %2 (iy) +} by { + ld hl, #%1+%2 + add hl, sp + or a, (hl) + ; peephole 68 used hl instead of iy. +} if notUsed('iy'), notUsed('hl') + +replace restart { + ld c,l + ld b,h + inc bc +} by { + ; peephole 69 incremented in hl instead of bc. + inc hl + ld c,l + ld b,h +} if notUsed('hl') + +replace restart { + ld iy, #%1 + inc %2 (iy) +} by { + ; peephole 70 incremented in (hl) instead of %2 (iy). + ld hl, #%1+%2 + inc (hl) +} if notUsed('hl'), notUsed('iy') + +replace restart { + ld iy, #%1 + dec %2 (iy) +} by { + ; peephole 71 decremented in (hl) instead of %2 (iy). + ld hl, #%1+%2 + dec (hl) +} if notUsed('hl'), notUsed('iy') + +replace restart { + ld a,%1 (%2) + bit %3,a +} by { + ; peephole 72 tested bit %3 of %1 (%2) directly instead of going through a. + bit %3,%1 (%2) +} if notUsed('a') + +replace restart { + ld %1, (hl) + bit %2, %1 +} by { + ; peephole 73 tested bit %2 of (hl) directly instead of going through %1. + bit %2, (hl) +} if notUsed(%1) + +replace restart { + ld a,%1 + bit %2,a +} by { + ; peephole 74 tested bit %2 of %1 directly instead of going through a. + bit %2,%1 +} if notUsed('a'), canAssign(%1 'b') + +replace restart { + ld a, %1 + set %2, a + ld %1, a +} by { + ; peephole 75 set bit %2 of %1 directly instead of going through a. + set %2, %1 + ld a, %1 +} if canAssign(%1 'b') +// canAssign(%1 'b') is true, iff set b, %1 is possible. + +replace restart { + ld a, %1 (%2) + set %3, a + ld %1 (%2), a +} by { + ; peephole 76 set bit %3 of %1 (%2) directly instead of going through a. + set %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + ld a, %1 + res %2, a + ld %1, a +} by { + ; peephole 77 reset bit %2 of %1 directly instead of going through a. + res %2, %1 + ld a, %1 +} if canAssign(%1 'b') +// canAssign(%1 'b') is true, iff set b, %1 is possible. + +replace restart { + ld a, %1 (%2) + res %3, a + ld %1 (%2), a +} by { + ; peephole 78 reset bit %3 of %1 (%2) directly instead of going through a. + res %3, %1 (%2) + ld a, %1 (%2) +} + +replace restart { + jp NC,%1 + jp %2 +%1: +} by { + jp C,%2 + ; peephole 79 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp C,%1 + jp %2 +%1: +} by { + jp NC,%2 + ; peephole 80 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp NZ,%1 + jp %2 +%1: +} by { + jp Z,%2 + ; peephole 81 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp Z,%1 + jp %2 +%1: +} by { + jp NZ,%2 + ; peephole 82 removed jp by using inverse jump logic +%1: +} if labelRefCountChange(%1 -1) + +replace restart { + jp %5 +} by { + jp %6 + ; peephole 83 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1,%5 +} by { + jp %1,%6 + ; peephole 84 jumped to %6 directly instead of via %5. +} if labelIsUncondJump(), notSame(%5 %6), labelRefCountChange(%5 -1), labelRefCountChange(%6 +1) + +replace restart { + jp %1 +%2: +%1: +} by { + ; peephole 85 eliminated jump. +%2: +%1: +} if labelRefCountChange(%1 -1) + +// A peephole that makes the code longer. Let's hope it's worth it in speed gain and further optimization potential. +replace restart { + ld a,#0x00 +%1: + bit %2,a + jp Z,%3 +} by { + ld a,#0x00 + jp %3 + ; peephole 86 jumped directly to %3 instead of testing a first. +%1: + bit %2,a + jp Z,%3 +} if labelRefCountChange(%3 +1) + +replace restart { + ld %1, %2 + jp %3 + jp %4 +} by { + ld %1, %2 + jp %3 + ; peephole 87 removed unreachable jump to %3. +} + +replace restart { + ld %1, %2 + jp %3 +%3: +} by { + ld %1, %2 +%3: + ; peephole 88 removed redundant jump to %3. +} if labelRefCountChange(%3 -1) + +replace restart { + ld %1, #0x01 + bit 0, %1 + jp Z, %2 +} by { + ld %1, #0x01 + ; peephole 89 removed impossible jump to %2. +} if labelRefCountChange(%2 -1) + +replace restart { + rlca + and a, #0x01 + jp Z, %1 +} by { + rlca + ; peephole 90 removed and by changing jump condition. + jp NC, %1 +} if notUsed('a'), notUsedFrom(%1 'a') + +replace restart { + rlca + and a, #0x01 + jp NZ, %1 +} by { + rlca + ; peephole 91 removed and by changing jump condition. + jp C, %1 +} if notUsed('a'), notUsedFrom(%1 'a') + +replace { + ld e, #0x%1 + ld d, #0x%2 +} by { + ld de, #0x%2%1 + ; peephole 92 combined constant loads into register pair. +} + +replace { + ld d, #0x%1 + ld e, #0x%2 +} by { + ld de, #0x%1%2 + ; peephole 93 combined constant loads into register pair. +} + +replace { + ld l, #0x%1 + ld h, #0x%2 +} by { + ld hl, #0x%2%1 + ; peephole 94 combined constant loads into register pair. +} + +replace { + ld h, #0x%1 + ld l, #0x%2 +} by { + ld hl, #0x%1%2 + ; peephole 95 combined constant loads into register pair. +} + +replace { + ld c, #0x%1 + ld b, #0x%2 +} by { + ld bc, #0x%2%1 + ; peephole 96 combined constant loads into register pair. +} + +replace { + ld b, #0x%1 + ld c, #0x%2 +} by { + ld bc, #0x%1%2 + ; peephole 97 combined constant loads into register pair. +} + +replace restart { + ld %1, a + ld a, %1 +} by { + ld %1, a + ; peephole 98 removed redundant load from %1 into a. +} if notVolatile(%1) +// This gives many false negatives and without the test no problems are encountered in the regression tests +// Maybe we can try this after 2.7.0 release + +replace restart { + ld %2 (ix), %1 + ld %1, %2 (ix) +} by { + ld %2 (ix), %1 + ; peephole 98a removed redundant load of %1 from %2 (ix) +} + +replace restart { + ld %1,a + ld a,%2 + or a,%1 +} by { + ld %1,a + or a,%2 + ; peephole 99 removed load by reordering or arguments. +} if notVolatile(%1), canAssign('b' %2) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + ld %1, (hl) + inc hl + ld a, (hl) + or a, %1 +} by { + ld a, (hl) + inc hl + ; peephole 99a removed load by reordering or arguments. + or a, (hl) +} if notUsed(%1) + +replace restart { + and a,%1 + or a,a +} by { + and a,%1 + ; peephole 100 removed redundant or after and. +} + +replace restart { + xor a,%1 + or a,a +} by { + xor a,%1 + ; peephole 101 removed redundant or after xor. +} + +replace restart { + xor a,%1 (%2) + or a,a +} by { + xor a,%1 (%2) + ; peephole 102 removed redundant or after xor. +} + +replace restart { + dec a + or a, a +} by { + dec a + ; peephole 102a removed redundant or after dec. +} + +replace restart { + dec %1 + ld a, %1 + or a, a +} by { + dec %1 + ld a, %1 + ; peephole 102b removed redundant or after dec. +} + +replace { + ld %1,%2 + ld a,%2 +} by { + ld a,%2 + ld %1,a + ; peephole 103 loaded value in a first and used it next +} if notVolatile(%1 %2), canAssign(%1 'a') + +replace restart { + ld %1,%2 + ld %3,%4 + ld %2,%1 + ld %4,%3 +} by { + ld %1,%2 + ld %3,%4 + ; peephole 104 removed redundant load from %3%1 into %4%2 +} if notVolatile(%1 %2 %3 %4) + +replace restart { + push de + inc sp + ld a,e + push af + inc sp +} by { + push de + ; peephole 105 pushed de +} if notUsed('a') + +replace restart { + ld iy,%1 + add iy,sp + ld sp,iy +} by { + ld hl,%1 + add hl,sp + ld sp,hl + ; peephole 106 fixed stack using hl instead of iy. +} if notUsed('hl'), notUsed('iy') + +replace restart { + ld a,%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + ld a,%1 + cp a,%2 + jp %3,%4 + ; peephole 107 removed load from %1 into a by replacing sub with cp + assert a=%1 +} if notVolatile(%1), notUsedFrom(%4 'a') + +replace restart { + assert a=%1 + sub a,%2 + jp %3,%4 + ld a,%1 +} by { + cp a,%2 + jp %3,%4 + ; peephole 108 removed load from %1 into a by replacing sub with cp + assert a=%1 +} if notUsedFrom(%4 'a') + +replace restart { + assert a=%1 +} by { +} + +replace restart { + rlca + ld a,#0x00 + rla +} by { + rlca + and a,#0x01 + ; peephole 109 replaced zero load, rla by and since rlca writes the same value to carry bit and least significant bit. +} + +replace restart { + ld %1,%2 + push %1 + pop %4 + ld %1,%3 +} by { + ld %4,%2 + ; peephole 110 moved %2 directly into de instead of going through %1. + ld %1,%3 +} + +replace restart { + add a,#0x00 + ld %2,a + ld a,%3 + adc a,%4 +} by { + ; peephole 111 removed lower part of multibyte addition. + ld %2,a + ld a,%3 + add a,%4 +} + +replace restart { + ld a, l + add a, #0x%1 + ld e, a + ld a, h + adc a, #0x%2 + ld d, a +} by { + ld de, #0x%2%1 + add hl, de + ; peephole 112 used 16-bit addition. + ld e, l + ld d, h + ld a, h +} if notUsed('hl') + +replace restart { + ld a, l + add a, #0x%1 + ld c, a + ld a, h + adc a, #0x%2 + ld b, a +} by { + ld bc, #0x%2%1 + add hl,bc + ; peephole 113 used 16-bit addition. + ld c, l + ld b, h + ld a, h +} if notUsed('hl') + +replace restart { + ld l,%1 (ix) + ld h,%2 (ix) + ld a,(hl) + inc a + ld l,%1 (ix) + ld h,%2 (ix) + ld (hl),a +} by { + ld l,%1 (ix) + ld h,%2 (ix) + inc (hl) + ; peephole 114 incremented in (hl) instead of going through a. +} if notUsed('a') + +replace restart { + ld a,(hl) + inc a + ld (hl),a +} by { + inc (hl) + ; peephole 115 incremented in (hl) instead of going through a. +} if notUsed('a') + +// TODO: Check for volatile? +replace restart { + ld %1, %2 (%3) + inc %1 + ld %2 (%3), %1 +} by { + inc %2 (%3) + ld %1, %2 (%3) + ; peephole 116 incremented in %2 (%3) instead of going through %1. +} if canAssign('a' %1) + +// TODO: Check for volatile? +replace restart { + ld %1, %2 (%3) + dec %1 + ld %2 (%3), %1 +} by { + dec %2 (%3) + ld %1, %2 (%3) + ; peephole 117 decremented in %2 (%3) instead of going through %1. +} if canAssign('a' %1) + +replace restart { + ld %1,a + ld a,%2 + add a,%1 +} by { + ld %1, a + ; peephole 118 removed load by exploiting commutativity of addition. + add a,%2 +} if notSame(%2 '(bc)' '(de)'), canAssign('b' %2) +// canAssign('b' %2) is true, iff add a,%2 is possible. + +replace restart { + ld c,l + ld b,h + ld hl,#%1 + add hl,bc +} by { + ; peephole 119 removed loads by exploiting commutativity of addition. + ld bc,#%1 + add hl,bc +} if notUsed('bc') + +replace restart { + ld hl,#%1 + add hl,%2 + ld bc,#%4 + add hl,bc +} by { + ; peephole 120 removed loads by exploiting commutativity of addition. + ld hl,#%1 + %4 + add hl,%2 +} if notUsed('bc') + +replace restart { + or a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + or a,%1 + jp NZ,%2 + ld %3,a + ; peephole 121 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + and a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + and a,%1 + jp NZ,%2 + ld %3,a + ; peephole 122 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + sub a,%1 + jp NZ,%2 + ld %3,#0x00 +} by { + sub a,%1 + jp NZ,%2 + ld %3,a + ; peephole 123 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + inc a + jp NZ,%1 + ld %2,#0x00 +} by { + inc a + jp NZ,%1 + ld %2,a + ; peephole 124 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + dec a + jp NZ,%1 + ld %2,#0x00 +} by { + dec a + jp NZ,%1 + ld %2,a + ; peephole 125 replaced constant #0x00 by a (which has just been tested to be #0x00). +} + +replace restart { + or a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + or a,%1 + jp NZ,%2 + or a,%3 + ; peephole 126 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + sub a,%1 + jp NZ,%2 + ld a,%3 + or a,a +} by { + sub a,%1 + jp NZ,%2 + or a,%3 + ; peephole 127 shortened or using a (which has just been tested to be #0x00). +} if canAssign('b' %3) +// canAssign('b' %2) is true, iff or a,%2 is possible. + +replace restart { + ld hl,#%1 + add hl,%2 + inc hl +} by { + ld hl,#%1+1 + add hl,%2 + ; peephole 129 moved increment of hl to constant. +} + +replace restart { + push hl + pop iy + pop hl + inc iy +} by { + inc hl + push hl + pop iy + pop hl + ; peephole 130 incremented in hl instead of iy. +} + +replace restart { + push bc + pop iy + inc iy +} by { + inc bc + push bc + pop iy + ; peephole 131 incremented in bc instead of iy. +} if notUsed('bc') + +replace restart { + ld hl,%1 + add hl,%2 + push hl + pop iy +} by { + ld iy,%1 + add iy,%2 + ; peephole 132 added in iy instead of hl. +} if notUsed('hl'), notSame(%2 'hl') + +replace restart { + pop af + ld sp,%1 +} by { + ; peephole 133 removed redundant pop af. + ld sp,%1 +} if notUsed('a') + +replace restart { + inc sp + ld sp,%1 +} by { + ; peephole 134 removed redundant inc sp. + ld sp,%1 +} if notUsed('a') + +replace restart { + call %1 + ret +} by { + jp %1 + ; peephole 135 replaced call at end of function by jump (tail call optimization). +} if symmParmStack(%1) + +// Callee saves ix. +replace restart { + call %1 + pop ix + ret +} by { + pop ix + jp %1 + ; peephole 136 replaced call at end of function by jump moving call beyond pop ix (tail call optimization). +} if symmParmStack(%1) + +replace restart { + ld %1,#%2 + ld %3,%4 + ld %1,#%2 +} by { + ld %1,#%2 + ld %3,%4 + ; peephole 137 removed load of #%2 into %1 since it's still there. +} if notVolatile(%1), operandsNotRelated(%3 %1) + +replace restart { + ld hl,#%1 + ld de,#%1 +} by { + ; peephole 138 used #%1 from hl for load into de. + ld hl,#%1 + ld e,l + ld d,h +} + +replace restart { + ld sp,hl + ld hl,#0x0002 + add hl,sp +} by { + ld sp, hl + inc hl + inc hl + ; peephole 139 replaced addition by increment. +} + +replace restart { + ex de, hl + ld hl, #%1 + add hl, de +} by { + ; peephole 140 removed ex exploiting commutativity of addition. + ld de, #%1 + add hl, de +} if notUsed('de') + +replace restart { + ld hl, #%1 + add hl, %2 + ex de, hl + inc de +} by { + ld hl, #%1+1 + ; peephole 141 moved increment to constant. + add hl, %2 + ex de, hl +} if notUsed('hl') + +replace restart { + pop af + push hl +} by { + ; peephole 142 used ex to move hl onto the stack. + ex (sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + ld hl, #%1 + push hl +} by { + ld hl, #%1 + ; peephole 143 used ex to move hl onto the stack. + ex (sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + inc sp + ld hl,#%1 + push hl +} by { + inc sp + ld hl,#%1 + ; peephole 144 used ex to move #%1 onto the stack. + ex (sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + inc sp + ld h, %1 (ix) + ld l, %2 (ix) + push hl +} by { + inc sp + ld h, %1 (ix) + ld l, %2 (ix) + ; peephole 145 used ex to move %1 (ix) %2 (ix) onto the stack. + ex (sp),hl +} if notUsed('a'), notUsed('hl') + +replace restart { + pop af + ld a,#%1 + push af + inc sp +} by { + ld h,#%1 + ex (sp),hl + ; peephole 146 used ex to move #%1 onto the stack. + inc sp +} if notUsed('a'), notUsed('hl') + +replace restart { + ld %1,#%2 + ld %3 (%1),a +%4: + ld %1,%5 +} by { + ld (#%2 + %3),a + ; peephole 147 directly used #%2 instead of going through %1 using indirect addressing. +%4: + ld %1,%5 +} + +replace restart { + pop af + ld %1,#%2 + ld %3 (%1),%4 + ld %1,#%5 +} by { + ld a,%4 + ld (#%2 + %3),a + ; peephole 148 used #%2 directly instead of going through %1 using indirect addressing. + pop af + ld %1,#%5 +} if notSame(%3 'a') + +replace restart { + ld %1,a + bit %2,%1 +} by { + bit %2,a + ; peephole 149 tested bit %2 of a directly instead of going through %1. +} if notUsed(%1) + +replace restart { + sbc a,%1 + bit 7,a + jp NZ,%2 +} by { + sbc a,%1 + jp M,%2 + ; peephole 150 used sign flag instead of testing bit 7. +} + +replace restart { + ld %1,a + or a,a + jp %3,%4 + ld a,%1 +} by { + ld %1,a + or a,a + jp %3,%4 + ; peephole 151 used value still in a instead of reloading from %1. +} + +replace { + jp %5 + ret +} by { + jp %5 + ; peephole 152 removed unused ret. +} + +replace { + jp %5 + ld sp,ix + pop ix + ret +} by { + jp %5 + ; peephole 153 removed unused ret. +} + +replace restart { + or a,%1 + jp NZ,%2 + xor a,a + jp %3 +} by { + or a,%1 + jp NZ,%2 + ; peephole 155 removed redundant zeroing of a (which has just been tested to be #0x00). + jp %3 +} + +replace restart { + dec hl + inc hl +} by { + ; peephole 155a removed inc hl / dec hl pair. +} + +replace restart { + dec hl + inc bc + inc hl +} by { + inc bc + ; peephole 155b removed inc hl / dec hl pair. +} + +replace restart { + dec hl + inc de + inc hl +} by { + inc de + ; peephole 155v removed inc hl / dec hl pair. +} + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp NZ, %1 +%2: + ld a, #%4 + ; peephole 168z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + ld a, #%4 +%1: +} by { + ld a, #%3 + jp Z, %1 +%2: + ld a, #%4 + ; peephole 168nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp NZ, %1 + ; peephole 169xz used double assignment in case of Z condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld a, #%3 + jp %1 +%2: + xor a, a +%1: +} by { + ld a, #%3 + jp Z, %1 + ; peephole 169xnz used double assignment in case of NZ condition. +%2: + xor a, a +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp NZ, %1 +%2: + ld c, #%4 + ; peephole 170z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld c, #%3 + jp %1 +%2: + ld c, #%4 +%1: +} by { + ld c, #%3 + jp Z, %1 +%2: + ld c, #%4 + ; peephole 170nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp NZ, %1 +%2: + ld e, #%4 + ; peephole 171z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld e, #%3 + jp %1 +%2: + ld e, #%4 +%1: +} by { + ld e, #%3 + jp Z, %1 +%2: + ld e, #%4 + ; peephole 171nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp Z, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp NZ, %1 +%2: + ld l, #%4 + ; peephole 172z used double assignment in case of Z condition. +%1: +} if labelRefCountChange(%2 -1) + +replace restart { + jp NZ, %2 + ld l, #%3 + jp %1 +%2: + ld l, #%4 +%1: +} by { + ld l, #%3 + jp Z, %1 +%2: + ld l, #%4 + ; peephole 172nz used double assignment in case of NZ condition. +%1: +} if labelRefCountChange(%2 -1) + +// These ex-generating rules should be among the last ones since ex counts as a read from both hl and de for notUsed(). +barrier + +replace restart { + ld e,l + ld d,h +} by { + ; peephole 156 used ex to load hl into de. + ex de,hl +} if notUsed('hl') + +replace restart { + ld l,e + ld h,d +} by { + ; peephole 157 used ex to load de into hl. + ex de,hl +} if notUsed('de') + +barrier + +// Should be one of the last ones. Opens the code to further peephole optimization. +replace restart { +%1: +} by { + ; peephole 158 removed unused label %1. +} if labelRefCount(%1 0) + +// Ensure that all rules above see only jp, not jr. +barrier + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 159-3 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 +%2: +} by { + ; peephole 159-3' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 159-4 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 +%2: +} by { + ; peephole 159-4' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 159-5 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 +%2: +} by { + ; peephole 159-5' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 159-6 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 +%2: +} by { + ; peephole 159-6' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 +%2: +} if labelJTInRange + +replace { + add hl,de + pop de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 159-7 removed addition using short jumps in jump-table. + pop de + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +replace { + add hl,de + jp (hl) +%1: + jp %5 + jp %6 + jp %7 + jp %8 + jp %9 + jp %10 + jp %11 +%2: +} by { + ; peephole 159-7' removed addition using short jumps in jump-table. + jp (hl) +%1: + jr %5 + jr %6 + jr %7 + jr %8 + jr %9 + jr %10 + jr %11 +%2: +} if labelJTInRange + +barrier + +// Do all jump optimizations before replacing by ret. + +replace restart { + jp %5 +} by { + ret + ; peephole 160 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +replace restart { + jp %1,%5 +} by { + ret %1 + ; peephole 161 replaced jump by return. +} if labelIsReturnOnly(%5), labelRefCountChange(%5 -1) + +// Replace jp by ret before replacing jp by jr outside of jump tables, since using ret can reduce the number of references to jump labels. +barrier + +replace { + jp %5 +} by { + jr %5 + ; peephole 162 changed absolute to relative unconditional jump. +} if labelInRange(%5) + +replace { + jp Z,%5 +} by { + jr Z,%5 + ; peephole 163 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NZ,%5 +} by { + jr NZ,%5 + ; peephole 164 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp C,%5 +} by { + jr C,%5 + ; peephole 165 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + jp NC,%5 +} by { + jr NC,%5 + ; peephole 166 changed absolute to relative conditional jump. +} if labelInRange(%5) + +replace { + dec b + jr NZ, %5 +} by { + djnz %5 + ; peephole 167 used djnz +} + +barrier diff --git a/src/z80/peeph.def b/src/z80/peeph.def new file mode 100644 index 0000000..365e553 --- /dev/null +++ b/src/z80/peeph.def @@ -0,0 +1,7 @@ +// peeph.def - Common Z80 and gbz80 peephole rules + +// This file should contain common Z80/GBZ80 peepholes. +// However the GBZ80 port is currently unmaintained; +// a GBZ80 expert could extract peepholes that are useful for both Z80 and GBZ80 +// from peeph-z80.def and peeph-gbz80.def and move them here. + diff --git a/src/z80/profile.txt b/src/z80/profile.txt new file mode 100644 index 0000000..7c05ec0 --- /dev/null +++ b/src/z80/profile.txt @@ -0,0 +1,163 @@ +-- 2 +0000 71 0.00 +_main 136829936 10.65 +_Proc_1 69267324 5.39 +_Proc_2 16514064 1.28 +_Proc_3 13368528 1.04 +_Proc_4 11009376 0.86 +_Proc_5 4914900 0.38 +_Proc_6 18840450 1.47 +_Func_1 13466826 1.05 +_Func_3 5308092 0.41 +_Proc_7 22313646 1.74 +_Proc_8 85060536 6.62 +_Func_2 32438340 2.52 +_strcpy 215318528 16.75 +__mulsint_rrf_s 118416324 9.21 +__divsint_rrf_s 67760088 5.27 +__rlulong_rrf_s 13056 0.00 +__divulong 52953 0.00 +_memcpy 172381926 13.41 +_strcmp 282016962 21.94 +; 1285291953 t-states + +-- 3 +0000 71 0.00 +_main 134339608 12.06 +_Proc_1 69267324 6.22 +_Proc_2 16514064 1.48 +_Proc_3 13368528 1.20 +_Proc_4 11009376 0.99 +_Proc_5 4914900 0.44 +_Proc_6 18840450 1.69 +_Func_1 13466826 1.21 +_Func_3 5308092 0.48 +_Proc_7 22313646 2.00 +_Proc_8 85060536 7.64 +_Func_2 32438340 2.91 +_strcpy 46497792 4.17 +__mulsint_rrf_s 118416324 10.63 +__divsint_rrf_s 67760088 6.08 +__rlulong_rrf_s 13056 0.00 +__divulong 53244 0.00 +_memcpy 172381926 15.47 +_strcmp 282016962 25.32 +; 1113981180 t-states + +-- 4 +0000 71 0.00 +_main 134298428 15.24 +_Proc_1 69267324 7.86 +_Proc_2 16514064 1.87 +_Proc_3 13368528 1.52 +_Proc_4 11009376 1.25 +_Proc_5 4914900 0.56 +_Proc_6 18840450 2.14 +_Func_1 13466826 1.53 +_Func_3 5308092 0.60 +_Proc_7 22313646 2.53 +_Proc_8 85060536 9.65 +_Func_2 32438340 3.68 +_strcpy 46497792 5.28 +__mulsint_rrf_s 118416324 13.44 +__divsint_rrf_s 67760088 7.69 +__rlulong_rrf_s 13056 0.00 +__divulong 53427 0.01 +_memcpy 102098856 11.59 +_strcmp 119497602 13.56 +; 881137753 t-states + +-- 6 +0000 71 0.00 +_main 133963467 16.01 +_Proc_1 69267324 8.28 +_Proc_2 16514064 1.97 +_Proc_3 13368528 1.60 +_Proc_4 11009376 1.32 +_Proc_5 4914900 0.59 +_Proc_6 18840450 2.25 +_Func_1 13466826 1.61 +_Func_3 5308092 0.63 +_Proc_7 22313646 2.67 +_Proc_8 84405216 10.09 +_Func_2 32438340 3.88 +_strcpy 46497792 5.56 +__mulsint_rrf_s 118416324 14.16 +__divsint_rrf_s 67760088 8.10 +__rlulong_rrf_s 13056 0.00 +__divulong 52549 0.01 +_memcpy 80800956 9.66 +_strcmp 97216722 11.62 +; 836567814 t-states + +-- 7 +0000 71 0.00 +_main 122822995 15.44 +_Proc_1 69889878 8.78 +_Proc_2 16514064 2.08 +_Proc_3 13368528 1.68 +_Proc_4 11009376 1.38 +_Proc_5 4914900 0.62 +_Proc_6 18840450 2.37 +_Func_1 13466826 1.69 +_Func_3 5308092 0.67 +_Proc_7 22313646 2.80 +_Proc_8 83553300 10.50 +_Func_2 32438340 4.08 +_strcpy 46497792 5.84 +__mulsint_rrf_s 88861392 11.17 +__divsint_rrf_s 67760088 8.52 +__rlulong_rrf_s 13056 0.00 +__divulong 72840 0.01 +_memcpy 80800956 10.16 +_strcmp 97216722 12.22 +; 795663339 t-states + +-- 8 +; Function total-ticks total-calls ticks-per-call total-percent +0000 71 0 0 0.00 +_main 122823011 1 122823011 15.45 +_Proc_1 69267324 32766 2114 8.71 +_Proc_2 16514064 32766 504 2.08 +_Proc_3 13368528 32766 408 1.68 +_Proc_4 11009376 32766 336 1.38 +_Proc_5 4914900 32766 150 0.62 +_Proc_6 18840450 32766 575 2.37 +_Func_1 13466826 98298 137 1.69 +_Func_3 5308092 32766 162 0.67 +_Proc_7 22313646 98298 227 2.81 +_Proc_8 83553300 32766 2550 10.51 +_Func_2 32438340 32766 990 4.08 +_strcpy 46497792 32768 1419 5.85 +__mulsint_rrf_s 88861392 98298 904 11.18 +__divsint_rrf_s 67760088 32766 2068 8.52 +__rlulong_rrf_s 13056 128 102 0.00 +__divulong 52549 2 26274 0.01 +_memcpy 80800956 32766 2466 10.16 +_strcmp 97216722 32766 2967 12.23 +; 795020510 t-states +; So the lib functions except mul, div etc take 28% of the time. + +-- 9 +; Function total-ticks total-calls ticks-per-call total-percent +0000 71 0 0 0.00 +_main 122864113 1 122864113 18.00 +_Proc_1 96102678 32766 2933 14.08 +_Proc_2 16514064 32766 504 2.42 +_Proc_3 13368528 32766 408 1.96 +_Proc_4 11009376 32766 336 1.61 +_Proc_5 4914900 32766 150 0.72 +_Proc_6 18840450 32766 575 2.76 +_Func_1 13466826 98298 137 1.97 +_Func_3 5308092 32766 162 0.78 +_Proc_7 22313646 98298 227 3.27 +_Proc_8 83553300 32766 2550 12.24 +_Func_2 32438340 32766 990 4.75 +_strcpy 46497792 32768 1419 6.81 +__mulsint_rrf_s 88861392 98298 904 13.02 +__divsint_rrf_s 67760088 32766 2068 9.93 +__rlulong_rrf_s 13056 128 102 0.00 +__divulong 52213 2 26106 0.01 +__memcpy_rrf_s 3702558 32766 113 0.54 +__strcmp_rrf_s 34830258 32766 1063 5.10 +; 682411768 t-states diff --git a/src/z80/ralloc.c b/src/z80/ralloc.c new file mode 100644 index 0000000..3aadcc0 --- /dev/null +++ b/src/z80/ralloc.c @@ -0,0 +1,3224 @@ +/** @name Z80 Register allocation functions. + @author Michael Hope + + Note: much of this is ripped straight from Sandeep's mcs51 code. + + This code maps the virtual symbols and code onto the real + hardware. It allocates based on usage and how long the varible + lives into registers or temporary memory on the stack. + + On the Z80 hl and ix and a are reserved for the code generator, + leaving bc and de for allocation. iy is unusable due to currently + as it's only adressable as a pair. The extra register pressure + from reserving hl is made up for by how much easier the sub + operations become. You could swap hl for iy if the undocumented + iyl/iyh instructions are available. + + The stack frame is the common ix-bp style. Basically: + + ix+4+n: param 1 + ix+4: param 0 + ix+2: return address + ix+0: calling functions ix + ix-n: local varibles + ... + sp: end of local varibles + + There is currently no support for bit spaces or banked functions. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. This program is distributed in the + hope that it will be useful, but WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. In other words, you are welcome to use, share and improve + this program. You are forbidden to forbid anyone else to use, + share and improve what you give them. Help stamp out + software-hoarding! +*/ + +#include "z80.h" +#include "SDCCicode.h" +#include "dbuf_string.h" + +/* Flags to turn off optimisations. + */ +enum +{ + DISABLE_PACK_ACC = 0, + DISABLE_PACK_ASSIGN = 0, + DISABLE_PACK_ONE_USE = 0, + DISABLE_PACK_HL = 0, + DISABLE_PACK_IY = 0 +}; + +/* Flags to turn on debugging code. + */ +enum +{ + D_ALLOC = 0, + D_ALLOC2 = 0, + D_ACCUSE2 = 0, + D_ACCUSE2_VERBOSE = 0, + D_HLUSE = 0, + D_HLUSE2 = 0, + D_HLUSE2_VERBOSE = 0, + D_FILL_GAPS = 0, + D_PACK_IY = 0, + D_PACK_HLUSE3 = 0 +}; + +// #define D_ALLOC 1 + +#if 1 +#define D(_a, _s) if (_a) { printf _s; fflush(stdout); } +#else +#define D(_a, _s) +#endif + +#define DISABLE_PACKREGSFORSUPPORT 1 +#define DISABLE_PACKREGSFORACCUSE 1 + +// Build the old register allocator. It can be used by command-line options +#define OLDRALLOC 1 + +extern void genZ80Code (iCode *); + +/** Local static variables */ +static struct +{ + bitVect *spiltSet; + set *stackSpil; + bitVect *regAssigned; + bitVect *totRegAssigned; /* final set of LRs that got into registers */ + short blockSpil; + int slocNum; + /* registers used in a function */ + bitVect *funcrUsed; + int stackExtend; + int dataExtend; + int nRegs; +} _G; + +static reg_info _gbz80_regs[] = { + {REG_GPR, A_IDX, "a", 1}, + {REG_GPR, C_IDX, "c", 1}, + {REG_GPR, B_IDX, "b", 1}, + {REG_GPR, E_IDX, "e", 1}, + {REG_GPR, D_IDX, "d", 1}, + {REG_GPR, L_IDX, "l", 1}, + {REG_GPR, H_IDX, "h", 1}, + {REG_CND, CND_IDX, "c", 1} +}; + +static reg_info _z80_regs[] = { + {REG_GPR, A_IDX, "a", 1}, + {REG_GPR, C_IDX, "c", 1}, + {REG_GPR, B_IDX, "b", 1}, + {REG_GPR, E_IDX, "e", 1}, + {REG_GPR, D_IDX, "d", 1}, + {REG_GPR, L_IDX, "l", 1}, + {REG_GPR, H_IDX, "h", 1}, + {REG_GPR, IYL_IDX, "iyl", 1}, + {REG_GPR, IYH_IDX, "iyh", 1}, + {REG_CND, CND_IDX, "c", 1} +}; + +reg_info *regsZ80; + +/** Number of usable registers (all but C) */ +#define Z80_MAX_REGS ((sizeof(_z80_regs)/sizeof(_z80_regs[0]))-1) +#define GBZ80_MAX_REGS ((sizeof(_gbz80_regs)/sizeof(_gbz80_regs[0]))-1) + +void z80SpillThis (symbol *); +static void freeAllRegs (); + +#ifdef OLDRALLOC +/** Allocates register of given type. + 'type' is not used on the z80 version. It was used to select + between pointer and general purpose registers on the mcs51 version. + + @return Pointer to the newly allocated register. + */ +static reg_info * +allocReg (short type) +{ + int i; + + for (i = C_IDX; i < _G.nRegs; i++) + { + /* For now we allocate from any free */ + if (regsZ80[i].isFree) + { + regsZ80[i].isFree = 0; + if (currFunc) + { + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + } + D (D_ALLOC, ("allocReg: alloced %s\n", regsZ80[i].name)); + return ®sZ80[i]; + } + } + D (D_ALLOC, ("allocReg: No free.\n")); + return NULL; +} +#endif + +/** Returns pointer to register wit index number + */ +reg_info * +regWithIdx (int idx) +{ + int i; + + for (i = C_IDX; i < _G.nRegs; i++) + { + if (regsZ80[i].rIdx == idx) + { + return ®sZ80[i]; + } + } + + wassertl (0, "regWithIdx not found"); + exit (1); +} + +/** Frees a register. + */ +static void +freeReg (reg_info *reg) +{ + wassert (!reg->isFree); + reg->isFree = 1; + D (D_ALLOC, ("freeReg: freed %p\n", reg)); +} + +#ifdef OLDRALLOC +/** Returns number of free registers. + */ +static int +nFreeRegs (int type) +{ + int i; + int nfr = 0; + + for (i = C_IDX; i < _G.nRegs; i++) + { + /* For now only one reg type */ + if (regsZ80[i].isFree) + { + nfr++; + } + } + return nfr; +} +#endif + +#ifdef OLDRALLOC +/** Free registers with type. + */ +static int +nfreeRegsType (int type) +{ + int nfr; + if (type == REG_PTR) + { + if ((nfr = nFreeRegs (type)) == 0) + { + return nFreeRegs (REG_GPR); + } + } + + return nFreeRegs (type); +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* useReg - marks a register as used */ +/*-----------------------------------------------------------------*/ +static void +useReg (reg_info * reg) +{ + reg->isFree = 0; +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* computeSpillable - given a point find the spillable live ranges */ +/*-----------------------------------------------------------------*/ +static bitVect * +computeSpillable (iCode * ic) +{ + bitVect *spillable; + + /* spillable live ranges are those that are live at this + point . the following categories need to be subtracted + from this set. + a) - those that are already spilt + b) - if being used by this one + c) - defined by this one */ + + spillable = bitVectCopy (ic->rlive); + spillable = bitVectCplAnd (spillable, _G.spiltSet); /* those already spilt */ + spillable = bitVectCplAnd (spillable, ic->uses); /* used in this one */ + bitVectUnSetBit (spillable, ic->defKey); + spillable = bitVectIntersect (spillable, _G.regAssigned); + + return spillable; +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* noSpilLoc - return true if a variable has no spil location */ +/*-----------------------------------------------------------------*/ +static int +noSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 0 : 1); +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* hasSpilLoc - will return 1 if the symbol has spil location */ +/*-----------------------------------------------------------------*/ +static int +hasSpilLoc (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return (sym->usl.spillLoc ? 1 : 0); +} +#endif + +#ifdef OLDRALLOC +/** Will return 1 if the remat flag is set. + A symbol is rematerialisable if it doesn't need to be allocated + into registers at creation as it can be re-created at any time - + i.e. it's constant in some way. +*/ +static int +rematable (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return sym->remat; +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* allLRs - return true for all */ +/*-----------------------------------------------------------------*/ +static int +allLRs (symbol * sym, eBBlock * ebp, iCode * ic) +{ + return 1; +} +#endif + +#ifdef OLDRALLOC +/** liveRangesWith - applies function to a given set of live range + */ +static set * +liveRangesWith (bitVect * lrs, int (func) (symbol *, eBBlock *, iCode *), eBBlock * ebp, iCode * ic) +{ + set *rset = NULL; + int i; + + if (!lrs || !lrs->size) + return NULL; + + for (i = 1; i < lrs->size; i++) + { + symbol *sym; + if (!bitVectBitValue (lrs, i)) + continue; + + /* if we don't find it in the live range + hash table we are in serious trouble */ + if (!(sym = hTabItemWithKey (liveRanges, i))) + { + wassertl (0, "liveRangesWith could not find liveRange"); + exit (1); + } + + if (func (sym, ebp, ic) && bitVectBitValue (_G.regAssigned, sym->key)) + { + addSetHead (&rset, sym); + } + } + + return rset; +} +#endif + +#ifdef OLDRALLOC +/** leastUsedLR - given a set determines which is the least used + */ +static symbol * +leastUsedLR (set * sset) +{ + symbol *sym = NULL, *lsym = NULL; + + sym = lsym = setFirstItem (sset); + + if (!lsym) + return NULL; + + for (; lsym; lsym = setNextItem (sset)) + { + + /* if usage is the same then prefer + the spill the smaller of the two */ + if (lsym->used == sym->used) + if (getSize (lsym->type) < getSize (sym->type)) + sym = lsym; + + /* if less usage */ + if (lsym->used < sym->used) + sym = lsym; + + } + + setToNull ((void *) &sset); + sym->blockSpil = 0; + return sym; +} +#endif + +/** noOverLap - will iterate through the list looking for over lap + */ +static int +noOverLap (set *itmpStack, symbol *fsym) +{ + symbol *sym; + + for (sym = setFirstItem (itmpStack); sym; sym = setNextItem (itmpStack)) + { + if (bitVectBitValue (sym->clashes, fsym->key)) + return 0; +#if 0 + // if sym starts before (or on) our end point + // and ends after (or on) our start point, + // it is an overlap. + if (sym->liveFrom <= fsym->liveTo && sym->liveTo >= fsym->liveFrom) + { + return 0; + } +#endif + } + return 1; +} + +/*-----------------------------------------------------------------*/ +/* isFree - will return 1 if the a free spil location is found */ +/*-----------------------------------------------------------------*/ +DEFSETFUNC (isFree) +{ + symbol *sym = item; + V_ARG (symbol **, sloc); + V_ARG (symbol *, fsym); + + /* if already found */ + if (*sloc) + return 0; + + /* if it is free && and the itmp assigned to + this does not have any overlapping live ranges + with the one currently being assigned and + the size can be accomodated */ + if (sym->isFree && noOverLap (sym->usl.itmpStack, fsym) && getSize (sym->type) >= getSize (fsym->type)) + { + *sloc = sym; + return 1; + } + + return 0; +} + +/*-----------------------------------------------------------------*/ +/* createStackSpil - create a location on the stack to spil */ +/*-----------------------------------------------------------------*/ +static symbol * +createStackSpil (symbol *sym) +{ + symbol *sloc = NULL; + struct dbuf_s dbuf; + + D (D_ALLOC, ("createStackSpil: for sym %p (%s)\n", sym, sym->name)); + + /* first go try and find a free one that is already + existing on the stack */ + if (applyToSet (_G.stackSpil, isFree, &sloc, sym) && USE_OLDSALLOC) + { + /* found a free one : just update & return */ + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + sloc->isFree = 0; + addSetHead (&sloc->usl.itmpStack, sym); + D (D_ALLOC, ("createStackSpil: found existing\n")); + return sym; + } + + /* could not then have to create one , this is the hard part + we need to allocate this on the stack : this is really a + hack!! but cannot think of anything better at this time */ + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "sloc%d", _G.slocNum++); + sloc = newiTemp (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + + /* set the type to the spilling symbol */ + sloc->type = copyLinkChain (sym->type); + sloc->etype = getSpec (sloc->type); + SPEC_SCLS (sloc->etype) = S_AUTO; + SPEC_EXTR (sloc->etype) = 0; + SPEC_STAT (sloc->etype) = 0; + SPEC_VOLATILE (sloc->etype) = 0; + + allocLocal (sloc); + + sloc->isref = 1; /* to prevent compiler warning */ + + wassertl (currFunc, "Local variable used outside of function."); + + /* if it is on the stack then update the stack */ + if (IN_STACK (sloc->etype)) + { + if (currFunc) + currFunc->stack += getSize (sloc->type); + _G.stackExtend += getSize (sloc->type); + } + else + { + _G.dataExtend += getSize (sloc->type); + } + + /* add it to the stackSpil set */ + addSetHead (&_G.stackSpil, sloc); + sym->usl.spillLoc = sloc; + sym->stackSpil = 1; + + /* add it to the set of itempStack set + of the spill location */ + addSetHead (&sloc->usl.itmpStack, sym); + + D (D_ALLOC, ("createStackSpil: created new %s for %s\n", sloc->name, sym->name)); + return sym; +} + +/*-----------------------------------------------------------------*/ +/* spillThis - spils a specific operand */ +/*-----------------------------------------------------------------*/ +void +z80SpillThis (symbol * sym) +{ + int i; + + D (D_ALLOC, ("z80SpillThis: spilling %p (%s)\n", sym, sym->name)); + + /* if this is rematerializable or has a spillLocation + we are okay, else we need to create a spillLocation + for it */ + if (!(sym->remat || sym->usl.spillLoc) || (sym->usl.spillLoc && !sym->usl.spillLoc->onStack)) // z80 port currently only supports on-stack spill locations in code generation. + createStackSpil (sym); + else + D (D_ALLOC, ("Already has spilllocation %p, %s\n", sym->usl.spillLoc, sym->usl.spillLoc->name)); + + /* mark it as spilt & put it in the spilt set */ + sym->isspilt = sym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, sym->key); + + bitVectUnSetBit (_G.regAssigned, sym->key); + bitVectUnSetBit (_G.totRegAssigned, sym->key); + + for (i = 0; i < sym->nRegs; i++) + { + if (sym->regs[i]) + { + freeReg (sym->regs[i]); + sym->regs[i] = NULL; + } + } + + if (sym->usl.spillLoc && !sym->remat) + { + sym->usl.spillLoc->allocreq++; + } + return; +} + +#ifdef OLDRALLOC +/** Select a iTemp to spil : rather a simple procedure. + */ +symbol * +selectSpil (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + bitVect *lrcs = NULL; + set *selectS; + symbol *sym; + + D (D_ALLOC, ("selectSpil: finding spill for ic %p\n", ic)); + /* get the spillable live ranges */ + lrcs = computeSpillable (ic); + + /* get all live ranges that are rematerizable */ + if ((selectS = liveRangesWith (lrcs, rematable, ebp, ic))) + { + D (D_ALLOC, ("selectSpil: using remat.\n")); + /* return the least used of these */ + return leastUsedLR (selectS); + } + +#if 0 + /* get live ranges with spillLocations in direct space */ + if ((selectS = liveRangesWith (lrcs, directSpilLoc, ebp, ic))) + { + sym = leastUsedLR (selectS); + strcpy (sym->rname, (sym->usl.spillLoc->rname[0] ? sym->usl.spillLoc->rname : sym->usl.spillLoc->name)); + sym->spildir = 1; + /* mark it as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* if the symbol is local to the block then */ + if (forSym->liveTo < ebp->lSeq) + { + + /* check if there are any live ranges allocated + to registers that are not used in this block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInBlock, ebp, ic))) + { + sym = leastUsedLR (selectS); + /* if this is not rematerializable */ + if (!sym->remat) + { + _G.blockSpil++; + wassertl (0, "Attempted to do an unsupported block spill"); + sym->blockSpil = 1; + } + return sym; + } + + /* check if there are any live ranges that not + used in the remainder of the block */ + if (!_G.blockSpil && (selectS = liveRangesWith (lrcs, notUsedInRemaining, ebp, ic))) + { + sym = leastUsedLR (selectS); + if (sym != forSym) + { + if (!sym->remat) + { + wassertl (0, "Attempted to do an unsupported remain spill"); + sym->remainSpil = 1; + _G.blockSpil++; + } + return sym; + } + } + } + /* find live ranges with spillocation && not used as pointers */ + if ((selectS = liveRangesWith (lrcs, hasSpilLocnoUptr, ebp, ic))) + { + + sym = leastUsedLR (selectS); + /* mark this as allocation required */ + sym->usl.spillLoc->allocreq++; + return sym; + } +#endif + + /* find live ranges with spillocation */ + if ((selectS = liveRangesWith (lrcs, hasSpilLoc, ebp, ic))) + { + D (D_ALLOC, ("selectSpil: using with spill.\n")); + sym = leastUsedLR (selectS); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* couldn't find then we need to create a spil + location on the stack , for which one? the least + used ofcourse */ + if ((selectS = liveRangesWith (lrcs, noSpilLoc, ebp, ic))) + { + D (D_ALLOC, ("selectSpil: creating new spill.\n")); + /* return a created spil location */ + sym = createStackSpil (leastUsedLR (selectS)); + sym->usl.spillLoc->allocreq++; + return sym; + } + + /* this is an extreme situation we will spill + this one : happens very rarely but it does happen */ + D (D_ALLOC, ("selectSpil: using z80SpillThis.\n")); + z80SpillThis (forSym); + return forSym; + +} +#endif + +#ifdef OLDRALLOC +/** Spil some variable & mark registers as free. + A spill occurs when an iTemp wont fit into the available registers. + */ +bool +spilSomething (iCode * ic, eBBlock * ebp, symbol * forSym) +{ + symbol *ssym; + int i; + + D (D_ALLOC, ("spilSomething: spilling on ic %p\n", ic)); + + /* get something we can spil */ + ssym = selectSpil (ic, ebp, forSym); + + /* mark it as spilt */ + ssym->isspilt = ssym->spillA = 1; + _G.spiltSet = bitVectSetBit (_G.spiltSet, ssym->key); + + /* mark it as not register assigned & + take it away from the set */ + bitVectUnSetBit (_G.regAssigned, ssym->key); + bitVectUnSetBit (_G.totRegAssigned, ssym->key); + + /* mark the registers as free */ + for (i = 0; i < ssym->nRegs; i++) + if (ssym->regs[i]) + freeReg (ssym->regs[i]); + + wassertl (ssym->blockSpil == 0, "Encountered a sym with a block spill"); + wassertl (ssym->remainSpil == 0, "Encountered a sym with a remain spill"); +#if 0 + /* if spilt on stack then free up r0 & r1 + if they could have been assigned to as gprs */ + if (!ptrRegReq && isSpiltOnStack (ssym)) + { + ptrRegReq++; + spillLRWithPtrReg (ssym); + } + + /* if this was a block level spil then insert push & pop + at the start & end of block respectively */ + if (ssym->blockSpil) + { + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push to the start of the block */ + addiCodeToeBBlock (ebp, nic, (ebp->sch->op == LABEL ? ebp->sch->next : ebp->sch)); + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } + + /* if spilt because not used in the remainder of the + block then add a push before this instruction and + a pop at the end of the block */ + if (ssym->remainSpil) + { + + iCode *nic = newiCode (IPUSH, operandFromSymbol (ssym), NULL); + /* add push just before this instruction */ + addiCodeToeBBlock (ebp, nic, ic); + + nic = newiCode (IPOP, operandFromSymbol (ssym), NULL); + /* add pop to the end of the block */ + addiCodeToeBBlock (ebp, nic, NULL); + } +#endif + + D (D_ALLOC, ("spilSomething: done.\n")); + + if (ssym == forSym) + return FALSE; + else + return TRUE; +} +#endif + +#ifdef OLDRALLOC +/** Will try for GPR if not spil. + */ +reg_info * +getRegGpr (iCode * ic, eBBlock * ebp, symbol * sym) +{ + reg_info *reg; + int j; + + D (D_ALLOC, ("getRegGpr: on ic %p\n", ic)); +tryAgain: + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + { + D (D_ALLOC, ("getRegGpr: got a reg.\n")); + return reg; + } + + /* we have to spil */ + if (!spilSomething (ic, ebp, sym)) + { + D (D_ALLOC, ("getRegGpr: have to spill.\n")); + return NULL; + } + + /* make sure partially assigned registers aren't reused */ + for (j = 0; j <= sym->nRegs; j++) + if (sym->regs[j]) + sym->regs[j]->isFree = 0; + + /* this looks like an infinite loop but + in really selectSpil will abort */ + goto tryAgain; +} +#endif + +#ifdef OLDRALLOC +static reg_info * +getRegGprNoSpil () +{ + reg_info *reg; + + /* try for gpr type */ + if ((reg = allocReg (REG_GPR))) + { + D (D_ALLOC, ("getRegGprNoSpil: got a reg.\n")); + return reg; + } + assert (0); + + /* just to make the compiler happy */ + return 0; +} +#endif + +/** Symbol has a given register. + */ +static bool +symHasReg (symbol *sym, const reg_info *reg) +{ + int i; + + for (i = 0; i < sym->nRegs; i++) + if (sym->regs[i] == reg) + return TRUE; + + return FALSE; +} + +/** Check the live to and if they have registers & are not spilt then + free up the registers +*/ +static void +deassignLRs (iCode *ic, eBBlock *ebp) +{ + symbol *sym; + int k; +#ifdef OLDRALLOC + symbol *result; +#endif + + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + + symbol *psym = NULL; + /* if it does not end here */ + if (sym->liveTo > ic->seq) + continue; + + /* if it was spilt on stack then we can + mark the stack spil location as free */ + if (sym->isspilt) + { + if (sym->stackSpil) + { + sym->usl.spillLoc->isFree = 1; + sym->stackSpil = 0; + } + continue; + } + + if (!bitVectBitValue (_G.regAssigned, sym->key)) + continue; + + /* special case check if this is an IFX & + the privious one was a pop and the + previous one was not spilt then keep track + of the symbol */ + if (ic->op == IFX && ic->prev && ic->prev->op == IPOP && !ic->prev->parmPush && !OP_SYMBOL (IC_LEFT (ic->prev))->isspilt) + psym = OP_SYMBOL (IC_LEFT (ic->prev)); + + D (D_ALLOC, ("deassignLRs: in loop on sym %p nregs %u\n", sym, sym->nRegs)); + + if (sym->nRegs) + { + int i = 0; + + bitVectUnSetBit (_G.regAssigned, sym->key); + +#ifdef OLDRALLOC + /* if the result of this one needs registers + and does not have it then assign it right + away */ + if (options.oldralloc && IC_RESULT (ic) && + !(SKIP_IC2 (ic) || ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || ic->op == RETURN) && + IS_SYMOP (IC_RESULT (ic)) && (result = OP_SYMBOL (IC_RESULT (ic))) && /* has a result */ + result->liveTo > ic->seq && /* and will live beyond this */ + result->liveTo <= ebp->lSeq && /* does not go beyond this block */ + result->liveFrom == ic->seq && /* does not start before here */ + result->regType == sym->regType && /* same register types */ + result->nRegs && /* which needs registers */ + !result->isspilt && /* and does not already have them */ + !result->remat && !bitVectBitValue (_G.regAssigned, result->key) && + /* the number of free regs + number of regs in this LR + can accomodate the what result Needs */ + ((nfreeRegsType (result->regType) + sym->nRegs) >= result->nRegs)) + { + for (i = 0; i < result->nRegs; i++) + { + if (i < sym->nRegs) + result->regs[i] = sym->regs[i]; + else + result->regs[i] = getRegGpr (ic, ebp, result); + + /* if the allocation failed which means + this was spilt then break */ + if (!result->regs[i]) + { + wassert (0); + assert (0); + break; + } + } + + _G.regAssigned = bitVectSetBit (_G.regAssigned, result->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, result->key); + } +#endif + + /* free the remaining */ + for (; i < sym->nRegs; i++) + { + if (psym) + { + if (!symHasReg (psym, sym->regs[i])) + freeReg (sym->regs[i]); + } + else + freeReg (sym->regs[i]); + } + } + } +} + +/** Reassign this to registers. + */ +static void +reassignLR (operand *op) +{ + symbol *sym = OP_SYMBOL (op); + int i; + + D (D_ALLOC, ("reassingLR: on sym %p\n", sym)); + + /* not spilt any more */ + sym->isspilt = sym->spillA = sym->blockSpil = sym->remainSpil = 0; + bitVectUnSetBit (_G.spiltSet, sym->key); + + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + _G.blockSpil--; + + for (i = 0; i < sym->nRegs; i++) + sym->regs[i]->isFree = 0; +} + +#ifdef OLDRALLOC +/** Determines if allocating will cause a spill. + */ +static int +willCauseSpill (int nr, int rt) +{ + /* first check if there are any avlb registers + of te type required */ + if (nFreeRegs (0) >= nr) + return 0; + + /* it will cause a spil */ + return 1; +} +#endif + +#ifdef OLDRALLOC +/** The allocator can allocate same registers to result and operand, + if this happens make sure they are in the same position as the operand + otherwise chaos results. +*/ +static int +positionRegs (symbol * result, symbol * opsym) +{ + int count = min (result->nRegs, opsym->nRegs); + int i, j = 0, shared = 0; + int change = 0; + + D (D_ALLOC, ("positionRegs: on result %p opsym %p line %u\n", result, opsym, lineno)); + + /* if the result has been spilt then cannot share */ + if (opsym->isspilt) + return 0; +again: + shared = 0; + /* first make sure that they actually share */ + + for (i = 0; i < count; i++) + { + for (j = 0; j < count; j++) + { + if (result->regs[i] == opsym->regs[j] && i != j) + { + shared = 1; + goto xchgPositions; + } + } + } +xchgPositions: + if (shared) + { + reg_info *tmp = result->regs[i]; + result->regs[i] = result->regs[j]; + result->regs[j] = tmp; + change++; + goto again; + } + return change; +} +#endif + +#ifdef OLDRALLOC +/** Try to allocate a pair of registers to the symbol. + */ +bool +tryAllocatingRegPair (symbol * sym) +{ + int i; + wassert (sym->nRegs == 2); + for (i = C_IDX; i < _G.nRegs; i += 2) + { + if ((regsZ80[i].isFree) && (regsZ80[i + 1].isFree)) + { + regsZ80[i].isFree = 0; + sym->regs[0] = ®sZ80[i]; + regsZ80[i + 1].isFree = 0; + sym->regs[1] = ®sZ80[i + 1]; + sym->regType = REG_PAIR; + + if (currFunc) + { + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i); + currFunc->regsUsed = bitVectSetBit (currFunc->regsUsed, i + 1); + } + D (D_ALLOC, ("tryAllocatingRegPair: succeded for sym %p\n", sym)); + return TRUE; + } + } + D (D_ALLOC, ("tryAllocatingRegPair: failed on sym %p\n", sym)); + return FALSE; +} +#endif +/*------------------------------------------------------------------*/ +/* verifyRegsAssigned - make sure an iTemp is properly initialized; */ +/* it should either have registers or have beed spilled. Otherwise, */ +/* there was an uninitialized variable, so just spill this to get */ +/* the operand in a valid state. */ +/*------------------------------------------------------------------*/ +static void +verifyRegsAssigned (operand *op, iCode *ic) +{ + symbol *sym; + + if (!op) + return; + if (!IS_ITEMP (op)) + return; + + sym = OP_SYMBOL (op); + if (sym->isspilt) + return; + if (!sym->nRegs) + return; + if (sym->regs[0]) + return; + + // Don't warn for new allocator, since this is now used by default. + if (options.oldralloc) + werrorfl (ic->filename, ic->lineno, W_LOCAL_NOINIT, sym->prereqv ? sym->prereqv->name : sym->name); + z80SpillThis (sym); +} + +#ifdef OLDRALLOC +/** Serially allocate registers to the variables. + This is the main register allocation function. It is called after + packing. + */ +static void +serialRegAssign (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + { + wassert (0); + reassignLR (IC_LEFT (ic)); + } + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + bitVect *spillable; + int willCS; + int j; + + D (D_ALLOC, ("serialRegAssign: in loop on result %p %s\n", sym, sym->name)); + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers + or will not live beyond this instructions */ + if (!sym->nRegs || sym->isspilt || bitVectBitValue (_G.regAssigned, sym->key) || sym->liveTo <= ic->seq) + { + D (D_ALLOC, ("serialRegAssign: won't live long enough.\n")); + continue; + } + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + D (D_ALLOC, ("serialRegAssign: \"spilling to be safe.\"\n")); + z80SpillThis (sym); + continue; + } + /* if trying to allocate this will cause + a spill and there is nothing to spill + or this one is rematerializable then + spill this one */ + willCS = willCauseSpill (sym->nRegs, sym->regType); + spillable = computeSpillable (ic); + if (sym->remat || (willCS && bitVectIsZero (spillable))) + { + + D (D_ALLOC, ("serialRegAssign: \"remat spill\"\n")); + z80SpillThis (sym); + continue; + + } + + /* If the live range preceeds the point of definition + then ideally we must take into account registers that + have been allocated after sym->liveFrom but freed + before ic->seq. This is complicated, so spill this + symbol instead and let fillGaps handle the allocation. */ + if (sym->liveFrom < ic->seq) + { + z80SpillThis (sym); + continue; + } + + /* if it has a spillocation & is used less than + all other live ranges then spill this */ + if (willCS) + { + if (sym->usl.spillLoc) + { + symbol *leastUsed = leastUsedLR (liveRangesWith (spillable, + allLRs, ebbs[i], ic)); + if (leastUsed && leastUsed->used > sym->used) + { + z80SpillThis (sym); + continue; + } + } + else + { + /* if none of the liveRanges have a spillLocation then better + to spill this one than anything else already assigned to registers */ + if (liveRangesWith (spillable, noSpilLoc, ebbs[i], ic)) + { + /* if this is local to this block then we might find a block spil */ + if (!(sym->liveFrom >= ebbs[i]->fSeq && sym->liveTo <= ebbs[i]->lSeq)) + { + z80SpillThis (sym); + continue; + } + } + } + } + + /* else we assign registers to it */ + _G.regAssigned = bitVectSetBit (_G.regAssigned, sym->key); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + + /* Special case: Try to fit into a reg pair if + available */ + D (D_ALLOC, ("serialRegAssign: actually allocing regs!\n")); + if ((sym->nRegs == 2) && tryAllocatingRegPair (sym)) + { + } + else + { + for (j = 0; j < sym->nRegs; j++) + { + sym->regs[j] = getRegGpr (ic, ebbs[i], sym); + + /* if the allocation falied which means + this was spilt then break */ + if (!sym->regs[j]) + { + D (D_ALLOC, ("Couldnt alloc (spill)\n")) break; + } + } + /* Make sure we didn't allocate a register pair with bytes swapped */ + if (sym->nRegs == 2 && sym->regs[0] == sym->regs[1] + 1 && sym->regs[0] != ®sZ80[2]) + { + freeReg (sym->regs[0]); + freeReg (sym->regs[1]); + if (!tryAllocatingRegPair (sym)) + wassertl (0, "Failed to swap register pair bytes back."); + } + } + /* if it shares registers with operands make sure + that they are in the same position */ + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->nRegs && ic->op != '=') + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_LEFT (ic))); + /* do the same for the right operand */ + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->nRegs) + positionRegs (OP_SYMBOL (IC_RESULT (ic)), OP_SYMBOL (IC_RIGHT (ic))); + + } + } + } + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } + +} +#endif + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* fillGaps - Try to fill in the Gaps left by Pass1 */ +/*-----------------------------------------------------------------*/ +static void +fillGaps () +{ + symbol *sym = NULL; + int key = 0; + + if (getenv ("DISABLE_FILL_GAPS")) + return; + + /* look for livernages that was spilt by the allocator */ + for (sym = hTabFirstItem (liveRanges, &key); sym; sym = hTabNextItem (liveRanges, &key)) + { + + int i; + int pdone = 0; + + if (sym->accuse == ACCUSE_SCRATCH) + { + sym->nRegs = getSize (sym->type); + sym->regs[0] = regsZ80 + L_IDX; + sym->regs[1] = regsZ80 + H_IDX; + sym->accuse = 0; + sym->isspilt = FALSE; + continue; + } + + if (!sym->spillA || !sym->clashes || sym->remat) + continue; + + /* find the liveRanges this one clashes with, that are + still assigned to registers & mark the registers as used */ + for (i = 0; i < sym->clashes->size; i++) + { + int k; + symbol *clr; + + if (bitVectBitValue (sym->clashes, i) == 0 || /* those that clash with this */ + bitVectBitValue (_G.totRegAssigned, i) == 0) /* and are still assigned to registers */ + continue; + + clr = hTabItemWithKey (liveRanges, i); + assert (clr); + + /* mark these registers as used */ + for (k = 0; k < clr->nRegs; k++) + useReg (clr->regs[k]); + } + + if (willCauseSpill (sym->nRegs, sym->regType)) + { + /* NOPE :( clear all registers & and continue */ + freeAllRegs (); + continue; + } + + /* THERE IS HOPE !!!! */ + for (i = 0; i < sym->nRegs; i++) + { + sym->regs[i] = getRegGprNoSpil (); + } + + /* for all its definitions check if the registers + allocated needs positioning NOTE: we can position + only ONCE if more than One positioning required + then give up */ + sym->isspilt = 0; + for (i = 0; i < sym->defs->size; i++) + { + if (bitVectBitValue (sym->defs, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (SKIP_IC (ic)) + continue; + assert (isSymbolEqual (sym, OP_SYMBOL (IC_RESULT (ic)))); /* just making sure */ + /* if left is assigned to registers */ + if (IS_SYMOP (IC_LEFT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_LEFT (ic))->key)) + { + pdone += positionRegs (sym, OP_SYMBOL (IC_LEFT (ic))); + } + if (IS_SYMOP (IC_RIGHT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RIGHT (ic))->key)) + { + pdone += positionRegs (sym, OP_SYMBOL (IC_RIGHT (ic))); + } + if (pdone > 1) + break; + } + } + for (i = 0; i < sym->uses->size; i++) + { + if (bitVectBitValue (sym->uses, i)) + { + iCode *ic; + if (!(ic = hTabItemWithKey (iCodehTab, i))) + continue; + if (SKIP_IC (ic)) + continue; + if (IS_ASSIGN_ICODE (ic)) + continue; + + /* if result is assigned to registers */ + if (IS_SYMOP (IC_RESULT (ic)) && bitVectBitValue (_G.totRegAssigned, OP_SYMBOL (IC_RESULT (ic))->key)) + { + pdone += positionRegs (sym, OP_SYMBOL (IC_RESULT (ic))); + } + if (pdone > 1) + break; + } + } + /* had to position more than once GIVE UP */ + if (pdone > 1) + { + /* UNDO all the changes we made to try this */ + sym->isspilt = 1; + for (i = 0; i < sym->nRegs; i++) + { + sym->regs[i] = NULL; + } + freeAllRegs (); + D (D_FILL_GAPS, + ("Fill Gap gave up due to positioning for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + continue; + } + D (D_FILL_GAPS, ("FILLED GAP for %s in function %s\n", sym->name, currFunc ? currFunc->name : "UNKNOWN")); + _G.totRegAssigned = bitVectSetBit (_G.totRegAssigned, sym->key); + sym->isspilt = sym->spillA = 0; + sym->usl.spillLoc->allocreq--; + freeAllRegs (); + } +} +#endif + +/*-----------------------------------------------------------------*/ +/* rUmaskForOp :- returns register mask for an operand */ +/*-----------------------------------------------------------------*/ +bitVect * +rUmaskForOp (const operand * op) +{ + bitVect *rumask; + symbol *sym; + int j; + + /* only temporaries are assigned registers */ + if (!IS_ITEMP (op)) + return NULL; + + sym = OP_SYMBOL_CONST (op); + + /* if spilt or no registers assigned to it + then nothing */ + if (sym->isspilt || !sym->nRegs) + return NULL; + + rumask = newBitVect (_G.nRegs + (IS_GB ? 0 : 2)); + + for (j = 0; j < sym->nRegs; j++) + { + if (!(sym->regs[j]) || sym->regs[j]->rIdx < 0 || sym->regs[j]->rIdx > CND_IDX) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "rUmaskForOp: Register not found"); + exit (0); + } + rumask = bitVectSetBit (rumask, sym->regs[j]->rIdx); + } + + return rumask; +} + +bitVect * +z80_rUmaskForOp (const operand * op) +{ + return rUmaskForOp (op); +} + +/** Returns bit vector of registers used in iCode. + */ +bitVect * +regsUsedIniCode (iCode * ic) +{ + bitVect *rmask = newBitVect (_G.nRegs + (IS_GB ? 0 : 2)); + + /* do the special cases first */ + if (ic->op == IFX) + { + rmask = bitVectUnion (rmask, rUmaskForOp (IC_COND (ic))); + goto ret; + } + + /* for the jumptable */ + if (ic->op == JUMPTABLE) + { + rmask = bitVectUnion (rmask, rUmaskForOp (IC_JTCOND (ic))); + + goto ret; + } + + /* of all other cases */ + if (IC_LEFT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_LEFT (ic))); + + if (IC_RIGHT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_RIGHT (ic))); + + if (IC_RESULT (ic)) + rmask = bitVectUnion (rmask, rUmaskForOp (IC_RESULT (ic))); + +ret: + return rmask; +} + +/** For each instruction will determine the regsUsed. + */ +static void +createRegMask (eBBlock ** ebbs, int count) +{ + int i; + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + + int j; + + if (SKIP_IC2 (ic) || !ic->rlive) + continue; + + /* first mark the registers used in this + instruction */ + + ic->rSurv = newBitVect(port->num_regs); + ic->rUsed = regsUsedIniCode (ic); + _G.funcrUsed = bitVectUnion (_G.funcrUsed, ic->rUsed); + + /* now create the register mask for those + registers that are in use : this is a + super set of ic->rUsed */ + ic->rMask = newBitVect (_G.nRegs + 1 + (IS_GB ? 0 : 2)); + + /* for all live Ranges alive at this point */ + for (j = 1; j < ic->rlive->size; j++) + { + symbol *sym; + int k; + + /* if not alive then continue */ + if (!bitVectBitValue (ic->rlive, j)) + continue; + + /* find the live range we are interested in */ + if (!(sym = hTabItemWithKey (liveRanges, j))) + { + werror (E_INTERNAL_ERROR, __FILE__, __LINE__, "createRegMask cannot find live range"); + exit (0); + } + + /* if no register assigned to it */ + if (!sym->nRegs || sym->isspilt) + continue; + + /* for all the registers allocated to it */ + for (k = 0; k < sym->nRegs; k++) + { + if (!sym->regs[k]) + continue; + ic->rMask = bitVectSetBit (ic->rMask, sym->regs[k]->rIdx); + if (sym->liveTo != ic->key) + ic->rSurv = bitVectSetBit (ic->rSurv, sym->regs[k]->rIdx); + } + } + } + } +} + +#if 0 +/** Returns the rematerialized string for a remat var. + */ +static char * +rematStr (symbol * sym) +{ + iCode *ic = sym->rematiCode; + int offset = 0; + + while (1) + { + /* if plus adjust offset to right hand side */ + if (ic->op == '+') + { + offset += (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* if minus adjust offset to right hand side */ + if (ic->op == '-') + { + offset -= (int) operandLitValue (IC_RIGHT (ic)); + ic = OP_SYMBOL (IC_LEFT (ic))->rematiCode; + continue; + } + + /* cast then continue */ + if (IS_CAST_ICODE (ic)) + { + ic = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + continue; + } + /* we reached the end */ + break; + } + + if (offset) + { + SNPRINTF (buffer, sizeof (buffer), + "(%s %c 0x%04x)", OP_SYMBOL (IC_LEFT (ic))->rname, offset >= 0 ? '+' : '-', abs (offset) & 0xffff); + } + else + { + strncpyz (buffer, OP_SYMBOL (IC_LEFT (ic))->rname, sizeof (buffer)); + } + return buffer; +} +#endif + +/*-----------------------------------------------------------------*/ +/* regTypeNum - computes the type & number of registers required */ +/*-----------------------------------------------------------------*/ +static void +regTypeNum (void) +{ + symbol *sym; + int k; + + /* for each live range do */ + for (sym = hTabFirstItem (liveRanges, &k); sym; sym = hTabNextItem (liveRanges, &k)) + { + /* if used zero times then no registers needed */ + if ((sym->liveTo - sym->liveFrom) == 0 && getSize (sym->type) <= 4) + continue; + else if ((sym->liveTo - sym->liveFrom) == 0 && bitVectnBitsOn (sym->defs) <= 1) + { + iCode *dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (sym->defs)); + if (!dic || dic->op != CALL && dic->op != PCALL) + continue; + } + + D (D_ALLOC, ("regTypeNum: loop on sym %p\n", sym)); + + /* if the live range is a temporary */ + if (sym->isitmp) + { + /* if the type is marked as a conditional */ + if (sym->regType == REG_CND) + continue; + + /* if used in return only then we don't + need registers */ + if (sym->ruonly || sym->accuse) + { + if (IS_AGGREGATE (sym->type) || sym->isptr) + sym->type = aggrToPtr (sym->type, FALSE); + continue; + } + + /* if not then we require registers */ + D (D_ALLOC, + ("regTypeNum: isagg %u nRegs %u type %p\n", IS_AGGREGATE (sym->type) || sym->isptr, sym->nRegs, sym->type)); + sym->nRegs = + ((IS_AGGREGATE (sym->type) + || sym->isptr) ? getSize (sym->type = aggrToPtr (sym->type, FALSE)) : getSize (sym->type)); + D (D_ALLOC, ("regTypeNum: setting nRegs of %s (%p) to %u\n", sym->name, sym, sym->nRegs)); + + D (D_ALLOC, ("regTypeNum: setup to assign regs sym %p\n", sym)); + + if (sym->nRegs > 8) + { + fprintf (stderr, "allocated more than 8 registers for type "); + printTypeChain (sym->type, stderr); + fprintf (stderr, "\n"); + } + + /* determine the type of register required */ + /* Always general purpose */ + sym->regType = REG_GPR; + } + else + { + /* for the first run we don't provide */ + /* registers for true symbols we will */ + /* see how things go */ + D (D_ALLOC, ("regTypeNum: #2 setting num of %p to 0\n", sym)); + sym->nRegs = 0; + } + } +} + +/** Mark all registers as free. + */ +static void +freeAllRegs () +{ + int i; + + D (D_ALLOC, ("freeAllRegs: running.\n")); + + for (i = C_IDX; i < _G.nRegs; i++) + regsZ80[i].isFree = 1; +} + +/*-----------------------------------------------------------------*/ +/* deallocStackSpil - this will set the stack pointer back */ +/*-----------------------------------------------------------------*/ +static +DEFSETFUNC (deallocStackSpil) +{ + symbol *sym = item; + + deallocLocal (sym); + return 0; +} + +/** Register reduction for assignment. + */ +static int +packRegsForAssign (iCode * ic, eBBlock * ebp) +{ + iCode *dic, *sic; + + D (D_ALLOC, ("packRegsForAssign: running on ic %p\n", ic)); + + if (!IS_ITEMP (IC_RIGHT (ic)) || OP_SYMBOL (IC_RIGHT (ic))->isind || OP_LIVETO (IC_RIGHT (ic)) > ic->seq) + return 0; + + /* Avoid having multiple named address spaces in one iCode. */ + if (IS_SYMOP (IC_RESULT (ic)) && SPEC_ADDRSPACE (OP_SYMBOL (IC_RESULT (ic))->etype)) + return 0; + + /* find the definition of iTempNN scanning backwards if we find a + a use of the true symbol in before we find the definition then + we cannot */ + for (dic = ic->prev; dic; dic = dic->prev) + { + /* PENDING: Don't pack across function calls. */ + if (dic->op == CALL || dic->op == PCALL || dic->op == INLINEASM || dic->op == CRITICAL || dic->op == ENDCRITICAL) + { + dic = NULL; + break; + } + + if (SKIP_IC2 (dic)) + continue; + + if (dic->op == IFX) + { + if (IS_SYMOP (IC_COND (dic)) && + (IC_COND (dic)->key == IC_RESULT (ic)->key || IC_COND (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + } + else + { + if (IS_TRUE_SYMOP (IC_RESULT (dic)) && IS_OP_VOLATILE (IC_RESULT (dic))) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RIGHT (ic)->key) + { + if (POINTER_SET (dic)) + dic = NULL; + + break; + } + + if (IS_SYMOP (IC_RIGHT (dic)) && + (IC_RIGHT (dic)->key == IC_RESULT (ic)->key || IC_RIGHT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_LEFT (dic)) && + (IC_LEFT (dic)->key == IC_RESULT (ic)->key || IC_LEFT (dic)->key == IC_RIGHT (ic)->key)) + { + dic = NULL; + break; + } + + if (IS_SYMOP (IC_RESULT (dic)) && IC_RESULT (dic)->key == IC_RESULT (ic)->key) + { + dic = NULL; + break; + } + } + } + + if (!dic) + return 0; /* did not find */ + + /* if assignment then check that right is not a bit */ + if (ASSIGNMENT (ic) && !POINTER_SET (ic)) + { + sym_link *etype = operandType (IC_RESULT (dic)); + if (IS_BITFIELD (etype)) + { + /* if result is a bit too then it's ok */ + etype = operandType (IC_RESULT (ic)); + if (!IS_BITFIELD (etype)) + { + return 0; + } + } + } + + /* if the result is on stack or iaccess then it must be + the same atleast one of the operands */ + if (OP_SYMBOL (IC_RESULT (ic))->onStack || OP_SYMBOL (IC_RESULT (ic))->iaccess) + { + /* the operation has only one symbol + operator then we can pack */ + if ((IC_LEFT (dic) && !IS_SYMOP (IC_LEFT (dic))) || (IC_RIGHT (dic) && !IS_SYMOP (IC_RIGHT (dic)))) + goto pack; + + if (!((IC_LEFT (dic) && + IC_RESULT (ic)->key == IC_LEFT (dic)->key) || (IC_RIGHT (dic) && IC_RESULT (ic)->key == IC_RIGHT (dic)->key))) + return 0; + } + +pack: + /* Keep assignment if it is an sfr write - not all of code generation can deal with result in sfr */ + if (IC_RESULT (ic) && IS_TRUE_SYMOP (IC_RESULT (ic)) && SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype) && IN_REGSP (SPEC_OCLS (OP_SYMBOL (IC_RESULT (ic))->etype)) && + (dic->op == LEFT_OP || dic->op == RIGHT_OP)) + return 0; + + /* found the definition */ + + /* delete from liverange table also + delete from all the points inbetween and the new + one */ + for (sic = dic; sic != ic; sic = sic->next) + { + bitVectUnSetBit (sic->rlive, IC_RESULT (ic)->key); + if (IS_ITEMP (IC_RESULT (dic))) + bitVectSetBit (sic->rlive, IC_RESULT (dic)->key); + } + + /* replace the result with the result of */ + /* this assignment and remove this assignment */ + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + IC_RESULT (dic) = IC_RESULT (ic); + + if (IS_ITEMP (IC_RESULT (dic)) && OP_SYMBOL (IC_RESULT (dic))->liveFrom > dic->seq) + { + OP_SYMBOL (IC_RESULT (dic))->liveFrom = dic->seq; + } + + remiCodeFromeBBlock (ebp, ic); + // PENDING: Check vs mcs51 + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (ic))->defs, ic->key); + hTabDeleteItem (&iCodehTab, ic->key, ic, DELETE_ITEM, NULL); + OP_DEFS (IC_RESULT (dic)) = bitVectSetBit (OP_DEFS (IC_RESULT (dic)), dic->key); + return 1; +} + +/** Scanning backwards looks for first assig found. + */ +iCode * +findAssignToSym (operand * op, iCode * ic) +{ + iCode *dic; + + for (dic = ic->prev; dic; dic = dic->prev) + { + + /* if definition by assignment */ + if (dic->op == '=' && !POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + /* && IS_TRUE_SYMOP(IC_RIGHT(dic)) */ + { + + /* we are interested only if defined in far space */ + /* or in stack space in case of + & - */ + + /* if assigned to a non-symbol then return + true */ + if (!IS_SYMOP (IC_RIGHT (dic))) + break; + + /* if the symbol is in far space then + we should not */ + if (isOperandInFarSpace (IC_RIGHT (dic))) + return NULL; + + /* for + & - operations make sure that + if it is on the stack it is the same + as one of the three operands */ + if ((ic->op == '+' || ic->op == '-') && OP_SYMBOL (IC_RIGHT (dic))->onStack) + { + + if (IC_RESULT (ic)->key != IC_RIGHT (dic)->key && + IC_LEFT (ic)->key != IC_RIGHT (dic)->key && IC_RIGHT (ic)->key != IC_RIGHT (dic)->key) + return NULL; + } + + break; + + } + + /* if we find an usage then we cannot delete it */ + if (IC_LEFT (dic) && IC_LEFT (dic)->key == op->key) + return NULL; + + if (IC_RIGHT (dic) && IC_RIGHT (dic)->key == op->key) + return NULL; + + if (POINTER_SET (dic) && IC_RESULT (dic)->key == op->key) + return NULL; + } + + /* now make sure that the right side of dic + is not defined between ic & dic */ + if (dic) + { + iCode *sic = dic->next; + + for (; sic != ic; sic = sic->next) + if (IC_RESULT (sic) && IC_RESULT (sic)->key == IC_RIGHT (dic)->key) + return NULL; + } + + return dic; + + +} + +#if !DISABLE_PACKREGSFORSUPPORT +// PENDING + +/*-----------------------------------------------------------------*/ +/* packRegsForSupport :- reduce some registers for support calls */ +/*-----------------------------------------------------------------*/ +static int +packRegsForSupport (iCode * ic, eBBlock * ebp) +{ + int change = 0; + /* for the left & right operand :- look to see if the + left was assigned a true symbol in far space in that + case replace them */ + D (D_ALLOC, ("packRegsForSupport: running on ic %p\n", ic)); + + if (IS_ITEMP (IC_LEFT (ic)) && OP_SYMBOL (IC_LEFT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_LEFT (ic), ic); + iCode *sic; + + if (!dic) + goto right; + + /* found it we need to remove it from the + block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_LEFT (ic)->key); + + IC_LEFT (ic)->operand.symOperand = IC_RIGHT (dic)->operand.symOperand; + IC_LEFT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + // PENDING: Check vs mcs51 + change++; + } + + /* do the same for the right operand */ +right: + if (!change && IS_ITEMP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->liveTo <= ic->seq) + { + iCode *dic = findAssignToSym (IC_RIGHT (ic), ic); + iCode *sic; + + if (!dic) + return change; + + /* found it we need to remove it from the block */ + for (sic = dic; sic != ic; sic = sic->next) + bitVectUnSetBit (sic->rlive, IC_RIGHT (ic)->key); + + IC_RIGHT (ic)->operand.symOperand = IC_RIGHT (dic)->operand.symOperand; + IC_RIGHT (ic)->key = IC_RIGHT (dic)->operand.symOperand->key; + + remiCodeFromeBBlock (ebp, dic); + bitVectUnSetBit (OP_SYMBOL (IC_RESULT (dic))->defs, dic->key); + hTabDeleteItem (&iCodehTab, dic->key, dic, DELETE_ITEM, NULL); + // PENDING: vs mcs51 + change++; + } + + return change; +} +#endif + +/** Will reduce some registers for single use. + */ +static iCode * +packRegsForOneuse (iCode * ic, operand * op, eBBlock * ebp) +{ + bitVect *uses; + iCode *dic, *sic; + + // PENDING: Disable + D (D_ALLOC, ("packRegsForOneUse: running on ic %p\n", ic)); + + /* if returning a literal then do nothing */ + if (!IS_SYMOP (op)) + return NULL; + + /* only upto 2 bytes since we cannot predict + the usage of b, & acc */ + if (getSize (operandType (op)) > 2) + return NULL; + + if (ic->op != RETURN && ic->op != SEND) + return NULL; + + /* this routine will mark the a symbol as used in one + instruction use only && if the defintion is local + (ie. within the basic block) && has only one definition && + that definiion is either a return value from a + function or does not contain any variables in + far space */ + uses = bitVectCopy (OP_USES (op)); + bitVectUnSetBit (uses, ic->key); /* take away this iCode */ + if (!bitVectIsZero (uses)) /* has other uses */ + return NULL; + + /* if it has only one defintion */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; /* has more than one definition */ + + /* get that definition */ + if (!(dic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_DEFS (op))))) + return NULL; + + /* found the definition now check if it is local */ + if (dic->seq < ebp->fSeq || dic->seq > ebp->lSeq) + return NULL; /* non-local */ + + /* now check if it is the return from a function call */ + if (dic->op == CALL || dic->op == PCALL) + { + if (ic->op != SEND && ic->op != RETURN && !POINTER_SET (ic) && !POINTER_GET (ic)) + { + OP_SYMBOL (op)->ruonly = 1; + return dic; + } + dic = dic->next; + } + + /* otherwise check that the definition does + not contain any symbols in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || IS_OP_RUONLY (IC_LEFT (ic)) || IS_OP_RUONLY (IC_RIGHT (ic))) + { + return NULL; + } + + /* if pointer set then make sure the pointer is one byte */ + if (POINTER_SET (dic)) + return NULL; + + if (POINTER_GET (dic)) + return NULL; + + sic = dic; + + /* also make sure the intervenening instructions + don't have any thing in far space */ + for (dic = dic->next; dic && dic != ic; dic = dic->next) + { + /* if there is an intervening function call then no */ + if (dic->op == CALL || dic->op == PCALL) + return NULL; + /* if pointer set then make sure the pointer + is one byte */ + if (POINTER_SET (dic)) + return NULL; + + if (POINTER_GET (dic)) + return NULL; + + /* if address of & the result is remat the okay */ + if (dic->op == ADDRESS_OF && OP_SYMBOL (IC_RESULT (dic))->remat) + continue; + + /* if left or right or result is in far space */ + if (isOperandInFarSpace (IC_LEFT (dic)) || + isOperandInFarSpace (IC_RIGHT (dic)) || + isOperandInFarSpace (IC_RESULT (dic)) || + IS_OP_RUONLY (IC_LEFT (dic)) || IS_OP_RUONLY (IC_RIGHT (dic)) || IS_OP_RUONLY (IC_RESULT (dic))) + { + return NULL; + } + } + + /* Fixes #2646174, but there might be a better way */ + if (ic->op == SEND) + return NULL; + + /* Fixes #2982135, but there might be a better way */ + if (ic->op == RETURN) + return NULL; + + OP_SYMBOL (op)->ruonly = 1; + return sic; +} + +/*-----------------------------------------------------------------*/ +/* isBitwiseOptimizable - requirements of JEAN LOUIS VERN */ +/*-----------------------------------------------------------------*/ +static bool +isBitwiseOptimizable (iCode * ic) +{ + sym_link *rtype = getSpec (operandType (IC_RIGHT (ic))); + + /* bitwise operations are considered optimizable + under the following conditions (Jean-Louis VERN) + + x & lit + bit & bit + bit & x + bit ^ bit + bit ^ x + x ^ lit + x | lit + bit | bit + bit | x + */ + if (IS_LITERAL (rtype)) + return TRUE; + return FALSE; +} + +static iCode * +packRegsForHLUse3 (iCode * lic, operand * op, eBBlock * ebp) +{ + int i, key; + symbol *sym; + iCode *ic, *dic; + bool isFirst = TRUE; + bool exstk = (currFunc && currFunc->stack > 127); + + D (D_PACK_HLUSE3, + ("Checking HL on %p lic key %u first def %u line %u:\n", OP_SYMBOL (op), lic->key, bitVectFirstBit (OP_DEFS (op)), + lic->lineno)); + if (D_PACK_HLUSE3) + piCode (lic, NULL); + + if (OP_SYMBOL (op)->accuse) + { + return NULL; + } + + if (OP_SYMBOL (op)->remat) + { + return NULL; + } + + /* Only defined once */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; + + if (!options.oldralloc ? getSize (operandType (op)) != 2 : getSize (operandType (op)) > 2) + return NULL; + + /* And this is the definition */ + if (bitVectFirstBit (OP_DEFS (op)) != lic->key) + return NULL; + + /* first check if any overlapping liverange has already been + assigned to DPTR */ + if (OP_SYMBOL (op)->clashes) + { + for (i = 0; i < OP_SYMBOL (op)->clashes->size; i++) + { + if (bitVectBitValue (OP_SYMBOL (op)->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->accuse == ACCUSE_SCRATCH) + { + return NULL; + } + } + } + } + + /* Nothing else that clashes with this is using the scratch + register. Scan through all of the intermediate instructions and + see if any of them could nuke HL. + */ + dic = ic = hTabFirstItemWK (iCodeSeqhTab, OP_SYMBOL (op)->liveFrom); + + for (; ic && ic->seq <= OP_SYMBOL (op)->liveTo; ic = hTabNextItem (iCodeSeqhTab, &key)) + { + if (D_PACK_HLUSE3) + piCode (ic, NULL); + D (D_PACK_HLUSE3, ("(On %p: op: %u next: %p)\n", ic, ic->op, ic->next)); + + if (isFirst) + { + isFirst = FALSE; + if (ic->op == ADDRESS_OF) + continue; + if (POINTER_GET (ic)) + continue; + if (ic->op == '=' && !POINTER_SET (ic)) + continue; + } + + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && (isOperandInDirSpace (IC_RESULT (ic)) || exstk)) + return NULL; + + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && isOperandInDirSpace (IC_LEFT (ic))) + return NULL; + + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && isOperandInDirSpace (IC_RIGHT (ic))) + return NULL; + + /* Handle the non left/right/result ones first */ + if (ic->op == IFX) + continue; + if (ic->op == JUMPTABLE) + return NULL; + + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == CAST) + continue; + + if (ic->op == IPUSH && isOperandEqual (op, IC_LEFT (ic))) + continue; + + if (ic->op == SEND && isOperandEqual (op, IC_LEFT (ic))) + continue; + + if (ic->op == CALL && isOperandEqual (op, IC_RESULT (ic))) + continue; + + if (ic->op == LEFT_OP && isOperandLiteral (IC_RIGHT (ic))) + continue; + + if (ic->op == '+' && (isOperandEqual (op, IC_LEFT (ic)) || isOperandEqual (op, IC_RIGHT (ic)))) + continue; + + if ((ic->op == '=' && !POINTER_SET (ic)) || + ic->op == UNARYMINUS || + ic->op == RETURN || + ic->op == RIGHT_OP || + (ic->op == '-' && getSize (operandType (IC_RESULT (ic))) == 1) || + ic->op == BITWISEAND || + ic->op == '|' || + ic->op == '>' || ic->op == '<' || ic->op == EQ_OP || (ic->op == '+' && getSize (operandType (IC_RESULT (ic))) == 1)) + /* 16 bit addition uses add hl, rr */ + continue; + + if (ic->op == '*' && isOperandEqual (op, IC_LEFT (ic))) + continue; + + if (POINTER_SET (ic) && isOperandEqual (op, IC_RESULT (ic))) + continue; + + if (POINTER_GET (ic) && isOperandEqual (op, IC_LEFT (ic))) + continue; + + if (IS_VALOP (IC_RIGHT (ic)) && (ic->op == EQ_OP || 0)) + { + continue; + } + + /* By default give up */ + D (D_PACK_HLUSE3, ("packRegsForHLUse3 giving up at icode %u\n", (unsigned) (ic->op))); + return NULL; + } + + D (D_PACK_HLUSE3, ("Succeeded!\n")) OP_SYMBOL (op)->accuse = ACCUSE_SCRATCH; + return dic; +} + +static iCode * +packRegsForIYUse (iCode * lic, operand * op, eBBlock * ebp) +{ + int i, key; + symbol *sym; + iCode *ic, *dic; + bitVect *uses; + + D (D_PACK_IY, + ("Checking IY on %p lic key %u first def %u line %u:\n", OP_SYMBOL (op), lic->key, bitVectFirstBit (OP_DEFS (op)), + lic->lineno)); + if (D_PACK_IY) + piCode (lic, NULL); + + if (OP_SYMBOL (op)->accuse) + { + return NULL; + } + + if (OP_SYMBOL (op)->remat) + { + return NULL; + } + + /* Only defined once */ + if (bitVectnBitsOn (OP_DEFS (op)) > 1) + return NULL; + + /* And this is the definition */ + if (bitVectFirstBit (OP_DEFS (op)) != lic->key) + return NULL; + + /* first check if any overlapping liverange has already been + assigned to DPTR */ + if (OP_SYMBOL (op)->clashes) + { + for (i = 0; i < OP_SYMBOL (op)->clashes->size; i++) + { + if (bitVectBitValue (OP_SYMBOL (op)->clashes, i)) + { + sym = hTabItemWithKey (liveRanges, i); + if (sym->accuse == ACCUSE_IY) + { + return NULL; + } + } + } + } + + /* Only a few instructions can load into IY */ + if (lic->op != '=') + { + return NULL; + } + + if (getSize (operandType (op)) != 2) + { + D (D_PACK_IY, (" + Dropping as operation has size is too big\n")); + return FALSE; + } + + /* Nothing else that clashes with this is using the scratch + register. Scan through all of the intermediate instructions and + see if any of them could nuke HL. + */ + dic = ic = hTabFirstItemWK (iCodeSeqhTab, OP_SYMBOL (op)->liveFrom); + uses = OP_USES (op); + + for (; ic && ic->seq <= OP_SYMBOL (op)->liveTo; ic = hTabNextItem (iCodeSeqhTab, &key)) + { + if (D_PACK_IY) + piCode (ic, NULL); + + if (ic->op == PCALL || ic->op == CALL || ic->op == JUMPTABLE) + return NULL; + + if (SKIP_IC2 (ic)) + continue; + + /* Be pessamistic. */ + if (ic->op == IFX) + return NULL; + + D (D_PACK_IY, (" op: %u uses %u result: %d left: %d right: %d\n", ic->op, bitVectBitValue (uses, ic->key), + IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) ? isOperandInDirSpace (IC_RESULT (ic)) : -1, + IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) ? isOperandInDirSpace (IC_LEFT (ic)) : -1, + IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) ? isOperandInDirSpace (IC_RIGHT (ic)) : -1)); + + if (IC_RESULT (ic) && IS_SYMOP (IC_RESULT (ic)) && isOperandInDirSpace (IC_RESULT (ic))) + return NULL; + + if (IC_RIGHT (ic) && IS_SYMOP (IC_RIGHT (ic)) && isOperandInDirSpace (IC_RIGHT (ic))) + return NULL; + + if (IC_LEFT (ic) && IS_SYMOP (IC_LEFT (ic)) && isOperandInDirSpace (IC_LEFT (ic))) + return NULL; + + /* Only certain rules will work against IY. Check if this iCode uses + this symbol. */ + if (bitVectBitValue (uses, ic->key) != 0) + { + if (ic->op == '=' && isOperandEqual (IC_RESULT (ic), op)) + continue; + + if (ic->op == GET_VALUE_AT_ADDRESS && isOperandEqual (IC_LEFT (ic), op)) + continue; + + if (isOperandEqual (IC_RESULT (ic), IC_LEFT (ic)) == FALSE) + return NULL; + + if (IC_RIGHT (ic) && IS_VALOP (IC_RIGHT (ic))) + { + if (ic->op == '+' || ic->op == '-') + { + /* Only works if the constant is small */ + if (operandLitValue (IC_RIGHT (ic)) < 4) + continue; + } + } + + return NULL; + } + else + { + /* This iCode doesn't use the sym. See if this iCode preserves IY. + */ + continue; + } + + /* By default give up */ + return NULL; + } + + D (D_PACK_IY, ("Succeeded IY!\n")); + + OP_SYMBOL (op)->accuse = ACCUSE_IY; + return dic; +} + +/** Returns TRUE if this operation can use acc and if it preserves the value. + */ +static bool +opPreservesA (iCode * uic) +{ + if (uic->op == IFX) + { + /* If we've gotten this far then the thing to compare must be + small enough and must be in A. + */ + return TRUE; + } + + if (uic->op == JUMPTABLE) + { + D (D_ACCUSE2, (" + Dropping as operation is a Jumptable\n")); + return FALSE; + } + + /* A pointer assign preserves A if A is the left value. */ + if (uic->op == '=' && POINTER_SET (uic) && !IY_RESERVED) + { + return TRUE; + } + + /* if the usage has only one operand then we can */ + /* PENDING: check */ + if (IC_LEFT (uic) == NULL || IC_RIGHT (uic) == NULL) + { + D (D_ACCUSE2, (" + Dropping as operation has only one operand\n")); + return FALSE; + } + + /* PENDING: check this rule */ + if (getSize (operandType (IC_RESULT (uic))) > 1) + { + D (D_ACCUSE2, (" + Dropping as operation has size is too big\n")); + return FALSE; + } + + return FALSE; +} + +/* Return TRUE if this operation can use A (it doesn't have to keep A unchanged) */ +static bool +opCanUseA (iCode * uic) +{ + if (uic->op == IFX) + { + /* If we've gotten this far then the thing to compare must be + small enough and must be in A. + */ + return TRUE; + } + if (uic->op == JUMPTABLE) + { + D (D_ACCUSE2, (" + Dropping as operation is a Jumptable\n")); + return FALSE; + } + + if (uic->op == '=' && !(IY_RESERVED && POINTER_SET (uic))) + { + return TRUE; + } + + if ((uic->op == RIGHT_OP || uic->op == LEFT_OP) && IS_OP_LITERAL (IC_RIGHT (uic))) + { + return TRUE; + } + + /* Somehow fails for unary minus. */ + if (IC_LEFT (uic) == NULL || IC_RIGHT (uic) == NULL) + { + D (D_ACCUSE2, (" + Dropping as operation has only one operand\n")); + return FALSE; + } + + if (IS_BITWISE_OP (uic)) + { + return TRUE; + } + + return FALSE; +} + +/** Returns true if this operand preserves the value of A. + */ +static bool +opIgnoresA (iCode * ic, iCode * uic) +{ + /* A increment of an iTemp by a constant is OK. */ + if (uic->op == '+' && IS_ITEMP (IC_LEFT (uic)) && IS_ITEMP (IC_RESULT (uic)) && IS_OP_LITERAL (IC_RIGHT (uic))) + { + unsigned int icount = (unsigned int) ulFromVal (OP_VALUE (IC_RIGHT (uic))); + + /* Being an ITEMP means that we're already a symbol. */ + if (icount == 1 && OP_KEY (IC_RESULT (uic)) == OP_KEY (IC_LEFT (uic))) + { + return TRUE; + } + } + else if (uic->op == '=' && !POINTER_SET (uic)) + { + /* If they are equal and get optimised out then things are OK. */ + if (isOperandEqual (IC_RESULT (uic), IC_RIGHT (uic))) + { + /* Straight assign is OK. */ + return TRUE; + } + } + + return FALSE; +} + +/** Pack registers for acc use. + When the result of this operation is small and short lived it may + be able to be stored in the accumulator. + + Note that the 'A preserving' list is currently emperical :) + */ +static void +packRegsForAccUse2 (iCode * ic) +{ + iCode *uic; + + D (D_ACCUSE2, ("packRegsForAccUse2: running on ic %p line %u\n", ic, ic->lineno)); + if (D_ACCUSE2) + piCode (ic, NULL); + + /* Filter out all but those 'good' commands */ + if (!POINTER_GET (ic) && + ic->op != '+' && + ic->op != '-' && + !IS_BITWISE_OP (ic) && + ic->op != '=' && + ic->op != EQ_OP && + ic->op != '<' && + ic->op != '>' && + ic->op != CAST && ic->op != GETHBIT && !((ic->op == LEFT_OP || ic->op == RIGHT_OP) && IS_OP_LITERAL (IC_RIGHT (ic)))) + { + D (D_ACCUSE2, (" + Dropping as not a 'good' source command\n")); + return; + } + + /* if + or - then it has to be one byte result. + MLH: Ok. + */ + if ((ic->op == '+' || ic->op == '-' || ic->op == LEFT_OP || ic->op == RIGHT_OP) && getSize (operandType (IC_RESULT (ic))) > 1) + { + D (D_ACCUSE2, (" + Dropping as it's a big one\n")); + return; + } + + /* has only one definition */ + if (bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) > 1) + { + D (D_ACCUSE2, (" + Dropping as it has more than one definition\n")); + return; + } + + /* Right. We may be able to propagate it through if: + For each in the chain of uses the intermediate is OK. + */ + /* Get next with 'uses result' bit on + If this->next == next + Validate use of next + If OK, increase count + */ + /* and the usage immediately follows this iCode */ + if (!(uic = hTabItemWithKey (iCodehTab, bitVectFirstBit (OP_USES (IC_RESULT (ic)))))) + { + D (D_ACCUSE2, (" + Dropping as usage does not follow first\n")); + return; + } + + { + /* Create a copy of the OP_USES bit vect */ + bitVect *uses = bitVectCopy (OP_USES (IC_RESULT (ic))); + int setBit; + iCode *scan = ic, *next; + + do + { + setBit = bitVectFirstBit (uses); + next = hTabItemWithKey (iCodehTab, setBit); + if (scan->next == next) + { + D (D_ACCUSE2_VERBOSE, (" ! Is next in line\n")); + + bitVectUnSetBit (uses, setBit); + + if (bitVectIsZero (uses) && opCanUseA (next)) + { + D (D_ACCUSE2, (" Arrived at last op safely.\n")); + break; + } + /* Still contigous. */ + if (!opPreservesA (next)) + { + D (D_ACCUSE2, (" + Dropping as operation doesn't preserve A\n")); + return; + } + D (D_ACCUSE2_VERBOSE, (" ! Preserves A, so continue scanning\n")); + scan = next; + } + /*else if (scan->next == NULL && bitVectnBitsOn (uses) == 1 && next != NULL) + { + if (next->prev == NULL) + { + if (!opPreservesA (next)) + { + D (D_ACCUSE2, (" + Dropping as operation doesn't preserve A #2\n")); + return; + } + bitVectUnSetBit (uses, setBit); + scan = next; + } + else + { + D (D_ACCUSE2, (" + Dropping as last in list and next doesn't start a block\n")); + return; + } + } //This caused bug #1292721 */ + else if (scan->next == NULL) + { + D (D_ACCUSE2, (" + Dropping as hit the end of the list\n")); + D (D_ACCUSE2, (" + Next in htab: %p\n", next)); + return; + } + else + { + if (opIgnoresA (ic, scan->next)) + { + /* Safe for now. */ + scan = scan->next; + D (D_ACCUSE2_VERBOSE, (" ! Op ignores A, so continue scanning\n")); + } + else + { + D (D_ACCUSE2, (" + Dropping as parts are not consecuitive and intermediate might use A\n")); + return; + } + } + } + while (!bitVectIsZero (uses)); + + OP_SYMBOL (IC_RESULT (ic))->accuse = ACCUSE_A; + return; + } +} + +/** Does some transformations to reduce register pressure. + */ +static void +packRegisters (eBBlock * ebp) +{ + iCode *ic; + int change = 0; + + D (D_ALLOC, ("packRegisters: entered.\n")); + + while (1 && !DISABLE_PACK_ASSIGN) + { + change = 0; + /* look for assignments of the form */ + /* iTempNN = TRueSym (someoperation) SomeOperand */ + /* .... */ + /* TrueSym := iTempNN:1 */ + for (ic = ebp->sch; ic; ic = ic->next) + { + /* find assignment of the form TrueSym := iTempNN:1 */ + if (ic->op == '=' && !POINTER_SET (ic)) + change += packRegsForAssign (ic, ebp); + } + if (!change) + break; + } + + for (ic = ebp->sch; ic; ic = ic->next) + { + D (D_ALLOC, ("packRegisters: looping on ic %p\n", ic)); + + /* Safe: address of a true sym is always constant. */ + /* if this is an itemp & result of a address of a true sym + then mark this as rematerialisable */ + if (ic->op == ADDRESS_OF && + IS_ITEMP (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) /* The receiving of the paramter isnot accounted for in DEFS */ && + IS_TRUE_SYMOP (IC_LEFT (ic)) && !OP_SYMBOL (IC_LEFT (ic))->onStack) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* Safe: just propagates the remat flag */ + /* if straight assignment then carry remat flag if this is the + only definition */ + if (ic->op == '=' && !POINTER_SET (ic) && IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_SYMBOL (IC_RESULT (ic))->defs) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter isnot accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + OP_SYMBOL (IC_RESULT (ic))->remat = OP_SYMBOL (IC_RIGHT (ic))->remat; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = OP_SYMBOL (IC_RIGHT (ic))->rematiCode; + } + + /* if cast to a generic pointer & the pointer being + cast is remat, then we can remat this cast as well */ + if (ic->op == CAST && + IS_SYMOP (IC_RIGHT (ic)) && OP_SYMBOL (IC_RIGHT (ic))->remat && + !isOperandGlobal (IC_RESULT (ic)) && bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1 && !IS_PARM (IC_RESULT (ic)) && /* The receiving of the paramter isnot accounted for in DEFS */ + !OP_SYMBOL (IC_RESULT (ic))->addrtaken) + { + sym_link *to_type = operandType (IC_LEFT (ic)); + sym_link *from_type = operandType (IC_RIGHT (ic)); + if ((IS_PTR (to_type) || IS_INT (to_type)) && IS_PTR (from_type)) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + } + + /* if this is a +/- operation with a rematerizable + then mark this as rematerializable as well */ + if ((ic->op == '+' || ic->op == '-') && + (IS_SYMOP (IC_LEFT (ic)) && + IS_ITEMP (IC_RESULT (ic)) && + IS_OP_LITERAL (IC_RIGHT (ic))) && + OP_SYMBOL (IC_LEFT (ic))->remat && + (!IS_SYMOP (IC_RIGHT (ic)) || !IS_CAST_ICODE (OP_SYMBOL (IC_RIGHT (ic))->rematiCode)) && + bitVectnBitsOn (OP_DEFS (IC_RESULT (ic))) == 1) + { + OP_SYMBOL (IC_RESULT (ic))->remat = 1; + OP_SYMBOL (IC_RESULT (ic))->rematiCode = ic; + OP_SYMBOL (IC_RESULT (ic))->usl.spillLoc = NULL; + } + + /* if the condition of an if instruction is defined in the + previous instruction then mark the itemp as a conditional */ + if ((IS_CONDITIONAL (ic) || + ((ic->op == BITWISEAND || + ic->op == '|' || + ic->op == '^') && + isBitwiseOptimizable (ic))) && + ic->next && ic->next->op == IFX && + bitVectnBitsOn (OP_USES (IC_RESULT (ic))) == 1 && + isOperandEqual (IC_RESULT (ic), IC_COND (ic->next)) && OP_SYMBOL (IC_RESULT (ic))->liveTo <= ic->next->seq) + { + + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + continue; + } + + /* some cases the redundant moves can + can be eliminated for return statements */ + if (ic->op == RETURN || ic->op == SEND) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* if pointer set & left has a size more than + one and right is not in far space */ + if (!DISABLE_PACK_ONE_USE && POINTER_SET (ic) && IS_SYMOP (IC_RESULT (ic)) && + /* MLH: no such thing. + !isOperandInFarSpace(IC_RIGHT(ic)) && */ + !OP_SYMBOL (IC_RESULT (ic))->remat && + !IS_OP_RUONLY (IC_RIGHT (ic)) && getSize (aggrToPtr (operandType (IC_RESULT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_RESULT (ic), ebp); + } + + /* if pointer get */ + if (!DISABLE_PACK_ONE_USE && POINTER_GET (ic) && IS_SYMOP (IC_LEFT (ic)) && + /* MLH: dont have far space + !isOperandInFarSpace(IC_RESULT(ic))&& */ + !OP_SYMBOL (IC_LEFT (ic))->remat && + !IS_OP_RUONLY (IC_RESULT (ic)) && getSize (aggrToPtr (operandType (IC_LEFT (ic)), FALSE)) > 1) + { + packRegsForOneuse (ic, IC_LEFT (ic), ebp); + } + + /* pack registers for accumulator use, when the result of an + arithmetic or bit wise operation has only one use, that use is + immediately following the defintion and the using iCode has + only one operand or has two operands but one is literal & the + result of that operation is not on stack then we can leave the + result of this operation in acc:b combination */ + + if ((options.oldralloc || !OPTRALLOC_HL) && !DISABLE_PACK_HL && IS_ITEMP (IC_RESULT (ic))) + if (!IS_GB && !IY_RESERVED) + packRegsForHLUse3 (ic, IC_RESULT (ic), ebp); + + if ((options.oldralloc || !OPTRALLOC_IY) && !DISABLE_PACK_IY && !IY_RESERVED && IS_ITEMP (IC_RESULT (ic)) && !IS_GB) + packRegsForIYUse (ic, IC_RESULT (ic), ebp); + + if (options.oldralloc && !DISABLE_PACK_ACC && IS_ITEMP (IC_RESULT (ic)) && + getSize (operandType (IC_RESULT (ic))) == 1) + packRegsForAccUse2 (ic); + } +} + +/** Joins together two byte constant pushes into one word push. + */ +static iCode * +joinPushes (iCode * lic) +{ + iCode *ic, *uic, *fic; + + for (ic = lic; ic; ic = ic->next) + { + int first, second; + value *val; + struct dbuf_s dbuf; + + uic = ic->next; + + /* Anything past this? */ + if (uic == NULL) + continue; + + /* This and the next pushes? */ + if (ic->op != IPUSH || uic->op != IPUSH) + continue; + + /* Find call */ + for(fic = uic; fic->op == IPUSH; fic = fic->next); + if (ic->op != CALL && fic->op != PCALL) + continue; + { + sym_link *dtype = operandType (IC_LEFT (fic)); + sym_link *ftype = IS_FUNCPTR (dtype) ? dtype->next : dtype; + if (IFFUNC_ISSMALLC (ftype)) /* SmallC calling convention pushes 8-bit values as 16 bit */ + continue; + } + + /* Both literals? */ + if (!IS_OP_LITERAL (IC_LEFT (ic)) || !IS_OP_LITERAL (IC_LEFT (uic))) + continue; + + /* Both characters? */ + if (getSize (operandType (IC_LEFT (ic))) != 1 || getSize (operandType (IC_LEFT (uic))) != 1) + { + continue; + } + /* Pull out the values, make a new type, and create the new iCode for it. + */ + first = (int) operandLitValue (IC_LEFT (ic)); + second = (int) operandLitValue (IC_LEFT (uic)); + + dbuf_init (&dbuf, 128); + dbuf_printf (&dbuf, "%uu", ((first << 8) | (second & 0xFF)) & 0xFFFFU); + val = constVal (dbuf_c_str (&dbuf)); + dbuf_destroy (&dbuf); + SPEC_NOUN (val->type) = V_INT; + IC_LEFT (ic) = operandFromValue (val); + + /* Now remove the second one from the list. */ + ic->next = uic->next; + if (uic->next) + { + /* Patch up the reverse link */ + uic->next->prev = ic; + } + } + + return lic; +} + +/** Serially allocate registers to the variables. + This was the main register allocation function. It is called after + packing. + In the new register allocator it only serves to mark variables for the new register allocator. + */ +static void +serialRegMark (eBBlock ** ebbs, int count) +{ + int i; + short int max_alloc_bytes = SHRT_MAX; // Byte limit. Set this to a low value to pass only few variables to the register allocator. This can be useful for debugging. + + /* for all blocks */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + /* for all instructions do */ + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + /* if this is an ipop that means some live + range will have to be assigned again */ + if (ic->op == IPOP) + { + wassert (0); + reassignLR (IC_LEFT (ic)); + } + + /* if result is present && is a true symbol */ + if (IC_RESULT (ic) && ic->op != IFX && IS_TRUE_SYMOP (IC_RESULT (ic))) + { + OP_SYMBOL (IC_RESULT (ic))->allocreq++; + } + + /* take away registers from live + ranges that end at this instruction */ + deassignLRs (ic, ebbs[i]); + + /* some don't need registers */ + if (SKIP_IC2 (ic) || + ic->op == JUMPTABLE || ic->op == IFX || ic->op == IPUSH || ic->op == IPOP || (IC_RESULT (ic) && POINTER_SET (ic))) + { + continue; + } + + /* now we need to allocate registers only for the result */ + if (IC_RESULT (ic)) + { + symbol *sym = OP_SYMBOL (IC_RESULT (ic)); + + D (D_ALLOC, ("serialRegAssign: in loop on result %p (%s)\n", sym, sym->name)); + + /* Make sure any spill location is definately allocated */ + if (sym->isspilt && !sym->remat && sym->usl.spillLoc && !sym->usl.spillLoc->allocreq) + { + sym->usl.spillLoc->allocreq++; + } + + /* if it does not need or is spilt + or is already assigned to registers (or marked for the new allocator) + or will not live beyond this instructions */ + if (!sym->nRegs || + sym->isspilt || bitVectBitValue (_G.regAssigned, sym->key) || sym->for_newralloc || (sym->liveTo <= ic->seq && (sym->nRegs <= 4 || ic->op != CALL && ic->op != PCALL))) + { + D (D_ALLOC, ("serialRegAssign: won't live long enough.\n")); + continue; + } + + /* if some liverange has been spilt at the block level + and this one live beyond this block then spil this + to be safe */ + if (_G.blockSpil && sym->liveTo > ebbs[i]->lSeq) + { + D (D_ALLOC, ("serialRegAssign: \"spilling to be safe.\"\n")); + sym->for_newralloc = 0; + z80SpillThis (sym); + continue; + } + + if (sym->usl.spillLoc && !sym->usl.spillLoc->_isparm && !USE_OLDSALLOC) // I have no idea where these spill locations come from. Sometime two symbols even have the same spill location, whic tends to mess up stack allocation. THose that come from previous iterations in this loop would be okay, but those from outside are a problem. + { + sym->usl.spillLoc = 0; + sym->isspilt = false; + } + + if (sym->nRegs > 4) /* TODO. Change this once we can allocate bigger variables (but still spill when its a big return value). */ + { + D (D_ALLOC, ("Spilling %s (too large)\n", sym->name)); + sym->for_newralloc = 0; + z80SpillThis (sym); + } + else if (max_alloc_bytes >= sym->nRegs) + { + sym->for_newralloc = 1; + max_alloc_bytes -= sym->nRegs; + } + else if (!sym->for_newralloc) + { + z80SpillThis (sym); + printf ("Spilt %s due to byte limit.\n", sym->name); + } + } + } + } +} + +void +Z80RegFix (eBBlock ** ebbs, int count) +{ + int i; + + /* Check for and fix any problems with uninitialized operands */ + for (i = 0; i < count; i++) + { + iCode *ic; + + if (ebbs[i]->noPath && (ebbs[i]->entryLabel != entryLabel && ebbs[i]->entryLabel != returnLabel)) + continue; + + for (ic = ebbs[i]->sch; ic; ic = ic->next) + { + deassignLRs (ic, ebbs[i]); + + if (SKIP_IC2 (ic)) + continue; + + if (ic->op == IFX) + { + verifyRegsAssigned (IC_COND (ic), ic); + continue; + } + + if (ic->op == JUMPTABLE) + { + verifyRegsAssigned (IC_JTCOND (ic), ic); + continue; + } + + verifyRegsAssigned (IC_RESULT (ic), ic); + verifyRegsAssigned (IC_LEFT (ic), ic); + verifyRegsAssigned (IC_RIGHT (ic), ic); + } + } +} + +void z80_init_asmops (void); + +#ifdef OLDRALLOC +/*-----------------------------------------------------------------*/ +/* Old, obsolete register allocator */ +/*-----------------------------------------------------------------*/ +void +z80_oldralloc (ebbIndex * ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + D (D_ALLOC, ("\n-> z80_oldralloc: entered for %s.\n", currFunc ? currFunc->name : "[no function]")); + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.totRegAssigned); + _G.stackExtend = _G.dataExtend = 0; + + if (IS_GB) + { + /* DE is required for the code gen. */ + _G.nRegs = 3; + regsZ80 = _gbz80_regs; + } + else + { + _G.nRegs = 5; + regsZ80 = _z80_regs; + } + + z80_init_asmops (); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + packRegisters (ebbs[i]); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* and serially allocate registers */ + serialRegAssign (ebbs, count); + + freeAllRegs (); + fillGaps (); + + /* if stack was extended then tell the user */ + if (_G.stackExtend) + { +/* werror(W_TOOMANY_SPILS,"stack", */ +/* _G.stackExtend,currFunc->name,""); */ + _G.stackExtend = 0; + } + + if (_G.dataExtend) + { +/* werror(W_TOOMANY_SPILS,"data space", */ +/* _G.dataExtend,currFunc->name,""); */ + _G.dataExtend = 0; + } + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + /* after that create the register mask + for each of the instruction */ + createRegMask (ebbs, count); + + /* now get back the chain */ + ic = iCodeLabelOptimize (iCodeFromeBBlock (ebbs, count)); + + ic = joinPushes (ic); + + /* redo that offsets for stacked automatic variables */ + redoStackOffsets (); + + genZ80Code (ic); + + /* free up any stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} +#endif + +/*-----------------------------------------------------------------*/ +/* New register allocator */ +/*-----------------------------------------------------------------*/ +void +z80_ralloc (ebbIndex *ebbi) +{ + eBBlock **ebbs = ebbi->bbOrder; + int count = ebbi->count; + iCode *ic; + int i; + + D (D_ALLOC, ("\n-> z80_ralloc: entered for %s.\n", currFunc ? currFunc->name : "[no function]")); + + setToNull ((void *) &_G.funcrUsed); + setToNull ((void *) &_G.totRegAssigned); + _G.stackExtend = _G.dataExtend = 0; + + if (IS_GB) + { + /* DE is required for the code gen. */ + _G.nRegs = GBZ80_MAX_REGS; + regsZ80 = _gbz80_regs; + } + else + { + _G.nRegs = Z80_MAX_REGS; + regsZ80 = _z80_regs; + } + + z80_init_asmops (); + + /* change assignments this will remove some + live ranges reducing some register pressure */ + for (i = 0; i < count; i++) + packRegisters (ebbs[i]); + + /* liveranges probably changed by register packing + so we compute them again */ + recomputeLiveRanges (ebbs, count, FALSE); + + if (options.dump_i_code) + dumpEbbsToFileExt (DUMP_PACK, ebbi); + + /* first determine for each live range the number of + registers & the type of registers required for each */ + regTypeNum (); + + /* Mark variables for assignment by the new allocator */ + serialRegMark (ebbs, count); + + joinPushes (iCodeLabelOptimize(iCodeFromeBBlock (ebbs, count))); + + /* The new register allocator invokes its magic */ + ic = z80_ralloc2_cc (ebbi); + + if (options.dump_i_code) + { + dumpEbbsToFileExt (DUMP_RASSGN, ebbi); + dumpLiveRanges (DUMP_LRANGE, liveRanges); + } + + genZ80Code (ic); + + /* free up any stackSpil locations allocated */ + applyToSet (_G.stackSpil, deallocStackSpil); + _G.slocNum = 0; + setToNull ((void *) &_G.stackSpil); + setToNull ((void *) &_G.spiltSet); + /* mark all registers as free */ + freeAllRegs (); + + return; +} + +/*-----------------------------------------------------------------*/ +/* assignRegisters - assigns registers to each live range as need */ +/*-----------------------------------------------------------------*/ +void +z80_assignRegisters (ebbIndex * ebbi) +{ +#ifdef OLDRALLOC + if (options.oldralloc) + z80_oldralloc (ebbi); + else +#endif + z80_ralloc (ebbi); +} + diff --git a/src/z80/ralloc.h b/src/z80/ralloc.h new file mode 100644 index 0000000..88e97d4 --- /dev/null +++ b/src/z80/ralloc.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + + SDCCralloc.h - header file register allocation + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1998) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ +#include "SDCCicode.h" +#include "SDCCBBlock.h" +#ifndef SDCCRALLOC_H +#define SDCCRALLOC_H 1 + +#define DEBUG_FAKE_EXTRA_REGS 0 + +#define USE_OLDSALLOC 0 // Change to 1 to use old stack allocator + +enum +{ + A_IDX = 0, + C_IDX, + B_IDX, + E_IDX, + D_IDX, + L_IDX, + H_IDX, + IYL_IDX, + IYH_IDX, +#if DEBUG_FAKE_EXTRA_REGS + M_IDX, + N_IDX, + O_IDX, + P_IDX, + Q_IDX, + R_IDX, + S_IDX, + T_IDX, +#endif + CND_IDX, + + // These pairs are for internal use in code generation only. + IY_IDX, + BC_IDX, + DE_IDX, + HL_IDX +}; + +enum +{ + REG_PTR = 1, + REG_GPR = 2, + REG_CND = 4, + REG_PAIR = 8 +}; + +/* definition for the registers */ +typedef struct reg_info +{ + short type; /* can have value + REG_GPR, REG_PTR or REG_CND */ + short rIdx; /* index into register table */ + char *name; /* name */ + unsigned isFree:1; /* is currently unassigned */ +} reg_info; + +extern reg_info *regsZ80; + +void assignRegisters (eBBlock **, int); +reg_info *regWithIdx (int); + +void z80_assignRegisters (ebbIndex *); +bitVect *z80_rUmaskForOp (const operand * op); + +void z80SpillThis (symbol *); +iCode *z80_ralloc2_cc(ebbIndex *ebbi); + +void Z80RegFix (eBBlock ** ebbs, int count); +#endif diff --git a/src/z80/ralloc2.cc b/src/z80/ralloc2.cc new file mode 100644 index 0000000..fe06563 --- /dev/null +++ b/src/z80/ralloc2.cc @@ -0,0 +1,1707 @@ +// Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2011 +// +// (c) 2010-2012 Goethe-Universität Frankfurt +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the +// Free Software Foundation; either version 2, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// An optimal, polynomial-time register allocator. + +// #define DEBUG_RALLOC_DEC // Uncomment to get debug messages while doing register allocation on the tree decomposition. +// #define DEBUG_RALLOC_DEC_ASS // Uncomment to get debug messages about assignments while doing register allocation on the tree decomposition (much more verbose than the one above). + +#include "SDCCralloc.hpp" +#include "SDCCsalloc.hpp" + +extern "C" +{ + #include "z80.h" + unsigned char dryZ80iCode (iCode * ic); + bool z80_assignment_optimal; + bool should_omit_frame_ptr; +} + +#define REG_A 0 +#define REG_C 1 +#define REG_B 2 +#define REG_E 3 +#define REG_D 4 +#define REG_L 5 +#define REG_H 6 +#define REG_IYL 7 +#define REG_IYH 8 + +template +float default_operand_cost(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + float c = 0.0f; + + operand_map_t::const_iterator oi, oi_end; + + var_t byteregs[4]; // Todo: Change this when sdcc supports variables larger than 4 bytes in registers. + unsigned short int size; + + if(o && IS_SYMOP(o)) + { + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + if(oi != oi_end) + { + var_t v = oi->second; + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), v)) + { + c += 1.0f; + byteregs[I[v].byte] = a.global[v]; + size = 1; + + while(++oi != oi_end) + { + v = oi->second; + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); + byteregs[I[v].byte] = a.global[v]; + size++; + } + + // Penalty for not placing 2- and 4-byte variables in register pairs + // Todo: Extend this once the register allcoator can use registers other than bc, de: + if ((size == 2 || size == 4) && + (byteregs[1] != byteregs[0] + 1 || (byteregs[0] != REG_C && byteregs[0] != REG_E && byteregs[0] != REG_L))) + c += 2.0f; + if (size == 4 && + (byteregs[3] != byteregs[2] + 1 || (byteregs[2] != REG_C && byteregs[2] != REG_E && byteregs[0] != REG_L))) + c += 2.0f; + + // Code generator cannot handle variables only partially in A. + if(size > 1) + for(unsigned short int i = 0; i < size; i++) + if(byteregs[i] == REG_A) + c += std::numeric_limits::infinity(); + + if(byteregs[0] == REG_A) + c -= 0.4f; + else if(OPTRALLOC_HL && byteregs[0] == REG_L) + c -= 0.1f; + else if((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) + c += 0.1f; + } + // Spilt. + else + { + c += OP_SYMBOL_CONST(o)->remat ? 1.5f : 4.0f; + while(++oi != oi_end) + { + v = oi->second; + c += (!std::binary_search(a.local.begin(), a.local.end(), v) ? 4.0f : std::numeric_limits::infinity()); + } + } + } + } + + return(c); +} + +// Check that the operand is either fully in registers or fully in memory. +template +static bool operand_sane(const operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return(true); + + operand_map_t::const_iterator oi, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + + if(oi == oi_end) + return(true); + + // In registers. + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + { + while(++oi != oi_end) + if(!std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + else + { + while(++oi != oi_end) + if(std::binary_search(a.local.begin(), a.local.end(), oi->second)) + return(false); + } + + return(true); +} + +template +static float default_instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + float c = 0.0f; + + const iCode *ic = G[i].ic; + + c += default_operand_cost(IC_RESULT(ic), a, i, G, I); + c += default_operand_cost(IC_LEFT(ic), a, i, G, I); + c += default_operand_cost(IC_RIGHT(ic), a, i, G, I); + + return(c); +} + +template +static bool inst_sane(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + // for a sequence of built-in SENDs, all of the SENDs must be sane + if (ic->op == SEND && ic->builtinSEND && ic->next->op == SEND && !inst_sane(a, *(adjacent_vertices(i, G).first), G, I)) + return(false); + + return(operand_sane(IC_RESULT(ic), a, i, G, I) && operand_sane(IC_LEFT(ic), a, i, G, I) && operand_sane(IC_RIGHT(ic), a, i, G, I)); +} + +// Treat assignment separately to handle coalescing. +template static float +assign_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + float c = 0.0f; + + const iCode *ic = G[i].ic; + + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + if(!right || !IS_SYMOP(right) || !result || !IS_SYMOP(result) || POINTER_GET(ic) || POINTER_SET(ic)) + return(default_instruction_cost(a, i, G, I)); + + reg_t byteregs[4] = {-1, -1, -1, -1}; // Todo: Change this when sdcc supports variables larger than 4 bytes in register allocation for z80. + + operand_map_t::const_iterator oi, oi_end; + + int size1 = 0, size2 = 0; + + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(right)->key); + if(oi != oi_end) + { + var_t v = oi->second; + + if(!std::binary_search(a.local.begin(), a.local.end(), v)) + return(default_instruction_cost(a, i, G, I)); + + c += 1.0f; + byteregs[I[v].byte] = a.global[v]; + size1 = 1; + + while(++oi != oi_end) + { + v = oi->second; + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); + byteregs[I[v].byte] = a.global[v]; + size1++; + } + + // Code generator cannot handle variables only partially in A. + if(size1 > 1) + for(unsigned short int i = 0; i < size1; i++) + if(byteregs[i] == REG_A) + c += std::numeric_limits::infinity(); + + if(byteregs[0] == REG_A) + c -= 0.4f; + else if((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) + c += 0.1f; + } + + if(!size1) + return(default_instruction_cost(a, i, G, I)); + + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oi != oi_end) + { + var_t v = oi->second; + + if(!std::binary_search(a.local.begin(), a.local.end(), v)) + return(default_instruction_cost(a, i, G, I)); + + c += 1.0f; + if(byteregs[I[v].byte] == a.global[v]) + c -= 2.0f; + size2 = 1; + + while(++oi != oi_end) + { + v = oi->second; + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); + if(byteregs[I[v].byte] == a.global[v]) + c -= 2.0f; + size2++; + } + + if(byteregs[0] == REG_A) + c -= 0.4f; + else if((OPTRALLOC_IY && byteregs[0] == REG_IYL) || byteregs[0] == REG_IYH) + c += 0.1f; + } + + if(!size2) + return(default_instruction_cost(a, i, G, I)); + + return(c); +} + +template static float +return_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + float c = 0.0f; + + const iCode *ic = G[i].ic; + + const operand *left = IC_LEFT(ic); + + if(!left || !IS_SYMOP(left)) + return(default_instruction_cost(a, i, G, I)); + + reg_t byteregs[4] = {-1, -1, -1, -1}; // Todo: Change this when sdcc supports variables larger than 4 bytes. + + operand_map_t::const_iterator oi, oi_end; + + int size = 0; + + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(left)->key); + if(oi != oi_end) + { + var_t v = oi->second; + + if(!std::binary_search(a.local.begin(), a.local.end(), v)) + return(default_instruction_cost(a, i, G, I)); + + c += 1.0f; + byteregs[I[v].byte] = a.global[v]; + size = 1; + + while(++oi != oi_end) + { + v = oi->second; + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); + byteregs[I[v].byte] = a.global[v]; + size++; + } + + if(byteregs[0] == REG_A) + c -= 0.4f; + + if(byteregs[0] == REG_L) + c -= 1.0f; + if(byteregs[1] == REG_H) + c -= 1.0f; + if(byteregs[2] == REG_E) + c -= 1.0f; + if(byteregs[3] == REG_D) + c -= 1.0f; + } + + return(c); +} + +template static float +call_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + float c = 0.0f; + + const iCode *ic = G[i].ic; + + const operand *result = IC_RESULT(ic); + + if(!result || !IS_SYMOP(result)) + return(default_instruction_cost(a, i, G, I)); + + reg_t byteregs[4] = {-1, -1, -1, -1}; // Todo: Change this when sdcc supports variables larger than 4 bytes. + + operand_map_t::const_iterator oi, oi_end; + + int size = 0; + + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oi != oi_end) + { + var_t v = oi->second; + + if(!std::binary_search(a.local.begin(), a.local.end(), v)) + return(default_instruction_cost(a, i, G, I)); + + c += 1.0f; + byteregs[I[v].byte] = a.global[v]; + size = 1; + + while(++oi != oi_end) + { + v = oi->second; + c += (std::binary_search(a.local.begin(), a.local.end(), v) ? 1.0f : std::numeric_limits::infinity()); + byteregs[I[v].byte] = a.global[v]; + size++; + } + + // Code generator cannot handle variables only partially in A. + if(size > 1) + for(unsigned short int i = 0; i < size; i++) + if(byteregs[i] == REG_A) + c += std::numeric_limits::infinity(); + + if(byteregs[0] == REG_A) + c -= 0.4f; + + if(byteregs[0] == REG_L) + c -= 1.0f; + if(byteregs[1] == REG_H) + c -= 1.0f; + if(byteregs[2] == REG_E) + c -= 1.0f; + if(byteregs[3] == REG_D) + c -= 1.0f; + } + + return(c); +} + +template static float +ifx_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(default_operand_cost(IC_COND(ic), a, i, G, I)); +} + +template static float +jumptab_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + return(default_operand_cost(IC_JTCOND(ic), a, i, G, I)); +} + +template +static void add_operand_conflicts_in_node(const cfg_node &n, I_t &I) +{ + const iCode *ic = n.ic; + + const operand *result = IC_RESULT(ic); + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + + if(!result || !IS_SYMOP(result)) + return; + + if(!(ic->op == UNARYMINUS || ic->op == '+' || ic->op == '-' || ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND)) + return; // Code generation can always handle all other operations. Todo: Handle ^, |, BITWISEAND and float UNARYMINUS there as well. + + operand_map_t::const_iterator oir, oir_end, oirs; + boost::tie(oir, oir_end) = n.operands.equal_range(OP_SYMBOL_CONST(result)->key); + if(oir == oir_end) + return; + + operand_map_t::const_iterator oio, oio_end; + + if(left && IS_SYMOP(left)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(left)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } + + if(right && IS_SYMOP(right)) + for(boost::tie(oio, oio_end) = n.operands.equal_range(OP_SYMBOL_CONST(right)->key); oio != oio_end; ++oio) + for(oirs = oir; oirs != oir_end; ++oirs) + { + var_t rvar = oirs->second; + var_t ovar = oio->second; + if(I[rvar].byte < I[ovar].byte) + boost::add_edge(rvar, ovar, I); + } +} + +// Return true, iff the operand is placed (partially) in r. +template +static bool operand_in_reg(const operand *o, reg_t r, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + if(r >= port->num_regs) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Return true, iff the operand is placed in a reg. +template +static bool operand_in_reg(const operand *o, const i_assignment_t &ia, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + for(reg_t r = 0; r < port->num_regs; r++) + if(oi->second == ia.registers[r][1] || oi->second == ia.registers[r][0]) + return(true); + + return(false); +} + +// Return true, iff the operand is placed in a reg. +template +static bool operand_byte_in_reg(const operand *o, int offset, reg_t r, const assignment &a, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi3, oi_end; + + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); offset && oi != oi_end; offset--, oi++); + + if(oi == oi_end) + return(false); + + return(a.global[oi->second] == r); +} + +// Return true, iff the operand is placed on the stack. +template +bool operand_on_stack(const operand *o, const assignment &a, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + if(OP_SYMBOL_CONST(o)->remat) + return(false); + + if(OP_SYMBOL_CONST(o)->_isparm && !IS_REGPARM (OP_SYMBOL_CONST(o)->etype)) + return(true); + + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + if(a.global[oi->second] < 0) + return(true); + + return(false); +} + +template +static bool operand_is_pair(const operand *o, const assignment &a, unsigned short int i, const G_t &G) +{ + if(!o || !IS_SYMOP(o)) + return(false); + + operand_map_t::const_iterator oi, oi2, oi3, oi_end; + boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); + if(oi == oi_end) + return(false); + oi2 = oi; + ++oi2; + if(oi2 == oi_end) + return(false); + oi3 = oi2; + ++oi3; + if(oi3 != oi_end) + return(false); + + if(a.global[oi->second] != REG_C && a.global[oi->second] != REG_E && a.global[oi->second] != REG_L && a.global[oi->second] != REG_IYL) + return(false); + if(a.global[oi->second] + 1 != a.global[oi2->second]) + return(false); + + return(true); +} + +template +static bool Ainst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + const i_assignment_t &ia = a.i_assignment; + + operand *const left = IC_LEFT(ic); + operand *const right = IC_RIGHT(ic); + const operand *const result = IC_RESULT(ic); + + if(ia.registers[REG_A][1] < 0) + return(true); // Register A not in use. + + // Some instructions don't touch registers. + if(SKIP_IC2(ic)) + return(true); + + bool exstk = (should_omit_frame_ptr || (currFunc && currFunc->stack > 127) || IS_GB); + + //std::cout << "Ainst_ok at " << G[i].ic->key << ": A = (" << ia.registers[REG_A][0] << ", " << ia.registers[REG_A][1] << "), inst " << i << ", " << ic->key << "\n"; + + // Check if the result of this instruction is placed in A. + bool result_in_A = operand_in_reg(IC_RESULT(ic), REG_A, ia, i, G); + + // Check if an input of this instruction is placed in A. + bool input_in_A; + switch(ic->op) + { + case IFX: + input_in_A = operand_in_reg(IC_COND(ic), REG_A, ia, i, G); + break; + case JUMPTABLE: + input_in_A = operand_in_reg(IC_JTCOND(ic), REG_A, ia, i, G); + break; + default: + input_in_A = operand_in_reg(left, REG_A, ia, i, G) || operand_in_reg(right, REG_A, ia, i, G); + break; + } + + // sfr access needs to go through a. + if(input_in_A && + (IS_TRUE_SYMOP (left) && IN_REGSP (SPEC_OCLS (OP_SYMBOL (left)->etype)) || + IS_TRUE_SYMOP (right) && IN_REGSP (SPEC_OCLS (OP_SYMBOL (right)->etype)))) + return(false); + + if (ic->op == '^' || ic->op == BITWISEAND || ic->op == '|' || ic->op == '~') // Codegen can handle it all. + return(true); + + if (ic->op == RIGHT_OP && getSize(operandType(result)) == 1 && IS_OP_LITERAL(right)) + return(true); + + // Can use non-destructive cp on == and < (> might swap operands). + if((ic->op == EQ_OP || ic->op == '<' && SPEC_USIGN(getSpec(operandType(left))) && SPEC_USIGN(getSpec(operandType(right)))) && + getSize(operandType(IC_LEFT(ic))) == 1 && ifxForOp (IC_RESULT(ic), ic) && operand_in_reg(left, REG_A, ia, i, G) && + (IS_OP_LITERAL (right) || operand_in_reg(right, REG_C, ia, i, G) || operand_in_reg(right, REG_B, ia, i, G) || operand_in_reg(right, REG_E, ia, i, G) || operand_in_reg(right, REG_D, ia, i, G) || operand_in_reg(right, REG_H, ia, i, G) || operand_in_reg(right, REG_L, ia, i, G))) + return(true); + + const cfg_dying_t &dying = G[i].dying; + const bool dying_A = result_in_A || dying.find(ia.registers[REG_A][1]) != dying.end() || dying.find(ia.registers[REG_A][0]) != dying.end(); + + if((ic->op == '+' || ic->op == '-' && !operand_in_reg(right, REG_A, ia, i, G) || ic->op == UNARYMINUS && !IS_GB) && + getSize(operandType(IC_RESULT(ic))) == 1 && dying_A) + return(true); + + if((ic->op == '+' || ic->op == '-' && !operand_in_reg(right, REG_A, ia, i, G) || ic->op == UNARYMINUS && !IS_GB || ic->op == '~') && // First byte of input and last byte of output may be in A. + IS_ITEMP(result) && dying_A && + (IS_ITEMP(left) || IS_OP_LITERAL(left) || operand_on_stack(left, a, i, G)) && + (!right || IS_ITEMP(right) || IS_OP_LITERAL(right) || operand_on_stack(right, a, i, G))) + { + + if((operand_byte_in_reg(left, 0, REG_A, a, i, G) || !operand_in_reg(left, REG_A, ia, i, G)) && + (operand_byte_in_reg(right, 0, REG_A, a, i, G) || !operand_in_reg(right, REG_A, ia, i, G)) && + (operand_byte_in_reg(result, getSize(operandType(IC_RESULT(ic))) - 1, REG_A, a, i, G) || !result_in_A)) + return(true); + } + + // First two bytes of input may be in A. + if(ic->op == IFX && dying_A && (getSize(operandType(left)) >= 1 && + operand_byte_in_reg(left, 0, REG_A, a, i, G) || getSize(operandType(left)) >= 2 && !IS_FLOAT (operandType(left)) && operand_byte_in_reg(left, 1, REG_A, a, i, G))) + return(true); + + // Any input byte in A is ok, when all operands are registers other than iy. + if(ic->op == CAST && operand_in_reg(right, REG_A, ia, i, G) && + !operand_in_reg(result, REG_A, ia, i, G) && (operand_in_reg(result, REG_C, ia, i, G) || operand_in_reg(result, REG_E, ia, i, G) || operand_in_reg(result, REG_L, ia, i, G))) + return(true); + + // Last byte of output may be in A. + if((ic->op == GET_VALUE_AT_ADDRESS || ic->op == CAST && !operand_in_reg(right, REG_A, ia, i, G)) && IS_ITEMP(result) && operand_byte_in_reg(result, getSize(operandType(IC_RESULT(ic))) - 1, REG_A, a, i, G)) + return(true); + + if (ic->op == LEFT_OP && getSize(operandType(IC_RESULT(ic))) == 2 && IS_OP_LITERAL(right) && byteOfVal (OP_VALUE (IC_RIGHT(ic)), 0) == 7) + { + if(!operand_in_reg(left, REG_A, ia, i, G) || dying_A) + return(true); + } + + // inc / dec does not affect a. + if ((ic->op == '+' || ic->op == '-') && IS_OP_LITERAL(right) && ulFromVal (OP_VALUE (IC_RIGHT(ic))) <= 2 && + (getSize(operandType(IC_RESULT(ic))) == 2 && operand_is_pair(IC_RESULT(ic), a, i, G) || getSize(operandType(IC_RESULT(ic))) == 1 && operand_in_reg(result, ia, i, G) && operand_in_reg(result, ia, i, G))) + return(true); + + if(ic->op == GET_VALUE_AT_ADDRESS) // Any register can be assigned from (hl) and (iy), so we don't need to go through a then. + return(!IS_BITVAR(getSpec(operandType(result))) && + (getSize(operandType(result)) == 1 || operand_is_pair(left, a, i, G) && (operand_in_reg(left, REG_L, ia, i, G) && !ulFromVal (OP_VALUE (IC_RIGHT(ic))) || operand_in_reg(left, REG_IYL, ia, i, G) && ulFromVal (OP_VALUE (IC_RIGHT(ic))) <= 127))); + + if(ic->op == '=' && POINTER_SET (ic) && // Any register can be assigned to (hl) and (iy), so we don't need to go through a then. + !(IS_BITVAR(getSpec(operandType (result))) || IS_BITVAR(getSpec(operandType (right)))) && + (getSize(operandType(right)) == 1 || operand_is_pair(result, a, i, G) && (operand_in_reg(result, REG_L, ia, i, G) || operand_in_reg(result, REG_IYL, ia, i, G)))) + return(true); + + // Code generator mostly cannot handle variables that are only partially in A. + if(operand_in_reg(left, REG_A, ia, i, G) && getSize(operandType(left)) != 1 || + operand_in_reg(right, REG_A, ia, i, G) && getSize(operandType(right)) != 1 || + operand_in_reg(result, REG_A, ia, i, G) && getSize(operandType(result)) != 1) + return(false); + + if(ic->op == '!' && getSize(operandType(left)) <= 2 && dying_A) + return(true); + + if(ic->op == '=' && POINTER_SET (ic)) + return(dying_A || !(IS_BITVAR(getSpec(operandType (result))) || IS_BITVAR(getSpec(operandType (right))))); + + if(1) + { + // Variable in A is not used by this instruction + if(ic->op == '+' && IS_ITEMP (left) && IS_ITEMP (IC_RESULT(ic)) && IS_OP_LITERAL (right) && + ulFromVal (OP_VALUE (IC_RIGHT(ic))) == 1 && + OP_KEY (IC_RESULT(ic)) == OP_KEY (IC_LEFT(ic))) + return(true); + + if((ic->op == '=' || ic->op == CAST) && !POINTER_SET (ic) && isOperandEqual (result, right)) + return(true); + + if((ic->op == '=' || ic->op == CAST) && !POINTER_SET (ic) && !(ic->op == CAST && IS_BOOL (operandType (result))) && + (operand_in_reg(right, REG_A, ia, i, G) || operand_in_reg(right, REG_B, ia, i, G) || operand_in_reg(right, REG_C, ia, i, G) || operand_in_reg(right, REG_D, ia, i, G) || operand_in_reg(right, REG_E, ia, i, G) || operand_in_reg(right, REG_H, ia, i, G) || operand_in_reg(right, REG_L, ia, i, G)) && + (operand_in_reg(right, REG_A, ia, i, G) || operand_in_reg(result, REG_B, ia, i, G) || operand_in_reg(result, REG_C, ia, i, G) || operand_in_reg(result, REG_D, ia, i, G) || operand_in_reg(result, REG_E, ia, i, G) || operand_in_reg(right, REG_H, ia, i, G) || operand_in_reg(right, REG_L, ia, i, G))) + return(true); + + if(ic->op == GOTO || ic->op == LABEL) + return(true); + + if(ic->op == IPUSH && getSize(operandType(IC_LEFT(ic))) <= 2 && + (operand_in_reg(left, REG_A, ia, i, G) || + operand_in_reg(left, REG_B, ia, i, G) && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_C, ia, i, G) && I[ia.registers[REG_C][1]].byte == 0) || + operand_in_reg(left, REG_D, ia, i, G) && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_E, ia, i, G) && I[ia.registers[REG_E][1]].byte == 0) || + operand_in_reg(left, REG_H, ia, i, G) && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_L, ia, i, G) && I[ia.registers[REG_L][1]].byte == 0) || + operand_in_reg(left, REG_IYL, ia, i, G) && I[ia.registers[REG_IYL][1]].byte == 0 && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_IYH, ia, i, G)))) + return(true); + if(!result_in_A && !input_in_A) + return(false); + } + + // Last use of operand in A. + if(input_in_A && dying_A) + { + if(ic->op != IFX && + ic->op != RETURN && + !((ic->op == RIGHT_OP || ic->op == LEFT_OP) && + (IS_OP_LITERAL(right) || operand_in_reg(right, REG_A, ia, i, G) || getSize(operandType(IC_RESULT(ic))) == 1 && ia.registers[REG_B][1] < 0)) && + !((ic->op == '=' || ic->op == CAST) && !(IY_RESERVED && POINTER_SET(ic))) && + !IS_BITWISE_OP (ic) && + !(ic->op == '~') && + !(ic->op == '*' && (IS_ITEMP(IC_LEFT(ic)) || IS_OP_LITERAL(IC_LEFT(ic))) && (IS_ITEMP(IC_RIGHT(ic)) || IS_OP_LITERAL(IC_RIGHT(ic)))) && + !((ic->op == '-' || ic->op == '+' || ic->op == EQ_OP) && IS_OP_LITERAL(IC_RIGHT(ic)))) + { + //std::cout << "Last use: Dropping at " << i << ", " << ic->key << "(" << int(ic->op) << ")\n"; + return(false); + } + } + // A is used, and has to be preserved for later use. + else if(input_in_A && + ic->op != IFX && + ic->op != JUMPTABLE) + { + //std::cout << "Intermediate use: Dropping at " << i << ", " << ic->key << "(" << int(ic->op) << "\n"; + return(false); + } + + // First use of operand in A. + if(result_in_A && + !POINTER_GET(ic) && + ic->op != '+' && + ic->op != '-' && + (ic->op != '*' || !IS_OP_LITERAL(IC_LEFT(ic)) && !IS_OP_LITERAL(right)) && + !IS_BITWISE_OP(ic) && + ic->op != GET_VALUE_AT_ADDRESS && + ic->op != '=' && + ic->op != EQ_OP && + ic->op != '<' && + ic->op != '>' && + ic->op != CAST && + ic->op != CALL && + ic->op != PCALL && + ic->op != GETHBIT && + !((ic->op == LEFT_OP || ic->op == RIGHT_OP) && IS_OP_LITERAL(right))) + { + //std::cout << "First use: Dropping at " << i << ", " << ic->key << "(" << int(ic->op) << "\n"; + return(false); + } + + //std::cout << "Default OK\n"; + + return(true); +} + +template +static bool HLinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + // HL always unused on gbz80. + if(TARGET_IS_GBZ80) + return(true); + + bool exstk = (should_omit_frame_ptr || (currFunc && currFunc->stack > 127) || IS_GB); + + const i_assignment_t &ia = a.i_assignment; + + bool unused_L = (ia.registers[REG_L][1] < 0); + bool unused_H = (ia.registers[REG_H][1] < 0); + + if(unused_L && unused_H) + return(true); // Register HL not in use. + +#if 0 + if (ic->key == 3) + std::cout << "HLinst_ok: at (" << i << ", " << ic->key << ")\nL = (" << ia.registers[REG_L][0] << ", " << ia.registers[REG_L][1] << "), H = (" << ia.registers[REG_H][0] << ", " << ia.registers[REG_H][1] << ")inst " << i << ", " << ic->key << "\n"; +#endif + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_L = operand_in_reg(result, REG_L, ia, i, G); + bool result_in_H = operand_in_reg(result, REG_H, ia, i, G); + bool result_in_HL = result_in_L || result_in_H; + + bool input_in_L, input_in_H; + switch(ic->op) + { + case IFX: + input_in_L = operand_in_reg(IC_COND(ic), REG_L, ia, i, G); + input_in_H = operand_in_reg(IC_COND(ic), REG_L, ia, i, G); + break; + case JUMPTABLE: + input_in_L = operand_in_reg(IC_JTCOND(ic), REG_L, ia, i, G); + input_in_H = operand_in_reg(IC_JTCOND(ic), REG_L, ia, i, G); + break; + default: + input_in_L = operand_in_reg(left, REG_L, ia, i, G) || operand_in_reg(right, REG_L, ia, i, G); + input_in_H = operand_in_reg(left, REG_H, ia, i, G) || operand_in_reg(right, REG_H, ia, i, G); + break; + } + bool input_in_HL = input_in_L || input_in_H; + + const cfg_dying_t &dying = G[i].dying; + + bool dying_L = result_in_L || dying.find(ia.registers[REG_L][1]) != dying.end() || dying.find(ia.registers[REG_L][0]) != dying.end(); + bool dying_H = result_in_H || dying.find(ia.registers[REG_H][1]) != dying.end() || dying.find(ia.registers[REG_H][0]) != dying.end(); + + bool result_only_HL = (result_in_L || unused_L || dying_L) && (result_in_H || unused_H || dying_H); + +#if 0 + if (ic->key == 4) + { + std::cout << "Result in L: " << result_in_L << ", result in H: " << result_in_H << "\n"; + std::cout << "Unsued L: " << unused_L << ", unused H: " << unused_H << "\n"; + std::cout << "Dying L: " << dying_L << ", dying H: " << dying_H << "\n"; + std::cout << "Result only HL: " << result_only_HL << "\n"; + } +#endif + + if(ic->op == RETURN || ic->op == SEND || ic->op == RECEIVE) + return(true); + + if((IS_GB || IY_RESERVED) && (IS_TRUE_SYMOP(left) || IS_TRUE_SYMOP(right))) + return(false); + + if((IS_GB || IY_RESERVED) && IS_TRUE_SYMOP(result) && getSize(operandType(IC_RESULT(ic))) > 2) + return(false); + + // __z88dk_fastcall passes paramter in hl + if(ic->op == PCALL && ic->prev && ic->prev->op == SEND && input_in_HL && IFFUNC_ISZ88DK_FASTCALL(operandType(IC_LEFT(ic))->next)) + return(false); + + // HL overwritten by result. + if(result_only_HL && ic->op == PCALL) + return(true); + + if(ic->op == '-' && getSize(operandType(result)) == 2 && !IS_GB && IS_TRUE_SYMOP (left) && IS_TRUE_SYMOP (right) && result_only_HL) + return(true); + + if(exstk && + (operand_on_stack(result, a, i, G) + operand_on_stack(left, a, i, G) + operand_on_stack(right, a, i, G) >= 2) && + (result && IS_SYMOP(result) && getSize(operandType(result)) >= 2 || !result_only_HL)) + // Todo: Make this more accurate to get better code when using --fomit-frame-pointer + return(false); + if(exstk && (operand_on_stack(left, a, i, G) || operand_on_stack(right, a, i, G)) && (ic->op == '>' || ic->op == '<')) + return(false); + if(ic->op == '+' && getSize(operandType(result)) >= 2 && input_in_HL && + ((exstk ? operand_on_stack(left, a, i, G) : IS_TRUE_SYMOP (left) ) && (ia.registers[REG_L][1] > 0 || ia.registers[REG_H][1] > 0) || + (exstk ? operand_on_stack(right, a, i, G) : IS_TRUE_SYMOP (right)) && (ia.registers[REG_L][1] > 0 || ia.registers[REG_H][1] > 0) )) + return(false); + + if(ic->op == '+' && getSize(operandType(result)) == 2 && + (IS_OP_LITERAL (right) && ulFromVal (OP_VALUE (IC_RIGHT(ic))) <= 3 || IS_OP_LITERAL (left) && ulFromVal (OP_VALUE (IC_LEFT(ic))) <= 3) && + (operand_in_reg(result, REG_L, ia, i, G) && I[ia.registers[REG_L][1]].byte == 0 && operand_in_reg(result, REG_H, ia, i, G))) + return(true); // Uses inc hl. + + if(ic->op == '+' && getSize(operandType(result)) == 2 && !IS_TRUE_SYMOP (result) && + (result_only_HL || operand_in_reg(result, REG_IYL, ia, i, G) && operand_in_reg(result, REG_IYH, ia, i, G)) && + (ia.registers[REG_C][1] < 0 && ia.registers[REG_B][1] < 0 || ia.registers[REG_E][1] < 0 && ia.registers[REG_D][1] < 0)) // Can use ld rr, (nn) instead of (hl). + return(true); + + if((ic->op == '+' || ic->op == '-' || ic->op == UNARYMINUS) && getSize(operandType(result)) >= 2 && + (IS_TRUE_SYMOP (result) && !operand_on_stack(result, a, i, G) || (operand_on_stack(left, a, i, G) ? exstk : IS_TRUE_SYMOP (left)) || (operand_on_stack(right, a, i, G) ? exstk : IS_TRUE_SYMOP (right)))) // Might use (hl). + return(false); + + if(ic->op == '+' && input_in_HL && (operand_on_stack(result, a, i, G) ? exstk : IS_TRUE_SYMOP (result))) // Might use (hl) for result. + return(false); + + // HL overwritten by result. + if(result_only_HL && !POINTER_SET(ic) && + (ic->op == ADDRESS_OF || + ic->op == GET_VALUE_AT_ADDRESS || + ic->op == '+' || + ic->op == '*' || + ic->op == '=' || + ic->op == CAST)) + return(true); + + if(!exstk && !isOperandInDirSpace(IC_LEFT(ic)) && !isOperandInDirSpace(IC_RIGHT(ic)) && !isOperandInDirSpace(IC_RESULT(ic)) && + (ic->op == '-' || + ic->op == UNARYMINUS || + ic->op == '~' || + ic->op == '<' || + ic->op == '>')) + return(true); + + if(ic->op == LEFT_OP && getSize(operandType(result)) <= 2 && IS_OP_LITERAL (right) && result_only_HL) + return(true); + if((ic->op == LEFT_OP || ic->op == RIGHT_OP) && (getSize(operandType(result)) <= 1 || !IS_TRUE_SYMOP(result) || !IY_RESERVED) && + (!exstk || + ((!operand_on_stack(left, a, i, G) || !input_in_HL && result_only_HL) && + (!operand_on_stack(right, a, i, G) || !input_in_HL && result_only_HL) && + !operand_on_stack(result, a, i, G)))) + return(true); + + if(result && IS_SYMOP(result) && isOperandInDirSpace(IC_RESULT(ic))) + return(false); + + if((input_in_HL || !result_only_HL) && left && IS_SYMOP(left) && isOperandInDirSpace(IC_LEFT(ic))) + return(false); + + if((input_in_HL || !result_only_HL) && right && IS_SYMOP(right) && isOperandInDirSpace(IC_RIGHT(ic))) + return(false); + + // Operations that leave HL alone. + if(ic->op == IFX) + return(true); + if(SKIP_IC2(ic)) + return(true); + if(ic->op == IPUSH && input_in_H && (getSize(operandType(IC_LEFT(ic))) <= 2 || ia.registers[REG_L][1] > 0 && I[ia.registers[REG_L][1]].byte == 2 && ia.registers[REG_H][1] > 0 && I[ia.registers[REG_H][1]].byte == 3)) + return(true); + if(ic->op == IPUSH && getSize(operandType(left)) == 1 && IS_OP_LITERAL(left) && ia.registers[REG_A][1] < 0) // Can be pushed in A. + return(true); + if(ic->op == IPUSH && ic->next && ic->next->op == CALL) + return(true); + if(ic->op == IPUSH && getSize(operandType(left)) == 2 && + (ia.registers[REG_C][1] < 0 && ia.registers[REG_B][1] < 0 || ia.registers[REG_E][1] < 0 && ia.registers[REG_D][1] < 0)) // Can use pair other than HL. + return(true); + if(ic->op == IPUSH && getSize(operandType(left)) <= 2 && + (operand_in_reg(left, REG_C, ia, i, G) && I[ia.registers[REG_C][1]].byte == 0 && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_B, ia, i, G)) || + operand_in_reg(left, REG_E, ia, i, G) && I[ia.registers[REG_E][1]].byte == 0 && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_D, ia, i, G)) || + operand_in_reg(left, REG_IYL, ia, i, G) && I[ia.registers[REG_IYL][1]].byte == 0 && (getSize(operandType(left)) < 2 || operand_in_reg(left, REG_IYH, ia, i, G)))) + return(true); + if (ic->op == IPUSH && getSize(operandType(left)) == 4 && + operand_in_reg(left, REG_L, ia, i, G) && I[ia.registers[REG_L][1]].byte == 0 && operand_in_reg(left, REG_H, ia, i, G) && I[ia.registers[REG_H][1]].byte == 1 && + (operand_in_reg(left, REG_C, ia, i, G) && I[ia.registers[REG_C][1]].byte == 2 && operand_in_reg(left, REG_B, ia, i, G) && I[ia.registers[REG_B][1]].byte == 3 || + operand_in_reg(left, REG_E, ia, i, G) && I[ia.registers[REG_E][1]].byte == 2 && operand_in_reg(left, REG_D, ia, i, G) && I[ia.registers[REG_D][1]].byte == 3)) + return(true); + if(POINTER_GET(ic) && input_in_L && input_in_H && (getSize(operandType(IC_RESULT(ic))) == 1 || !result_in_HL)) + return(true); + if(!IS_GB && ic->op == ADDRESS_OF && + (operand_in_reg(result, REG_IYL, ia, i, G) && ia.registers[REG_IYL][1] > 0 && I[ia.registers[REG_IYL][1]].byte == 0 && operand_in_reg(result, REG_IYH, ia, i, G) || + !OP_SYMBOL_CONST (left)->onStack && operand_in_reg(result, REG_C, ia, i, G) && ia.registers[REG_C][1] > 0 && I[ia.registers[REG_C][1]].byte == 0 && operand_in_reg(result, REG_B, ia, i, G) || + !OP_SYMBOL_CONST (left)->onStack && operand_in_reg(result, REG_E, ia, i, G) && ia.registers[REG_E][1] > 0 && I[ia.registers[REG_E][1]].byte == 0 && operand_in_reg(result, REG_D, ia, i, G))) + return(true); + + if(ic->op == LEFT_OP && isOperandLiteral(IC_RIGHT(ic))) + return(true); + + if(exstk && !result_only_HL && (operand_on_stack(left, a, i, G) || operand_on_stack(right, a, i, G) || operand_on_stack(result, a, i, G)) && ic->op == '+') + return(false); + + if((!POINTER_SET(ic) && !POINTER_GET(ic) && ( + (ic->op == '=' || + ic->op == CAST || + ic->op == UNARYMINUS || + ic->op == RIGHT_OP || + /*ic->op == '-' ||*/ + IS_BITWISE_OP(ic) || + /*ic->op == '>' || + ic->op == '<' || + ic->op == EQ_OP ||*/ + (ic->op == '+' && getSize(operandType(IC_RESULT(ic))) == 1) || + (ic->op == '+' && getSize(operandType(IC_RESULT(ic))) <= 2 && (result_only_HL || !IS_GB)) )))) // 16 bit addition on gbz80 might need to use add hl, rr. + return(true); + + if((ic->op == '<' || ic->op == '>') && (IS_ITEMP(left) || IS_OP_LITERAL(left) || IS_ITEMP(right) || IS_OP_LITERAL(right))) // Todo: Fix for large stack. + return(true); + + if(ic->op == EQ_OP && IS_VALOP(right)) + return(true); + + if(ic->op == CALL) + return(true); + + if(POINTER_GET(ic) && getSize(operandType(IC_RESULT(ic))) == 1 && !IS_BITVAR(getSpec(operandType(result))) && + (operand_in_reg(right, REG_C, ia, i, G) && I[ia.registers[REG_C][1]].byte == 0 && operand_in_reg(right, REG_B, ia, i, G) || // Uses ld a, (bc) + operand_in_reg(right, REG_E, ia, i, G) && I[ia.registers[REG_E][1]].byte == 0 && operand_in_reg(right, REG_D, ia, i, G) || // Uses ld a, (de) + operand_in_reg(right, REG_IYL, ia, i, G) && I[ia.registers[REG_IYL][1]].byte == 0 && operand_in_reg(right, REG_IYH, ia, i, G))) // Uses ld r, 0 (iy) + return(true); + + if((ic->op == '=') && POINTER_SET(ic) && operand_in_reg(result, REG_IYL, ia, i, G) && I[ia.registers[REG_IYL][1]].byte == 0 && operand_in_reg(result, REG_IYH, ia, i, G)) // Uses ld 0 (iy), l etc + return(true); + + if((ic->op == '=' || ic->op == CAST) && POINTER_SET(ic) && !result_only_HL) // loads result pointer into (hl) first. + return(false); + + if((ic->op == '=' || ic->op == CAST) && !POINTER_GET(ic) && !input_in_HL) + return(true); + +#if 0 + if(ic->key == 4) + { + std::cout << "HLinst_ok: L = (" << ia.registers[REG_L][0] << ", " << ia.registers[REG_L][1] << "), H = (" << ia.registers[REG_H][0] << ", " << ia.registers[REG_H][1] << ")inst " << i << ", " << ic->key << "\n"; + std::cout << "Result in L: " << result_in_L << ", result in H: " << result_in_H << "\n"; + std::cout << "HL default drop at " << ic->key << ", operation: " << ic->op << "\n"; + } +#endif + + return(false); +} + +template +static bool IYinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + // IY always unused on gbz80. + if(TARGET_IS_GBZ80) + return(true); + + const i_assignment_t &ia = a.i_assignment; + + /*if(ic->key == 40) + std::cout << "1IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << "\n";*/ + + bool exstk = (should_omit_frame_ptr || (currFunc && currFunc->stack > 127)); + + bool unused_IYL = (ia.registers[REG_IYL][1] < 0); + bool unused_IYH = (ia.registers[REG_IYH][1] < 0); + + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + bool result_in_IYL = operand_in_reg(result, REG_IYL, ia, i, G); + bool result_in_IYH = operand_in_reg(result, REG_IYH, ia, i, G); + bool result_in_IY = result_in_IYL || result_in_IYH; + + bool input_in_IYL, input_in_IYH; + switch(ic->op) + { + case IFX: + input_in_IYL = operand_in_reg(IC_COND(ic), REG_IYL, ia, i, G); + input_in_IYH = operand_in_reg(IC_COND(ic), REG_IYL, ia, i, G); + break; + case JUMPTABLE: + input_in_IYL = operand_in_reg(IC_JTCOND(ic), REG_IYL, ia, i, G); + input_in_IYH = operand_in_reg(IC_JTCOND(ic), REG_IYL, ia, i, G); + break; + default: + input_in_IYL = operand_in_reg(left, REG_IYL, ia, i, G) || operand_in_reg(right, REG_IYL, ia, i, G); + input_in_IYH = operand_in_reg(left, REG_IYH, ia, i, G) || operand_in_reg(right, REG_IYH, ia, i, G); + break; + } + bool input_in_IY = input_in_IYL || input_in_IYH; + + //const std::set &dying = G[i].dying; + + //bool dying_IYL = result_in_IYL || dying.find(ia.registers[REG_IYL][1]) != dying.end() || dying.find(ia.registers[REG_IYL][0]) != dying.end(); + //bool dying_IYH = result_in_IYH || dying.find(ia.registers[REG_IYH][1]) != dying.end() || dying.find(ia.registers[REG_IYH][0]) != dying.end(); + + //bool result_only_IY = (result_in_IYL || unused_IYL || dying_IYL) && (result_in_IYH || unused_IYH || dying_IYH); + + if(unused_IYL && unused_IYH) + return(true); // Register IY not in use. + + if(SKIP_IC2(ic)) + return(true); + + if(exstk && (operand_on_stack(result, a, i, G) || operand_on_stack(left, a, i, G) || operand_on_stack(right, a, i, G))) // Todo: Make this more accurate to get better code when using --fomit-frame-pointer + return(false); + + if(ic->op == CALL) + return(true); + + if(!result_in_IY && !input_in_IY && + !(IC_RESULT(ic) && isOperandInDirSpace(IC_RESULT(ic))) && + !(IC_RIGHT(ic) && IS_TRUE_SYMOP(IC_RIGHT(ic))) && + !(IC_LEFT(ic) && IS_TRUE_SYMOP(IC_LEFT(ic)))) + return(true); + + // variables partially in IY can be pushed. + if(ic->op == IPUSH && + operand_in_reg(left, REG_IYL, ia, i, G) && operand_in_reg(left, REG_IYH, ia, i, G) && + (I[ia.registers[REG_IYL][1]].byte == 0 && I[ia.registers[REG_IYH][1]].byte == 1 || I[ia.registers[REG_IYL][1]].byte == 2 && I[ia.registers[REG_IYH][1]].byte == 3)) + return(true); + + // Code generator mostly cannot handle variables that are only partially in IY. + if(unused_IYL ^ unused_IYH) + return(false); + if(!unused_IYL && I[ia.registers[REG_IYL][1]].size != 2 || !unused_IYH && I[ia.registers[REG_IYH][1]].size != 2 || + ia.registers[REG_IYL][0] >= 0 && I[ia.registers[REG_IYL][0]].size != 2 || ia.registers[REG_IYH][0] >= 0 && I[ia.registers[REG_IYH][0]].size != 2) + return(false); + if(ia.registers[REG_IYL][1] >= 0 && (ia.registers[REG_IYH][1] <= 0 || I[ia.registers[REG_IYL][1]].v != I[ia.registers[REG_IYH][1]].v)) + return(false); + if(ia.registers[REG_IYH][1] >= 0 && (ia.registers[REG_IYL][1] <= 0 || I[ia.registers[REG_IYH][1]].v != I[ia.registers[REG_IYL][1]].v)) + return(false); + if(ia.registers[REG_IYL][0] >= 0 && (ia.registers[REG_IYH][0] <= 0 || I[ia.registers[REG_IYL][0]].v != I[ia.registers[REG_IYH][0]].v)) + return(false); + if(ia.registers[REG_IYH][0] >= 0 && (ia.registers[REG_IYL][0] <= 0 || I[ia.registers[REG_IYH][0]].v != I[ia.registers[REG_IYL][0]].v)) + return(false); + if(I[ia.registers[REG_IYL][1]].byte != 0 || I[ia.registers[REG_IYH][1]].byte != 1) + return(false); + if(ia.registers[REG_IYL][0] >= 0 && I[ia.registers[REG_IYL][0]].byte != 0 || ia.registers[REG_IYH][0] >= 0 && I[ia.registers[REG_IYH][0]].byte != 1) + return(false); + +#if 0 + if(ic->key == 32) + { + std::cout << "A IYinst_ok: Assignment: "; + //print_assignment(a); + std::cout << "\n"; + std::cout << "2IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << "\n"; + } +#endif + + if(result_in_IY && + (ic->op == '=' && !(POINTER_SET(ic) && isOperandInDirSpace(IC_RIGHT(ic))) || + ic->op == CAST && getSize(operandType(IC_RESULT(ic))) <= getSize(operandType(IC_RIGHT(ic))) || + ic->op == '+')) // todo: More instructions that can write iy. + return(true); + + // Todo: Multiplication. + + if(ic->op == LEFT_OP && result_in_IY && input_in_IY && IS_VALOP (IC_RIGHT (ic)) && operandLitValue (IC_RIGHT (ic)) < 8) + return(true); + + if(ic->op == '-' && result_in_IY && input_in_IY && IS_VALOP (IC_RIGHT (ic)) && operandLitValue (IC_RIGHT (ic)) < 4) + return(true); + +#if 0 + if(ic->key == 32) + { + std::cout << "B IYinst_ok: Assignment: "; + //print_assignment(a); + std::cout << "\n"; + std::cout << "2IYinst_ok: at (" << i << ", " << ic->key << ")\nIYL = (" << ia.registers[REG_IYL][0] << ", " << ia.registers[REG_IYL][1] << "), IYH = (" << ia.registers[REG_IYH][0] << ", " << ia.registers[REG_IYH][1] << ")inst " << i << ", " << ic->key << "\n"; + } +#endif + + if(!result_in_IY && !input_in_IY && + (ic->op == '=' || ic->op == CAST && getSize(operandType(IC_RIGHT (ic))) >= 2 && (getSize(operandType(IC_RESULT (ic))) <= getSize(operandType(IC_RIGHT (ic))) || !IS_SPEC(operandType(IC_RIGHT (ic))) || SPEC_USIGN(operandType(IC_RIGHT(ic))))) && + operand_is_pair(IC_RESULT(ic), a, i, G)) // DirSpace access won't use iy here. + return(true); + + if(ic->op == IPUSH) // todo: More instructions that can use IY. + return(true); + + if(ic->op == GET_VALUE_AT_ADDRESS && isOperandInDirSpace(IC_RESULT(ic))) + return(false); + + if(input_in_IY && !result_in_IY && + (ic->op == '=' && !POINTER_SET(ic) || + ic->op == CAST && getSize(operandType(IC_RESULT(ic))) <= getSize(operandType(IC_RIGHT(ic))) || + ic->op == GET_VALUE_AT_ADDRESS)) + return(true); + +#if 0 + if(ic->key == 99) + { + std::cout << "Default drop.\n"; + std::cout << "result is pair: " << operand_is_pair(IC_RESULT(ic), a, i, G) << "\n"; + } +#endif + + return(false); +} + +template +bool DEinst_ok(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!IS_GB) // Only gbz80 might need de for code generation. + return(true); + + const i_assignment_t &ia = a.i_assignment; + + bool unused_E = (ia.registers[REG_E][1] < 0); + bool unused_D = (ia.registers[REG_D][1] < 0); + + if(unused_E && unused_D) + return(true); // Register DE not in use. + + const iCode *ic = G[i].ic; + const operand *left = IC_LEFT(ic); + const operand *right = IC_RIGHT(ic); + const operand *result = IC_RESULT(ic); + + //const std::set &dying = G[i].dying; + + if(ic->op == PCALL) + return(false); + + if(ic->op == GET_VALUE_AT_ADDRESS && (getSize(operandType(result)) >= 2 || !operand_is_pair(left, a, i, G))) + return(false); + + if (ic->op == '=' && POINTER_SET(ic) && !operand_is_pair(result, a, i, G)) + return(false); + + if((ic->op == '=' || ic->op == CAST) && getSize(operandType(result)) >= 2 && + (operand_on_stack(right, a, i, G) || operand_in_reg(right, REG_L, ia, i, G) || operand_in_reg(right, REG_H, ia, i, G)) && + (operand_on_stack(result, a, i, G) || operand_in_reg(result, REG_L, ia, i, G) || operand_in_reg(result, REG_H, ia, i, G))) + return(false); + + if(ic->op == '+' && getSize(operandType(result)) >= 2) + return(false); + + if(ic->op == UNARYMINUS || ic->op == '-' || ic->op == '*') + return(false); + + if(ic->op == '>' || ic->op == '<') + return(false); + + return(true); +} + +template +static void set_surviving_regs(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + + bitVectClear(ic->rMask); + bitVectClear(ic->rSurv); + + cfg_alive_t::const_iterator v, v_end; + for (v = G[i].alive.begin(), v_end = G[i].alive.end(); v != v_end; ++v) + { + if(a.global[*v] < 0) + continue; + ic->rMask = bitVectSetBit(ic->rMask, a.global[*v]); + + if(G[i].dying.find(*v) == G[i].dying.end()) + if(!((IC_RESULT(ic) && !POINTER_SET(ic)) && IS_SYMOP(IC_RESULT(ic)) && OP_SYMBOL_CONST(IC_RESULT(ic))->key == I[*v].v)) + ic->rSurv = bitVectSetBit(ic->rSurv, a.global[*v]); + } +} + +template +static void assign_operand_for_cost(operand *o, const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + if(!o || !IS_SYMOP(o)) + return; + symbol *sym = OP_SYMBOL(o); + operand_map_t::const_iterator oi, oi_end; + for(boost::tie(oi, oi_end) = G[i].operands.equal_range(OP_SYMBOL_CONST(o)->key); oi != oi_end; ++oi) + { + var_t v = oi->second; + if(a.global[v] >= 0) + { + sym->regs[I[v].byte] = regsZ80 + a.global[v]; + sym->accuse = 0; + sym->isspilt = false; + sym->nRegs = I[v].size; + } + else + { + sym->isspilt = true; + sym->accuse = 0; + sym->nRegs = I[v].size; + sym->regs[I[v].byte] = 0; + } + } +} + +template +static void assign_operands_for_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const iCode *ic = G[i].ic; + + if(ic->op == IFX) + assign_operand_for_cost(IC_COND(ic), a, i, G, I); + else if(ic->op == JUMPTABLE) + assign_operand_for_cost(IC_JTCOND(ic), a, i, G, I); + else + { + assign_operand_for_cost(IC_LEFT(ic), a, i, G, I); + assign_operand_for_cost(IC_RIGHT(ic), a, i, G, I); + assign_operand_for_cost(IC_RESULT(ic), a, i, G, I); + } + + if(ic->op == SEND && ic->builtinSEND) + assign_operands_for_cost(a, (unsigned short)*(adjacent_vertices(i, G).first), G, I); +} + +// Cost function. +template +static float instruction_cost(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + iCode *ic = G[i].ic; + float c; + + wassert (TARGET_Z80_LIKE); + + if(!inst_sane(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(ic->generated) + return(0.0f); + + if(!Ainst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(OPTRALLOC_HL && !HLinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(!DEinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + if(OPTRALLOC_IY && !IYinst_ok(a, i, G, I)) + return(std::numeric_limits::infinity()); + + switch(ic->op) + { + // Register assignment doesn't matter for these: + case FUNCTION: + case ENDFUNCTION: + case LABEL: + case GOTO: + case INLINEASM: + return(0.0f); + // Exact cost: + case '!': + case '~': + case UNARYMINUS: + case '+': + case '-': + case '^': + case '|': + case BITWISEAND: + case IPUSH: + //case IPOP: + case CALL: + case PCALL: + case RETURN: + case '*': + case '>': + case '<': + case EQ_OP: + case AND_OP: + case OR_OP: + case GETHBIT: + case LEFT_OP: + case RIGHT_OP: + case GET_VALUE_AT_ADDRESS: + case '=': + case IFX: + case ADDRESS_OF: + case JUMPTABLE: + case CAST: + case RECEIVE: + case SEND: + case DUMMY_READ_VOLATILE: + case CRITICAL: + case ENDCRITICAL: + assign_operands_for_cost(a, i, G, I); + set_surviving_regs(a, i, G, I); + c = dryZ80iCode(ic); + ic->generated = false; + return(c); + // Inexact cost: + default: + return(default_instruction_cost(a, i, G, I)); + } +} + +template +float weird_byte_order(const assignment &a, const I_t &I) +{ + float c = 0.0f; + + varset_t::const_iterator vi, vi_end; + for(vi = a.local.begin(), vi_end = a.local.end(); vi != vi_end; ++vi) + if(a.global[*vi] > 0 && (a.global[*vi] - 1) % 2 != I[*vi].byte % 2) + c += 8.0f; + + return(c); +} + +// Check for gaps, i.e. higher bytes of a variable being assigned to regs, while lower byte are not. +template +bool local_assignment_insane(const assignment &a, const I_t &I, var_t lastvar) +{ + varset_t::const_iterator v, v_end, v_old; + + for(v = a.local.begin(), v_end = a.local.end(); v != v_end;) + { + v_old = v; + ++v; + if(v == v_end) + { + if(*v_old != lastvar && I[*v_old].byte != I[*v_old].size - 1) + return(true); + break; + } + if(I[*v_old].v == I[*v].v) + { + if(I[*v_old].byte != I[*v].byte - 1) + return(true); + } + else + { + if(*v_old != lastvar && I[*v_old].byte != I[*v_old].size - 1 || I[*v].byte) + return(true); + } + } + + return(false); +} + +// For early removal of assignments that cannot be extended to valid assignments. +template +static bool assignment_hopeless(const assignment &a, unsigned short int i, const G_t &G, const I_t &I, const var_t lastvar) +{ + if(local_assignment_insane(a, I, lastvar)) + return(true); + + const i_assignment_t &ia = a.i_assignment; + + // Can only check for HLinst_ok() in some cases. + if(OPTRALLOC_HL && + (ia.registers[REG_L][1] >= 0 && ia.registers[REG_H][1] >= 0) && + (ia.registers[REG_L][0] >= 0 && ia.registers[REG_H][0] >= 0) && + !HLinst_ok(a, i, G, I)) + return(true); + + // Can only check for IYinst_ok() in some cases. + if(OPTRALLOC_IY && + (ia.registers[REG_IYL][1] >= 0 || ia.registers[REG_IYH][1] >= 0) && + !IYinst_ok(a, i, G, I)) + return(true); + + return(false); +} + +// Increase chance of finding good compatible assignments at join nodes. +template +static void get_best_local_assignment_biased(assignment &a, typename boost::graph_traits::vertex_descriptor t, const T_t &T) +{ + const assignment_list_t &alist = T[t].assignments; + + assignment_list_t::const_iterator ai, ai_end, ai_best; + for(ai = ai_best = alist.begin(), ai_end = alist.end(); ai != ai_end; ++ai) + { + if(ai->s < ai_best->s) + { + varset_t::const_iterator vi, vi_end; + for(vi = ai->local.begin(), vi_end = ai->local.end(); vi != vi_end; ++vi) + if(ai->global[*vi] == REG_A || OPTRALLOC_HL && (ai->global[*vi] == REG_H || ai->global[*vi] == REG_L) || OPTRALLOC_IY && (ai->global[*vi] == REG_IYH || ai->global[*vi] == REG_IYL)) + goto too_risky; + ai_best = ai; + } +too_risky: + ; + } + + a = *ai_best; + + std::set::const_iterator vi, vi_end; + varset_t newlocal; + std::set_union(T[t].alive.begin(), T[t].alive.end(), a.local.begin(), a.local.end(), std::inserter(newlocal, newlocal.end())); + a.local = newlocal; +} + +template +static float rough_cost_estimate(const assignment &a, unsigned short int i, const G_t &G, const I_t &I) +{ + const i_assignment_t &ia = a.i_assignment; + float c = 0.0f; + + c += weird_byte_order(a, I); + + if(OPTRALLOC_HL && + ia.registers[REG_L][1] >= 0 && + ia.registers[REG_H][1] >= 0 && + ((ia.registers[REG_L][0] >= 0) == (ia.registers[REG_H][0] >= 0)) && + !HLinst_ok(a, i, G, I)) + c += 8.0f; + + if(ia.registers[REG_A][1] < 0) + c += 0.03f; + + if(OPTRALLOC_HL && ia.registers[REG_L][1] < 0) + c += 0.02f; + + // Using IY is rarely a good choice, so discard the IY-users first when in doubt. + if(OPTRALLOC_IY) + { + varset_t::const_iterator vi, vi_end; + for(vi = a.local.begin(), vi_end = a.local.end(); vi != vi_end; ++vi) + if(a.global[*vi] == REG_IYL || a.global[*vi] == REG_IYH) + c += 8.0f; + } + + // An artifical ordering of assignments. + if(ia.registers[REG_E][1] < 0) + c += 0.0001f; + if(ia.registers[REG_D][1] < 0) + c += 0.00001f; + + if(a.marked) + c -= 0.5f; + + varset_t::const_iterator v, v_end; + for(v = a.local.begin(), v_end = a.local.end(); v != v_end; ++v) + { + const symbol *const sym = (symbol *)(hTabItemWithKey(liveRanges, I[*v].v)); + if(a.global[*v] < 0 && IS_REGISTER(sym->type)) // When in doubt, try to honour register keyword. + c += 32.0f; + if((I[*v].byte % 2) && (a.global[*v] == REG_L || a.global[*v] == REG_E || a.global[*v] == REG_C || a.global[*v] == REG_IYL)) // Try not to reverse bytes. + c += 8.0f; + if(!(I[*v].byte % 2) && I[*v].size > 1 && (a.global[*v] == REG_H || a.global[*v] == REG_D || a.global[*v] == REG_B || a.global[*v] == REG_IYH)) // Try not to reverse bytes. + c += 8.0f; + if(I[*v].byte == 0 && I[*v].size > 1 || I[*v].byte == 2 && I[*v].size > 3) + { + if (a.global[*v] == REG_L && a.global[*v + 1] >= 0 && a.global[*v + 1] != REG_H) + c += 16.0f; + if (a.global[*v] == REG_E && a.global[*v + 1] >= 0 && a.global[*v + 1] != REG_D) + c += 16.0f; + if (a.global[*v] == REG_C && a.global[*v + 1] >= 0 && a.global[*v + 1] != REG_B) + c += 16.0f; + } + else if(I[*v].byte == 1 || I[*v].byte == 3) + { + if (a.global[*v] == REG_H && a.global[*v - 1] >= 0 && a.global[*v - 1] != REG_L) + c += 16.0f; + if (a.global[*v] == REG_D && a.global[*v - 1] >= 0 && a.global[*v - 1] != REG_E) + c += 16.0f; + if (a.global[*v] == REG_B && a.global[*v - 1] >= 0 && a.global[*v - 1] != REG_C) + c += 16.0f; + } + } + + c -= a.local.size() * 0.2f; + + return(c); +} + +// Code for another ic is generated when generating this one. Mark the other as generated. +static void extra_ic_generated(iCode *ic) +{ + if(ic->op == '>' || ic->op == '<' || ic->op == LE_OP || ic->op == GE_OP || ic->op == EQ_OP || ic->op == NE_OP || + (ic->op == '^' || ic->op == '|' || ic->op == BITWISEAND) && (IS_OP_LITERAL (IC_LEFT (ic)) || IS_OP_LITERAL (IC_RIGHT (ic)))) + { + iCode *ifx; + if (ifx = ifxForOp (IC_RESULT (ic), ic)) + { + OP_SYMBOL (IC_RESULT (ic))->for_newralloc = false; + OP_SYMBOL (IC_RESULT (ic))->regType = REG_CND; + ifx->generated = true; + } + } + + if(ic->op == SEND && ic->builtinSEND && (!ic->prev || ic->prev->op != SEND || !ic->prev->builtinSEND)) + { + iCode *icn; + for(icn = ic->next; icn->op != CALL; icn = icn->next) + icn->generated = true; + icn->generated = true; + ic->generated = false; + } +} + +template +static bool tree_dec_ralloc(T_t &T, G_t &G, const I_t &I, SI_t &SI) +{ + bool assignment_optimal; + + con2_t I2(boost::num_vertices(I)); + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + I2[i].v = I[i].v; + I2[i].byte = I[i].byte; + I2[i].size = I[i].size; + I2[i].name = I[i].name; + } + typename boost::graph_traits::edge_iterator e, e_end; + for(boost::tie(e, e_end) = boost::edges(I); e != e_end; ++e) + add_edge(boost::source(*e, I), boost::target(*e, I), I2); + + assignment ac; + assignment_optimal = true; + tree_dec_ralloc_nodes(T, find_root(T), G, I2, ac, &assignment_optimal); + + const assignment &winner = *(T[find_root(T)].assignments.begin()); + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Winner: "; + for(unsigned int i = 0; i < boost::num_vertices(I); i++) + { + std::cout << "(" << i << ", " << int(winner.global[i]) << ") "; + } + std::cout << "\n"; + std::cout << "Cost: " << winner.s << "\n"; + std::cout.flush(); +#endif + + // Todo: Make this an assertion + if(winner.global.size() != boost::num_vertices(I)) + { + std::cerr << "ERROR: No Assignments at root\n"; + exit(-1); + } + + for(unsigned int v = 0; v < boost::num_vertices(I); v++) + { + symbol *sym = (symbol *)(hTabItemWithKey(liveRanges, I[v].v)); + if(winner.global[v] >= 0) + { + + sym->regs[I[v].byte] = regsZ80 + winner.global[v]; + sym->accuse = 0; + sym->isspilt = false; + sym->nRegs = I[v].size; + } + else + { + for(int i = 0; i < I[v].size; i++) + sym->regs[i] = 0; + sym->accuse = 0; + sym->nRegs = I[v].size; + if (USE_OLDSALLOC) + sym->isspilt = false; // Leave it to Z80RegFix, which can do some spillocation compaction. + else + z80SpillThis(sym); + } + } + + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + set_surviving_regs(winner, i, G, I); + + if (!USE_OLDSALLOC) + set_spilt(G, I, SI); + + return(!assignment_optimal); +} + +// Omit the frame pointer for functions with low register pressure and few parameter accesses. +template +static bool omit_frame_ptr(const G_t &G) +{ + if(IS_GB || IY_RESERVED || z80_opts.noOmitFramePtr) + return(false); + + if(options.omitFramePtr) + return(true); + + signed char omitcost = -16; + for(unsigned int i = 0; i < boost::num_vertices(G); i++) + { + if((int)G[i].alive.size() > port->num_regs - 4) + return(false); + + const iCode *const ic = G[i].ic; + const operand *o; + o = IC_RESULT(ic); + if(o && IS_SYMOP(o) && OP_SYMBOL_CONST(o)->_isparm && !IS_REGPARM (OP_SYMBOL_CONST(o)->etype)) + omitcost += 6; + o = IC_LEFT(ic); + if(o && IS_SYMOP(o) && OP_SYMBOL_CONST(o)->_isparm && !IS_REGPARM (OP_SYMBOL_CONST(o)->etype)) + omitcost += 6; + o = IC_RIGHT(ic); + if(o && IS_SYMOP(o) && OP_SYMBOL_CONST(o)->_isparm && !IS_REGPARM (OP_SYMBOL_CONST(o)->etype)) + omitcost += 6; + + if(omitcost > 14) // Chosen greater than zero, since the peephole optimizer often can optimize the use of iy into use of hl, reducing the cost. + return(false); + } + + return(true); +} + +// Adjust stack location when deciding to omit frame pointer. +void move_parms(void) +{ + if(!currFunc || IS_GB || options.omitFramePtr || !should_omit_frame_ptr) + return; + + for(value *val = FUNC_ARGS (currFunc->type); val; val = val->next) + { + if(IS_REGPARM(val->sym->etype) || !val->sym->onStack) + continue; + + val->sym->stack -= 2; + } +} + +iCode *z80_ralloc2_cc(ebbIndex *ebbi) +{ + eBBlock **const ebbs = ebbi->bbOrder; + const int count = ebbi->count; + +#ifdef DEBUG_RALLOC_DEC + std::cout << "Processing " << currFunc->name << " from " << dstFileName << "\n"; std::cout.flush(); +#endif + + cfg_t control_flow_graph; + + con_t conflict_graph; + + iCode *ic = create_cfg(control_flow_graph, conflict_graph, ebbi); + + should_omit_frame_ptr = omit_frame_ptr(control_flow_graph); + move_parms(); + + if(options.dump_graphs) + dump_cfg(control_flow_graph); + + if(options.dump_graphs) + dump_con(conflict_graph); + + tree_dec_t tree_decomposition; + + get_nice_tree_decomposition(tree_decomposition, control_flow_graph); + + alive_tree_dec(tree_decomposition, control_flow_graph); + + good_re_root(tree_decomposition); + nicify(tree_decomposition); + alive_tree_dec(tree_decomposition, control_flow_graph); + + if(options.dump_graphs) + dump_tree_decomposition(tree_decomposition); + + guessCounts (ic, ebbi); + + scon_t stack_conflict_graph; + + z80_assignment_optimal = !tree_dec_ralloc(tree_decomposition, control_flow_graph, conflict_graph, stack_conflict_graph); + + Z80RegFix (ebbs, count); + + if (USE_OLDSALLOC) + redoStackOffsets (); + else + chaitin_salloc(stack_conflict_graph); // new Chaitin-style stack allocator + + if(options.dump_graphs && !USE_OLDSALLOC) + dump_scon(stack_conflict_graph); + + return(ic); +} + diff --git a/src/z80/support.c b/src/z80/support.c new file mode 100644 index 0000000..05352a5 --- /dev/null +++ b/src/z80/support.c @@ -0,0 +1,37 @@ +/** @file z80/support.c + */ +#include "z80.h" +#include + +int +convertFloat (Z80_FLOAT * f, double native) +{ + unsigned long mantissa, exponent; + double f2; + wassert (f); + if (native != 0) + { + f2 = floor (log (fabs (native)) / log (2)) + 1; + mantissa = (unsigned long) (0x1000000 * fabs (native) / exp (f2 * log (2))) ; + mantissa &= 0xffffff; + exponent = (unsigned long) (f2 + 0x40) ; + if (native < 0) + exponent |= 0x80; + } + else + { + mantissa = 0; + exponent = 0; + } + + f->w[0] = (WORD) mantissa; + f->w[1] = (BYTE) (mantissa >> 16); + f->w[1] |= exponent << 8; + + f->b[0] = (BYTE) f->w[0]; + f->b[1] = (BYTE) (f->w[0] >> 8); + f->b[2] = (BYTE) f->w[1]; + f->b[3] = (BYTE) (f->w[1] >> 8); + + return 0; +} diff --git a/src/z80/support.h b/src/z80/support.h new file mode 100644 index 0000000..a2902b7 --- /dev/null +++ b/src/z80/support.h @@ -0,0 +1,20 @@ +/** @file z80/support.h + Support functions for the z80 port. +*/ +#ifndef Z80_SUPPORT_INCLUDE +#define Z80_SUPPORT_INCLUDE + +typedef unsigned short WORD; +typedef unsigned char BYTE; + +typedef struct + { + WORD w[2]; + BYTE b[4]; + } +Z80_FLOAT; + +/** Convert a native float into 'z80' format */ +int convertFloat (Z80_FLOAT * f, double native); + +#endif diff --git a/src/z80/z80.h b/src/z80/z80.h new file mode 100644 index 0000000..3df8a6b --- /dev/null +++ b/src/z80/z80.h @@ -0,0 +1,55 @@ +/** @file z80/z80.h + Common definitions between the z80 and gbz80 parts. +*/ +#include "common.h" +#include "ralloc.h" +#include "gen.h" +#include "peep.h" +#include "support.h" + +typedef enum + { + SUB_Z80, + SUB_Z180, + SUB_R2K, + SUB_R3KA, + SUB_GBZ80, + SUB_TLCS90, + SUB_EZ80_Z80 + } +Z80_SUB_PORT; + +typedef struct + { + Z80_SUB_PORT sub; + int calleeSavesBC; + int port_mode; + int port_back; + int reserveIY; + int noOmitFramePtr; + } +Z80_OPTS; + +extern Z80_OPTS z80_opts; + +#define IS_Z80 (z80_opts.sub == SUB_Z80) +#define IS_Z180 (z80_opts.sub == SUB_Z180) +#define IS_R2K (z80_opts.sub == SUB_R2K) +#define IS_R3KA (z80_opts.sub == SUB_R3KA) +#define IS_RAB (IS_R2K || IS_R3KA) +#define IS_GB (z80_opts.sub == SUB_GBZ80) +#define IS_TLCS90 (z80_opts.sub == SUB_TLCS90) +#define IS_EZ80_Z80 (z80_opts.sub == SUB_EZ80_Z80) + +#define IY_RESERVED (z80_opts.reserveIY) + +#define OPTRALLOC_HL (!IS_GB) +#define OPTRALLOC_IY !(IY_RESERVED || IS_GB) + +enum + { + ACCUSE_A = 1, + ACCUSE_SCRATCH, + ACCUSE_IY + }; + diff --git a/src/z80/z80.vcxproj b/src/z80/z80.vcxproj new file mode 100644 index 0000000..89f8aeb --- /dev/null +++ b/src/z80/z80.vcxproj @@ -0,0 +1,214 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7444A72F-C7C6-4F90-9A62-2D46240B52DB} + + + + StaticLibrary + false + MultiByte + + + StaticLibrary + false + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(Configuration)\ + port + port + + + + Disabled + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;_DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + .\Debug/z80.pch + .\Debug/ + .\Debug/ + .\Debug/ + true + Level3 + true + EditAndContinue + CompileAsCpp + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Debug/z80.bsc + + + + + MaxSpeed + OnlyExplicitInline + ..;.;..\..;..\..\support\util;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;WIN32;_LIB;NDEBUG;%(PreprocessorDefinitions) + true + MultiThreaded + true + .\Release/z80.pch + .\Release/ + .\Release/ + .\Release/ + Level3 + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(Configuration)\$(TargetFileName) + true + + + true + .\Release/z80.bsc + + + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + CompileAsC + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + CompileAsC + %(AdditionalIncludeDirectories) + LNK;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + CompileAsC + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + CompileAsC + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + _SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + CompileAsC + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + + + + + + + + + + + + + {2f87ba6f-8ee1-48d0-9817-6ba30bddb3c1} + false + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-gbz80.rul + peeph-gbz80.rul;%(Outputs) + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-gbz80.rul + peeph-gbz80.rul;%(Outputs) + Generating Peephole Rule: peeph-gbz80.rul + Generating Peephole Rule: peeph-gbz80.rul + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-z80.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-z80.rul + peeph-z80.rul;%(Outputs) + peeph-z80.rul;%(Outputs) + Generating Peephole Rule: peeph-z80.rul + Generating Peephole Rule: peeph-z80.rul + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph.rul + peeph.rul;%(Outputs) + peeph.rul;%(Outputs) + Generating Peephole Rule: peeph.rul + Generating Peephole Rule: peeph.rul + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-r2k.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-r2k.rul + peeph-r2k.rul;%(Outputs) + peeph-r2k.rul;%(Outputs) + Generating Peephole Rule: peeph-r2k.rul + Generating Peephole Rule: peeph-r2k.rul + + + + + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-tlcs90.rul + gawk -f ../SDCCpeeph.awk %(Identity) >peeph-tlcs90.rul + Generating Peephole Rule: peeph-tlcs90.rul + Generating Peephole Rule: peeph-tlcs90.rul + peeph-tlcs90.rul;%(Outputs) + peeph-tlcs90.rul;%(Outputs) + + + + + + \ No newline at end of file diff --git a/src/z80/z80.vcxproj.filters b/src/z80/z80.vcxproj.filters new file mode 100644 index 0000000..83228e2 --- /dev/null +++ b/src/z80/z80.vcxproj.filters @@ -0,0 +1,79 @@ + + + + + {a79ae3fe-4665-451f-9794-5d3c74e69f7e} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {0cb331ec-ba78-49f3-ba51-cc6e8c09a18c} + h;hpp;hxx;hm;inl + + + {89ad5f5a-fe40-4b1d-81af-ac5fda0e57b3} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Custom Build + + + Custom Build + + + Custom Build + + + Custom Build + + + Custom Build + + + \ No newline at end of file diff --git a/src/z80/z80a.vcxproj b/src/z80/z80a.vcxproj new file mode 100644 index 0000000..545504d --- /dev/null +++ b/src/z80/z80a.vcxproj @@ -0,0 +1,82 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {02D1EF3D-F584-4F1E-9B45-6CF0DBF76076} + MakeFileProj + + + + Utility + false + + + Utility + false + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + .\ + .\ + .\ + .\ + + + + ./z80a.tlb + + + + + + + ./z80a.tlb + + + + + + + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph-gbz80.rul + peeph-gbz80.rul;%(Outputs) + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph-gbz80.rul + peeph-gbz80.rul;%(Outputs) + + + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph-z80.rul + peeph-z80.rul;%(Outputs) + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph-z80.rul + peeph-z80.rul;%(Outputs) + + + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph.rul + peeph.rul;%(Outputs) + gawk -f ../SDCCpeeph.awk %(FullPath) >peeph.rul + peeph.rul;%(Outputs) + + + + + + \ No newline at end of file -- cgit v1.2.3