| // Inferno utils/5l/span.c | |
| // http://code.google.com/p/inferno-os/source/browse/utils/5l/span.c | |
| // | |
| // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. | |
| // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) | |
| // Portions Copyright © 1997-1999 Vita Nuova Limited | |
| // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) | |
| // Portions Copyright © 2004,2006 Bruce Ellis | |
| // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) | |
| // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | |
| // Portions Copyright © 2009 The Go Authors. All rights reserved. | |
| // | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy | |
| // of this software and associated documentation files (the "Software"), to deal | |
| // in the Software without restriction, including without limitation the rights | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| // copies of the Software, and to permit persons to whom the Software is | |
| // furnished to do so, subject to the following conditions: | |
| // | |
| // The above copyright notice and this permission notice shall be included in | |
| // all copies or substantial portions of the Software. | |
| // | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| // THE SOFTWARE. | |
| // Instruction layout. | |
| #include <u.h> | |
| #include <libc.h> | |
| #include <bio.h> | |
| #include <link.h> | |
| #include "../cmd/5l/5.out.h" | |
| #include "../pkg/runtime/stack.h" | |
| typedef struct Optab Optab; | |
| typedef struct Oprang Oprang; | |
| typedef uchar Opcross[32][2][32]; | |
| struct Optab | |
| { | |
| char as; | |
| uchar a1; | |
| char a2; | |
| uchar a3; | |
| uchar type; | |
| char size; | |
| char param; | |
| char flag; | |
| uchar pcrelsiz; | |
| }; | |
| struct Oprang | |
| { | |
| Optab* start; | |
| Optab* stop; | |
| }; | |
| enum | |
| { | |
| LFROM = 1<<0, | |
| LTO = 1<<1, | |
| LPOOL = 1<<2, | |
| LPCREL = 1<<3, | |
| C_NONE = 0, | |
| C_REG, | |
| C_REGREG, | |
| C_REGREG2, | |
| C_SHIFT, | |
| C_FREG, | |
| C_PSR, | |
| C_FCR, | |
| C_RCON, /* 0xff rotated */ | |
| C_NCON, /* ~RCON */ | |
| C_SCON, /* 0xffff */ | |
| C_LCON, | |
| C_LCONADDR, | |
| C_ZFCON, | |
| C_SFCON, | |
| C_LFCON, | |
| C_RACON, | |
| C_LACON, | |
| C_SBRA, | |
| C_LBRA, | |
| C_HAUTO, /* halfword insn offset (-0xff to 0xff) */ | |
| C_FAUTO, /* float insn offset (0 to 0x3fc, word aligned) */ | |
| C_HFAUTO, /* both H and F */ | |
| C_SAUTO, /* -0xfff to 0xfff */ | |
| C_LAUTO, | |
| C_HOREG, | |
| C_FOREG, | |
| C_HFOREG, | |
| C_SOREG, | |
| C_ROREG, | |
| C_SROREG, /* both nil and R */ | |
| C_LOREG, | |
| C_PC, | |
| C_SP, | |
| C_HREG, | |
| C_ADDR, /* reference to relocatable address */ | |
| C_GOK, | |
| }; | |
| static Optab optab[] = | |
| { | |
| /* struct Optab: | |
| OPCODE, from, prog->reg, to, type,size,param,flag */ | |
| { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, | |
| { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, | |
| { AADD, C_REG, C_REG, C_REG, 1, 4, 0 }, | |
| { AADD, C_REG, C_NONE, C_REG, 1, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_REG, 1, 4, 0 }, | |
| { AMVN, C_REG, C_NONE, C_REG, 1, 4, 0 }, | |
| { ACMP, C_REG, C_REG, C_NONE, 1, 4, 0 }, | |
| { AADD, C_RCON, C_REG, C_REG, 2, 4, 0 }, | |
| { AADD, C_RCON, C_NONE, C_REG, 2, 4, 0 }, | |
| { AMOVW, C_RCON, C_NONE, C_REG, 2, 4, 0 }, | |
| { AMVN, C_RCON, C_NONE, C_REG, 2, 4, 0 }, | |
| { ACMP, C_RCON, C_REG, C_NONE, 2, 4, 0 }, | |
| { AADD, C_SHIFT,C_REG, C_REG, 3, 4, 0 }, | |
| { AADD, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, | |
| { AMVN, C_SHIFT,C_NONE, C_REG, 3, 4, 0 }, | |
| { ACMP, C_SHIFT,C_REG, C_NONE, 3, 4, 0 }, | |
| { AMOVW, C_RACON,C_NONE, C_REG, 4, 4, REGSP }, | |
| { AB, C_NONE, C_NONE, C_SBRA, 5, 4, 0, LPOOL }, | |
| { ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, | |
| { ABX, C_NONE, C_NONE, C_SBRA, 74, 20, 0 }, | |
| { ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, | |
| { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, | |
| { ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0 }, | |
| { ABL, C_REG, C_NONE, C_ROREG, 7, 4, 0 }, | |
| { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, | |
| { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, | |
| { ASLL, C_RCON, C_REG, C_REG, 8, 4, 0 }, | |
| { ASLL, C_RCON, C_NONE, C_REG, 8, 4, 0 }, | |
| { ASLL, C_REG, C_NONE, C_REG, 9, 4, 0 }, | |
| { ASLL, C_REG, C_REG, C_REG, 9, 4, 0 }, | |
| { ASWI, C_NONE, C_NONE, C_NONE, 10, 4, 0 }, | |
| { ASWI, C_NONE, C_NONE, C_LOREG, 10, 4, 0 }, | |
| { ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0 }, | |
| { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, | |
| { AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0 }, | |
| { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, | |
| { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, | |
| { AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM }, | |
| { AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4}, | |
| { AADD, C_NCON, C_REG, C_REG, 13, 8, 0 }, | |
| { AADD, C_NCON, C_NONE, C_REG, 13, 8, 0 }, | |
| { AMVN, C_NCON, C_NONE, C_REG, 13, 8, 0 }, | |
| { ACMP, C_NCON, C_REG, C_NONE, 13, 8, 0 }, | |
| { AADD, C_LCON, C_REG, C_REG, 13, 8, 0, LFROM }, | |
| { AADD, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM }, | |
| { AMVN, C_LCON, C_NONE, C_REG, 13, 8, 0, LFROM }, | |
| { ACMP, C_LCON, C_REG, C_NONE, 13, 8, 0, LFROM }, | |
| { AMOVB, C_REG, C_NONE, C_REG, 1, 4, 0 }, | |
| { AMOVBS, C_REG, C_NONE, C_REG, 14, 8, 0 }, | |
| { AMOVBU, C_REG, C_NONE, C_REG, 58, 4, 0 }, | |
| { AMOVH, C_REG, C_NONE, C_REG, 1, 4, 0 }, | |
| { AMOVHS, C_REG, C_NONE, C_REG, 14, 8, 0 }, | |
| { AMOVHU, C_REG, C_NONE, C_REG, 14, 8, 0 }, | |
| { AMUL, C_REG, C_REG, C_REG, 15, 4, 0 }, | |
| { AMUL, C_REG, C_NONE, C_REG, 15, 4, 0 }, | |
| { ADIV, C_REG, C_REG, C_REG, 16, 4, 0 }, | |
| { ADIV, C_REG, C_NONE, C_REG, 16, 4, 0 }, | |
| { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, | |
| { AMULA, C_REG, C_REG, C_REGREG2, 17, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, | |
| { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, | |
| { AMOVB, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, | |
| { AMOVB, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, | |
| { AMOVBS, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, | |
| { AMOVBS, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, | |
| { AMOVBU, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, | |
| { AMOVBU, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, | |
| { AMOVW, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, | |
| { AMOVW, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, | |
| { AMOVBU, C_SAUTO,C_NONE, C_REG, 21, 4, REGSP }, | |
| { AMOVBU, C_SOREG,C_NONE, C_REG, 21, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, | |
| { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, | |
| { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, | |
| { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, | |
| { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVBS, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, | |
| { AMOVBS, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, | |
| { AMOVBS, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, | |
| { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, | |
| { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, | |
| { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, | |
| { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, | |
| { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, | |
| { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, | |
| { AMOVW, C_PSR, C_NONE, C_REG, 35, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_PSR, 36, 4, 0 }, | |
| { AMOVW, C_RCON, C_NONE, C_PSR, 37, 4, 0 }, | |
| { AMOVM, C_LCON, C_NONE, C_SOREG, 38, 4, 0 }, | |
| { AMOVM, C_SOREG,C_NONE, C_LCON, 39, 4, 0 }, | |
| { ASWPW, C_SOREG,C_REG, C_REG, 40, 4, 0 }, | |
| { ARFE, C_NONE, C_NONE, C_NONE, 41, 4, 0 }, | |
| { AMOVF, C_FREG, C_NONE, C_FAUTO, 50, 4, REGSP }, | |
| { AMOVF, C_FREG, C_NONE, C_FOREG, 50, 4, 0 }, | |
| { AMOVF, C_FAUTO,C_NONE, C_FREG, 51, 4, REGSP }, | |
| { AMOVF, C_FOREG,C_NONE, C_FREG, 51, 4, 0 }, | |
| { AMOVF, C_FREG, C_NONE, C_LAUTO, 52, 12, REGSP, LTO }, | |
| { AMOVF, C_FREG, C_NONE, C_LOREG, 52, 12, 0, LTO }, | |
| { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, | |
| { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, | |
| { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4}, | |
| { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, | |
| { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, | |
| { AMOVF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_FCR, 56, 4, 0 }, | |
| { AMOVW, C_FCR, C_NONE, C_REG, 57, 4, 0 }, | |
| { AMOVW, C_SHIFT,C_NONE, C_REG, 59, 4, 0 }, | |
| { AMOVBU, C_SHIFT,C_NONE, C_REG, 59, 4, 0 }, | |
| { AMOVB, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, | |
| { AMOVBS, C_SHIFT,C_NONE, C_REG, 60, 4, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, | |
| { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, | |
| { AMOVBS, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, | |
| { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, | |
| { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 }, | |
| { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0, LPCREL, 0 }, | |
| { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, | |
| { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, | |
| { AMOVHS, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, | |
| { AMOVHS, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, | |
| { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, | |
| { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, 0 }, | |
| { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, | |
| { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, | |
| { AMOVBS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, | |
| { AMOVBS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, | |
| { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, | |
| { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, | |
| { AMOVHS, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, | |
| { AMOVHS, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, | |
| { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, | |
| { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, 0 }, | |
| { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, | |
| { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, | |
| { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVHS, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, | |
| { AMOVHS, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, | |
| { AMOVHS, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, | |
| { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, | |
| { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, | |
| { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, | |
| { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, | |
| { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVBS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, | |
| { AMOVBS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, | |
| { AMOVBS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, | |
| { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, | |
| { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVHS, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, | |
| { AMOVHS, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, | |
| { AMOVHS, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, | |
| { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, | |
| { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, | |
| { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, | |
| { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, | |
| { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, | |
| { AMOVF, C_ZFCON,C_NONE, C_FREG, 80, 8, 0 }, | |
| { AMOVF, C_SFCON,C_NONE, C_FREG, 81, 4, 0 }, | |
| { ACMPF, C_FREG, C_REG, C_NONE, 82, 8, 0 }, | |
| { ACMPF, C_FREG, C_NONE, C_NONE, 83, 8, 0 }, | |
| { AMOVFW, C_FREG, C_NONE, C_FREG, 84, 4, 0 }, | |
| { AMOVWF, C_FREG, C_NONE, C_FREG, 85, 4, 0 }, | |
| { AMOVFW, C_FREG, C_NONE, C_REG, 86, 8, 0 }, | |
| { AMOVWF, C_REG, C_NONE, C_FREG, 87, 8, 0 }, | |
| { AMOVW, C_REG, C_NONE, C_FREG, 88, 4, 0 }, | |
| { AMOVW, C_FREG, C_NONE, C_REG, 89, 4, 0 }, | |
| { ATST, C_REG, C_NONE, C_NONE, 90, 4, 0 }, | |
| { ALDREXD, C_SOREG,C_NONE, C_REG, 91, 4, 0 }, | |
| { ASTREXD, C_SOREG,C_REG, C_REG, 92, 4, 0 }, | |
| { APLD, C_SOREG,C_NONE, C_NONE, 95, 4, 0 }, | |
| { AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0 }, | |
| { ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0 }, | |
| { AMULWT, C_REG, C_REG, C_REG, 98, 4, 0 }, | |
| { AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 }, | |
| { AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 }, | |
| { APCDATA, C_LCON, C_NONE, C_LCON, 0, 0, 0 }, | |
| { AFUNCDATA, C_LCON, C_NONE, C_ADDR, 0, 0, 0 }, | |
| { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, | |
| }; | |
| static struct { | |
| uint32 start; | |
| uint32 size; | |
| uint32 extra; | |
| } pool; | |
| static int checkpool(Link*, Prog*, int); | |
| static int flushpool(Link*, Prog*, int, int); | |
| static void addpool(Link*, Prog*, Addr*); | |
| static void asmout(Link*, Prog*, Optab*, int32*); | |
| static Optab* oplook(Link*, Prog*); | |
| static int32 oprrr(Link*, int, int); | |
| static int32 olr(Link*, int32, int, int, int); | |
| static int32 olhr(Link*, int32, int, int, int); | |
| static int32 olrr(Link*, int, int, int, int); | |
| static int32 olhrr(Link*, int, int, int, int); | |
| static int32 osr(Link*, int, int, int32, int, int); | |
| static int32 oshr(Link*, int, int32, int, int); | |
| static int32 ofsr(Link*, int, int, int32, int, int, Prog*); | |
| static int32 osrr(Link*, int, int, int, int); | |
| static int32 oshrr(Link*, int, int, int, int); | |
| static int32 omvl(Link*, Prog*, Addr*, int); | |
| static int32 immaddr(int32); | |
| static int aclass(Link*, Addr*); | |
| static int32 immrot(uint32); | |
| static int32 immaddr(int32); | |
| static int32 opbra(Link*, int, int); | |
| static Opcross opcross[8]; | |
| static Oprang oprange[ALAST]; | |
| static char xcmp[C_GOK+1][C_GOK+1]; | |
| static uchar repop[ALAST]; | |
| static Prog zprg = { | |
| .as = AGOK, | |
| .scond = C_SCOND_NONE, | |
| .reg = NREG, | |
| .from = { | |
| .name = D_NONE, | |
| .type = D_NONE, | |
| .reg = NREG, | |
| }, | |
| .to = { | |
| .name = D_NONE, | |
| .type = D_NONE, | |
| .reg = NREG, | |
| }, | |
| }; | |
| static void | |
| nocache(Prog *p) | |
| { | |
| p->optab = 0; | |
| p->from.class = 0; | |
| p->to.class = 0; | |
| } | |
| static int | |
| scan(Link *ctxt, Prog *op, Prog *p, int c) | |
| { | |
| Prog *q; | |
| for(q = op->link; q != p && q != nil; q = q->link){ | |
| q->pc = c; | |
| c += oplook(ctxt, q)->size; | |
| nocache(q); | |
| } | |
| return c; | |
| } | |
| /* size of a case statement including jump table */ | |
| static int32 | |
| casesz(Link *ctxt, Prog *p) | |
| { | |
| int jt = 0; | |
| int32 n = 0; | |
| Optab *o; | |
| for( ; p != nil; p = p->link){ | |
| if(p->as == ABCASE) | |
| jt = 1; | |
| else if(jt) | |
| break; | |
| o = oplook(ctxt, p); | |
| n += o->size; | |
| } | |
| return n; | |
| } | |
| static void buildop(Link*); | |
| void | |
| span5(Link *ctxt, LSym *cursym) | |
| { | |
| Prog *p, *op; | |
| Optab *o; | |
| int m, bflag, i, v; | |
| int32 c, out[6]; | |
| uchar *bp; | |
| p = cursym->text; | |
| if(p == nil || p->link == nil) // handle external functions and ELF section symbols | |
| return; | |
| if(oprange[AAND].start == nil) | |
| buildop(ctxt); | |
| ctxt->cursym = cursym; | |
| ctxt->autosize = p->to.offset + 4; | |
| c = 0; | |
| for(op = p, p = p->link; p != nil; op = p, p = p->link) { | |
| ctxt->curp = p; | |
| p->pc = c; | |
| o = oplook(ctxt, p); | |
| m = o->size; | |
| // must check literal pool here in case p generates many instructions | |
| if(ctxt->blitrl){ | |
| if(checkpool(ctxt, op, p->as == ACASE ? casesz(ctxt, p) : m)) { | |
| p->pc = scan(ctxt, op, p, c); | |
| c = p->pc; | |
| } | |
| } | |
| if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { | |
| ctxt->diag("zero-width instruction\n%P", p); | |
| continue; | |
| } | |
| switch(o->flag & (LFROM|LTO|LPOOL)) { | |
| case LFROM: | |
| addpool(ctxt, p, &p->from); | |
| break; | |
| case LTO: | |
| addpool(ctxt, p, &p->to); | |
| break; | |
| case LPOOL: | |
| if ((p->scond&C_SCOND) == C_SCOND_NONE) | |
| flushpool(ctxt, p, 0, 0); | |
| break; | |
| } | |
| if(p->as==AMOVW && p->to.type==D_REG && p->to.reg==REGPC && (p->scond&C_SCOND) == C_SCOND_NONE) | |
| flushpool(ctxt, p, 0, 0); | |
| c += m; | |
| } | |
| if(ctxt->blitrl){ | |
| if(checkpool(ctxt, op, 0)) | |
| c = scan(ctxt, op, nil, c); | |
| } | |
| cursym->size = c; | |
| /* | |
| * if any procedure is large enough to | |
| * generate a large SBRA branch, then | |
| * generate extra passes putting branches | |
| * around jmps to fix. this is rare. | |
| */ | |
| do { | |
| if(ctxt->debugvlog) | |
| Bprint(ctxt->bso, "%5.2f span1\n", cputime()); | |
| bflag = 0; | |
| c = 0; | |
| for(p = cursym->text; p != nil; p = p->link) { | |
| ctxt->curp = p; | |
| p->pc = c; | |
| o = oplook(ctxt,p); | |
| /* very large branches | |
| if(o->type == 6 && p->pcond) { | |
| otxt = p->pcond->pc - c; | |
| if(otxt < 0) | |
| otxt = -otxt; | |
| if(otxt >= (1L<<17) - 10) { | |
| q = ctxt->arch->prg(); | |
| q->link = p->link; | |
| p->link = q; | |
| q->as = AB; | |
| q->to.type = D_BRANCH; | |
| q->pcond = p->pcond; | |
| p->pcond = q; | |
| q = ctxt->arch->prg(); | |
| q->link = p->link; | |
| p->link = q; | |
| q->as = AB; | |
| q->to.type = D_BRANCH; | |
| q->pcond = q->link->link; | |
| bflag = 1; | |
| } | |
| } | |
| */ | |
| m = o->size; | |
| if(m == 0 && (p->as != AFUNCDATA && p->as != APCDATA)) { | |
| if(p->as == ATEXT) { | |
| ctxt->autosize = p->to.offset + 4; | |
| continue; | |
| } | |
| ctxt->diag("zero-width instruction\n%P", p); | |
| continue; | |
| } | |
| c += m; | |
| } | |
| cursym->size = c; | |
| } while(bflag); | |
| /* | |
| * lay out the code. all the pc-relative code references, | |
| * even cross-function, are resolved now; | |
| * only data references need to be relocated. | |
| * with more work we could leave cross-function | |
| * code references to be relocated too, and then | |
| * perhaps we'd be able to parallelize the span loop above. | |
| */ | |
| if(ctxt->gmsym == nil) | |
| ctxt->gmsym = linklookup(ctxt, "runtime.tlsgm", 0); | |
| p = cursym->text; | |
| ctxt->autosize = p->to.offset + 4; | |
| symgrow(ctxt, cursym, cursym->size); | |
| bp = cursym->p; | |
| for(p = p->link; p != nil; p = p->link) { | |
| ctxt->pc = p->pc; | |
| ctxt->curp = p; | |
| o = oplook(ctxt, p); | |
| asmout(ctxt, p, o, out); | |
| for(i=0; i<o->size/4; i++) { | |
| v = out[i]; | |
| *bp++ = v; | |
| *bp++ = v>>8; | |
| *bp++ = v>>16; | |
| *bp++ = v>>24; | |
| } | |
| } | |
| } | |
| /* | |
| * when the first reference to the literal pool threatens | |
| * to go out of range of a 12-bit PC-relative offset, | |
| * drop the pool now, and branch round it. | |
| * this happens only in extended basic blocks that exceed 4k. | |
| */ | |
| static int | |
| checkpool(Link *ctxt, Prog *p, int sz) | |
| { | |
| if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) | |
| return flushpool(ctxt, p, 1, 0); | |
| else if(p->link == nil) | |
| return flushpool(ctxt, p, 2, 0); | |
| return 0; | |
| } | |
| static int | |
| flushpool(Link *ctxt, Prog *p, int skip, int force) | |
| { | |
| Prog *q; | |
| if(ctxt->blitrl) { | |
| if(skip){ | |
| if(0 && skip==1)print("note: flush literal pool at %llux: len=%ud ref=%ux\n", p->pc+4, pool.size, pool.start); | |
| q = ctxt->arch->prg(); | |
| q->as = AB; | |
| q->to.type = D_BRANCH; | |
| q->pcond = p->link; | |
| q->link = ctxt->blitrl; | |
| q->lineno = p->lineno; | |
| ctxt->blitrl = q; | |
| } | |
| else if(!force && (p->pc+pool.size-pool.start < 2048)) | |
| return 0; | |
| ctxt->elitrl->link = p->link; | |
| p->link = ctxt->blitrl; | |
| // BUG(minux): how to correctly handle line number for constant pool entries? | |
| // for now, we set line number to the last instruction preceding them at least | |
| // this won't bloat the .debug_line tables | |
| while(ctxt->blitrl) { | |
| ctxt->blitrl->lineno = p->lineno; | |
| ctxt->blitrl = ctxt->blitrl->link; | |
| } | |
| ctxt->blitrl = 0; /* BUG: should refer back to values until out-of-range */ | |
| ctxt->elitrl = 0; | |
| pool.size = 0; | |
| pool.start = 0; | |
| pool.extra = 0; | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| static void | |
| addpool(Link *ctxt, Prog *p, Addr *a) | |
| { | |
| Prog *q, t; | |
| int c; | |
| c = aclass(ctxt, a); | |
| t = zprg; | |
| t.as = AWORD; | |
| switch(c) { | |
| default: | |
| t.to = *a; | |
| if(ctxt->flag_shared && t.to.sym != nil) | |
| t.pcrel = p; | |
| break; | |
| case C_SROREG: | |
| case C_LOREG: | |
| case C_ROREG: | |
| case C_FOREG: | |
| case C_SOREG: | |
| case C_HOREG: | |
| case C_FAUTO: | |
| case C_SAUTO: | |
| case C_LAUTO: | |
| case C_LACON: | |
| t.to.type = D_CONST; | |
| t.to.offset = ctxt->instoffset; | |
| break; | |
| } | |
| if(t.pcrel == nil) { | |
| for(q = ctxt->blitrl; q != nil; q = q->link) /* could hash on t.t0.offset */ | |
| if(q->pcrel == nil && memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { | |
| p->pcond = q; | |
| return; | |
| } | |
| } | |
| q = ctxt->arch->prg(); | |
| *q = t; | |
| q->pc = pool.size; | |
| if(ctxt->blitrl == nil) { | |
| ctxt->blitrl = q; | |
| pool.start = p->pc; | |
| } else | |
| ctxt->elitrl->link = q; | |
| ctxt->elitrl = q; | |
| pool.size += 4; | |
| p->pcond = q; | |
| } | |
| static int32 | |
| regoff(Link *ctxt, Addr *a) | |
| { | |
| ctxt->instoffset = 0; | |
| aclass(ctxt, a); | |
| return ctxt->instoffset; | |
| } | |
| static int32 | |
| immrot(uint32 v) | |
| { | |
| int i; | |
| for(i=0; i<16; i++) { | |
| if((v & ~0xff) == 0) | |
| return (i<<8) | v | (1<<25); | |
| v = (v<<2) | (v>>30); | |
| } | |
| return 0; | |
| } | |
| static int32 | |
| immaddr(int32 v) | |
| { | |
| if(v >= 0 && v <= 0xfff) | |
| return (v & 0xfff) | | |
| (1<<24) | /* pre indexing */ | |
| (1<<23); /* pre indexing, up */ | |
| if(v >= -0xfff && v < 0) | |
| return (-v & 0xfff) | | |
| (1<<24); /* pre indexing */ | |
| return 0; | |
| } | |
| static int | |
| immfloat(int32 v) | |
| { | |
| return (v & 0xC03) == 0; /* offset will fit in floating-point load/store */ | |
| } | |
| static int | |
| immhalf(int32 v) | |
| { | |
| if(v >= 0 && v <= 0xff) | |
| return v| | |
| (1<<24)| /* pre indexing */ | |
| (1<<23); /* pre indexing, up */ | |
| if(v >= -0xff && v < 0) | |
| return (-v & 0xff)| | |
| (1<<24); /* pre indexing */ | |
| return 0; | |
| } | |
| static int | |
| aclass(Link *ctxt, Addr *a) | |
| { | |
| LSym *s; | |
| int t; | |
| switch(a->type) { | |
| case D_NONE: | |
| return C_NONE; | |
| case D_REG: | |
| return C_REG; | |
| case D_REGREG: | |
| return C_REGREG; | |
| case D_REGREG2: | |
| return C_REGREG2; | |
| case D_SHIFT: | |
| return C_SHIFT; | |
| case D_FREG: | |
| return C_FREG; | |
| case D_FPCR: | |
| return C_FCR; | |
| case D_OREG: | |
| switch(a->name) { | |
| case D_EXTERN: | |
| case D_STATIC: | |
| if(a->sym == 0 || a->sym->name == 0) { | |
| print("null sym external\n"); | |
| print("%D\n", a); | |
| return C_GOK; | |
| } | |
| ctxt->instoffset = 0; // s.b. unused but just in case | |
| return C_ADDR; | |
| case D_AUTO: | |
| ctxt->instoffset = ctxt->autosize + a->offset; | |
| t = immaddr(ctxt->instoffset); | |
| if(t){ | |
| if(immhalf(ctxt->instoffset)) | |
| return immfloat(t) ? C_HFAUTO : C_HAUTO; | |
| if(immfloat(t)) | |
| return C_FAUTO; | |
| return C_SAUTO; | |
| } | |
| return C_LAUTO; | |
| case D_PARAM: | |
| ctxt->instoffset = ctxt->autosize + a->offset + 4L; | |
| t = immaddr(ctxt->instoffset); | |
| if(t){ | |
| if(immhalf(ctxt->instoffset)) | |
| return immfloat(t) ? C_HFAUTO : C_HAUTO; | |
| if(immfloat(t)) | |
| return C_FAUTO; | |
| return C_SAUTO; | |
| } | |
| return C_LAUTO; | |
| case D_NONE: | |
| ctxt->instoffset = a->offset; | |
| t = immaddr(ctxt->instoffset); | |
| if(t) { | |
| if(immhalf(ctxt->instoffset)) /* n.b. that it will also satisfy immrot */ | |
| return immfloat(t) ? C_HFOREG : C_HOREG; | |
| if(immfloat(t)) | |
| return C_FOREG; /* n.b. that it will also satisfy immrot */ | |
| t = immrot(ctxt->instoffset); | |
| if(t) | |
| return C_SROREG; | |
| if(immhalf(ctxt->instoffset)) | |
| return C_HOREG; | |
| return C_SOREG; | |
| } | |
| t = immrot(ctxt->instoffset); | |
| if(t) | |
| return C_ROREG; | |
| return C_LOREG; | |
| } | |
| return C_GOK; | |
| case D_PSR: | |
| return C_PSR; | |
| case D_OCONST: | |
| switch(a->name) { | |
| case D_EXTERN: | |
| case D_STATIC: | |
| ctxt->instoffset = 0; // s.b. unused but just in case | |
| return C_ADDR; | |
| } | |
| return C_GOK; | |
| case D_FCONST: | |
| if(chipzero5(ctxt, a->u.dval) >= 0) | |
| return C_ZFCON; | |
| if(chipfloat5(ctxt, a->u.dval) >= 0) | |
| return C_SFCON; | |
| return C_LFCON; | |
| case D_CONST: | |
| case D_CONST2: | |
| switch(a->name) { | |
| case D_NONE: | |
| ctxt->instoffset = a->offset; | |
| if(a->reg != NREG) | |
| goto aconsize; | |
| t = immrot(ctxt->instoffset); | |
| if(t) | |
| return C_RCON; | |
| t = immrot(~ctxt->instoffset); | |
| if(t) | |
| return C_NCON; | |
| return C_LCON; | |
| case D_EXTERN: | |
| case D_STATIC: | |
| s = a->sym; | |
| if(s == nil) | |
| break; | |
| ctxt->instoffset = 0; // s.b. unused but just in case | |
| return C_LCONADDR; | |
| case D_AUTO: | |
| ctxt->instoffset = ctxt->autosize + a->offset; | |
| goto aconsize; | |
| case D_PARAM: | |
| ctxt->instoffset = ctxt->autosize + a->offset + 4L; | |
| aconsize: | |
| t = immrot(ctxt->instoffset); | |
| if(t) | |
| return C_RACON; | |
| return C_LACON; | |
| } | |
| return C_GOK; | |
| case D_BRANCH: | |
| return C_SBRA; | |
| } | |
| return C_GOK; | |
| } | |
| static void | |
| prasm(Prog *p) | |
| { | |
| print("%P\n", p); | |
| } | |
| static Optab* | |
| oplook(Link *ctxt, Prog *p) | |
| { | |
| int a1, a2, a3, r; | |
| char *c1, *c3; | |
| Optab *o, *e; | |
| a1 = p->optab; | |
| if(a1) | |
| return optab+(a1-1); | |
| a1 = p->from.class; | |
| if(a1 == 0) { | |
| a1 = aclass(ctxt, &p->from) + 1; | |
| p->from.class = a1; | |
| } | |
| a1--; | |
| a3 = p->to.class; | |
| if(a3 == 0) { | |
| a3 = aclass(ctxt, &p->to) + 1; | |
| p->to.class = a3; | |
| } | |
| a3--; | |
| a2 = C_NONE; | |
| if(p->reg != NREG) | |
| a2 = C_REG; | |
| r = p->as; | |
| o = oprange[r].start; | |
| if(o == 0) { | |
| a1 = opcross[repop[r]][a1][a2][a3]; | |
| if(a1) { | |
| p->optab = a1+1; | |
| return optab+a1; | |
| } | |
| o = oprange[r].stop; /* just generate an error */ | |
| } | |
| if(0 /*debug['O']*/) { | |
| print("oplook %A %d %d %d\n", | |
| (int)p->as, a1, a2, a3); | |
| print(" %d %d\n", p->from.type, p->to.type); | |
| } | |
| e = oprange[r].stop; | |
| c1 = xcmp[a1]; | |
| c3 = xcmp[a3]; | |
| for(; o<e; o++) | |
| if(o->a2 == a2) | |
| if(c1[o->a1]) | |
| if(c3[o->a3]) { | |
| p->optab = (o-optab)+1; | |
| return o; | |
| } | |
| ctxt->diag("illegal combination %P; %d %d %d, %d %d", | |
| p, a1, a2, a3, p->from.type, p->to.type); | |
| ctxt->diag("from %d %d to %d %d\n", p->from.type, p->from.name, p->to.type, p->to.name); | |
| prasm(p); | |
| if(o == 0) | |
| o = optab; | |
| return o; | |
| } | |
| static int | |
| cmp(int a, int b) | |
| { | |
| if(a == b) | |
| return 1; | |
| switch(a) { | |
| case C_LCON: | |
| if(b == C_RCON || b == C_NCON) | |
| return 1; | |
| break; | |
| case C_LACON: | |
| if(b == C_RACON) | |
| return 1; | |
| break; | |
| case C_LFCON: | |
| if(b == C_ZFCON || b == C_SFCON) | |
| return 1; | |
| break; | |
| case C_HFAUTO: | |
| return b == C_HAUTO || b == C_FAUTO; | |
| case C_FAUTO: | |
| case C_HAUTO: | |
| return b == C_HFAUTO; | |
| case C_SAUTO: | |
| return cmp(C_HFAUTO, b); | |
| case C_LAUTO: | |
| return cmp(C_SAUTO, b); | |
| case C_HFOREG: | |
| return b == C_HOREG || b == C_FOREG; | |
| case C_FOREG: | |
| case C_HOREG: | |
| return b == C_HFOREG; | |
| case C_SROREG: | |
| return cmp(C_SOREG, b) || cmp(C_ROREG, b); | |
| case C_SOREG: | |
| case C_ROREG: | |
| return b == C_SROREG || cmp(C_HFOREG, b); | |
| case C_LOREG: | |
| return cmp(C_SROREG, b); | |
| case C_LBRA: | |
| if(b == C_SBRA) | |
| return 1; | |
| break; | |
| case C_HREG: | |
| return cmp(C_SP, b) || cmp(C_PC, b); | |
| } | |
| return 0; | |
| } | |
| static int | |
| ocmp(const void *a1, const void *a2) | |
| { | |
| Optab *p1, *p2; | |
| int n; | |
| p1 = (Optab*)a1; | |
| p2 = (Optab*)a2; | |
| n = p1->as - p2->as; | |
| if(n) | |
| return n; | |
| n = p1->a1 - p2->a1; | |
| if(n) | |
| return n; | |
| n = p1->a2 - p2->a2; | |
| if(n) | |
| return n; | |
| n = p1->a3 - p2->a3; | |
| if(n) | |
| return n; | |
| return 0; | |
| } | |
| static void | |
| buildop(Link *ctxt) | |
| { | |
| int i, n, r; | |
| for(i=0; i<C_GOK; i++) | |
| for(n=0; n<C_GOK; n++) | |
| xcmp[i][n] = cmp(n, i); | |
| for(n=0; optab[n].as != AXXX; n++) { | |
| if((optab[n].flag & LPCREL) != 0) { | |
| if(ctxt->flag_shared) | |
| optab[n].size += optab[n].pcrelsiz; | |
| else | |
| optab[n].flag &= ~LPCREL; | |
| } | |
| } | |
| qsort(optab, n, sizeof(optab[0]), ocmp); | |
| for(i=0; i<n; i++) { | |
| r = optab[i].as; | |
| oprange[r].start = optab+i; | |
| while(optab[i].as == r) | |
| i++; | |
| oprange[r].stop = optab+i; | |
| i--; | |
| switch(r) | |
| { | |
| default: | |
| ctxt->diag("unknown op in build: %A", r); | |
| sysfatal("bad code"); | |
| case AADD: | |
| oprange[AAND] = oprange[r]; | |
| oprange[AEOR] = oprange[r]; | |
| oprange[ASUB] = oprange[r]; | |
| oprange[ARSB] = oprange[r]; | |
| oprange[AADC] = oprange[r]; | |
| oprange[ASBC] = oprange[r]; | |
| oprange[ARSC] = oprange[r]; | |
| oprange[AORR] = oprange[r]; | |
| oprange[ABIC] = oprange[r]; | |
| break; | |
| case ACMP: | |
| oprange[ATEQ] = oprange[r]; | |
| oprange[ACMN] = oprange[r]; | |
| break; | |
| case AMVN: | |
| break; | |
| case ABEQ: | |
| oprange[ABNE] = oprange[r]; | |
| oprange[ABCS] = oprange[r]; | |
| oprange[ABHS] = oprange[r]; | |
| oprange[ABCC] = oprange[r]; | |
| oprange[ABLO] = oprange[r]; | |
| oprange[ABMI] = oprange[r]; | |
| oprange[ABPL] = oprange[r]; | |
| oprange[ABVS] = oprange[r]; | |
| oprange[ABVC] = oprange[r]; | |
| oprange[ABHI] = oprange[r]; | |
| oprange[ABLS] = oprange[r]; | |
| oprange[ABGE] = oprange[r]; | |
| oprange[ABLT] = oprange[r]; | |
| oprange[ABGT] = oprange[r]; | |
| oprange[ABLE] = oprange[r]; | |
| break; | |
| case ASLL: | |
| oprange[ASRL] = oprange[r]; | |
| oprange[ASRA] = oprange[r]; | |
| break; | |
| case AMUL: | |
| oprange[AMULU] = oprange[r]; | |
| break; | |
| case ADIV: | |
| oprange[AMOD] = oprange[r]; | |
| oprange[AMODU] = oprange[r]; | |
| oprange[ADIVU] = oprange[r]; | |
| break; | |
| case AMOVW: | |
| case AMOVB: | |
| case AMOVBS: | |
| case AMOVBU: | |
| case AMOVH: | |
| case AMOVHS: | |
| case AMOVHU: | |
| break; | |
| case ASWPW: | |
| oprange[ASWPBU] = oprange[r]; | |
| break; | |
| case AB: | |
| case ABL: | |
| case ABX: | |
| case ABXRET: | |
| case ASWI: | |
| case AWORD: | |
| case AMOVM: | |
| case ARFE: | |
| case ATEXT: | |
| case AUSEFIELD: | |
| case ACASE: | |
| case ABCASE: | |
| case ATYPE: | |
| break; | |
| case AADDF: | |
| oprange[AADDD] = oprange[r]; | |
| oprange[ASUBF] = oprange[r]; | |
| oprange[ASUBD] = oprange[r]; | |
| oprange[AMULF] = oprange[r]; | |
| oprange[AMULD] = oprange[r]; | |
| oprange[ADIVF] = oprange[r]; | |
| oprange[ADIVD] = oprange[r]; | |
| oprange[ASQRTF] = oprange[r]; | |
| oprange[ASQRTD] = oprange[r]; | |
| oprange[AMOVFD] = oprange[r]; | |
| oprange[AMOVDF] = oprange[r]; | |
| oprange[AABSF] = oprange[r]; | |
| oprange[AABSD] = oprange[r]; | |
| break; | |
| case ACMPF: | |
| oprange[ACMPD] = oprange[r]; | |
| break; | |
| case AMOVF: | |
| oprange[AMOVD] = oprange[r]; | |
| break; | |
| case AMOVFW: | |
| oprange[AMOVDW] = oprange[r]; | |
| break; | |
| case AMOVWF: | |
| oprange[AMOVWD] = oprange[r]; | |
| break; | |
| case AMULL: | |
| oprange[AMULAL] = oprange[r]; | |
| oprange[AMULLU] = oprange[r]; | |
| oprange[AMULALU] = oprange[r]; | |
| break; | |
| case AMULWT: | |
| oprange[AMULWB] = oprange[r]; | |
| break; | |
| case AMULAWT: | |
| oprange[AMULAWB] = oprange[r]; | |
| break; | |
| case AMULA: | |
| case ALDREX: | |
| case ASTREX: | |
| case ALDREXD: | |
| case ASTREXD: | |
| case ATST: | |
| case APLD: | |
| case AUNDEF: | |
| case ACLZ: | |
| case AFUNCDATA: | |
| case APCDATA: | |
| break; | |
| } | |
| } | |
| } | |
| static void | |
| asmout(Link *ctxt, Prog *p, Optab *o, int32 *out) | |
| { | |
| int32 o1, o2, o3, o4, o5, o6, v; | |
| int r, rf, rt, rt2; | |
| Reloc *rel; | |
| ctxt->printp = p; | |
| o1 = 0; | |
| o2 = 0; | |
| o3 = 0; | |
| o4 = 0; | |
| o5 = 0; | |
| o6 = 0; | |
| ctxt->armsize += o->size; | |
| if(0 /*debug['P']*/) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); | |
| switch(o->type) { | |
| default: | |
| ctxt->diag("unknown asm %d", o->type); | |
| prasm(p); | |
| break; | |
| case 0: /* pseudo ops */ | |
| if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->fnptr); | |
| break; | |
| case 1: /* op R,[R],R */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| rf = p->from.reg; | |
| rt = p->to.reg; | |
| r = p->reg; | |
| if(p->to.type == D_NONE) | |
| rt = 0; | |
| if(p->as == AMOVB || p->as == AMOVH || p->as == AMOVW || p->as == AMVN) | |
| r = 0; | |
| else | |
| if(r == NREG) | |
| r = rt; | |
| o1 |= rf | (r<<16) | (rt<<12); | |
| break; | |
| case 2: /* movbu $I,[R],R */ | |
| aclass(ctxt, &p->from); | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= immrot(ctxt->instoffset); | |
| rt = p->to.reg; | |
| r = p->reg; | |
| if(p->to.type == D_NONE) | |
| rt = 0; | |
| if(p->as == AMOVW || p->as == AMVN) | |
| r = 0; | |
| else if(r == NREG) | |
| r = rt; | |
| o1 |= (r<<16) | (rt<<12); | |
| break; | |
| case 3: /* add R<<[IR],[R],R */ | |
| mov: | |
| aclass(ctxt, &p->from); | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= p->from.offset; | |
| rt = p->to.reg; | |
| r = p->reg; | |
| if(p->to.type == D_NONE) | |
| rt = 0; | |
| if(p->as == AMOVW || p->as == AMVN) | |
| r = 0; | |
| else if(r == NREG) | |
| r = rt; | |
| o1 |= (r<<16) | (rt<<12); | |
| break; | |
| case 4: /* add $I,[R],R */ | |
| aclass(ctxt, &p->from); | |
| o1 = oprrr(ctxt, AADD, p->scond); | |
| o1 |= immrot(ctxt->instoffset); | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 |= r << 16; | |
| o1 |= p->to.reg << 12; | |
| break; | |
| case 5: /* bra s */ | |
| o1 = opbra(ctxt, p->as, p->scond); | |
| v = -8; | |
| if(p->to.sym != nil) { | |
| rel = addrel(ctxt->cursym); | |
| rel->off = ctxt->pc; | |
| rel->siz = 4; | |
| rel->sym = p->to.sym; | |
| rel->add = o1 | ((v >> 2) & 0xffffff); | |
| rel->type = D_CALL; | |
| break; | |
| } | |
| if(p->pcond != nil) | |
| v = (p->pcond->pc - ctxt->pc) - 8; | |
| o1 |= (v >> 2) & 0xffffff; | |
| break; | |
| case 6: /* b ,O(R) -> add $O,R,PC */ | |
| aclass(ctxt, &p->to); | |
| o1 = oprrr(ctxt, AADD, p->scond); | |
| o1 |= immrot(ctxt->instoffset); | |
| o1 |= p->to.reg << 16; | |
| o1 |= REGPC << 12; | |
| break; | |
| case 7: /* bl (R) -> blx R */ | |
| aclass(ctxt, &p->to); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("%P: doesn't support BL offset(REG) where offset != 0", p); | |
| o1 = oprrr(ctxt, ABL, p->scond); | |
| o1 |= p->to.reg; | |
| break; | |
| case 8: /* sll $c,[R],R -> mov (R<<$c),R */ | |
| aclass(ctxt, &p->from); | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| r = p->reg; | |
| if(r == NREG) | |
| r = p->to.reg; | |
| o1 |= r; | |
| o1 |= (ctxt->instoffset&31) << 7; | |
| o1 |= p->to.reg << 12; | |
| break; | |
| case 9: /* sll R,[R],R -> mov (R<<R),R */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| r = p->reg; | |
| if(r == NREG) | |
| r = p->to.reg; | |
| o1 |= r; | |
| o1 |= (p->from.reg << 8) | (1<<4); | |
| o1 |= p->to.reg << 12; | |
| break; | |
| case 10: /* swi [$con] */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| if(p->to.type != D_NONE) { | |
| aclass(ctxt, &p->to); | |
| o1 |= ctxt->instoffset & 0xffffff; | |
| } | |
| break; | |
| case 11: /* word */ | |
| aclass(ctxt, &p->to); | |
| o1 = ctxt->instoffset; | |
| if(p->to.sym != nil) { | |
| // This case happens with words generated | |
| // in the PC stream as part of the literal pool. | |
| rel = addrel(ctxt->cursym); | |
| rel->off = ctxt->pc; | |
| rel->siz = 4; | |
| rel->sym = p->to.sym; | |
| rel->add = p->to.offset; | |
| // runtime.tlsgm (aka gmsym) is special. | |
| // Its "address" is the offset from the TLS thread pointer | |
| // to the thread-local g and m pointers. | |
| // Emit a TLS relocation instead of a standard one. | |
| if(rel->sym == ctxt->gmsym) { | |
| rel->type = D_TLS; | |
| if(ctxt->flag_shared) | |
| rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz; | |
| rel->xadd = rel->add; | |
| rel->xsym = rel->sym; | |
| } else if(ctxt->flag_shared) { | |
| rel->type = D_PCREL; | |
| rel->add += ctxt->pc - p->pcrel->pc - 8; | |
| } else | |
| rel->type = D_ADDR; | |
| o1 = 0; | |
| } | |
| break; | |
| case 12: /* movw $lcon, reg */ | |
| o1 = omvl(ctxt, p, &p->from, p->to.reg); | |
| if(o->flag & LPCREL) { | |
| o2 = oprrr(ctxt, AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; | |
| } | |
| break; | |
| case 13: /* op $lcon, [R], R */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = oprrr(ctxt, p->as, p->scond); | |
| o2 |= REGTMP; | |
| r = p->reg; | |
| if(p->as == AMOVW || p->as == AMVN) | |
| r = 0; | |
| else if(r == NREG) | |
| r = p->to.reg; | |
| o2 |= r << 16; | |
| if(p->to.type != D_NONE) | |
| o2 |= p->to.reg << 12; | |
| break; | |
| case 14: /* movb/movbu/movh/movhu R,R */ | |
| o1 = oprrr(ctxt, ASLL, p->scond); | |
| if(p->as == AMOVBU || p->as == AMOVHU) | |
| o2 = oprrr(ctxt, ASRL, p->scond); | |
| else | |
| o2 = oprrr(ctxt, ASRA, p->scond); | |
| r = p->to.reg; | |
| o1 |= (p->from.reg)|(r<<12); | |
| o2 |= (r)|(r<<12); | |
| if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) { | |
| o1 |= (24<<7); | |
| o2 |= (24<<7); | |
| } else { | |
| o1 |= (16<<7); | |
| o2 |= (16<<7); | |
| } | |
| break; | |
| case 15: /* mul r,[r,]r */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| rf = p->from.reg; | |
| rt = p->to.reg; | |
| r = p->reg; | |
| if(r == NREG) | |
| r = rt; | |
| if(rt == r) { | |
| r = rf; | |
| rf = rt; | |
| } | |
| if(0) | |
| if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) { | |
| ctxt->diag("bad registers in MUL"); | |
| prasm(p); | |
| } | |
| o1 |= (rf<<8) | r | (rt<<16); | |
| break; | |
| case 16: /* div r,[r,]r */ | |
| o1 = 0xf << 28; | |
| o2 = 0; | |
| break; | |
| case 17: | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| rf = p->from.reg; | |
| rt = p->to.reg; | |
| rt2 = p->to.offset; | |
| r = p->reg; | |
| o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12); | |
| break; | |
| case 20: /* mov/movb/movbu R,O(R) */ | |
| aclass(ctxt, &p->to); | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = osr(ctxt, p->as, p->from.reg, ctxt->instoffset, r, p->scond); | |
| break; | |
| case 21: /* mov/movbu O(R),R -> lr */ | |
| aclass(ctxt, &p->from); | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = olr(ctxt, ctxt->instoffset, r, p->to.reg, p->scond); | |
| if(p->as != AMOVW) | |
| o1 |= 1<<22; | |
| break; | |
| case 30: /* mov/movb/movbu R,L(R) */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = osrr(ctxt, p->from.reg, REGTMP,r, p->scond); | |
| if(p->as != AMOVW) | |
| o2 |= 1<<22; | |
| break; | |
| case 31: /* mov/movbu L(R),R -> lr[b] */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = olrr(ctxt, REGTMP,r, p->to.reg, p->scond); | |
| if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) | |
| o2 |= 1<<22; | |
| break; | |
| case 34: /* mov $lacon,R */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = oprrr(ctxt, AADD, p->scond); | |
| o2 |= REGTMP; | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 |= r << 16; | |
| if(p->to.type != D_NONE) | |
| o2 |= p->to.reg << 12; | |
| break; | |
| case 35: /* mov PSR,R */ | |
| o1 = (2<<23) | (0xf<<16) | (0<<0); | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| o1 |= (p->from.reg & 1) << 22; | |
| o1 |= p->to.reg << 12; | |
| break; | |
| case 36: /* mov R,PSR */ | |
| o1 = (2<<23) | (0x29f<<12) | (0<<4); | |
| if(p->scond & C_FBIT) | |
| o1 ^= 0x010 << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| o1 |= (p->to.reg & 1) << 22; | |
| o1 |= p->from.reg << 0; | |
| break; | |
| case 37: /* mov $con,PSR */ | |
| aclass(ctxt, &p->from); | |
| o1 = (2<<23) | (0x29f<<12) | (0<<4); | |
| if(p->scond & C_FBIT) | |
| o1 ^= 0x010 << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| o1 |= immrot(ctxt->instoffset); | |
| o1 |= (p->to.reg & 1) << 22; | |
| o1 |= p->from.reg << 0; | |
| break; | |
| case 38: /* movm $con,oreg -> stm */ | |
| o1 = (0x4 << 25); | |
| o1 |= p->from.offset & 0xffff; | |
| o1 |= p->to.reg << 16; | |
| aclass(ctxt, &p->to); | |
| goto movm; | |
| case 39: /* movm oreg,$con -> ldm */ | |
| o1 = (0x4 << 25) | (1 << 20); | |
| o1 |= p->to.offset & 0xffff; | |
| o1 |= p->from.reg << 16; | |
| aclass(ctxt, &p->from); | |
| movm: | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in MOVM; %P", p); | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| if(p->scond & C_PBIT) | |
| o1 |= 1 << 24; | |
| if(p->scond & C_UBIT) | |
| o1 |= 1 << 23; | |
| if(p->scond & C_SBIT) | |
| o1 |= 1 << 22; | |
| if(p->scond & C_WBIT) | |
| o1 |= 1 << 21; | |
| break; | |
| case 40: /* swp oreg,reg,reg */ | |
| aclass(ctxt, &p->from); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in SWP"); | |
| o1 = (0x2<<23) | (0x9<<4); | |
| if(p->as != ASWPW) | |
| o1 |= 1 << 22; | |
| o1 |= p->from.reg << 16; | |
| o1 |= p->reg << 0; | |
| o1 |= p->to.reg << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ | |
| o1 = 0xe8fd8000; | |
| break; | |
| case 50: /* floating point store */ | |
| v = regoff(ctxt, &p->to); | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = ofsr(ctxt, p->as, p->from.reg, v, r, p->scond, p); | |
| break; | |
| case 51: /* floating point load */ | |
| v = regoff(ctxt, &p->from); | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = ofsr(ctxt, p->as, p->to.reg, v, r, p->scond, p) | (1<<20); | |
| break; | |
| case 52: /* floating point store, int32 offset UGLY */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = oprrr(ctxt, AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; | |
| o3 = ofsr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond, p); | |
| break; | |
| case 53: /* floating point load, int32 offset UGLY */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = oprrr(ctxt, AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; | |
| o3 = ofsr(ctxt, p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); | |
| break; | |
| case 54: /* floating point arith */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| rf = p->from.reg; | |
| rt = p->to.reg; | |
| r = p->reg; | |
| if(r == NREG) { | |
| r = rt; | |
| if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD) | |
| r = 0; | |
| } | |
| o1 |= rf | (r<<16) | (rt<<12); | |
| break; | |
| case 56: /* move to FP[CS]R */ | |
| o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); | |
| o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12); | |
| break; | |
| case 57: /* move from FP[CS]R */ | |
| o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); | |
| o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20); | |
| break; | |
| case 58: /* movbu R,R */ | |
| o1 = oprrr(ctxt, AAND, p->scond); | |
| o1 |= immrot(0xff); | |
| rt = p->to.reg; | |
| r = p->from.reg; | |
| if(p->to.type == D_NONE) | |
| rt = 0; | |
| if(r == NREG) | |
| r = rt; | |
| o1 |= (r<<16) | (rt<<12); | |
| break; | |
| case 59: /* movw/bu R<<I(R),R -> ldr indexed */ | |
| if(p->from.reg == NREG) { | |
| if(p->as != AMOVW) | |
| ctxt->diag("byte MOV from shifter operand"); | |
| goto mov; | |
| } | |
| if(p->from.offset&(1<<4)) | |
| ctxt->diag("bad shift in LDR"); | |
| o1 = olrr(ctxt, p->from.offset, p->from.reg, p->to.reg, p->scond); | |
| if(p->as == AMOVBU) | |
| o1 |= 1<<22; | |
| break; | |
| case 60: /* movb R(R),R -> ldrsb indexed */ | |
| if(p->from.reg == NREG) { | |
| ctxt->diag("byte MOV from shifter operand"); | |
| goto mov; | |
| } | |
| if(p->from.offset&(~0xf)) | |
| ctxt->diag("bad shift in LDRSB"); | |
| o1 = olhrr(ctxt, p->from.offset, p->from.reg, p->to.reg, p->scond); | |
| o1 ^= (1<<5)|(1<<6); | |
| break; | |
| case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */ | |
| if(p->to.reg == NREG) | |
| ctxt->diag("MOV to shifter operand"); | |
| o1 = osrr(ctxt, p->from.reg, p->to.offset, p->to.reg, p->scond); | |
| if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) | |
| o1 |= 1<<22; | |
| break; | |
| case 62: /* case R -> movw R<<2(PC),PC */ | |
| if(o->flag & LPCREL) { | |
| o1 = oprrr(ctxt, AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; | |
| o2 = olrr(ctxt, REGTMP, REGPC, REGTMP, p->scond); | |
| o2 |= 2<<7; | |
| o3 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; | |
| } else { | |
| o1 = olrr(ctxt, p->from.reg, REGPC, REGPC, p->scond); | |
| o1 |= 2<<7; | |
| } | |
| break; | |
| case 63: /* bcase */ | |
| if(p->pcond != nil) { | |
| rel = addrel(ctxt->cursym); | |
| rel->off = ctxt->pc; | |
| rel->siz = 4; | |
| if(p->to.sym != nil && p->to.sym->type != 0) { | |
| rel->sym = p->to.sym; | |
| rel->add = p->to.offset; | |
| } else { | |
| rel->sym = ctxt->cursym; | |
| rel->add = p->pcond->pc; | |
| } | |
| if(o->flag & LPCREL) { | |
| rel->type = D_PCREL; | |
| rel->add += ctxt->pc - p->pcrel->pc - 16 + rel->siz; | |
| } else | |
| rel->type = D_ADDR; | |
| o1 = 0; | |
| } | |
| break; | |
| /* reloc ops */ | |
| case 64: /* mov/movb/movbu R,addr */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = osr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond); | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| case 65: /* mov/movbu addr,R */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = olr(ctxt, 0, REGTMP, p->to.reg, p->scond); | |
| if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) | |
| o2 |= 1<<22; | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| case 68: /* floating point store -> ADDR */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = ofsr(ctxt, p->as, p->from.reg, 0, REGTMP, p->scond, p); | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| case 69: /* floating point load <- ADDR */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = ofsr(ctxt, p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| /* ArmV4 ops: */ | |
| case 70: /* movh/movhu R,O(R) -> strh */ | |
| aclass(ctxt, &p->to); | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = oshr(ctxt, p->from.reg, ctxt->instoffset, r, p->scond); | |
| break; | |
| case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ | |
| aclass(ctxt, &p->from); | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o1 = olhr(ctxt, ctxt->instoffset, r, p->to.reg, p->scond); | |
| if(p->as == AMOVB || p->as == AMOVBS) | |
| o1 ^= (1<<5)|(1<<6); | |
| else if(p->as == AMOVH || p->as == AMOVHS) | |
| o1 ^= (1<<6); | |
| break; | |
| case 72: /* movh/movhu R,L(R) -> strh */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->to.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = oshrr(ctxt, p->from.reg, REGTMP,r, p->scond); | |
| break; | |
| case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| r = p->from.reg; | |
| if(r == NREG) | |
| r = o->param; | |
| o2 = olhrr(ctxt, REGTMP, r, p->to.reg, p->scond); | |
| if(p->as == AMOVB || p->as == AMOVBS) | |
| o2 ^= (1<<5)|(1<<6); | |
| else if(p->as == AMOVH || p->as == AMOVHS) | |
| o2 ^= (1<<6); | |
| break; | |
| case 74: /* bx $I */ | |
| ctxt->diag("ABX $I"); | |
| break; | |
| case 75: /* bx O(R) */ | |
| aclass(ctxt, &p->to); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("non-zero offset in ABX"); | |
| /* | |
| o1 = oprrr(ctxt, AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR | |
| o2 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg; // BX R | |
| */ | |
| // p->to.reg may be REGLINK | |
| o1 = oprrr(ctxt, AADD, p->scond); | |
| o1 |= immrot(ctxt->instoffset); | |
| o1 |= p->to.reg << 16; | |
| o1 |= REGTMP << 12; | |
| o2 = oprrr(ctxt, AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR | |
| o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp | |
| break; | |
| case 76: /* bx O(R) when returning from fn*/ | |
| ctxt->diag("ABXRET"); | |
| break; | |
| case 77: /* ldrex oreg,reg */ | |
| aclass(ctxt, &p->from); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in LDREX"); | |
| o1 = (0x19<<20) | (0xf9f); | |
| o1 |= p->from.reg << 16; | |
| o1 |= p->to.reg << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 78: /* strex reg,oreg,reg */ | |
| aclass(ctxt, &p->from); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in STREX"); | |
| o1 = (0x18<<20) | (0xf90); | |
| o1 |= p->from.reg << 16; | |
| o1 |= p->reg << 0; | |
| o1 |= p->to.reg << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 80: /* fmov zfcon,freg */ | |
| if(p->as == AMOVD) { | |
| o1 = 0xeeb00b00; // VMOV imm 64 | |
| o2 = oprrr(ctxt, ASUBD, p->scond); | |
| } else { | |
| o1 = 0x0eb00a00; // VMOV imm 32 | |
| o2 = oprrr(ctxt, ASUBF, p->scond); | |
| } | |
| v = 0x70; // 1.0 | |
| r = p->to.reg; | |
| // movf $1.0, r | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| o1 |= r << 12; | |
| o1 |= (v&0xf) << 0; | |
| o1 |= (v&0xf0) << 12; | |
| // subf r,r,r | |
| o2 |= r | (r<<16) | (r<<12); | |
| break; | |
| case 81: /* fmov sfcon,freg */ | |
| o1 = 0x0eb00a00; // VMOV imm 32 | |
| if(p->as == AMOVD) | |
| o1 = 0xeeb00b00; // VMOV imm 64 | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| o1 |= p->to.reg << 12; | |
| v = chipfloat5(ctxt, p->from.u.dval); | |
| o1 |= (v&0xf) << 0; | |
| o1 |= (v&0xf0) << 12; | |
| break; | |
| case 82: /* fcmp freg,freg, */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= (p->reg<<12) | (p->from.reg<<0); | |
| o2 = 0x0ef1fa10; // VMRS R15 | |
| o2 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 83: /* fcmp freg,, */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= (p->from.reg<<12) | (1<<16); | |
| o2 = 0x0ef1fa10; // VMRS R15 | |
| o2 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 84: /* movfw freg,freg - truncate float-to-fix */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= (p->from.reg<<0); | |
| o1 |= (p->to.reg<<12); | |
| break; | |
| case 85: /* movwf freg,freg - fix-to-float */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= (p->from.reg<<0); | |
| o1 |= (p->to.reg<<12); | |
| break; | |
| case 86: /* movfw freg,reg - truncate float-to-fix */ | |
| // macro for movfw freg,FTMP; movw FTMP,reg | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= (p->from.reg<<0); | |
| o1 |= (FREGTMP<<12); | |
| o2 = oprrr(ctxt, AMOVFW+AEND, p->scond); | |
| o2 |= (FREGTMP<<16); | |
| o2 |= (p->to.reg<<12); | |
| break; | |
| case 87: /* movwf reg,freg - fix-to-float */ | |
| // macro for movw reg,FTMP; movwf FTMP,freg | |
| o1 = oprrr(ctxt, AMOVWF+AEND, p->scond); | |
| o1 |= (p->from.reg<<12); | |
| o1 |= (FREGTMP<<16); | |
| o2 = oprrr(ctxt, p->as, p->scond); | |
| o2 |= (FREGTMP<<0); | |
| o2 |= (p->to.reg<<12); | |
| break; | |
| case 88: /* movw reg,freg */ | |
| o1 = oprrr(ctxt, AMOVWF+AEND, p->scond); | |
| o1 |= (p->from.reg<<12); | |
| o1 |= (p->to.reg<<16); | |
| break; | |
| case 89: /* movw freg,reg */ | |
| o1 = oprrr(ctxt, AMOVFW+AEND, p->scond); | |
| o1 |= (p->from.reg<<16); | |
| o1 |= (p->to.reg<<12); | |
| break; | |
| case 90: /* tst reg */ | |
| o1 = oprrr(ctxt, ACMP+AEND, p->scond); | |
| o1 |= p->from.reg<<16; | |
| break; | |
| case 91: /* ldrexd oreg,reg */ | |
| aclass(ctxt, &p->from); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in LDREX"); | |
| o1 = (0x1b<<20) | (0xf9f); | |
| o1 |= p->from.reg << 16; | |
| o1 |= p->to.reg << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 92: /* strexd reg,oreg,reg */ | |
| aclass(ctxt, &p->from); | |
| if(ctxt->instoffset != 0) | |
| ctxt->diag("offset must be zero in STREX"); | |
| o1 = (0x1a<<20) | (0xf90); | |
| o1 |= p->from.reg << 16; | |
| o1 |= p->reg << 0; | |
| o1 |= p->to.reg << 12; | |
| o1 |= (p->scond & C_SCOND) << 28; | |
| break; | |
| case 93: /* movb/movh/movhu addr,R -> ldrsb/ldrsh/ldrh */ | |
| o1 = omvl(ctxt, p, &p->from, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = olhr(ctxt, 0, REGTMP, p->to.reg, p->scond); | |
| if(p->as == AMOVB || p->as == AMOVBS) | |
| o2 ^= (1<<5)|(1<<6); | |
| else if(p->as == AMOVH || p->as == AMOVHS) | |
| o2 ^= (1<<6); | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| case 94: /* movh/movhu R,addr -> strh */ | |
| o1 = omvl(ctxt, p, &p->to, REGTMP); | |
| if(!o1) | |
| break; | |
| o2 = oshr(ctxt, p->from.reg, 0, REGTMP, p->scond); | |
| if(o->flag & LPCREL) { | |
| o3 = o2; | |
| o2 = oprrr(ctxt, AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; | |
| } | |
| break; | |
| case 95: /* PLD off(reg) */ | |
| o1 = 0xf5d0f000; | |
| o1 |= p->from.reg << 16; | |
| if(p->from.offset < 0) { | |
| o1 &= ~(1 << 23); | |
| o1 |= (-p->from.offset) & 0xfff; | |
| } else | |
| o1 |= p->from.offset & 0xfff; | |
| break; | |
| case 96: /* UNDEF */ | |
| // This is supposed to be something that stops execution. | |
| // It's not supposed to be reached, ever, but if it is, we'd | |
| // like to be able to tell how we got there. Assemble as | |
| // 0xf7fabcfd which is guaranteed to raise undefined instruction | |
| // exception. | |
| o1 = 0xf7fabcfd; | |
| break; | |
| case 97: /* CLZ Rm, Rd */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= p->to.reg << 12; | |
| o1 |= p->from.reg; | |
| break; | |
| case 98: /* MULW{T,B} Rs, Rm, Rd */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= p->to.reg << 16; | |
| o1 |= p->from.reg << 8; | |
| o1 |= p->reg; | |
| break; | |
| case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ | |
| o1 = oprrr(ctxt, p->as, p->scond); | |
| o1 |= p->to.reg << 12; | |
| o1 |= p->from.reg << 8; | |
| o1 |= p->reg; | |
| o1 |= p->to.offset << 16; | |
| break; | |
| } | |
| out[0] = o1; | |
| out[1] = o2; | |
| out[2] = o3; | |
| out[3] = o4; | |
| out[4] = o5; | |
| out[5] = o6; | |
| return; | |
| #ifdef NOTDEF | |
| v = p->pc; | |
| switch(o->size) { | |
| default: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux:\t\t%P\n", v, p); | |
| break; | |
| case 4: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); | |
| lputl(o1); | |
| break; | |
| case 8: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux %.8ux%P\n", v, o1, o2, p); | |
| lputl(o1); | |
| lputl(o2); | |
| break; | |
| case 12: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, p); | |
| lputl(o1); | |
| lputl(o2); | |
| lputl(o3); | |
| break; | |
| case 16: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux%P\n", | |
| v, o1, o2, o3, o4, p); | |
| lputl(o1); | |
| lputl(o2); | |
| lputl(o3); | |
| lputl(o4); | |
| break; | |
| case 20: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", | |
| v, o1, o2, o3, o4, o5, p); | |
| lputl(o1); | |
| lputl(o2); | |
| lputl(o3); | |
| lputl(o4); | |
| lputl(o5); | |
| break; | |
| case 24: | |
| if(debug['a']) | |
| Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", | |
| v, o1, o2, o3, o4, o5, o6, p); | |
| lputl(o1); | |
| lputl(o2); | |
| lputl(o3); | |
| lputl(o4); | |
| lputl(o5); | |
| lputl(o6); | |
| break; | |
| } | |
| #endif | |
| } | |
| static int32 | |
| oprrr(Link *ctxt, int a, int sc) | |
| { | |
| int32 o; | |
| o = (sc & C_SCOND) << 28; | |
| if(sc & C_SBIT) | |
| o |= 1 << 20; | |
| if(sc & (C_PBIT|C_WBIT)) | |
| ctxt->diag(".nil/.W on dp instruction"); | |
| switch(a) { | |
| case AMULU: | |
| case AMUL: return o | (0x0<<21) | (0x9<<4); | |
| case AMULA: return o | (0x1<<21) | (0x9<<4); | |
| case AMULLU: return o | (0x4<<21) | (0x9<<4); | |
| case AMULL: return o | (0x6<<21) | (0x9<<4); | |
| case AMULALU: return o | (0x5<<21) | (0x9<<4); | |
| case AMULAL: return o | (0x7<<21) | (0x9<<4); | |
| case AAND: return o | (0x0<<21); | |
| case AEOR: return o | (0x1<<21); | |
| case ASUB: return o | (0x2<<21); | |
| case ARSB: return o | (0x3<<21); | |
| case AADD: return o | (0x4<<21); | |
| case AADC: return o | (0x5<<21); | |
| case ASBC: return o | (0x6<<21); | |
| case ARSC: return o | (0x7<<21); | |
| case ATST: return o | (0x8<<21) | (1<<20); | |
| case ATEQ: return o | (0x9<<21) | (1<<20); | |
| case ACMP: return o | (0xa<<21) | (1<<20); | |
| case ACMN: return o | (0xb<<21) | (1<<20); | |
| case AORR: return o | (0xc<<21); | |
| case AMOVB: | |
| case AMOVH: | |
| case AMOVW: return o | (0xd<<21); | |
| case ABIC: return o | (0xe<<21); | |
| case AMVN: return o | (0xf<<21); | |
| case ASLL: return o | (0xd<<21) | (0<<5); | |
| case ASRL: return o | (0xd<<21) | (1<<5); | |
| case ASRA: return o | (0xd<<21) | (2<<5); | |
| case ASWI: return o | (0xf<<24); | |
| case AADDD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (0<<4); | |
| case AADDF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (0<<4); | |
| case ASUBD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (4<<4); | |
| case ASUBF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (4<<4); | |
| case AMULD: return o | (0xe<<24) | (0x2<<20) | (0xb<<8) | (0<<4); | |
| case AMULF: return o | (0xe<<24) | (0x2<<20) | (0xa<<8) | (0<<4); | |
| case ADIVD: return o | (0xe<<24) | (0x8<<20) | (0xb<<8) | (0<<4); | |
| case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); | |
| case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); | |
| case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); | |
| case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4); | |
| case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4); | |
| case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); | |
| case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); | |
| case AMOVF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (4<<4); | |
| case AMOVD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (4<<4); | |
| case AMOVDF: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | | |
| (1<<8); // dtof | |
| case AMOVFD: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | | |
| (0<<8); // dtof | |
| case AMOVWF: | |
| if((sc & C_UBIT) == 0) | |
| o |= 1<<7; /* signed */ | |
| return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | | |
| (0<<18) | (0<<8); // toint, double | |
| case AMOVWD: | |
| if((sc & C_UBIT) == 0) | |
| o |= 1<<7; /* signed */ | |
| return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | | |
| (0<<18) | (1<<8); // toint, double | |
| case AMOVFW: | |
| if((sc & C_UBIT) == 0) | |
| o |= 1<<16; /* signed */ | |
| return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | | |
| (1<<18) | (0<<8) | (1<<7); // toint, double, trunc | |
| case AMOVDW: | |
| if((sc & C_UBIT) == 0) | |
| o |= 1<<16; /* signed */ | |
| return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | | |
| (1<<18) | (1<<8) | (1<<7); // toint, double, trunc | |
| case AMOVWF+AEND: // copy WtoF | |
| return o | (0xe<<24) | (0x0<<20) | (0xb<<8) | (1<<4); | |
| case AMOVFW+AEND: // copy FtoW | |
| return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); | |
| case ACMP+AEND: // cmp imm | |
| return o | (0x3<<24) | (0x5<<20); | |
| case ACLZ: | |
| // CLZ doesn't support .nil | |
| return (o & (0xf<<28)) | (0x16f<<16) | (0xf1<<4); | |
| case AMULWT: | |
| return (o & (0xf<<28)) | (0x12 << 20) | (0xe<<4); | |
| case AMULWB: | |
| return (o & (0xf<<28)) | (0x12 << 20) | (0xa<<4); | |
| case AMULAWT: | |
| return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); | |
| case AMULAWB: | |
| return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); | |
| case ABL: // BLX REG | |
| return (o & (0xf<<28)) | (0x12fff3 << 4); | |
| } | |
| ctxt->diag("bad rrr %d", a); | |
| prasm(ctxt->curp); | |
| return 0; | |
| } | |
| static int32 | |
| opbra(Link *ctxt, int a, int sc) | |
| { | |
| if(sc & (C_SBIT|C_PBIT|C_WBIT)) | |
| ctxt->diag(".nil/.nil/.W on bra instruction"); | |
| sc &= C_SCOND; | |
| if(a == ABL) | |
| return (sc<<28)|(0x5<<25)|(0x1<<24); | |
| if(sc != 0xe) | |
| ctxt->diag(".COND on bcond instruction"); | |
| switch(a) { | |
| case ABEQ: return (0x0<<28)|(0x5<<25); | |
| case ABNE: return (0x1<<28)|(0x5<<25); | |
| case ABCS: return (0x2<<28)|(0x5<<25); | |
| case ABHS: return (0x2<<28)|(0x5<<25); | |
| case ABCC: return (0x3<<28)|(0x5<<25); | |
| case ABLO: return (0x3<<28)|(0x5<<25); | |
| case ABMI: return (0x4<<28)|(0x5<<25); | |
| case ABPL: return (0x5<<28)|(0x5<<25); | |
| case ABVS: return (0x6<<28)|(0x5<<25); | |
| case ABVC: return (0x7<<28)|(0x5<<25); | |
| case ABHI: return (0x8<<28)|(0x5<<25); | |
| case ABLS: return (0x9<<28)|(0x5<<25); | |
| case ABGE: return (0xa<<28)|(0x5<<25); | |
| case ABLT: return (0xb<<28)|(0x5<<25); | |
| case ABGT: return (0xc<<28)|(0x5<<25); | |
| case ABLE: return (0xd<<28)|(0x5<<25); | |
| case AB: return (0xe<<28)|(0x5<<25); | |
| } | |
| ctxt->diag("bad bra %A", a); | |
| prasm(ctxt->curp); | |
| return 0; | |
| } | |
| static int32 | |
| olr(Link *ctxt, int32 v, int b, int r, int sc) | |
| { | |
| int32 o; | |
| if(sc & C_SBIT) | |
| ctxt->diag(".nil on LDR/STR instruction"); | |
| o = (sc & C_SCOND) << 28; | |
| if(!(sc & C_PBIT)) | |
| o |= 1 << 24; | |
| if(!(sc & C_UBIT)) | |
| o |= 1 << 23; | |
| if(sc & C_WBIT) | |
| o |= 1 << 21; | |
| o |= (1<<26) | (1<<20); | |
| if(v < 0) { | |
| if(sc & C_UBIT) | |
| ctxt->diag(".U on neg offset"); | |
| v = -v; | |
| o ^= 1 << 23; | |
| } | |
| if(v >= (1<<12) || v < 0) | |
| ctxt->diag("literal span too large: %d (R%d)\n%P", v, b, ctxt->printp); | |
| o |= v; | |
| o |= b << 16; | |
| o |= r << 12; | |
| return o; | |
| } | |
| static int32 | |
| olhr(Link *ctxt, int32 v, int b, int r, int sc) | |
| { | |
| int32 o; | |
| if(sc & C_SBIT) | |
| ctxt->diag(".nil on LDRH/STRH instruction"); | |
| o = (sc & C_SCOND) << 28; | |
| if(!(sc & C_PBIT)) | |
| o |= 1 << 24; | |
| if(sc & C_WBIT) | |
| o |= 1 << 21; | |
| o |= (1<<23) | (1<<20)|(0xb<<4); | |
| if(v < 0) { | |
| v = -v; | |
| o ^= 1 << 23; | |
| } | |
| if(v >= (1<<8) || v < 0) | |
| ctxt->diag("literal span too large: %d (R%d)\n%P", v, b, ctxt->printp); | |
| o |= (v&0xf)|((v>>4)<<8)|(1<<22); | |
| o |= b << 16; | |
| o |= r << 12; | |
| return o; | |
| } | |
| static int32 | |
| osr(Link *ctxt, int a, int r, int32 v, int b, int sc) | |
| { | |
| int32 o; | |
| o = olr(ctxt, v, b, r, sc) ^ (1<<20); | |
| if(a != AMOVW) | |
| o |= 1<<22; | |
| return o; | |
| } | |
| static int32 | |
| oshr(Link *ctxt, int r, int32 v, int b, int sc) | |
| { | |
| int32 o; | |
| o = olhr(ctxt, v, b, r, sc) ^ (1<<20); | |
| return o; | |
| } | |
| static int32 | |
| osrr(Link *ctxt, int r, int i, int b, int sc) | |
| { | |
| return olr(ctxt, i, b, r, sc) ^ ((1<<25) | (1<<20)); | |
| } | |
| static int32 | |
| oshrr(Link *ctxt, int r, int i, int b, int sc) | |
| { | |
| return olhr(ctxt, i, b, r, sc) ^ ((1<<22) | (1<<20)); | |
| } | |
| static int32 | |
| olrr(Link *ctxt, int i, int b, int r, int sc) | |
| { | |
| return olr(ctxt, i, b, r, sc) ^ (1<<25); | |
| } | |
| static int32 | |
| olhrr(Link *ctxt, int i, int b, int r, int sc) | |
| { | |
| return olhr(ctxt, i, b, r, sc) ^ (1<<22); | |
| } | |
| static int32 | |
| ofsr(Link *ctxt, int a, int r, int32 v, int b, int sc, Prog *p) | |
| { | |
| int32 o; | |
| if(sc & C_SBIT) | |
| ctxt->diag(".nil on FLDR/FSTR instruction"); | |
| o = (sc & C_SCOND) << 28; | |
| if(!(sc & C_PBIT)) | |
| o |= 1 << 24; | |
| if(sc & C_WBIT) | |
| o |= 1 << 21; | |
| o |= (6<<25) | (1<<24) | (1<<23) | (10<<8); | |
| if(v < 0) { | |
| v = -v; | |
| o ^= 1 << 23; | |
| } | |
| if(v & 3) | |
| ctxt->diag("odd offset for floating point op: %d\n%P", v, p); | |
| else | |
| if(v >= (1<<10) || v < 0) | |
| ctxt->diag("literal span too large: %d\n%P", v, p); | |
| o |= (v>>2) & 0xFF; | |
| o |= b << 16; | |
| o |= r << 12; | |
| switch(a) { | |
| default: | |
| ctxt->diag("bad fst %A", a); | |
| case AMOVD: | |
| o |= 1 << 8; | |
| case AMOVF: | |
| break; | |
| } | |
| return o; | |
| } | |
| static int32 | |
| omvl(Link *ctxt, Prog *p, Addr *a, int dr) | |
| { | |
| int32 v, o1; | |
| if(!p->pcond) { | |
| aclass(ctxt, a); | |
| v = immrot(~ctxt->instoffset); | |
| if(v == 0) { | |
| ctxt->diag("missing literal"); | |
| prasm(p); | |
| return 0; | |
| } | |
| o1 = oprrr(ctxt, AMVN, p->scond&C_SCOND); | |
| o1 |= v; | |
| o1 |= dr << 12; | |
| } else { | |
| v = p->pcond->pc - p->pc - 8; | |
| o1 = olr(ctxt, v, REGPC, dr, p->scond&C_SCOND); | |
| } | |
| return o1; | |
| } | |
| int | |
| chipzero5(Link *ctxt, float64 e) | |
| { | |
| // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. | |
| if(ctxt->goarm < 7 || e != 0) | |
| return -1; | |
| return 0; | |
| } | |
| int | |
| chipfloat5(Link *ctxt, float64 e) | |
| { | |
| int n; | |
| ulong h1; | |
| int32 l, h; | |
| uint64 ei; | |
| // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. | |
| if(ctxt->goarm < 7) | |
| goto no; | |
| memmove(&ei, &e, 8); | |
| l = (int32)ei; | |
| h = (int32)(ei>>32); | |
| if(l != 0 || (h&0xffff) != 0) | |
| goto no; | |
| h1 = h & 0x7fc00000; | |
| if(h1 != 0x40000000 && h1 != 0x3fc00000) | |
| goto no; | |
| n = 0; | |
| // sign bit (a) | |
| if(h & 0x80000000) | |
| n |= 1<<7; | |
| // exp sign bit (b) | |
| if(h1 == 0x3fc00000) | |
| n |= 1<<6; | |
| // rest of exp and mantissa (cd-efgh) | |
| n |= (h >> 16) & 0x3f; | |
| //print("match %.8lux %.8lux %d\n", l, h, n); | |
| return n; | |
| no: | |
| return -1; | |
| } |